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:
@@ -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+disclaimer,medical 需要 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)})"]
|
||||
Reference in New Issue
Block a user