docs(report): add skill-parity evidence and knowledge-pack task brief
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016P5RoqzmUQEbeC2qjAkeGr
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
# 任务书 · 个人报告内容对齐 skill 解读深度(2026-09-01)
|
||||
|
||||
基线:`origin/staging` @ `8617eb56`。
|
||||
|
||||
## 为什么要做
|
||||
|
||||
个人报告的目标体验是"像本地 Agent 调用 jyotish skill 那样解盘"。当前管道做不到,且存在一个**结构性矛盾**:
|
||||
|
||||
1. **引擎算了,管道丢了。** Python 引擎的 `/api/consultation_workflow` 响应里已经带全量 `chart`(`scripts/jyotish_api_server.py:2406` 整体透传),其中 `chart.ai_prompt_pack.evidence_snapshot`(`scripts/jyotish_engine.py` ~1700–1790 行)包含:功能吉凶星表、shadbala 排名、SAV 分值、当前 Mahadasha/Antardasha、dasa convergence top domains、`career_narrative` / `relationship_narrative` / `finance_narrative`(服务器生成的中文 headline/strengths/risks/boundaries,见 `_build_career_narrative_payload` 等,~1264 行起);`chart.modules.yoga.yogas` 有 Yoga 名单;`chart.modules.guided_topics` 有逐主题的证据化条目。**报告路径的提取层把这些全部丢弃**:`frontend/src/lib/personal-report-generation.ts` 的 `buildReportEvidencePacket`(:484)只留行星/宫位/上升、两条 Dasha 时间线、varga 宫位占据和技法执行回执。
|
||||
2. **Claim card 是回执,不是结论。** `buildReportEvidenceBundleV2`(:991,claim card 构造在 :1121–1144)产出的 `conclusion` 是模板句"服务器已闭合XX所需的最低证据组…",`supportingFacts` 是"D9 已执行并纳入本主题证据计划"。里面没有任何占星学结论。
|
||||
3. **写作端被合同锁死。** `frontend/src/mastra/personal-report.ts` 的指令与 `skills/jyotish-personal-report/SKILL.md` 都规定:claimCards 是唯一结论来源,不得从盘面自行推导含义。结论来源没有结论 → 写作模型要么输出空洞合规文本,要么悄悄违规解读原始盘面(schema 查不出来)。
|
||||
4. **写作端没有任何解读方法论。** 本地 Agent 调 skill 时能读 `references/` 下 188 个解读文件(宫位含义、现代措辞、误区纠正、判读方法);报告写作模型一个字都拿不到。
|
||||
|
||||
## 决策记录(覆盖既有默认,执行方据此放行)
|
||||
|
||||
产品负责人 2026-09-01 确认并授权本轮三件事:
|
||||
|
||||
- **(a)** 扩展 `ReportEvidenceBundleV2`,承载服务器生成的解读性事实与主题叙事种子(来源仅限引擎响应中服务器自产字段,仍走 allowlist + 净化 + 确定性校验);
|
||||
- **(b)** claim card 的 `conclusion` / `supportingFacts` 从执行回执升级为真实占星结论(内容全部由服务器确定性投影生成,不引入模型自由发挥);
|
||||
- **(c)** 为写作 agent 注入**静态、蒸馏过的**解读知识包(产品自有语料,非用户数据)。
|
||||
|
||||
**授权不包括**:放松输入隔离(聊天史、工具轨迹、raw workflow、SKILL.md 原文仍不得进写作 prompt)、放松 writer 输出 schema、改变 assertionLevel 推导与防升级校验、改数据库结构。
|
||||
|
||||
## 硬红线
|
||||
|
||||
1. **输入隔离不放松。** 写作 agent 仍然 no skills / no tools / no memory。进入 prompt 的只有:过滤后的 bundle、section plan、已完成标题、以及任务 3 的静态知识包。知识包内不得含文件路径、内部模块名、引擎/供应商名、prompt 模板或任何用户数据。
|
||||
2. **Bundle 内自由文本只许来自服务器自产生成器**(叙事种子、guided topic 文案)。用户 `question` 文本、模型历史输出一律不得写入 bundle。所有新字段必须有长度上限、数量上限与字符净化,Zod 校验保持 `.strict()`。
|
||||
3. **writer 输出 schema 只可收紧,不可放宽。** `assertionLevel` 防升级校验(`validateReportEvidenceBundleV2` 中 consensus ≥2 verified 等规则)一条不得删改。
|
||||
4. **本轮不得改数据库结构、不得新增迁移。** `personal_report_sections` / `report_document` 语义不变。
|
||||
5. **章节串行生成的既有红线延续**,不得顺手并发化。
|
||||
6. **成本必须被证明可控。** bundle 变大后,每章 prompt 必须沿用 `filterReportEvidenceBundleForSection`(`personal-report-generation.ts:2178` 附近)的过滤思路:新增的解读性事实与知识包都要按主题裁剪,只随本章下发。PROGRESS 里给改前改后的每章 `inputTokens` 对照;若 p50 超过改前 2 倍,停下摆数据,不得直接接受。
|
||||
7. **隐私红线延续**:测试与 fixture 只用虚构 smoke 出生数据;telemetry 只记指标名与数值,不得记 bundle、叙事、知识包内容。
|
||||
8. 推 staging 前 `./node_modules/.bin/tsc --noEmit` 必须通过(**不要用 `npx tsc`**,会装到空包 `tsc@2.0.4`)。不得改 `.gitea/workflows/**`,不得自行提升 main。
|
||||
9. **不得修改既有测试断言**——除非该断言锁的正是本轮要改的缺陷本身(claim card 回执文案、bundle 字段清单属于此类);改动必须在断言上方注明原值与原因,并在 PROGRESS 单列。
|
||||
|
||||
让步顺序:数据不损坏 > 隔离与真相边界不回退 > 功能与测试不回归 > 可验证的内容改进 > 成本 > 代码整洁。
|
||||
|
||||
## 开工前置
|
||||
|
||||
```bash
|
||||
git fetch origin --prune
|
||||
git worktree add -b codex/report-skill-parity-20260901 \
|
||||
../.worktrees/report-skill-parity-20260901 origin/staging
|
||||
```
|
||||
|
||||
读 `pre_work_error_ledger.md`,跑 `scripts/pre_work_check.py`,读 `frontend/AGENTS.md`,改前在 `docs/BUG_HISTORY.md` 检索同类记录。
|
||||
|
||||
**先读这几个文件再动手**:
|
||||
|
||||
- `frontend/src/lib/personal-report-generation.ts` —— `buildReportEvidencePacket`、`buildReportEvidenceBundleV2`、claim card 构造、`filterReportEvidenceBundleForSection`
|
||||
- `frontend/src/lib/report-evidence-bundle-v2.ts` —— bundle schema、canonical 序列化与 hash、防升级校验
|
||||
- `frontend/src/mastra/personal-report.ts` —— writer 指令、`sectionPrompt`、`cachedSystemMessage` 用法
|
||||
- `frontend/src/mastra/consultation-workflow.ts` —— workflow 响应契约(`chart` 为 passthrough record)
|
||||
- `scripts/jyotish_engine.py` ~1264–1830 —— 叙事种子生成器与 `_build_ai_prompt_pack`
|
||||
- `skills/jyotish-personal-report/SKILL.md` —— 报告 skill 合同(任务 3 需同步更新)
|
||||
|
||||
---
|
||||
|
||||
## 任务 0(P0,门控)· 固定引擎响应契约快照
|
||||
|
||||
### 做法
|
||||
|
||||
- 用**虚构 smoke 出生数据**真实调用一次本地 Python API `/api/consultation_workflow`(每个报告主题各一次即可),把响应中后续任务依赖的字段**存在性与类型**(只记 key 路径和类型,不记值)写进 PROGRESS:
|
||||
- `chart.ai_prompt_pack.evidence_snapshot.functional_benefic_malefic`
|
||||
- `chart.ai_prompt_pack.evidence_snapshot.strength.shadbala_ranking` / `.sav_scores`
|
||||
- `chart.ai_prompt_pack.evidence_snapshot.timing.vimshottari`(含 antardasha)/ `.convergence_top_domains`
|
||||
- `chart.ai_prompt_pack.evidence_snapshot.career_narrative` / `relationship_narrative` / `finance_narrative`
|
||||
- `chart.modules.yoga.yogas`
|
||||
- `chart.modules.guided_topics`
|
||||
- 据此在前端新增**契约 fixture 测试**(合成数据),锁住任务 1 的提取函数对这些路径的读取行为。
|
||||
|
||||
### 验收
|
||||
|
||||
- PROGRESS 有逐字段的存在性清单。
|
||||
- 某字段实际缺失时:该解读层在后续任务中标记为不可提取并**跳过**,不得伪造,不得为此大改 Python 引擎(真要补引擎字段属于止损分支,单独说明改动面,只允许在响应中**新增**字段)。
|
||||
|
||||
---
|
||||
|
||||
## 任务 1(P0)· Bundle 承载解读性事实
|
||||
|
||||
### 做法
|
||||
|
||||
在 `report-evidence-bundle-v2.ts` 为 `ReportEvidenceBundleV2` 新增两组字段(`schemaVersion` 字面量保持 `report_evidence_bundle.v2`,新字段全部必填但允许空数组/null,保持 `.strict()`):
|
||||
|
||||
1. `interpretiveFacts`(结构化、闭合词表优先):
|
||||
- `yogas`:`{ name(≤80, 净化 pattern), category(闭合枚举,按引擎实际输出归并), planets(celestial 枚举数组), evidenceRef }[]`,数量上限 40
|
||||
- `functionalRoles`:`{ planet(celestial 枚举), role(benefic|malefic|neutral|yogakaraka 等闭合枚举), evidenceRef }[]`
|
||||
- `shadbalaRanking`:`{ planet, rank(int), rupa(number|null) }[]`(相对强弱定位,上限 9)
|
||||
- `savScores`:`{ house(1-12), score(number) }[]` + `savTotal(number|null)`
|
||||
- `currentDasha`:`{ mahadasha, antardasha, start, end }`(沿用既有 dashaPeriod 校验)
|
||||
- `convergenceDomains`:`string[]`(≤6 条,每条 ≤80 字符,净化)
|
||||
2. `themeNarrativeSeeds`:按主题 `{ theme, headline(≤300), strengths(string[] ≤8×≤400), risks(string[] ≤8×≤400), boundaries(string[] ≤8×≤400), evidenceRefs }[]`——内容只允许来自引擎叙事种子生成器与 `guided_topics` 文案字段,写入前做长度截断与控制字符过滤。
|
||||
|
||||
在 `personal-report-generation.ts`:
|
||||
|
||||
- `buildReportEvidencePacket` / `buildReportEvidenceBundleV2` 新增对上述路径的提取(allowlist 风格:未知 key 永不复制;行星/宫位经既有 `safeCelestialName` / 枚举净化)。
|
||||
- **确定性要求**:新增数组在构建端排序(进 `sortedBundleContent` 的规范化范围),保证 `bundleHash` 可复现;hash 计算覆盖新字段。
|
||||
- `filterReportEvidenceBundleForSection` 同步扩展:`themeNarrativeSeeds` 只保留本章主题;`interpretiveFacts` 按该章 `evidenceRefs` / 主题相关性裁剪(yoga、功能吉凶为通用层可全量保留,但要在 PROGRESS 用数据说明每章 prompt 增量)。
|
||||
|
||||
### 验收
|
||||
|
||||
- 单元测试:给定任务 0 的 fixture,断言提取出的每类事实的条数、净化行为(非法行星名被丢弃、超长截断)、以及 bundle hash 在字段顺序扰动下不变。
|
||||
- `validateReportEvidenceBundleV2` 对新增字段的越界输入(超长、未知枚举、非法 ref)fail closed。
|
||||
|
||||
---
|
||||
|
||||
## 任务 2(P0)· Claim card 从回执升级为结论
|
||||
|
||||
### 做法
|
||||
|
||||
改 `buildReportEvidenceBundleV2` 的 claim card 构造(:1121–1144):
|
||||
|
||||
- `conclusion`:由该主题的叙事种子 headline + 最多 2 条 strengths **确定性拼装**(无种子时回退到现状回执文案并把 `assertionLevel` 压到不高于 `single_system_inference`)。
|
||||
- `supportingFacts`:从 `interpretiveFacts` 与叙事种子生成真实事实条目(如"D10 已执行;A10/Karma Pada 进入主链"),每条绑定真实存在的 evidenceRef;risks 进 `counterFacts`;boundaries 进 `verificationQuestions` 或保留在种子内由 writer 引用。
|
||||
- `assertionLevel` 推导规则**一字不改**;不得因内容变富而提升级别。
|
||||
- blocked 主题逻辑不变。
|
||||
|
||||
同步收紧 writer 侧:`personal-report.ts` 指令中"Use its claimCards exclusively for narrative conclusions"保持,并明确 `themeNarrativeSeeds` / `interpretiveFacts` 属于可引用事实层。
|
||||
|
||||
### 验收
|
||||
|
||||
- 单元测试:有种子主题的 conclusion 含真实占星内容且非模板句;无种子主题回退且级别受压;consensus 校验回归测试全绿。
|
||||
- 端到端一份 smoke 报告:抽查任一 write 章节,叙事中出现的 yoga 名/强弱/大运表述均能回溯到 bundle 内条目(人工抽查 3 处,PROGRESS 记录)。
|
||||
|
||||
---
|
||||
|
||||
## 任务 3(P1)· 写作端静态解读知识包
|
||||
|
||||
### 做法
|
||||
|
||||
- 新建 `frontend/src/lib/report-interpretation-packs/`(纯静态 TS 常量或构建期内联的文本),**从本仓 `references/` 蒸馏**(重点:`modern-language-guide.md`、`common-misconceptions.md`、`house-domain-planet-mapping.md`、各主题判读要点)。每主题一个包 + 一个通用包;单包 ≤ 3,000 字符;内容只写"怎么解释、怎么措辞、哪些断语禁止",不写具体人的断语,不含路径/内部名词。
|
||||
- 在 `createPersonalReportAgent` 的章节调用里,把「通用包 + 本章主题包」放进 `cachedSystemMessage` 缓存边界之前的系统内容,吃 prompt cache;摘要调用只带通用包。
|
||||
- 更新 writer 指令与 `skills/jyotish-personal-report/SKILL.md`(版本号 +1):知识包仅约束**措辞与解释方式**,不得据此产生 bundle 之外的新占星断言;claimCards + interpretiveFacts + 叙事种子仍是唯一事实来源。
|
||||
|
||||
### 验收
|
||||
|
||||
- 单元测试:每个主题都能解析到知识包;知识包内容通过一个"违禁词/路径扫描"断言(不含 `references/`、`scripts/`、引擎名、`SKILL.md` 字样等)。
|
||||
- 端到端对照:同一 smoke 盘改前改后各生成一次,PROGRESS 给出两版任一相同主题章节全文对照与每章 `inputTokens`(含 cache 命中)对照。
|
||||
|
||||
### 止损
|
||||
|
||||
- 知识包导致每章 `inputTokens` p50 超改前 2 倍且 cache 未命中:先查 `cachedSystemMessage` 是否生效,不得直接砍证据来凑成本。
|
||||
|
||||
---
|
||||
|
||||
## 任务 4(P2)· 观测与对照
|
||||
|
||||
- telemetry(沿用现有隐私边界)补记每章 bundle 内 `interpretiveFacts` 条数与知识包字符数(数值,不记内容)。
|
||||
- staging 部署后真实生成一份报告,PROGRESS 给出:章节数、每章 in/out tokens、总墙钟时间、与改前(PROGRESS-report-sectioned-20260830.md 的数据)对照。
|
||||
|
||||
## 不在本轮范围
|
||||
|
||||
- 写作 agent 的工具循环 / 只读检索工具(P2 另立项)。
|
||||
- MEVG / web 验证接入报告端。
|
||||
- Python 引擎侧新增计算模块或改动既有模块输出(除任务 0 止损分支允许的"响应新增字段")。
|
||||
- 聊天路径(consultation-tools / agent-reply)的证据消费方式。
|
||||
- 章节并发、模型更换、计费改动。
|
||||
|
||||
## 收尾
|
||||
|
||||
- PROGRESS 文件名写 `PROGRESS-report-skill-parity-20260901.md`(不要写成 `PROGRESS.md`)。
|
||||
- 任务 0 可与任务 1–2 合并推送;任务 3 建议单独一次推送便于对照。staging push 触发全量构建+部署,流水线 `validate` → `publish` → `deploy` 全链路约 20 分钟,推送后核对 `https://staging.jyotisha.chat/api/health` 的 `.deployment.gitCommit`。
|
||||
- 不自行提升 main。
|
||||
|
||||
## 交付物清单
|
||||
|
||||
1. 任务 0 契约快照(字段存在性清单 + fixture 测试)
|
||||
2. bundle 新字段 schema + 提取 + 净化 + hash 覆盖 + fail-closed 测试
|
||||
3. `filterReportEvidenceBundleForSection` 的主题裁剪扩展与每章 prompt 增量数据
|
||||
4. 实质化 claim cards + 回退与级别压制逻辑 + consensus 回归测试
|
||||
5. 端到端抽查记录:叙事内容可回溯到 bundle(≥3 处)
|
||||
6. 解读知识包目录 + 违禁词扫描测试 + `skills/jyotish-personal-report/SKILL.md` 更新
|
||||
7. 改前改后同盘同主题章节全文对照 + tokens/墙钟对照
|
||||
8. `docs/BUG_HISTORY.md` 条目(编号先对远端确认)
|
||||
9. `BLOCKED.md`:任何触发止损的项
|
||||
10. `PROGRESS-report-skill-parity-20260901.md`
|
||||
11. `tsc --noEmit` / `eslint`(0 error)/ `tsx --test`(失败清单与基线逐条比对)/ `next build` 的实际输出
|
||||
Reference in New Issue
Block a user