fix(rectification): server-append spoken collect stems after free-text turns
Choice path already wrote spokenFollowupForUser into the body; free-text dropped that stem and only filled empty answers, so a new collect_spoken focus stayed invisible after “记下了”. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+21
-5
@@ -6756,14 +6756,14 @@
|
||||
- 状态:resolved
|
||||
- 首次发现:2026-08-30
|
||||
- 最近更新:2026-08-30
|
||||
- 影响面:GET `current_question`、`openQuestionFromPersistedFocus`、`agentVisibleLatestProjection`、`stableFollowupQuestionId`、POST `/api/rectification/agent` 出卡判定与空正文兜底、Agent 系统提示第 4 条
|
||||
- 影响面:GET `current_question`、`openQuestionFromPersistedFocus`、`agentVisibleLatestProjection`、`stableFollowupQuestionId`、POST `/api/rectification/agent` 出卡判定与空正文兜底、Agent 系统提示第 4 条。可见性策略已被 BUG-449 取代。
|
||||
- 用户现象:助手正文只写「接下来看下面这一问。」,界面没有任何问题。GET 里 `choice_card` 为 null,`current_question` 已是合法口述采集题(`kind=collect_spoken`、prompt 非空),问题只存在于数据库和 API。
|
||||
- 触发条件:方法覆盖回落到不带 choice_frame 的口述采集焦点;Agent 按选择卡承接措辞说话;前端只解析 `choice_card`。
|
||||
- 根因:三段同时成立。(1) 前端从不渲染 `current_question`:`applyCaseSnapshot` 只解析 `latest_result` / `choice_card` / `case`,`kind=collect_spoken` 在界面上没有承载位置。(2) 面向 Agent 的两套投影自相矛盾:`projectTurnDecision` 给出 `current_question.kind=collect_spoken` 与 prompt;记完证据后的 `agentVisibleLatestProjection` 取自 `openQuestionFromPersistedFocus`,后者对 collect focus 一律返回 null。Agent 收到打架信号,退回「卡片会显示」的措辞。(3) `stableFollowupQuestionId` 在 followup 既无 semantic_key 也无 probe_year 时退到 `${method_id}:${ask_theme}`;`persistPlanFocus` 传入的 active_focus 计划正好两者都没有,questionId 退化成 `active_focus:active_focus`。
|
||||
- 修复:P1/P2 仍在:`openQuestionFromPersistedFocus` 对 collect focus 返回带 `kind=collect_spoken` 的同形结果;`choiceReady` / 出卡判定只对 `kind=choice` 为真;`current_probe` 与 `inference.next_probe` 仍只对可渲染选择题放行;collect 在无 semantic_key / probe_year 时用 `collect:<domain>:<intent>`,`active_focus:active_focus` 不得与新 id 互相 already_open。P0 独立提示条已撤回:前端不再解析或渲染 `current_question`;口述采集题改由 Agent 用自己的话在正文里问出来,一次一问,不得照抄服务端 prompt 原文,但意思和时间范围不得改。「请点选」「看下面这一问」仍只允许在确实有选择卡时使用。服务端 collect focus 继续持久化,GET 仍返回 `current_question`。`runV9AgentTurn` 返回后若 `result.ok` 且 `answerText` 为空、且存在 `kind=collect_spoken` 的 active focus,则补一条普通助手消息(内容等于 focus prompt)并在关流前发出;`answerText` 非空一律不补。Skill 保持 10.0.13,未放宽 confirmation gate。
|
||||
- 验证:`frontend/tests/rectification-spoken-collect.test.ts` 锁:GET `collect_spoken` 不再渲染独立块、`.rectification-spoken-collect-prompt` 不存在、选择卡分支仍在、提示词要求正文提问、空正文落 focus prompt、非空正文不补。collect openQuestion 不为卡片、probe 在 collect 下为空、active_focus 派生 questionId 带 domain。BUG-440 collect-stall 全绿,D12 同领域计分卡仍优先于家人口述题。
|
||||
- 防复发:服务端问题的可见性由正文与确定性兜底保证,不得为无选项的问题新造视觉容器,更不得借用选择卡的样式类。同一焦点在所有面向 Agent 的投影里形态必须一致。「请点选」类措辞只允许在有选择卡时使用。不得用正文文本判断助手有没有问出来。collect questionId 不得退化为 `active_focus:active_focus`。
|
||||
- 相关记录:BUG-404、BUG-411、BUG-432、BUG-437、BUG-440
|
||||
- 修复:P1/P2 仍在:`openQuestionFromPersistedFocus` 对 collect focus 返回带 `kind=collect_spoken` 的同形结果;`choiceReady` / 出卡判定只对 `kind=choice` 为真;`current_probe` 与 `inference.next_probe` 仍只对可渲染选择题放行;collect 在无 semantic_key / probe_year 时用 `collect:<domain>:<intent>`,`active_focus:active_focus` 不得与新 id 互相 already_open。P0 独立提示条已撤回:前端不再解析或渲染 `current_question`。~~口述采集题改由 Agent 用自己的话在正文里问出来~~(该可见性策略已被 BUG-449 取代:题干改由服务器按焦点生命周期确定性接在正文之后,模型不得自行写题干)。「请点选」「看下面这一问」仍只允许在确实有选择卡时使用。服务端 collect focus 继续持久化,GET 仍返回 `current_question`。Skill 保持 10.0.13,未放宽 confirmation gate。
|
||||
- 验证:`frontend/tests/rectification-spoken-collect.test.ts` 锁:GET `collect_spoken` 不再渲染独立块、`.rectification-spoken-collect-prompt` 不存在、选择卡分支仍在。collect openQuestion 不为卡片、probe 在 collect 下为空、active_focus 派生 questionId 带 domain。可见性回归见 BUG-449。BUG-440 collect-stall 全绿,D12 同领域计分卡仍优先于家人口述题。
|
||||
- 防复发:服务端问题的可见性由正文与确定性兜底保证,不得为无选项的问题新造视觉容器,更不得借用选择卡的样式类。同一焦点在所有面向 Agent 的投影里形态必须一致。「请点选」类措辞只允许在有选择卡时使用。不得用正文文本判断助手有没有问出来。collect questionId 不得退化为 `active_focus:active_focus`。口述采集题的可见性策略见 BUG-449,不得再改回「指望模型问」。
|
||||
- 相关记录:BUG-404、BUG-411、BUG-432、BUG-437、BUG-440、BUG-449
|
||||
- 复发自:BUG-432(无选择卡的问题曾要求由 Agent 正文问出,前端仍不渲染 `current_question`)
|
||||
- 修复版本:待发布
|
||||
|
||||
@@ -6878,3 +6878,19 @@
|
||||
- 相关记录:无
|
||||
- 复发自:无
|
||||
- 修复版本:待发布
|
||||
|
||||
## BUG-449 | 口述采集题已落库但自由文本后助手只说记下了,用户看不到下一问
|
||||
|
||||
- 状态:resolved
|
||||
- 首次发现:2026-08-30
|
||||
- 最近更新:2026-08-30
|
||||
- 影响面:POST `/api/rectification/agent` 自由文本路径、`persistNextInterviewIfIdle`、`persistCollectSpokenAssistantIfNew`、`runV9AgentTurn` 口述题可见性、Agent 系统提示第 4 条
|
||||
- 用户现象:答完一件事后助手只说「记下了:…」,之后没有任何问题也没有卡片,但 GET 里 `current_question` 已是合法 `collect_spoken` 题。
|
||||
- 触发条件:自由文本记下带年份经历(本例唯一证据为 2022 搬家、`precision=year`);轮内 `rectification-record-evidence-batch` → `autoRescoreAfterEvidenceChange` → `persistPlanFocus` 新建 `collect:relationship:collect_method_evidence`;助手正文非空。
|
||||
- 根因:五段同时成立。(1) `persistNextInterviewAfterChoice` 对 collect 焦点返回 `hostNarration = spokenFollowupForUser(followup)`;点选路径把它写进正文,所以点选没坏。(2) `persistNextInterviewIfIdle` 调用同一个函数,却丢弃 `hostNarration`,只返回 `{persisted, choiceReady}`。自由文本路径因此没有题干输出。(3) 它还在开头 `if (dossier.conversationSummary.activeFocus) return` 提前返回;本 case 的焦点是轮内 `persistPlanFocus` 建的,所以这条兜底连跑都没跑。(4) route 唯一的兜底 `persistEmptyCollectSpokenAssistant` 只在 `!result.answerText.trim()` 时触发;正文非空一律不补。(5) 选择卡不受影响(卡片由 `current_question` 渲染);区分题有 `discriminatorInvariant` fail-closed。collect 两样都没有。BUG-441 把可见性改成「指望模型问」后,非空正文不再由服务器保证。
|
||||
- 修复:P0:在 `runV9AgentTurn` 已有的首份 dossier 上快照 `activeFocus.id`;轮后若 `projectCurrentQuestion` 为 `collect_spoken` 且 focus id 与轮前不同(含轮前为 null),服务器补题干。同一 id 不补。choice 永不补。P1:题干复用 `spokenFollowupForUser` / schema `prompt`;非空正文作为独立一段接在同一条助手消息后并 `answer.delta`;空正文整条消息就是题干。补出文本过 `INTERNAL_TOKEN_RE`,不得含「请点选」。agent-run 与 route 共用 helper + 本轮 `collectSpokenEmitted`,同一轮最多一次。P2:`persistNextInterviewIfIdle` 返回 `hostNarration`,交给 P1;activeFocus 提前返回保留,但不吞掉 P0。P3:撤回「口述采集题必须由你用自己的话问出来」;有持久化当前问题时题干一律不由模型写。Skill 保持 10.0.13。未放宽 confirmation gate / coverageComplete,未回退 b43808b0 无年份收窄。未用正文文本判断是否已问,未新造视觉容器。
|
||||
- 验证:`frontend/tests/rectification-spoken-collect.test.ts` 锁:2022 relocation 自由文本后正文含感情采集题干;同一 focus id 下一轮不重复补;choice 正文无题干;空正文整条即题干;in-turn / idle 共用同一句题干且每轮一次;无内部 token、无「请点选」。改写「非空不补」与 route `if (!answerText.trim())` 旧锁。`assert.doesNotMatch(/answerText\.(?:includes|match|search)\(/)` 仍绿。BUG-440 / BUG-441 / BUG-442 / b43808b0 无年份收窄回归保持绿。修复前:`persistEmptyCollectSpokenAssistant({ answerText: "记下了:2022年搬家。" })` 返回 `null`。
|
||||
- 防复发:口述采集题的可见性由服务器确定性输出保证,不得依赖模型是否照做,也不得用正文文本反推;点选路径与自由文本路径必须共用同一句题干来源(`spokenFollowupForUser` / schema prompt)。不得为口述题新造视觉容器或复用选择卡样式。
|
||||
- 相关记录:BUG-404、BUG-432、BUG-440、BUG-441
|
||||
- 复发自:BUG-441
|
||||
- 修复版本:待发布
|
||||
|
||||
Reference in New Issue
Block a user