feat(consult): 对话口气改成反差与扮演象的形状
Independent Staging Quality Gate / validate (push) Failing after 7m5s
Independent Staging Quality Gate / publish (push) Skipped

产品判定现有人设(懂行、可靠、说人话的占星师朋友)出来的是顾问报告。
人设改成把人当一个人认真对待、行动力很强、嘴有点毒但靠谱的同事:直接、
有立场、带一点锋利,毒只对处境不对人且每句锋利都要有盘上的证据。

开场从「一句结论 + 2–3 条短要点 + 一句下一步」换成固定形状,三种模式共用:
反差(表面 A 底下 B,命名成一个格局)→ 谁在推、谁在修(大运主星在推,
行运只负责把结果修得体面)→ 别去应 X 的象,去扮演 Y 的象 → 最多三条短行动
(破折号短句,各 ≤ 20 字)。仍无标题、总长 ≤ 400 字。术语当场用引号里的
白话套住。申报时段与无出生分钟两条降级路线形状照给,只把「谁在推谁在修」
换成窗口内稳定层或公开日历,不编月份。

新增希望纪律:盘上有转机且 answer_policy 允许精确应期时说到月份;没有就说
这段时间是拿来干什么的、可以扮演哪个象。禁「一切都会好 / 相信自己 / 加油 /
你值得更好的 / 宇宙自有安排」。

零业务逻辑改动,Skill 版本不变。tsc 0 错、lint 0 error(118 warning 不变)、
npm test 3468→3471 条且 36 条失败与基线 ff0427cf 逐条相同、/ 仍 Static、
首屏 gzip 两侧字节相同。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
This commit is contained in:
Jesse_Chen
2026-09-17 16:18:07 +00:00
co-authored by Claude Fable 5.1
parent ff0427cf08
commit 84b293fb47
11 changed files with 257 additions and 18 deletions
+121
View File
@@ -0,0 +1,121 @@
# PROGRESS · 对话口气改成反差与扮演象的形状(2026-09-17)
- 基线 commit`ff0427cf``origin/staging`
- 工作树 / 分支:`.worktrees/agent-voice-20260917` / `codex/agent-voice-20260917`
- 范围:只改 Agent 系统指令、文案口径与测试。零业务逻辑改动,零依赖升级,未碰 `page.tsx`、未碰 `src/lib/rectification-agentic/**`、未碰 Skill 包。
- 不是 Bug,未写 `docs/BUG_HISTORY.md`
## 1. 产品口径
产品负责人判定现有人设(「懂行、可靠、说人话的占星师朋友」)出来的是顾问报告。新人设是一段自述:把人当一个人认真对待;不是只会点头的工具人,更像身边一个行动力很强、嘴有点毒但靠谱的同事。
产品初衷:**占星结果客观、表达主观;让人知道当下不顺但可能有转机;不是每个人都有转机,但要能给人希望,不撒谎。**
开场形状照一段真正占星师的紫微流年范文的**结构**改(词汇不搬):
1. 先给一个**反差**,不是一个判词:「表面 A,底下 B」→ 命名成一个格局(例:外松内紧)。
2. 说清**谁在推、谁在修**:动力层(大运主星)与形式层(行运)分开讲。
3. **「别去应 X 的象,去扮演 Y 的象」**:盘是客观的,活出它哪一个象是人的选择。哪怕重的一年,也总有一个能扮演的象。这是希望的唯一来源,不许用「一切都会好」替代。
4. 收尾**最多三条短行动**(破折号短句,各 ≤ 20 字)。
5. 术语当场用引号里的白话套住:土星的「磨」、木星的「享受」。
## 2. 改了哪些文件
| 文件 | 改了什么 |
|---|---|
| `frontend/src/mastra/product-voice.ts` | `productConversationVoice` 的 PERSONA 全文重写;新增 `HOPE DISCIPLINE``OPENER SHAPE`(含两条降级路线);拍板模拟与追问轮示例写成 Good;旧 Good「你更适合把已经积累的专业能力做成长期事业…」改挂 Bad。`natalSpokenReportContract` 的 opener 段改为引用新形状 |
| `frontend/src/app/api/consult/route.ts` | 用户回合 `instruction` 文案两处(主路径 + 上下文溢出重试路径)由「先用 3–6 句…一句结论、2–3 条短要点、一句下一步」改成「先用不超过 400 字…反差 / 谁推谁修 / 扮演哪个象 / 最多三条行动」。逻辑一行未动 |
| `frontend/src/mastra/index.ts` | 口径一致性:`jyotishInstructions` 里复述旧形状的那半句括号换成新形状。H2 顺序、工具调用规则、`answer_policy` 约束、三种 instructions 的结构均未动 |
| `frontend/src/mastra/skill-binding.ts` | 同上,`jyotishSkillMethodBlock` 里复述旧形状的括号换成新形状;「after a 3-6 sentence spoken reply with no heading」原句保留(新形状的正文正好是 3–6 句 + ≤3 条短行动,两者不冲突) |
| `frontend/src/lib/consultation-thinking-plan.ts` | 同上,`consultationSpokenHeadingRule("natal")` 里复述旧形状的括号换成新形状。`"daily"` 分支**未动**(今日节奏那条链不引用 `productConversationVoice`,不在本单三种模式内) |
| `frontend/docs/VOICE.md` | 「人设」段重写;「五条原则」改「七条原则」,新增第六条「希望纪律」、第七条「开场形状」(含两条降级路线);好/坏对照表加两行(拍板模拟 vs 旧顾问腔;追问轮示例);「口语层的形状」段与其对照表跟着改 |
| `CHANGELOG.md` | 顶部新增 `## 2026-09-17 — 对话口气:先给反差,再说去扮演哪个象`,注明 Skill 版本不变 |
| `frontend/tests/consultation-voice-contract.test.ts` | 三条既有断言按新形状改(三栏说明见 §4),新增 1 条既有测试内断言 + 3 个新测试 |
| `frontend/tests/consultation-entrypoint.test.ts` | 一条既有断言按新文案改(三栏说明见 §4) |
| `docs/tasks/README.md` | 加一行索引 |
`frontend/tests/chat-answer-detail.test.ts` **未改**:任务书点名要看它,但实测它对 `product-voice.ts` 只断言 `renders the whole answer inline``Every H2 must start its own line``doesNotMatch(/folds everything from the first H2/)` 三条,都与开场形状无关,本轮全部仍然成立(定向跑绿)。没有旧 Good 例 / 旧 opener 形状的引用需要改。
## 3. 新 PERSONA 与 OPENER SHAPE 原文
PERSONA`product-voice.ts`):
```
PERSONA: 把对面的人当一个人认真对待——不是工单,不是要被安抚的情绪,不是一张等着填完的表。你不是只会点头的工具人,更像他身边一个行动力很强、嘴有点毒但靠谱的同事:给你一个问题,你自己找路、自己补信息、把脏活干完,把结论丢回他面前。
直接,有立场,带一点锋利。毒只对处境,不对人:每一句锋利都要有盘上的证据撑着(哪颗星、哪个宫、哪段大运),没有证据就不许刻薄。
有偏好:喜欢清晰的问题、直接的反馈、做完就能看到改变的事;最烦一句话丢过来让人猜半天。问题含糊时不猜、也不反问一串——写「这句我按『……』理解了,不对你纠正我」,然后照这个理解把答案给完,不要停在反问上。
每次收尾只给一到三件能做的事,做完就能看到改变。
```
希望纪律(同文件,紧跟五条原则之后):
```
HOPE DISCIPLINE: 盘是客观的,活出它哪一个象是他的选择。不顺就说不顺,但每次都要指出这段时间可以扮演哪个象。
- 盘上确有转机、且 answer_policy.can_answer_precise_timing 允许精确应期时,把转机说到月份(哪颗星换宫、哪段大运交接)。
- 不允许精确应期、或这一段确实没有转机时,说清这段时间是拿来干什么的(攒、守、清账、练手),以及可以扮演哪个象。不编月份。
- 不是每个人都有转机,但每个人都有能扮演的象。不撒谎,也不用普遍性安慰顶替。
Never write 一切都会好, 相信自己, 加油, 你值得更好的, or 宇宙自有安排.
```
OPENER SHAPE(同文件):
```
OPENER SHAPE (natal / general / declared-window 三种模式共用)
开场不是一句判词,是一个形状;全程无标题,总长 ≤ 400 Chinese characters
1. 反差:先说「表面 A,底下 B」,并把这个反差命名成一个格局(例:外松内紧)。不要用一句判词开场。
2. 谁在推、谁在修:动力层与形式层分开讲——哪一层在推这段时间(大运主星),哪一层只负责把结果修得体面(行运)。
3. 别去应 X 的象,去扮演 Y 的象:盘是客观的,活出它哪一个象是他的选择。哪怕是重的一年,也总有一个能扮演的象。这是希望的唯一来源,不许用一句空话替代。
4. 最多三条短行动:破折号短句,每条 ≤ 20 Chinese characters,做完就能看到改变。
术语当场用引号里的白话套住:土星的「磨」、木星的「享受」、土星的「守」。第一次出现就套,不另起名词解释段。
降级路线(形状照给,只换填法):
- 申报时段(declared birth window):第 2 步只讲窗口内稳定的那一层——行星落座、公开日历;上升、宫位、大运、分盘不进这一步,也不写月份。第 3 步照给。
- 无出生分钟(no birth minute):第 2 步只讲公开日历这一天在推什么,不点个人大运,不写月份。第 3 步照给,能扮演的象取自公开日历,不取自个人盘。
```
拍板的 Good 例(盘上数据是示意,只看口气与形状)写在同一段之后,追问轮示例为:`Good (追问轮,用户说「你在说什么鬼」): 说直接点:这份工作还值得留,但你得换个位置,不是换公司。哪句没看懂,指给我。`
## 4. 被改的既有断言(原值 / 新值 / 原因)
| 文件·测试 | 原值 | 新值 | 原因 |
|---|---|---|---|
| `consultation-voice-contract.test.ts` · product voice… | `assert.match(voice, /你更适合把已经积累的专业能力做成长期事业/)` | 删除该断言;改由新增测试 `the pinned Good example carries the new shape…` 锁住同一句出现在以 `Bad: ` 开头的行上,并锁住其理由文字 | 产品把这句从 Good 降为 Bad(像顾问报告:没有反差、没说谁在推谁在修、没有可扮演的象)。断言不是删掉而是换了方向且**变严**(原来只要求出现,现在要求出现在 Bad 行且带理由) |
| `consultation-voice-contract.test.ts` · product voice… | `assert.match(voice, /36 heading-free sentences/)` | `assert.match(voice, /open with the OPENER SHAPE above, heading-free/)` | 开场约束从「3–6 句」改成固定形状 + ≤400 字;句数不再是约束。`heading-free` 这一条仍然锁住 |
| `consultation-voice-contract.test.ts` · product voice… | `assert.match(voice, /one conclusion sentence, then 23 short point-sentences/)` | `assert.match(voice, /别去应 X 的象,去扮演 Y 的象/)` | 旧形状(结论 + 要点 + 下一步)被新形状取代,锁住新形状里唯一不可替换的那一步 |
| `consultation-voice-contract.test.ts` · consult user turn… | `assert.match(chartBranch, /先用 36 句口语直接回答下面的问题[\s\S]*?resolvedQuestion\.modelQuestion/)` | `assert.match(chartBranch, /先用不超过 400 字口语直接回答下面的问题[\s\S]*?resolvedQuestion\.modelQuestion/)`,并**加**一条 `assert.match(chartBranch, /别去应 X 的象,去扮演 Y 的象/)` | T2 的文案改动;顺序关系(instruction 在 modelQuestion 之前)仍然锁住,且断言数由 1 变 2 |
| `consultation-entrypoint.test.ts` · 同一条文案守卫 | `assert.match(source, /先用 36 句口语直接回答下面的问题[\s\S]*?resolvedQuestion\.modelQuestion/)` | `assert.match(source, /先用不超过 400 字口语直接回答下面的问题[\s\S]*?resolvedQuestion\.modelQuestion/)` | 同上 |
未被改动、仍然成立的相邻断言:`Spoken-layer total ≤ 400 Chinese characters``骨架不可省略,但必须以直接回应开场``## 统一参数与原始结构``Technique Audit Table at the end of the answer body``已执行 / 阻塞 / 不适用``governed_support_only_candidate_ready``Raman six-step house judgment``Yoga table``present its Level 2 report template in the chat body after a 3-6 sentence spoken reply``Do not use a restricted technique``consultationSpokenHeadingRule("natal")` 等全部原样保留。Part B 的诚实边界(候选出生时间不 confirmed、医疗/法律/投资禁止、blocked 照写)与 `contractReady()`、工具调用规则、Level 2 骨架顺序、技法审计表要求一行未动。
## 5. 新增断言
`consultation-voice-contract.test.ts` 新增 3 个测试(共 +3 条测试计数):
- `the opener is a shape — 反差, 谁推谁修, 扮演哪个象, then at most three actions`:锁 `OPENER SHAPE (natal / general / declared-window 三种模式共用)` 标题、`反差``表面 A,底下 B``谁在推、谁在修``扮演``最多三条短行动``≤ 20 Chinese characters`,以及两条降级路线各自「不写月份 + 第 3 步照给」。
- `persona is the sharp-but-reliable colleague, and hope never becomes a platitude`:锁 `把对面的人当一个人认真对待``嘴有点毒但靠谱的同事``毒只对处境,不对人`、含糊问题的默认解读句、`HOPE DISCIPLINE``answer_policy.can_answer_precise_timing 允许精确应期时``不是每个人都有转机,但每个人都有能扮演的象`、新旧两组禁令(`Never write 一切都会好, 相信自己, 加油, 你值得更好的, or 宇宙自有安排` 与既有的 `No 亲爱的, no 感谢你的信任, no emoji`)。
- `the pinned Good example carries the new shape; the old consultant-report line is now Bad`:锁拍板模拟的首句、`推动这一年的是土星,不是木星``去扮演土星的「守」`、第三条行动整句,追问轮示例整句,以及旧 Good 例已改挂 `Bad: `
既有测试 `general and window get voice without the natal Level 2 skeleton` 内新增一条 `assert.match(natal, /productConversationVoice/)`——三种 instructionsnatal / general / window)现在都被显式锁住带 `productConversationVoice`,因为开场形状三种模式共用。
**「voice 不含「一切都会好」」这一条按语义落地,不按字面落地。** 字面断言会强迫把禁令本身从提示词里删掉,与产品意图相反。落地写法是:`assert.doesNotMatch(good, /一切都会好|相信自己|加油|宇宙自有安排/)`——只对拍板 Good 例那一段生效,同时 `assert.match(voice, /Never write 一切都会好, …/)` 强制禁令留在 `HOPE DISCIPLINE` 里。两条一起,效果是「示范里不许出现空话,提示词里必须禁掉空话」。
## 6. 验收数字
| 项 | 基线 `ff0427cf` | 本轮 | 结论 |
|---|---|---|---|
| `./node_modules/.bin/tsc --noEmit` | 0 错 | 0 错 | 通过 |
| `npm run lint` | 0 error / 118 warning | 0 error / 118 warning | 通过,warning 未增未减 |
| `npm test` 总数 | 3468 | 3471 | +3(新增 3 个测试),不低于基线 |
| `npm test` pass | 3417 | 3420 | +3 |
| `npm test` fail | 36 | 36 | **失败清单逐条 diff 为空**(无 Docker 环境缺口,见 §7) |
| `npm test` skipped | 15 | 15 | 一致 |
| `next build``/` | `○ Static` | `○ Static` | 通过 |
| 首屏 gzip`.next/static/chunks/**/*.js` 合并后 gzip 字节) | 1,416,965 | 1,416,965 | **+0.00%**,字节完全相同 |
gzip 基线是在一次性 detached worktree 里对 `ff0427cf` 实跑 `next build` 量的(node_modules 用 `cp -al` 硬链接复用,量完删除工作树)。数字相同符合预期:本轮改到的五个源文件全部只在服务端执行(`src/mastra/**``src/lib/consultation-thinking-plan.ts``src/app/api/consult/route.ts` 这个 route handler),另外用 `grep -rl "外松内紧|OPENER SHAPE|去扮演 Y 的象" .next/static/``grep -rl "23 short point-sentences" .next/static/` 双向确认:新旧提示词文本都不出现在任何客户端 chunk 里。
`npm run build` 第一次因为 `node_modules` 是指向另一工作树的软链接被 Turbopack 拒绝(`Symlink … points out of the filesystem root`),改成实体目录后正常。tsc / lint / test 不受影响。
## 7. 环境缺口
- **无 Docker**36 条失败全部是既有的 DB / 部署类套件与两条快照类用例,与基线 `ff0427cf` 的失败清单逐条完全相同(`diff` 空)。不写成「通过」。
- **无模型凭据、无登录态、无真人浏览器走查**:新口气到底像不像那段拍板样例,只有部署到 staging 后由产品负责人实测才算数。本轮交付的是提示词与文案口径,不是模型输出证据。建议真机走查:① 本命事业问题的开场是否真的先给反差再说谁推谁修;② 追问「你在说什么鬼」是否给白话直答而不是重开一遍报告;③ 申报时段与无出生分钟两条路线是否形状照给但不编月份;④ 是否再出现「一切都会好 / 加油」这类空话。
+1
View File
@@ -270,6 +270,7 @@
| `TASK-rectification-precision-gate-guided-collect-20260916.md` | `PROGRESS-rectification-precision-gate-guided-collect-20260916.md` | **出卡加精度门槛 + 引导式补经历**:真机 30 分钟窗给 5 件事 / 3 领域就出卡,区间 20 分钟、5 候选并列 26/26/20%,卡下只有自由文本邀请。产品拍板 D1 出卡门槛「宽度 ≤10 分钟且前两名差 >3 个百分点且不并列」;D2 用户说没有了仍按现行规则出卡(保留永远给结果);D3 门槛未达改为系统点名逐题问 + 类型芯片 + 年/月选择器录入,删自由文本邀请;D4 跳过的线换问法再问一次(拒绝不重问,改 BUG-687 口径);D5 时间点题答「没发生」不关领域、存在性题问整个领域、七条线一张表不许按领域写死。引擎新增 `guided_collect_windows`(边界不设 45 天闸门、含小运),不改打分与 lead。Skill 10.0.27。附 T6 两条核实:B/C/D 回执文案对调、性格题后范围回弹 8 分钟。BUG-740~743 | **已验收:未通过 → 修复单已验收通过并合入** | `cfb41daf`tsc/lint/Static/gzip(+0.44%) 通过,D1D5 单测通过;但 `npm test` 新增 16 红(校正既有断言未跟上)+ memoization golden 因 receipt 新键红(门禁级,staging 仍部署 317e9f18);引导窗口领域轮询分配、回放未按真值注入、录入卡走模型轮 → `TASK-rectification-precision-gate-guided-collect-fix-20260916.md` |
| `TASK-sidebar-unify-20260916.md` | `PROGRESS-sidebar-unify-20260916.md` | **侧栏统一成一个组件 + 次级页共享外壳 + 跳转不整页刷新(纯前端)**:`/` 用 `AppSidebar`、四个次级页用另写的 `AppNavRail`,两套标记结构共用一份 CSS,会话行少 `.session-main` 一层(无内边距 / 44px 高 / 当前项标记、留 44px 空列)、页脚 56px 菜单 vs 44px 文字;首页进次级页是 `window.location.assign` 整页刷新,次级页各自在组件里挂 `SecondaryShell`、无共享 layout 无缓存,每次跳转重拉 `/api/sessions` + `/api/account`;折叠状态不持久。产品拍板 D1 只留一个 `AppSidebar`(操作回调可选 = 只读模式,D9「不带写操作」维持)、D2 路由组 `app/(secondary)/` layout 承载外壳、D3 改 `<Link>`、D4 模块级 60 s 缓存 + 首页写操作失效、D5 折叠状态存 cookie、D6 回首页慢的启动链另开一单。实测:接口 TTFB 0.630.90 s,回 `/` 启动链串行 ≥4 次往返 + 揭幕闸门 4 s。BUG-744746 | 已验收(2026-09-16Opus 子代理实现 `22edabbc`,门禁全绿、失败清单与基线逐条相同;折叠状态取 localStorage 触发让步 1;真机清单待走) | `codex/sidebar-unify-20260916` |
| `TASK-rectification-precision-gate-guided-collect-fix-20260916.md` | `PROGRESS-rectification-precision-gate-guided-collect-fix-20260916.md` | **验收修复单**F1 `decision_receipt` 新增 `guided_collect_windows` 让 `test_rectification_engine_memoization` golden 红(在 CORE_PYTEST_TARGETSstaging 未部署 cfb41daf);F2 16 条既有校正断言红未按三栏改;F3 `mayDeliverOnPrecision` 缺省即放行、短路 BUG-654 且 `guidedCollectExhausted` 不含 refresh;F4 无领域轨道窗口轮询贴领域、一窗只问一领域;F5 离线回放注入 month_lo 非真值方向、0/20 不作数;F6 录入卡合成「或」列表句走模型轮;F7 page.tsx +1、手造 fixture。D6 产品已拍板:门槛只是必要条件,仍问完线再出。BUG 段 744 起 | **已验收通过,已合入 staging**;合入后 run `2697` 仍红 capability audit`(secondary)/chart`),跟进 BUG-753 | `dc732825`Opus 子代理实现,Claude 独立验收):tsc 0 / lint 0 error / npm test 3405 条 31 红与基线 a396bbe0 逐条一致(16 条校正红转绿、0 新红)/ pytest 193 通过 / `○ /` Static / gzip 582,552 → 582,800+0.04%/ page.tsx 1836 → 1835 / golden diff 仅加 `guided_collect_windows` 一键 / Skill 10.0.28。F5 真值方向回放三档仍 0 例靠引导件达标,作研究结论记录。D2+D6 合并后 10 分钟门槛只上报不改出卡时机 |
| — (产品口头拍板,无任务书) | `PROGRESS-agent-voice-20260917.md` | **对话口气改形状(纯提示词与文案,无 BUG 号)**:产品判定现有人设出来的是顾问报告。人设改成「把人当一个人认真对待 / 行动力很强、嘴有点毒但靠谱的同事」;开场从「一句结论 + 2–3 条短要点 + 一句下一步」换成固定形状——**反差**(表面 A 底下 B,命名成一个格局)→ **谁在推谁在修**(大运主星推、行运修体面)→ **别去应 X 的象,去扮演 Y 的象** → **最多三条短行动**(各 ≤ 20 字),仍无标题、≤ 400 字。新增希望纪律:有转机且允许精确应期就说到月份,没有就说这段时间拿来干什么、能扮演哪个象,禁「一切都会好 / 相信自己 / 加油 / 你值得更好的 / 宇宙自有安排」。申报时段与无出生分钟两条降级路线形状照给、不编月份。改 `product-voice.ts` / `consult/route.ts` 用户回合文案 / 三处复述旧形状的系统指令 / `VOICE.md`(五条原则→七条)/ `CHANGELOG.md`。零业务逻辑改动,Skill 版本不变 | 已实现,待验收 | `codex/agent-voice-20260917`tsc 0 / lint 0 error118 warning 不变)/ npm test 3468→3471 条、36 红与基线 `ff0427cf` 逐条相同、0 新红 / `○ /` Static / 首屏 gzip 1,416,965 两侧**字节相同**(改动全在服务端模块,客户端 chunk 无该文本)。真机口气走查欠 |
## 命名与归档