fix: 兼容QA检查LLM字段
This commit is contained in:
@@ -182,6 +182,40 @@ describe("domain validation", () => {
|
|||||||
).toThrow();
|
).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", () => {
|
it("normalizes object-shaped changed sections from LLM output", () => {
|
||||||
const parsed = optimizedArticleSchema.parse({
|
const parsed = optimizedArticleSchema.parse({
|
||||||
title: "Optimized article",
|
title: "Optimized article",
|
||||||
|
|||||||
+107
-11
@@ -20,13 +20,77 @@ export const publishPlatformSchema = z.enum([
|
|||||||
"recommendation_list",
|
"recommendation_list",
|
||||||
]) satisfies z.ZodType<PublishPlatform>;
|
]) satisfies z.ZodType<PublishPlatform>;
|
||||||
|
|
||||||
export const checkStatusSchema = z.enum([
|
const checkStatusAliases: Record<string, CheckStatus> = {
|
||||||
"pass",
|
pass: "pass",
|
||||||
"warn",
|
passed: "pass",
|
||||||
"fail",
|
ok: "pass",
|
||||||
]) satisfies z.ZodType<CheckStatus>;
|
"通过": "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",
|
"industry_alignment",
|
||||||
"image_text_match",
|
"image_text_match",
|
||||||
"voice_consistency",
|
"voice_consistency",
|
||||||
@@ -37,7 +101,8 @@ export const qualityRuleIdSchema = z.enum([
|
|||||||
"hallucination_risk",
|
"hallucination_risk",
|
||||||
"claim_consistency",
|
"claim_consistency",
|
||||||
"context_sensitive_terms",
|
"context_sensitive_terms",
|
||||||
]) satisfies z.ZodType<QualityRuleId>;
|
]),
|
||||||
|
) satisfies z.ZodType<QualityRuleId>;
|
||||||
|
|
||||||
export const imageInputSchema = z.object({
|
export const imageInputSchema = z.object({
|
||||||
type: z.enum(["description", "link"]),
|
type: z.enum(["description", "link"]),
|
||||||
@@ -308,13 +373,44 @@ export const optimizedArticleSchema = z.object({
|
|||||||
requires_user_confirmation: stringListSchema,
|
requires_user_confirmation: stringListSchema,
|
||||||
}) satisfies z.ZodType<OptimizedArticle>;
|
}) 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({
|
export const qaCheckSchema = z.object({
|
||||||
rule_id: qualityRuleIdSchema,
|
rule_id: qualityRuleIdSchema,
|
||||||
status: checkStatusSchema,
|
status: checkStatusSchema,
|
||||||
evidence: z.string().trim().min(1),
|
evidence: requiredLlmStringSchema,
|
||||||
reason: z.string().trim().min(1),
|
reason: requiredLlmStringSchema,
|
||||||
suggested_fix: z.string().trim().default(""),
|
suggested_fix: optionalLlmStringSchema,
|
||||||
target_agent: z.string().trim().min(1).nullable().default(null),
|
target_agent: targetAgentSchema,
|
||||||
}) satisfies z.ZodType<QaCheck>;
|
}) satisfies z.ZodType<QaCheck>;
|
||||||
|
|
||||||
export const qaReportSchema = z.object({
|
export const qaReportSchema = z.object({
|
||||||
|
|||||||
Reference in New Issue
Block a user