fix: 兼容QA检查LLM字段

This commit is contained in:
Codex
2026-06-24 10:12:11 +08:00
parent 3709c64a17
commit 1802f3dd9d
2 changed files with 151 additions and 21 deletions
@@ -182,6 +182,40 @@ describe("domain validation", () => {
).toThrow();
});
it("normalizes near-valid LLM QA report values", () => {
const parsed = qaReportSchema.parse({
overall_status: "警告",
checks: [
{
rule_id: "标题质量",
status: "警告",
evidence: { detail: "标题仍然偏营销化" },
reason: { reason: "官网标题需要更克制" },
suggested_fix: null,
target_agent: "",
},
{
rule_id: "company_name_integrity",
status: "通过",
evidence: "公司全称一致",
reason: "正文保留了事实卡中的公司全称",
target_agent: "无",
},
],
});
expect(parsed.overall_status).toBe("warn");
expect(parsed.checks[0]).toEqual({
rule_id: "title_quality",
status: "warn",
evidence: "标题仍然偏营销化",
reason: "官网标题需要更克制",
suggested_fix: "",
target_agent: null,
});
expect(parsed.checks[1]?.target_agent).toBeNull();
});
it("normalizes object-shaped changed sections from LLM output", () => {
const parsed = optimizedArticleSchema.parse({
title: "Optimized article",
+107 -11
View File
@@ -20,13 +20,77 @@ export const publishPlatformSchema = z.enum([
"recommendation_list",
]) satisfies z.ZodType<PublishPlatform>;
export const checkStatusSchema = z.enum([
"pass",
"warn",
"fail",
]) satisfies z.ZodType<CheckStatus>;
const checkStatusAliases: Record<string, CheckStatus> = {
pass: "pass",
passed: "pass",
ok: "pass",
"通过": "pass",
"合格": "pass",
warn: "warn",
warning: "warn",
"警告": "warn",
"提醒": "warn",
fail: "fail",
failed: "fail",
failure: "fail",
"失败": "fail",
"不通过": "fail",
};
export const qualityRuleIdSchema = z.enum([
function normalizeCheckStatus(value: unknown): unknown {
const normalized = normalizedStringOrNull(value);
if (!normalized) return value;
return (
checkStatusAliases[normalized.toLowerCase()] ??
checkStatusAliases[normalized] ??
normalized
);
}
export const checkStatusSchema = z.preprocess(
normalizeCheckStatus,
z.enum(["pass", "warn", "fail"]),
) satisfies z.ZodType<CheckStatus>;
const qualityRuleIdAliases: Record<string, QualityRuleId> = {
industry_alignment: "industry_alignment",
"行业对齐": "industry_alignment",
"行业一致性": "industry_alignment",
image_text_match: "image_text_match",
"图文匹配": "image_text_match",
"图片文本匹配": "image_text_match",
voice_consistency: "voice_consistency",
"语气一致性": "voice_consistency",
"口吻一致性": "voice_consistency",
platform_fit: "platform_fit",
"平台适配": "platform_fit",
company_name_integrity: "company_name_integrity",
"公司名一致性": "company_name_integrity",
"公司名称一致性": "company_name_integrity",
title_quality: "title_quality",
"标题质量": "title_quality",
body_quality: "body_quality",
"正文质量": "body_quality",
hallucination_risk: "hallucination_risk",
"幻觉风险": "hallucination_risk",
"虚构风险": "hallucination_risk",
claim_consistency: "claim_consistency",
"事实一致性": "claim_consistency",
"主张一致性": "claim_consistency",
context_sensitive_terms: "context_sensitive_terms",
"语境敏感词": "context_sensitive_terms",
"敏感词": "context_sensitive_terms",
};
function normalizeQualityRuleId(value: unknown): unknown {
const normalized = normalizedStringOrNull(value);
if (!normalized) return value;
return qualityRuleIdAliases[normalized] ?? normalized;
}
export const qualityRuleIdSchema = z.preprocess(
normalizeQualityRuleId,
z.enum([
"industry_alignment",
"image_text_match",
"voice_consistency",
@@ -37,7 +101,8 @@ export const qualityRuleIdSchema = z.enum([
"hallucination_risk",
"claim_consistency",
"context_sensitive_terms",
]) satisfies z.ZodType<QualityRuleId>;
]),
) satisfies z.ZodType<QualityRuleId>;
export const imageInputSchema = z.object({
type: z.enum(["description", "link"]),
@@ -308,13 +373,44 @@ export const optimizedArticleSchema = z.object({
requires_user_confirmation: stringListSchema,
}) satisfies z.ZodType<OptimizedArticle>;
function normalizeTargetAgent(value: unknown): unknown {
const normalized = normalizedStringOrNull(value);
if (!normalized) return null;
const aliases: Record<string, string | null> = {
none: null,
null: null,
"无": null,
"无需": null,
title: "title",
"标题": "title",
body: "body",
"正文": "body",
fact_card: "fact_card",
factcard: "fact_card",
"事实卡": "fact_card",
};
const normalizedKey = normalized.toLowerCase();
if (Object.prototype.hasOwnProperty.call(aliases, normalizedKey)) {
return aliases[normalizedKey];
}
if (Object.prototype.hasOwnProperty.call(aliases, normalized)) {
return aliases[normalized];
}
return normalized;
}
const targetAgentSchema = z.preprocess(
normalizeTargetAgent,
z.string().trim().min(1).nullable().default(null),
);
export const qaCheckSchema = z.object({
rule_id: qualityRuleIdSchema,
status: checkStatusSchema,
evidence: z.string().trim().min(1),
reason: z.string().trim().min(1),
suggested_fix: z.string().trim().default(""),
target_agent: z.string().trim().min(1).nullable().default(null),
evidence: requiredLlmStringSchema,
reason: requiredLlmStringSchema,
suggested_fix: optionalLlmStringSchema,
target_agent: targetAgentSchema,
}) satisfies z.ZodType<QaCheck>;
export const qaReportSchema = z.object({