接入文章优化LLM追踪
This commit is contained in:
@@ -205,18 +205,17 @@ describe("job API routes", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("streams a one-click optimization from body-only input", async () => {
|
it("streams a one-click optimization from body-only input", async () => {
|
||||||
llmMocks.generateValidatedJson
|
mockTrackedLlmResult(validCandidateFactCard, "llmcall_fact");
|
||||||
.mockResolvedValueOnce(validCandidateFactCard)
|
mockTrackedLlmResult({
|
||||||
.mockResolvedValueOnce({
|
title: "流式优化标题",
|
||||||
title: "流式优化标题",
|
summary: "流式优化摘要。",
|
||||||
summary: "流式优化摘要。",
|
body_markdown:
|
||||||
body_markdown:
|
"## 服务能力\nExample Technology Co., Ltd. 提供 GEO optimization 服务。",
|
||||||
"## 服务能力\nExample Technology Co., Ltd. 提供 GEO optimization 服务。",
|
image_suggestions: [],
|
||||||
image_suggestions: [],
|
changed_sections: ["title", "body"],
|
||||||
changed_sections: ["title", "body"],
|
requires_user_confirmation: [],
|
||||||
requires_user_confirmation: [],
|
}, "llmcall_draft");
|
||||||
})
|
mockTrackedLlmResult({ checks: [] }, "llmcall_qa");
|
||||||
.mockResolvedValueOnce({ checks: [] });
|
|
||||||
|
|
||||||
const response = await optimizeStream(
|
const response = await optimizeStream(
|
||||||
request({
|
request({
|
||||||
@@ -231,13 +230,25 @@ describe("job API routes", () => {
|
|||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(events.map((event) => event.type)).toEqual([
|
expect(events.map((event) => event.type)).toEqual([
|
||||||
"job_created",
|
"job_created",
|
||||||
|
"llm_call_started",
|
||||||
|
"llm_call_responded",
|
||||||
|
"llm_call_validated",
|
||||||
"fact_card_ready",
|
"fact_card_ready",
|
||||||
"draft_started",
|
"draft_started",
|
||||||
|
"llm_call_started",
|
||||||
|
"llm_call_responded",
|
||||||
|
"llm_call_validated",
|
||||||
"draft_ready",
|
"draft_ready",
|
||||||
"qa_started",
|
"qa_started",
|
||||||
|
"llm_call_started",
|
||||||
|
"llm_call_responded",
|
||||||
|
"llm_call_validated",
|
||||||
"qa_ready",
|
"qa_ready",
|
||||||
"final_ready",
|
"final_ready",
|
||||||
]);
|
]);
|
||||||
|
expect(JSON.stringify(events)).not.toContain("messages");
|
||||||
|
expect(JSON.stringify(events)).not.toContain("choices");
|
||||||
|
expect(JSON.stringify(events)).not.toContain("test-key");
|
||||||
expect(
|
expect(
|
||||||
events.find((event) => event.type === "fact_card_ready")?.fact_card,
|
events.find((event) => event.type === "fact_card_ready")?.fact_card,
|
||||||
).toEqual(expect.objectContaining({ confirmed_by_user: false }));
|
).toEqual(expect.objectContaining({ confirmed_by_user: false }));
|
||||||
@@ -784,6 +795,47 @@ async function createJobFixture() {
|
|||||||
return response.json() as Promise<{ job: { id: string } }>;
|
return response.json() as Promise<{ job: { id: string } }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mockTrackedLlmResult(value: unknown, callId: string) {
|
||||||
|
llmMocks.generateValidatedJson.mockImplementationOnce(async (input) => {
|
||||||
|
const onTraceEvent = input.onTraceEvent as
|
||||||
|
| ((event: Record<string, unknown>) => void | Promise<void>)
|
||||||
|
| undefined;
|
||||||
|
await onTraceEvent?.({
|
||||||
|
type: "started",
|
||||||
|
call_id: callId,
|
||||||
|
task: input.task ?? "unknown",
|
||||||
|
context: {
|
||||||
|
workflow_stage: input.traceStage ?? "unknown",
|
||||||
|
rewrite_round: input.rewriteRound,
|
||||||
|
schema_name: input.schemaName,
|
||||||
|
},
|
||||||
|
provider: "deepseek",
|
||||||
|
model: "deepseek-v4-pro",
|
||||||
|
request: { model: "deepseek-v4-pro", messages: [] },
|
||||||
|
started_at: "2026-07-16T00:00:00.000Z",
|
||||||
|
});
|
||||||
|
await onTraceEvent?.({
|
||||||
|
type: "responded",
|
||||||
|
call_id: callId,
|
||||||
|
response: {
|
||||||
|
choices: [{ message: { content: JSON.stringify(value) } }],
|
||||||
|
usage: { prompt_tokens: 10, completion_tokens: 4, total_tokens: 14 },
|
||||||
|
},
|
||||||
|
duration_ms: 1200,
|
||||||
|
responded_at: "2026-07-16T00:00:01.200Z",
|
||||||
|
});
|
||||||
|
await onTraceEvent?.({
|
||||||
|
type: "validated",
|
||||||
|
call_id: callId,
|
||||||
|
schema_name: input.schemaName ?? "anonymousSchema",
|
||||||
|
schema_valid: true,
|
||||||
|
validation_issues: [],
|
||||||
|
validated_at: "2026-07-16T00:00:01.300Z",
|
||||||
|
});
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function request(body: unknown, options: { apiKey?: string | null } = {}) {
|
function request(body: unknown, options: { apiKey?: string | null } = {}) {
|
||||||
const headers: Record<string, string> = { "content-type": "application/json" };
|
const headers: Record<string, string> = { "content-type": "application/json" };
|
||||||
const apiKey = options.apiKey === undefined ? "test-key" : options.apiKey;
|
const apiKey = options.apiKey === undefined ? "test-key" : options.apiKey;
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ import { getRepositoryFromRuntime } from "../../../../lib/db/repository";
|
|||||||
import { optimizationFactCardSchema } from "../../../../lib/domain/validation";
|
import { optimizationFactCardSchema } from "../../../../lib/domain/validation";
|
||||||
import type { LlmAuditSummary } from "../../../../lib/llm/audit";
|
import type { LlmAuditSummary } from "../../../../lib/llm/audit";
|
||||||
import { LlmValidationError } from "../../../../lib/llm/client";
|
import { LlmValidationError } from "../../../../lib/llm/client";
|
||||||
|
import { getLlmTracePayloadStoreFromRuntime } from "../../../../lib/llm/trace-payload-store";
|
||||||
|
import {
|
||||||
|
createLlmTraceRecorder,
|
||||||
|
createNoopLlmTraceRecorder,
|
||||||
|
safeTraceError,
|
||||||
|
} from "../../../../lib/llm/trace-recorder";
|
||||||
|
import { getLlmTraceRepositoryFromRuntime } from "../../../../lib/llm/trace-repository";
|
||||||
import { getExportStoreFromRuntime } from "../../../../lib/workflow/export-store";
|
import { getExportStoreFromRuntime } from "../../../../lib/workflow/export-store";
|
||||||
import { extractCandidateFactCard } from "../../../../lib/workflow/fact-extractor";
|
import { extractCandidateFactCard } from "../../../../lib/workflow/fact-extractor";
|
||||||
import {
|
import {
|
||||||
@@ -61,6 +68,7 @@ export async function POST(request: Request) {
|
|||||||
let jobId: string | undefined;
|
let jobId: string | undefined;
|
||||||
let caseId: string | undefined;
|
let caseId: string | undefined;
|
||||||
let stage: OptimizationStreamStage = "job";
|
let stage: OptimizationStreamStage = "job";
|
||||||
|
let traceRecorder = createNoopLlmTraceRecorder();
|
||||||
const llmAuditSummary: LlmAuditSummary[] = [];
|
const llmAuditSummary: LlmAuditSummary[] = [];
|
||||||
const processSummary: ProcessSummaryStep[] = [];
|
const processSummary: ProcessSummaryStep[] = [];
|
||||||
const requestStartedAt = Date.now();
|
const requestStartedAt = Date.now();
|
||||||
@@ -104,6 +112,23 @@ export async function POST(request: Request) {
|
|||||||
case: { id: optimizationCase.id, case_type: "article" },
|
case: { id: optimizationCase.id, case_type: "article" },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
traceRecorder = await createLlmTraceRecorder({
|
||||||
|
jobId: job.id,
|
||||||
|
caseId: optimizationCase.id,
|
||||||
|
repository: getLlmTraceRepositoryFromRuntime(),
|
||||||
|
payloadStore: getLlmTracePayloadStoreFromRuntime(),
|
||||||
|
publish: (event) => send(event),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
send({
|
||||||
|
type: "trace_warning",
|
||||||
|
job_id: job.id,
|
||||||
|
trace_completeness: "incomplete",
|
||||||
|
error_summary: safeTraceError(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
stage = "fact_card";
|
stage = "fact_card";
|
||||||
const factCardStartedAt = Date.now();
|
const factCardStartedAt = Date.now();
|
||||||
const factCard = optimizationFactCardSchema.parse(
|
const factCard = optimizationFactCardSchema.parse(
|
||||||
@@ -112,6 +137,7 @@ export async function POST(request: Request) {
|
|||||||
onAuditSummary: (summary) => {
|
onAuditSummary: (summary) => {
|
||||||
llmAuditSummary.push(summary);
|
llmAuditSummary.push(summary);
|
||||||
},
|
},
|
||||||
|
onTraceEvent: traceRecorder.onLlmEvent,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
processSummary.push(
|
processSummary.push(
|
||||||
@@ -124,20 +150,24 @@ export async function POST(request: Request) {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const savedFactCard = await repository.saveFactCard(job.id, factCard);
|
const savedFactCard = await repository.saveFactCard(job.id, factCard);
|
||||||
send({
|
const factCardReadyEvent: OptimizationStreamEvent = {
|
||||||
type: "fact_card_ready",
|
type: "fact_card_ready",
|
||||||
job_id: job.id,
|
job_id: job.id,
|
||||||
fact_card: savedFactCard,
|
fact_card: savedFactCard,
|
||||||
});
|
};
|
||||||
|
await traceRecorder.onWorkflowEvent(factCardReadyEvent);
|
||||||
|
send(factCardReadyEvent);
|
||||||
|
|
||||||
const result = await runStreamingOptimizationWorkflow({
|
const result = await runStreamingOptimizationWorkflow({
|
||||||
jobId: job.id,
|
jobId: job.id,
|
||||||
input: normalized.articleInput,
|
input: normalized.articleInput,
|
||||||
factCard: savedFactCard,
|
factCard: savedFactCard,
|
||||||
onEvent: (event) => {
|
onEvent: async (event) => {
|
||||||
stage = stageForEvent(event, stage);
|
stage = stageForEvent(event, stage);
|
||||||
|
await traceRecorder.onWorkflowEvent(event);
|
||||||
send(event);
|
send(event);
|
||||||
},
|
},
|
||||||
|
onTraceEvent: traceRecorder.onLlmEvent,
|
||||||
onAuditSummary: (summary) => {
|
onAuditSummary: (summary) => {
|
||||||
llmAuditSummary.push(summary);
|
llmAuditSummary.push(summary);
|
||||||
},
|
},
|
||||||
@@ -180,7 +210,7 @@ export async function POST(request: Request) {
|
|||||||
error_stage: null,
|
error_stage: null,
|
||||||
error_summary: null,
|
error_summary: null,
|
||||||
});
|
});
|
||||||
send({
|
const finalReadyEvent: OptimizationStreamEvent = {
|
||||||
type: "final_ready",
|
type: "final_ready",
|
||||||
job_id: job.id,
|
job_id: job.id,
|
||||||
case: { id: optimizationCase.id, case_type: "article" },
|
case: { id: optimizationCase.id, case_type: "article" },
|
||||||
@@ -188,7 +218,10 @@ export async function POST(request: Request) {
|
|||||||
optimized_article: optimizedArticle,
|
optimized_article: optimizedArticle,
|
||||||
qa_report: qaReport,
|
qa_report: qaReport,
|
||||||
export_paths: exportPaths,
|
export_paths: exportPaths,
|
||||||
});
|
};
|
||||||
|
await traceRecorder.onWorkflowEvent(finalReadyEvent);
|
||||||
|
await traceRecorder.finish({ status: "completed" });
|
||||||
|
send(finalReadyEvent);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : "优化失败";
|
const message = error instanceof Error ? error.message : "优化失败";
|
||||||
let failedVersion:
|
let failedVersion:
|
||||||
@@ -225,14 +258,21 @@ export async function POST(request: Request) {
|
|||||||
failedVersion = undefined;
|
failedVersion = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
send({
|
const failedEvent: OptimizationStreamEvent = {
|
||||||
type: "failed",
|
type: "failed",
|
||||||
job_id: jobId,
|
job_id: jobId,
|
||||||
case: caseId ? { id: caseId, case_type: "article" } : undefined,
|
case: caseId ? { id: caseId, case_type: "article" } : undefined,
|
||||||
result_version: failedVersion,
|
result_version: failedVersion,
|
||||||
stage,
|
stage,
|
||||||
error: message,
|
error: message,
|
||||||
|
};
|
||||||
|
await traceRecorder.onWorkflowEvent(failedEvent);
|
||||||
|
await traceRecorder.finish({
|
||||||
|
status: "failed",
|
||||||
|
errorStage: stage,
|
||||||
|
errorSummary: message,
|
||||||
});
|
});
|
||||||
|
send(failedEvent);
|
||||||
} finally {
|
} finally {
|
||||||
controller.close();
|
controller.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import type {
|
|||||||
LlmTraceCallStatus,
|
LlmTraceCallStatus,
|
||||||
LlmTraceCompleteness,
|
LlmTraceCompleteness,
|
||||||
LlmTraceErrorType,
|
LlmTraceErrorType,
|
||||||
LlmTraceRun,
|
|
||||||
LlmTraceRunStatus,
|
LlmTraceRunStatus,
|
||||||
LlmTraceWorkflowStage,
|
LlmTraceWorkflowStage,
|
||||||
} from "./trace-types";
|
} from "./trace-types";
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import type {
|
|||||||
LlmTraceCallStatus,
|
LlmTraceCallStatus,
|
||||||
LlmTraceCompleteness,
|
LlmTraceCompleteness,
|
||||||
LlmTraceErrorType,
|
LlmTraceErrorType,
|
||||||
LlmTraceRun,
|
|
||||||
LlmTraceRunStatus,
|
LlmTraceRunStatus,
|
||||||
LlmTraceWorkflowStage,
|
LlmTraceWorkflowStage,
|
||||||
} from "./trace-types";
|
} from "./trace-types";
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ import {
|
|||||||
export interface OptimizeArticleInput {
|
export interface OptimizeArticleInput {
|
||||||
input: ArticleInput;
|
input: ArticleInput;
|
||||||
factCard: OptimizationFactCard;
|
factCard: OptimizationFactCard;
|
||||||
|
onTraceEvent?: GenerateInput["onTraceEvent"];
|
||||||
onAuditSummary?: GenerateInput["onAuditSummary"];
|
onAuditSummary?: GenerateInput["onAuditSummary"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function optimizeArticle({
|
export async function optimizeArticle({
|
||||||
input,
|
input,
|
||||||
factCard,
|
factCard,
|
||||||
|
onTraceEvent,
|
||||||
onAuditSummary,
|
onAuditSummary,
|
||||||
}: OptimizeArticleInput): Promise<OptimizedArticle> {
|
}: OptimizeArticleInput): Promise<OptimizedArticle> {
|
||||||
const llmArticle = await generateValidatedJson({
|
const llmArticle = await generateValidatedJson({
|
||||||
@@ -27,6 +29,9 @@ export async function optimizeArticle({
|
|||||||
prompt: buildArticleOptimizerPrompt(input, factCard),
|
prompt: buildArticleOptimizerPrompt(input, factCard),
|
||||||
temperature: 0.2,
|
temperature: 0.2,
|
||||||
task: "article_optimizer",
|
task: "article_optimizer",
|
||||||
|
schemaName: "optimizedArticleSchema",
|
||||||
|
traceStage: "draft",
|
||||||
|
onTraceEvent,
|
||||||
onAuditSummary,
|
onAuditSummary,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ import {
|
|||||||
|
|
||||||
export async function extractCandidateFactCard(
|
export async function extractCandidateFactCard(
|
||||||
input: ArticleInput,
|
input: ArticleInput,
|
||||||
options: { onAuditSummary?: GenerateInput["onAuditSummary"] } = {},
|
options: {
|
||||||
|
onAuditSummary?: GenerateInput["onAuditSummary"];
|
||||||
|
onTraceEvent?: GenerateInput["onTraceEvent"];
|
||||||
|
} = {},
|
||||||
): Promise<CandidateFactCard> {
|
): Promise<CandidateFactCard> {
|
||||||
return generateValidatedJson({
|
return generateValidatedJson({
|
||||||
schema: candidateFactCardSchema,
|
schema: candidateFactCardSchema,
|
||||||
@@ -16,6 +19,9 @@ export async function extractCandidateFactCard(
|
|||||||
prompt: buildFactExtractorPrompt(input),
|
prompt: buildFactExtractorPrompt(input),
|
||||||
temperature: 0.1,
|
temperature: 0.1,
|
||||||
task: "fact_extractor",
|
task: "fact_extractor",
|
||||||
|
schemaName: "candidateFactCardSchema",
|
||||||
|
traceStage: "fact_card",
|
||||||
|
onTraceEvent: options.onTraceEvent,
|
||||||
onAuditSummary: options.onAuditSummary,
|
onAuditSummary: options.onAuditSummary,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ export interface InspectQualityInput {
|
|||||||
factCard: OptimizationFactCard;
|
factCard: OptimizationFactCard;
|
||||||
platform: PublishPlatform;
|
platform: PublishPlatform;
|
||||||
sourceImages: ImageInput[];
|
sourceImages: ImageInput[];
|
||||||
|
rewriteRound?: number;
|
||||||
|
onTraceEvent?: GenerateInput["onTraceEvent"];
|
||||||
onAuditSummary?: GenerateInput["onAuditSummary"];
|
onAuditSummary?: GenerateInput["onAuditSummary"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +74,10 @@ export async function inspectQualityWithLlm(
|
|||||||
}),
|
}),
|
||||||
temperature: 0.1,
|
temperature: 0.1,
|
||||||
task: "quality_inspector",
|
task: "quality_inspector",
|
||||||
|
schemaName: "llmQaPatchSchema",
|
||||||
|
traceStage: "qa",
|
||||||
|
rewriteRound: input.rewriteRound,
|
||||||
|
onTraceEvent: input.onTraceEvent,
|
||||||
onAuditSummary: input.onAuditSummary,
|
onAuditSummary: input.onAuditSummary,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface RunStreamingOptimizationWorkflowInput {
|
|||||||
input: ArticleInput;
|
input: ArticleInput;
|
||||||
factCard: OptimizationFactCard;
|
factCard: OptimizationFactCard;
|
||||||
onEvent: (event: OptimizationStreamEvent) => void | Promise<void>;
|
onEvent: (event: OptimizationStreamEvent) => void | Promise<void>;
|
||||||
|
onTraceEvent?: GenerateInput["onTraceEvent"];
|
||||||
onAuditSummary?: GenerateInput["onAuditSummary"];
|
onAuditSummary?: GenerateInput["onAuditSummary"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ export async function runStreamingOptimizationWorkflow({
|
|||||||
input,
|
input,
|
||||||
factCard,
|
factCard,
|
||||||
onEvent,
|
onEvent,
|
||||||
|
onTraceEvent,
|
||||||
onAuditSummary,
|
onAuditSummary,
|
||||||
}: RunStreamingOptimizationWorkflowInput) {
|
}: RunStreamingOptimizationWorkflowInput) {
|
||||||
const processSummary: ProcessSummaryStep[] = [];
|
const processSummary: ProcessSummaryStep[] = [];
|
||||||
@@ -31,7 +33,12 @@ export async function runStreamingOptimizationWorkflow({
|
|||||||
message: "正在生成优化草稿",
|
message: "正在生成优化草稿",
|
||||||
});
|
});
|
||||||
let stageStartedAt = Date.now();
|
let stageStartedAt = Date.now();
|
||||||
let article = await optimizeArticle({ input, factCard, onAuditSummary });
|
let article = await optimizeArticle({
|
||||||
|
input,
|
||||||
|
factCard,
|
||||||
|
onTraceEvent,
|
||||||
|
onAuditSummary,
|
||||||
|
});
|
||||||
processSummary.push(
|
processSummary.push(
|
||||||
createProcessStep({
|
createProcessStep({
|
||||||
stage: "draft",
|
stage: "draft",
|
||||||
@@ -54,6 +61,8 @@ export async function runStreamingOptimizationWorkflow({
|
|||||||
factCard,
|
factCard,
|
||||||
platform: input.platform,
|
platform: input.platform,
|
||||||
sourceImages: input.images,
|
sourceImages: input.images,
|
||||||
|
rewriteRound: 0,
|
||||||
|
onTraceEvent,
|
||||||
onAuditSummary,
|
onAuditSummary,
|
||||||
});
|
});
|
||||||
processSummary.push(
|
processSummary.push(
|
||||||
@@ -81,6 +90,8 @@ export async function runStreamingOptimizationWorkflow({
|
|||||||
article,
|
article,
|
||||||
factCard,
|
factCard,
|
||||||
failedChecks,
|
failedChecks,
|
||||||
|
rewriteRound: nextRound,
|
||||||
|
onTraceEvent,
|
||||||
onAuditSummary,
|
onAuditSummary,
|
||||||
});
|
});
|
||||||
rewriteRounds = nextRound;
|
rewriteRounds = nextRound;
|
||||||
@@ -112,6 +123,8 @@ export async function runStreamingOptimizationWorkflow({
|
|||||||
factCard,
|
factCard,
|
||||||
platform: input.platform,
|
platform: input.platform,
|
||||||
sourceImages: input.images,
|
sourceImages: input.images,
|
||||||
|
rewriteRound: nextRound,
|
||||||
|
onTraceEvent,
|
||||||
onAuditSummary,
|
onAuditSummary,
|
||||||
});
|
});
|
||||||
processSummary.push(
|
processSummary.push(
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export interface RewriteFailedSectionsInput {
|
|||||||
article: OptimizedArticle;
|
article: OptimizedArticle;
|
||||||
factCard: OptimizationFactCard;
|
factCard: OptimizationFactCard;
|
||||||
failedChecks: QaCheck[];
|
failedChecks: QaCheck[];
|
||||||
|
rewriteRound?: number;
|
||||||
|
onTraceEvent?: GenerateInput["onTraceEvent"];
|
||||||
onAuditSummary?: GenerateInput["onAuditSummary"];
|
onAuditSummary?: GenerateInput["onAuditSummary"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,6 +19,8 @@ export async function rewriteFailedSections({
|
|||||||
article,
|
article,
|
||||||
factCard,
|
factCard,
|
||||||
failedChecks,
|
failedChecks,
|
||||||
|
rewriteRound,
|
||||||
|
onTraceEvent,
|
||||||
onAuditSummary,
|
onAuditSummary,
|
||||||
}: RewriteFailedSectionsInput): Promise<OptimizedArticle> {
|
}: RewriteFailedSectionsInput): Promise<OptimizedArticle> {
|
||||||
const llmArticle = await generateValidatedJson({
|
const llmArticle = await generateValidatedJson({
|
||||||
@@ -25,6 +29,10 @@ export async function rewriteFailedSections({
|
|||||||
prompt: buildTargetedRewritePrompt({ article, factCard, failedChecks }),
|
prompt: buildTargetedRewritePrompt({ article, factCard, failedChecks }),
|
||||||
temperature: 0.15,
|
temperature: 0.15,
|
||||||
task: "targeted_rewriter",
|
task: "targeted_rewriter",
|
||||||
|
schemaName: "optimizedArticleSchema",
|
||||||
|
traceStage: "rewrite",
|
||||||
|
rewriteRound,
|
||||||
|
onTraceEvent,
|
||||||
onAuditSummary,
|
onAuditSummary,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user