15 KiB
| title | status | created | type | origin |
|---|---|---|---|---|
| fix: 回测问题修复 + 路由优化 + 质量门控强化 | completed | 2026-06-20 | fix | test/full-regression-real-llm-e2e 回测结果 |
fix: 回测问题修复 + 路由优化 + 质量门控强化
Summary
修复全面回测中发现的 5 个代码问题,优化当前 RequestPreprocessor 路由准确率,强化 QualityGate 质量门控,并重新基准测试建立当前架构基线。
Problem Frame
回测发现以下问题(基于 test/full-regression-real-llm-e2e 分支):
- Benchmark 超时过短 —
llm-001(easy 难度)超时阈值 20s,真实 LLM(qwen3.7-plus)无法在 20s 内完成工具调用推理,导致 2/5 用例超时 - LLM Provider httpx 超时硬编码 —
OpenAICompatibleProvider的 httpx 客户端硬编码timeout=60.0,忽略ProviderConfig.timeout(120s) - QualityGate skill_match 休眠 —
_check_skill_match()方法存在但无调用方传入skill_context,质量门控形同虚设 - QualityGate 自定义验证器过于宽松 — 验证器导入/执行失败时静默跳过(
passed=True),不拦截低质量输出 - 16 个技能配置均无 disambiguation_keywords — 易混淆技能对(reflexion_agent↔code_reviewer 等)无法消歧
- 路由优化 — 当前 RequestPreprocessor 仅 3 条正则(问候/闲聊/身份),大量简单 factual 问题被送入 REACT 循环,浪费 token
Requirements
- R1: Benchmark easy 难度超时从 20s 提升至 45s,medium 从 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 环境)
- 计划 001(U7/U8/U9/U10 未完成项)
- 计划 002(8 个待决策问题)
- 计划 003(7 项 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: U4(disambiguation_keywords 提供 intent_keywords 消歧)
Files:
src/agentkit/core/config_driven.py— 在_execute_skill_task返回前调用 QualityGate.validate 传入 skill_contextsrc/agentkit/quality/gate.py— 确认_check_skill_match读取 disambiguation_keywordstests/unit/quality/test_gate.py— 新增 skill_match 激活测试
Approach:
- 在
ConfigDrivenAgent._execute_skill_task()中,构造skill_context = {"intent_keywords": skill_config.intent.keywords + skill_config.intent.disambiguation_keywords} - 调用
self._quality_gate.validate(output, skill_context=skill_context) - 在
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_keywordsconfigs/skills/code_reviewer.yaml— 添加 disambiguation_keywordsconfigs/skills/react_agent.yaml— 添加 disambiguation_keywordsconfigs/skills/goal_driven_agent.yaml— 添加 disambiguation_keywordsconfigs/skills/rewoo_agent.yaml— 添加 disambiguation_keywordsconfigs/skills/competitor_analyzer.yaml— 添加 disambiguation_keywordsconfigs/skills/content_generator.yaml— 添加 disambiguation_keywordsconfigs/skills/geo_optimizer.yaml— 添加 disambiguation_keywordssrc/agentkit/skills/base.py— SkillConfig.intent 添加 disambiguation_keywords 字段
Approach:
- 在
SkillIntentmodel 中添加disambiguation_keywords: list[str] = []字段 - 为每对易混淆技能添加互斥关键词:
- reflexion_agent:
["反思", "自我验证", "迭代优化"] - code_reviewer:
["代码审查", "代码问题", "bug 检查"] - react_agent:
["实时搜索", "工具调用", "信息查询"] - goal_driven_agent:
["目标分解", "多步规划", "方案对比"] - rewoo_agent:
["并行采集", "批量获取", "多源数据"] - competitor_analyzer:
["竞品分析", "竞争对比", "市场对手"] - content_generator:
["内容创作", "文章生成", "选题写作"] - geo_optimizer:
["SEO 优化", "GEO 优化", "搜索排名"]
- reflexion_agent:
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:
_FACTUAL_RE— "什么是X/X是什么/解释一下X/define X" 等纯知识问答_MATH_RE— "计算X/算一下X/calculate X" 等简单数学(无变量、无方程)_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:
- 运行
agentkit benchmark --mode llm(full 模式,真实 LLM) - 运行
agentkit benchmark --mode llm --fast(fast 模式) - 对比修复前后准确率、超时率、延迟
- 更新
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)
U1–U5 代码修复验证
| 单元 | 验证方式 | 结果 |
|---|---|---|
| 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。