Status: ✅ engineering-verified(Phase A+B 完成 2026-06-01,billing-adapted) CI:
.github/workflows/eng-m4.yml编写完毕;结构性 smoke 本地验证通过;GitHub 自动跑 deferred:billing(finding-011 继承限制) v1 baseline: weighted_score 0.712,71 case,judge schema_failures 0,case_errors 2(首个有意义的 baseline;此前所有 baseline 均为 async-gen 缺陷产生的伪像,已作废) Owner: 本 session 协作产出 Source-of-truth M4 docs:
- Spec:
docs/superpowers/specs/M4 相关条目- 硬标准:
07-Phase7-实战篇-从零到生产.md+08-可观测与可评估Agent深度设计.md依赖前提:M0/M1/M2a/M2b/M2b-Obs/M2c/M3 engineering-verified。M4 在其基础上闭合 eval 循环。
M4 分三个 Phase:
- Phase A:修复 M3 deferred findings(016/017/018/019/010/020)
- Phase B:校准 v1 baseline(修复 eval 管线自身的 finding-021 ~ finding-026,跑出可信的第一条 baseline)
- Phase C(deferred):3 轮 prompt 优化迭代(advisor prompt → baseline 提升 → 写 insight doc)
Phase A+B 已 engineering-verified(本文档所覆盖范围)。Phase C 推迟——在 v1 baseline 稳定后的下一 session 执行。
Phase A+B 期间共修复 12 个 finding(含此前 deferred 的)。finding-024 是核心:它揭示了 agent 自 M2b 起从未真正执行工具调用,所有此前的 eval baseline 均为伪像。
| Finding | Title(一行) | Fix commit | Status |
|---|---|---|---|
| 010 | SSE last token duplicated | 7a38421 |
fixed |
| 016 | Tantivy BM25 422/400:DSL metachars(-/://)+ >200-char query |
db691ce, cfe7e80, 253d39f |
fixed |
| 017 | TEI rerank 总是 5s-timeout(Rosetta emulation 下 bge-reranker-v2-m3 per-call 慢) | d54ed26 |
fixed |
| 018 | Self-Eval hits iter cap(016/017 下游症状,随 016/017 修复自愈) | 7210f20 |
fixed |
| 019 | auth 错误被分类为 INTERNAL_ERROR 而非 UPSTREAM_LLM_ERROR | 8ce8655 |
fixed |
| 020 | advisor 偶发输出良性 https:// URL(观察,非 M3 多模态缺陷) | — | closed-observation |
| 021 | HyDE/multi_query ValidationError ABORT 整个 gold-set 批次 | 08b70a8 |
fixed |
| 022 | run_gold_set 单 case 异常中止整个 71-case 批次 | a4dfb93 |
fixed |
| 023 | eval 从 terminal state 读 advisor 输出全为空(被 024 supersede) | b78eaa1 |
superseded-by-024 |
| 024 ⭐ | CORE:agent 工具调用循环从未真正执行(async-gen state 传播缺陷,自 M2b 起) | b3d0118, 9726c49 |
fixed |
| 025 | judge JSON 截断:中文 reasoning_brief unicode escape 膨胀超过 max_tokens=2048,46/71 case 评分丢失 | 88ea956 |
fixed |
| 026 | run_gold_set case 级无限挂起(工具循环真正运行后,hang 不被 exception 保护覆盖) | ee3b7be |
fixed |
现象:advisor 生成了含 2 个 ToolUseBlock 的响应(Langfuse 可见),但 _route_after_advisor 始终看到 _pending_tool_use=False → 路由永远到 END → tool_dispatch_node 从未执行 → 所有工具(get_product_details / compare_products / emit_cards 等)从未运行 → advisor 的输出只是开场白 preamble → 不含真实规格数据,不含商品卡片。
Root cause:advisor_node 和 tool_dispatch_node 被实现为 async generator(用 yield 流式推送 token),同时原地修改 state dict。LangGraph StateGraph 的 channel 系统只 commit 节点函数 RETURN 的 delta dict;yield 的原地 mutation 不进 channel,router 读到的永远是 init state。
Fix(user-approved §4 locked-core refactor):
- 两节点改为普通
async def,RETURN state delta dict - 流式 token/card/tool 事件改用
get_stream_writer()写入 custom stream channel run_graph→GRAPH.astream(stream_mode=["custom","updates","debug"])- 消费侧(SSE handler + run_gold_set)适配新 stream 格式
SpanHook从实时 debugtask/task_result事件取 latency_ms + input(恢复真实可观测数据)
Live 验证(首次真正运行工具循环):tool_dispatch 节点执行 → get_product_details×2 + emit_cards 执行 → CardEvent 发出 → final_answer 含真实规格(5300mAh / 90W)。
重要:M2b-Obs 起到 finding-024 修复前,所有 eval baseline 都是在"工具从未运行,advisor 只输出开场白"的条件下产生的,属于统计伪像。以下是 finding-024 修复 + eval 管线修复(021-026)后产出的第一个可信 baseline(commit
3d3e0d0)。
| 指标 | 值 |
|---|---|
weighted_score |
0.712 |
case_count |
71 |
judge schema_failures |
0(judge JSON 截断问题已彻底修复) |
case_errors |
2(error_005、error_103 — 空 query → advisor empty-content 400) |
| CI gate | exit 1(2 个 errored case safety=0.0 < SAFETY_FLOOR=0.5 → 正确行为,informative 非 run failure) |
| 维度 | 权重 | 得分 | 说明 |
|---|---|---|---|
task_completion |
30% | 0.701 | 任务完成度基线合理 |
output_quality |
30% | 0.713 | 输出质量,真实规格数据驱动 |
process_quality |
20% | 0.885 | 最强维度,工具循环 + 推理链完整 |
efficiency |
10% | 0.291 | 最弱维度——retrieval ~35s/case,latency-bound |
safety |
10% | 0.819 | 安全行为良好(除 2 个 empty-query edge case) |
| 类别 | 得分 | 说明 |
|---|---|---|
happy |
0.696 | 主流路径 |
edge |
0.715 | |
error |
0.623(最低) | 2 个 errored case 拉低 |
adversarial |
0.818 | 对抗场景表现最好 |
regression |
0.685 |
| Cohort | 得分 |
|---|---|
cross-modal(有图) |
0.705 |
no-image(纯文) |
0.714 |
跨模态与纯文本得分基本持平(±0.009),多模态路径与文本路径质量相当。
efficiency=0.291 是当前最明显的优化方向。主要瓶颈:
- TEI reranker latency(Rosetta emulation 下 bge-reranker-v2-m3 每次 ~10s)
- Self-Eval LLM 仲裁(finding-018 修复后 iter cap 消失,但仍有 1 轮 LLM 调用)
- 整体 retrieval pipeline ~35s/case
Phase C prompt 优化可部分改善(减少不必要的工具调用轮次);硬件/部署优化(TEI native arm64 / batch size 调整)是长期项。
教训:M2b / M2b-Obs / M2c / M3 的工程验证(共 4 个 session)均声称"工具接入"和"卡片出库",但实际上工具循环从未执行。这 4 次验证的检查项包括:
- 单测通过(工具定义 / dispatch cap / emit_cards 路由)
- SSE 事件流有文字输出(token + done)
- Langfuse trace 有 span 树
- CI workflow 绿
这些检查全部通过,但工具循环仍然没有运行。 原因:上述检查都是结构性或流式检查,没有一项直接验证"工具 X 的返回值出现在 advisor 的后续 LLM 上下文中"或"emit_cards 的 product_id 来自真实 Postgres 数据"。
正确的工具执行验证清单:
_pending_tool_use在 router 被读到时是True(不是 init state)tool_dispatch_node的执行日志/span 真实出现在 trace 里final_answer包含只有工具调用才能获得的真实规格数据(如"5300mAh / 90W")CardEvent中的product_id与 Postgres 中真实存在的 product 对应- gold-set 评分中
process_quality(包含工具调用链评分)显著高于零
finding-024 之后,上述 5 项均已通过。这条 meta-lesson 应成为后续里程碑(M5/M6)工程验证的标准检查项之一。
eng-m4.yml 与 M3 保持一致的"结构性 smoke"策略:
- CI 内(structural-only):
- J1:
advisor_node/tool_dispatch_node不再 yield state dict(finding-024 回归守卫) - J2:
run_gold_set有 per-case try/except(finding-022 回归守卫) - J3:
run_gold_set有 per-case timeout(finding-026 回归守卫) - J4:judge
max_tokens >= 4096(finding-025 回归守卫) - 全量 unit/structural test suite(not integration, not e2e)
- gold-set load validation(import + assert 71 cases,NO real run)
- ruff lint + format check
- J1:
- 本地 only(需要真 LLM):真实 eval 运行、baseline 校准、Langfuse/Grafana 面板
finding-011 已确立"CI 只做结构性 smoke,真 LLM 流本地跑"的策略。GitHub Actions 自动跑等账单恢复后激活;不削弱实质验证。
| Phase | 状态 |
|---|---|
| Phase A — 修复 deferred findings(010/016-020) | ✅ 完成 |
| Phase B — 修复 eval 管线(021-026)+ 跑 v1 baseline | ✅ 完成 |
| Phase C — 3 轮 prompt 优化迭代 | ⏳ deferred(下一 session) |
2 个遗留 errored case(error_005 / error_103,空 query → empty-content 400):advisor 在空 query 时返回 empty-content 会触发 Anthropic 400,导致 safety=0.0。Phase C 修复方向:orchestrator 或 intent 层加空 query 守卫,在到达 advisor 前截断。
efficiency 提升空间:TEI reranker 改用 native arm64 镜像(如可用)、batch size 从 20 降到 8(finding-017 临时修复方向的改进空间)、advisor prompt 压缩减少工具调用轮次——这些都是 Phase C 的优化方向。
- finding-016:Tantivy BM25 client 加 content-guard(T3,
db691ce)+ server 端 sanitize DSL metachars + 提升 length bound(T9a,cfe7e80,253d39f) - finding-017:TEI reranker candidates 20→8 + timeout 5s→10s(
d54ed26) - finding-018:016/017 修复后 Self-Eval iter cap 自愈 + 回归守卫(
7210f20) - finding-019:FallbackChain 用尽时保留最后一个 typed exception(
8ce8655,M2a 锁定层,user-approved) - finding-010:SSE last token dedup by chunk object identity(
7a38421) - finding-020:closed-observation(advisor URL 行为待 Phase C spot-check)
- finding-021:HyDE/multi_query ValidationError 降级到 passthrough(
08b70a8) - finding-022:run_gold_set per-case try/except + systemic-failure guard(
a4dfb93) - finding-023:eval-side stream reconstruction(
b78eaa1,被 024 supersede) - finding-024:CORE refactor — nodes return delta + get_stream_writer + astream debug mode(
b3d0118,9726c49) - finding-025:judge max_tokens 2048→4096 + terse reasoning_brief + truncate-not-reject(
88ea956) - finding-026:run_gold_set per-case timeout 300s(
ee3b7be) - v1 baseline 跑出(commit
3d3e0d0):weighted_score 0.712,71 case,0 schema_failures,2 errored case - eng-m4.yml 编写完毕(结构性 smoke,4 J-级守卫 + unit + gold-load + ruff)
- M4 wrap 文档写完(本文件 + _findings.md 追加 + README M4 行更新 + m2b/m3 更正注)