新增普通文案优化数据校验

This commit is contained in:
czj
2026-07-02 14:32:20 +08:00
parent 31d53d3165
commit c2010fa8f4
3 changed files with 150 additions and 0 deletions
@@ -4,6 +4,8 @@ import {
articleInputSchema,
confirmedFactCardSchema,
candidateFactCardSchema,
copyOptimizationRequestSchema,
copyOptimizationResultSchema,
optimizationFactCardSchema,
optimizedArticleSchema,
qaReportSchema,
@@ -51,6 +53,59 @@ describe("domain validation", () => {
).toThrow();
});
it("accepts a trimmed copy optimization request", () => {
const parsed = copyOptimizationRequestSchema.parse({
source_text: " 我写了一段有点卡的文案 ",
goal: "",
intensity: "light",
user_instructions: " 保留口语感 ",
});
expect(parsed).toEqual({
source_text: "我写了一段有点卡的文案",
goal: "保留原意,减少 AI 味",
intensity: "light",
user_instructions: "保留口语感",
});
});
it("rejects empty copy optimization source text", () => {
expect(() =>
copyOptimizationRequestSchema.parse({
source_text: " ",
intensity: "light",
}),
).toThrow();
});
it("accepts structured copy optimization results", () => {
const parsed = copyOptimizationResultSchema.parse({
optimized_text: "我把句子顺了一下。",
change_notes: [
{
original: "我把句子顺顺。",
revised: "我把句子顺了一下。",
reason: "修正口语里不顺的重复。",
confidence: "confident",
revertible: false,
},
],
ai_taste_checks: [
{
rule_id: "promotion_tone",
status: "pass",
evidence: "没有新增宣传词。",
suggestion: "",
},
],
warnings: [],
});
expect(parsed.optimized_text).toBe("我把句子顺了一下。");
expect(parsed.change_notes[0]?.confidence).toBe("confident");
expect(parsed.ai_taste_checks[0]?.rule_id).toBe("promotion_tone");
});
it("accepts an unconfirmed optimization fact card with unresolved items", () => {
const parsed = optimizationFactCardSchema.parse({
company_full_name: "",
+43
View File
@@ -86,3 +86,46 @@ export interface QaReport {
overall_status: CheckStatus;
checks: QaCheck[];
}
export type CopyOptimizationIntensity = "light" | "medium" | "conversational";
export type CopyChangeConfidence = "confident" | "uncertain";
export type CopyAiTasteRuleId =
| "meaning_inflation"
| "promotion_tone"
| "formulaic_sentence"
| "format_trace"
| "chat_trace"
| "filler_hedging";
export type CopyAiTasteStatus = "pass" | "warn";
export interface CopyOptimizationRequest {
source_text: string;
goal: string;
intensity: CopyOptimizationIntensity;
user_instructions: string;
}
export interface CopyChangeNote {
original: string;
revised: string;
reason: string;
confidence: CopyChangeConfidence;
revertible: boolean;
}
export interface CopyAiTasteCheck {
rule_id: CopyAiTasteRuleId;
status: CopyAiTasteStatus;
evidence: string;
suggestion: string;
}
export interface CopyOptimizationResult {
optimized_text: string;
change_notes: CopyChangeNote[];
ai_taste_checks: CopyAiTasteCheck[];
warnings: string[];
}
+52
View File
@@ -5,6 +5,10 @@ import type {
CandidateFactCard,
CheckStatus,
ConfirmedFactCard,
CopyAiTasteCheck,
CopyChangeNote,
CopyOptimizationRequest,
CopyOptimizationResult,
ImageInput,
OptimizationFactCard,
OptimizedArticle,
@@ -431,3 +435,51 @@ export const qaReportSchema = z.object({
overall_status: checkStatusSchema,
checks: z.array(qaCheckSchema).default([]),
}) satisfies z.ZodType<QaReport>;
export const copyOptimizationIntensitySchema = z.enum([
"light",
"medium",
"conversational",
]);
export const copyOptimizationRequestSchema = z.object({
source_text: z.string().trim().min(1),
goal: z
.preprocess((value) => {
if (typeof value !== "string") return value;
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : "保留原意,减少 AI 味";
}, z.string().trim().min(1))
.default("保留原意,减少 AI 味"),
intensity: copyOptimizationIntensitySchema.default("light"),
user_instructions: z.string().trim().default(""),
}) satisfies z.ZodType<CopyOptimizationRequest>;
const copyChangeNoteSchema = z.object({
original: requiredLlmStringSchema,
revised: requiredLlmStringSchema,
reason: requiredLlmStringSchema,
confidence: z.enum(["confident", "uncertain"]),
revertible: z.boolean(),
}) satisfies z.ZodType<CopyChangeNote>;
const copyAiTasteCheckSchema = z.object({
rule_id: z.enum([
"meaning_inflation",
"promotion_tone",
"formulaic_sentence",
"format_trace",
"chat_trace",
"filler_hedging",
]),
status: z.enum(["pass", "warn"]),
evidence: requiredLlmStringSchema,
suggestion: optionalLlmStringSchema,
}) satisfies z.ZodType<CopyAiTasteCheck>;
export const copyOptimizationResultSchema = z.object({
optimized_text: requiredLlmStringSchema,
change_notes: z.array(copyChangeNoteSchema).default([]),
ai_taste_checks: z.array(copyAiTasteCheckSchema).default([]),
warnings: stringListSchema,
}) satisfies z.ZodType<CopyOptimizationResult>;