diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 02de7188..2976dc18 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -12509,3 +12509,23 @@ - 相关记录:BUG-947、BUG-942 - 复发自:BUG-947 - 修复版本:`cd4775ae` + +## BUG-954 | 申报时段咨询整轮失败:模型没调计算工具,合同判未完成 + +- 状态:investigating +- 首次发现:2026-09-18 +- 最近更新:2026-09-18 +- 影响面:`windowJyotishInstructions`、`consultationWindowPrepareStep`、`consult/route.ts` 申报时段分支、`contractReady()` +- 用户现象:staging(已部署 `9cdcf96b`)申报时段会话问「未来半年我事业如何?」,等待后 `run.failed code=runtime_contract_incomplete`,提示「Agent 未完成必要的方法与计算步骤,本次不会扣点。」公开事件只有 `run.started` → `skill.started/completed` → `activity loading-method` → `run.failed`。 +- 触发条件:`consultationMode=declared_birth_window`、`theme=general`、`history=[]` 的首轮提问(问题带应期语义)。 +- 已确认事实: + 1. 回执 `steps` 只有 `skill` 与 `runtime-contract-retry` 两条,`stepBudget.used=2`,**没有任何 tool 步骤**,`workflow.route=declared-birth-window / status=blocked`,`skill.referenceReads=0`、`methodologySections=0`。 + 2. 该形状**不是**供应商 error 块那条路:BUG-938 的修复规定 error 块会追加 `validation model-stream-error failed`、不触发合同 retry、公开码为 `calculation_failed`。本次回执无该步、有 retry、公开码是 `runtime_contract_incomplete`,因此两次 attempt 都是「模型没调工具」。 + 3. `contractReady()`(`stream-agent-response.ts:336-339`)要求 `consultationToolSuccessCount === 1`;未绿时模型正文一律丢弃,所以模型若改成直接写一段文字回答,用户端看到的就是整轮失败而不是那段文字。 + 4. `run-jyotish-window-consultation` 的 `inputSchema` 只有 `question`(`consultation-tools.ts:765-767`),出生数据全部服务端绑定——由模型决定调不调,对计算结果没有任何信息增益。 +- 初判根因(待日志确认):BUG-923 记录过「提示词是软约束,`toolChoice: "auto"` 不能保证第 0 步调用排盘工具」;BUG-937 因为某些 thinking 供应商拒收 `required`,把第 0 步改回 `activeTools` + `auto`,**等于把 BUG-923 的洞原样放回来**,且没有补第三条路。窗口指令同时写着「每轮必须先调工具」和「`can_answer_precise_timing` 恒为 false,不得给出月份 / 日期 / 大运边界」,而本次问题正是应期语义;模型在冲突下选择不调工具的可能性最大。 +- 待取证据:staging 该 `requestId` 的服务端日志(`modelFinishReason`、是否有 `[consult-provider-error]`、模型是否产出过被丢弃的正文)、本次使用的模型 id 对应的供应商。没有这条日志之前不得把根因写成 resolved。 +- 关联记录:BUG-205、BUG-214、BUG-922、BUG-923、BUG-937、BUG-938 +- 复发自:BUG-923(`auto` 不保证调用这一条在 BUG-937 修复后回归) +- 修复版本:待发布 + diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 8ea72972..198c0140 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -138,6 +138,7 @@ | `TASK-consult-three-channels-20260918.md` | `PROGRESS-consult-three-channels-20260918.md` | 咨询运行时三通道:进度 / 思考 / 正文生成时分离。设计分支误用 BUG-939/940/941,落地改号 942–944 | 验收未通过(见修复单) | `94c1e81f` | | `TASK-consult-three-channels-fix-20260918.md` | `PROGRESS-consult-three-channels-fix-20260918.md` | 验收修复单:领域上限解耦(945)、31s 预算断言(946)、校正思考分片门(947)、Pass 4 按模式分流(948)、容量算术(949) | 已验收通过(Claude:tsc 0 / lint 0 error / npm test 3491 条 31 红且与基线 `742ffbc6` 同一组、原 12 条红全绿 / `/` 仍 Static / 首屏 js gzip 468,388 B 较基线 +0.02%);review 另出 BUG-950~953 见下一行 | `e32ce624` | | `TASK-consult-pass4-streaming-20260918.md` | `PROGRESS-consult-pass4-streaming-20260918.md` | 验收 review 三轮:Pass 4 一 hold 正文就整段蹦出、逐字流式消失(BUG-950 产品拍板按句放行);无出生分钟模式整段被一句拒绝顶掉、一般知识句一起丢(951 改按句丢弃);该模式下日期不留痕(952);校正流 token 级 thinking 是死链,按 P2 删除并把测试翻转成否定合同(953)。基线 `1e553976` | 待验收 | `cd4775ae` | +| `TASK-window-consult-contract-20260918.md` | — | **P0 线上**:申报时段问「未来半年我事业如何」整轮 `runtime_contract_incomplete`,回执零 tool 步(BUG-954)。根因链 922→923→937:`required` 被供应商拒收后改回 `auto`,「模型可以不调工具」的洞回归;而窗口工具入参只有 question,计算本就不需要模型触发 → 改服务端预跑。另含合同未绿不得静默丢正文(955)、窗口指令应期冲突(956)。基线 `9cdcf96b` | 待领取 | — | | `TASK-first-paint-dead-screen-fallback-20260917.md` | — | 真机:首页永远停在「正在载入账户」,兜底全在没跑起来的 bundle 里(BUG-936 investigating)。根 layout 加与 bundle 无关的内联兜底 + 去掉本仓正则后行断言 | 待领取 | — | | `TASK-consultation-answer-start-anchor-20260917.md` | `PROGRESS-consultation-answer-start-anchor-20260917.md` | 主会话回答落在结尾:`useConversationScrollAnchor` 是贴底跟随,流式期间视口钉在最后一个字,回答开头滚出视口;改为发送后问题钉顶、回答向下长、长出视口显示「跳到最新」、末尾动态留白;产品追加拍板:校正面同一语义(推翻 BUG-041/048 贴底),本轮开头 = 用户行或新助手行。BUG 段 930 起 | 已验收(经修复单) | `worktree/green-harbor-5be3` | | `TASK-consultation-answer-start-anchor-fix-20260917.md` | `PROGRESS-consultation-answer-start-anchor-fix-20260917.md` | 验收修复单:F1 头就是留白行时留白按整视口算(BUG-931);F2 留白只在钉住期间存在(BUG-932);前置:先修 e4e73f56 的两处 TS 错否则门禁不过 | 已验收 | `cc1a8980`(Claude 验收:tsc 0 / lint 0 error / npm test 3457 条 39 红与 11c0028d 逐条一致、新增 2 条绿 / `next build --webpack` 通过、`/` Static、首屏 gzip 591,242(较 09-16 基线 582,800 +1.45%,含会话列表单)/ Chrome 真实布局 S1–S6 全部通过,S6 新助手行距顶 16px 且增高不动,S5 不再写留白);真机六条欠 | diff --git a/docs/tasks/TASK-window-consult-contract-20260918.md b/docs/tasks/TASK-window-consult-contract-20260918.md new file mode 100644 index 00000000..da455d34 --- /dev/null +++ b/docs/tasks/TASK-window-consult-contract-20260918.md @@ -0,0 +1,82 @@ +# TASK · 申报时段咨询不再靠模型触发计算(2026-09-18 第四轮) + +> 基线:`origin/staging` @ `9cdcf96b`(已部署,健康检查一致)。 +> 事故记录:`docs/BUG_HISTORY.md` BUG-954(investigating)。 +> BUG 编号起点:开工时最大号为 **BUG-954**,本单占 **BUG-954(转 resolved)~ BUG-956**。 + +## 0. 事故实证 + +staging 真实一轮(`consultationMode=declared_birth_window`、`theme=general`、问题「未来半年我事业如何?」、`history=[]`): + +``` +run.started → skill.started/completed → activity(loading-method) → run.failed +code = runtime_contract_incomplete +receipt.steps = [skill, runtime-contract-retry] ← 没有任何 tool 步 +receipt.stepBudget = {planned: 11, used: 2} +receipt.workflow = {route: declared-birth-window, status: blocked, missingLayers: [birth-minute]} +receipt.skill = {loaded: true, referenceReads: 0, methodologySections: 0} +``` + +排除项:这不是供应商 error 块那条路。BUG-938 的修复规定 error 块会写 `validation model-stream-error failed`、**不**触发合同 retry、公开码为 `calculation_failed`。本次三项全不符,所以两次 attempt 都是**模型没有调用 `run-jyotish-window-consultation`**。 + +## 1. 根因链(为什么「修过了还犯」) + +| 轮次 | 做了什么 | 留下什么 | +| --- | --- | --- | +| BUG-922 | 提示词改成「每一轮都必须先调工具」 | 提示词是软约束 | +| BUG-923 | 第 0 步 `toolChoice: "required"` | 记录里写明「`auto` 不能保证第 0 步调用」 | +| BUG-937 | 某些 thinking 供应商拒收 `required`,改回 `activeTools` + `auto` | **把 BUG-923 的洞原样放回来**,没有补第三条路 | +| 本轮 | —— | 模型不调工具 → 合同未绿 → 正文被丢弃 → 整轮失败 | + +叠加因素:`windowJyotishInstructions`(`src/mastra/index.ts:176-190`)同时写着「每轮必须先调工具」和「`can_answer_precise_timing` 恒为 false,不得给出月份 / 日期 / 大运边界」。本次问题(未来半年事业)正是应期语义,模型在两条指令冲突下选择「我答不了、不调工具」是最可能的路径;它若写了解释文字,也会因为 `contractReady()` 未绿被整段丢弃(`stream-agent-response.ts:336-339`)。 + +**结构缺口一句话**:`run-jyotish-window-consultation` 的 `inputSchema` 只有 `question`(`consultation-tools.ts:765-767`),出生数据完全服务端绑定——**让模型决定调不调,对计算结果没有任何信息增益,只增加一条失败路径。** 本命路线同理。 + +## 2. 决策记录(待产品确认,实现前必须回填「已授权」) + +产品诉求(2026-09-18):只知道出生范围的用户必须能正常使用产品。本单把「模型触发计算」改成「服务端预跑计算」,模型只负责写作。这**推翻**了 BUG-922/923/937 那条「靠提示词 + toolChoice 让模型调工具」的路线,但不推翻它们的目的(每轮必须有本请求内的真实计算)。 + +## 3. BUG-954(P0)服务端预跑,模型不再负责触发计算 + +1. 申报时段路线在进入模型循环**之前**,服务端直接执行窗口计算(现有 `createWindowConsultationTools` 的 `execute` 主体抽成可独立调用的函数),把 evidence packet 作为服务端消息注入上下文。 +2. `consultationToolSuccessCount` 由这次服务端执行记账,`contractReady()` 因此在模型开口前就已绿;工具仍然留在 Agent 上(模型想再调一次,命中同一请求内缓存,不重复计算)。 +3. 合同 retry 那条路保留,但它现在只可能因为「服务端计算失败」触发,不再因为「模型没调」触发。 +4. 验收标准: + - 新增合同测试——申报时段路线在模型**一次工具都不调**的假流下,仍然产出回答、`run.failed` 不出现、回执里有 `run-jyotish-window-consultation` 的 completed 步; + - 现有 `consultationWindowPrepareStep` 的两条断言按新口径改写(三栏说明); + - 不得再出现 `toolChoice: "required"` 或 named toolChoice(BUG-937 防复发条款仍然有效)。 + +## 4. BUG-955(P1)合同未绿时不得静默丢弃模型正文 + +`stream-agent-response.ts` 现在在合同未绿时丢弃全部正文,用户只看到「未完成」。即便 BUG-954 落地,这条兜底仍要有: + +1. 两次 attempt 后合同仍未绿、但模型产出过非空正文时,不再整轮失败;改为交付该正文并在回执追加 `validation contract-degraded failed`,同时在正文末尾附一句服务端确定性说明(文案对照 `frontend/docs/VOICE.md`,不得由模型生成)。 +2. 模型既没调工具也没写字时,维持现有 `runtime_contract_incomplete`(不扣点)。 +3. 验收标准:假流「无工具 + 有正文」→ 用户拿到正文 + 降级说明;假流「无工具 + 无正文」→ 仍是 `runtime_contract_incomplete`。 + +## 5. BUG-956(P2)窗口指令里「必须调工具」与「不得给应期」的冲突要写清 + +`src/mastra/index.ts:178-190` 补一句显式口径:应期类问题**仍然要先调工具**,再用稳定层给方向性回答,并说明哪部分需要出生分钟;不得因为「精确应期不可用」而跳过计算或拒答整题。验收:`consultation-workflow-contract.test.ts` 加一条源码断言。 + +## 6. 待取证据(不阻塞开工) + +staging 该 `requestId=95de38fd-…` 的服务端日志:`modelFinishReason`、有无 `[consult-provider-error]`、模型是否产出过被丢弃的正文、本次模型 id 的供应商。拿到后回填 BUG-954 的根因段并转 `resolved`;拿不到就保持 `investigating`,但 §3 的结构修法与日志结论无关,可以先做。 + +## 7. 硬红线 + +1. `tsc --noEmit` 0 错、`npm run lint` 0 error、`npm test` 失败数不超过基线 `9cdcf96b` 实测的 31 条且清单逐条一致;测试总数不低于 3495。 +2. 不得放宽 `contractReady()` 的「每请求一次真实计算」语义——本单是把计算**提前到服务端**,不是取消它。 +3. 不得再写 `toolChoice: "required"` / named toolChoice。 +4. 改既有断言写「原值 / 新值 / 原因」三栏。 + +## 8. 开工前置 + +```bash +git fetch origin --prune +git worktree add -b codex/window-consult-contract-20260918 \ + .worktrees/window-consult-contract-20260918 origin/staging +cd .worktrees/window-consult-contract-20260918/frontend +npm test 2>&1 | grep -E "^# (tests|pass|fail)" # 开工基线:tests 3495 / pass 3449 / fail 31 +``` + +收工:`docs/tasks/PROGRESS-window-consult-contract-20260918.md` + `docs/BUG_HISTORY.md`(954 转 resolved 或保持 investigating,955/956 新增)+ `CHANGELOG.md`,与代码同一批推 `staging`。