Files
projectAIpopular/router_system/inference.py
T
tzt 8820e8da48 feat(v2): 架构与算法优化二轮——推理机不变量外提、知识库匹配预编译、协作循环增量索引
- inference.py(/chat/legacy 热路径):kb.match 循环不变量外提(原每步全量重扫+重排序,
  最坏 O(steps×rules×patterns));fired 查重 list→set
- knowledge.py:Rule patterns 注册侧懒缓存小写副本(原每条规则每次匹配重复 lower);
  match() 文本只 lower 一次(原逐规则重复);load() 的 yaml 文件名集合提到循环外
- worker.py:本地端点生成器 httpx.AsyncClient 懒建复用(原每步新建/销毁连接,
  对齐 ArchitectClient 惯用法;协作循环最多 10 次生成免重复建连)
- pipeline.py(协作循环):plan_by_id O(1) 步定义查找;done 集合增量维护
  (原每轮重建 progress+archive 扫描);领域只解析一次(原 _artifact_name 每步
  全领域 kb.match);_deps_done 支持传入预填集合(保持旧签名兼容)
- v2stats.py:回合数分布改增量聚合(sum/max/分桶计数),summary() O(n)→O(1),
  不再持有无界 list(修长时运行内存增长)
- gateway/agent.py + api.py:AgentService 运行计数 O(1) 化(原 register 全量扫描),
  状态迁移收敛到 _transition_state 单一入口(api.py cancel/异常两处绕过点一并接入,
  消除计数与状态脱节隐患);21 项 agent 测试全绿(两轮全量 230 passed 复核)
2026-09-18 23:45:35 +08:00

100 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""前向链推理机:知识库规则驱动的工作记忆演化(专家系统推理核心,零依赖)。
流程(经典前向链 forward chaining):
1. 初始化黑板:写入领域/难度/置信度等事实
2. 循环:在领域内匹配规则(未触发过的)→ 按优先级执行
- 命中即记录轨迹 rule:<id>@<priority>
- 规则带 output 模板 → 渲染后写入黑板章节(部分解)
- 规则带 actions → 执行动作(写事实/写章节)
3. 终止:无新规则可触发 / 达到步数上限(防死循环)
确定性保证:规则匹配基于子串包含,无随机性;同输入 → 同轨迹。
"""
from __future__ import annotations
from typing import Any, Dict, List, Optional
from .knowledge import KnowledgeBase, Rule
from .memory import WorkingMemory
def render_template(template: str, query: str, facts: Dict[str, Any]) -> str:
"""渲染输出模板:替换 {query} 与 {facts.<key>} 占位符;缺失以 [未提供] 占位,不抛异常。"""
out = template.replace("{query}", query)
for key, value in facts.items():
out = out.replace(f"{{facts.{key}}}", str(value))
# 剩余占位符兜底
while "{" in out and "}" in out:
start = out.find("{")
end = out.find("}", start)
if end == -1:
break
out = out[:start] + "[未提供]" + out[end + 1:]
return out
class InferenceEngine:
"""前向链推理机。"""
def __init__(self, kb: KnowledgeBase, max_steps: int = 20):
self.kb = kb
self.max_steps = max_steps
# ---------------------------------------------------------------
def initialize(self, query: str, domain: str, difficulty: str,
confidence: float, memory: WorkingMemory) -> None:
"""把分类结果写入黑板(事实初始化)。"""
memory.write_fact("query", query)
memory.write_fact("domain", domain)
memory.write_fact("difficulty", difficulty)
memory.write_fact("confidence", round(confidence, 4))
memory.add_trace(f"init:domain={domain},difficulty={difficulty},conf={confidence:.2f}")
# ---------------------------------------------------------------
def run(self, query: str, domain: str, memory: WorkingMemory,
max_steps: Optional[int] = None) -> List[str]:
"""前向链主循环。返回触发规则 id 列表(按触发顺序)。
循环不变量外提:query/domain 在循环内不变,kb.match 结果只算一次
(原实现每步全量重扫+重排序,最坏 O(steps × rules × patterns))。
"""
steps = max_steps or self.max_steps
rules = self.kb.match(query, domain=domain)
fired: List[str] = []
fired_set: set = set() # O(1) 查重(fired 保持列表维护触发顺序)
for _ in range(steps):
# 选第一个"未触发过"的规则
target: Optional[Rule] = None
for r in rules:
if r.id not in fired_set:
target = r
break
if target is None:
break # 无新规则可触发 → 终止
fired.append(target.id)
fired_set.add(target.id)
self._fire(target, query, memory)
return fired
# ---------------------------------------------------------------
def _fire(self, rule: Rule, query: str, memory: WorkingMemory) -> None:
"""执行一条规则:记录轨迹 + 写事实 + 产出章节。"""
memory.add_trace(f"rule:{rule.id}@{rule.priority}")
# 规则动作
for action in rule.actions:
self._apply_action(action, rule, query, memory)
# 规则输出模板 → 章节
if rule.output:
text = render_template(rule.output, query, memory.facts)
memory.write_section(rule.id, text)
def _apply_action(self, action: str, rule: Rule, query: str,
memory: WorkingMemory) -> None:
"""动作格式:write_fact:key=valuevalue 支持 {query} 占位)。"""
if action.startswith("write_fact:"):
kv = action[len("write_fact:"):]
key, _, value = kv.partition("=")
value = value.replace("{query}", query)
memory.write_fact(key.strip(), value.strip(), rule_id=rule.id)
# 其他动作类型暂不实现(保留扩展位)