chore: T-P-1 工作区收敛——并行会话成果与历史未入库文件整理入库

- 入库历史遗漏源码/测试:router_system 9 模块(agent/executors/inference/knowledge/
  memory/planner/skills/trace)、tests 11 个测试文件、config/knowledge 领域知识
- 入库根目录方案文档(v2/v3/可行性×2)、references 文献(arxiv 14-18/cnki_open/
  参考文献清单)、research 论文素材(routerarena/paper/中文文献 PDF)
- 前端构建产物刷新(新 hash);webapp 误写文档删除
- gitignore 增补:deepseek-harness、research/_refs、.mimosa/.zcode、网关日志/pid、
  临时调试脚本、tests/e2e/node_modules、AI代理功能开发/prefix
- 基线确认:318 passed
This commit is contained in:
tzt
2026-09-05 08:28:25 +08:00
parent 747d85c3ba
commit ce0f6170d3
82 changed files with 74132 additions and 36 deletions
+103
View File
@@ -0,0 +1,103 @@
"""规则 Planner:把查询拆解为子任务 DAG(任务分解,专家系统风格,零参数)。
拆解逻辑(确定性规则):
1. 在分类领域内匹配知识规则
2. 取最高优先级且带 template 的命中规则 → 对应任务模板
3. 非 easy 难度且有模板 → 生成多节点 DAG(模板 steps 转 TaskNode,含依赖)
4. easy 难度或无模板命中 → 单节点直接求解(不拆,最小开销)
5. 拆解深度防护:节点不再递归拆解(当前为单层拆解,模板本身即最终粒度)
对齐架构目标:"路由模型把任务拆解后分步骤交给各个小模型"
L0 模式下各子任务由规则执行器完成(零参数),L2 模式可交给本地小模型。
"""
from __future__ import annotations
from typing import List, Optional
from .knowledge import KnowledgeBase
from .memory import TaskGraph, TaskNode
from .models import Classification
# 单节点求解时按领域选择默认动作 kind
_SINGLE_KIND = {
"code": "implement",
"math": "solve",
"legal": "conclude",
"medical": "advise",
"general": "explain",
"finance": "conclude",
"life": "advise",
"education": "design",
}
# 强制拆解领域:即使 easy 也走完整任务模板
# legal 需要 retrieve+disclaimermedical 需要 advise+warning
# finance 需要 retrieve+风险免责——均为领域硬要求)
FORCE_SPLIT_DOMAINS = {"legal", "medical", "finance"}
# 强制拆解模板:命中即拆(debug 流程必须 analyze→diagnose→fix→verify
FORCE_SPLIT_TEMPLATES = {"code-debug"}
class Planner:
"""规则 Planner:查询 → 子任务 DAG。"""
def __init__(self, kb: KnowledgeBase, max_depth: int = 3):
self.kb = kb
self.max_depth = max_depth
# ---------------------------------------------------------------
def plan(self, query: str, classification: Classification) -> TaskGraph:
domain = classification.domain
difficulty = classification.difficulty
# 1. 领域内匹配规则,取最高优先级带模板的规则
template_id: Optional[str] = None
hits = self.kb.match(query, domain=domain)
for h in hits:
if h.template:
template_id = h.template
break
graph = TaskGraph()
# 2. 非 easy / 强制拆解领域 / 强制拆解模板 → 多节点 DAG
if template_id and (difficulty != "easy"
or domain in FORCE_SPLIT_DOMAINS
or template_id in FORCE_SPLIT_TEMPLATES):
tpl = self.kb.task_template(template_id)
if tpl and tpl.get("steps"):
for step in tpl["steps"]:
node = TaskNode(
id=str(step["id"]),
kind=str(step.get("kind", "solve")),
domain=str(step.get("domain", domain)),
query=query,
deps=[str(d) for d in step.get("deps", [])],
desc=str(step.get("desc", "")),
)
graph.add_node(node)
return graph
# 3. easy / 无模板 → 单节点
kind = _SINGLE_KIND.get(domain, "explain")
graph.add_node(TaskNode(
id="solve",
kind=kind,
domain=domain,
query=query,
desc=f"单节点求解({domain}/{difficulty}",
))
return graph
# ---------------------------------------------------------------
def explain_plan(self, graph: TaskGraph) -> List[str]:
"""把 DAG 渲染为可读的拆解轨迹(用于 route 与 --trace)。"""
if len(graph) == 1:
n = graph.nodes()[0]
return [f"plan:single[{n.kind}]"]
parts = []
for n in graph.topo_order():
dep = f"<{','.join(n.deps)}" if n.deps else ""
parts.append(f"{n.id}:{n.kind}{dep}")
return [f"plan:multi[{len(graph)}]({' -> '.join(parts)})"]