Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
14 KiB
14 KiB
TASK · 生时校正并入聊天 · 第一步:从对话里进、做完回到原对话(2026-10-06)
- 基线:
origin/staging@da226db1 - 执行分支:
codex/rectification-in-chat-step1-20261006,worktree.worktrees/rectification-in-chat-step1-20261006 - BUG 编号起点:BUG-1247(现最大 1246;开工时再核对)
- 串行:本单改
use-consultation-run.ts、use-rectification-surface.ts、starter-home.tsx、rectification-agentic-chat.tsx、consult 提示词与工具。同日TASK-rectification-delivery-dup-adopt-20261006(含 fix 单,BUG-1241~1243/1246)也改校正交付与采用路径,必须等它合入 staging 后再从最新origin/staging开分支;开工前git log origin/staging -5 -- frontend/src/lib/rectification-agentic frontend/src/app/api/rectification确认。 - 第二步(另立单,本单不做):把校正的题目卡片直接嵌进同一条对话,不再切换画面。本单的设计不得堵死这条路(见 §4 第 6 条)。
1. 现状实证
| 事实 | 位置 |
|---|---|
校正没有独立页面,是首页 page.tsx 里的另一套界面:rectificationSurfaceOpen && rectificationCaseId 时渲染 ConversationalBirthTimeRectification,替换普通对话和输入框 |
app/(app)/page.tsx |
| 首页有「生时校正」按钮(「开始新的生时校正 / 再次校正」),只支持本人 | components/starter-home.tsx 的 openRectificationFromHomepage;lib/rectification-entry.ts |
| 对话里进校正只靠前端精确文字匹配:用户打出「生时校正 / 先完成生时校正 / 出生时间校正」才跳,新开一个校正会话;原问题只当一行展示(「结束后新建对话,按采用的时间再问」) | lib/consultation-entrypoint.ts 的 isRectificationHandoffQuestion;hooks/use-consultation-run.ts(调用 openRectificationFromHomepage(pendingQuestion, currentSession.id));rectification-agentic-chat.tsx 的 pendingConsultationQuestion |
| 普通对话 agent 遇到「推得不准」没有任何校正相关指令;👎「内容不准」只记反馈 | mastra/index.ts;lib/reply-quality-labels.ts |
两种会话都存在 chat_sessions,用 session_type(consultation / birth_time_rectification)区分,一个校正 case 绑一个会话;/api/consult 拒绝非咨询会话(session_not_consultation) |
迁移 20260721110000、20260812010000_…v9_runtime.sql;app/api/consult/route.ts |
采用(accept)直接写本人 profiles 的出生时间并置 birth_time_status='accepted';普通对话每轮都重新读 profiles,下一轮就按新时间排盘 |
app/api/rectification/cases/[caseId]/candidates/accept/route.ts → RPC;consult/route.ts 读 profile;lib/birth-time-consultation-consent.ts 的 resolveBirthTimeConsultationRoute |
| 但校正结束文案让用户「新建对话」 | lib/rectification-agentic/user-copy.ts:postAdoptVerifyDone、已采用 ${clock},新建对话即按这个时间排盘。 |
计费:普通对话按轮扣;校正按 case 扣一次(第一个实质回合),功能开关 isProductEnabled("rectification") |
consult/route.ts;v9/agent-route-billing.ts |
open 接口已接受 sourceSessionId,目前只用来推产品领域 |
app/api/rectification/cases/open/route.ts;v9/open-request.ts;v9/product-domain.ts |
2. 要解决的问题
用户要在聊天里自然地发现「可能是出生时间的问题」,校正完回到刚才那条对话继续聊。现在有三处断开:入口在首页而不在对话里;对话里只有一句固定口令能跳;做完被要求新建对话,原来那个问题和上下文都丢了。
3. 决策记录(产品 2026-10-06 授权)
产品当面选择:
- P1 分两步做:本单是第一步——从对话里进校正(仍暂时切到校正画面),做完自动回到原对话。第二步另立单。
- P2 采用新时间后,原对话里之前的回答保留原文,在上方加一行「按校正前的时间」;之后按新时间回答。
- P3 删除首页「生时校正」按钮(含「再次校正」文案)。以后入口只在对话里。
Claude 定(架构):
- C1 由 agent 判断,不用正则。 删掉
isRectificationHandoffQuestion文字匹配这条路和pendingConsultationQuestion那行展示(多余入口宁可删除)。用户直接说「我想校正出生时间」,同样由 agent 判断后给卡。沿用 BUG-976/977「禁正则判意图」口径。 - C2 先分清是读法问题还是时间问题。 用户说不准时,agent 先看他说的是什么:只是不同意某个解释,按现有
FOLLOW_UP_CORRECTION_RULE认下、改读;说出过去的具体经历和盘对不上、或说自己出生时间记不准,才提议校正。同一会话提议过一次、用户没接,之后不再主动提(用户再问另说)。 - C3 提议是一张卡,不是一句话。 consult agent 新增一个只产出事件的工具(名字自定,例:
offer-birth-time-rectification),服务端校验:问的是本人、isProductEnabled("rectification")、账户可用。不满足时工具返回原因,agent 用一句话说明(例:「给家人校正暂时还不支持」),不出卡。卡上写清:用几道过去经历的题核对出生时间、大概几题、会扣几点(价格从feature_pricing读,不写死)。点了才开 case;开 case 本身不扣费(沿用现有「第一个实质回合才扣」)。 - C4 点卡 → 打开校正,并带上来源。 复用
openRectificationCase与sourceSessionId;同时把触发提议的那条用户问题存成可恢复的来源信息(服务端存,刷新后仍在;字段放哪由执行方定,优先不动表,见 §4 第 3 条)。 - C5 什么时候回去。 以下任一情况回到来源对话:采用完成且采用后核对结束;校正结束但没有采用(含「先不改」);用户在校正画面点「回到刚才的对话」(新按钮,只在有来源对话时出现)。没有来源对话的老校正会话(历史里点开的)不出这个按钮,行为不变。
- C6 回去之后。 来源对话里追加一条分隔消息:采用了写「出生时间改为 HH:MM(生时校正)」,没采用写「出生时间没有改」。分隔线之前的助手回答都加一行小字「按校正前的时间」(P2,只在采用了时加)。分隔线下面给一个按钮「按新时间重新看:「原问题」」,点了才发,按普通对话一轮计费;不自动发,不偷扣点。
- C7 让模型知道旧回答用的是旧时间。 来源对话后续轮次的历史窗口 / 会话摘要里,分隔线之前的助手内容要带一句说明(例:「以下回答按校正前的出生时间,不要沿用其中的盘面判断」),避免模型拿旧盘的结论接着说。
- C8 文案。
user-copy.ts里「新建对话即按这个时间排盘」两处改成回到原对话的说法;无来源对话时改成「之后的对话按这个时间排盘」(不再叫人新建)。 - 不变:校正内部流程、打分、出卡、采用门、采用后核对;校正会话仍是独立的
chat_sessions行、仍出现在侧栏历史且能打开(BUG-621 教训);校正计费;首次引导里的出生时间步骤(home-onboarding-shell.tsx);只支持本人。
4. 硬红线
frontend/src/app/(app)/page.tsx不得增长(冻结);新逻辑进hooks/、lib/或组件。- 不改校正打分、引擎、采用 RPC 的判定逻辑;不放宽采用门。
- 优先不动数据库结构(来源问题、分隔消息能放进现有 JSON 字段就放)。必须动表时:新迁移 + 真跑
npm run test:db(无 Docker 用本机 PG17 替身法,见docs/既有说明),并在进度记录写明为什么不能不动表。 - 不得用正则或关键词判断「用户觉得不准 / 想校正」。
- 不自动替用户发起任何扣费请求(C6 的重看必须点按钮)。
- 不堵第二步:来源对话 ↔ 校正 case 的关联做成数据(谁从哪来、回哪去),不要写死在画面切换里;第二步要在同一条对话里渲染校正回合时能复用。
- 隐私:任务书、测试、Bug 历史里不写真实出生资料;fixture 用公开名人或虚构资料。
- 改既有断言三栏;测试总数不低于基线;
tsc0、lint 0 error、build 后/仍 Static、首屏 gzip ±2%。
5. 任务分解
T1 删首页入口与文字口令(P3、C1,BUG-1247)
- 删
starter-home.tsx的「生时校正」按钮及其 props 链路;rectification-entry.ts里只服务这个按钮的文案一并删。 - 删
isRectificationHandoffQuestion及use-consultation-run.ts里的跳转分支;删pendingConsultationQuestion展示与传参。 - 侧栏历史里的校正会话照常能打开、能「重新校正」以外的既有操作不变(若「重新校正」只经首页按钮,就改成在对话里说)。
- 验收:
git grep -n "isRectificationHandoffQuestion\|pendingConsultationQuestion\|openRectificationFromHomepage" frontend/src为 0 行(或只剩 C4 复用的函数,改名后列进进度记录);首页无该按钮的合同测试;历史校正会话可打开的既有测试仍绿。
T2 对话里提议校正(C2、C3,BUG-1248)
- consult 提示词(形状只定义一处的规矩照旧)加入 C2 的判断规则与「同会话最多主动提一次」。
- 新工具:服务端校验本人 / 开关 / 价格,产出一个公开事件;前端在该条助手消息下渲染提议卡(复用现有卡片样式,不新造第二套)。卡片一条消息最多一张。
- 验收:工具对非本人、开关关、已有进行中校正 case 三种情况不出卡并返回原因(单测);卡片价格来自
feature_pricing(单测);提示词合同测试锁 C2 两分法与「最多主动提一次」;不含任何意图正则(静态检查)。
T3 带来源打开校正(C4,BUG-1249)
- 点卡调用
open,带sourceSessionId与来源问题;服务端持久化,GETcase / entry-summary 能读回来源。 - 验收:刷新校正画面后来源仍在(集成或路由测试);来源会话不属于本人时拒绝(单测)。
T4 回到原对话(C5、C6、C8,BUG-1250)
- 三种回去时机按 C5 实现;校正画面新增「回到刚才的对话」按钮(仅有来源时)。
- 来源对话追加分隔消息;采用时给分隔线前的助手回答加「按校正前的时间」;分隔线下「按新时间重新看」按钮,点击按普通一轮发送原问题。
- 文案按 C8 改;对照
frontend/docs/VOICE.md。 - 验收:采用 / 未采用 / 手动返回三条路径各一条测试;刷新来源对话后分隔线、小字、按钮仍在;按钮不点不发请求(测试断言无
/api/consult调用)。
T5 模型上下文(C7,BUG-1251)
- 来源对话后续轮次的历史窗口与会话摘要,对分隔线前的助手内容加说明。
- 验收:历史窗口构造的单测:采用后分隔线前内容带说明、之后的不带;未采用时不带。
T6 文档与记录
frontend/DESIGN.md(入口、提议卡、分隔线、小字、返回按钮);CONTEXT.md(若新增术语如「来源对话」「提议卡」,先进 glossary);CHANGELOG.md;docs/BUG_HISTORY.md(BUG-1247~1251,部署 + 真机前不写 resolved);docs/tasks/PROGRESS-rectification-in-chat-step1-20261006.md;docs/tasks/README.md状态板。- 真机清单
docs/testing/rectification-in-chat-step1-20261006.md,至少包含:- 首页没有「生时校正」按钮。
- 普通对话里说「你说我 2019 年换工作,其实那年什么都没发生」→ agent 先问 / 改读;再说「我出生时间是我妈估的」→ 出现提议卡,写明题数和点数。
- 只说「这个解释我不认同」→ 不出卡,按纠正改读。
- 给家人档案问同样的话 → 一句话说明暂不支持,不出卡。
- 点卡 → 进入校正;刷新后仍记得从哪来。
- 采用一个时间、做完采用后核对 → 自动回到原对话;有分隔线「出生时间改为 …」;之前的回答上方有「按校正前的时间」;有「按新时间重新看」按钮,点了才开始回答。
- 不采用就结束 → 回到原对话,分隔线写「出生时间没有改」,没有小字。
- 校正中途点「回到刚才的对话」→ 回去,校正会话仍在侧栏可再打开。
- 侧栏里以前的校正会话都能打开,没有「回到刚才的对话」按钮。
- 同一会话拒绝过一次提议后,再说不准,agent 不再主动出卡。
6. 让步顺序
T1 + T2 + T3 + T4 是一个可感知的整体,必须同一次交付;T5 > T6。时间不够时先砍 T4 里的「手动返回按钮」(保留自动返回),不得砍「按钮点了才发」。
7. 开工前置命令
cd /workspace/Jyotisha
git status -sb
git fetch origin --prune
git log origin/staging -5 --oneline -- frontend/src/lib/rectification-agentic frontend/src/app/api/rectification # 确认 dup-adopt 单已合入
git worktree add -b codex/rectification-in-chat-step1-20261006 .worktrees/rectification-in-chat-step1-20261006 origin/staging
cd .worktrees/rectification-in-chat-step1-20261006/frontend && npm ci
./node_modules/.bin/tsc --noEmit && npm run lint && npm test 2>&1 | grep -E "^# (tests|fail)" # 基线(Node 22,Linux 优先)
grep -o "BUG-1[0-9]\{3\}" ../docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1
交付门:tsc 0 错;lint 0 error;npm test 失败名单与基线逐条比对后贴进进度记录(Windows 跑不全就写明,由 Claude 在 Linux 复核);动表则 npm run test:db;next build 后 / Static、首屏 gzip ±2%。推 codex/rectification-in-chat-step1-20261006 后回报,Claude 验收后再推 staging。