Files
Jyotisha/docs/tasks/TASK-report-section-writer-failure-20260902.md
T
Jesse_Chen 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

9.6 KiB
Raw Blame History

任务书 · 个人报告写作阶段 report_schema_invalid 修复(2026-09-02

基线:origin/staging 最新(开工时 git fetch 后以 origin/staging HEAD 为准;本任务书出稿时为 d9404976 之后)。

事故实证

blocked-repairs7faf8555)部署后,2026-09-02 21:19UTC 13:19requestId 77b79067-bd70-465e-ac23-adc4500d4738)真实生成 personal_fullcareer/marriage/wealth/timingfailedfailureCode = report_schema_invalid。历史同码失败另有 4 次(08-30 三次、09-02 05:10 一次)。

证据链已收窄到模型写作路径:委托方已在本地对最新 staging 代码做过确定性全管线复现——真实引擎四主题响应 → buildReportEvidenceBundleV24 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

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.evidenceRefsentry.evidenceRefs 集合相等。按章过滤后的 bundle evidenceRefs = entry.evidenceRefs 全局解读层 refsyoga/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)。

开工前置

git fetch origin --prune
git worktree add -b codex/report-writer-failure-20260902 \
  ../.worktrees/report-writer-failure-20260902 origin/staging

pre_work_error_ledger.mdfrontend/AGENTS.mddocs/BUG_HISTORY.md 检索(本轮与 8-30 sectioned 轮的任务 0 观测、BUG-486 系列相邻)。

先读personal-report-generation.tsgenerateSectionedPersonalReport(预算、重试、blocked 判定)、src/mastra/personal-report.tsrunStructured / REPAIR_PROMPT_SUFFIX / telemetry、personal-report-worker-core.ts 的失败映射、PROGRESS-report-sectioned-20260830.md 的任务 0 数据。


任务 0(P0,门控)· 用 staging 实证定案

做法

执行环境有 staging 部署访问权,取证 requestId 77b79067-bd70-465e-ac23-adc4500d4738

  1. 服务器日志中该时段的 [personal-report-agent] telemetry 行:每次尝试的 outcome / finishReason / outputTokens,与 [personal-report] generation_failedinnerReason
  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_BUDGET3072;若实测 research 深度需要更高,单独说明再调)。禁止再出现"字符 ÷ 2"式换算
  • 摘要预算同口径重算(schema 允许 headline 200 + summary 2000 + priorities1536 明显不足)。
  • 修复重试遇 finishReason: "length" 时,repair 提示词明确要求压缩篇幅至 targetCharacters.min 附近,且重试预算不得低于首次。
  • telemetry 已记 finishReason 与 tokens,保持。

验收

  • 单元测试锁新预算公式(各深度的每章/摘要预算值表)。
  • staging 部署后真实生成一份 standard personal_full:四章有正文;telemetry 无 lengthPROGRESS 给每章实测 outputTokens 与预算余量。

任务 2P1)· evidenceRefs 回显加固

做法

  • sectionPrompt 明确一句:evidenceRefs 必须逐字复制 plan.evidenceRefs 数组,不得增删(模型已能看到 plan entry)。
  • 校验失败时把失败类别identity mismatch / refs mismatch / schema invalid)拼进 repair 提示词(只有类别词,不带内容),让第二次尝试有的放矢。
  • assertWriterOutput 语义不变。

验收

  • 单元测试:refs mismatch 的 repair 提示词包含类别提示;断言校验语义未变(原有 mismatch 用例全保留)。

任务 3P1)· 失败可观测透出

做法

  • 报告失败时把 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.mddocs/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