日志:input-processor jyotish-skill-bound abort ×2,modelStepCount 0、 0 token、整轮 69ms,模型从未被调用。windowJyotishInstructions 从不含 BOUND_METHOD_MARKER,而 getWindowJyotishAgent 照样 attach 绑定,自 9958e00a(08-21)起申报时段每轮必败。abort 与「模型没调工具」同码, 是它藏四周的原因。初版任务书的「模型没调工具」立论已证伪并改写。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
90 lines
7.5 KiB
Markdown
90 lines
7.5 KiB
Markdown
# TASK · 申报时段咨询修复(2026-09-18 第四轮)
|
||
|
||
> 基线:`origin/staging` @ `9cdcf96b`(已部署,健康检查一致)。
|
||
> 事故记录:`docs/BUG_HISTORY.md` BUG-954(根因已由服务端日志确认)。
|
||
> BUG 编号起点:开工时最大号为 **BUG-954**,本单占 **BUG-954 ~ BUG-958**。
|
||
>
|
||
> **修订说明(2026-09-18 晚)**:本单初版按「模型没调工具」立论,服务端日志已证伪。根因是窗口 Agent 的系统提示里没有 skill 方法块,模型**从未被调用**。原 §3「服务端预跑」降级为 P2 结构性改进(BUG-957),不再是本单的 P0。
|
||
|
||
## 0. 事故实证(服务端日志)
|
||
|
||
staging 真实一轮(`consultationMode=declared_birth_window`、`theme=general`、问题「未来半年我事业如何?」、`history=[]`),`docker logs jyotisha-staging-web-1`:
|
||
|
||
```
|
||
[WORKFLOW] Error executing step ...input-processor.step.processor:jyotish-skill-bound:
|
||
Error: Jyotish skill method is not bound into the system prompt for jyotish-vedic-astrology ← 出现两次(两次 attempt)
|
||
[agent-observability] toolCalls: [] modelStepCount: 0 inputTokens: 0 outputTokens: 0
|
||
run.total durationMs: 69 retryCount: 1 errorCode: runtime_contract_incomplete
|
||
```
|
||
|
||
**整轮 69 毫秒,模型一次都没被调用,0 token。** 公开事件里的 `skill.started/completed` 是绑定步骤自己报的,不代表方法块真的进了提示。
|
||
|
||
## 1. 根因
|
||
|
||
| 位置 | 事实 |
|
||
| --- | --- |
|
||
| `skill-binding.ts:143-153` | `jyotishSkillBoundProcessor` 在输入处理阶段断言系统提示含 `BOUND_METHOD_MARKER` = `<jyotish-skill name="jyotish-vedic-astrology">`,缺失即 `abort()` |
|
||
| `index.ts:21` | `${jyotishSkillMethodBlock}` **只**插值进 `jyotishInstructions`(本命) |
|
||
| `index.ts:176-190` | `windowJyotishInstructions` 从来没有这个块 |
|
||
| `index.ts:198` | `getWindowJyotishAgent` 照样 `...jyotishSkillBinding()` |
|
||
|
||
时间线:处理器 `d04fc30b`(08-18)引入 → 窗口 Agent `9958e00a`(08-21)新建时就带绑定、不带方法块 → **申报时段路线自 2026-08-21 起每轮必败,已持续约四周**。
|
||
|
||
为什么四周没人发现:`abort()` 被翻成与「模型没调工具」同一个公开码 `runtime_contract_incomplete`。BUG-922/923/937 处理的正是同名现象,本命线被救活后现象消失一半,窗口线这条根因从未被触及。
|
||
|
||
## 2. BUG-954(P0)窗口 Agent 必须绑定方法块
|
||
|
||
1. `windowJyotishInstructions` 注入带 marker 的方法块。**不能直接照抄 `jyotishSkillMethodBlock`**:它带本命 Level 2 报告骨架与「六步宫位 / Yoga 表 / 技法审计表」的输出要求,与窗口口径(无精确应期、只报稳定层、变动层列可能性)冲突。做法二选一,在进度记录里写明选了哪条:
|
||
- (a) 抽出 `jyotishSkillMethodBlock` 的「方法主体 + marker」部分,报告骨架段落作为可选后缀,本命加、窗口不加;
|
||
- (b) 窗口保留完整方法块,紧随其后用窗口口径逐条覆盖(明确写「以下窗口约束优先于上面方法块里的报告骨架与应期表述」)。
|
||
2. 验收标准:
|
||
- 新增源码合同测试——**凡 attach `jyotishSkillBinding()` 的 Agent,其 `instructions` 必须包含 `BOUND_METHOD_MARKER`**(遍历 `index.ts` 里的 Agent 工厂,不是逐个手写断言);
|
||
- 新增运行测试——构造窗口 Agent 的输入处理,`jyotishSkillBoundProcessor` 不 abort;
|
||
- 部署后在 staging 真发一轮申报时段提问,`/api/health` SHA 对得上,回执里有 `run-jyotish-window-consultation` 的 completed 步。
|
||
|
||
## 3. BUG-955(P1)处理器 abort 不得与「合同未完成」同码
|
||
|
||
`abort()` 目前落到与「模型没调工具」相同的公开码,是这条 bug 藏四周的直接原因。
|
||
|
||
1. 输入处理器 abort 走独立内部码(例如 `skill_binding_failed`),回执追加 `validation skill-binding-abort failed`,服务端打 `[consult-binding-error]` 日志(requestId + 内部码,不含提示词原文)。
|
||
2. 公开层可以继续用现有枚举,但**回执必须能一眼区分**「装配失败」与「模型没调工具」。
|
||
3. 参照 BUG-938 对供应商 error 块的同类处置。
|
||
4. 验收:假流两条——装配失败 / 模型没调工具,回执步骤名不同;`agent-observability` 两个码都能落日志。
|
||
|
||
## 4. BUG-956(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-957(P2)结构性改进:计算不该由模型触发
|
||
|
||
本单初版的主修法,现在降级为独立改进项,与 BUG-954 无因果关系,但值得单独做:`run-jyotish-window-consultation` 的 `inputSchema` 只有 `question`(`consultation-tools.ts:765-767`),出生数据完全服务端绑定——由模型决定调不调,对计算结果没有信息增益,只多一条失败路径(BUG-205/214/922/923/937 都在这条链上)。建议服务端在模型循环前预跑计算并记账,工具仍留在 Agent 上命中同请求缓存。**不要与 BUG-954 同轮做**:先让窗口路线活过来并在真实环境验证,再动运行时结构。
|
||
|
||
## 6. BUG-958(P3)窗口指令里「必须调工具」与「不得给应期」的冲突要写清
|
||
|
||
`src/mastra/index.ts:178-190` 补一句显式口径:应期类问题**仍然要先调工具**,再用稳定层给方向性回答,并说明哪部分需要出生分钟;不得因为「精确应期不可用」而跳过计算或拒答整题。验收:`consultation-workflow-contract.test.ts` 加一条源码断言。
|
||
|
||
## 7. 硬红线
|
||
|
||
1. `tsc --noEmit` 0 错、`npm run lint` 0 error、`npm test` 失败数不超过基线 `9cdcf96b` 实测的 31 条且清单逐条一致;测试总数不低于 3495。
|
||
2. 不得放宽 `contractReady()` 的「每请求一次真实计算」语义。BUG-954 是把方法块补进提示,不是绕过绑定校验;不得用「删掉 `jyotishSkillBinding()`」的方式让窗口路线通过。
|
||
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
|
||
```
|
||
|
||
让步顺序:**BUG-954 单独一轮先上**(四周不可用,越快越好,改动面是一段提示词 + 两条测试),955 同轮或紧随;956 次之;957 必须等 954 在真实环境验证通过后另开一轮;958 顺手。
|
||
|
||
收工:`docs/tasks/PROGRESS-window-consult-contract-20260918.md` + `docs/BUG_HISTORY.md`(954 按证据转 `resolved`,955~958 新增)+ `CHANGELOG.md`(申报时段咨询四周不可用属用户可感知),与代码同一批推 `staging`。
|