Files
GEOAgentArticleOptimizer/src/lib/llm/prompts.ts
T

351 lines
15 KiB
TypeScript

import type {
ArticleInput,
CopyOptimizationRequest,
OptimizationFactCard,
OptimizedArticle,
PublishPlatform,
QaCheck,
} from "../domain/types";
export const JSON_ONLY_PROMPT =
"Return valid JSON only. Do not include markdown fences or commentary.";
const CUSTOMER_RISK_GUIDANCE = [
"客户最担心的内容风险:行业漂移、公司全称/简称/品牌名不一致、官网文章出现第三方口吻、平台语气和文章类型不匹配、标题或正文语义不顺、虚构资质/年限/案例/能力、产品/服务/年限前后冲突。",
"任何客户案例、资质荣誉、经验年限、服务能力、出海/多语种/合规能力、效果承诺和排名,都必须能从原文或已确认事实卡中找到明确依据。",
].join(" ");
const PLATFORM_GUIDANCE: Record<PublishPlatform, string> = {
official_site:
"official_site / 官网文章:第一方品牌口吻,表达克制可信,可使用“我们/公司/品牌”视角;避免第三方评测腔、夸大宣传和无依据背书。",
media_article:
"media_article / 媒体稿:第三方客观报道口吻,突出事实、背景和行业价值;不要替品牌自夸,不制造新闻来源。",
comparison_review:
"comparison_review / 对比评测:对比分析口吻,只比较原文或事实卡中明确出现的信息;不得虚构竞品、排名、参数或测试结论。",
recommendation_list:
"recommendation_list / 推荐榜单:推荐榜单口吻,必须解释推荐依据;不得制造榜单排名、奖项或第三方认证。",
};
const FACT_CARD_OUTPUT_CONTRACT = [
"Output type contract:",
"- company_full_name, target_industry, target_audience must be strings.",
"- experience_years must be a number or null.",
"- company_short_names, brand_names, product_names, core_claims, forbidden_claims, image_topics, uncertain_items must be string[].",
"- core_claims must be string[].",
"- uncertain_items must be string[].",
"- Do not return objects inside string arrays; put the readable claim text directly in the array.",
"Example:",
JSON.stringify(
{
company_full_name: "示例科技有限公司",
company_short_names: ["示例科技"],
brand_names: ["示例品牌"],
product_names: ["GEO内容优化平台"],
target_industry: "GEO内容优化",
target_audience: "市场团队",
experience_years: null,
core_claims: ["提供GEO内容优化服务"],
forbidden_claims: ["行业第一"],
image_topics: ["产品后台截图"],
uncertain_items: ["客户案例缺少来源"],
},
null,
2,
),
].join("\n");
const OPTIMIZED_ARTICLE_OUTPUT_CONTRACT = [
"Output type contract:",
"- title, summary, body_markdown must be strings.",
"- image_suggestions must be [].",
"- changed_sections must be string[].",
"- requires_user_confirmation must be string[].",
"- Do not return objects inside string arrays; put the readable item text directly in the array.",
"Example:",
JSON.stringify(
{
title: "示例科技 GEO 内容优化方案",
summary: "围绕事实卡重写后的官网文章摘要。",
body_markdown: "## 服务能力\n示例科技有限公司提供GEO内容优化服务。",
image_suggestions: [],
changed_sections: ["title", "body"],
requires_user_confirmation: ["客户案例需要确认"],
},
null,
2,
),
].join("\n");
const QA_OUTPUT_CONTRACT = [
"Output type contract:",
"- Return { checks: QaCheck[] }.",
"- rule_id must reuse the exact English rule_id from deterministicChecks.",
"- status must be one of pass, warn, fail.",
"- evidence, reason, suggested_fix must be strings.",
"- target_agent must be title, body, fact_card, or null.",
"Example check:",
JSON.stringify(
{
rule_id: "title_quality",
status: "warn",
evidence: "标题偏营销化",
reason: "官网标题需要更克制",
suggested_fix: "改成事实型标题",
target_agent: "title",
},
null,
2,
),
].join("\n");
const RENWEI_OUTPUT_CONTRACT = [
"Output type contract:",
"- optimized_text must be a string.",
"- change_notes must be an array of { original, revised, reason, confidence, revertible }.",
"- confidence must be confident or uncertain.",
"- revertible must be boolean.",
"- ai_taste_checks must be an array of { rule_id, status, evidence, suggestion }.",
"- rule_id must be one of meaning_inflation, promotion_tone, formulaic_sentence, format_trace, chat_trace, filler_hedging.",
"- status must be pass or warn.",
"- warnings must be string[].",
"Example:",
JSON.stringify(
{
optimized_text: "我把句子顺了一下。",
change_notes: [
{
original: "我把句子顺顺。",
revised: "我把句子顺了一下。",
reason: "修掉重复,让句子自然收住。",
confidence: "confident",
revertible: false,
},
],
ai_taste_checks: [
{
rule_id: "promotion_tone",
status: "pass",
evidence: "没有新增宣传腔。",
suggestion: "",
},
],
warnings: [],
},
null,
2,
),
].join("\n");
function formatPlatformGuidance(selected: PublishPlatform) {
return [
"平台模板选项:",
...Object.values(PLATFORM_GUIDANCE).map((item) => `- ${item}`),
`当前目标平台:${selected}。必须优先遵守该平台模板。`,
].join("\n");
}
export const ARTICLE_OPTIMIZER_SYSTEM_PROMPT = [
"你是中文 GEO 内容优化编辑。目标不是复述原文,而是在事实卡约束下进行实质性重写。",
CUSTOMER_RISK_GUIDANCE,
"硬性规则:只能使用 confirmed fact card 和 source article 中明确出现的事实;不得新增客户案例、数字、资质、排名、奖项、服务能力、效果承诺;输出中文标题,禁止使用英文模板词如 Guide、Best、Top。",
"必须修复原文中的病句、断裂句、错别字、逻辑跳跃和段落结构问题。",
JSON_ONLY_PROMPT,
].join(" ");
export const QUALITY_INSPECTOR_SYSTEM_PROMPT = [
"你是质量门禁审核器。逐项检查 deterministicChecks 中的每个 rule_id。",
CUSTOMER_RISK_GUIDANCE,
"不得把 deterministic fail 降级。每项必须给出 status、evidence、reason、suggested_fix、target_agent。",
JSON_ONLY_PROMPT,
].join(" ");
export const FACT_EXTRACTOR_SYSTEM_PROMPT = [
"你是事实卡提取器。只从原文和图片描述中抽取事实,不推测、不补全。",
CUSTOMER_RISK_GUIDANCE,
"把事实分为三类:明确事实进入对应字段;含糊、冲突、缺少来源的事实进入 uncertain_items;营销夸张、无法验证、容易引发幻觉的主张进入 forbidden_claims。",
"特别检查:公司全称、简称、品牌名、产品/服务名、目标行业、目标受众、经验年限、客户案例、资质荣誉、服务能力、地域/出海能力、图片主题。不要把营销夸张词当事实。",
JSON_ONLY_PROMPT,
].join(" ");
export const TARGETED_REWRITER_SYSTEM_PROMPT = [
"你是定向修复器。只修复 failedChecks 指出的失败项,不整篇重写。",
CUSTOMER_RISK_GUIDANCE,
"不得新增事实。无法修复或需要客户确认的内容放入 requires_user_confirmation。",
JSON_ONLY_PROMPT,
].join(" ");
export const RENWEI_COPY_OPTIMIZER_SYSTEM_PROMPT = [
"你是中文普通文案编辑,不是营销代笔工具。",
"目标是让文字更顺,但改完后作者还在。",
"硬性规则:少动,只改真正打绊的地方;保留作者的位置、语气、口头习惯和可识别手迹。",
"不凭空新增时间、地点、数字、案例、情绪、场景、资质、效果承诺或没有来源的事实。",
"不写过度金句,不要把普通句子改成宣传腔、排比、格言、万能展望或意义拔高。",
"拿不准时白描,不拔高;拿不准的改动必须标记为 uncertain 且 revertible 为 true。",
"改后只检查被改动句子是否引入 AI 味,并逐处说明改了什么、为什么改。",
JSON_ONLY_PROMPT,
].join(" ");
function describeRenweiIntensity(
intensity: CopyOptimizationRequest["intensity"],
) {
const descriptions = {
light: "light / 轻微整理:尽量只修顺病句、错别字、明显卡顿。",
medium: "medium / 适度润色:允许调整句序和连接,但不改变作者表达的粗糙感。",
conversational:
"conversational / 更口语自然:让口吻更像真人说话,但不凭空增加表演性口语。",
} satisfies Record<CopyOptimizationRequest["intensity"], string>;
return descriptions[intensity];
}
export function buildFactExtractorPrompt(input: ArticleInput) {
return [
"Return a CandidateFactCard JSON object with these exact keys:",
"company_full_name, company_short_names, brand_names, product_names, target_industry, target_audience, experience_years, core_claims, forbidden_claims, image_topics, uncertain_items.",
"Do not include confirmed_by_user.",
"",
FACT_CARD_OUTPUT_CONTRACT,
"",
"字段要求:",
"- core_claims 只放原文明示、可作为后续优化硬约束的事实。",
"- forbidden_claims 放行业第一、最强、知名客户、显著提升、保证转化等无法验证或高风险主张。",
"- uncertain_items 放客户案例、资质荣誉、经验年限、产品能力、出海能力等证据不足或前后冲突的内容。",
"- image_topics 必须来自图片描述或图片链接文本;没有图片就返回空数组。",
"- target_industry 必须是文章主行业,避免被局部案例带偏。",
"",
"Article input:",
JSON.stringify(input, null, 2),
].join("\n");
}
export function buildArticleOptimizerPrompt(
input: ArticleInput,
factCard: OptimizationFactCard,
) {
return [
"Return an OptimizedArticle JSON object with these exact keys:",
"title, summary, body_markdown, image_suggestions, changed_sections, requires_user_confirmation.",
"",
OPTIMIZED_ARTICLE_OUTPUT_CONTRACT,
"",
formatPlatformGuidance(input.platform),
"",
"输出要求:",
"- 输出中文标题,禁止使用英文模板词如 Guide、Best、Top。",
"- 进行实质性重写:重组标题、摘要、段落顺序、长句、病句和逻辑连接,而不是只在原文前后拼接内容。",
"- body_markdown 使用清晰的 Markdown 层级;标题层级要规范,中文标题后保留空格,例如 `## 标题`。",
"- 如果用户要求包含未确认事实,正文不写入,放入 requires_user_confirmation。",
"- 必须保留事实卡确认的公司全称、目标行业、目标受众和核心事实。",
"- 必须删除或弱化 factCard.forbidden_claims 中的主张。",
"- 不得新增客户案例、数字、资质、排名、奖项、服务能力、效果承诺。",
"- 当前版本只优化文本,不生成图片建议;image_suggestions 必须返回空数组 []。",
"",
"Confirmed fact card:",
JSON.stringify(factCard, null, 2),
"",
"Article input:",
JSON.stringify(input, null, 2),
].join("\n");
}
export function buildQualityInspectorPrompt(input: {
article: OptimizedArticle;
factCard: OptimizationFactCard;
platform: PublishPlatform;
deterministicChecks: QaCheck[];
}) {
return [
"Return a JSON object with a checks array.",
"Each check must include rule_id, status, evidence, reason, suggested_fix, and target_agent.",
"Only use rule_id values already present in deterministicChecks.",
QA_OUTPUT_CONTRACT,
"不得把 deterministic fail 降级。",
"",
formatPlatformGuidance(input.platform),
"",
"fail 标准:行业漂移、公司名不一致、事实卡外新增数字/客户/资质/案例、未确认案例、产品服务前后冲突、平台口吻严重错误、标题明显病句。",
"warn 标准:句子过长、表达可读性一般、平台适配轻微不足。",
"当前版本暂不评估图片内容;image_text_match 只能基于 deterministicChecks 原状态保留或给出暂不评估说明,不得要求生成图片建议。",
"target_agent 只能使用 title、body、fact_card 或 null。",
"",
"Confirmed fact card:",
JSON.stringify(input.factCard, null, 2),
"",
"Optimized article:",
JSON.stringify(input.article, null, 2),
"",
"Deterministic checks:",
JSON.stringify(input.deterministicChecks, null, 2),
].join("\n");
}
export function buildTargetedRewritePrompt(input: {
article: OptimizedArticle;
factCard: OptimizationFactCard;
failedChecks: QaCheck[];
}) {
return [
"Return an OptimizedArticle JSON object.",
OPTIMIZED_ARTICLE_OUTPUT_CONTRACT,
"Rewrite only the fields needed for failedChecks.",
"",
"按 rule_id 执行修复动作:",
"- industry_alignment:删除偏离行业的段落,改回 factCard.target_industry。",
"- company_name_integrity:统一公司全称和简称。",
"- hallucination_risk:删除所有未确认数字、客户案例、资质、奖项、排名、效果承诺。",
"- claim_consistency:统一年限、产品名、服务能力和核心主张。",
"- title_quality:生成自然中文标题,禁止英文模板词。",
"- body_quality:拆分长句,修复病句和断裂表达。",
"- voice_consistency / platform_fit:改成目标平台对应口吻。",
"- image_text_match:当前版本暂不处理图片,保持原文文本不变,可把需要人工补图的事项放入 requires_user_confirmation。",
"不得新增事实。无法修复的内容放入 requires_user_confirmation。",
"",
"Confirmed fact card:",
JSON.stringify(input.factCard, null, 2),
"",
"Current optimized article:",
JSON.stringify(input.article, null, 2),
"",
"Failed checks:",
JSON.stringify(input.failedChecks, null, 2),
].join("\n");
}
export function buildRenweiCopyOptimizationPrompt(
input: CopyOptimizationRequest,
) {
return [
"Return a CopyOptimizationResult JSON object with these exact keys:",
"optimized_text, change_notes, ai_taste_checks, warnings.",
"",
RENWEI_OUTPUT_CONTRACT,
"",
"强度选项:",
"- light:轻微整理。",
"- medium:适度润色。",
"- conversational:更口语自然。",
`当前修改强度:${describeRenweiIntensity(input.intensity)}`,
"",
"改稿要求:",
"- 默认少动,不追求更漂亮,只清掉绊脚处。",
"- 毛边先假设是作者手迹,不要机械删口语、停顿或重复。",
"- 不写“不是X,而是Y”、排比三连、万能展望、宣传腔或假深刻句式。",
"- 不使用破折号作为装饰性解释。",
"- 必须逐处说明改动;未改动的句子不要编造改动说明。",
"- AI 味检查只检查被改动句子。",
"",
"User request:",
JSON.stringify(
{
goal: input.goal,
intensity: input.intensity,
user_instructions: input.user_instructions,
},
null,
2,
),
"",
"source_text:",
input.source_text,
].join("\n");
}