fischer-agentkit/docs/plans/2026-06-20-001-fix-regressi...

321 lines
15 KiB
Markdown
Raw 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.

---
title: "fix: 回测问题修复 + 路由优化 + 质量门控强化"
status: completed
created: 2026-06-20
type: fix
origin: test/full-regression-real-llm-e2e 回测结果
---
# fix: 回测问题修复 + 路由优化 + 质量门控强化
## Summary
修复全面回测中发现的 5 个代码问题,优化当前 RequestPreprocessor 路由准确率,强化 QualityGate 质量门控,并重新基准测试建立当前架构基线。
## Problem Frame
回测发现以下问题(基于 `test/full-regression-real-llm-e2e` 分支):
1. **Benchmark 超时过短**`llm-001`easy 难度)超时阈值 20s真实 LLMqwen3.7-plus无法在 20s 内完成工具调用推理,导致 2/5 用例超时
2. **LLM Provider httpx 超时硬编码**`OpenAICompatibleProvider` 的 httpx 客户端硬编码 `timeout=60.0`,忽略 `ProviderConfig.timeout`120s
3. **QualityGate skill_match 休眠**`_check_skill_match()` 方法存在但无调用方传入 `skill_context`,质量门控形同虚设
4. **QualityGate 自定义验证器过于宽松** — 验证器导入/执行失败时静默跳过(`passed=True`),不拦截低质量输出
5. **16 个技能配置均无 disambiguation_keywords** — 易混淆技能对reflexion_agent↔code_reviewer 等)无法消歧
6. **路由优化** — 当前 RequestPreprocessor 仅 3 条正则(问候/闲聊/身份),大量简单 factual 问题被送入 REACT 循环,浪费 token
## Requirements
- R1: Benchmark easy 难度超时从 20s 提升至 45smedium 从 40s 提升至 60s
- R2: OpenAICompatibleProvider httpx 客户端使用 ProviderConfig.timeout 而非硬编码 60s
- R3: QualityGate skill_match 在执行管线中被实际调用(传入 skill_context
- R4: QualityGate 自定义验证器失败时支持严格模式(可配置拦截 vs 警告)
- R5: 为 4 对易混淆技能添加 disambiguation_keywords 字段
- R6: RequestPreprocessor 新增 factual/数学/翻译类正则,减少不必要的 REACT 调用
- R7: 修复后重新运行 benchmark 建立当前架构基线
## Key Technical Decisions
### KTD1: Benchmark 超时按难度分级保留,但提升阈值
**决策**: 保留 `_LLM_TIMEOUT_BY_DIFFICULTY` 字典结构,提升 easy→45s、medium→60s、hard→90s。
**理由**: 分级超时是合理设计(简单任务不应等太久),但 20s 对真实 LLM 工具调用太短。qwen3.7-plus 的 p50 延迟 35s、p95 42s来自 benchmark 报告20s 必然超时。
### KTD2: httpx 超时从 ProviderConfig 透传,保留硬编码作为 fallback
**决策**: `OpenAICompatibleProvider.__init__` 读取 `config.timeout`,若未设置则 fallback 到 60s。
**理由**: ProviderConfig.timeout 默认 120s 是有意的LLM 推理慢httpx 硬编码 60s 会先于 ProviderConfig 触发,导致配置无效。
### KTD3: QualityGate skill_match 在 ConfigDrivenAgent 执行后调用
**决策**: 在 `ConfigDrivenAgent._execute_skill_task()` 返回前调用 `QualityGate.validate(output, skill_context=skill_config)`
**理由**: skill_match 需要技能上下文intent_keywords才能校验输出一致性。ConfigDrivenAgent 是技能执行的统一入口,在此处调用覆盖面最广。
### KTD4: disambiguation_keywords 作为 QualityGate 消歧输入,不用于路由
**决策**: disambiguation_keywords 添加到 skill yaml 的 `intent` 节点下,由 QualityGate 读取用于输出校验,不影响 RequestPreprocessor 路由决策。
**理由**: 当前路由已简化为"显式前缀 + 正则 + 默认 REACT"不依赖关键词。disambiguation_keywords 的价值在于 QualityGate 校验输出是否与技能意图一致。
### KTD5: 路由优化采用"扩展正则 + 不引入 LLM 分类"策略
**决策**: 新增 factual是什么/什么是/解释)、数学(计算/算一下)、翻译(翻译/translate三类正则走 DIRECT_CHAT不引入 LLM quick_classify。
**理由**: 保持 RequestPreprocessor 的"零 token 成本快速路径"设计哲学。LLM 二次分类已被明确移除docstring: "LLM blind-classification without tool context is unreliable"),不回退。
## Scope Boundaries
### In Scope
- Benchmark 超时阈值调整
- OpenAICompatibleProvider httpx 超时修复
- QualityGate skill_match 激活 + 严格模式
- 4 对易混淆技能 disambiguation_keywords
- RequestPreprocessor 正则扩展
- 重新基准测试
### Deferred to Follow-Up Work
- DockerComputerUseSession 4 个 stub需真实 Docker 环境)
- 计划 001U7/U8/U9/U10 未完成项)
- 计划 0028 个待决策问题)
- 计划 0037 项 Deferred
- LLM 二次分类消歧P2需评估延迟代价
- 复杂度校准数据集构建P2需收集标注数据
---
## Implementation Units
### U1. 修复 Benchmark 超时阈值
**Goal:** 提升 easy/medium/hard 难度的 LLM 超时阈值,避免真实 LLM 因超时失败
**Requirements:** R1
**Dependencies:**
**Files:**
- `src/agentkit/cli/benchmark.py` — 修改 `_LLM_TIMEOUT_BY_DIFFICULTY` 字典
**Approach:**
`_LLM_TIMEOUT_BY_DIFFICULTY``{"easy": 20.0, "medium": 40.0, "hard": 60.0}` 改为 `{"easy": 45.0, "medium": 60.0, "hard": 90.0}`。默认 fallback 从 30.0 改为 60.0。
**Patterns to follow:** 现有 `_LLM_TIMEOUT_BY_DIFFICULTY` 字典结构
**Test scenarios:**
- Happy path: easy 难度用例在 45s 内完成 → passed=True
- Edge case: easy 难度用例在 20-45s 之间完成 → 旧逻辑会超时,新逻辑 passed=True
- Error path: easy 难度用例超过 45s → 超时失败detail 包含 "45s"
**Verification:** 运行 `agentkit benchmark --mode llm`llm-001 不再因超时失败
---
### U2. 修复 OpenAICompatibleProvider httpx 超时硬编码
**Goal:** httpx 客户端使用 ProviderConfig.timeout 而非硬编码 60s
**Requirements:** R2
**Dependencies:**
**Files:**
- `src/agentkit/llm/providers/openai.py` — 修改 httpx.AsyncClient 构造
- `tests/unit/llm/test_openai_provider.py` — 新增超时透传测试
**Approach:**
`OpenAICompatibleProvider.__init__` 中,将 `httpx.AsyncClient(timeout=60.0)` 改为 `httpx.AsyncClient(timeout=self._config.timeout)`。若 `self._config` 不存在或 `timeout` 未设置fallback 到 60.0。
**Patterns to follow:** `RemoteLLMProvider` 已使用 `timeout=120.0` 参数模式
**Test scenarios:**
- Happy path: ProviderConfig(timeout=120) → httpx client timeout=120
- Edge case: ProviderConfig(timeout=0) → fallback 到 60.0
- Edge case: ProviderConfig 未设置 timeout → 使用默认 120.0
- Integration: 实际 LLM 调用在 60-120s 之间完成 → 旧逻辑会超时,新逻辑成功
**Verification:** 单元测试通过 + benchmark 中无 httpx 超时错误
---
### U3. 激活 QualityGate skill_match 校验
**Goal:** 在技能执行管线中传入 skill_context激活 skill_match 输出一致性校验
**Requirements:** R3
**Dependencies:** U4disambiguation_keywords 提供 intent_keywords 消歧)
**Files:**
- `src/agentkit/core/config_driven.py` — 在 `_execute_skill_task` 返回前调用 QualityGate.validate 传入 skill_context
- `src/agentkit/quality/gate.py` — 确认 `_check_skill_match` 读取 disambiguation_keywords
- `tests/unit/quality/test_gate.py` — 新增 skill_match 激活测试
**Approach:**
1.`ConfigDrivenAgent._execute_skill_task()` 中,构造 `skill_context = {"intent_keywords": skill_config.intent.keywords + skill_config.intent.disambiguation_keywords}`
2. 调用 `self._quality_gate.validate(output, skill_context=skill_context)`
3.`gate.py``_check_skill_match` 中,同时检查 `intent_keywords``disambiguation_keywords`
**Patterns to follow:** `gate.py` 现有 `_check_skill_match` 方法签名
**Test scenarios:**
- Happy path: 技能输出包含 intent_keywords → skill_match passed=True
- Error path: 技能输出不包含任何 intent_keywords → skill_match 警告
- Integration: reflexion_agent 输出包含 "review" → 与 code_reviewer 的 disambiguation_keywords 匹配 → 触发消歧警告
- Edge case: skill_context=None → 跳过 skill_match向后兼容
**Verification:** 单元测试通过 + 技能执行日志中出现 skill_match 校验记录
---
### U4. 添加 disambiguation_keywords 到易混淆技能对
**Goal:** 为 4 对易混淆技能添加 disambiguation_keywords支持 QualityGate 消歧
**Requirements:** R5
**Dependencies:**
**Files:**
- `configs/skills/reflexion_agent.yaml` — 添加 disambiguation_keywords
- `configs/skills/code_reviewer.yaml` — 添加 disambiguation_keywords
- `configs/skills/react_agent.yaml` — 添加 disambiguation_keywords
- `configs/skills/goal_driven_agent.yaml` — 添加 disambiguation_keywords
- `configs/skills/rewoo_agent.yaml` — 添加 disambiguation_keywords
- `configs/skills/competitor_analyzer.yaml` — 添加 disambiguation_keywords
- `configs/skills/content_generator.yaml` — 添加 disambiguation_keywords
- `configs/skills/geo_optimizer.yaml` — 添加 disambiguation_keywords
- `src/agentkit/skills/base.py` — SkillConfig.intent 添加 disambiguation_keywords 字段
**Approach:**
1.`SkillIntent` model 中添加 `disambiguation_keywords: list[str] = []` 字段
2. 为每对易混淆技能添加互斥关键词:
- reflexion_agent: `["反思", "自我验证", "迭代优化"]`
- code_reviewer: `["代码审查", "代码问题", "bug 检查"]`
- react_agent: `["实时搜索", "工具调用", "信息查询"]`
- goal_driven_agent: `["目标分解", "多步规划", "方案对比"]`
- rewoo_agent: `["并行采集", "批量获取", "多源数据"]`
- competitor_analyzer: `["竞品分析", "竞争对比", "市场对手"]`
- content_generator: `["内容创作", "文章生成", "选题写作"]`
- geo_optimizer: `["SEO 优化", "GEO 优化", "搜索排名"]`
**Patterns to follow:** 现有 `intent.keywords` 字段结构
**Test scenarios:**
- Happy path: SkillConfig 加载 yaml 含 disambiguation_keywords → 字段非空
- Edge case: yaml 未含 disambiguation_keywords → 字段默认空列表
- Integration: QualityGate 读取 disambiguation_keywords 用于消歧校验
**Verification:** `agentkit skill list` 正常加载所有技能 + 单元测试通过
---
### U5. 优化 RequestPreprocessor 路由正则
**Goal:** 新增 factual/数学/翻译类正则,减少不必要的 REACT 调用
**Requirements:** R6
**Dependencies:**
**Files:**
- `src/agentkit/chat/request_preprocessor.py` — 新增 3 条正则
- `tests/unit/chat/test_request_preprocessor.py` — 新增路由测试
**Approach:**
新增 3 条正则走 DIRECT_CHAT
1. `_FACTUAL_RE` — "什么是X/X是什么/解释一下X/define X" 等纯知识问答
2. `_MATH_RE` — "计算X/算一下X/calculate X" 等简单数学(无变量、无方程)
3. `_TRANSLATION_RE` — "翻译X/translate X" 等纯翻译请求
**注意**: 这些正则必须严格匹配,避免误拦截需要工具的请求。例如 "分析一下服务器的IP" 不应匹配 `_FACTUAL_RE`(包含"分析"动词暗示需要工具)。
**Patterns to follow:** 现有 `_GREETING_RE` / `_CHAT_MODE_RE` / `_IDENTITY_RE` 正则模式
**Test scenarios:**
- Happy path: "什么是机器学习" → 匹配 _FACTUAL_RE → DIRECT_CHAT
- Happy path: "计算 1+2+3" → 匹配 _MATH_RE → DIRECT_CHAT
- Happy path: "translate hello to Chinese" → 匹配 _TRANSLATION_RE → DIRECT_CHAT
- Edge case: "什么是当前服务器的IP地址" → 不匹配 _FACTUAL_RE含"当前服务器"暗示需要工具)→ REACT
- Edge case: "计算斐波那契数列的第100项" → 不匹配 _MATH_RE含"斐波那契数列"暗示需要代码)→ REACT
- Error path: 空字符串 → 不匹配任何正则 → REACT
**Verification:** 单元测试通过 + benchmark 中 DIRECT_CHAT 比例提升
---
### U6. 重新基准测试 + 建立当前架构基线
**Goal:** 修复后重新运行 benchmark建立当前 RequestPreprocessor 架构的基线
**Requirements:** R7
**Dependencies:** U1, U2, U3, U4, U5所有修复完成后
**Files:**
- `test-results/benchmark/baseline.json` — 更新基线
- `test-results/benchmark/benchmark_report.md` — 更新报告
**Approach:**
1. 运行 `agentkit benchmark --mode llm`full 模式,真实 LLM
2. 运行 `agentkit benchmark --mode llm --fast`fast 模式)
3. 对比修复前后准确率、超时率、延迟
4. 更新 `baseline.json` 作为当前架构基线
**Test scenarios:**
- Happy path: full 模式准确率 ≥ 80%5 用例至少 4 通过)
- Happy path: fast 模式准确率 = 100%
- Edge case: llm-001 不再超时
- Edge case: llm-004 不再超时
**Verification:** benchmark 报告生成 + 准确率达标
---
## Risks & Dependencies
| 风险 | 严重度 | 缓解措施 |
|------|--------|----------|
| 新增正则误拦截需要工具的请求 | 中 | 正则设计保守,仅匹配纯知识/数学/翻译,单元测试覆盖边界 |
| QualityGate skill_match 误报导致输出被拦截 | 中 | skill_match 单独不拦截(现有设计),仅与其他失败共病时拦截 |
| disambiguation_keywords 与现有 keywords 语义重叠 | 低 | disambiguation_keywords 是 keywords 的补充,不替代 |
| benchmark 超时提升后延迟增加 | 低 | 超时是上限而非目标,快速完成的用例不受影响 |
## Open Questions
无 — 所有技术决策已在 KTD 中明确。
## System-Wide Impact
- **LLM 网关**: httpx 超时修复影响所有 LLM 调用(更宽松的超时)
- **技能执行**: QualityGate 激活影响所有技能输出校验
- **Benchmark**: 超时阈值影响所有 benchmark 用例
- **路由**: 新增正则影响所有非显式前缀的请求
## Verification Results (2026-06-20)
### U1U5 代码修复验证
| 单元 | 验证方式 | 结果 |
|------|----------|------|
| U1: Benchmark 超时 | `agentkit benchmark --mode llm` | ✅ llm-001/llm-004 不再超时 |
| U2: httpx 超时 | `pytest tests/unit/test_llm_provider.py` | ✅ 2 个新测试通过 |
| U3: QualityGate 激活 | `pytest tests/unit/quality/` | ✅ 176 个质量门控测试通过 |
| U4: disambiguation_keywords | 16 个技能 yaml 加载验证 | ✅ 全部加载成功 |
| U5: 路由正则 | `pytest tests/unit/chat/test_request_preprocessor.py` | ✅ 38 个测试通过19 新增) |
### U6 基准测试结果
| 指标 | 修复前 (2026-06-20 03:18) | 修复后 (2026-06-20 11:05) | 变化 |
|------|--------------------------|--------------------------|------|
| 准确率 | 60.0% | 93.3% ± 9.4% | **+33.3%** |
| 通过/总数 | 3/5 | 4/5 | +1 |
| 超时数 | 2 | 0 (llm-002 偶发) | **-2** |
| 一致性 | N/A | 100% | — |
| p50 延迟 | 35.3s | 40.8s | +5.5s(可接受) |
**剩余问题**: llm-002 (tool_selection, medium) 在 3 次运行中 1 次超时p95=56.3s 接近 medium 60s 阈值。后续可考虑提升 medium 超时至 75s。