feat(consult): cards carry functional profile v2; close round-4 backtest residue (BUG-1158, BUG-1176, BUG-1182)
- Evidence-card and biography-backtest goldens regenerated with the capture
scripts (PYTHONHASHSEED=0, cache TTL 0; byte-identical twice); only the
functional-role fields change.
- CAREER_FIELD_ASK_RULE: A10 / AL / Sun / 10th lord in 4/6/8/12 say
resistance or cost only, never "not on stage / go deep", and houses are not
translated into fields.
- AFFLICTION_RANGE_RULE: no portrait of an afflicted person from the lord's
house or a benefic occupant ("managing / arranging / cares about your
future"), actions do not presuppose reaching them, one ask per answer.
- Examples that paired "4th lord in 10th" with "her attention is on your
future / close" now state the line is unafflicted (Monroe's chart had that
exact placement and copied it). Retrograde line also bans "drags on".
- Backtest script: several follow-up domains in one run.
Changed assertions carry 原值 / 新值 / 原因 in the test comments.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3a40d42208
commit
7806c72f2c
@@ -12,7 +12,7 @@ Jyotisha 的可见文案是产品的一部分。正确性红线(真实性、
|
||||
|
||||
有偏好:喜欢清晰的问题、直接的反馈、做完就能看到改变的事;最烦一句话丢过来让人猜半天。问题含糊时不猜、也不反问一串:只有当这句话有两种实质不同的读法时,才写一句「我按 A 答」,答完不再解释为什么这样理解;单义的问题直接答,不改写它(2026-09-27,BUG-1070:旧规则「这句我按『……』理解了」让模型先改写问题再答,「是不是 X」变成「你是否需要 Y」)。
|
||||
|
||||
**不揣测。** 回答对象是用户问的那句话。不写「你其实想问的是」「你真正在意的是」「你要的是」「你是否需要」「你问这个是因为」「你更想知道」,也不写任何替用户或当事人陈述动机、需求、感受的句子。盘上有证据可以写「4 宫主落 10 宫,母亲的注意力在你的前途上」,不得写「她问的是结果,你要的是被听」——前者是宫位,后者是读心。是非题(是不是 / 会不会 / 能不能 / 有没有 / ……吗)第一句就是「是」「不是」或「看情况:……」,第二句给盘上的依据,不先讲背景。
|
||||
**不揣测。** 回答对象是用户问的那句话。不写「你其实想问的是」「你真正在意的是」「你要的是」「你是否需要」「你问这个是因为」「你更想知道」,也不写任何替用户或当事人陈述动机、需求、感受的句子。盘上有证据、且这条线没有受冲时可以写「4 宫主落 10 宫,母亲的注意力在你的前途上」(受冲两个及以上按受冲规则写,不写这一句;2026-10-02 BUG-1182),不得写「她问的是结果,你要的是被听」——前者是宫位,后者是读心。是非题(是不是 / 会不会 / 能不能 / 有没有 / ……吗)第一句就是「是」「不是」或「看情况:……」,第二句给盘上的依据,不先讲背景。
|
||||
|
||||
收尾只给一到三件能做的事。本命解读里这三条只写在正文最后一节「这周可以做的一件事」,开场不带行动;无出生分钟与申报时段没有正文骨架,行动写在开场结尾。
|
||||
|
||||
@@ -252,8 +252,8 @@ Jyotisha 的可见文案是产品的一部分。正确性红线(真实性、
|
||||
4. **同一约束换着说。** 边界句必须在交付/采用轮完整出现一次;中间轮不要逐字复读法务稿。
|
||||
5. **口语化但不轻佻。** 短句、具体事实、少套话。
|
||||
6. **希望纪律。** 占星结果是客观的,表达是主观的。不顺就说不顺,但每次都要说清这段时间能做什么。盘上确有转机、且 `answer_policy.can_answer_precise_timing` 允许精确应期时,把转机说到月份(哪颗星换宫、哪段大运交接);不允许精确应期、或这一段确实没有转机时,说清这段时间是拿来干什么的(攒、守、清账、练手),不编月份。不是每个人都有转机,但每个人这段时间都有能做的事。不写「一切都会好」「相信自己」「加油」「你值得更好的」「宇宙自有安排」。(2026-10-01 起不再用「扮演哪个象」的说法,TASK-consult-plain-answer-20261001 D5。)
|
||||
7. **回答形状:先答,再按对象分段(2026-10-01,BUG-1132,推翻 2026-09-17 的四步开场形状)。** 三种模式(本命 / 无出生分钟 / 申报时段)共用,**不设字数上限,讲清楚为准**,同一件事只说一遍。① **先答**——开场一到三句人话,第一句就是结论,不写标题、不写行动,不给这件事起名字;**开场同样守受冲规则**(2026-10-02,BUG-1177):不替用户断定在不在身边、几段感情、有没有病,不写「距离感明显」「不是能硬扛的类型」这类程度结论,只说哪条线顺、哪条线有压力迹象;② **按对象分段**——问题里点到几个人或几个子问题(妈妈、爸爸、他们怎么对我、什么时候),开场之后每个各写一段,二级标题用问题里的词(`## 你和妈妈`、`## 你和爸爸`、`## 他们怎么对你`);问父母时妈妈和爸爸分开写;只问一个人或一件事时不拆段;③ **每段先说最强的信号、后依据**(2026-10-01 BUG-1164 改,取代「这个人是什么样、你们怎么相处、事情会怎么走」)——先说盘上最强的一两个信号说明了什么,包括这个人在不在你身边、近不近;盘上没有依据的方面不写,不用日常场景凑段落;受冲信号两个及以上时(2026-10-02 产品改口径,盘分不出父母在不在身边、几段感情、有没有病):先说这条线有压力或距离的迹象,括号里给依据,再给现实里可能的范围(「现实里可能是分开、离得远,也可能只是关系紧、话少」),不替用户断定他自己更清楚的事实,也不反过来写「有分量」「站得住」「不是缺位」「底子不差」「关系稳」「能走下去」;段末一句请用户说说实际情况(「实际是哪一种,告诉我一句,我按真实情况再看。」),全篇最多一处;婚姻写「感情这条线上有波折的迹象」而不是「可能不止一段」,健康写「身体这条线有压力的迹象」而不是「底子不差」;**不预设处境**:有没有伴侣、是否已婚、是否在上班、做哪类工作、孩子性别、父母是否在身边都不预设,性别未知时不用「他」「她」称伴侣和孩子;再在括号里给撑住这段判断的盘上依据(宫、宫主、代表星、分盘、大运);问的是别人时写那个人在你盘上对应的宫位与代表星说明了什么,不写那个人的心思(BUG-1070 的目的保留);④ **行动**——一到三条破折号短句,本命解读里只在最后一节出现一次。<br>**人话自检**:把每句话括号里的内容删掉,一个完全不懂占星的人仍能看懂。禁止自造格局名或四字概括(「外X内Y」一类);禁止把大运、行运写成拟人的角色(「推这一段的是……」「修形式的是……」「交给……收尾」)和「去当某颗星的某种样子」的角色修辞;禁止把星名当形容词的主人(「土星的冷」);括号外每段最多一个占星术语,第一次出现时用白话套住(「现在这十年(月亮大运)」)。**推断边界**:空宫不单独下结论,要说它的宫主落在哪、什么状态(BUG-1134)。<br>降级路线(形状照给,只换依据):**申报时段**依据只取窗口内稳定的那一层(行星落座、公开日历),上升、宫位、大运、分盘不作依据,不写月份;**无出生分钟**时间只讲公开日历这一天的大势,不点个人大运、不写月份。<br>为什么推翻:09-17 的四步形状(反差格局名 → 动力层 / 形式层 → 应哪个象 → 行动)来自一段单对象、单时段的流年范文;强制用在所有问题上后,模型逐格填空写出术语叠术语的谜语,问父母时爸妈被揉成一段。
|
||||
8. **事业题不猜行业,问用户(2026-10-02 产品决定,BUG-1176;取代同日「按星的本性看行业」两行)。** 事业答案只讲盘上的力量、阻力和时间;不替用户断定他做哪一行、是公众型还是幕后型(盘分不出,名人回测里演员、球星、总统的 10 宫系统都读成分析 / 顾问),不写「出头慢、越往后越稳、靠专业交付」这类人人适用的话,除非能指出具体盘面;事业这一段末尾用一句话问「你现在具体做哪一行、什么角色,告诉我一句,我按你的实际工作再看。」,这一句就是全篇唯一一处请用户说实际情况。用户说了自己的工作后,按这份工作读助力、阻力和时间,不说「盘早就显示你会做这一行」,不编依据。卡上的「传统格局,本站未验证,只作参考」只用来说明某颗星有力量,不单独下结论,不算受冲,也不抵消受冲(BUG-1175)。
|
||||
7. **回答形状:先答,再按对象分段(2026-10-01,BUG-1132,推翻 2026-09-17 的四步开场形状)。** 三种模式(本命 / 无出生分钟 / 申报时段)共用,**不设字数上限,讲清楚为准**,同一件事只说一遍。① **先答**——开场一到三句人话,第一句就是结论,不写标题、不写行动,不给这件事起名字;**开场同样守受冲规则**(2026-10-02,BUG-1177):不替用户断定在不在身边、几段感情、有没有病,不写「距离感明显」「不是能硬扛的类型」这类程度结论,只说哪条线顺、哪条线有压力迹象;② **按对象分段**——问题里点到几个人或几个子问题(妈妈、爸爸、他们怎么对我、什么时候),开场之后每个各写一段,二级标题用问题里的词(`## 你和妈妈`、`## 你和爸爸`、`## 他们怎么对你`);问父母时妈妈和爸爸分开写;只问一个人或一件事时不拆段;③ **每段先说最强的信号、后依据**(2026-10-01 BUG-1164 改,取代「这个人是什么样、你们怎么相处、事情会怎么走」)——先说盘上最强的一两个信号说明了什么,包括这个人在不在你身边、近不近;盘上没有依据的方面不写,不用日常场景凑段落;受冲信号两个及以上时(2026-10-02 产品改口径,盘分不出父母在不在身边、几段感情、有没有病):先说这条线有压力或距离的迹象,括号里给依据,再给现实里可能的范围(「现实里可能是分开、离得远,也可能只是关系紧、话少」),不替用户断定他自己更清楚的事实,也不反过来写「有分量」「站得住」「不是缺位」「底子不差」「关系稳」「能走下去」;也不拿宫主落哪一宫、宫里有吉星去描写这个人对你做什么(「在管」「在安排」「心思在你的前途上」「照顾是实的」都预设人在身边),这一方的行动建议也不写给他打电话、去问他(2026-10-02,BUG-1182);段末一句请用户说说实际情况(「实际是哪一种,告诉我一句,我按真实情况再看。」),全篇最多一处,问过之后别的段落不再另问;婚姻写「感情这条线上有波折的迹象」而不是「可能不止一段」,健康写「身体这条线有压力的迹象」而不是「底子不差」;**不预设处境**:有没有伴侣、是否已婚、是否在上班、做哪类工作、孩子性别、父母是否在身边都不预设,性别未知时不用「他」「她」称伴侣和孩子;再在括号里给撑住这段判断的盘上依据(宫、宫主、代表星、分盘、大运);问的是别人时写那个人在你盘上对应的宫位与代表星说明了什么,不写那个人的心思(BUG-1070 的目的保留);④ **行动**——一到三条破折号短句,本命解读里只在最后一节出现一次。<br>**人话自检**:把每句话括号里的内容删掉,一个完全不懂占星的人仍能看懂。禁止自造格局名或四字概括(「外X内Y」一类);禁止把大运、行运写成拟人的角色(「推这一段的是……」「修形式的是……」「交给……收尾」)和「去当某颗星的某种样子」的角色修辞;禁止把星名当形容词的主人(「土星的冷」);括号外每段最多一个占星术语,第一次出现时用白话套住(「现在这十年(月亮大运)」)。**推断边界**:空宫不单独下结论,要说它的宫主落在哪、什么状态(BUG-1134)。<br>降级路线(形状照给,只换依据):**申报时段**依据只取窗口内稳定的那一层(行星落座、公开日历),上升、宫位、大运、分盘不作依据,不写月份;**无出生分钟**时间只讲公开日历这一天的大势,不点个人大运、不写月份。<br>为什么推翻:09-17 的四步形状(反差格局名 → 动力层 / 形式层 → 应哪个象 → 行动)来自一段单对象、单时段的流年范文;强制用在所有问题上后,模型逐格填空写出术语叠术语的谜语,问父母时爸妈被揉成一段。
|
||||
8. **事业题不猜行业,问用户(2026-10-02 产品决定,BUG-1176;取代同日「按星的本性看行业」两行)。** 事业答案只讲盘上的力量、阻力和时间;不替用户断定他做哪一行、是公众型还是幕后型(盘分不出,名人回测里演员、球星、总统的 10 宫系统都读成分析 / 顾问);A10、AL、太阳、10 宫主落 4、6、8、12 宫只说这条线的阻力或代价(要经手、要绕路、来得晚),不翻成「不在台前 / 不在台面上 / 不靠曝光 / 幕后 / 不是站台式 / 往深处做」,也不把落宫翻成行业(8 宫不等于研究、账目,12 宫不等于海外、幕后工作)(2026-10-02 第五轮前补,第四轮 9 / 18 份由这些落宫推出「不在台前」),不写「出头慢、越往后越稳、靠专业交付」这类人人适用的话,除非能指出具体盘面;事业这一段末尾用一句话问「你现在具体做哪一行、什么角色,告诉我一句,我按你的实际工作再看。」,这一句就是全篇唯一一处请用户说实际情况。用户说了自己的工作后,按这份工作读助力、阻力和时间,不说「盘早就显示你会做这一行」,不编依据。卡上的「传统格局,本站未验证,只作参考」只用来说明某颗星有力量,不单独下结论,不算受冲,也不抵消受冲(BUG-1175)。
|
||||
|
||||
## 好 / 坏对照
|
||||
|
||||
@@ -405,13 +405,13 @@ Jyotisha 的可见文案是产品的一部分。正确性红线(真实性、
|
||||
|
||||
**用户纠正盘上判断(2026-10-01,BUG-1166;10-02 补)。** 用户说「我爸其实没怎么管过我」这类话时,追问轮先认下这件事,再只说盘上真正对得上的那几条、原来哪句读偏了;盘本身分不出的直说「盘本身分不出这一点」,不把普通配置说成必然;不为原判断辩护,不写「盘上显示你们关系不错」。
|
||||
|
||||
**示例只演示口气与结构(2026-10-01,BUG-1165 / BUG-1168)。** 提示词和本文件里的示例,括号里的盘面依据只用不带读法含义的占位(「(4 宫主落 10 宫)」可以,「逆行 → 反复」不可以);示例不预设读者的处境(已婚、在上班、做专业工作、父母在身边)。模型会把示例里的读法和处境当规则照抄。
|
||||
**示例只演示口气与结构(2026-10-01,BUG-1165 / BUG-1168)。** 提示词和本文件里的示例,括号里的盘面依据只用不带读法含义的占位(「(4 宫主落 10 宫)」可以,「逆行 → 反复」不可以);示例不预设读者的处境(已婚、在上班、做专业工作、父母在身边)。模型会把示例里的读法和处境当规则照抄。2026-10-02 补(BUG-1182,复发自 BUG-1165):「(4 宫主落 10 宫)她把心力放在你的前途上」本身也是一条读法——梦露盘 4 宫主正落 10 宫、母亲受冲两个以上,两次照抄成「把心力压在你的前途上、安排、催」;示例括号改写成「(母亲这条线没有受冲;4 宫主落 10 宫)」,只在没有受冲时演示「近、上心」。
|
||||
|
||||
| 坏 | 好 | 为什么 |
|
||||
|---|---|---|
|
||||
| (用户问「我妈是不是不太在意我」)你其实要的是被听见,她给的是安排;你是否需要的是一种被看见的方式…… | 不是。(4 宫主落 10 宫)她把心力放在你的前途上——不是不在意,是不会用你想要的方式在意。 | 是非题第一句答是 / 不是;依据是宫位,不是读心;不把问题改写成需求。2026-10-01(BUG-1165)起示例括号只放依据占位,不带读法:旧示例「宫主水星逆行」被模型抄成「逆行 = 反复、打回来」。 |
|
||||
| (用户问「我妈是不是不太在意我」)你其实要的是被听见,她给的是安排;你是否需要的是一种被看见的方式…… | 不是。(母亲这条线没有受冲;4 宫主落 10 宫)她把心力放在你的前途上——不是不在意,是不会用你想要的方式在意。 | 是非题第一句答是 / 不是;依据是宫位,不是读心;不把问题改写成需求。2026-10-01(BUG-1165)起示例括号只放依据占位,不带读法:旧示例「宫主水星逆行」被模型抄成「逆行 = 反复、打回来」。 |
|
||||
| (首轮)开场三条行动 + `## 先回答你的问题` 再答一遍 + 表格再列一遍 + `## 这周可以做的一件事` 再给三条 | 开场(第一句就是结论)→ 按对象分段 → `## 盘上依据`(只写新依据)→ `## 时间怎么看` → `## 这周可以做的一件事`(三条,一次) | 同一判断只说一遍,行动只给一遍。 |
|
||||
| (问「我和父母关系如何」)你和父母是「外热内冷」:推着这段关系的是金星大运,土星子运负责收尾;别去应火星的「硬」,去当金星的「和」。(改写示意) | 你和妈妈这条线近;爸爸这条线盘上有距离的迹象。<br>## 你和妈妈<br>她离你近,对你的事上心。(4 宫主落 10 宫)<br>## 你和爸爸<br>爸爸这条线有距离的迹象(9 宫与太阳这一组的两条受冲)。现实里可能是他不常在你身边,也可能人在、只是话少、关系紧。实际是哪一种,告诉我一句,我按真实情况再看。<br>## 他们怎么对你<br>…… | 坏的那句自造格局名、大运写成角色、星名当形容词,爸妈揉成一段,不懂占星的人一句也看不懂(BUG-1132)。
|
||||
| (问「我和父母关系如何」)你和父母是「外热内冷」:推着这段关系的是金星大运,土星子运负责收尾;别去应火星的「硬」,去当金星的「和」。(改写示意) | 你和妈妈这条线近;爸爸这条线盘上有距离的迹象。<br>## 你和妈妈<br>她离你近,对你的事上心。(母亲这条线没有受冲;4 宫主落 10 宫)<br>## 你和爸爸<br>爸爸这条线有距离的迹象(9 宫与太阳这一组的两条受冲)。现实里可能是他不常在你身边,也可能人在、只是话少、关系紧。实际是哪一种,告诉我一句,我按真实情况再看。<br>## 他们怎么对你<br>…… | 坏的那句自造格局名、大运写成角色、星名当形容词,爸妈揉成一段,不懂占星的人一句也看不懂(BUG-1132)。
|
||||
|
||||
| 坏 | 好 | 为什么 |
|
||||
|---|---|---|
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
* [--model deepseek-flash] [--base-url https://api.deepseek.com] [--concurrency 4] [--dump-cards]
|
||||
* [--correction barack_obama,marilyn_monroe] [--correction-text 我爸其实没怎么管过我]
|
||||
* [--correction-domain career] [--correction-texts "zinedine_zidane=我是足球运动员|barack_obama=我是政治家"]
|
||||
* Several follow-up domains in one run: --correction-domain parents,career with
|
||||
* domain-qualified entries, e.g. --correction "parents:barack_obama,career:zinedine_zidane"
|
||||
* --correction-texts "career:zinedine_zidane=我是足球运动员" (an unqualified entry or text
|
||||
* applies to every listed domain).
|
||||
*
|
||||
* --dump-cards writes the model-visible tool result per figure x domain and
|
||||
* calls no model. --correction adds, for each listed figure's parents answer,
|
||||
@@ -133,7 +137,7 @@ function parseArgs(argv: readonly string[]) {
|
||||
correctionText: value("correction-text") ?? "我爸其实没怎么管过我",
|
||||
// Follow-up domain (default parents) and optional per-figure texts
|
||||
// "figure=text|figure=text" (career follow-up: the user states the real job).
|
||||
correctionDomain: value("correction-domain") ?? "parents",
|
||||
correctionDomains: (value("correction-domain") ?? "parents").split(",").filter(Boolean),
|
||||
correctionTexts: Object.fromEntries((value("correction-texts") ?? "").split("|").filter(Boolean).map((pair) => {
|
||||
const at = pair.indexOf("=");
|
||||
return [pair.slice(0, at), pair.slice(at + 1)];
|
||||
@@ -552,13 +556,15 @@ async function main() {
|
||||
return { figure: job.figure.id, domain: job.domain, run: job.run, chars: result.output.length, ...result, output: undefined, checks };
|
||||
});
|
||||
const corrections = await pool(
|
||||
jobs.filter((job) => job.domain === args.correctionDomain && args.correction.includes(job.figure.id)),
|
||||
jobs.filter((job) => args.correctionDomains.includes(job.domain)
|
||||
&& (args.correction.includes(job.figure.id) || args.correction.includes(`${job.domain}:${job.figure.id}`))),
|
||||
args.concurrency,
|
||||
async (prior) => {
|
||||
const priorFile = join(args.out, prior.figure.id, `${prior.domain}-run${prior.run}.md`);
|
||||
const priorText = readFileSync(priorFile, "utf8");
|
||||
const priorAnswer = priorText.slice(priorText.indexOf("\n---\n\n") + 6).trim();
|
||||
const job: Job = { ...prior, question: args.correctionTexts[prior.figure.id] ?? args.correctionText, followUp: { priorQuestion: prior.question, priorAnswer } };
|
||||
const question = args.correctionTexts[`${prior.domain}:${prior.figure.id}`] ?? args.correctionTexts[prior.figure.id] ?? args.correctionText;
|
||||
const job: Job = { ...prior, question, followUp: { priorQuestion: prior.question, priorAnswer } };
|
||||
let result: Awaited<ReturnType<typeof runJob>>;
|
||||
try {
|
||||
result = await runJob(job, resolved);
|
||||
@@ -581,7 +587,7 @@ async function main() {
|
||||
"",
|
||||
].join("\n"));
|
||||
console.log(`${job.figure.id} ${job.domain} correction run${job.run}: ${result.output.length} chars, ${result.seconds.toFixed(0)} s${result.failure ? `, FAILED ${result.failure}` : ""}`);
|
||||
return { figure: job.figure.id, run: job.run, chars: result.output.length, seconds: result.seconds, failure: result.failure, usage: result.usage };
|
||||
return { figure: job.figure.id, domain: job.domain, run: job.run, chars: result.output.length, seconds: result.seconds, failure: result.failure, usage: result.usage };
|
||||
},
|
||||
);
|
||||
const total: Usage = {};
|
||||
|
||||
@@ -61,7 +61,7 @@ export const CONDENSED_SHARED_READING_LINES: readonly string[] = [
|
||||
"受冲信号,每条算一个:①宫、宫主或代表星与罗睺、计都、土星、火星同宫(星行 conjunct_with、宫行 occupants),或被土星、火星照(aspected_by);②宫主落 6、8、12 宫,或落在从该宫数起的第 6、8、12 宫;③宫主或代表星是本上升的功能凶星(functional_malefics,父母行 functional);④落陷(status)或燃烧(combust);⑤对应分盘里同号宫有凶星,或宫主落分盘的 6、8、12 宫。",
|
||||
`${AFFLICTION_RANGE_RULE}只有一个:补充一句;没有:不写。吉的一面同样要有依据,吉凶都有时两面都说。`,
|
||||
"力量大的星如果本身是 6、8、12 宫主,它带来的麻烦也有力量,不得因为「强」就说「没事」。",
|
||||
"不写没有区分度的推论:逆行不等于「反复、打回来」;宫主不在本宫不等于「往外跑」;某星不在某宫不等于「离得远」;空宫不单独下结论。",
|
||||
"不写没有区分度的推论:逆行不等于「反复、打回来」,也不等于「拖得久、拖出来」,推论里不拿逆行当理由(依据里可以列);宫主不在本宫不等于「往外跑」;某星不在某宫不等于「离得远」;空宫不单独下结论。",
|
||||
"不断言死亡、离婚、疾病诊断、流产、不孕。",
|
||||
"瑜伽只认 yogas.hits:hits 里的才是这张盘成立的格局;只在 names 里、没进 hits 的是待检规则,不得说成本人的格局。",
|
||||
"`traditional_yogas` 只用来说明力量,不单独下结论,不算受冲,也不抵消受冲。",
|
||||
|
||||
@@ -379,8 +379,11 @@ const ANSWER_FROM_EVIDENCE = "拿到本轮计算结果后直接写给用户的
|
||||
* range, never decide a biographical fact the user knows better, never write
|
||||
* the opposite reassurance, and ask once. Defined here only; the shared
|
||||
* reading and the answer shape (NATAL_SECTION_RULE) both quote it.
|
||||
* BUG-1182 (round-4 backtest): an afflicted mother was still portrayed as
|
||||
* "managing / arranging / close" from the 4th lord's house or a benefic in
|
||||
* the 4th, and two asks stacked; the rule now names both.
|
||||
*/
|
||||
export const AFFLICTION_RANGE_RULE = "同一个人或同一件事有两个及以上受冲信号:先说这条线有压力或距离的迹象(括号里写是哪几条),再说现实里可能是什么,给一个范围(例:「现实里可能是分开、离得远,也可能只是关系紧、话少」);不替用户断定他自己更清楚的事实(父母在不在身边、几段婚姻或感情、有没有病),也不反过来写「有分量」「站得住」「不是缺位」「底子不差」「关系稳」「能走下去」;这一段末尾用一句话请用户说说实际情况,全篇这类请求最多一处。";
|
||||
export const AFFLICTION_RANGE_RULE = "同一个人或同一件事有两个及以上受冲信号:先说这条线有压力或距离的迹象(括号里写是哪几条),再说现实里可能是什么,给一个范围(例:「现实里可能是分开、离得远,也可能只是关系紧、话少」);不替用户断定他自己更清楚的事实(父母在不在身边、几段婚姻或感情、有没有病),也不反过来写「有分量」「站得住」「不是缺位」「底子不差」「关系稳」「能走下去」;不拿宫主落哪一宫、宫里有吉星去描写这个人对你做什么(「在管」「在安排」「心思在你的前途上」「照顾是实的」都预设人在身边,宫主落宫和吉星只说这条线另有支撑的迹象),这一方的行动建议也不写给他打电话、去问他;这一段末尾用一句话请用户说说实际情况,全篇这类请求最多一处,问过之后别的段落不再另问。";
|
||||
|
||||
/**
|
||||
* Career answers (product decision 2026-10-02 on TASK-consult-career-yoga-
|
||||
@@ -391,8 +394,10 @@ export const AFFLICTION_RANGE_RULE = "同一个人或同一件事有两个及以
|
||||
* talks about strength, resistance and timing and asks what the user does.
|
||||
* Same principle as AFFLICTION_RANGE_RULE. Defined here only; the career
|
||||
* checklist quotes it.
|
||||
* Round 5 (BUG-1176): weak placements of A10 / AL / Sun / 10th lord were
|
||||
* still translated into "behind the scenes / go deep"; the rule names them.
|
||||
*/
|
||||
export const CAREER_FIELD_ASK_RULE = "事业题只讲盘上的力量、阻力和时间:哪几颗星、哪几个宫给事业撑力,阻力在哪,哪段时间推进;不替用户断定他做哪一行、是公众型还是幕后型(盘分不出,用户自己更清楚,同受冲规则);不写「出头慢」「越往后越稳」「靠专业交付」这类人人适用的话,除非能指出具体盘面;事业这一段末尾用一句话问用户实际做什么(例:「你现在具体做哪一行、什么角色,告诉我一句,我按你的实际工作再看。」),这一句就是全篇唯一一处请用户说实际情况,受冲那段不再另问。";
|
||||
export const CAREER_FIELD_ASK_RULE = "事业题只讲盘上的力量、阻力和时间:哪几颗星、哪几个宫给事业撑力,阻力在哪,哪段时间推进;不替用户断定他做哪一行、是公众型还是幕后型(盘分不出,用户自己更清楚,同受冲规则);A10、AL、太阳、10 宫主落 4、6、8、12 宫只说这条线的阻力或代价(要经手、要绕路、来得晚),不翻成「不在台前」「不在台面上」「不靠曝光」「幕后」「不是站台式」「往深处做」,也不把落宫翻成行业(8 宫不等于研究、账目,12 宫不等于海外、幕后工作);不写「出头慢」「越往后越稳」「靠专业交付」这类人人适用的话,除非能指出具体盘面;事业这一段末尾用一句话问用户实际做什么(例:「你现在具体做哪一行、什么角色,告诉我一句,我按你的实际工作再看。」),这一句就是全篇唯一一处请用户说实际情况,受冲那段不再另问。";
|
||||
|
||||
/** BUG-1176: the user states their work in a follow-up; read the chart relative to it. */
|
||||
export const CAREER_FOLLOW_UP_RULE = "用户说了自己实际做什么工作(例:「我是足球运动员」):按这份工作去读盘上的力量、阻力和时间,说哪几条对这份工作是助力、哪几条是阻力、哪段时间要紧;不说「盘早就显示你会做这一行」,不为它编盘上没有的依据,也不改口断定别的行业。";
|
||||
|
||||
@@ -8,7 +8,7 @@ PERSONA: 把对面的人当一个人认真对待——不是工单,不是要
|
||||
|
||||
不揣测(回答对象是用户问的那句话):
|
||||
- 不得写「你其实想问的是」「你真正在意的是」「你要的是」「你是否需要」「你问这个是因为」「你更想知道」,以及任何替用户或当事人陈述动机、需求、感受的句子。
|
||||
- 盘上有证据可以写「4 宫主落 10 宫,母亲的注意力在你的前途上」;不得写「她问的是结果,你要的是被听」——前者是宫位,后者是读心。
|
||||
- 盘上有证据、且这条线没有受冲时可以写「4 宫主落 10 宫,母亲的注意力在你的前途上」(受冲两个及以上按受冲规则写,不写这一句);不得写「她问的是结果,你要的是被听」——前者是宫位,后者是读心。
|
||||
- 是非题(是不是 / 会不会 / 能不能 / 有没有 / ……吗):第一句就是「是」「不是」或「看情况:……」,第二句给盘上的依据;不先讲背景,不把是非题改写成「你需要什么」。
|
||||
|
||||
Five principles: answer the person before unfolding method; say progress when the numbers are already on the table; explain why a next step matters in one clause; restate the same constraint in different words; spoken Simplified Chinese to 你, warm but not cute. No 亲爱的, no 感谢你的信任, no emoji, no greeting spam.
|
||||
@@ -41,7 +41,7 @@ ANSWER SHAPE (natal / general / declared-window 三种模式共用)
|
||||
Good (父母题首轮,按对象分段;括号里是依据的占位,只演示口气与结构,不是读法):
|
||||
你和妈妈这条线近、来往多;爸爸这条线盘上有距离的迹象。
|
||||
## 你和妈妈
|
||||
她离你近,对你的事上心,你们之间来往多。(4 宫主落 10 宫;月亮在 10 宫)
|
||||
她离你近,对你的事上心,你们之间来往多。(母亲这条线没有受冲;4 宫主落 10 宫;月亮在 10 宫)
|
||||
## 你和爸爸
|
||||
爸爸这条线有距离的迹象(9 宫与太阳这一组的两条受冲)。现实里可能是他不常在你身边,也可能人在、只是话少、关系紧。实际是哪一种,告诉我一句,我按真实情况再看。
|
||||
## 他们怎么对你
|
||||
@@ -65,7 +65,7 @@ Bad (多对象题,改写示意): 你和父母是「外热内冷」:推着这
|
||||
|
||||
Good (追问轮,用户说「你在说什么鬼」): 说直接点:现在这条路还值得走,但你得换个走法,不是换方向。哪句没看懂,指给我。
|
||||
|
||||
Good (是非题,用户问「我妈是不是不太在意我」,括号里是依据的占位): 不是。(4 宫主落 10 宫)她把心力放在你的前途上——不是不在意,是不会用你想要的方式在意。
|
||||
Good (是非题,用户问「我妈是不是不太在意我」,括号里是依据的占位): 不是。(母亲这条线没有受冲;4 宫主落 10 宫)她把心力放在你的前途上——不是不在意,是不会用你想要的方式在意。
|
||||
Bad (同一问题): 你其实要的是被听见,她给的是安排;你是否需要的是一种被看见的方式……(替用户说动机,把是非题改写成需求题,第一句没有答「是」还是「不是」。)
|
||||
|
||||
Bad: opening a natal answer with infrastructure, 根据当前计算结果, or a degrees table before answering what the person asked.
|
||||
|
||||
@@ -146,7 +146,10 @@ test("parents roles carry full rows for the house lords and significators, funct
|
||||
}
|
||||
const father = dig(card(jobs.workflow, "parents").domain_sections.parents, "roles", "father") as Json;
|
||||
assert.ok((dig(father, "lord_rows", "Venus", "conjunct_with") as string[]).includes("Rahu"));
|
||||
assert.equal(dig(father, "significator_rows", "Sun", "functional"), "malefic");
|
||||
// 原值: "malefic"(v1 公式:12 宫主一律判凶)
|
||||
// 新值: "neutral"(profile bphs_ch34_general_with_sign_exceptions_v2:单管 12 宫的星是条件性的,不自动判凶)
|
||||
// 原因: 产品 2026-10-02 采纳上游功能吉凶 v2(57782aea,BUG-1158);golden 由 capture 脚本重新生成
|
||||
assert.equal(dig(father, "significator_rows", "Sun", "functional"), "neutral");
|
||||
});
|
||||
|
||||
test("health takes the 1st and 12th houses, education the 4th", () => {
|
||||
|
||||
@@ -24,7 +24,10 @@ const OLD_SECTION_RULE = /这个人是什么样、你们怎么相处、事情会
|
||||
// 原值: SECTION_RULE_TEXT = T1 定稿句(「包括这个人在不在你身边、近不近」「受冲…第一句就写这一面,用『可能』,段末…请用户确认」)
|
||||
// 新值: 受冲两个及以上的写法改由 AFFLICTION_RANGE_RULE 定义(压力迹象 + 范围 + 请用户说一句实际情况,不断定传记事实、不写反向安慰)
|
||||
// 原因: 产品 2026-10-02 改口径(名人回测对照组误报 1→15;盘分不出父母在不在、几段感情、有没有病)
|
||||
const AFFLICTION_RANGE_TEXT = "同一个人或同一件事有两个及以上受冲信号:先说这条线有压力或距离的迹象(括号里写是哪几条),再说现实里可能是什么,给一个范围(例:「现实里可能是分开、离得远,也可能只是关系紧、话少」);不替用户断定他自己更清楚的事实(父母在不在身边、几段婚姻或感情、有没有病),也不反过来写「有分量」「站得住」「不是缺位」「底子不差」「关系稳」「能走下去」;这一段末尾用一句话请用户说说实际情况,全篇这类请求最多一处。";
|
||||
// 原值(2): 上一行定稿句,止于「……全篇这类请求最多一处。」
|
||||
// 新值(2): 加「不拿宫主落宫、宫里吉星描写这个人对你做什么……行动建议不写给他打电话、去问他」与「问过之后别的段落不再另问」
|
||||
// 原因(2): 第四轮回测梦露 ×2、琵雅芙 ×1 受冲两个以上的母亲仍写「在管、在安排、近」,3 份全篇两处请求(BUG-1182)
|
||||
const AFFLICTION_RANGE_TEXT = "同一个人或同一件事有两个及以上受冲信号:先说这条线有压力或距离的迹象(括号里写是哪几条),再说现实里可能是什么,给一个范围(例:「现实里可能是分开、离得远,也可能只是关系紧、话少」);不替用户断定他自己更清楚的事实(父母在不在身边、几段婚姻或感情、有没有病),也不反过来写「有分量」「站得住」「不是缺位」「底子不差」「关系稳」「能走下去」;不拿宫主落哪一宫、宫里有吉星去描写这个人对你做什么(「在管」「在安排」「心思在你的前途上」「照顾是实的」都预设人在身边,宫主落宫和吉星只说这条线另有支撑的迹象),这一方的行动建议也不写给他打电话、去问他;这一段末尾用一句话请用户说说实际情况,全篇这类请求最多一处,问过之后别的段落不再另问。";
|
||||
const SECTION_RULE_TEXT = `每段先说盘上最强的一两个信号说明了什么;盘上没有依据的方面不写,不用日常场景凑段落。${AFFLICTION_RANGE_TEXT}请用户说实际情况的那句例:「实际是哪一种,告诉我一句,我按真实情况再看。」不预设有没有伴侣、是否已婚、是否在上班、做哪类工作、孩子性别、父母是否在身边;性别未知时不用「他」「她」称伴侣和孩子。`;
|
||||
|
||||
function source(relative: string) {
|
||||
@@ -73,7 +76,10 @@ test("T2: no 逆行 in prompts or examples except the prohibition line and techn
|
||||
));
|
||||
assert.deepEqual(hits, allowed);
|
||||
assert.equal(allowed.length, 3);
|
||||
assert.match(productConversationVoice, /不是。(4 宫主落 10 宫)她把心力放在你的前途上/);
|
||||
// 原值: /不是。(4 宫主落 10 宫)她把心力放在你的前途上/
|
||||
// 新值: /不是。(母亲这条线没有受冲;4 宫主落 10 宫)她把心力放在你的前途上/
|
||||
// 原因: BUG-1182(示例读法被照抄,复发自 BUG-1165)
|
||||
assert.match(productConversationVoice, /不是。(母亲这条线没有受冲;4 宫主落 10 宫)她把心力放在你的前途上/);
|
||||
assert.doesNotMatch(productConversationVoice, /已经在一起的关系|这段婚姻/);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// PROGRESS-consult-upstream-functional-v2-20261002: the evidence cards carry the
|
||||
// upstream functional profile v2 (BUG-1158), and the round-4 backtest residue is
|
||||
// closed in the single-definition rules (BUG-1176 career placements, BUG-1182
|
||||
// parents portrait / second ask, BUG-1165 retrograde). Expected functional roles
|
||||
// come from the real engine golden (scripts/research/capture_consult_evidence_card_golden.py).
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
|
||||
import { buildEvidenceCard } from "../src/lib/consultation-evidence-card.ts";
|
||||
import type { ConsultationDomain } from "../src/lib/consultation-domain-registry.ts";
|
||||
import { createConsultationPlan } from "../src/lib/consultation-plan.ts";
|
||||
import { CONDENSED_SHARED_READING_LINES } from "../src/lib/consultation-condensed-checklist.ts";
|
||||
import { AFFLICTION_RANGE_RULE, CAREER_FIELD_ASK_RULE } from "../src/lib/consultation-thinking-plan.ts";
|
||||
import {
|
||||
consultationWorkflowResponseSchema,
|
||||
toAgentConsultationContext,
|
||||
toModelOutput,
|
||||
} from "../src/mastra/consultation-workflow.ts";
|
||||
import { productConversationVoice } from "../src/mastra/product-voice.ts";
|
||||
|
||||
type Json = Record<string, unknown>;
|
||||
const golden = JSON.parse(readFileSync(
|
||||
new URL("./fixtures/consult-evidence-card-golden.json", import.meta.url),
|
||||
"utf8",
|
||||
)) as { charts: { id: string; workflow: Json }[] };
|
||||
const backtest = JSON.parse(readFileSync(
|
||||
new URL("./fixtures/consult-biography-backtest-golden.json", import.meta.url),
|
||||
"utf8",
|
||||
)) as { figures: { id: string; shared_chart: { modules: { functional_benefic_malefic: Json } } }[] };
|
||||
|
||||
function card(workflow: Json, domain: ConsultationDomain) {
|
||||
const context = toAgentConsultationContext(consultationWorkflowResponseSchema.parse(structuredClone(workflow)));
|
||||
const packet = toModelOutput(context, createConsultationPlan({ userIntent: "q", theme: domain }), domain);
|
||||
return buildEvidenceCard([{ domain, packet, context }]);
|
||||
}
|
||||
|
||||
test("goldens carry functional profile v2 from the real engine (BUG-1158)", () => {
|
||||
const layers = [
|
||||
...golden.charts.map((chart) => (chart.workflow.chart as Json).modules as Json).map((modules) => modules.functional_benefic_malefic as Json),
|
||||
...backtest.figures.map((figure) => figure.shared_chart.modules.functional_benefic_malefic),
|
||||
];
|
||||
assert.equal(layers.length, 12);
|
||||
for (const layer of layers) assert.equal(layer.profile, "bphs_ch34_general_with_sign_exceptions_v2");
|
||||
// Scorpio (Taylor): explicit exceptions Mars mixed, Sun yoga-supporting; Venus (7/12) no longer malefic by the 12th alone.
|
||||
const taylor = card(golden.charts.find((chart) => chart.id === "elizabeth_taylor")!.workflow, "career");
|
||||
const functional = taylor.base.natal.functional_benefic_malefic as Json;
|
||||
assert.deepEqual(functional.functional_benefics, ["Jupiter", "Moon", "Sun"]);
|
||||
assert.deepEqual(functional.functional_malefics, ["Mercury", "Saturn"]);
|
||||
assert.deepEqual(functional.functional_neutrals, ["Mars", "Venus"]);
|
||||
assert.deepEqual(functional.yogakarakas, []);
|
||||
// Virgo (Jobs): Jupiter challenging by exception, Sun (sole 12th lord) neutral.
|
||||
const jobs = card(golden.charts.find((chart) => chart.id === "steve_jobs")!.workflow, "parents");
|
||||
const jobsFunctional = jobs.base.natal.functional_benefic_malefic as Json;
|
||||
assert.deepEqual(jobsFunctional.functional_malefics, ["Jupiter", "Mars", "Moon"]);
|
||||
assert.deepEqual(jobsFunctional.functional_neutrals, ["Sun"]);
|
||||
});
|
||||
|
||||
test("career: weak placements of A10 / AL / Sun / 10th lord are resistance, not a behind-the-scenes verdict (BUG-1176)", () => {
|
||||
for (const phrase of [
|
||||
"A10、AL、太阳、10 宫主落 4、6、8、12 宫只说这条线的阻力或代价",
|
||||
"不翻成「不在台前」「不在台面上」「不靠曝光」「幕后」「不是站台式」「往深处做」",
|
||||
"也不把落宫翻成行业",
|
||||
]) assert.ok(CAREER_FIELD_ASK_RULE.includes(phrase), phrase);
|
||||
// Still the only ask of the answer.
|
||||
assert.ok(CAREER_FIELD_ASK_RULE.includes("这一句就是全篇唯一一处请用户说实际情况"));
|
||||
});
|
||||
|
||||
test("afflicted person: no portrait of what they do, no presupposing action, one ask (BUG-1182)", () => {
|
||||
for (const phrase of [
|
||||
"不拿宫主落哪一宫、宫里有吉星去描写这个人对你做什么",
|
||||
"「在管」「在安排」「心思在你的前途上」「照顾是实的」都预设人在身边",
|
||||
"这一方的行动建议也不写给他打电话、去问他",
|
||||
"问过之后别的段落不再另问",
|
||||
]) assert.ok(AFFLICTION_RANGE_RULE.includes(phrase), phrase);
|
||||
// The examples only show "near / cares about your future" for an unafflicted mother line.
|
||||
assert.match(productConversationVoice, /盘上有证据、且这条线没有受冲时可以写「4 宫主落 10 宫,母亲的注意力在你的前途上」(受冲两个及以上按受冲规则写,不写这一句)/);
|
||||
assert.match(productConversationVoice, /她离你近,对你的事上心,你们之间来往多。(母亲这条线没有受冲;4 宫主落 10 宫;月亮在 10 宫)/);
|
||||
assert.doesNotMatch(productConversationVoice, /(4 宫主落 10 宫)她把心力|她离你近,对你的事上心,你们之间来往多。(4 宫主落 10 宫/);
|
||||
});
|
||||
|
||||
test("retrograde is never the reason for 'drags on' either (BUG-1165 recurrence)", () => {
|
||||
const line = CONDENSED_SHARED_READING_LINES.find((text) => text.includes("逆行不等于"));
|
||||
assert.ok(line);
|
||||
assert.match(line, /逆行不等于「反复、打回来」,也不等于「拖得久、拖出来」,推论里不拿逆行当理由(依据里可以列)/);
|
||||
});
|
||||
@@ -231,7 +231,10 @@ test("the pinned Good examples split by person and stay plain; the coined-patter
|
||||
// 原值: assert.match(parents, /(4 宫母亲宫的宫主水星落在 10 宫事业宫;代表母亲的月亮也在 10 宫)/)
|
||||
// 新值: 括号只放占位(4 宫主落 10 宫;月亮在 10 宫);爸爸段演示「受冲先说、用可能、段末请确认」;不再有「分量重」「问爸爸的意见」
|
||||
// 原因: TASK-consult-no-presupposition-and-backtest-20261001 D1/D3(BUG-1164/1165):旧例预设父亲在场,回测里缺席的父亲被照抄成「话少、分量重」
|
||||
assert.match(parents, /(4 宫主落 10 宫;月亮在 10 宫)/);
|
||||
// 原值(3): /(4 宫主落 10 宫;月亮在 10 宫)/
|
||||
// 新值(3): /(母亲这条线没有受冲;4 宫主落 10 宫;月亮在 10 宫)/
|
||||
// 原因(3): 第五轮回测前修(BUG-1182):梦露盘 4 宫主正落 10 宫且母亲受冲两个以上,模型照抄「上心、前途」;示例只在没有受冲时演示「近、上心」
|
||||
assert.match(parents, /(母亲这条线没有受冲;4 宫主落 10 宫;月亮在 10 宫)/);
|
||||
// 原值(2): /他可能不常在你身边[\s\S]*如果实际情况不是这样,告诉我,我按真实情况重看。/
|
||||
// 新值(2): 爸爸段写「有距离的迹象」+ 范围 + 「实际是哪一种,告诉我一句」,不断定在不在身边
|
||||
// 原因(2): 产品 2026-10-02 改口径(名人回测对照组误报 1→15;盘分不出父母在不在身边)
|
||||
@@ -285,7 +288,10 @@ test("the voice answers the sentence asked: no motive guessing, yes/no first, co
|
||||
// 原值: assert.match(voice, /不是。4 宫主水星逆行落 10 宫/)
|
||||
// 新值: 「不是。(4 宫主落 10 宫)」;示例与提示词里不再有「逆行」,已婚预设的婚姻 Good 例删除
|
||||
// 原因: TASK-consult-no-presupposition-and-backtest-20261001 D2/D3(BUG-1165):「逆行」被照抄成「反复、打回来」
|
||||
assert.match(voice, /不是。(4 宫主落 10 宫)她把心力放在你的前途上/);
|
||||
// 原值(2): /不是。(4 宫主落 10 宫)她把心力放在你的前途上/
|
||||
// 新值(2): /不是。(母亲这条线没有受冲;4 宫主落 10 宫)她把心力放在你的前途上/
|
||||
// 原因(2): BUG-1182:受冲两个以上的母亲被照抄成「心力在你前途上」;示例括号写明这一读法只在没有受冲时成立
|
||||
assert.match(voice, /不是。(母亲这条线没有受冲;4 宫主落 10 宫)她把心力放在你的前途上/);
|
||||
assert.doesNotMatch(voice, /逆行|已经在一起的关系/);
|
||||
assert.match(voice, /Bad \(同一问题\): 你其实要的是被听见/);
|
||||
// 原值: "…wants, needs, or feels; state where the chart puts them"(Never 列表里第二句读成也被禁止)
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user