feat: implement article optimization workflow

This commit is contained in:
Codex
2026-06-21 23:42:37 +08:00
parent cb56ff9ee7
commit 75bcab33c6
10 changed files with 834 additions and 1 deletions
+83
View File
@@ -0,0 +1,83 @@
import type {
ArticleInput,
ConfirmedFactCard,
OptimizedArticle,
} from "../domain/types";
import { optimizedArticleSchema } from "../domain/validation";
export interface OptimizeArticleInput {
input: ArticleInput;
factCard: ConfirmedFactCard;
}
export async function optimizeArticle({
input,
factCard,
}: OptimizeArticleInput): Promise<OptimizedArticle> {
const unsupported = findUnsupportedInstructionClaims(
input.user_instructions,
factCard,
);
const title = `${factCard.brand_names[0] ?? factCard.company_short_names[0] ?? factCard.company_full_name} ${factCard.target_industry} Guide`;
const coreClaims =
factCard.core_claims.length > 0
? factCard.core_claims.map((claim) => `- ${claim}`).join("\n")
: "- Confirmed facts only; no extra claims added.";
const body = [
`## ${factCard.company_full_name}`,
cleanBody(input.body, factCard),
"",
"### Confirmed Facts",
coreClaims,
].join("\n");
return optimizedArticleSchema.parse({
title,
summary: `A ${input.platform.replace(/_/g, " ")} article for ${factCard.target_audience} about ${factCard.target_industry}.`,
body_markdown: body,
image_suggestions: factCard.image_topics.map((topic, index) => ({
source: `image_${index + 1}`,
suggestion: `Use image content related to ${topic}.`,
})),
changed_sections: ["title", "body structure", "summary"],
requires_user_confirmation: unsupported,
});
}
function cleanBody(body: string, factCard: ConfirmedFactCard) {
let cleaned = body.trim();
for (const forbidden of factCard.forbidden_claims) {
cleaned = cleaned.replace(new RegExp(escapeRegExp(forbidden), "gi"), "");
}
return cleaned;
}
function findUnsupportedInstructionClaims(
instructions: string,
factCard: ConfirmedFactCard,
) {
const unsupported: string[] = [];
const numbers = [...instructions.matchAll(/\b\d+\s*[A-Za-z]+\b/g)].map(
(match) => match[0],
);
const knownText = [
factCard.experience_years?.toString() ?? "",
...factCard.core_claims,
].join(" ");
for (const claim of numbers) {
if (!knownText.includes(claim.replace(/\D/g, ""))) {
unsupported.push(`Unsupported requested claim: ${claim}`);
}
}
if (/fortune\s*500/i.test(instructions)) {
unsupported.push("Unsupported requested claim: Fortune 500 customer cases");
}
return unsupported;
}
function escapeRegExp(value: string) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}