接入文章优化案例自动保存

This commit is contained in:
czj
2026-07-08 13:24:50 +08:00
parent ee98c3304b
commit 79913ef7f7
11 changed files with 446 additions and 14 deletions
+4 -1
View File
@@ -4,7 +4,7 @@ import type {
OptimizedArticle,
} from "../domain/types";
import { optimizedArticleSchema } from "../domain/validation";
import { generateValidatedJson } from "../llm/client";
import { generateValidatedJson, type GenerateInput } from "../llm/client";
import {
ARTICLE_OPTIMIZER_SYSTEM_PROMPT,
buildArticleOptimizerPrompt,
@@ -13,11 +13,13 @@ import {
export interface OptimizeArticleInput {
input: ArticleInput;
factCard: OptimizationFactCard;
onAuditSummary?: GenerateInput["onAuditSummary"];
}
export async function optimizeArticle({
input,
factCard,
onAuditSummary,
}: OptimizeArticleInput): Promise<OptimizedArticle> {
const llmArticle = await generateValidatedJson({
schema: optimizedArticleSchema,
@@ -25,6 +27,7 @@ export async function optimizeArticle({
prompt: buildArticleOptimizerPrompt(input, factCard),
temperature: 0.2,
task: "article_optimizer",
onAuditSummary,
});
return optimizedArticleSchema.parse({
+3 -1
View File
@@ -1,6 +1,6 @@
import type { ArticleInput, CandidateFactCard } from "../domain/types";
import { candidateFactCardSchema } from "../domain/validation";
import { generateValidatedJson } from "../llm/client";
import { generateValidatedJson, type GenerateInput } from "../llm/client";
import {
FACT_EXTRACTOR_SYSTEM_PROMPT,
buildFactExtractorPrompt,
@@ -8,6 +8,7 @@ import {
export async function extractCandidateFactCard(
input: ArticleInput,
options: { onAuditSummary?: GenerateInput["onAuditSummary"] } = {},
): Promise<CandidateFactCard> {
return generateValidatedJson({
schema: candidateFactCardSchema,
@@ -15,5 +16,6 @@ export async function extractCandidateFactCard(
prompt: buildFactExtractorPrompt(input),
temperature: 0.1,
task: "fact_extractor",
onAuditSummary: options.onAuditSummary,
});
}
+7 -2
View File
@@ -1,4 +1,5 @@
import type { ArticleInput, OptimizationFactCard } from "../domain/types";
import type { GenerateInput } from "../llm/client";
import { optimizeArticle } from "./article-optimizer";
import { inspectQualityWithLlm } from "./quality-inspector";
@@ -8,6 +9,7 @@ export interface RunOptimizationWorkflowInput {
input: ArticleInput;
factCard: OptimizationFactCard;
onProgress?: (event: WorkflowProgressEvent) => void | Promise<void>;
onAuditSummary?: GenerateInput["onAuditSummary"];
}
export interface WorkflowTimingStep {
@@ -68,13 +70,14 @@ export async function runOptimizationWorkflow({
input,
factCard,
onProgress,
onAuditSummary,
}: RunOptimizationWorkflowInput) {
const startedAt = Date.now();
const timingSteps: WorkflowTimingStep[] = [];
let article = await timedStep(
"生成优化稿",
timingSteps,
() => optimizeArticle({ input, factCard }),
() => optimizeArticle({ input, factCard, onAuditSummary }),
onProgress,
);
let qaReport = await timedStep(
@@ -86,6 +89,7 @@ export async function runOptimizationWorkflow({
factCard,
platform: input.platform,
sourceImages: input.images,
onAuditSummary,
}),
onProgress,
);
@@ -97,7 +101,7 @@ export async function runOptimizationWorkflow({
article = await timedStep(
`定向修复第 ${nextRound}`,
timingSteps,
() => rewriteFailedSections({ article, factCard, failedChecks }),
() => rewriteFailedSections({ article, factCard, failedChecks, onAuditSummary }),
onProgress,
);
rewriteRounds = nextRound;
@@ -110,6 +114,7 @@ export async function runOptimizationWorkflow({
factCard,
platform: input.platform,
sourceImages: input.images,
onAuditSummary,
}),
onProgress,
);
+3 -1
View File
@@ -9,7 +9,7 @@ import type {
QualityRuleId,
} from "../domain/types";
import { qaCheckSchema, qaReportSchema } from "../domain/validation";
import { generateValidatedJson } from "../llm/client";
import { generateValidatedJson, type GenerateInput } from "../llm/client";
import {
QUALITY_INSPECTOR_SYSTEM_PROMPT,
buildQualityInspectorPrompt,
@@ -43,6 +43,7 @@ export interface InspectQualityInput {
factCard: OptimizationFactCard;
platform: PublishPlatform;
sourceImages: ImageInput[];
onAuditSummary?: GenerateInput["onAuditSummary"];
}
export function inspectQuality(input: InspectQualityInput): QaReport {
@@ -71,6 +72,7 @@ export async function inspectQualityWithLlm(
}),
temperature: 0.1,
task: "quality_inspector",
onAuditSummary: input.onAuditSummary,
});
const patchedChecks = deterministicReport.checks.map((deterministicCheck) => {
+9 -1
View File
@@ -14,7 +14,11 @@ export type OptimizationStreamStage =
| "final";
export type OptimizationStreamEvent =
| { type: "job_created"; job: { id: string } }
| {
type: "job_created";
job: { id: string };
case?: { id: string; case_type: "article" };
}
| {
type: "fact_card_ready";
job_id: string;
@@ -34,6 +38,8 @@ export type OptimizationStreamEvent =
| {
type: "final_ready";
job_id: string;
case?: { id: string; case_type: "article" };
result_version?: { id: string; version: number };
optimized_article: OptimizedArticle;
qa_report: QaReport;
export_paths: Record<string, string>;
@@ -41,6 +47,8 @@ export type OptimizationStreamEvent =
| {
type: "failed";
job_id?: string;
case?: { id: string; case_type: "article" };
result_version?: { id: string; version: number };
stage: OptimizationStreamStage;
error: string;
};
+59 -2
View File
@@ -1,4 +1,7 @@
import type { ArticleInput, OptimizationFactCard } from "../domain/types";
import type { ProcessSummaryStep } from "../cases/types";
import { createProcessStep } from "../cases/summaries";
import type { GenerateInput } from "../llm/client";
import { optimizeArticle } from "./article-optimizer";
import { inspectQualityWithLlm } from "./quality-inspector";
@@ -10,6 +13,7 @@ export interface RunStreamingOptimizationWorkflowInput {
input: ArticleInput;
factCard: OptimizationFactCard;
onEvent: (event: OptimizationStreamEvent) => void | Promise<void>;
onAuditSummary?: GenerateInput["onAuditSummary"];
}
export async function runStreamingOptimizationWorkflow({
@@ -17,13 +21,26 @@ export async function runStreamingOptimizationWorkflow({
input,
factCard,
onEvent,
onAuditSummary,
}: RunStreamingOptimizationWorkflowInput) {
const processSummary: ProcessSummaryStep[] = [];
await onEvent({
type: "draft_started",
job_id: jobId,
message: "正在生成优化草稿",
});
let article = await optimizeArticle({ input, factCard });
let stageStartedAt = Date.now();
let article = await optimizeArticle({ input, factCard, onAuditSummary });
processSummary.push(
createProcessStep({
stage: "draft",
startedAt: stageStartedAt,
endedAt: Date.now(),
status: "success",
producedResultVersion: false,
}),
);
await onEvent({ type: "draft_ready", job_id: jobId, article });
await onEvent({
@@ -31,12 +48,23 @@ export async function runStreamingOptimizationWorkflow({
job_id: jobId,
message: "正在检查质量",
});
stageStartedAt = Date.now();
let qaReport = await inspectQualityWithLlm({
article,
factCard,
platform: input.platform,
sourceImages: input.images,
onAuditSummary,
});
processSummary.push(
createProcessStep({
stage: "qa",
startedAt: stageStartedAt,
endedAt: Date.now(),
status: "success",
producedResultVersion: false,
}),
);
await onEvent({ type: "qa_ready", job_id: jobId, qa_report: qaReport });
let rewriteRounds = 0;
@@ -48,8 +76,24 @@ export async function runStreamingOptimizationWorkflow({
job_id: jobId,
round: nextRound,
});
article = await rewriteFailedSections({ article, factCard, failedChecks });
stageStartedAt = Date.now();
article = await rewriteFailedSections({
article,
factCard,
failedChecks,
onAuditSummary,
});
rewriteRounds = nextRound;
processSummary.push(
createProcessStep({
stage: "rewrite",
startedAt: stageStartedAt,
endedAt: Date.now(),
status: "success",
rewriteRound: nextRound,
producedResultVersion: false,
}),
);
await onEvent({
type: "rewrite_ready",
job_id: jobId,
@@ -62,12 +106,24 @@ export async function runStreamingOptimizationWorkflow({
job_id: jobId,
message: `正在复检第 ${nextRound} 轮修复`,
});
stageStartedAt = Date.now();
qaReport = await inspectQualityWithLlm({
article,
factCard,
platform: input.platform,
sourceImages: input.images,
onAuditSummary,
});
processSummary.push(
createProcessStep({
stage: "qa",
startedAt: stageStartedAt,
endedAt: Date.now(),
status: "success",
rewriteRound: nextRound,
producedResultVersion: false,
}),
);
await onEvent({ type: "qa_ready", job_id: jobId, qa_report: qaReport });
}
@@ -77,5 +133,6 @@ export async function runStreamingOptimizationWorkflow({
rewriteRounds,
stoppedAfterMaxRewrites:
qaReport.overall_status === "fail" && rewriteRounds >= 2,
processSummary,
};
}
+4 -1
View File
@@ -1,6 +1,6 @@
import type { OptimizationFactCard, OptimizedArticle, QaCheck } from "../domain/types";
import { optimizedArticleSchema } from "../domain/validation";
import { generateValidatedJson } from "../llm/client";
import { generateValidatedJson, type GenerateInput } from "../llm/client";
import {
TARGETED_REWRITER_SYSTEM_PROMPT,
buildTargetedRewritePrompt,
@@ -10,12 +10,14 @@ export interface RewriteFailedSectionsInput {
article: OptimizedArticle;
factCard: OptimizationFactCard;
failedChecks: QaCheck[];
onAuditSummary?: GenerateInput["onAuditSummary"];
}
export async function rewriteFailedSections({
article,
factCard,
failedChecks,
onAuditSummary,
}: RewriteFailedSectionsInput): Promise<OptimizedArticle> {
const llmArticle = await generateValidatedJson({
schema: optimizedArticleSchema,
@@ -23,6 +25,7 @@ export async function rewriteFailedSections({
prompt: buildTargetedRewritePrompt({ article, factCard, failedChecks }),
temperature: 0.15,
task: "targeted_rewriter",
onAuditSummary,
});
return optimizedArticleSchema.parse({