Files
Jyotisha/docs/tasks/TASK-report-section-writer-failure-20260902.md
T
Jesse_ChenandClaude Fable 5.1 8db71aaf81 docs: product-level README, AGENTS.md split into code/reading parts, add CLAUDE.md, move task briefs to docs/tasks
- README.md is now the product/repo front door (architecture, repo map,
  local dev, test tiers, delivery flow, doc map). Engine positioning,
  VedAstro/Codex setup and the oracle/benchmark command reference move
  verbatim to docs/engine/README.md, docs/engine/vedastro-gateway.md and
  docs/benchmark/README.md. Capability badges realigned with the registry
  (91/78/8/0); tests/test_readme_badges.py was red on staging.
- AGENTS.md: Part A (environment truth, delivery, worktrees, record
  placement, bug workflow, growth freeze, frontend red lines, privacy,
  pre-work check, test tiers) and Part B (reading-rigor constraints).
  GitHub issue-tracker/triage boilerplate removed: GitHub is a read-only
  mirror. All strings locked by tests/ are preserved.
- CLAUDE.md added: roles, three working modes, task-brief sections,
  acceptance criteria, session discipline; imports AGENTS.md.
- 50 tracked TASK-*/PROGRESS-* files and 3 never-committed briefs move to
  docs/tasks/ with an index; REPO_LAYOUT.md merged into README.

Docs-only change (no gated path touched).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
2026-09-03 06:56:06 +00:00

147 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 任务书 · 个人报告写作阶段 report_schema_invalid 修复(2026-09-02
基线:`origin/staging` 最新(开工时 `git fetch` 后以 `origin/staging` HEAD 为准;本任务书出稿时为 `d9404976` 之后)。
## 事故实证
blocked-repairs`7faf8555`)部署后,2026-09-02 21:19UTC 13:19requestId `77b79067-bd70-465e-ac23-adc4500d4738`)真实生成 personal_fullcareer/marriage/wealth/timing**failedfailureCode = `report_schema_invalid`**。历史同码失败另有 4 次(08-30 三次、09-02 05:10 一次)。
**证据链已收窄到模型写作路径**:委托方已在本地对最新 staging 代码做过确定性全管线复现——真实引擎四主题响应 → `buildReportEvidenceBundleV2`4 claim cards / 0 blocked)→ `generatePersonalReport`(注入 schema 合规的确定性假 writer + 内存 sectionService)→ **PIPELINE-READY4 章节装配、guard、最终解析全部通过**。即 bundle 校验、plan 校验、按章过滤(`filterReportEvidenceBundleForSection` 的 finalize 校验)、装配与文档合同都没有确定性缺陷;失败只可能来自真实模型输出未通过校验的路径:每章两次尝试均失败 → 章节 blocked → 四章全 blocked → `all_sections_blocked`,或摘要生成失败。
## 两个候选根因(按嫌疑排序,任务 0 定案)
**主嫌 · 输出预算的中文 token 口径反了。**
`frontend/src/lib/personal-report-generation.ts`
```ts
const SECTION_OUTPUT_TOKEN_BUDGET = 3072;
const SUMMARY_OUTPUT_TOKEN_BUDGET = 1536;
// 每章:
maxOutputTokens: Math.min(3072, Math.max(1024, Math.ceil(entry.targetCharacters.max / 2)))
```
standard 深度 `targetCharacters.max = 1200`(中文字符)→ 每章预算 `max(1024, 600) = 1024` tokens。`/2` 隐含"2 字符 ≈ 1 token",这是英文口径;**中文约 1 字 ≈ 1 token 或更多**。writer 被系统提示与 plan 要求写 2601200 字中文 narrative + title + actions + caveats + evidenceRefs 的完整 JSON,写入目标区间上半段必然超 1024 → `finishReason: "length"` → JSON 截断 → schema 校验失败 → 修复重试**预算不变**再截断 → 耗尽 2 次 → 章节 blocked。四章同构 → `all_sections_blocked`。摘要同理:schema 允许 `summary` 2000 字符中文,1536 预算同样可截断。
(这正是 `TASK-report-sectioned-generation-20260830.md` 红线"输出上限取值要用数据决定,不许拍脑袋"要防的事故。)
**次嫌 · evidenceRefs 精确回显失败。**
`assertWriterOutput` 要求 `candidate.evidenceRefs``entry.evidenceRefs` **集合相等**。按章过滤后的 bundle `evidenceRefs` = `entry.evidenceRefs 全局解读层 refs`yoga/functional 回执随章下发),模型若照抄 bundle 的 refs 而不是 plan entry 的 refs → mismatch → 失败。修复重试的提示词只说"未通过校验",不说差在哪。
## 决策记录
- 产品方向不变:writer 输出 schema 不放宽;`assertWriterOutput` 的 id/theme/evidenceRefs 精确匹配语义不改(服务器主权);章节串行不变。
- 授权:按任务 0 实测数据调整输出 token 预算公式与 repair 提示词;在报告失败记录/详情中透出失败细节(仅错误码级别,不含内容)。
## 硬红线
1. **任务 0 是门控。** 必须先从 staging 拿到 21:19 这次失败的实证(telemetry `finishReason` 分布 + `personal_report_sections.last_error_code`),确认主嫌/次嫌,再改数值。**预算新值必须由数据 + 中文 token 口径推导,在 PROGRESS 写出推导过程**,不得再拍脑袋。
2. writer 输出 schema 只可收紧;`assertWriterOutput` 语义不改(允许把失败原因喂给 repair 提示词,不允许放松校验)。
3. 隐私红线延续:日志/PROGRESS 只引用错误码、token 数、finishReason;不得摘录 prompt、bundle、模型输出、用户资料。
4. 不改数据库结构(`personal_report_sections` 已有 `last_error_code`,透出走应用层)。
5. 上两轮任务书的其余红线继续有效(隔离、assertionLevel、最低证据组、串行、`./node_modules/.bin/tsc` 而非 `npx tsc`、不改 `.gitea/workflows/**`、不提升 main)。
## 开工前置
```bash
git fetch origin --prune
git worktree add -b codex/report-writer-failure-20260902 \
../.worktrees/report-writer-failure-20260902 origin/staging
```
`pre_work_error_ledger.md``frontend/AGENTS.md``docs/BUG_HISTORY.md` 检索(本轮与 8-30 sectioned 轮的任务 0 观测、BUG-486 系列相邻)。
**先读**`personal-report-generation.ts``generateSectionedPersonalReport`(预算、重试、blocked 判定)、`src/mastra/personal-report.ts``runStructured` / `REPAIR_PROMPT_SUFFIX` / telemetry、`personal-report-worker-core.ts` 的失败映射、`PROGRESS-report-sectioned-20260830.md` 的任务 0 数据。
---
## 任务 0P0,门控)· 用 staging 实证定案
### 做法
执行环境有 staging 部署访问权,取证 requestId `77b79067-bd70-465e-ac23-adc4500d4738`
1. 服务器日志中该时段的 `[personal-report-agent]` telemetry 行:每次尝试的 `outcome` / `finishReason` / `outputTokens`,与 `[personal-report] generation_failed``innerReason`
2. `personal_report_sections` 表该 requestId 的行:`section_id` / `status` / `attempt_count` / `last_error_code`
3. 汇总成表写进 PROGRESS(只有错误码与数值)。
### 判定
- `finishReason: "length"` 占多数 / `last_error_code = section_output_truncated` → 主嫌成立,任务 1 为主修。
- `section_output_invalid` 且 telemetry `finishReason: "stop"` → 次嫌成立,任务 2 为主修。
- 两者混合 → 两任务都做(大概率如此,都在本轮范围内)。
- 若实证指向第三种原因(如 summary 失败、worker 层指纹错配),停下,把数据写进 PROGRESS/BLOCKED 再定方案,不得按本任务书猜测硬改。
---
## 任务 1(P0)· 输出预算按中文口径重算
### 做法
- 每章预算改为以中文口径推导:narrative 上限 `targetCharacters.max` 字 ≈ `max × 1.3` tokens,再加 title/actions/caveats/refs 与 JSON 结构余量(按任务 0 实测 outputTokens 校准),向上取整后 cap 在 `SECTION_OUTPUT_TOKEN_BUDGET`3072;若实测 research 深度需要更高,单独说明再调)。**禁止再出现"字符 ÷ 2"式换算**。
- 摘要预算同口径重算(schema 允许 headline 200 + summary 2000 + priorities1536 明显不足)。
- 修复重试遇 `finishReason: "length"` 时,repair 提示词明确要求压缩篇幅至 `targetCharacters.min` 附近,且重试预算不得低于首次。
- telemetry 已记 `finishReason` 与 tokens,保持。
### 验收
- 单元测试锁新预算公式(各深度的每章/摘要预算值表)。
- staging 部署后真实生成一份 standard personal_full:四章有正文;telemetry 无 `length`PROGRESS 给每章实测 `outputTokens` 与预算余量。
---
## 任务 2P1)· evidenceRefs 回显加固
### 做法
- `sectionPrompt` 明确一句:`evidenceRefs 必须逐字复制 plan.evidenceRefs 数组,不得增删`(模型已能看到 plan entry)。
- 校验失败时把**失败类别**identity mismatch / refs mismatch / schema invalid)拼进 repair 提示词(只有类别词,不带内容),让第二次尝试有的放矢。
- `assertWriterOutput` 语义不变。
### 验收
- 单元测试:refs mismatch 的 repair 提示词包含类别提示;断言校验语义未变(原有 mismatch 用例全保留)。
---
## 任务 3(P1)· 失败可观测透出
### 做法
- 报告失败时把 `innerReason`(如 `all_sections_blocked`)与各章 `last_error_code` 汇总透出到报告详情 API(错误码级别;`personal_report_sections` 已有数据,应用层聚合即可,不改表)。
- 报告中心失败卡片显示可读的失败摘要(如"4 个主题中 4 个写作失败:输出被截断"),替代裸码 `report_schema_invalid`
### 验收
- 单元测试 + 一次人为构造的失败在页面上可读。
---
## 任务 4(P1)· 真实验证与上两轮欠账
staging 部署后真实生成 standard personal_full(四主题):
1. 报告 ready、四章有正文、摘要非降级文案。
2. writer 输出回溯抽查 ≥3 处(叙事中的 yoga/大运/强弱表述能对回 bundle 条目)。
3. 每章实测 `inputTokens` / `outputTokens` / 墙钟,对照 `PROGRESS-report-skill-parity-20260901.md` 的 2 倍线裁决(产品已接受估算 2.28×,以本次实测为准;实测超线把数据摆进 PROGRESS,不自行裁决)。
## 不在本轮范围
- writer schema、assert 语义、最低证据组、bundle 内容的任何改动。
- 章节并发、模型更换、提示缓存策略调整。
- 数据库结构。
## 收尾
- `PROGRESS-report-writer-failure-20260902.md``docs/BUG_HISTORY.md` 条目(编号对远端确认;与 8-30 任务 0 观测、`final_parse_rejected` 历史关联)。
- 建议推送顺序:任务 0 取证先写 PROGRESS(可不推代码);任务 1–3 一次推送;任务 4 部署后补记。
- 推送后核对 `https://staging.jyotisha.chat/api/health``.deployment.gitCommit`;流水线约 20 分钟。
- 不自行提升 main。
## 交付物清单
1. 任务 0 实证表(telemetry finishReason/outputTokens + 各章 last_error_code)与定案结论
2. 新预算公式 + 推导过程 + 各深度预算表 + 单元测试
3. length 重试的压缩提示 + repair 类别提示 + 测试
4. 失败详情透出(API + 报告中心文案)+ 测试
5. 真实报告验收:四章正文、telemetry 无 length、回溯抽查 ≥3 处、实测 tokens/墙钟与 2 倍线对照
6. 全套质量门实际输出、BUG_HISTORY、BLOCKED(如有止损)、PROGRESS