feat(consult): 进度、思考、正文三通道在生成时分开(BUG-942/943/944)

This commit is contained in:
jesse-ux
2026-09-18 12:16:20 +08:00
parent 742ffbc6b6
commit 94c1e81fa9
31 changed files with 796 additions and 568 deletions
+18 -16
View File
@@ -39,8 +39,9 @@ import { createAdminSupabaseClient } from "@/lib/supabase/admin";
import { createServerSupabaseClient } from "@/lib/supabase/server";
import { streamTextResponse } from "@/lib/stream-text-response";
import { streamAgentResponse } from "@/lib/stream-agent-response";
import { detectMethodologyBookkeeping } from "@/lib/timing-output-guard";
import type { AgentExecutionReceipt, WorkflowReceipt } from "@/lib/consultation-agent-events";
import { consultationContinuePrompt, consultationSectionPrompt, type PublicThinkingSection } from "@/lib/consultation-thinking-plan";
import { consultationComposePrompt, consultationContinuePrompt, natalConsultationThinkingPlan, type PublicThinkingSection } from "@/lib/consultation-thinking-plan";
import {
AGENT_MAX_STEPS,
AGENT_TIMEOUT_MS,
@@ -49,7 +50,6 @@ import {
consultationGenerationSettings,
consultationNatalPrepareStep,
consultationWindowPrepareStep,
consultationSliceGenerationSettings,
createConsultationAgentContext,
createWindowConsultationAgentContext,
consultationModelStepTelemetry,
@@ -858,7 +858,7 @@ export async function POST(request: Request) {
: "如需新的个人星盘结论,必须调用服务器绑定的排盘工具。";
const natalInstruction = consultEntrypoint === "daily_starlanguage"
? `${natalToolInstruction}按三节写:今日趋势、适合推进 / 需要避开、一个行动(把技法审计表和「探索性日提示,不是确定预测」放进最后一节)。不要复述内部 JSON 字段。`
: `${natalToolInstruction}事业/财富/婚恋/家庭按 skill Level 2 模板写:原始结构、六步宫位、Yoga 表、时机、综合、文末技法审计表,然后才是现代生活措辞。不要复述内部 JSON 字段。`;
: `${natalToolInstruction}事业/财富/婚恋/家庭先给口语开场,再按四个标题写结论:先回答你的问题、盘里支持这个判断的地方、时间怎么看、这周可以做的一件事。不要把统一参数或技法审计表写进正文。不要复述内部 JSON 字段。`;
const adoptedRangeNote = consultationMode === "verified_chart"
&& prepared.serverChart?.truth.birthTimeStatus === "accepted"
&& prepared.serverChart.toolInput.candidate_range
@@ -1131,6 +1131,9 @@ export async function POST(request: Request) {
}
if (!prepared.serverChart) throw new Error("server_chart_truth_missing");
state.thinkingPlan = natalConsultationThinkingPlan({
domains: [consultationTheme],
});
const agentContext = createConsultationAgentContext({
userId,
sessionId,
@@ -1182,24 +1185,21 @@ export async function POST(request: Request) {
usages.push(continued.totalUsage);
return continued.fullStream;
};
const composeSection = async (
heading: string,
priorOutput: string,
reason: "write" | "empty-retry" = "write",
) => {
const sliced = await agent.stream([
const interpretFindings = async () => (
(state.thinkingPlan ?? []).map((section) => ({ id: section.id }))
);
const composeAnswer = async () => {
const composed = await agent.stream([
...baseMessages,
...(priorOutput.trim() ? [{ role: "assistant" as const, content: priorOutput }] : []),
{ role: "user" as const, content: consultationSectionPrompt(heading, priorOutput, { reason }) },
{ role: "user" as const, content: consultationComposePrompt() },
], {
...streamOptions,
maxSteps: AGENT_SLICE_MAX_STEPS,
// 原值:分段可调工具 → 新值:分段禁用 → 决策 3
toolChoice: "none",
...consultationSliceGenerationSettings(selectedModel.model),
...consultationContinueGenerationSettings(selectedModel.model),
});
usages.push(sliced.totalUsage);
return sliced.fullStream;
usages.push(composed.totalUsage);
return composed.fullStream;
};
const executionReceipt = (): AgentExecutionReceipt => ({
runId: requestId,
@@ -1228,7 +1228,9 @@ export async function POST(request: Request) {
retry,
retryForAnswer,
continueAfterLength,
composeSection,
interpretFindings,
composeAnswer,
pass4Violations: (text) => detectMethodologyBookkeeping(text).map((item) => item.kind),
continueAfterDisconnect: true,
transformText: (text) => createBirthTimeModeOutputGuard(
consultationMode,