Files
Jyotisha/TASK-agent-voice-20260901.md
T
Jesse_Chen 8617eb565c
Independent Staging Quality Gate / validate (push) Failing after 15m32s
Independent Staging Quality Gate / publish (push) Has been skipped
docs(voice): add task E — opening dedupe and user_meaning layering
硬编码盘点新发现的两处定位错误:opening 轮双重提问去重(结构化
判定,不用正文正则);引擎 user_meaning 作为素材分层,指令语气
不得直出用户。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
2026-09-01 15:40:51 +00:00

103 lines
11 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.
# 任务书 · Agent 输出人味修复:语气是产品的一部分,不是正确性的牺牲品(2026-09-01)
基线:`origin/staging` @ `ff97de5e`
## 0. 问题定性
用户反馈:Agent 输出"没有人味"。经排查这是**结构性**的,三个来源,全部在产品侧,均可在不动任何真实性红线的前提下修复:
1. **咨询系统提示配比失衡**`mastra/index.ts` + `mastra/product-voice.ts`):约 60 条指令里 ~90% 是禁令与合同(Never/Do not/Treat as final),语气指引只有三句零星短语,无 persona、无共情原则、无正面语料示例。模型被挤成审计报告腔。
2. **Level 2 模板把温度压到最后**`product-voice.ts`):本命问题强制以"## 统一参数与原始结构"的度数表开场,"## 现代生活"在技法审计表之后垫底。用户问"我婚姻怎么样",第一眼是岁差和上升度数。
3. **校正 agent 限一句话 + 硬编码文案无语气**`mastra/agentic-rectification.ts` 指令"每轮正文只输出一句确认或承接";`method-followup.ts` / `answer-choice.ts` / `rectification-decision.ts` / route.ts 里的服务端直出字符串):用户在校正里读到的话大半是修 BUG-456/D-1 那几轮为正确性手写的字符串,公文腔("按同一规则计入当前区分题")、审讯腔("有没有记得住时间的收入变化、大笔支出或欠债?")、法务腔边界句每轮重复;答完 5 题把 30 分钟缩到 7 分钟,没有任何进度反馈。
**关键边界**skill 10.0.14 文本只要求"正文只做承接与解释",并未限制一句话——"一句"是产品 prompt 自加的。问题槽服务端唯一所有、正文不得出题/复述题干、candidate/accepted/confirmed 语言边界、不得宣称唯一分钟:这些红线全部保留,本任务只改**语气与节奏**。
## 1. 硬红线
1. **真实性与合同边界一字不放宽**:技法审计表仍在正文文末;不得虚构;`REPRESENTATIVE_MINUTE_DISCLAIMER` 的**语义**必须保留(措辞可重写、频率可调,但交付轮必须完整出现一次"不是已确认的唯一出生分钟"语义);answer_policy / precise-timing 守卫不动。
2. **问题槽归属不动**:服务端 focus 仍是唯一问题源;正文仍不得提问、复述、改写题干;选择卡仍由结构化槽位承载。放宽的只是承接正文的**长度与温度**(一句 → 2-4 句,可含进度感),不是职能。
3. **不改 skill 包**(10.0.14 文本已兼容本任务;如发现某条硬编码文案被 skill 逐字锁定,先报告再动)。
4. **禁止空洞暖场**:人味 ≠ 客套话轰炸。不得加"亲爱的""感谢你的信任"式填充;不得每轮重复问候;不得用 emoji。人味的定义见 §voice 原则。
5. 既有测试断言若逐字锁定旧文案,允许同步更新断言文本,但**断言的语义约束**(如"必须含不可分区间语义""不得出现唯一分钟措辞")必须保留并改为语义级匹配;在 PR 里逐条列出改动的文案断言。
6. `cd frontend && ./node_modules/.bin/tsc --noEmit` 通过(不要用 `npx tsc`);`rectification-*` / `consultation-*` / `consult-*` / `chat-*` 测试 fail=0。
## 2. Voice 原则(写进代码库作为持久参照)
新建 `frontend/docs/VOICE.md`(或 `frontend/src/lib/voice-guide.ts` 中的文档常量),至少包含:
- **人设**:一位懂行、可靠、说人话的占星师朋友——不是客服,不是法务,不是表单。
- **五条原则**:先回应人再展开方法;说进度("我们已经把范围从 30 分钟缩到 7 分钟了");解释为什么问(一句就够);同一约束换着说(边界句不逐字复读);口语化但不轻佻。
- **好/坏对照各 ≥5 组**,取材自真实文案(见 §任务 C 清单)。
## 任务 0(门控)· 快照当前腔调,先立对照
把事故 case `f83d9b42` 的完整 assistant 输出串(route 直出 + 硬编码文案)按当前代码复排一份存入 PR 描述,作为"改前"样本。改后用同一形状复排"改后"样本。这是本任务书唯一的"红→绿":主观质量靠对照呈现,不靠断言。
## 任务 AP0)· 咨询 prompt 重构:voice 置顶,禁令收纳
`mastra/index.ts` / `mastra/product-voice.ts`
1. `productConversationVoice` 开头新增 voice 段(人设 + 五原则 + 2 组好/坏对照),放在所有合同条款**之前**。
2. Level 2 骨架**顺序不动**,但明确允许并要求:在 `## 统一参数与原始结构` 之前写一段 3-6 句、无标题的**直接回应**——先用人话回答用户问的事(方向性结论 + 一句共情/背景承接),再进入骨架。相应更新 "Do not shorten…" 措辞为"骨架不可省略,但必须以直接回应开场"。
3. 禁令清单归并去重(内容不变,压缩语言密度),把散落的三句语气指令并入 voice 段。
4. `generalJyotishInstructions` / onboarding 指令同样加 voice 段。
## 任务 BP0)· 校正 agent 松绑到"2-4 句自然承接"
`mastra/agentic-rectification.ts`
- "每轮正文只输出一句确认或承接" → "每轮正文 2-4 句:确认收到什么、(可选)一句为什么有用或当前进度、自然过渡到界面上的下一步。不提问、不复述题干、不预告选项。"
- 新增进度感指引:当 dossier 显示候选区间较开场收窄时,允许且鼓励用一句自然语言说出(数字来自服务端投影,不得自算)。
- 加 2 组好/坏对照(如 坏:"好的,记下了。" 好:"2016 年 9 月上大学,记下了——这类有明确月份的节点对校正特别有用。")。
## 任务 C(P1)· 硬编码文案人味重写(集中 + 逐条)
先把散落的用户可见字符串集中到一个 copy 模块(如 `lib/rectification-agentic/v9/user-copy.ts`),再逐条重写。**清单**(行号是线索,按符号定位;语义约束在括号里,必须保留):
| 现文案 | 位置 | 问题 | 方向 |
|---|---|---|---|
| "请先说一件你记得大概时间的人生经历,比如升学、入职、搬家、结婚或生病;只记得年份也可以。" | `method-followup.ts` GENERIC_COLLECT_QUESTION | 公文枚举 | "从你最容易想起来的开始就好——比如哪年上的大学、哪年换的工作,记得大概年份就行。" |
| "有没有记得住时间的收入变化、大笔支出或欠债?" 等 USER_COLLECT_QUESTION 表 | `method-followup.ts` | 审讯式 | 每条加一层自然引子,去掉罗列感 |
| "请点选下面的选项作答。点选和一句话回答会按同一规则计入当前区分题。" | route.ts | 内部规则外漏 | "直接点下面的选项就行,打字回答也一样算数。" |
| "我没能确定这句话是否在回答当前问题。请点选下面的选项,或换一种说法。" | route.ts | 生硬 | "我不太确定这句是不是在回答上面的问题——点个选项,或者换个说法都行。" |
| "当前可信区间是 X–Y,代表分钟 Z。代表分钟只是代表性候选,不是已确认的唯一出生分钟。" | `rectification-decision.ts` nonConvergingRangeNarration | 法务腔+每轮复读 | 交付轮完整版带进度感("从最初的 30 分钟缩到了 X–Y 这 7 分钟");中间轮允许省略边界句或用短版(语义约束:交付/采用轮必须含完整边界语义) |
| "…可以从下面的时间里选一个采用。" | `answer-choice.ts` | 生硬 | 与上一条合并重写 |
| "当前问题已更新,请刷新后重新作答。" | route.ts | 机器腔 | 口语化 |
| "接下来请点选下面这一问。" 类占位 | turn-exit/答题链 | 占位残留 | 确认 Task C(上轮)已替换为真实题干后删除死文案 |
| `UNVERIFIED_BIRTH_TIME_NOTICE` 等咨询侧 notice | `consultation-birth-time-mode.ts` | 与 TASK-consultation-birth-accuracy 的任务 D 协同,避免双改冲突 | 以该任务书为准,本轮跳过 |
重写要求:每条在 PR 里"旧 → 新 → 保留的语义约束"三栏列出。
## 任务 E(P1)· 定位错误的硬编码:开场去重 + user_meaning 分层
硬编码全量盘点(2026-09-01)发现两处不是"文案难听"而是"定位错误",重写解决不了,需要结构处理:
### E-1 · 开场双重提问去重
事故 case 转写第一屏:opening 轮模型正文已自然包含收集引导("就从你最容易想起来的一件事开始……你先随便说一件吧"),紧接着 `finalizeSuccessfulTurnExit` 把服务端 focus 的 `GENERIC_COLLECT_QUESTION` 作为**第二条独立 assistant 消息**写进 turn 历史,用户连续看到两条语义重复、语气断裂的消息。
修法(保持问题槽红线):focus 仍照常持久化(UI 问题槽渲染不变);但当本轮模型正文存在且该 focus 是 opening 轮的 collect 类 focus 时,**不再把题干作为独立 turn 消息追加**。判定条件必须是结构化的(action === opening && focus.intent 为 collect 且本轮已有非空 assistant 正文),不得用正文字符串匹配(红线:不得靠正文正则判断"是否已问过"——BUG-456 教训的反面同样成立:这里判断的是"本轮有无正文"这个结构事实,不是正文内容)。非 opening 轮的问题写入历史(上轮任务 C 的成果)保持不变。
### E-2 · 引擎 user_meaning 分层:素材,不是用户文案
Python 引擎的 `user_meaning` 字段(`refinement_packet.py` / `decision_policy.py` / `confirmation-gate.ts` 等,如"主限更偏向 05:00,分盘大运更偏向 04:54。冲突时不能按更高把握收口。""不得把它描述为已确认的唯一出生分钟")设计定位是给模型/内部的解释素材,但存在被 UI 或确定性叙述**字段直出**给用户的路径——用户会读到混着内部指令语气的句子。
修法:
1. 盘点所有 `user_meaning` 的消费点,分成两栏:进模型上下文(保留原样)vs 直出用户(逐个处理)。
2. 直出用户的点:改为消费专门的展示字段(新增 `display_copy` 或由 TS copy 模块按结构字段生成),或改为模型转述路径;指令语气句("不得…")一律不得出现在用户可见文本。
3. 引擎侧字段语义不变、不删字段(模型和审计仍在用);这是消费端分层,不是引擎改造。
4. 在 PR 里贴出直出点清单与前后对照。
## 任务 DP2)· 防回归
- copy 模块导出集中后,加一条"机器腔词表"测试:用户可见文案不得包含 "按同一规则计入"、"请刷新后重新作答"、"当前状态为" 等词条(词表放测试文件,≥8 条,含本次清理的全部旧词)。
- 交付/采用轮边界语义测试改为语义级:断言包含"不是已确认的唯一出生分钟"或等义白名单短语之一,而非逐字全句。
## 验收标准
1. 任务 0 的改前/改后对照(事故 case 形状全程文案 + 一次婚姻主题咨询的开场三段)贴 PR;对照中开场不得再出现语义重复的两连问(任务 E-1)。
2. `tsc --noEmit` exit 0;四组前端测试 fail=0;改动的文案断言逐条三栏说明。
3. 红线 1/2 的证明:交付轮输出仍含边界语义;正文仍不含题干(现有 server-focus/question-ownership 测试全绿)。
4. `VOICE.md` 落库;机器腔词表测试落库。
5. 未在真实环境验证的部分如实注明。