Files
Jyotisha/docs/tasks/TASK-rectification-collect-semantics-20260910.md
T

11 KiB
Raw Blame History

TASK · 采集语义三改:Skill 去掉「财务只有主动说才问」、带年份线索的采集题优先并改口径、跳过/没有一律由分类器判(2026-09-10)

  • 基线:origin/staging @ 82eb3b79(代码同 8f9eb587,已部署;BUG-635~640 的实现已改过 route.ts / turn-intent-classifier.ts / method-followup.ts,开工先 rebase 到最新 staging
  • 分支:codex/rectification-collect-semantics-20260910,基于 origin/staging
  • 串行:BUG-635~640 已由另一会话实现并合入 staging(a998b6ec8f9eb587),本单直接基于最新 staging 开工;与 TASK-rectification-superseded-focus-duplicate-20260910.mdBUG-644/645)在 answer-choice.ts 有交集,那份先做
  • 执行方:coding agent;验收:Claude
  • BUG 编号起点:BUG-641635/636、637~640 已由前两份任务书占用;开工时核对 docs/BUG_HISTORY.md 最大号)
  • 本单升 Skill10.0.21 → 10.0.22(改 SKILL.md 正文必须升版本,包目录哈希钉在 skills/skill-package-registry.json)。

0. 产品决策(2026-09-10,产品负责人口头授权)

  1. 去掉「财务与健康只有用户主动说才问」。财务、健康与其他领域同权:服务器可以主动问,用户说了就记、就计分。
  2. 带年份的题优先。区分候选的带年月点选题永远先问(10.0.21 已定);采集题里,带年份线索的先于没有线索的,同领域的无年份性格卡不得抢在采集题前面。
  3. 「没有」「记不清」这类业务判断不得靠正则或原字匹配,一律由意图分类器判;分类器要能分出「这方面没有」和「记不清、以后再说」两种。

1. 现状实证(同一份 Case JSONSkill 10.0.21

现象 代码事实
Skill 说「财务与健康只有用户主动说才问,仍可计分」(skills/jyotish-birth-time-rectification/SKILL.md L84),服务器却主动问了财务 TS 采集轮转 DATED_COLLECT_ORDERmethod-followup.ts L1249)含 finance,自 BUG-462 起主动问;Python event_probes.py L145 VOLUNTEER_ONLY = {finance, health_pressure} 让这两域不进 evidence_collection_probes、不进 missing_collection_domains。三处口径各自为政,模型同时读 Skill 和回执。
家人采集题 question_id=probe:family.2021,题干却没有年份 2021 来自 evidence_collection_probessource=age_band_age_band_year L290:出生年 + 该领域典型年龄段中点)。它不是引擎按候选算出的区分年份,只是帮回忆的线索。 BUG-539 因「某年前后…记得大概哪年就行」自相矛盾而把前缀整句删掉,probe_year 只留给计分与去重。用户答「不记得了」后家人线关闭,丢掉的是一个回忆线索,不是区分题——此前审计单观察项 2 把它说成「带年探针被吞」,措辞过重,以本单为准。
家人分支 sameDomainYearlessCard("family") ?? makeFollowup(采集)L2578 同领域无年份性格卡优先级高于带年份线索的采集题,与决策 2 相反。
「没有」「记不清」走原字匹配 route.ts L524isCollectDeclineUtterance / isCollectSkipUtteranceturn-intent-classifier.ts L3743)只认去掉句末标点后完全等于「没有」/「记不清」的句子。「不记得了」「记不得」「忘了」「这方面没什么」全部落到 LLM 分类器;而采集题分类器提示词只教了 noshouldDeclineCollectFocus 只认 no,没有「跳过」一类。本例「不记得了」靠 Agent 自己调 rectification-resolve-focus 兜住。

2. 任务分解

T1 · BUG-641(P2):财务 / 健康同权采集,三处口径合一,Skill 升 10.0.22

  • SKILL.md L84 删去「财务与健康只有用户主动说才问,仍可计分。」,改为「财务、健康与其他领域同权:服务器按 method_followup_plan.next_followup 主动问,用户说了就记、就计分。」其余句子不动。
  • PythonVOLUNTEER_ONLY 清空(或删除常量与 L353 分支),使 finance / health_pressure 进 missing_collection_domainsevidence_collection_probes(带 age_band 年份线索)。DOMAIN_CATALOG 若缺这两域的 age_lo/age_hi,按事业线同段补。oos_blind_prompts 语义不变。
  • Skill 升版:复制 versions/10.0.21versions/10.0.22,根 SKILL.md 与版本目录逐字节相等;skill-package-registry.json 新条目(sha256 / sourceCommit / packagePath / status=active);case-status.ts RECTIFICATION_SKILL_VERSION = "10.0.22";钉版本的测试(skill-registry.test.tsagent-voice-copy-contractrectification-collect-stall 等)按「原值 / 新值 / 原因」更新;CHANGELOG.md 写明 Skill bump。
  • 验收:grep -n "主动说才问" skills/ 为空;Python 定向测试:evidence_collection_probes 对只有学业/感情事件的账本返回含 finance 与 health_pressure 的行;BUG-621 回归:绑定 10.0.21 的历史 Case 仍能打开(open-request.ts 不要求绑定版本等于当前版本),写进真人清单。

T2 · BUG-642(P2):带年份线索的采集题优先,题干带线索但不矛盾

  • 口径(推翻 BUG-539 的「整句去前缀」,但保留它指出的矛盾不得复现):有 probe_year 的采集题题干写成「家里在 2021 年前后有没有结婚、添丁或住院?有就说个大概年月,别的年份也行。」——年份是线索,不是限定;不再同时出现「记得大概哪年就行」。USER_COLLECT_QUESTION.<domain> 拆成 withYearCue(year) / withoutCue 两个模板,文案对照 frontend/docs/VOICE.md
  • 优先级:方法覆盖链里,每个领域先出带年份线索的采集题sameDomainYearlessCard 只在该领域已覆盖(账本有事件 / answeredYes)或已拒答后才可出;nextDatedCollectFollowupDATED_COLLECT_ORDER 内先排有 evidence_collection_probes 年份线索的领域,再排没有的。带年月的区分点选题(rankedCatalog dated)仍在一切采集题之前,不动 10.0.21 规则。
  • 验收:rectification-spoken-collect.test.ts / rectification-method-followup*.test.ts:家人域有 age_band 2021 且有 D12 无年份对照卡时,next_followup 是采集题且 user_prompt 含「2021 年前后」、不含「记得大概哪年就行」;家人已拒答后才轮到 D12 卡;无 probe_year 的领域用无线索模板;BUG-539 的「不得既指定年份又让报年份」断言改写为新模板断言并注明原值/新值/原因。

T3 · BUG-643(P2):跳过 / 没有一律由分类器判,删掉原字匹配

  • 删除 isCollectDeclineUtterance / isCollectSkipUtterance 及其在 route.ts L524 的短路;不得用任何正则或词表替代。
  • 采集题分类器提示词(turn-intent-classifier.ts 的 collect 分支)加两条明确口径:「没有、没发生过、这方面没什么」→ answer_current_focus + answer_class=no(该方面没有事,本次不再问);「记不清、不记得、忘了、想不起来、以后再说」→ answer_current_focus + answer_class=unsure(先放着,以后可补)。带年月的新经历仍按现有规则进 provide_new_evidence / has_new_dated_eventweak_yes / yes 对采集题无意义,返回即视为 unclear 走 Agent。
  • 路由:shouldDeclineCollectFocus 改为 collectFocusCloseStatus(classified): "declined" | "skipped" | nullno→declined、unsure→skipped,两者都走 applyCollectFocusDenialack 文案沿用 collectDeclinedAck / collectSkippedAck);has_new_dated_event 为 true 时仍 deferFollowup 并进 Agent。分类器抛错或返回 null:不做关键词兜底,按现状进 AgentAgent 可调 resolve-focus),并在 RectificationRunDiagnosticcollectIntent=unclassified
  • 多说一句:分类器对每条采集回答都已在跑,本改动不增加模型调用次数,只是删掉两条捷径。
  • 补(2026-09-10 验收 BUG-635 时发现,P2expectedWriteFromCollectIntent 漏掉了最常见的一种——用户直接用一件带年月的事回答采集题。按分类器提示词它会是 answer_current_focus + yes/weak_yeshas_new_dated_event=false,于是 expectedWrite="none",BUG-635 守卫只剩「记下了」文字判定。本单改口径:collect 焦点answer_current_focusanswer_class ∈ {yes, weak_yes}expectedWrite="evidence"choice 焦点不变)。验收:rectification-turn-intent-classifier.test.ts 加该映射用例;rectification-unwritten-evidence.test.ts 加「collect 焦点 + yes + 无写入 → 重试」用例。
  • 验收:rectification-turn-intent-classifier.test.ts:「没有」→no、「不记得了」「记不得」「忘了」→unsure、「2021 年搬过家」→provide_new_evidence、「没有,不过 2019 年换过工作」→no + has_new_dated_eventrectification-spoken-collect.test.ts L442「没有 and 记不清 short-circuit without a model run」改为「由分类器判定 no / unsure 后走 denial」,原值/新值/原因写进进度记录;BUG-626skipped 健康不进 holdout)回归仍过;源码合同测试断言 route.tsturn-intent-classifier.ts 不再导出/引用这两个原字匹配函数。

T4 · 记录

  • docs/BUG_HISTORY.md BUG-641643BUG-539 补「口径被 BUG-642 改为带线索模板」;审计单观察项 2 的措辞在本单 §1 已更正。
  • CHANGELOG.md:财务/健康同权采集;采集题带年份线索;「没有 / 记不清」改由分类器判;Skill 10.0.22。
  • docs/tasks/PROGRESS-rectification-collect-semantics-20260910.md
  • docs/testing/:真人清单三条——(a)新 Case 走到财务题,回「没有」与回「不记得了」各一次,分别看到「这方面先跳过」与「这题先放着」;(b)家人题题干带年份线索且能用别的年份回答;(c)打开一个绑定 10.0.21 的历史校正。

3. 让步顺序

T2 的「优先级」半边可延后(写 BLOCKED.md),T2 的「题干带线索」半边与 T1、T3 不可省。

4. 开工前置命令

git fetch origin --prune
git worktree add -b codex/rectification-collect-semantics-20260910 .worktrees/rectification-collect-semantics-20260910 origin/staging
grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | tail -1
.venv/bin/python -m pytest tests -k "event_probes or collection" -q
cd frontend && npm test -- tests/rectification-spoken-collect.test.ts tests/rectification-turn-intent-classifier.test.ts tests/skill-registry.test.ts

5. 验收口径

  • 前端 tsc --noEmit 0 错、npm run lint 0 error、相关套件 fail=0、测试总数 ≥ 基线;next build/ 仍 Static,首屏 gzip ±2%。
  • Python 定向测试与 run_quality_gate.py --profile quick 通过。
  • 推 staging 后 /api/healthdeployment.gitCommit 等于最新含代码改动的提交;部署后先打开一个旧版本历史校正(BUG-621 教训),再做其余真人清单。