fischer-agentkit/docs/plans/2026-06-16-005-refactor-rou...

305 lines
11 KiB
Markdown
Raw Permalink 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.

# refactor: 路由架构简化 — 统一 REACT Agent Loop
status: active
created: 2026-06-16
depth: Standard
---
## Summary
将当前 4 层路由架构HeuristicClassifier → LLM classify → SemanticRouter → IntentRouter简化为极简路由层 + 统一 REACT Agent LoopHermes 模式 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 走 IntentRouterWebSocket 走 CostAwareRouter
5. **工具格式不兼容** — 百炼 Coding 不支持原生 function calling模型输出 `<tool_use>` 文本但引擎无法解析
**行业验证**Codex、Trae、Hermes、OpenClaw 均无独立路由层,统一 agent loop 是业界标准。
## Requirements
- R1: 删除 HeuristicClassifier、IntentRouter、SemanticRouter 的路由决策功能
- R2: 保留极简路由层(@skill 前缀 + 问候/闲聊检测)
- R3: 统一 REACT Agent LoopSystem 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 标签后端解析执行
**理由**
- 百炼 Codingqwen3.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 配置可选模式
**决策**ReWOOAgentReflexionAgent 等保留通过 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" 路由到 REACTLLM 决定不需要工具)
- 无前缀无问候的复杂查询路由到 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 作为可选模式向后兼容