# refactor: 路由架构简化 — 统一 REACT Agent Loop status: active created: 2026-06-16 depth: Standard --- ## Summary 将当前 4 层路由架构(HeuristicClassifier → LLM classify → SemanticRouter → IntentRouter)简化为极简路由层 + 统一 REACT Agent Loop(Hermes 模式 Prompt-based XML tool calling)。删除意图预测层,让 LLM 在 agent loop 中看到完整工具描述后自主决策。 ## Problem Frame 当前 CostAwareRouter 的 4 层路由架构存在根本性设计缺陷: 1. **路由层预测意图是反模式** — LLM 在路由层看不到工具上下文,必然误判(如"查下ip"被分为 direct_agent) 2. **枚举永远覆盖不完** — HeuristicClassifier 的关键词列表无法覆盖所有口语化说法 3. **多层路由增加延迟** — 每次查询 3 次 LLM 调用(路由1 + REACT2),响应 3-5s 4. **双链路不一致** — Portal REST 走 IntentRouter,WebSocket 走 CostAwareRouter 5. **工具格式不兼容** — 百炼 Coding 不支持原生 function calling,模型输出 `` 文本但引擎无法解析 **行业验证**:Codex、Trae、Hermes、OpenClaw 均无独立路由层,统一 agent loop 是业界标准。 ## Requirements - R1: 删除 HeuristicClassifier、IntentRouter、SemanticRouter 的路由决策功能 - R2: 保留极简路由层(@skill 前缀 + 问候/闲聊检测) - R3: 统一 REACT Agent Loop,System Prompt 注入完整工具描述 - R4: Prompt-based XML tool calling(`` 格式),后端解析执行 - R5: Portal REST 和 WebSocket 统一路由路径 - R6: 聊天记录持久化(Portal ConversationStore → SessionManager) - R7: 回测验证:执行模式准确率 >85%,工具调用成功率 >95%,口语化查询成功率 >90% - R8: 性能指标:响应时间 <3s(简单查询),LLM 调用次数 ≤2 次/查询 --- ## Key Technical Decisions ### KTD-1: 采用 Hermes 模式 Prompt-based XML Tool Calling **决策**:System Prompt 中定义 `` 格式,LLM 输出 XML 标签,后端解析执行。 **理由**: - 百炼 Coding(qwen3.7-plus)不支持原生 function calling - 截图验证模型已理解 `` 格式 - 与 Hermes 架构一致,模型无关 **替代方案**: - 原生 function calling:百炼 Coding 不兼容 - Action: 格式:不如 XML 结构化 ### KTD-2: 删除路由层意图预测,保留极简规则层 **决策**:只保留 @skill 前缀路由和问候/闲聊检测,其他所有查询默认走 REACT。 **理由**: - 路由层预测意图的准确率远低于 LLM 在 agent loop 中的决策 - 删除路由层节省 1 次 LLM 调用(~500ms,~1000 tokens) - 问候/闲聊检测是确定性规则,零误判 ### KTD-3: 工具全量加载(第一阶段) **决策**:默认加载所有 21 个工具到 System Prompt,通过 @skill 前缀实现按需加载。 **理由**: - 21 个工具的描述约 2000 tokens,成本可接受 - 全量加载保证 LLM 能看到所有工具,零误判 - 按需加载(Regex 筛选)留作第二阶段优化 ### KTD-4: 保留其他 Agent 架构作为 skill 配置可选模式 **决策**:ReWOOAgent、ReflexionAgent 等保留,通过 skill YAML 的 `execution_mode` 字段切换。 **理由**: - 不同场景需要不同执行模式(代码生成用 ReWOO,失败重试用 Reflexion) - 已有投入不应浪费 - 只是路由方式变了,执行模式不变 --- ## Scope Boundaries ### In Scope - 简化 CostAwareRouter 为极简路由层 - ReActEngine 改为 prompt-based tool calling - Portal REST/WebSocket 统一路由 - 聊天记录持久化 - E2E 回测和指标验证 ### Out of Scope - Embedding API 集成(待用户提供 API key) - 前端 GUI 改造 - Expert Team 模式重构 - 工具按需加载的 Regex 筛选层(第二阶段) ### Deferred to Follow-Up Work - SemanticRouter 降级为可选插件 - 工具数量 >30 时的分组加载策略 - 响应流式优化(SSE chunk 细化) --- ## High-Level Technical Design ### 目标架构 ``` 用户输入 ↓ SimpleRouter(极简路由层,<1ms) ├─ @skill:xxx → 加载指定 skill 工具 → REACT Agent ├─ 问候/闲聊(regex)→ DIRECT_CHAT(无工具,快速路径) └─ 其他 → 加载所有默认工具 → REACT Agent ↓ REACT Agent Loop ├─ System Prompt: 工具描述 + 格式说明 ├─ LLM 决策: 需要 → 输出 → 解析执行 → Observation → 继续 └─ LLM 决策: 不需要 → 直接回答 → final_answer ``` ### 路由简化对比 | 组件 | 当前 | 目标 | |------|------|------| | CostAwareRouter.route() | 1688 行,4 层 | ~200 行,1 层 | | HeuristicClassifier | 310 行 | 删除 | | IntentRouter | 206 行 | 删除路由功能 | | SemanticRouter | 224 行 | 删除路由功能 | | _classify_merged | 200 行 | 删除 | | _route_layer2 | 210 行 | 删除 | --- ## Implementation Units ### U1. 创建 SimpleRouter 替代 CostAwareRouter **Goal**: 实现极简路由层,只保留 @skill 前缀和问候/闲聊检测 **Requirements**: R1, R2 **Dependencies**: 无 **Files**: - `src/agentkit/chat/simple_router.py` (新建) - `src/agentkit/chat/skill_routing.py` (修改 — 保留 SkillRoutingResult、ExecutionMode、parse_skill_prefix) - `tests/unit/chat/test_simple_router.py` (新建) **Approach**: 1. 新建 `SimpleRouter` 类,包含 `route()` 方法 2. `route()` 逻辑:@skill 前缀 → 指定 skill;问候/闲聊 regex → DIRECT_CHAT;其他 → REACT 3. 保留 `SkillRoutingResult` 数据类和 `ExecutionMode` 枚举 4. 保留 `parse_skill_prefix()` 函数 5. 保留 `_GREETING_RE` 和 `_CHAT_MODE_RE` 正则 **Test scenarios**: - @skill:shell 前缀正确路由到 shell skill - "你好" 路由到 DIRECT_CHAT - "查看当前ip" 路由到 REACT - "查下ip" 路由到 REACT - "翻译hello" 路由到 REACT(LLM 决定不需要工具) - 无前缀无问候的复杂查询路由到 REACT **Verification**: 所有测试通过,SimpleRouter.route() 返回正确的 ExecutionMode ### U2. ReActEngine 改为 Prompt-based XML Tool Calling **Goal**: ReActEngine 的 system prompt 注入完整工具描述和 `` 格式说明 **Requirements**: R3, R4 **Dependencies**: U1 **Files**: - `src/agentkit/core/react.py` (修改) - `tests/unit/core/test_react_tool_format.py` (新建) **Approach**: 1. 新增 `_build_tool_use_system_prompt()` 方法,生成包含工具描述和 `` 格式说明的 system prompt 2. 在 `execute_stream()` 中,当 LLM 不支持原生 function calling 时,使用 prompt-based 模式 3. 确保 `_parse_text_tool_calls()` 正确解析 `` XML 格式(已实现) 4. 添加工具描述格式:每个工具包含 name、description、parameters **Test scenarios**: - system prompt 包含所有工具描述 - `` 格式被正确解析 - LLM 不使用工具时直接返回 final_answer - LLM 使用工具时正确执行并返回 observation - 多步工具调用(think → act → observe → think → answer) **Verification**: curl 测试"查下ip"正确执行 shell 命令 ### U3. Portal REST/WebSocket 统一路由路径 **Goal**: Portal REST chat 和 WebSocket 使用相同的 SimpleRouter 路由逻辑 **Requirements**: R5 **Dependencies**: U1 **Files**: - `src/agentkit/server/routes/portal.py` (修改) - `src/agentkit/server/app.py` (修改 — 替换 cost_aware_router 为 simple_router) **Approach**: 1. `_resolve_for_chat()` 改用 SimpleRouter 2. WebSocket `portal_websocket()` 改用 SimpleRouter 3. 两条路径统一走 SimpleRouter.route() → REACT Agent Loop 4. 保留 DIRECT_CHAT 快速路径 **Test scenarios**: - REST "查看当前ip" 正确执行 shell - WebSocket "查看当前ip" 正确执行 shell - REST "你好" 走 DIRECT_CHAT - WebSocket "你好" 走 DIRECT_CHAT **Verification**: curl 和前端测试均通过 ### U4. 聊天记录持久化 **Goal**: Portal ConversationStore 接入后端 SessionManager,支持 file 持久化 **Requirements**: R6 **Dependencies**: U3 **Files**: - `src/agentkit/server/routes/portal.py` (修改) - `src/agentkit/session/manager.py` (修改 — 如需新增方法) - `tests/unit/server/test_portal_persistence.py` (新建) **Approach**: 1. ConversationStore 委托 SessionManager 进行持久化 2. 新消息写入时同步写入 SessionManager 3. 加载会话时从 SessionManager 恢复 4. 保持内存缓存作为热路径 **Test scenarios**: - 新消息写入后可从 SessionManager 读取 - 服务重启后会话历史保留 - 多轮对话上下文正确 **Verification**: 重启服务后聊天记录仍在 ### U5. 更新 E2E 回测用例和指标 **Goal**: 更新回测用例覆盖口语化说法,定义和跟踪指标 **Requirements**: R7, R8 **Dependencies**: U1, U2, U3 **Files**: - `tests/e2e/test_capability_router_direct.py` (修改) - `tests/e2e/capability_metrics.py` (修改) - `docs/plans/2026-06-16-005-refactor-routing-architecture-plan.md` (本文档) **Approach**: 1. 更新回测用例:增加口语化说法("查下ip"、"获取ip"、"看下ip"等) 2. 更新指标:增加响应时间、LLM 调用次数、token 消耗 3. 定义目标值:执行模式准确率 >85%,工具调用成功率 >95%,口语化成功率 >90% 4. 运行回测并记录结果 **Test scenarios**: - 口语化查询("查下ip")正确路由到 REACT - 工具调用查询正确执行工具 - 问候语正确路由到 DIRECT_CHAT - 响应时间 <3s - LLM 调用次数 ≤2 **Verification**: 回测报告显示所有指标达标 --- ## Success Metrics | 指标 | 当前值 | 目标值 | 测量方式 | |------|-------|-------|---------| | 执行模式准确率 | 40% | >85% | E2E 回测 | | 工具调用成功率 | 60% | >95% | E2E 回测 | | 口语化查询成功率 | 30% | >90% | E2E 回测 | | 响应时间(简单查询)| 3-5s | <3s | curl -w "%{time_total}" | | 响应时间(工具调用)| 5-8s | <4s | curl -w "%{time_total}" | | LLM 调用次数/查询 | 3 | ≤2 | 日志统计 | | Token 消耗/查询 | ~2400 | <1800 | LLM gateway 统计 | --- ## Risks & Mitigations | 风险 | 影响 | 缓解措施 | |------|------|---------| | 百炼 Coding 不理解 `` 格式 | 工具调用失败 | 已验证模型输出 ``;回退到 Action: 格式 | | 全量工具描述 token 过多 | 响应变慢 | 21 个工具约 2000 tokens,可接受;第二阶段按需加载 | | 删除路由层后 skill 匹配丢失 | 特定 skill 不被选中 | @skill 前缀显式指定;LLM 在 agent loop 中自然匹配 | | 聊天记录迁移不兼容 | 旧数据丢失 | 新旧格式兼容;渐进迁移 | --- ## Open Questions 1. Embedding API key 何时提供?(SemanticRouter 降级为可选插件依赖此 key) 2. 是否需要保留 CostAwareRouter 作为可选模式?(向后兼容)