From fa6d026f1248a02449fe150c8936e72fc4ffad3b Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Thu, 1 Oct 2026 19:21:28 +0800 Subject: [PATCH] fix(consult): Chinese-only terms, one name per thing in answers (BUG-1148) Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE --- CHANGELOG.md | 5 +++++ docs/BUG_HISTORY.md | 15 +++++++++++++++ frontend/src/lib/consultation-thinking-plan.ts | 6 +++--- frontend/src/mastra/product-voice.ts | 1 + frontend/tests/consultation-thinking-plan.test.ts | 11 +++++++++++ .../tests/consultation-voice-contract.test.ts | 8 ++++++++ 6 files changed, 43 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5db33d4a..2a1c1344 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # 印度占星 Skill 更新日志 +## 2026-10-01 — 普通对话不再夹英文,名词叫法统一 + +- 回答里不再出现 sade sati、vargottama、rupas 这类英文或梵文音译;计都不再写成「克图」;大运下面的时段统一叫子运、小运;行星力量只说强、中、弱(BUG-1148)。 +- Skill 版本不变;不改数据库结构。 + ## 2026-10-01 — 生时校正点「采用」后的核对题有按钮了;回复不再念分钟排名 - 修复:点「采用」后出现的核对题(例如「那次升学结果更接近哪一种」)只有题目、没有选项按钮,刷新也不出。现在采用后会单独出一条消息,核对题挂在它下面,四个按钮可点,刷新后仍在(BUG-1146)。 diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index bfdc6f18..5972e843 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -15421,3 +15421,18 @@ - 相关记录:BUG-1115~1117(盘型口径实现)、BUG-1085、BUG-1086。 - 复发自:无 - 修复版本:`codex/rectification-post-adopt-verify-20261001`。 + +## BUG-1148 | 普通对话回答夹英文 / 梵文音译,同一个东西几种叫法 + +- 状态:resolved(2026-10-01 Claude 直接执行;单元测试 + 真实模型复测;真机复核以部署后 `docs/testing/consult-plain-answer-20261001.md` 为准) +- 首次发现 / 最近更新:2026-10-01 / 2026-10-01 +- 影响面:普通对话所有领域的首轮与追问(本命 / 无出生分钟 / 申报时段共用 `product-voice.ts` 与 `consultation-thinking-plan.ts` 的形状说明)。 +- 用户现象:产品要求「其他领域不说人话就改」。10-01 用产品提供的临时 DeepSeek key(deepseek-flash)对公开名人盘跑婚恋、财富、健康、子女、学业、迁移、年运、时机、综合九个领域:整体已是人话、分段、空宫看宫主;但正文出现 sade sati、vargottama、「6.61 rupas」「Sade Sati 未启动」等英文 / 梵文音译,计都写成「克图」,大运下面一层时叫子运、时叫中运 / 副运 / 小段 / 小运。 +- 触发条件:证据卡与引擎字段是英文 / 梵文名,形状说明只要求「术语首次用白话套住」,没规定用哪个中文名、也没禁音译。 +- 根因:BUG-1132 的人话自检只管「括号外看不看得懂」,没有名词表。 +- 修复:形状说明(`product-voice.ts` ANSWER SHAPE、`NATAL_ANSWER_SHAPE_SUMMARY`、`natalAnswerShapeBody()`、追问轮指令)加一条:名词只用中文、全篇一个叫法(罗睺、计都;大运 → 子运 → 小运),不写英文或梵文音译(sade sati、vargottama、rupas 等,给出中文替代说法),力量只说强 / 中 / 弱。 +- 验证:`consultation-thinking-plan.test.ts`、`consultation-voice-contract.test.ts` 各加一条;真实模型复测同 5 个问题:英文 / 音译 6 处 → 0,「克图」4 处 → 0,子运混称 5 处 → 1(那一处是在解释「子运是大运里的一小段」)。 +- 防复发:名词规则与人话自检同处定义;新增术语先进这条名词表。 +- 相关记录:BUG-1132(人话自检)。 +- 复发自:无 +- 修复版本:`codex/consult-term-glossary-20261001`。 diff --git a/frontend/src/lib/consultation-thinking-plan.ts b/frontend/src/lib/consultation-thinking-plan.ts index 7d5aeb02..080b6bb6 100644 --- a/frontend/src/lib/consultation-thinking-plan.ts +++ b/frontend/src/lib/consultation-thinking-plan.ts @@ -28,7 +28,7 @@ export const REPORT_HEADING = { * answer-20261001 D2/D7/D8): every English prompt that names the shape quotes * this, and the Chinese user-turn instruction below says the same thing. */ -export const NATAL_ANSWER_SHAPE_SUMMARY = `On the first natal answer of a session: a heading-free opener of one to three plain sentences whose first sentence is the conclusion, with no action list in the opener; then one H2 section per person or sub-question the user named, titled with the user's own words (## 你和妈妈, ## 你和爸爸, ## 他们怎么对你), each in plain words with its chart evidence in parentheses; then ## ${REPORT_HEADING.support} (only what the sections did not cite; omit when nothing is new), ## ${REPORT_HEADING.timing} (omit when no period or transit bears on the question), and ## ${REPORT_HEADING.action} (one to three actions, once). A question about one person or one thing is not split. There is no length cap: write until it is clear and say each thing once. Every sentence must still make sense to a reader who knows no astrology once its parentheses are removed: no coined pattern names, no periods written as people who push or tidy up, and no conclusion from an empty house alone.`; +export const NATAL_ANSWER_SHAPE_SUMMARY = `On the first natal answer of a session: a heading-free opener of one to three plain sentences whose first sentence is the conclusion, with no action list in the opener; then one H2 section per person or sub-question the user named, titled with the user's own words (## 你和妈妈, ## 你和爸爸, ## 他们怎么对你), each in plain words with its chart evidence in parentheses; then ## ${REPORT_HEADING.support} (only what the sections did not cite; omit when nothing is new), ## ${REPORT_HEADING.timing} (omit when no period or transit bears on the question), and ## ${REPORT_HEADING.action} (one to three actions, once). A question about one person or one thing is not split. There is no length cap: write until it is clear and say each thing once. Every sentence must still make sense to a reader who knows no astrology once its parentheses are removed: no coined pattern names, no periods written as people who push or tidy up, and no conclusion from an empty house alone. Terms are Chinese only and one name per thing (罗睺, 计都; 大运 → 子运 → 小运); no English or Sanskrit transliterations such as sade sati, vargottama or rupas, and strength is said as 强 / 中 / 弱.`; /** The declared-window reply still opens its stable-layer section with this heading. */ export const WINDOW_OPEN_HEADING = "先回答你的问题"; @@ -384,7 +384,7 @@ export function natalAnswerShapeBody(): string { "每段先用人话讲清楚这个人是什么样、你们怎么相处、事情会怎么走,再在括号里给撑住这段判断的盘上依据。", `然后依次写:## ${REPORT_HEADING.support}(只写上面没引过、判断又需要的依据,表格与正文二选一;没有新依据就省掉这一节)、## ${REPORT_HEADING.timing}(卡里没有相关大运或行运就省掉)、## ${REPORT_HEADING.action}(一到三条行动,只在这里出现一次)。`, "不限字数,讲清楚为准;同一件事只说一遍。", - "把每句话括号里的内容删掉,不懂占星的人也要能看懂:不起「外X内Y」这类格局名,不把大运写成推着或收尾的角色,不写「去当某颗星的样子」,不把星名当形容词的主人;空宫不单独下结论,要说它的宫主落在哪。", + "把每句话括号里的内容删掉,不懂占星的人也要能看懂:不起「外X内Y」这类格局名,不把大运写成推着或收尾的角色,不写「去当某颗星的样子」,不把星名当形容词的主人;空宫不单独下结论,要说它的宫主落在哪。名词只用中文、全篇一个叫法(罗睺、计都;大运、子运、小运),不写英文或梵文音译(sade sati、vargottama、rupas 之类),力量只说强、中、弱。", "不要写「先回答你的问题」这个标题,不要写「统一参数与原始结构」,不要写技法审计表,不要写思考过程清单,不要复述内部 JSON 字段。", ].join(""); } @@ -404,7 +404,7 @@ export function natalAnswerShapeInstruction(): string { export function followUpAnswerShapeInstruction(): string { return [ ANSWER_FROM_EVIDENCE, - "这是追问轮:本会话前面已经写过完整解读。第一句就回答用户这句话(是非题先答「是」「不是」或「看情况:……」,再给盘上的依据),讲清楚为准,不限字数;只引与这句话直接相关的盘面事实,够说明就停;用人话说,盘上依据放括号里;不写二级标题,不写行动清单(用户问了才给);不重述上一轮已经写过的结构;不解释用户为什么问,不替用户说他想要什么。", + "这是追问轮:本会话前面已经写过完整解读。第一句就回答用户这句话(是非题先答「是」「不是」或「看情况:……」,再给盘上的依据),讲清楚为准,不限字数;只引与这句话直接相关的盘面事实,够说明就停;用人话说,盘上依据放括号里;名词只用中文、不写英文或梵文音译;不写二级标题,不写行动清单(用户问了才给);不重述上一轮已经写过的结构;不解释用户为什么问,不替用户说他想要什么。", `只有用户明确换了领域或要求完整看一下,才回到完整形状:口语开场、按对象分段,再接二级标题(## ${REPORT_HEADING.support}、## ${REPORT_HEADING.timing}、## ${REPORT_HEADING.action})。`, "不要写「统一参数与原始结构」,不要写技法审计表,不要写思考过程清单,不要复述内部 JSON 字段。", ].join(""); diff --git a/frontend/src/mastra/product-voice.ts b/frontend/src/mastra/product-voice.ts index 5c4d00ec..6ee9c6be 100644 --- a/frontend/src/mastra/product-voice.ts +++ b/frontend/src/mastra/product-voice.ts @@ -33,6 +33,7 @@ ANSWER SHAPE (natal / general / declared-window 三种模式共用) - 把星名当形容词的主人(「土星的冷」「月亮的照看」); - 括号外每段最多一个占星术语,第一次出现时用白话套住(例:「现在这十年(月亮大运)」「4 宫(母亲宫)」)。 推断边界:空宫不单独下结论——要说这个宫,就说它的宫主落在哪、什么状态。只用卡上和补取到的事实。 +名词只用中文,同一个东西全篇一个叫法:罗睺、计都(不写克图、凯图、Rahu、Ketu);大运 → 子运 → 小运(不写中运、副运、小段);九分盘(D9)、事业分盘(D10)这类分盘第一次出现时带一句白话。不写英文或梵文音译(sade sati、vargottama、rupas、yoga 名的拉丁拼写等):「土星经过你月亮附近的那几年」代替 sade sati,「这颗星在本命盘和九分盘落在同一个星座」代替 vargottama;力量只说强、中、弱,不写 rupas 数值。 降级路线(形状照给,只换依据): - 申报时段(declared birth window):依据只取窗口内稳定的那一层——行星落座、公开日历;上升、宫位、大运、分盘不作依据,也不写月份。 - 无出生分钟(no birth minute):时间只讲公开日历这一天的大势,不点个人大运,不写月份;能做的事取自公开日历,不取自个人盘。 diff --git a/frontend/tests/consultation-thinking-plan.test.ts b/frontend/tests/consultation-thinking-plan.test.ts index ebc06bbe..4b54c855 100644 --- a/frontend/tests/consultation-thinking-plan.test.ts +++ b/frontend/tests/consultation-thinking-plan.test.ts @@ -298,3 +298,14 @@ test("a follow-up turn answers directly and the thinking bar says so (TASK-consu const settled = applyThinkingSectionProgress(plan, "不是。4 宫主落 10 宫,她把心力投在你的前途上。", { settled: true }); assert.equal(settled.every((section) => section.steps.every((step) => step.status === "done")), true); }); + +test("answers name things in Chinese only, one name per thing (BUG-1148)", () => { + // 10-01 real-model run over ten domains: sade sati, vargottama, rupas and + // Ketu leaked into the body, and 计都 / 子运 had three spellings each. + for (const instruction of [natalAnswerShapeInstruction(), followUpAnswerShapeInstruction()]) { + assert.match(instruction, /名词只用中文/); + assert.match(instruction, /不写英文或梵文音译/); + } + assert.match(natalAnswerShapeInstruction(), /罗睺、计都/); + assert.match(natalAnswerShapeInstruction(), /大运、子运、小运/); +}); diff --git a/frontend/tests/consultation-voice-contract.test.ts b/frontend/tests/consultation-voice-contract.test.ts index f6a412c1..7ec8e448 100644 --- a/frontend/tests/consultation-voice-contract.test.ts +++ b/frontend/tests/consultation-voice-contract.test.ts @@ -275,3 +275,11 @@ test("the voice answers the sentence asked: no motive guessing, yes/no first, co // BUG-1134: the empty-house rule is also in the natal Never list. assert.match(voice, /Draw a conclusion from an empty house alone\. Say where its lord sits and in what state instead/); }); + +test("the voice pins Chinese-only terms and one spelling per thing (BUG-1148)", () => { + assert.match(voice, /名词只用中文,同一个东西全篇一个叫法/); + for (const term of ["克图", "凯图", "中运", "sade sati", "vargottama", "rupas"]) { + assert.ok(voice.includes(term), `the voice names ${term} as a spelling not to use`); + } + assert.match(voice, /力量只说强、中、弱/); +});