Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
6.6 KiB
6.6 KiB
TASK · 记完经历后 rectification-set-focus 连续失败六次(tool_failed),原始错误无处可查(2026-09-12)
- 基线:
origin/staging@529fcc09(已部署;含 BUG-656~658) - 分支:
codex/rectification-set-focus-tool-failed-20260912,基于origin/staging - 执行方:coding agent;验收:Claude
- BUG 编号起点:BUG-659(开工时核对
docs/BUG_HISTORY.md最大号,应为 658) - 不改引擎、不改决策门、不升 Skill
1. 事故实证(2026-09-12 13:06 staging 真机,代码 529fcc09,Skill 10.0.24)
开场 → 用户说两件学业(2016-09 入学、2020-06 毕业)→ 本轮工具回执:rectification-read-case 完成、rectification-record-evidence-batch 完成(1.7 s)、rectification-set-focus 失败,界面显示「设置对话焦点未完成」六次;回执 detail = {error:"tool_failed", safe_error_code:"tool_failed"},elapsed_ms: 10。本轮 answer_origin = host_fallback,正文「记下了:2016-09 2016年9月上大学(本科入学)、2020-06 2020年6月毕业。」(日期重复)。GET 快照:current_question = collect:invite:more(active,焦点 15ef4f5c…),next_user_action = ask_method_followup。用户没有被卡住,但这一轮多等了约 20 秒、六次无效重试、正文是宿主兜底文案。
2. 已定事实(按 529fcc09 源码)
- 「还有吗」这个焦点不是 set-focus 写的,是 batch 工具顺手写的。
rectification-v9-tools.tsL963–972:记完证据后同一工具内调persistServerOwnedFocus(followup = 计划的 next_followup),把collect:invite:more落成 active。随后模型再调rectification-set-focus(同一 questionId)就走 RPC 的「已存在同 id 焦点」分支。 tool_failed是兜底码:safeToolErrorCode的已知列表(tool-service.tsL2030 起)之外的任何异常都记成tool_failed;invalid_case_id / invalid_domain / invalid_event_kind / invalid_focus这几个工具自己抛的码也不在列表里,同样显示为tool_failed。set-focus 的失败回执(L1183)只写safeErrorCode,没有任何地方记录原始错误文本;console.warn也没有。所以六次失败的真实原因目前无法从库或日志判断。- 可疑点按代码排序:(a) RPC
set_agentic_rectification_conversation_focus(20260902010000迁移 L74–111)对同 id 的 active 焦点做 identity 比较(schema 去 prompt),batch 写入的 schema 与 set-focus 工具写入的 schema(serverOwnedExpectedAnswerSchema+withSpokenPrompt)若键集合不同即抛focus_idempotency_conflict——但该码在已知列表里,理论上不会显示为tool_failed,需实测排除;(b) 模型把next_followup.kind_hint(invite_more/targeted:*/anchor:*)抄进targetKind,zodevidenceKindSchema拒绝 → Mastra 以validationErrors返回,mapStreamChunkToActivity应记成tool_call_rejected而不是tool_failed,与回执不符,也需实测排除;(c) 工具内assertCaseRef/invalid_domain/invalid_event_kind抛出(模型传错 caseId 或 domain)→ 正是tool_failed;六次同样失败、每次 10 ms,与「入参错、无 DB 往返」相符。 - 宿主兜底正文日期重复:
host-fallback.tsrecapLine把display_date_label(2016-09)和模型写的event_phrase(已含「2016年9月」)直接拼接。
3. 决策记录(产品负责人 2026-09-12)
- 工具失败必须可诊断:失败回执的
resultFingerprint写入engineMessageForReceipt(error)(脱敏、≤120 字),并console.warn一条rectification_tool_failed {tool, case_id, code, message};invalid_case_id / invalid_domain / invalid_event_kind / invalid_focus / tool_call_rejected进入已知列表,界面按码显示原因。 - 同一 questionId 的焦点已由服务端落下时,set-focus 工具幂等返回已存在的焦点(不比较 schema 键集合,只更新 spoken_prompt),不得抛错让模型重试。
- 模型对 set-focus 的
targetKind只允许证据种类或空;kind_hint类提示由服务端忽略而不是拒绝(schema 放宽为 string,服务端 clamp 为 null)。 - 兜底文案:
event_phrase已含年月时不再前缀display_date_label。
4. 硬红线
- 不改 DB 迁移;不改 Skill;不动 BUG-656~658 的 kind 映射与刷新逻辑。
- 记录不含真实用户资料。
5. 任务分解
- T0 取证:在 staging 容器日志里找 13:06:28 前后本 Case 的 set-focus 六次调用(若无日志即证明 §2.2);本地用夹具复现 §1 序列的第二轮,分别构造 (a)(b)(c) 三种入参,记录各自的回执码,写进进度记录第一段。
- T1 失败可诊断(BUG-659):按 §3.1 改
rectification-v9-tools.ts的失败回执与safeToolErrorCode已知列表;mapStreamChunkToActivity对tool-error也带code。验收:合同测试断言三类工具内抛错的回执码各自可辨,tool_failed只剩真正未知错误。 - T2 同 id 幂等 + targetKind 放宽(BUG-659):按 §3.2 / §3.3;set-focus 在 RPC 前先读活动焦点,同 id 直接返回(写 spoken_prompt 用
linkFocusAskedTurn或更新 schema)。验收:batch 已落collect:invite:more后调 set-focus 同 id →status=completed、idempotent=true;传targetKind:"invite_more"→ 不拒绝、落库target_kind=null。 - T3 兜底文案(BUG-660):
recapLine在event_phrase含\d{4}年或以display_date_label开头时只用 phrase。验收:夹具「2016-09 / 2016年9月上大学」→「记下了:2016年9月上大学」。 - T4 记录:BUG-659/660 入历史(关联 BUG-650 host_fallback、BUG-442 工具路径);CHANGELOG;PROGRESS。
6. 让步顺序
T1 + T2 不可省;T3 可并入下一单。
7. 开工前置命令
git fetch origin --prune
git worktree add -b codex/rectification-set-focus-tool-failed-20260912 .worktrees/rectification-set-focus-tool-failed-20260912 origin/staging
grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1 # 应为 658
cd frontend && npm test -- tests/rectification-v9-agent.test.ts tests/rectification-server-focus.test.ts tests/rectification-collect-prompt.test.ts
8. 验收口径
- 前端
tsc --noEmit0 错、npm run lint0 error、相关套件 fail=0、测试总数 ≥ 基线;next build后/仍 Static、首屏 gzip ±2%。 - 部署后真机:开场说两件学业,本轮工具回执不得出现
tool_failed;正文由模型或兜底生成时日期不重复。