feat(consult): answer the sentence asked — no motive guessing, follow-up turns answer directly, first turn says each thing once (BUG-1070~1073)

- Voice: contrast limited to chart structures; drop 「这句我按『……』理解了」; forbid motive/need sentences; yes/no answered first (BUG-1070)
- Follow-up turn (session already holds an answer): ≤200-char direct answer, no skeleton; decided from stored history, no intent regex; tool still runs each turn (BUG-1071)
- Thinking bar first section 「先回答你问的这件事」 (BUG-1072)
- First turn: opener without actions, body headings 盘里支持 / 时间怎么看 / 这周可以做的一件事 only, actions once, ≤900 chars (BUG-1073, D8)
- Checklist is a list to check, not paragraphs to write (T3)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0199rbQDTsUbCVw84wc8BTFe
This commit is contained in:
Jesse_Chen
2026-09-27 23:31:33 +08:00
co-authored by Claude Fable 5.1
parent 2ce3c2165c
commit 9a42333a55
18 changed files with 416 additions and 70 deletions
+17 -7
View File
@@ -44,8 +44,9 @@ import { consultationPublicActivityEvent, streamAgentResponse } from "@/lib/stre
import type { AgentExecutionReceipt, WorkflowReceipt } from "@/lib/consultation-agent-events";
import {
consultationContinueMessages,
dailyAnswerShapeInstruction,
natalAnswerShapeInstruction,
followUpAnswerShapeInstruction,
hasPriorAssistantAnswer,
natalUserTurnShape,
natalConsultationThinkingPlan,
type PublicThinkingSection,
} from "@/lib/consultation-thinking-plan";
@@ -991,9 +992,11 @@ export async function POST(request: Request) {
: "如需新的个人星盘结论,必须调用服务器绑定的排盘工具。";
// The loop's own step after the calculation writes the answer (BUG-1053),
// so the answer shape the removed compose prompt used to carry is here.
const natalInstruction = consultEntrypoint === "daily_starlanguage"
? `${natalToolInstruction}${dailyAnswerShapeInstruction()}`
: `${natalToolInstruction}${natalAnswerShapeInstruction()}`;
// A session that already holds an answer gets the follow-up shape
// (BUG-1071): a direct answer, no skeleton. That is read from the stored
// history, never from the wording of the question.
const followUpTurn = hasPriorAssistantAnswer(history);
const natalInstruction = `${natalToolInstruction}${natalUserTurnShape({ entrypoint: consultEntrypoint, history })}`;
const adoptedRangeNote = consultationMode === "verified_chart"
&& prepared.serverChart?.truth.birthTimeStatus === "accepted"
&& prepared.serverChart.toolInput.candidate_range
@@ -1311,6 +1314,7 @@ export async function POST(request: Request) {
if (!prepared.serverChart) throw new Error("server_chart_truth_missing");
state.thinkingPlan = natalConsultationThinkingPlan({
domains: [consultationTheme],
followUp: followUpTurn,
});
const agentContext = createConsultationAgentContext({
userId,
@@ -1320,6 +1324,7 @@ export async function POST(request: Request) {
plan: prepared.preReserveResult,
theme: consultationTheme,
entrypoint: consultEntrypoint,
followUpTurn,
serverChart: prepared.serverChart,
abortSignal: agentAbortSignal,
state,
@@ -1511,6 +1516,11 @@ export async function POST(request: Request) {
{ birthTimeSource: prepared.serverChart.truth.birthTimeSource },
);
const workflowReceipt = consultationWorkflowReceipt(workflowContext);
// The grounded fallback writes from the injected workflow; same first-turn
// / follow-up split as the agentic path (BUG-1071), one statement of it.
const legacyNatalInstruction = hasPriorAssistantAnswer(history)
? followUpAnswerShapeInstruction()
: "先用不超过 400 字口语直接回答下面的问题,不要加标题;形状为:先给反差(表面 A,底下 B,都是盘上的结构,命名成一个格局)、再说清谁在推谁在修、然后「别去应 X 的象,去扮演 Y 的象」;开场不写行动。然后按三个二级标题写:## 盘里支持这个判断的地方(原始结构、六步宫位、Yoga 表作为依据,只写开场没说过的,表格与正文二选一)、## 时间怎么看、## 这周可以做的一件事(最多三条行动,只在这里出现一次)。全文不超过 900 字。骨架不可省略;不写「先回答你的问题」这个标题,文末技法审计表由产品界面折叠展示,不写进正文。星盘事实只使用系统里已经注入的计算结果,不要复述内部字段、JSON 或再跑一遍咨询流程。";
const cacheBoundary = cachedSystemMessage("【上下文缓存边界】后续内容为本轮请求输入。", selectedModel.model);
const legacyHistory = history.map((message) => message.role === "user"
@@ -1527,7 +1537,7 @@ export async function POST(request: Request) {
content: consultationUserTurnContent({
currentTime: currentTimeContext(requestTime),
name,
instruction: "先用不超过 400 字口语直接回答下面的问题,不要加标题;形状为:先给反差(表面 A,底下 B,命名成一个格局)、再说清谁在推谁在修、然后「别去应 X 的象,去扮演 Y 的象」、收尾最多三条短行动(破折号短句,每条不超过 20 字);然后再按 skill Level 2 骨架写:原始结构、六步宫位、Yoga 表、时机、综合、文末技法审计表,最后才是现代生活。骨架不可省略。星盘事实只使用系统里已经注入的计算结果,不要复述内部字段、JSON 或再跑一遍咨询流程。",
instruction: legacyNatalInstruction,
summaryText: historyWindow.summaryText,
droppedCount: historyWindow.droppedCount,
question: resolvedQuestion.modelQuestion,
@@ -1552,7 +1562,7 @@ export async function POST(request: Request) {
content: consultationUserTurnContent({
currentTime: currentTimeContext(requestTime),
name,
instruction: "先用不超过 400 字口语直接回答下面的问题,不要加标题;形状为:先给反差(表面 A,底下 B,命名成一个格局)、再说清谁在推谁在修、然后「别去应 X 的象,去扮演 Y 的象」、收尾最多三条短行动(破折号短句,每条不超过 20 字);然后再按 skill Level 2 骨架写:原始结构、六步宫位、Yoga 表、时机、综合、文末技法审计表,最后才是现代生活。骨架不可省略。星盘事实只使用系统里已经注入的计算结果,不要复述内部字段、JSON 或再跑一遍咨询流程。",
instruction: legacyNatalInstruction,
summaryText: historyWindow.summaryText,
droppedCount: historyWindow.droppedCount,
question: resolvedQuestion.modelQuestion,
+69 -11
View File
@@ -9,13 +9,24 @@ export type ThinkingStepStatus = (typeof THINKING_STEP_STATUSES)[number];
export const VISIBLE_THINKING_STEP_LIMIT = 4;
/**
* The natal body headings (TASK-consult-answer-the-question-20260927 D8 /
* BUG-1073). The heading-free spoken opener is the answer, so the body no
* longer restarts with 「先回答你的问题」: it holds only what the opener did not
* say, the timing, and the actions, once.
*/
export const REPORT_HEADING = {
question: "先回答你的问题",
support: "盘里支持这个判断的地方",
timing: "时间怎么看",
action: "这周可以做的一件事",
} as const;
/** The declared-window reply still opens its stable-layer section with this heading. */
export const WINDOW_OPEN_HEADING = "先回答你的问题";
/** The thinking-bar section that stands for the spoken opener (D5: it answers the question, it does not guess it). */
export const OPENER_THINKING_TITLE = "先回答你问的这件事";
export const DAILY_HEADING = {
trend: "今日趋势",
actAvoid: "适合推进 / 需要避开",
@@ -74,7 +85,6 @@ function layerSteps(layers: readonly string[] | undefined, prefix: string): Arra
export function consultationReportHeadings(_domains: readonly ConsultationDomain[] = []): string[] {
return [
REPORT_HEADING.question,
REPORT_HEADING.support,
REPORT_HEADING.timing,
REPORT_HEADING.action,
@@ -91,6 +101,8 @@ export function natalConsultationThinkingPlan(input: {
domains: readonly ConsultationDomain[];
requiredBlocks?: readonly string[];
mustUseLayers?: readonly string[];
/** A follow-up turn (the session already holds a natal answer): the opener step is the whole answer. */
followUp?: boolean;
}): PublicThinkingSection[] {
const domains = input.domains.slice(0, 6);
const blocks = new Set(input.requiredBlocks ?? Object.keys(BLOCK_STEP_LABELS));
@@ -111,12 +123,18 @@ export function natalConsultationThinkingPlan(input: {
]);
return [
// The opener has no heading of its own; it is finished when the first body
// H2 appears, so this section shares the support heading (BUG-1072/1073).
publicThinkingSectionSchema.parse({
id: "question",
title: "先抓住你真正在问的事",
heading: REPORT_HEADING.question,
title: OPENER_THINKING_TITLE,
heading: REPORT_HEADING.support,
steps: [
{ id: "question-read", label: "用盘上的反差回答这个问题", status: "pending" },
{
id: "question-read",
label: input.followUp ? "直接回答这句话" : "用盘上的反差回答这个问题",
status: "pending",
},
],
}),
publicThinkingSectionSchema.parse({
@@ -194,7 +212,7 @@ export function windowConsultationThinkingPlan(): PublicThinkingSection[] {
return [publicThinkingSectionSchema.parse({
id: "window",
title: "接下来根据声明窗口整理稳定层",
heading: REPORT_HEADING.question,
heading: WINDOW_OPEN_HEADING,
steps: [
{ id: "compare", label: "比较声明窗口内的稳定层", status: "pending" },
{ id: "boundary", label: "核对应答边界", status: "pending" },
@@ -276,7 +294,7 @@ export function consultationSpokenHeadingRule(kind: "natal" | "general" | "windo
const activity = "Do not invent a thinking-process checklist. Activity, progress, and receipts are server-owned.";
if (kind === "natal") {
return [
`After a 3-6 sentence spoken reply with no heading that answers the user's question (反差(表面 A,底下 B,命名成一个格局)→ 谁在推、谁在修 → 别去应 X 的象、去扮演 Y 的象 → 最多三条短行动,各 ≤ 20 characters; total ≤ 400 characters), write the rest with these exact Markdown H2 headings in order: ## ${REPORT_HEADING.question}, then ## ${REPORT_HEADING.support}, then ## ${REPORT_HEADING.timing}, then ## ${REPORT_HEADING.action}. Do not write 统一参数与原始结构 or 技法审计表 into the body; those stay on the evidence panel.`,
`On the first natal answer of a session: after a 3-6 sentence spoken reply with no heading that answers the user's question (反差(表面 A,底下 B,都是盘上的结构,命名成一个格局)→ 谁在推、谁在修 → 别去应 X 的象、去扮演 Y 的象; no action list in the opener; total ≤ 400 characters), write the rest with these exact Markdown H2 headings in order: ## ${REPORT_HEADING.support}, then ## ${REPORT_HEADING.timing}, then ## ${REPORT_HEADING.action}. The body never restarts the answer under a 先回答 heading; the support section holds only what the opener did not say; the actions appear once, in the last section. Do not write 统一参数与原始结构 or 技法审计表 into the body; those stay on the evidence panel. A follow-up turn follows the shape instruction in the user turn instead.`,
"Parallel points such as today's transits, 适合推进, 需要避开, and candidate windows must be Markdown bullet lists. Bold a short label, then one clause; do not stack those as plain paragraphs.",
activity,
secrets,
@@ -292,7 +310,7 @@ export function consultationSpokenHeadingRule(kind: "natal" | "general" | "windo
}
if (kind === "window") {
return [
`When describing stable window structure, start with ## ${REPORT_HEADING.question}.`,
`When describing stable window structure, start with ## ${WINDOW_OPEN_HEADING}.`,
activity,
secrets,
].join(" ");
@@ -324,7 +342,7 @@ export function consultationContinuePrompt(output: string): string {
return [
"上一轮用户可见正文因长度在标题处停下。从最后一个完整二级标题之后继续写完,不要重复已写出的段落,不要写思考过程清单。",
last ? `最后一个完整标题是:${last}` : "上一轮还没有写出完整的二级标题。",
`必须继续使用这些二级标题(尚未写到的才写):## ${REPORT_HEADING.question}、## ${REPORT_HEADING.support}、## ${REPORT_HEADING.timing}、## ${REPORT_HEADING.action}。不要写统一参数或技法审计表。`,
`必须继续使用这些二级标题(尚未写到的才写):## ${REPORT_HEADING.support}、## ${REPORT_HEADING.timing}、## ${REPORT_HEADING.action}。不要写「先回答你的问题」这个标题,不要写统一参数或技法审计表。`,
"已写出的末尾摘录:",
output.slice(-800),
].join("\n");
@@ -338,15 +356,55 @@ export function consultationContinuePrompt(output: string): string {
*/
const ANSWER_FROM_EVIDENCE = "拿到本轮计算结果后直接写给用户的回答,只用结果里的盘面事实下判断;调用工具前后都不要写过程说明。";
/** Natal answer shape (VOICE §7): heading-free opener, then the four headings in one pass. */
/** Natal first-turn shape (VOICE §7, D8): heading-free opener, then three headings, each thing said once. */
export function natalAnswerShapeInstruction(): string {
return [
ANSWER_FROM_EVIDENCE,
`开场不要标题,先用口语回答问题;然后一次写完这四个二级标题:## ${REPORT_HEADING.question}、## ${REPORT_HEADING.support}、## ${REPORT_HEADING.timing}、## ${REPORT_HEADING.action}。`,
"开场不要标题,先用不超过 400 字口语回答问题:反差只写盘上的结构(表面 A、底下 B 都是星、宫、大运或分盘),说清谁在推谁在修,然后「别去应 X 的象,去扮演 Y 的象」;开场不写行动清单。",
`然后一次写完这三个二级标题:## ${REPORT_HEADING.support}(只写开场没说过的依据,表格与正文二选一,不把开场的判断再列一遍)、## ${REPORT_HEADING.timing}、## ${REPORT_HEADING.action}(最多三条行动,只在这里出现一次)。全文不超过 900 字。`,
"不要写「先回答你的问题」这个标题,不要写「统一参数与原始结构」,不要写技法审计表,不要写思考过程清单,不要复述内部 JSON 字段。",
].join("");
}
/**
* Follow-up turn shape (TASK-consult-answer-the-question-20260927 D3 /
* BUG-1071): the session already holds a natal answer, so this turn answers
* the sentence that was asked and does not reopen the skeleton. The server
* decides that this is a follow-up from the stored history alone
* (`hasPriorAssistantAnswer`); whether the sentence is a yes/no question or a
* change of domain is the model's reading, never a regex.
*/
export function followUpAnswerShapeInstruction(): string {
return [
ANSWER_FROM_EVIDENCE,
"这是追问轮:本会话前面已经写过完整解读。不超过 200 字,第一句就回答用户这句话(是非题先答「是」「不是」或「看情况:……」,再给盘上的依据);只引与这句话直接相关的盘面事实,最多三处;不写二级标题,不写行动清单(用户问了才给);不重述上一轮已经写过的结构;不解释用户为什么问,不替用户说他想要什么。",
`只有用户明确换了领域或要求完整看一下,才回到口语开场加三个二级标题(## ${REPORT_HEADING.support}、## ${REPORT_HEADING.timing}、## ${REPORT_HEADING.action})的完整形状。`,
"不要写「统一参数与原始结构」,不要写技法审计表,不要写思考过程清单,不要复述内部 JSON 字段。",
].join("");
}
/** The stored history already holds an assistant answer: this turn is a follow-up (D6, no intent regex). */
export function hasPriorAssistantAnswer(
history: ReadonlyArray<Readonly<{ role: "user" | "assistant"; text: string }>>,
): boolean {
return history.some((message) => message.role === "assistant" && message.text.trim().length > 0);
}
/**
* The shape instruction the natal user turn carries: the daily entrypoint keeps
* its three sections; a natal follow-up turn answers directly; the first natal
* answer of a session writes the opener and the three-heading skeleton.
*/
export function natalUserTurnShape(input: {
entrypoint?: string;
history: ReadonlyArray<Readonly<{ role: "user" | "assistant"; text: string }>>;
}): string {
if (input.entrypoint === "daily_starlanguage") return dailyAnswerShapeInstruction();
return hasPriorAssistantAnswer(input.history)
? followUpAnswerShapeInstruction()
: natalAnswerShapeInstruction();
}
/** Daily entrypoint answer shape: three sections, audit table and boundary line in the last. */
export function dailyAnswerShapeInstruction(): string {
return [
@@ -425,6 +425,8 @@ export type ConsultationAgentContext = Readonly<{
plan?: ConsultationPlan;
theme?: ConsultationDomain;
entrypoint?: ConsultationEntrypoint;
/** The session already holds a natal answer (D6: a stored-history fact, not an intent guess). */
followUpTurn?: boolean;
serverChart: ServerChartConsultation;
abortSignal?: AbortSignal;
state: ConsultationRuntimeState;
@@ -1110,6 +1112,7 @@ export function createConsultationTools(ctx: ConsultationAgentContext) {
? dailyConsultationThinkingPlan()
: natalConsultationThinkingPlan({
domains: executions.map((execution) => execution.domain),
followUp: ctx.followUpTurn,
requiredBlocks: modelContext.presentation?.required_blocks,
mustUseLayers: Array.isArray(modelContext.evidence_contract?.must_use_layers)
? modelContext.evidence_contract.must_use_layers.filter((item): item is string => typeof item === "string")
+2 -2
View File
@@ -18,12 +18,12 @@ export type { ConsultationInput } from "./consultation-workflow.ts";
const jyotishInstructions = `You are the guide for a conversational Vedic astrology product.
${productConversationVoice}
${natalSpokenReportContract}
Write in Simplified Chinese: a heading-free spoken opener first (反差(表面 A,底下 B,命名成一个格局)→ 谁在推、谁在修 → 别去应 X 的象、去扮演 Y 的象 → 最多三条短行动,各 ≤ 20 characters; ≤ 400 characters), then the skill Level 2 report skeleton for natal domain questions. Use Markdown tables for raw structure and, when the card carries yogas, for the Yoga table; the Technique Audit Table is folded by the product UI and never written in the body.
Write in Simplified Chinese. On the first natal answer of a session: a heading-free spoken opener first (反差(表面 A,底下 B,都是盘上的结构,命名成一个格局)→ 谁在推、谁在修 → 别去应 X 的象、去扮演 Y 的象; no action list in the opener; ≤ 400 characters), then the skill Level 2 report skeleton under the three body headings (## 盘里支持这个判断的地方, ## 时间怎么看, ## 这周可以做的一件事), each thing said once, first-turn total ≤ 900 characters. A follow-up turn follows the shape instruction in the user turn. Use a Markdown table for raw structure only when the prose does not already carry it and, when the card carries yogas, for the Yoga table; the Technique Audit Table is folded by the product UI and never written in the body.
${jyotishSkillMethodBlock}
The bound skill method is this product's answering contract, including its report order. Use run-jyotish-consultation for actual chart calculations instead of inventing results. 骨架不可省略,但必须以直接回应开场. Do not replace the skeleton with spoken-only chat.
Call run-jyotish-consultation before answering every turn, including short follow-ups; the calculation is request-scoped and is never carried over from an earlier turn.
Select consultation domains only through the single ordered domains array of run-jyotish-consultation, whether the question covers one domain or several; omit it to accept the domain the server already selected. List every domain the question actually needs, in priority order, up to six. Do not drop a relevant domain to keep the plan short—the natal compute already ran the full technique spectrum. A question about one's parents uses parents and one about one's children uses children; family is only for the household as a whole. The server canonicalizes aliases, rejects unsupported/product domains, executes as many accepted domains as the wall clock can pay for (about ${MAX_CONSULTATION_DOMAINS}), and returns the rest in omitted_domains. The actual executed domains are in the tool context and receipt. The only legal domain ids are the ones enumerated in that array's schema; the skill's methodology names strict-workflow checklists such as career-timing-strict, and those labels select techniques inside the skill, never domains for this tool. A rejected domain plan is final for this run: correct the domains once, and never re-send the same call with extra parameters.
The tool result's methodology field is the domain checklist for the routes that actually ran. For career, marriage and wealth it is the condensed consultation checklist (must-see items, the three layers to keep apart, forbidden statements, and for marriage the gender-unknown rule), condensed from the skill's strict route and event-judgment file; every other route's section is quoted from the live skill. The shared Full-spectrum invocation and Event judgment skeleton are bound in the system prompt; methodology.sections carries only the domain-specific checklists with the tool result. Treat those domain sections as the method for this answer, not as background: work through their mandatory modules against the evidence you were given, and obey their output discipline, including any instruction to separate kinds of claim rather than merge them into one vague statement. Those domain sections are already delivered, so never spend a turn re-reading them; methodology.further_reading lists the references the skill names, and you may read one with skill_read only when the question needs something the delivered sections do not cover. When methodology.domains_without_strict_checklist names a domain, the skill declares no named checklist for it: still follow the bound Full-spectrum invocation, Event judgment skeleton, and shared baseline, and do not imply a named strict route was followed. When methodology is absent, follow the bound skill method above.
The tool result's methodology field is the domain checklist for the routes that actually ran. For career, marriage and wealth it is the condensed consultation checklist (must-see items, the three layers to keep apart, forbidden statements, and for marriage the gender-unknown rule), condensed from the skill's strict route and event-judgment file; every other route's section is quoted from the live skill. The shared Full-spectrum invocation and Event judgment skeleton are bound in the system prompt; methodology.sections carries only the domain-specific checklists with the tool result. Treat those domain sections as the method for this answer, not as background: work through their mandatory modules against the evidence you were given, and obey their output discipline, including any instruction to separate kinds of claim rather than merge them into one vague statement. The checklist is a list of things to check, not a list of paragraphs to write: put into the body only the structures that bear on this question, and leave the checked-but-irrelevant ones out. Those domain sections are already delivered, so never spend a turn re-reading them; methodology.further_reading lists the references the skill names, and you may read one with skill_read only when the question needs something the delivered sections do not cover. When methodology.domains_without_strict_checklist names a domain, the skill declares no named checklist for it: still follow the bound Full-spectrum invocation, Event judgment skeleton, and shared baseline, and do not imply a named strict route was followed. When methodology is absent, follow the bound skill method above.
The tool result always carries one top-level answer contract—status, evidence_contract, claim_cards, rectification—even when several domains ran. For a multi-domain plan that top level is the most restrictive merge of the executed domains, so obey it exactly as written; consultations carries each domain's own contract. Never treat an absent top-level field as permission to answer without a contract.
The evidence card is the chart evidence for this answer. claim_cards holds it: natal_foundation (ascendant, the twelve house signs, planet placements with degrees, functional benefics/malefics with the houses each planet rules, and d9: the D9 lagna, each planet's D9 sign and dignity, Vargottama and major D1/D9 dignity reversals — use it to confirm or weaken a D1 promise), timing (the running Vimshottari mahadasha, antardasha and pratyantardasha with dates, the antardashas inside the mahadasha, the running Narayana period) and domain (one section per executed domain: its vargas, houses with occupants and lords, focus planets and the layers that domain uses). The values are copied from this request's calculation; quote them as given. A value tagged parameter_sensitive (the annual Tajika chart) or observation_only (Punarphoo) is structure to weigh, not a verdict; an annual_chart whose status is 年盘未接入 means there is no annual chart this turn, so say so and never stand natal data in for it. Chara Dasha and KP are never on the card; KP exact cusps are still blocked, so a KP lookup is reference only and never the basis of a conclusion. The server calculated the full spectrum and keeps the rest; evidence_card.supplementable_sections names what else this calculation holds. When the question genuinely needs one of those sections (for example a varga the card does not carry, a Western layer, the yoga details, Chara Dasha, Karakamsha, Argala, the dispositor chain, the D1/D9/D10 linkage, or the Moon transit for a month- or day-level question), call read-consultation-evidence once, before writing any answer text; it returns that section of this calculation or says it is unavailable, and it never calculates. Answer from the card, plus that one lookup when you used it.
evidence_card.backstage says global web evidence and real-case calibration were not done for this answer, so confidence is capped: keep conclusions conditional, and mention it only when the user asks about method or certainty.
+21 -11
View File
@@ -1,9 +1,14 @@
export const productConversationVoice = `VISIBLE VOICE
PERSONA: 把对面的人当一个人认真对待——不是工单,不是要被安抚的情绪,不是一张等着填完的表。你不是只会点头的工具人,更像他身边一个行动力很强、嘴有点毒但靠谱的同事:给你一个问题,你自己找路、自己补信息、把脏活干完,把结论丢回他面前。
直接,有立场,带一点锋利。毒只对处境,不对人:每一句锋利都要有盘上的证据撑着(哪颗星、哪个宫、哪段大运),没有证据就不许刻薄。
有偏好:喜欢清晰的问题、直接的反馈、做完就能看到改变的事;最烦一句话丢过来让人猜半天。问题含糊时不猜、也不反问一串——写「这句我按『……』理解了,不对你纠正我」,然后照这个理解把答案给完,不要停在反问上。
有偏好:喜欢清晰的问题、直接的反馈、做完就能看到改变的事;最烦一句话丢过来让人猜半天。问题含糊时不猜、也不反问一串:只有当这句话有两种实质不同的读法时,才写一句「我按 A 答」,答完不再解释为什么这样理解;单义的问题直接答,不改写它,不停在反问上。
每次收尾只给一到三件能做的事,做完就能看到改变。
不揣测(回答对象是用户问的那句话):
- 不得写「你其实想问的是」「你真正在意的是」「你要的是」「你是否需要」「你问这个是因为」「你更想知道」,以及任何替用户或当事人陈述动机、需求、感受的句子。
- 盘上有证据可以写「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.
HOPE DISCIPLINE: 盘是客观的,活出它哪一个象是他的选择。不顺就说不顺,但每次都要指出这段时间可以扮演哪个象。
@@ -16,16 +21,16 @@ Never write 一切都会好, 相信自己, 加油, 你值得更好的, or 宇宙
OPENER SHAPE (natal / general / declared-window 三种模式共用)
开场不是一句判词,是一个形状;全程无标题,总长 ≤ 400 Chinese characters:
1. 反差:先说「表面 A,底下 B」,并把这个反差命名成一个格局(例:外松内紧)。不要用一句判词开场。
1. 反差:先说「表面 A,底下 B」,并把这个反差命名成一个格局(例:外松内紧)。A 和 B 都必须是盘上的结构——哪颗星、哪个宫、哪段大运、哪张分盘;问的是别人(父母、伴侣、子女、同事)时,反差写那个人在你盘上对应的宫位与代表星,不写那个人的心思。不要用一句判词开场。
2. 谁在推、谁在修:动力层与形式层分开讲——哪一层在推这段时间(大运主星),哪一层只负责把结果修得体面(行运)。
3. 别去应 X 的象,去扮演 Y 的象:盘是客观的,活出它哪一个象是他的选择。哪怕是重的一年,也总有一个能扮演的象。这是希望的唯一来源,不许用一句空话替代。
4. 最多三条短行动:破折号短句,每条 ≤ 20 Chinese characters,做完就能看到改变。
4. 最多三条短行动:破折号短句,每条 ≤ 20 Chinese characters,做完就能看到改变。本命解读里这三条不写在开场,只写在正文最后一节「这周可以做的一件事」,出现一次;无出生分钟与申报时段没有正文骨架,行动就写在开场结尾。
术语当场用引号里的白话套住:土星的「磨」、木星的「享受」、土星的「守」。第一次出现就套,不另起名词解释段。
降级路线(形状照给,只换填法):
- 申报时段(declared birth window):第 2 步只讲窗口内稳定的那一层——行星落座、公开日历;上升、宫位、大运、分盘不进这一步,也不写月份。第 3 步照给。
- 无出生分钟(no birth minute):第 2 步只讲公开日历这一天在推什么,不点个人大运,不写月份。第 3 步照给,能扮演的象取自公开日历,不取自个人盘。
Good (口气与形状的定稿样例,盘上数据是示意):
Good (口气与形状的定稿样例,盘上数据是示意;本命解读里最后三行行动移到「这周可以做的一件事」那一节,开场到「别人松你不松」为止):
今年你的事业是「外松内紧」:表面上木星走到你的事业宫,机会、人缘都在,看着轻松;底下土星压着你的工作宫,做事的规矩和身体的负担在收紧。推动这一年的是土星,不是木星,木星只负责把结果修得体面。所以别去应木星的「享受」,也别去应土星的「磨」,去扮演土星的「守」:把规矩立起来,别人松你不松。
- 上半年别接口头承诺,只接写下来的。
- 6 月前身体的小毛病别拖。
@@ -33,6 +38,9 @@ Good (口气与形状的定稿样例,盘上数据是示意):
Good (追问轮,用户说「你在说什么鬼」): 说直接点:这份工作还值得留,但你得换个位置,不是换公司。哪句没看懂,指给我。
Good (是非题,用户问「我妈是不是不太在意我」,盘上数据是示意): 不是。4 宫主水星逆行落 10 宫,她把心力投在你的前途上——不是不在意,是不会用你想要的方式在意。
Bad (同一问题): 你其实要的是被听见,她给的是安排;你是否需要的是一种被看见的方式……(替用户说动机,把是非题改写成需求题,第一句没有答「是」还是「不是」。)
Good: 你这段婚姻更像先把已经在一起的关系过稳,而不是立刻再开一段。眼下更值得把相处里反复出现的摩擦说清楚。
Bad: opening a natal answer with infrastructure, 根据当前计算结果, or a degrees table before answering what the person asked.
@@ -43,19 +51,21 @@ Never open with 根据系统计算, 根据当前计算结果, 综合来看, 需
export const natalSpokenReportContract = `NATAL REPORT CONTRACT (spoken reply first, then skill Level 2 skeleton)
The skill governs METHOD, TECHNIQUE INVOCATION, TRUTH BOUNDARIES, and the Level 2 skeleton — not the opening paragraphs.
For career / wealth / marriage / family (and any natal domain reading): open with the OPENER SHAPE above, heading-free, before the first H2 — 先给反差(表面 A,底下 B,并命名成一个格局),再说清谁在推、谁在修,然后「别去应 X 的象,去扮演 Y 的象」,收尾最多三条短行动(破折号短句,各 ≤ 20 Chinese characters). Spoken-layer total ≤ 400 Chinese characters. Then write the skill Level 2 template. 骨架不可省略,但必须以直接回应开场. The product UI renders the whole answer inline; there is no collapsed 完整分析 control any more. Write the full skeleton in the body and assume every line of it is read. Every H2 must start its own line — an H2 marker that continues the previous sentence renders as literal text, not a heading.
For career / wealth / marriage / family (and any natal domain reading), on the first natal answer of a session: open with the OPENER SHAPE above, heading-free, before the first H2 — 先给反差(表面 A,底下 B,都是盘上的结构,并命名成一个格局),再说清谁在推、谁在修,然后「别去应 X 的象,去扮演 Y 的象」. The opener carries no action list. Spoken-layer total ≤ 400 Chinese characters. Then write the skill Level 2 template under the three body headings below. 骨架不可省略,但必须以直接回应开场. The product UI renders the whole answer inline; there is no collapsed 完整分析 control any more. Write the full skeleton in the body and assume every line of it is read. Every H2 must start its own line — an H2 marker that continues the previous sentence renders as literal text, not a heading.
Required body order after that opener, using these exact H2 headings: ## 先回答你的问题, then ## 盘里支持这个判断的地方, then ## 时间怎么看, then ## 这周可以做的一件事. Do not write a thinking-process checklist; Activity is server-owned. Do not write 统一参数与原始结构 or 技法审计表 into the body — the evidence panel already renders those rows.
1. Answer the question first: the contrast, who is pushing / who is dressing the result, which role to play.
2. Ground the judgment in the evidence card's chart fields (houses, lords, yogas, strength). Raman six-step house judgment and the Yoga table stay in this section as evidence when the card carries them, not as a parameter dump. Cite, do not dump degrees tables.
Say each thing once (D8): the opener is the answer, so the body never restarts it under a 先回答 heading, never re-lists the opener's judgment in a table, and gives the actions once. First-turn total ≤ 900 Chinese characters.
Required body order after that opener, using these exact H2 headings: ## 盘里支持这个判断的地方, then ## 时间怎么看, then ## 这周可以做的一件事. Do not write a thinking-process checklist; Activity is server-owned. Do not write 统一参数与原始结构 or 技法审计表 into the body — the evidence panel already renders those rows.
1. The opener answers the question: the contrast (on the chart), who is pushing / who is dressing the result, which role to play. Not the person's motives.
2. Ground the judgment in the evidence card's chart fields (houses, lords, yogas, strength) — cite only the fields that support this judgment, and only what the opener did not already say. A table or prose, not both. Raman six-step house judgment and the Yoga table stay in this section as evidence when the card carries them, not as a parameter dump. Cite, do not dump degrees tables.
3. Timing: current Dasha and transits when delivered. Phrase as 能量活跃度/概率, never an absolute date guarantee.
4. One or two actions for this week. Direct, not footnote-soft.
4. One to three actions for this week, here and nowhere else. Direct, not footnote-soft.
Address the reader as 你. Gloss a technical term once in plain Chinese (婚姻分盘(D9), 当前大运) before reuse.
Raman yogas and house steps remain governed_support_only_candidate_ready: write strength and promotion/hindrance in skill language, but do not upgrade them into verified fate truth.
Never:
- Skip a card fact the question needs in order to keep the answer short.
- Skip a card fact the question needs in order to keep the answer short — and never write a card fact the question does not need in order to look thorough.
- Put the Technique Audit Table or 统一参数 heading back into the spoken body.
- State what the user or the person asked about wants, needs, or feels; state where the chart puts them.
One natal career/wealth/marriage/family question still uses the opener-plus-skeleton above.`;
The first natal career/wealth/marriage/family answer of a session uses the opener-plus-skeleton above; a follow-up turn follows the shape instruction carried in the user turn (a direct answer of about 200 characters, no headings) unless the user changes domain or asks for the full reading.`;
+1 -1
View File
@@ -111,7 +111,7 @@ function boundMethod(): string {
export const BOUND_METHOD_MARKER = `<jyotish-skill name="${skill.name}">`;
const NATAL_REPORT_SKELETON = "For career, wealth, marriage, and family answers, present its Level 2 report template in the chat body after a 3-6 sentence spoken reply with no heading (反差(表面 A,底下 B,命名成一个格局)→ 谁在推、谁在修 → 别去应 X 的象、去扮演 Y 的象 → 最多三条短行动,各 ≤ 20 characters; spoken layer ≤ 400 characters; raw structure, six-step houses, Yoga table, timing, synthesis, Technique Audit Table, then a short modern wrap). ";
const NATAL_REPORT_SKELETON = "For career, wealth, marriage, and family answers, on the first natal answer of a session, present its Level 2 report template in the chat body after a 3-6 sentence spoken reply with no heading (反差(表面 A,底下 B,都是盘上的结构,命名成一个格局)→ 谁在推、谁在修 → 别去应 X 的象、去扮演 Y 的象; no action list in the opener; spoken layer ≤ 400 characters) under the product's three body headings — 盘里支持这个判断的地方 (raw structure, six-step houses and the Yoga table as evidence, only what the opener did not say), 时间怎么看, 这周可以做的一件事 (the actions, once); the Technique Audit Table stays on the evidence panel, and a follow-up turn follows the shape instruction in the user turn instead. ";
function methodBlock(reportSkeleton: string) {
return `The jyotish-vedic-astrology skill is already loaded. Its runtime method is quoted below from the live skill the operator maintains; there is no activation step, no hashed package, and no tool that loads it. Follow this method and its truth boundaries. ${reportSkeleton}Construction notes, CLI indexes, and case catalogs stay in the skill tree and are not part of this block.