305 lines
11 KiB
Markdown
305 lines
11 KiB
Markdown
# 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,模型输出 `<tool_use>` 文本但引擎无法解析
|
||
|
||
**行业验证**: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(`<tool_use>` 格式),后端解析执行
|
||
- 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 中定义 `<tool_use>` 格式,LLM 输出 XML 标签,后端解析执行。
|
||
|
||
**理由**:
|
||
- 百炼 Coding(qwen3.7-plus)不支持原生 function calling
|
||
- 截图验证模型已理解 `<tool_use>` 格式
|
||
- 与 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: 工具描述 + <tool_use> 格式说明
|
||
├─ LLM 决策: 需要 → 输出 <tool_use> → 解析执行 → 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 注入完整工具描述和 `<tool_use>` 格式说明
|
||
|
||
**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()` 方法,生成包含工具描述和 `<tool_use>` 格式说明的 system prompt
|
||
2. 在 `execute_stream()` 中,当 LLM 不支持原生 function calling 时,使用 prompt-based 模式
|
||
3. 确保 `_parse_text_tool_calls()` 正确解析 `<tool_use>` XML 格式(已实现)
|
||
4. 添加工具描述格式:每个工具包含 name、description、parameters
|
||
|
||
**Test scenarios**:
|
||
- system prompt 包含所有工具描述
|
||
- `<tool_use>` 格式被正确解析
|
||
- 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 不理解 `<tool_use>` 格式 | 工具调用失败 | 已验证模型输出 `<tool_use>`;回退到 Action: 格式 |
|
||
| 全量工具描述 token 过多 | 响应变慢 | 21 个工具约 2000 tokens,可接受;第二阶段按需加载 |
|
||
| 删除路由层后 skill 匹配丢失 | 特定 skill 不被选中 | @skill 前缀显式指定;LLM 在 agent loop 中自然匹配 |
|
||
| 聊天记录迁移不兼容 | 旧数据丢失 | 新旧格式兼容;渐进迁移 |
|
||
|
||
---
|
||
|
||
## Open Questions
|
||
|
||
1. Embedding API key 何时提供?(SemanticRouter 降级为可选插件依赖此 key)
|
||
2. 是否需要保留 CostAwareRouter 作为可选模式?(向后兼容)
|