From fcad06372e6ed4ad65d1639c6b22d56fba74d013 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Sun, 20 Sep 2026 10:19:18 +0800 Subject: [PATCH] =?UTF-8?q?docs(tasks):=20=E6=99=AE=E9=80=9A=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E5=AF=92=E6=9A=84=E8=BD=AE=E5=BF=AB=E9=80=9F=E9=80=9A?= =?UTF-8?q?=E9=81=93=E4=BB=BB=E5=8A=A1=E4=B9=A6=EF=BC=88BUG-976/977?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 一句「你好」走完整窗口排盘 + 400 字判词 + 扣 1 点。三层实证与不扣点 通道缺口写进任务书;产品拍板不扣点不排盘回一句,分流禁用正则, BUG-922/923 的每轮必调合同不动。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8 --- docs/tasks/README.md | 1 + ...ASK-consult-smalltalk-fastpath-20260920.md | 120 ++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 docs/tasks/TASK-consult-smalltalk-fastpath-20260920.md diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 067953cb..62c7fa84 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -289,6 +289,7 @@ | — (产品口头拍板,无任务书) | `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 error(118 warning 不变)/ npm test 3468→3471 条、36 红与基线 `ff0427cf` 逐条相同、0 新红 / `○ /` Static / 首屏 gzip 1,416,965 两侧**字节相同**(改动全在服务端模块,客户端 chunk 无该文本)。真机口气走查欠 | | — (产品口头拍板,无任务书) | `PROGRESS-settings-ui-20260919.md` | **设置面板布局与资料入口整理**:基线 `4f4cd684`;四分区继续共用固定 `.settings-modal`,桌面导航 176px→200px 并加分隔,内容区增加内边距,表单 cap 440px→560px,导航移除误导性右箭头;账户头像 48px→56px;“添加其他人”移到分组标题操作区。已同步 `frontend/DESIGN.md` 与合同测试;tsc 0、lint 0 error、定向测试 21/21;build 被 Windows Skill runtime symlink 权限阻塞,浏览器走查待受控环境;BUG-970 保持 `investigating` | 待验收 | `codex/settings-ui-20260919` | | `TASK-owner-case-purge-20260919.md` | `PROGRESS-owner-case-purge-20260919.md` | **上游库主案例与本机路径残留清除(只含本仓)**:镜像同步带进库主本人案例(敏感案例标识与本机路径)并被 `origin/staging` 命中,涉及无引用的 `versions/` 三快照、前端 fixture、Python 测试、整机扫描台账、会话转录及上游 SKILL 快照。运行时无特判不用动。产品拍板整体删除不留匿名版;fixture 统一虚构常量;`import_yinduzhanxing.py` 加隐私排除项 + 新增仓库级隐私守卫测试;上游 SKILL 快照等库主清完再重导入(BLOCKED 记录)。上游仓的清理指令另见 `UPSTREAM-INSTRUCTION-owner-case-purge-20260919.md`(交给库主,不在本仓执行)。BUG-972/973 | **已验收通过(2026-09-20,`497798ac`);未合入 staging,等产品放行** | 两轮:首轮未通过(1 个隐私守卫冲突:新增路径规则与答案键守卫冲突,该守卫在快速门 glob 内,合入会让门禁红)→ 修复单 `TASK-owner-case-purge-fix2-20260919.md` → `497798ac` 通过。Claude 在 Linux 全依赖环境独立复验:快速门 Python 步 859 passed / 0 failed(上一轮就是这步红),Python 全量 63 红与基线逐条相同、0 新红,收集数与删除清单已记录;tsc 0 / lint 0 error、120 warning 同基线 / `npm test` 失败清单为基线子集(少 1 条工作流 YAML,非回归)/ `○ /` Static、首屏 gzip 与基线字节相同(前端自首轮提交起零改动)。上游指令文件已逐字节还原为基线原文;12 个已删测试名与计数已落进度记录;BLOCKED 两条已划掉。校正 Skill 哈希包、注册表、上游快照、`frontend/src`、主 API 全程 0 改动;库主本机用户名 0 命中。遗留 P3:进度记录和 BUG 状态文字待后续对账修正 | +| `TASK-consult-smalltalk-fastpath-20260920.md` | — | **普通对话寒暄轮快速通道**:真机一句「你好」触发完整窗口排盘(活动面板「已完成 4 步」)+ `## 先回答你的问题` + 400 字判词 + 扣 1 点。三层叠加:`index.ts` 两处「every turn 必调排盘」(BUG-922)与 `contractReady()` 的 `requireTool` 把排盘变成硬合同;`product-voice.ts` OPENER SHAPE 标「三种模式共用」;唯一的 chit-chat 豁免句只在 `natalSpokenReportContract` 里、只拼进本命 Agent(BUG-977)。计费侧「写回复」与「扣点」绑在 `complete_consultation_response` 同一次调用,`cancel` 只退款不写消息,所以今天没有「不扣点但保留对话」的通道。**产品拍板 a:不扣点、不排盘、回一句白话**;**分流不得用正则/关键词/长度阈值**,改为进 Agent 之前一次极短的结构化模型调用(复用本轮已选模型),fail-open 一律落回完整路径;BUG-922/923 的三处合同一个字不改;新增 `complete_consultation_free` 迁移。BUG-976/977 | 待领取 | — | ## 命名与归档 diff --git a/docs/tasks/TASK-consult-smalltalk-fastpath-20260920.md b/docs/tasks/TASK-consult-smalltalk-fastpath-20260920.md new file mode 100644 index 00000000..3cdc3992 --- /dev/null +++ b/docs/tasks/TASK-consult-smalltalk-fastpath-20260920.md @@ -0,0 +1,120 @@ +# TASK · 普通对话寒暄轮不再走全量排盘(快速通道) + +- 基线 commit:`origin/staging` `09b41009`(2026-09-20 fetch) +- 分支:`codex/consult-smalltalk-fastpath-20260920` +- 工作树:`.worktrees/consult-smalltalk-fastpath-20260920` +- BUG 编号起点:本单成文时 `docs/BUG_HISTORY.md` 最大号 **975**,本单用 **BUG-976 / BUG-977**(开工时重新核对最大号,若已被占用顺延) +- 串行:与 BUG-937/938(`8b11ae7d`,已在 staging)无冲突。**同期若有别的单改 `frontend/src/app/api/consult/route.ts` 或 `frontend/src/mastra/index.ts`,必须串行,本单在后。** + +--- + +## 1. 事故实证 + +真机(申报出生窗口账号,普通对话会话):用户整轮只发两个字「你好」。 + +返回的是:活动面板「已完成 4 步」→ `## 先回答你的问题` → 约 400 字的完整判词(「上亮下软」的反差 → 谁在推谁在修 → 别去应白羊的「抢」去扮演土星的「守」→ 三条短行动)→ 结尾「要落到具体年份和月份,还需要一个出生分钟」。并按一轮咨询扣 1 点。 + +这不是模型发挥失常,三层都是我们自己写死的(行号随代码漂移,**以符号名定位**): + +| # | 位置(符号) | 事实 | +| --- | --- | --- | +| 1 | `frontend/src/mastra/index.ts` · `windowJyotishInstructions` | 「Call run-jyotish-window-consultation before answering **every turn, including short follow-ups, clarifications, and complaints**」。`jyotishInstructions`(本命)同款 | +| 2 | `frontend/src/lib/stream-agent-response.ts` · `contractReady()` | `requireTool` 为真时要求本轮**恰好一次成功排盘调用**,否则整轮 `runtime_contract_incomplete`、正文被丢弃。`route.ts` 窗口与本命两条流都是 `requireTool: true` | +| 3 | `frontend/src/mastra/product-voice.ts` · `productConversationVoice` | OPENER SHAPE 明写「natal / general / declared-window **三种模式共用**」「开场不是一句判词,是一个形状」,四步强制 | +| 4 | `frontend/src/mastra/product-voice.ts` · `natalSpokenReportContract` 末行 | 唯一的豁免句「Short chit-chat that is not a natal domain claim may stay conversational」**只存在于这个常量**,而它只拼进 `getJyotishAgent`;`getWindowJyotishAgent` / `getGeneralJyotishAgent` 拿不到 → **BUG-977** | +| 5 | `frontend/src/lib/consultation-thinking-plan.ts` · `consultationSpokenHeadingRule("window")` | 窗口模式「描述稳定层就用 `## 先回答你的问题` 开头」,所以标题也是我们要求的 | +| 6 | `frontend/supabase/migrations/20260717000000_consultation_request_lifecycle.sql` · `begin_consultation_credit` | 预留即 `credits - 1`;`cancel_consultation_credit` 只退款、**不写回复消息** | +| 7 | `frontend/supabase/migrations/20260808030000_consultation_stream_recovery.sql` · `complete_consultation_response` | 「把回复写进 `chat_sessions.messages`」与「`complete_usage` 结算扣点」绑在同一次调用 → **今天没有「不扣点但保留对话」的通道** | + +## 2. 根因 + +产品从来没有定义过「非咨询轮」这一类。系统把每一条用户消息都当成一次咨询:先预留点数,再强制排盘,再按开场形状说话。09-17 的语气改造(`84b293fb`)与 BUG-922/923 的「每轮必调排盘」两边单看都对,叠在一起就让一句「你好」花掉一次完整窗口排盘、几十秒和 1 个点。 + +## 3. 决策记录(产品负责人 2026-09-20 口头拍板) + +- **D1**:纯寒暄轮 **不扣点、不排盘、回一句白话**(在 a / b / c 三选中选 a)。 +- **D2**:**分流判定不得用关键词表、正则、字数阈值**(产品原话「别整什么正则」)。判定由一次极短的结构化模型调用做出。 +- **D3**:该调用**复用本轮已选模型**,不引入便宜模型目录概念,沿用 2026-09-15 性能单已拍板的「分类用贵模型」口径。输入只有本轮可见问题 + 最后一对历史,输出上限极小。 +- **D4**:分流发生在**进入 Agent 之前**。BUG-922 的「every turn」两句、BUG-923 的第 0 步 `activeTools` 收窄与 `toolChoice: "auto"`、`contractReady()` 三处**一个字不改**,避免复发。 +- **D5**:**fail-open**。分类不确定、超时、报错、输出不合 schema,一律走现有完整咨询路径。宁可多花一次,不可敷衍用户。 +- **D6**:寒暄回复由同一次调用直接产出(一次往返既分流又出话),不做固定文案池。 +- **D7**:为了「不扣点但保留对话」,**新增**一个免费完成的数据库函数;不改现有 `complete_consultation_response` 的签名与行为。 + +## 4. 硬红线 + +1. 不改 `index.ts` 两处 `every turn` 句、不改 `consultationNatalPrepareStep` / `consultationWindowPrepareStep`、不改 `contractReady()`。`consultation-birth-time-mode.test.ts` 与 `consultation-workflow-contract.test.ts` 里 BUG-922 的四条断言必须保持绿。 +2. 代码里**不得出现**寒暄关键词表、匹配寒暄的正则、按长度判定的阈值。(工具名/JSON 解析用的既有正则不在此列。) +3. fail-open 是硬要求:任何异常路径都必须落回完整咨询路径,不得静默回一句话了事。 +4. 寒暄路径**不得调用任何排盘工具**,不得发 activity 步骤,不得写 workflow receipt 或技法审计行。 +5. 寒暄路径的回复**不得包含任何盘上主张**。实现方式是**能力剥夺**——这次调用不绑 Skill、不给工具、不注入任何星盘上下文,它手里根本没有盘——不是事后检查输出。 +6. 迁移**只加函数,不动表结构**;不得放宽任何计费校验、幂等或锁。 +7. 隐私:不得把用户问题原文写进除既有 observability 字段以外的任何地方。 + +## 5. 任务分解 + +### 5.1 分流模块(新文件 `frontend/src/lib/consultation-smalltalk.ts`) + +`classifyConsultationTurn()`:输入本轮可见问题、最后一对历史、用户称呼;输出 zod `.strict()` 联合 `{ kind: "smalltalk", reply: string } | { kind: "consult" }`。超时上限 3 s,任何失败返回 `{ kind: "consult" }`。 + +`reply` 的形状(对齐 `starter-greeting.ts` 定稿口径与 `frontend/docs/VOICE.md`):一句白话,≤ 20 字,不以句号结尾,不客服腔(禁「有什么可以帮您」「很高兴为您服务」),不带星月比喻,不追问一串。 + +- 验收:单测用假模型,不打真实供应商。判 `smalltalk`:你好 / 在吗 / 谢谢 / 晚安 / 哈哈 / 我回来了。判 `consult`:「你好,帮我看看事业」/「?」/「你在说什么鬼」/「今年怎么样」/ 空白追问。异常分支:超时、非法 JSON、`reply` 为空、`reply` 超长 → 全部 `consult`。 + +### 5.2 `route.ts` 分流接线 + +在 `resolveConsultationQuestion` 之后、进入 Agent 之前调用,且**仅当** `entrypoint === undefined`(普通对话)。`daily_starlanguage`、`birth_time_rectification`、`guided_topic` 三个入口一律不进分流。命中 `smalltalk` 时走轻量流:发与现在同构的 SSE 事件(`answer.delta` + 结束事件),不发 activity、不发 receipt、不进 `streamAgentResponse` 的 `requireTool: true` 路径。 + +- 验收:源码合同测试——寒暄分支不引用任何 `run-jyotish-*`;三个入口不进分流;`requireTool: true` 的两处调用点数量不变。 + +### 5.3 免费完成(新迁移 `frontend/supabase/migrations/_consultation_free_completion.sql`) + +新增 `complete_consultation_free(p_user_id, p_request_id, p_session_id, p_response_message, p_actual_usage)`:复用与 `complete_consultation_response` 相同的 advisory lock 与参数校验;把回复 append 进 `chat_sessions.messages`;把预留的 1 点按 `cancel_consultation_credit` 同一套 `credit_transactions` 记账退回;`consultation_requests.status` 置 `completed`;`complete_usage` 仍照常记账(成本要可观测),但不扣点。幂等:已 `completed` 且 `response_message` 相同 → `success = true`。 + +- 验收:`npm run test:db --prefix frontend`(需 Docker)。**本机无 Docker 时**:迁移照写,验收降级为部署后由产品在 staging 实测——发一句「你好」,点数不变、刷新后这轮对话还在——并把缺口写进 `BLOCKED.md`,**不得写成「通过」**。 + +### 5.4 纵深防御(提示词,只是兜底不是主修) + +把 `natalSpokenReportContract` 末行的 chit-chat 豁免句搬进 `productConversationVoice`,让三种模式共用,并补一句:用户只是打招呼、道谢、告别时,回一句话,不要套开场形状。 + +- 注意:这一条**只解决「话说多」,不解决「排盘」**——`requireTool` 仍会强制排盘。主修是 5.1–5.3。 +- 验收:`consultation-voice-contract.test.ts` 新增断言——本命 / 窗口 / 无生时三种模式的指令字符串都含该豁免句;既有 OPENER SHAPE 断言保持绿。 + +### 5.5 前端 + +寒暄轮不显示思考面板与活动步骤,不显示技法证据面板,点数显示不变。 + +- 验收:`tsc --noEmit` 0;`npm run lint` 0 error;`npm test` 失败清单与基线 `09b41009` 逐条一致、0 新红;`next build` 后 `/` 仍 `○ Static`;首屏 gzip ±2%。 + +### 5.6 记录 + +- `docs/BUG_HISTORY.md`:**BUG-976**(普通对话寒暄轮被当咨询轮:全量排盘 + 400 字判词 + 扣 1 点)、**BUG-977**(chit-chat 豁免句只发给本命 Agent,窗口与无生时模式拿不到)。两条都要写明与 BUG-922 / BUG-923 的关系:不是复发,是那两单的边界之外。 +- `CHANGELOG.md`:一行「打招呼不再触发整盘计算,也不扣点」。 +- `frontend/docs/VOICE.md`:加一条「寒暄只回一句」。 + +## 6. 让步顺序 + +1. 无 Docker → 5.3 的自动化验收降级为 staging 实测清单 + `BLOCKED.md`,其余照做。 +2. 免费完成函数在评审中被判定计费一致性风险过高 → 退到「寒暄轮照常扣点但不排盘、只回一句」,**并立即回报产品**:这会推翻 D1,必须产品二次确认后才算数。 +3. 分流调用实测 P50 超过 1.5 s → 功能保留,超时上限降到 1.5 s,并在进度记录里给出实测数字。 +4. 分流准确率不满意时,**不得**加关键词表或正则来补——宁可漏判(漏判的代价是照常花一次钱,用户无感)。 +5. 误判成本口径(写进进度记录):把咨询误判成寒暄 = 用户收到一句「在,想看哪一块?」,再问一次即可;这比 BUG-922 的整轮失败轻,可接受。 + +## 7. 开工前置命令 + +```bash +git fetch origin --prune +git worktree add -b codex/consult-smalltalk-fastpath-20260920 \ + .worktrees/consult-smalltalk-fastpath-20260920 origin/staging +grep -n "## BUG-922\|## BUG-923\|## BUG-937\|## BUG-938" docs/BUG_HISTORY.md # 四条全文读完再动手 +grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | sed 's/## BUG-//' | sort -n | tail -1 # 核对最大号 +``` + +前端门禁(改完逐条跑,结果进 `PROGRESS-consult-smalltalk-fastpath-20260920.md`): + +```bash +cd frontend +./node_modules/.bin/tsc --noEmit +npm run lint +npm test +npm run build +```