feat: use llm for targeted rewrite

This commit is contained in:
Codex
2026-06-21 23:43:02 +08:00
parent 9efbe1a354
commit 3d49789743
4 changed files with 92 additions and 4 deletions
@@ -16,6 +16,7 @@ vi.mock("../../llm/client", async () => {
import { extractCandidateFactCard } from "../fact-extractor"; import { extractCandidateFactCard } from "../fact-extractor";
import { optimizeArticle } from "../article-optimizer"; import { optimizeArticle } from "../article-optimizer";
import { rewriteFailedSections } from "../targeted-rewriter";
describe("LLM workflow integration", () => { describe("LLM workflow integration", () => {
const confirmedFactCard = { const confirmedFactCard = {
@@ -127,4 +128,70 @@ describe("LLM workflow integration", () => {
"Unsupported requested claim: 99 patents", "Unsupported requested claim: 99 patents",
); );
}); });
it("uses LLM output for targeted rewrite when valid", async () => {
llmMocks.generateValidatedJson.mockResolvedValueOnce({
title: "Rewritten By LLM",
summary: "Original summary",
body_markdown:
"## Body\nExample Technology Co., Ltd. focuses on GEO optimization.",
image_suggestions: [],
changed_sections: ["title"],
requires_user_confirmation: [],
});
const rewritten = await rewriteFailedSections({
article: {
title: "Bad title!!!",
summary: "Original summary",
body_markdown: "## Body\nOriginal body",
image_suggestions: [],
changed_sections: [],
requires_user_confirmation: [],
},
factCard: confirmedFactCard,
failedChecks: [
{
rule_id: "title_quality",
status: "fail",
evidence: "Bad title!!!",
reason: "Title has punctuation stuffing.",
suggested_fix: "Rewrite title.",
target_agent: "title",
},
],
});
expect(rewritten.title).toBe("Rewritten By LLM");
expect(llmMocks.generateValidatedJson).toHaveBeenCalledOnce();
});
it("falls back to deterministic targeted rewrite when LLM returns null", async () => {
llmMocks.generateValidatedJson.mockResolvedValueOnce(null);
const rewritten = await rewriteFailedSections({
article: {
title: "Bad title!!!",
summary: "Original summary",
body_markdown: "## Body\nOriginal body",
image_suggestions: [],
changed_sections: [],
requires_user_confirmation: [],
},
factCard: confirmedFactCard,
failedChecks: [
{
rule_id: "title_quality",
status: "fail",
evidence: "Bad title!!!",
reason: "Title has punctuation stuffing.",
suggested_fix: "Rewrite title.",
target_agent: "title",
},
],
});
expect(rewritten.title).toContain("GEO optimization Guide");
expect(rewritten.summary).toBe("Original summary");
});
}); });
+2 -2
View File
@@ -183,7 +183,7 @@ describe("workflow nodes", () => {
expect(report.overall_status).toBe("fail"); expect(report.overall_status).toBe("fail");
}); });
it("rewrites only the failing target area", () => { it("rewrites only the failing target area", async () => {
const article = { const article = {
title: "Bad title!!!", title: "Bad title!!!",
summary: "Original summary", summary: "Original summary",
@@ -193,7 +193,7 @@ describe("workflow nodes", () => {
requires_user_confirmation: [], requires_user_confirmation: [],
}; };
const rewritten = rewriteFailedSections({ const rewritten = await rewriteFailedSections({
article, article,
factCard: confirmedFactCard, factCard: confirmedFactCard,
failedChecks: [ failedChecks: [
+1 -1
View File
@@ -24,7 +24,7 @@ export async function runOptimizationWorkflow({
while (qaReport.overall_status === "fail" && rewriteRounds < 2) { while (qaReport.overall_status === "fail" && rewriteRounds < 2) {
const failedChecks = qaReport.checks.filter((check) => check.status === "fail"); const failedChecks = qaReport.checks.filter((check) => check.status === "fail");
article = rewriteFailedSections({ article, factCard, failedChecks }); article = await rewriteFailedSections({ article, factCard, failedChecks });
rewriteRounds += 1; rewriteRounds += 1;
qaReport = inspectQuality({ qaReport = inspectQuality({
article, article,
+22 -1
View File
@@ -1,4 +1,10 @@
import type { ConfirmedFactCard, OptimizedArticle, QaCheck } from "../domain/types"; import type { ConfirmedFactCard, OptimizedArticle, QaCheck } from "../domain/types";
import { optimizedArticleSchema } from "../domain/validation";
import { generateValidatedJson } from "../llm/client";
import {
TARGETED_REWRITER_SYSTEM_PROMPT,
buildTargetedRewritePrompt,
} from "../llm/prompts";
export interface RewriteFailedSectionsInput { export interface RewriteFailedSectionsInput {
article: OptimizedArticle; article: OptimizedArticle;
@@ -6,7 +12,22 @@ export interface RewriteFailedSectionsInput {
failedChecks: QaCheck[]; failedChecks: QaCheck[];
} }
export function rewriteFailedSections({ export async function rewriteFailedSections({
article,
factCard,
failedChecks,
}: RewriteFailedSectionsInput): Promise<OptimizedArticle> {
const llmArticle = await generateValidatedJson({
schema: optimizedArticleSchema,
system: TARGETED_REWRITER_SYSTEM_PROMPT,
prompt: buildTargetedRewritePrompt({ article, factCard, failedChecks }),
temperature: 0.15,
});
return llmArticle ?? rewriteFailedSectionsFallback({ article, factCard, failedChecks });
}
function rewriteFailedSectionsFallback({
article, article,
factCard, factCard,
failedChecks, failedChecks,