fischer-agentkit/docs/plans/2026-06-17-001-fix-benchmar...

224 lines
9.5 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: Benchmark 测试失败根因修复"
status: active
created: 2026-06-17
type: fix
origin: test-results/benchmark/benchmark_report.md
---
# fix: Benchmark 测试失败根因修复
## Summary
修复 benchmark 测试中 3 个失败项的根因LLM 推理超时2/5、WebSocket 连接失败1/5、verification P95 延迟失真。所有修复从根因层面解决,非简单调参。
## Problem Frame
最新 `--mode all` 回测结果63 个测试 60 通过 3 失败95.2%)。
| 失败项 | 维度 | 根因 |
|--------|------|------|
| llm-003 | llm_reasoning | 30s 硬超时对 hard 任务不足,且未用流式提前退出 |
| llm-005 | llm_reasoning | 同上 |
| gui-004 | gui_integration | WebSocket 端点路径错误 + 协议交互顺序错误 |
另有一个统计方法论缺陷verification 维度 P95=411ms 由 timeout 测试用例的 500ms 固有耗时扭曲,产生性能误报。
## Requirements
- R1: LLM 维度 hard 任务不再因超时失败(根因:流式 + 难度分级超时)
- R2: GUI 维度 WebSocket 测试通过(根因:修正端点路径 + 协议顺序)
- R3: verification 维度 P95 不再被 timeout 用例扭曲(根因:延迟统计排除 timeout 类用例)
- R4: LLM Gateway 支持超时透传,避免 asyncio.wait_for 取消后 HTTP 连接泄漏
- R5: 所有修复后 `--mode all` 回测准确率 >= 95%,无回归
## Key Technical Decisions
### KTD1: LLM 超时按难度分级 + 流式关键词提前退出
**决策**: 对 hard 难度 LLM 任务使用 `chat_stream()` 流式响应,检测到期望关键词后立即终止;对 easy/medium 保持非流式但按难度分级超时。
**理由**: 根因是 30s 硬超时 + 非流式等待完整响应。流式 + 关键词检测可将 hard 任务有效延迟从 30s+ 降至 5-15s关键词通常在前 200 tokens 出现)。难度分级超时避免 easy 任务等待过久。
**超时映射**: easy=20s, medium=40s, hard=60s流式模式下 hard 实际会在 5-15s 内完成)
### KTD2: WebSocket 测试修正端点路径和协议顺序
**决策**: 修正 benchmark 代码中的 WebSocket 测试,使用正确端点 `/api/v1/ws/tasks/{task_id}`,并遵循服务器协议(先接收 `connected` 消息,再发送 `ping`)。
**理由**: 根因是 benchmark 代码 bug路径 `/ws/bench-session` 不存在 + 未先接收 `connected`)。这是测试代码问题,非服务器缺陷。
### KTD3: 延迟统计排除 timeout 类用例
**决策**: 在 `_compute_metrics` 中新增 `exclude_latency_tags` 参数verification 维度排除 timeout 类用例的延迟统计,但保留其准确性统计。
**理由**: timeout 测试用例的 ~500ms 延迟是测试设计的固有耗时(必须等待超时触发),不是被测系统性能问题。将其纳入 P95 会导致永久误报。
### KTD4: LLM Gateway 超时透传
**决策**: 在 `LLMRequest` 中新增 `timeout` 字段,`gateway.chat()` 透传给 ProviderProvider 层面尊重超时。
**理由**: 当前 `asyncio.wait_for` 取消协程时,底层 HTTP 请求可能未被干净关闭。超时透传让 Provider 在 HTTP 层面超时,确保资源清理。
## Implementation Units
### U1. LLM 超时分级 + 流式关键词检测
**Goal**: 修复 llm-003/llm-005 超时失败
**Dependencies**: 无
**Files**:
- `src/agentkit/cli/benchmark.py``_execute_llm_reasoning_task` 函数(约第 622-694 行)
**Approach**:
1. 新增难度分级超时映射: `{"easy": 20.0, "medium": 40.0, "hard": 60.0}`
2. 对 hard 任务使用 `llm_gateway.chat_stream()` 流式响应
3. 流式过程中检测 `task.expected_keywords`,命中即 `break`
4. 非 hard 任务保持非流式,使用分级超时
5. 流式失败时回退到非流式fallback
**Test scenarios**:
- easy 任务在 20s 内完成,非流式
- medium 任务在 40s 内完成,非流式
- hard 任务使用流式,关键词在 15s 内检测到
- hard 任务流式失败时回退到非流式
- 所有难度任务不再因超时失败
**Verification**: `python3 -c "from agentkit.cli.benchmark import benchmark; benchmark(dimension='llm_reasoning', mode='llm', report=True, runs=1)"` 通过率 >= 80%
---
### U2. WebSocket 测试路径和协议修正(根因更新)
**Goal**: 修复 gui-004 WebSocket 连接失败
**Dependencies**: 无
**Files**:
- `src/agentkit/cli/benchmark.py``_run_gui_integration` 函数中 gui-004 测试块(约第 1038-1101 行)
**根因分析(调试验证)**:
1. HTTP GET 预检查断言 `status_code in (400, 426)`,但 FastAPI WebSocket 路由对 HTTP GET 返回 **404**(非 400/426
2. HTTP 预检查失败导致 `ws_pass=False`,实际 WebSocket 连接测试从未执行
3. 实际 WebSocket 连接是成功的:能连接、能收到 `connected` 消息
4. `pong` 未收到是因为服务器并发启动 ReAct 执行,执行失败后发送 `error` 并关闭连接listener task 被取消
**Approach**:
1. **移除 HTTP 预检查** — FastAPI WebSocket 路由不响应 HTTP GET预检查不可靠
2. **直接 WebSocket 连接测试**`websockets.connect()``ws://localhost:{port}/api/v1/ws/tasks/bench-session`
3. **`connected` 消息作为通过标准** — 收到 `{"type": "connected"}` 证明 WebSocket 协议正常工作
4. **ping/pong 作为附加信息** — 尝试 ping/pong 但不作为通过条件(服务器并发执行设计导致 pong 可能不可达)
5. **连接失败才判负** — WebSocket 连接本身失败或未收到 `connected` 才算失败
**Test scenarios**:
- WebSocket 连接到正确端点成功,收到 `connected` → PASS
- WebSocket 连接失败(端口错误)→ FAIL
- 未收到 `connected` 消息 → FAIL
- 收到 `connected` 后服务器发送 `error`/关闭连接 → 仍 PASSWebSocket 协议正常)
**Verification**: `python3 -c "from agentkit.cli.benchmark import benchmark; benchmark(dimension='gui_integration', mode='gui', report=True, runs=1)"` gui-004 通过
---
### U3. 延迟统计排除 timeout 类用例
**Goal**: 修复 verification P95 延迟失真
**Dependencies**: 无
**Files**:
- `src/agentkit/cli/benchmark.py``_compute_metrics` 函数(约第 1070-1136 行)和 `_run_dimension` 调用处
**Approach**:
1. `_compute_metrics` 新增 `exclude_latency_tags: list[str] | None = None` 参数
2. 计算延迟分位数时,排除 `detail``category` 包含排除标签的用例
3. 准确性统计不受影响timeout 用例仍计入 pass/fail
4. `_run_dimension` 对 verification 维度传入 `exclude_latency_tags=["timeout"]`
5. vf-004 的 `detail` 字段确保包含 "timeout" 字样
**Test scenarios**:
- verification 维度 P95 < 100ms排除 timeout 用例后
- timeout 用例仍计入 accuracypass/fail 不受影响
- 其他维度不受影响不传 exclude_latency_tags
- 空排除列表时行为不变向后兼容
**Verification**: `python3 -c "from agentkit.cli.benchmark import benchmark; benchmark(dimension='verification', mode='mock', report=True, runs=1)"` P95 < 100ms
---
### U4. LLM Gateway 超时透传
**Goal**: 避免 asyncio.wait_for 取消后 HTTP 连接泄漏
**Dependencies**: U1
**Files**:
- `src/agentkit/llm/protocol.py` `LLMRequest` 模型
- `src/agentkit/llm/gateway.py` `chat()` 方法
**Approach**:
1. `LLMRequest` 新增 `timeout: float | None = None` 字段
2. `gateway.chat()` 接受 `timeout` 参数透传到 `LLMRequest`
3. Provider `chat()` 方法检查 `req.timeout` HTTP 请求层面设置超时
4. benchmark `_execute_llm_reasoning_task` 使用 `gateway.chat(timeout=timeout_s)` 替代 `asyncio.wait_for`
**Test scenarios**:
- LLMRequest 包含 timeout 字段
- gateway.chat() 透传 timeout LLMRequest
- Provider timeout 秒后超时抛出 LLMProviderError
- 不传 timeout 时行为不变向后兼容
**Verification**: `ruff check src/agentkit/llm/protocol.py src/agentkit/llm/gateway.py` 通过
---
### U5. 全量回测验证
**Goal**: 验证所有修复后无回归
**Dependencies**: U1, U2, U3, U4
**Files**:
- 验证步骤
**Approach**:
1. 运行 `ruff check src/` 确认无 lint 错误
2. 运行 `pytest tests/e2e/test_capability_comprehensive.py -x -q -m e2e_capability` 确认 64 个测试通过
3. 运行 `agentkit benchmark --mode all --report --verbose --runs 1` 确认 63 个测试通过率 >= 95%
4. 检查报告LLM 维度 >= 80%GUI 维度 >= 80%verification P95 < 100ms
5. 对比基线确认无回归
**Verification**: 全量回测通过无回归
## Scope Boundaries
### In Scope
- 修复 benchmark.py 3 个失败项的根因
- LLM Gateway 超时透传
- 延迟统计方法论修正
### Out of Scope
- WebSocket 服务器端的设计缺陷task_id 当作消息内容)— 另行跟进
- LLM 模型本身的响应速度优化 依赖模型提供商
- 新增测试用例 本次只修复现有失败
### Deferred to Follow-Up
- WebSocket 端点支持纯心跳模式不触发 ReAct 执行
- LLM 维度增加更多用例515
- GUI 维度增加前端交互测试
## Risks
| 风险 | 影响 | 缓解 |
|------|------|------|
| 流式响应兼容性 | chat_stream 可能在某些 Provider 上行为不一致 | fallback 到非流式 |
| LLM 响应仍有波动 | hard 任务可能仍偶发超时 | 60s 超时 + 流式提前退出双保险 |
| WebSocket 服务器行为变化 | 服务器协议变更导致测试再次失败 | 测试代码遵循服务器文档协议 |
## Phased Delivery
- **Phase 1**U1+U2+U3: 修复 3 个失败项可独立验证
- **Phase 2**U4: LLM Gateway 超时透传架构层面改进
- **Phase 3**U5: 全量回测验证