feat: use llm for targeted rewrite
This commit is contained in:
@@ -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");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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: [
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user