docs(tasks): synastry report as relationship portrait; #79 backend-only

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Ad9rqAB8pXXvyzh7f4X8v
This commit is contained in:
Jesse_Chen
2026-10-18 14:41:08 +08:00
co-authored by Claude Opus 5.5
parent 128b5e25b0
commit ec30f4193e
4 changed files with 125 additions and 39 deletions
@@ -0,0 +1,58 @@
# 合盘报告的表述准则:从星曜语言到关系语言(2026-10-18)
来源:产品负责人 2026-10-18 提供,作为完整合盘报告(`docs/tasks/TASK-synastry-full-report-20261018.md`,issue #82)正文写作的首要准则。下文照原文整理,只调整了排版。
本质:把合盘从**星曜语言**翻译成**关系经验语言**。不报分数、不列 Koota、不提宫位星曜,直接描述「你们在一起时,关系会呈现什么样的质地、节奏和课题」。合盘不再是一张评分表,而是一幅**关系画像**。
## 一、先定基调:这段关系的「底层天气」
用一句话给出整体感受,不说吉凶,说质地。例如:
- 「你们之间有一种自然的熟悉感,像是节奏容易对上,但真正靠近后会发现彼此对安全感的要求并不一样。」
- 「这段关系的底色是『需要慢慢磨合』,不是一见钟情的顺滑,而是越相处越能长出默契的类型。」
- 「你们容易被彼此吸引,但吸引点和摩擦点可能来自同一个地方。」
要点:先给「天气」,不给「判决」。是顺滑、慢热、拉扯、互补、共振,还是需要经营。
## 二、分维度说,但不用术语
| 维度 | 不说 | 改说(示例) |
| --- | --- | --- |
| 情感节奏 | Nadi、月亮星宿 | 「你们表达关心的方式不太一样:一个偏向日常照顾,一个偏向语言确认。」「情绪上来时,一方需要立刻说开,另一方需要先安静消化,这会形成节奏差。」 |
| 沟通与理解 | 水星、第三宫 | 「你们讲道理时容易听懂彼此,但情绪一上来,容易各说各的。」「一个习惯把问题摊开,一个习惯先自己扛,误会往往出在这里。」 |
| 安全感与亲密 | 金星、月亮受克 | 「这段关系里,安全感不是靠承诺自动建立的,而是靠稳定的日常回应。」「亲密对你们不是问题,边界才是需要练习的部分。」 |
| 冲突与修复 | 火星、土星相位 | 「你们不是不吵架,而是吵完之后谁先递台阶的问题。」「压力下容易一个追、一个退,修复的关键是别把『退』理解成不在乎。」 |
| 生活节律与现实协作 | 第七宫、D9 | 「恋爱感和生活感是两套节奏,你们在『怎么过日子』上需要提前对齐。」「金钱、家庭角色、时间安排,这些现实议题会比浪漫更早考验你们。」 |
| 长期方向 | Dasha、D9 兑现 | 「这段关系有长期潜力,但潜力不是自动兑现的,它需要某个阶段双方都愿意把关系放到更优先的位置。」「你们适合把关系当成共同项目来经营,而不是靠感觉自然滚动。」 |
## 三、表述原则:说「互动」,不说「命定」
1. **说「你们之间」,不说「你 / 他是什么人」**。不说「你月亮受克,情感不稳定」,改说「你们之间在情绪确认上容易错位,需要把『我需要什么』说出口」。
2. **说「倾向」,不说「必然」**。不说「这个配置注定婚姻有摩擦」,改说「这个组合倾向于把激情和冲突放在同一条通道里,关系需要学会把对抗变成共同行动」。
3. **说「需求」,不说「缺点」**。不说「一方太黏,一方太冷」,改说「一方通过靠近获得安全,一方通过空间获得安全,协商的是距离感」。
4. **给条件,不给结论**。不说「你们不合」,改说「你们在情感本能上有共振,但在现实节律上需要更清醒地对齐;能不能走远,取决于愿不愿意把差异摆到桌面上」。
5. **保留双方主体性**。不说「合盘显示你们应该在一起 / 不适合在一起」,改说「合盘描述的是互动的质地,走不走、怎么走,仍然是你们两个人的选择」。
## 四、示例
> 你们这段关系的底色是「有吸引,但节奏不同」。刚靠近时,彼此容易觉得对方身上有自己缺的东西,所以会有自然的牵引。真正进入日常后,差异会浮出来:一个更习惯用行动表达关心,一个更需要语言和情绪回应;压力大的时候,一个想立刻解决,一个想先躲开。这不是谁对谁错,而是你们的安全感开关不在同一个位置。
>
> 你们的优势在于,只要愿意把话说明白,理解力是在的;难点在于,情绪上来时容易把「我需要空间」听成「你不在乎我」。长期看,这段关系不是靠感觉自动顺滑的类型,而是靠两个人把日常回应、冲突修复和现实安排一点点对齐。如果愿意练,它会长出很稳的默契;如果回避差异,摩擦会反复出现在同一个地方。
这段没有出现任何星曜、宫位、分数,但仍然是合盘表述。
## 五、篇幅形态
- 书面报告四段式:关系基调 → 天然顺流处 → 需要清醒经营的摩擦区 → 共同练习(两三条可操作的建议)。
- 口头咨询更短:底色 → 一个优势 → 一个摩擦 → 一个建议。
- 产品 10-18 选定的书面报告结构见任务书:基调 + 六个维度 + 共同练习,共八节。
## 六、要避免的表达
- 不报分数(「你们 22 分,所以不错」),分数会变成伪判决。
- 不用「克 / 煞 / 凶 / 吉」直接贴标签。
- 不说「注定」「必然」「命中」。
- 不把一方说成问题源(「他土星重所以冷」→「你们在压力下的反应节奏不同」)。
- 不替当事人做决定。
一句话:技术是底稿,表述是关系语言。好的非技术表述,听的人不需要懂占星,也能认出「这说的就是我们」。
+2 -2
View File
@@ -501,6 +501,6 @@
| `TASK-data-layer-types-20261013.md` | `PROGRESS-data-layer-types-20261013.md` | 数据访问层改用自己的 DataClient 类型第一阶段(BUG-1351;任务书预留的 1343 已被更大编号越过) | 待验收 | `codex/data-layer-types-20261013` |
| `TASK-contracts-helpers-dedupe-20261013.md` | — | 前后端契约与小工具去重第一阶段 | 待领取(排队:frontend-dead-code、consult-route-split) | `codex/contracts-helpers-dedupe-20261013` |
| `TASK-people-archive-limit-20-20261017.md` | — | 星盘档案上限 5→20 人(触发器新迁移 + 常量 + 文案;婚恋合盘前置单) | 待领取 | `codex/people-archive-limit-20-20261017`;动表,需 test:db |
| `TASK-marriage-match-p1-20261017.md` | — | 婚恋合盘第一期:独立页、任意两人、引擎与选人页(搬上游 bde5d615:marriage_match + 合盘证据包;35 分七项、不分男女;第五版不扣点)(BUG-1370 起) | 排队(等上限单合入) | `codex/marriage-match-p1-20261017`;动表 |
| `TASK-marriage-match-p1-20261017.md` | #79 | 婚恋合盘引擎(搬上游 bde5d615:marriage_match + 合盘证据包;35 分七项、不分男女;第六版不扣点、不做页面)(BUG-1370 起) | 待执行(不再等 #78) | `codex/marriage-match-p1-20261017`;动表 |
| `TASK-full-reading-module-parity-20261018.md` | #81 | full-reading 补齐上游 5 个模块(西占层文件没搬致永远 blocked、Navamsha / Niryaana 用我们自己的实现填键、Lagna Vargottamamsa / Pushkara / Punarphoo)(BUG-1375 起) | 待执行(可与 #78 并行) | `codex/full-reading-module-parity-20261018` |
| `TASK-synastry-full-report-20261018.md` | #82 | 婚恋合盘第二步:完整合盘报告(搬上游 full-synastry-reading,模型按节写中文、英文按需、计费键 synastry.full) | 排队(等 #79、#81) | `codex/synastry-full-report-20261018`;动表 |
| `TASK-synastry-full-report-20261018.md` | #82 | 婚恋合盘:选人页 + 完整合盘报告(关系画像八节,正文无术语;英文按需;计费键 synastry.full) | 排队(等 #78、#79、#81) | `codex/synastry-full-report-20261018`;动表 |
+13 -13
View File
@@ -4,9 +4,9 @@
- `origin/staging` = `f95eb670b`(开工前 `git fetch origin --prune`,以最新 `origin/staging` 为准)
- 分支:`codex/marriage-match-p1-20261017`;工作树:`.worktrees/marriage-match-p1-20261017`
- **前置**:`TASK-people-archive-limit-20-20261017.md` 必须先合入 staging。两单都改 `components/people/people-page.tsx`,本单在它之后从最新 `origin/staging` 开分支。
- **后续单**:`TASK-synastry-full-report-20261018.md`(完整合盘报告)在本单合入后开工,复用本单的证据包接口、选人页与存储。
- 原型(v7:结果即报告,本单只做其中的选人页与「传统配对参考」一节):https://claude.ai/artifact/Tq6vhXFMnGitDZhJdamaHe 。原型里的人名、分数、宫位全是虚构示例。
- **前置**:~~档案上限单先合入~~ 第六版起本单不碰前端页面(D16),**不再依赖 #78,可以直接开工**。
- **后续单**:`TASK-synastry-full-report-20261018.md`(#82,完整合盘报告)在本单与 #78、#81 合入后开工,复用本单的引擎、证据包接口、存储与服务端函数;选人页和所有入口改动都在那一单。
- 原型:https://claude.ai/artifact/Tq6vhXFMnGitDZhJdamaHe (v8;本单没有任何页面,原型给的是后续单的样子)
## 现状实证(按符号定位)
@@ -47,6 +47,7 @@
| D13 | 上游 main 10-17~18 新增的「完整合盘解读」流程(双语报告、D9 互动证据、章节覆盖门槛等 26 个提交)**不在本单范围**,留作第二期参考 |
| D14 | **第五版(产品 2026-10-18):完整报告就是合盘结果**。本单的结果页只是后续报告里的最后一节「传统配对参考」:七项配对、火星冲克、婚姻宫压力。**删掉**「助力多于考验」三档落点、三句话总述、「各自的婚姻征象」一节(由报告第一、二节代替;上游也要求开头不放分数和结论档位)。本单合入后 staging 上能看到选人页和这一节;生产保持 `compatibility` 开关关闭,直到后续单合入 |
| D15 | 证据包接口在本单做:两人出生资料 → 两次 `full-reading` → 上游 `build_full_synastry_dossier`(它内部已调用默认 profile 的 `marriage_match`),再对它的 `chart_snapshots` 补调一次 Raman profile。本单只用其中的 `marriage_match` 部分;其余写作素材留给后续单 |
| D16 | **第六版(产品 2026-10-18):报告正文是关系画像,技术层不给用户看**。35 分、火星冲克、婚姻宫压力是后续单的写作底稿,并进入后续单默认折叠的「技术依据摘要」(格式由产品另给)。因此本单**不做任何页面**:T6 结果页取消、选人页移到后续单;T5 由 Next 路由改为服务端函数;T8 的入口改动移到后续单。本单只做引擎、证据包接口、存储、退役旧模块。D10 的「并列展示」改为两个 profile 都算、都存,供后续单写作时使用 |
### 引擎口径(上游 `marriage_match.py`,摘要)
@@ -66,14 +67,14 @@
## 硬红线
1. `scripts/jyotish_api_server.py` 不增长(类方法数、`__new__` 伪造点、行数护栏):新计算放新模块(建议 `scripts/marriage_match.py`),主文件只做薄注册。
2. `app/(app)/page.tsx` 的 `Home()` 状态数不得增长;本单删掉 `SynastryDeepLink` 与开机拉合盘历史,只会减少。
2. 本单不改前端页面与 `app/(app)/page.tsx`(第六版,入口清理在后续单)。
3. 新功能里不得出现:五星合分、任何百分比或 100/80/50 之类的火星冲克分数、Papasamya 加权总分、按性别判轻重、「合 / 不合」「相合」「会不会在一起」「适合 / 不宜结婚」「分离」「寿命」。
4. 生产运行时不得 import PyJHora(只作测试对照,许可证边界);验收会屏蔽 PyJHora 模拟生产。
5. 合同测试的 fixture 用真实引擎输出(golden)。所有示例与测试只用虚构人物或公开名人。
6. ~~扣点~~:第五版移到后续单(D3)。本单不得调用 `authorizeUsage`。
7. 一次等待一次揭幕:等待只有一屏,结果整页一起出现;揭幕后无 spinner / 骨架。
7. ~~一次揭幕~~:本单无页面。
8. 加表 / 加列可以同轮,不得删旧列(AGENTS §7.6);动表必须真跑 `npm run test:db`。
9. 删人:删档案里的人时,这个人参与的合盘一并删除;删前确认句在「N 个对话和 M 份报告」后加上「K 份合盘」。
9. 删人:删档案里的人时,这个人参与的 `synastry_reports` 行一并删除(数据库层);删前确认句的文案改动在后续单。
## 任务分解
@@ -83,15 +84,15 @@
| T2 | **Python 接口**:`POST /api/synastry_full_dossier`(收两份出生资料,返回证据包 + Raman 结果)与 `POST /api/marriage-match`(与上游同名,只收快照)薄注册,实现在独立模块;证据包接口进 `HEAVY_COMPUTE_PATHS`,按两人出生资料 + 提交号做磁盘缓存(照 `report_reference_cache` 的做法);旧 `/api/synastry` 保留给 MCP / CLI 兼容但不再被网页调用;CLI 加 `marriage-match` 命令 | `tests/test_api_server_growth_contract.py` 绿;两个接口各一份 golden 进 `frontend/tests/fixtures/`(证据包可截取,写明截取规则) |
| T3 | **退役与文档纠错**:删 `scripts/synastry.py`,CLI `synastry` 改走新模块或删除(同步 `SKILL.md` 命令表、`tests/run_all.py`、`tests/test_open_source_integrations.py`);`ashtakoot.py` 的 `calc_kuja_dosha` / `match_kuja_dosha` 标为旧版、不被新功能调用;删掉三处 Papasamya / Mangal 匹配虚报 | 全量 pytest 与基线逐条比对无新增失败;`git grep "synastry.calc_ashtakoot"` 0 命中 |
| T4 | **数据**:`synastry_reports` 加列——两人的人物引用(本人用约定值,他人用 `chart_profiles.id`)、两人出生资料指纹、规则版本 `rules_version`、结构化结果;旧行不迁移、不显示 | `npm run test:db`:同一对人同指纹第二次读到旧行;任一人改出生资料后指纹不同;删人后其合盘行消失 |
| T5 | **Next 接口**:重写 `app/api/synastry/route.ts`(或新路由,旧的删):收两个人物 id → `loadSubjectBirth` 两次 → 查已保存(命中即返回)→ 调 T2 证据包接口 → 存结构结果(两个 profile 的 `marriage_match`);**不扣点**(D3);不读性别(D5);保留产品开关 `compatibility` 与限流桶;删掉 `relationshipType` 与 business / family / general 分支 | 路由测试覆盖:命中已保存不再调引擎;两个 id 相同 400;不属于本人的 id 拒绝;两人对调后结果一致 |
| T6 | **页面**:侧栏「婚恋合盘」;`/marriage-match`(路由名可议,写进 DESIGN)选人页(两个自定义下拉,底行「+ 新增星盘」复用星盘档案新建表;居中按钮;点数来自定价不写死;之前的合盘列表只列「人名 × 人名 · 日期」,点开不扣点);结果页只有「传统配对参考」一节(原型 v7 报告最后一节):七项配对(满分 35,含例外绿字 / 另一派灰字、两个合计、「种姓项不计入」小字)→ 火星冲克(每人逐处列出「从哪里数、落第几宫、是否触发」,减轻条件带分档标签并注明「不改变这一处」;Raman 一派不同时并列一句;两人对比)→ 婚姻宫压力 → 「可以一起聊聊」四问(D12④,上游 `shared_life_dialogue.prompts` 译成中文,不收集回答)→ 脚注(口径来源、分档含义、「计算层归纳不是定论」);这一节做成独立组件,后续单的报告阅读页直接复用;选人页按钮下暂不显示点数(后续单接定价) | 渲染测试:两个状态(两人火星冲克相当 / 一方明显)的 golden 渲染;20 人时下拉可滚动;手机 390 px 无横向滚动;`next build` 后 `/` 仍 Static,首屏 gzip ±2% |
| T5 | **服务端函数**(第六版):建议 `frontend/src/lib/marriage-match-service.ts`:收两个人物 id → `loadSubjectBirth` 两次 → 查已保存(同一对人不分先后 + 两人出生资料指纹 + `rules_version` 命中即返回)→ 调 T2 证据包接口 → 存结构结果(两个 profile 的 `marriage_match`)并返回证据包。**不扣点、不做路由**;旧 `app/api/synastry/route.ts` 暂不删(后续单删入口时一起删) | 单测覆盖:命中已保存不再调引擎;两个 id 相同报错;不属于本人的 id 拒绝;两人对调命中同一行 |
| T6 | ~~页面~~ 第六版移到后续单(D16) | — |
| T7 | ~~三句话总述~~ 第五版删除(D14),由后续单报告第一节代替 | — |
| T8 | **清旧入口**:档案页「和我合盘」改跳新页并预选两人;删 `SynastryDeepLink`、`use-synastry.ts` 中无人用的部分、`buildSynastryQuestion` 草稿路径、孤儿 CSS、`page.tsx` 开机拉合盘历史 | 按 AGENTS §7.8 grep 源码文本合同测试同轮改,三栏说明 |
| T9 | **记录**:调研记录已由 Claude 写好,执行方只需在 PROGRESS 引用;DESIGN.md、VOICE.md(新文案、删人确认句、侧栏名)、CHANGELOG、BUG_HISTORY(下节编号)、`docs/testing/marriage-match-p1-20261017.md` 真人清单(选人、新增星盘、再次打开读已保存、删人连带删除、手机宽) | 文档齐 |
| T8 | ~~清旧入口~~ 第六版移到后续单(D16) | — |
| T9 | **记录**:调研记录已由 Claude 写好,执行方只需在 PROGRESS 引用;CHANGELOG、BUG_HISTORY(下节编号)、PROGRESS(与上游的差异:无;改写的那条上游测试三栏说明) | 文档齐 |
## 让步顺序
先保 T1–T5(算对、存对);T6 不可省;T3 的 CLI 改写可降为「删 `synastry.py` + CLI 命令删除」。任何一项过不了红线就停下写进 PROGRESS,不得放宽口径。
先保 T1、T2、T4、T5(算对、存对);T3 的 CLI 改写可降为「删 `synastry.py` + CLI 命令删除」。任何一项过不了红线就停下写进 PROGRESS,不得放宽口径。
## 开工前置命令
@@ -99,7 +100,6 @@
git fetch origin --prune
git worktree add -b codex/marriage-match-p1-20261017 .worktrees/marriage-match-p1-20261017 origin/staging
cd .worktrees/marriage-match-p1-20261017
git log --oneline -5 -- frontend/src/lib/subject-birth.ts # 确认上限 20 人那单已在
grep -n "BUG-1[0-9][0-9][0-9]" docs/BUG_HISTORY.md | tail -3
python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45
# 上游文件(上游仓库私有,已原样放进本仓只读参照目录,固定到 bde5d615)
@@ -118,5 +118,5 @@ ls docs/research/upstream_marriage_match_bde5d615/ docs/research/upstream_full_s
| BUG-1370 | 合盘结果依赖顺序,本人恒为男方(实证 3;修法:改为对调平均、不分男女) |
| BUG-1371 | 网页合盘从不计算火星冲克(实证 4) |
| BUG-1372 | 旧 `synastry.py` 互控写死 2.0 / 星宿下标越界(实证 7) |
| BUG-1373 | 结果卡随旧面板删除,合盘只剩聊天草稿(实证 2) |
| BUG-1373 | 结果卡随旧面板删除,合盘只剩聊天草稿(实证 2;本单只登记 investigating,由后续单 #82 修复并改 resolved) |
| BUG-1374 | 文档虚报 Papasamya / Mangal 匹配已实现(实证 8) |
@@ -4,8 +4,10 @@
- `origin/staging` 以开工时最新为准(`git fetch origin --prune`)。
- 分支:`codex/synastry-full-report-20261018`;工作树:`.worktrees/synastry-full-report-20261018`。
- **前置(串行)**:`TASK-people-archive-limit-20-20261017.md`(#78)→ `TASK-marriage-match-p1-20261017.md`(#79)→ 本单;另需 `TASK-full-reading-module-parity-20261018.md` 已合入。本单从 #79 合入后的 `origin/staging` 开分支,复用 #79 的引擎、证据包接口、选人页和 `synastry_reports` 新列。
- 原型(v7,结果即报告):https://claude.ai/artifact/Tq6vhXFMnGitDZhJdamaHe 。原型里的人名和正文全是虚构示例。
- **前置**:#78(档案上限 20 人)、#79(引擎、证据包接口、存储;第六版起不做页面)、#81(full-reading 补齐 5 个模块)三单都合入后开工。三单彼此不冲突,可并行。
- **上线**:#79 与本单要一起提升到 `main`(#79 单独上线时旧入口还在、新入口不存在)。本单从 #79 合入后的 `origin/staging` 开分支,复用 #79 的引擎、证据包接口、选人页和 `synastry_reports` 新列。
- **正文写作首要准则**:`docs/research/synastry_relationship_language_guide_2026_10_18.md`(产品负责人 10-18 提供,「从星曜语言到关系语言」)。凡与上游写作规范冲突处,以它为准。
- 原型(v8,关系画像):https://claude.ai/artifact/Tq6vhXFMnGitDZhJdamaHe 。原型里的人名和正文全是虚构示例。
- 上游参照:占星师仓库 `yinduzhanxing` 提交 `bde5d615` 的 `skills/jyotish-full-synastry-reading/`。上游仓库私有,原样副本在 `docs/research/upstream_full_synastry_bde5d615/`(`skill/` 下是 Skill 本体,`tests/` 下是五个测试文件;`.py.txt` 搬入时去掉 `.txt`)。
## 现状实证(按符号定位)
@@ -27,12 +29,15 @@
| # | 决定 |
| --- | --- |
| E1 | **完整报告就是合盘结果**:点「开始合盘」即生成整份报告(要等几分钟),走报告任务的后台生成与等待面板;没有先出的即时结果页。#79 做的七项配对、火星冲克、婚姻宫压力,放到报告最后一节「传统配对参考」 |
| E2 | **先写中文,切到英文时再写**:英文不翻译中文稿,按上游英文写作规范从同一份证据包重新写;第一次切换时生成,不另扣点 |
| E1 | **完整报告就是合盘结果**:点「开始合盘」即生成整份报告(要等几分钟),走报告任务的后台生成与等待面板;没有先出的即时结果页 |
| E2 | **先写中文,切到英文时再写**:英文不翻译中文稿,从同一份证据包按同一准则重新写;第一次切换时生成,不另扣点 |
| E3 | **单独计费项 `synastry.full`**,默认价格同个人完整报告(`report.full`)。价格由后台发布,代码里不写死。同一对人、两人出生资料没改、规则版本没变,再次打开读已有报告,不扣点 |
| E4 | 报告写作**重新启用模型**,但只用于合盘报告;个人报告保持引擎渲染不变(`PERSONAL_REPORT_WRITER_ENABLED` 不动) |
| E5 | 报告结构以上游为准:九节(结论总纲、各自带入关系的需求、情绪安全与相处感受、吸引与亲密、沟通冲突与修复、关系如何推进、共同生活的收益与压力、能不能稳定走下去、关系应期)+ 我们的附录「传统配对参考」+「可以一起聊聊」四问。**开头不放分数、不放结论档位**(推翻 #79 任务书第四版的「助力多于考验」三档落点与三句话总述,由第一节代替) |
| E6 | 不收集两人现在的关系阶段与经历。「关系应期」按上游规则只写各自可用的窗口和需要同时具备的条件,不说「某年结婚」 |
| E5 | **正文是关系画像,不是技术报告**(产品 10-18 第二版,推翻本单第一版的「九节 + 传统配对参考」):按准则文件,把星曜语言翻译成关系经验语言。正文**不出现任何分数、Koota 名、星曜名、宫位、分盘、大运等术语**,也不出现「克 / 煞 / 凶 / 吉」「注定 / 必然 / 命中」。结构八节:一、关系基调(底层天气);二、情感节奏;三、沟通与理解;四、安全感与亲密;五、冲突与修复;六、生活节律与现实协作;七、长期方向;八、共同练习(两三条可操作的建议)。之后保留「可以一起聊聊」四问、折叠的「技术依据摘要」(E5b)与脚注。开头不放分数、不放结论档位 |
| E5b | **正文之后加一个默认折叠的「技术依据摘要」**(产品 10-18 第三版,取代第二版「完全不给用户看」):35 分、火星冲克、婚姻宫压力等技术层照算;摘要的内容与格式**由产品负责人另行提供**,到达前本单不得开工这一项(见让步顺序)。正文八节仍然不出现任何术语 |
| E5d | **印度占星依据要能给用户解读,引导到对话里**(产品 10-18):每节末尾一个「问问这一段的依据」入口,点开进入一个绑定这份合盘报告的对话,由对话用印度占星的语言解释这一节背后的依据(对话里可以用术语)。对话侧的能力另立任务书(Claude 写,编号待定),与本单、#79 一起上线;本单只做入口按钮与传给对话的参数,入口在对话侧合入前不显示 |
| E5c | 写法守五条:说「你们之间」不说「你 / 他是什么人」;说倾向不说必然;说需求不说缺点;给条件不给结论;保留双方主体性。可以写「一方……另一方……」或点名谁需要什么,但不得把一方写成问题源 |
| E6 | 不收集两人现在的关系阶段与经历。「长期方向」**写大致阶段,不提大运**(产品 10-18):如「明年下半年起,两人都更愿意把关系放到优先位置」;不说「某年结婚 / 领证 / 分开」,不出现大运、行运等术语 |
| E7 | 上游的发版前检查(`pre_work_check`)、全网资料核验(MEVG)、书库检索是写稿代理的开发流程,**不进产品运行时**;报告脚注照常写「计算层归纳,不是定论」 |
| E8 | 缺的 5 个模块全部补上(产品 10-18):`TASK-full-reading-module-parity-20261018.md`,**必须在本单开工前合入** |
@@ -44,32 +49,55 @@
→ 预留 synastry.full → 建报告行 + 任务
→ worker:取两人出生资料
→ Python POST /api/synastry_full_dossier(#79 已做):两次 full-reading → 上游证据包 + 两个 profile 的 marriage_match
→ 九节中文:每节一次模型调用,只喂这一节的 reader_section_brief 与公共上下文,并发 ≤ 3
→ 中文质检(Python,上游校验器的中文一半)
→ 八节中文:每节一次模型调用,只喂这一节对应的证据(映射见下表)与公共上下文,并发 ≤ 3
→ 中文质检(Python,本单自己的关系语言质检)
不过:只重写不过的节,最多 1 轮,把失败原因附进提示
仍不过:任务失败、释放预留
→ 存:中文九节 Markdown、证据包快照、质检回执、传统配对参考结构
→ 存:中文八节 Markdown、每节的私有依据(claims)、证据包快照、质检回执
→ 结算 synastry.full
阅读页切到英文(第一次)
→ POST /api/reports/:id/english → 任务(不扣点)→ 九节英文 + 英文质检 → 存
→ POST /api/reports/:id/english → 任务(不扣点)→ 八节英文 + 英文质检 → 存
```
### 八节与证据映射
| 节 | 英文标题 | 喂给模型的证据(上游证据包字段) |
| --- | --- | --- |
| 一、关系基调 | Relationship Climate | `reader_section_brief` 的 `relationship_thesis`、`what_each_person_brings`;`relationship_lenses` 总览 |
| 二、情感节奏 | Emotional Rhythm | `emotional_safety` |
| 三、沟通与理解 | Communication and Understanding | `communication_conflict_and_repair`(沟通部分) |
| 四、安全感与亲密 | Safety and Intimacy | `attraction_and_closeness` + `mutual_affection_activation`(两个方向都要写:什么让对方感到被重视、什么让对方退缩) |
| 五、冲突与修复 | Conflict and Repair | `communication_conflict_and_repair`(冲突与修复部分)、`can_it_stay_steady` 的修复部分 |
| 六、生活节律与现实协作 | Daily Life and Practical Partnership | `shared_life_gains_and_pressures` |
| 七、长期方向 | Long-term Direction | `relationship_progression`、`can_it_stay_steady`(D9 双人互动已执行时必须用上,但正文不提 D9)、`relationship_weather` + `parallel_timing_context` + `event_resonance_gate`(只换算成大致阶段) |
| 八、共同练习 | Practices to Share | 前七节的依据;每条建议必须能回指一条具体依据 |
`marriage_match`(35 分、火星冲克、婚姻宫压力)作为背景放进第二、四、五、六节的输入,只能被翻译成关系语言(例如「压力下一个追、一个退」),不得以分数或名称出现。
### 模型提示(新文件,建议 `frontend/src/mastra/synastry-report.ts`)
- 系统提示由上游三份文件改写成中文提示,**不改原意**:`chinese-synastry-writing-contract.md`、`customer-report-shape.md` 的对应节要求、`customer-topic-priorities.md`。英文用 `english-synastry-writing-contract.md`。
- 每节输入:两人称呼(档案里的称呼,不给出生日期、出生地)、这一节的标题与上游要求、`reader_section_brief` 里这一节的条目、`relationship_lenses` 里对应的 lane;「吸引与亲密」加 `mutual_affection_activation`;「关系应期」加 `parallel_timing_context` 与 `event_resonance_gate`;「能不能稳定走下去」在 D9 双人互动已执行时必须带上 D9 部分。
- 每节输出 JSON:`{ text, claims: [{ sentence, label }] }`,`label` 取上游六个证据标签之一。**标签只存不显示**(上游要求每条结论有标签,我们的读者不看标签)。
- 禁词合并:上游 `READER_BANNED_TERMS`、私有证据词、补充方法名(宿曜、西占、问卦等),加上 #79 红线 3 与 VOICE 禁用说法(「合不合」「会不会在一起」等)。
- 系统提示以准则文件为主体(原文要点全部进提示,含五条写法与示例);上游 `chinese-synastry-writing-contract.md`、`customer-report-shape.md` 只取「证据怎么用」的部分:每个判断要有两条同向依据、冲突要降级、时间要有条件、补充方法(宿曜、西占、问卦)不得点名。上游要求在正文里写术语、写「七宫主落……」句式、点名 D9 的部分**不采用**。英文提示同理,以准则文件为主体,参考 `english-synastry-writing-contract.md` 的证据部分。
- 每节输入:两人称呼(档案里的称呼,不给出生日期、出生地)、这一节的标题与要求、上表对应的证据。
- 每节输出 JSON:`{ text, claims: [{ sentence, evidence, label }] }`。`evidence` 指向这一节输入里的具体条目,`label` 取上游六个证据标签之一。**claims 只存不显示**:这是「技术是底稿」的落地,验收和以后复核靠它。
### 中文 / 英文质检
### 中文 / 英文质检(本单自己的,`scripts/synastry_full_reader_gate.py`)
上游 `validate_bilingual_synastry_delivery.build_delivery_receipt` 要求中英同时传入。本单在本地包一层 `scripts/synastry_full_delivery_gate.py`:复用上游文件里的常量和 `_heading_status`、`_section_bodies` 等函数,分别对中文、英文做上游同样的检查(九个标题、开头从靠近感写起、每节最少字数、全文最少字数、D9 覆盖、吸引与亲密双向覆盖、禁词、私有词、补充方法名)。**上游文件原样搬入不改**;包装层是与上游唯一的差异,写进 PROGRESS,并请占星师在上游加单语言模式(Claude 负责转达)。
上游校验器(`validate_bilingual_synastry_delivery.py`)原样搬入不改,但它的读者规则与准则冲突(要求正文出现 D9、要求首句含特定靠近感词),所以**不用它判正文**;只复用它的 `PRIVATE_EVIDENCE_TERMS`、`SUPPLEMENTARY_METHOD_TERMS` 两张表。本单自己的质检,中英各一套,逐条:
1. 八个标题齐全、顺序对;每节中文至少 120 字(英文 80 词),全文中文至少 1800 字(英文 1100 词)。
2. **术语禁表**(中文):行星名(太阳、月亮、水星、金星、火星、木星、土星、罗睺、计都)、「宫」、星座名、星宿、上升、分盘 / 九分盘 / 婚姻分盘、D1 / D9 / D 加数字、大运、小运、行运、Dasha、Koota、Kuja、火星冲克、Nadi、Yoni、Varna、Ashtakoota、Papasamya、UL、DK、「数字 + 分」、满分。英文对应一套(planet names、house、sign names、nakshatra、ascendant、Navamsa、D9、dasha、transit、koota、points/score 等)。
3. **判断禁表**:克、煞、凶、吉、注定、必然、命中、一定会、合不合、不合、相合、不适合、适合结婚、不宜、灵魂伴侣、宇宙安排、百分百、会不会在一起;「年份 / 月份 + 结婚 / 领证 / 分手 / 离婚」。
4. 上游私有词表、补充方法名表一个都不能出现。
5. 首句不得是分析腔(「这段关系的核心不是……」「不是有没有……」)。
6. 每节至少一条 claim,且 `evidence` 能在这一节的输入里找到;第八节每条建议都要有 claim。
不过的节带着失败原因重写一次,仍不过则任务失败、释放预留。
## 硬红线
1. `scripts/jyotish_api_server.py` 不增长(类方法数、`__new__` 伪造点、行数护栏);新接口在独立模块,主文件只加注册行。`app/(app)/page.tsx` 的 `Home()` 状态数不增长。
2. 上游 `skills/jyotish-full-synastry-reading/` 的脚本与参考文件**原样搬入**,`diff` 为空;不得为了让我们这边的测试通过去改上游文件。
3. 报告正文与页面不得出现:分数或百分比作标题、「合 / 不合」「相合」「会不会在一起」「适合 / 不宜结婚」「分离」「寿命」「灵魂伴侣」「注定」「一定会」,补充方法名(宿曜、西占、组合盘、问卦),私有证据词(`execution_receipt`、`reader_section_brief` 等)。质检必须拦住这些。
3. 报告正文八节不得出现任何分数、Koota、星曜、宫位、分盘、大运等术语,以及质检第 3 条的判断禁表、补充方法名、私有证据词。质检必须拦住这些。术语只允许出现在默认折叠的「技术依据摘要」和对话里。
4. 模型提示不得带出生日期、时间、出生地和经纬度;证据包快照只存在报告自己的行里,随报告删除;日志不打证据包和正文。
5. 扣点:任务失败、超时、质检两轮不过、用户中途删报告 → 释放不扣;读已有报告不预留;英文版不扣点。模型 token 用量照常记账(`kind: "synastry_full"`),进成本统计。
6. 动表的轮次真跑 `npm run test:db`;只加列、加枚举值、加键,不删旧列(AGENTS §7.6)。
@@ -80,20 +108,20 @@
| # | 内容 | 验收标准 |
| --- | --- | --- |
| T1 | **数据**:`personal_reports.report_type` 加 `synastry_full`;加两人引用列(另一人 `partner_chart_profile_id` 或等价字段,本人用约定值)与 `synastry_report_id`(指向 #79 的 `synastry_reports` 行,带两人出生资料指纹与 `rules_version`)。证据包快照、中文九节、质检回执存进 `personal_report_sections`(与个人报告快照同一套)。zod、v2 schema、列表标签同步 | `test:db`:同一对人同指纹第二次找到已有报告;任一人改资料后找不到;删任一人后报告与合盘行一并消失 |
| T1 | **数据**:`personal_reports.report_type` 加 `synastry_full`;加两人引用列(另一人 `partner_chart_profile_id` 或等价字段,本人用约定值)与 `synastry_report_id`(指向 #79 的 `synastry_reports` 行,带两人出生资料指纹与 `rules_version`)。证据包快照、中文八节与 claims、质检回执存进 `personal_report_sections`(与个人报告快照同一套)。zod、v2 schema、列表标签同步 | `test:db`:同一对人同指纹第二次找到已有报告;任一人改资料后找不到;删任一人后报告与合盘行一并消失 |
| T2 | **计费键 `synastry.full`**:一个迁移把键加进实证 5 列出的所有 SQL CHECK 与函数;TS 侧同步所有枚举与后台标签(「完整合盘报告」);不写价格 | `test:db`:未发布价格时创建报告返回与个人报告同样的「暂未定价」错误;发布后预留、结算、释放三条路径都绿;后台定价页能看到新键 |
| T3 | **创建入口**:`POST /api/reports` 接受 `reportType: "synastry_full"` + 两个人物 id(同一人 400,不属于本人的 id 拒绝);命中已有报告直接返回不预留;`compatibility` 开关与 `report_center` 开关都要开;沿用每日上限、限流、同时只能生成一份 | 路由测试覆盖以上每条 |
| T4 | **worker 分支**:按上面的链路实现;阶段进度 `loading_context` → `computing_charts` → `writing_report` → `checking_report` → `persisting_report`;Python 429 走现有 `upstream_busy` 重试;每节模型调用有超时,单节失败只重试该节 | 单测:一节失败只重写这一节;质检两轮不过任务失败并释放预留;删报告释放预留 |
| T5 | **质检包装层** `scripts/synastry_full_delivery_gate.py` + 薄注册接口(或 worker 直接调 CLI,二选一写进 PROGRESS) | pytest:用上游 `test_bilingual_synastry_delivery` 里的中英样例拆开后,中文、英文各自通过;去掉一个标题、加一个禁词、首句改成「这段关系的核心不是……」各失败一次 |
| T6 | **英文版按需生成**:`POST /api/reports/:id/english` 建任务(不扣点);同一报告只会有一个英文任务;失败可重试 | 路由与 worker 测试;英文提示里不出现中文稿内容(断言提示文本不含中文九节正文) |
| T7 | **阅读页**:合盘报告标题「A × B · 婚恋合盘」;九节正文 → 「传统配对参考」(复用 #79 的配对表、火星冲克、婚姻宫压力组件,去掉落点档位与三句话)→「可以一起聊聊」四问 → 脚注。切英文时若还没生成,原地显示等待面板(一次揭幕),生成好整份替换 | 渲染测试用名人 golden;手机 390 px 无横向滚动;`next build` 后 `/` 仍 Static,首屏 gzip ±2% |
| T8 | **入口与列表**:婚恋合盘页「开始合盘」改为创建报告并跳到阅读页,按钮下点数说明读 `synastry.full` 定价;「之前的合盘」列出合盘报告;报告中心列表里合盘报告的卡片写「A × B 婚恋合盘」;删人确认句的「K 份合盘」计入合盘报告 | 渲染与路由测试 |
| T5 | **质检** `scripts/synastry_full_reader_gate.py`(中英两套规则)+ 薄注册接口(或 worker 直接调 CLI,二选一写进 PROGRESS) | pytest:准则文件第四节的示例(补够字数的版本)通过;分别塞进「金星」「七宫」「22 分」「注定」「2027 年结婚」「宿曜」「execution_receipt」、首句改成「这段关系的核心不是……」、删掉一节、去掉某节 claims,各失败一次;英文同理 |
| T6 | **英文版按需生成**:`POST /api/reports/:id/english` 建任务(不扣点);同一报告只会有一个英文任务;失败可重试 | 路由与 worker 测试;英文提示里不出现中文稿内容(断言提示文本不含中文八节正文) |
| T7 | **阅读页**:合盘报告标题「A × B · 婚恋合盘」;八节正文(每节末尾「问问这一段的依据」入口,E5d,对话侧未合入时隐藏)→「可以一起聊聊」四问 →「技术依据摘要」(默认折叠,内容按产品提供的格式)→ 脚注(「合盘描述的是互动的质地,走不走、怎么走,仍然是你们两个人的选择」)。切英文时若还没生成,原地显示等待面板(一次揭幕),生成好整份替换 | 渲染测试用名人 golden;**正文八节**过一遍质检第 2、3 条禁表为空(折叠摘要不在此列);摘要默认收起;手机 390 px 无横向滚动;`next build` 后 `/` 仍 Static,首屏 gzip ±2% |
| T8 | **婚恋合盘页(从 #79 移来)与入口**:侧栏「婚恋合盘」;选人页(两个自定义下拉,底行「+ 新增星盘」复用星盘档案新建表;居中按钮,按钮下点数说明读 `synastry.full` 定价);「开始合盘」创建报告并跳到阅读页;「之前的合盘」列出合盘报告;档案页「和我合盘」改跳本页并预选两人;报告中心列表里合盘报告的卡片写「A × B 婚恋合盘」;删人确认句加「K 份合盘」 | 渲染与路由测试;20 人时下拉可滚动 |
| T9 | **上游测试搬入**:五个测试文件 + #79 已有的 `test_marriage_match_product.py`;`aspects.py` 同步上游 `bde5d615` 版本(方向角与速度判相位),跑全量 pytest 与基线逐条比对(相位值变了,报告类测试可能受影响,逐条写原因);读 `jyotish-app/main.js` 那条与调 `_compute_marriage_match` 那条改写为我们的等价断言(三栏说明) | 上游 49 条全过或有三栏说明;全量 pytest 无新增失败 |
| T10 | **记录**:DESIGN.md(阅读页附录与英文按需生成的等待态)、VOICE.md(报告禁词、标题格式)、CHANGELOG、PROGRESS(与上游的差异:质检包装层、两条改写的测试)、`docs/testing/synastry-full-report-20261018.md` 真人清单(生成一份、再次打开不扣点、切英文、删人连带删除、失败退点、手机宽) | 文档齐 |
| T10 | **记录**:BUG-1373(#79 登记为 investigating,本单修复后改 resolved);DESIGN.md(选人页、阅读页、英文按需生成的等待态)、VOICE.md(引用准则文件;报告禁表、标题格式)、CHANGELOG、PROGRESS(与上游的差异:质检包装层、两条改写的测试)、`docs/testing/synastry-full-report-20261018.md` 真人清单(生成一份、再次打开不扣点、切英文、删人连带删除、失败退点、手机宽) | 文档齐 |
## 让步顺序
先保 T1–T5(存对、扣对、写得出、拦得住);T7 不可省;T6 若来不及,英文切换先显示「英文版即将提供」并写进 BLOCKED,不得用机器翻译中文稿顶替;T9 的 `aspects.py` 同步若牵动过多既有测试,可拆成单独小单先行,但必须在本单合入前完成。任何一项过不了红线就停下写进 PROGRESS,不得放宽口径。
先保 T1–T5(存对、扣对、写得出、拦得住);T7 不可省;「技术依据摘要」的内容格式未到时,先做折叠容器与数据接口,摘要内容一项写进 BLOCKED,不得自拟格式;T6 若来不及,英文切换先显示「英文版即将提供」并写进 BLOCKED,不得用机器翻译中文稿顶替;T9 的 `aspects.py` 同步若牵动过多既有测试,可拆成单独小单先行,但必须在本单合入前完成。任何一项过不了红线就停下写进 PROGRESS,不得放宽口径。
## 开工前置命令