新增人味儿文案优化提示词

This commit is contained in:
czj
2026-07-02 14:34:29 +08:00
parent c2010fa8f4
commit 27d7a44ad0
5 changed files with 202 additions and 1 deletions
+37
View File
@@ -4,10 +4,12 @@ import {
ARTICLE_OPTIMIZER_SYSTEM_PROMPT,
FACT_EXTRACTOR_SYSTEM_PROMPT,
QUALITY_INSPECTOR_SYSTEM_PROMPT,
RENWEI_COPY_OPTIMIZER_SYSTEM_PROMPT,
TARGETED_REWRITER_SYSTEM_PROMPT,
buildArticleOptimizerPrompt,
buildFactExtractorPrompt,
buildQualityInspectorPrompt,
buildRenweiCopyOptimizationPrompt,
buildTargetedRewritePrompt,
} from "../prompts";
@@ -168,4 +170,39 @@ describe("LLM prompt builders", () => {
expect(prompt).toContain("body_quality");
expect(prompt).toContain("不得新增事实");
});
it("renwei copy prompt keeps the author present and defaults to small edits", () => {
const prompt = `${RENWEI_COPY_OPTIMIZER_SYSTEM_PROMPT}\n${buildRenweiCopyOptimizationPrompt({
source_text: "我观察到大家越来越难进入心流了。",
goal: "保留原意,减少 AI 味",
intensity: "light",
user_instructions: "保留作者的口语感。",
})}`;
expect(prompt).toContain("少动");
expect(prompt).toContain("保留作者");
expect(prompt).toContain("手迹");
expect(prompt).toContain("不凭空新增时间、地点、数字、案例");
expect(prompt).toContain("不写过度金句");
expect(prompt).toContain("逐处说明");
expect(prompt).toContain("只检查被改动句子");
expect(prompt).toContain("source_text");
});
it("renwei copy prompt describes all result fields and intensity modes", () => {
const prompt = buildRenweiCopyOptimizationPrompt({
source_text: "这是一段文案。",
goal: "更自然",
intensity: "conversational",
user_instructions: "",
});
expect(prompt).toContain("optimized_text");
expect(prompt).toContain("change_notes");
expect(prompt).toContain("ai_taste_checks");
expect(prompt).toContain("warnings");
expect(prompt).toContain("light");
expect(prompt).toContain("medium");
expect(prompt).toContain("conversational");
});
});
+2 -1
View File
@@ -6,7 +6,8 @@ export type LlmTaskName =
| "fact_extractor"
| "article_optimizer"
| "quality_inspector"
| "targeted_rewriter";
| "targeted_rewriter"
| "renwei_copy_optimizer";
export interface GenerateInput {
system?: string;
+101
View File
@@ -1,5 +1,6 @@
import type {
ArticleInput,
CopyOptimizationRequest,
OptimizationFactCard,
OptimizedArticle,
PublishPlatform,
@@ -97,6 +98,44 @@ const QA_OUTPUT_CONTRACT = [
),
].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 [
"平台模板选项:",
@@ -135,6 +174,29 @@ export const TARGETED_REWRITER_SYSTEM_PROMPT = [
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:",
@@ -247,3 +309,42 @@ export function buildTargetedRewritePrompt(input: {
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");
}
@@ -17,6 +17,7 @@ vi.mock("../../llm/client", async () => {
import { extractCandidateFactCard } from "../fact-extractor";
import { optimizeArticle } from "../article-optimizer";
import { inspectQualityWithLlm } from "../quality-inspector";
import { optimizeRenweiCopy } from "../renwei-copy-optimizer";
import { rewriteFailedSections } from "../targeted-rewriter";
describe("LLM workflow integration", () => {
@@ -356,4 +357,43 @@ describe("LLM workflow integration", () => {
expect(hallucinationCheck?.status).toBe("warn");
expect(report.overall_status).toBe("warn");
});
it("uses validated LLM output for renwei copy optimization", async () => {
llmMocks.generateValidatedJson.mockResolvedValueOnce({
optimized_text: "我观察到大家越来越难进入心流了。",
change_notes: [
{
original: "我观察到大家越来越难进入心流",
revised: "我观察到大家越来越难进入心流了。",
reason: "补足句尾语气,让句子自然收住。",
confidence: "confident",
revertible: false,
},
],
ai_taste_checks: [
{
rule_id: "promotion_tone",
status: "pass",
evidence: "没有新增宣传腔。",
suggestion: "",
},
],
warnings: [],
});
const result = await optimizeRenweiCopy({
source_text: "我观察到大家越来越难进入心流",
goal: "保留原意,减少 AI 味",
intensity: "light",
user_instructions: "",
});
expect(result.optimized_text).toBe("我观察到大家越来越难进入心流了。");
expect(llmMocks.generateValidatedJson).toHaveBeenCalledWith(
expect.objectContaining({
task: "renwei_copy_optimizer",
temperature: 0.2,
}),
);
});
});
+22
View File
@@ -0,0 +1,22 @@
import type {
CopyOptimizationRequest,
CopyOptimizationResult,
} from "../domain/types";
import { copyOptimizationResultSchema } from "../domain/validation";
import { generateValidatedJson } from "../llm/client";
import {
RENWEI_COPY_OPTIMIZER_SYSTEM_PROMPT,
buildRenweiCopyOptimizationPrompt,
} from "../llm/prompts";
export async function optimizeRenweiCopy(
input: CopyOptimizationRequest,
): Promise<CopyOptimizationResult> {
return generateValidatedJson({
schema: copyOptimizationResultSchema,
system: RENWEI_COPY_OPTIMIZER_SYSTEM_PROMPT,
prompt: buildRenweiCopyOptimizationPrompt(input),
temperature: 0.2,
task: "renwei_copy_optimizer",
});
}