feat: localize quality report

This commit is contained in:
Codex
2026-06-21 23:43:02 +08:00
parent a457a4acf3
commit 936084465d
3 changed files with 78 additions and 35 deletions
+24 -2
View File
@@ -12,6 +12,24 @@ const statusLabels = {
fail: "失败", fail: "失败",
} as const; } as const;
const ruleLabels: Record<string, string> = {
industry_alignment: "行业一致性",
image_text_match: "图文匹配",
voice_consistency: "表达视角一致性",
platform_fit: "平台适配",
company_name_integrity: "公司名称完整性",
title_quality: "标题质量",
body_quality: "正文质量",
hallucination_risk: "事实幻觉风险",
claim_consistency: "主张一致性",
context_sensitive_terms: "语境敏感词",
};
const targetAgentLabels: Record<string, string> = {
title: "标题改写",
body: "正文改写",
};
export function QaReportPanel({ report }: QaReportPanelProps) { export function QaReportPanel({ report }: QaReportPanelProps) {
if (!report) { if (!report) {
return ( return (
@@ -33,7 +51,7 @@ export function QaReportPanel({ report }: QaReportPanelProps) {
{report.checks.map((check) => ( {report.checks.map((check) => (
<div className="qa-item" key={check.rule_id}> <div className="qa-item" key={check.rule_id}>
<div className="qa-title-row"> <div className="qa-title-row">
<strong>{check.rule_id.replace(/_/g, " ")}</strong> <strong>{ruleLabels[check.rule_id] ?? check.rule_id}</strong>
<span className={`status-pill ${check.status}`}> <span className={`status-pill ${check.status}`}>
{statusLabels[check.status]} {statusLabels[check.status]}
</span> </span>
@@ -41,7 +59,11 @@ export function QaReportPanel({ report }: QaReportPanelProps) {
<p>{check.reason}</p> <p>{check.reason}</p>
<small>{check.evidence}</small> <small>{check.evidence}</small>
{check.suggested_fix && <em>{check.suggested_fix}</em>} {check.suggested_fix && <em>{check.suggested_fix}</em>}
{check.target_agent && <code>{check.target_agent}</code>} {check.target_agent && (
<code>
{targetAgentLabels[check.target_agent] ?? check.target_agent}
</code>
)}
</div> </div>
))} ))}
</div> </div>
@@ -133,6 +133,27 @@ describe("workflow nodes", () => {
]); ]);
}); });
it("returns quality report details in Chinese", () => {
const report = inspectQuality({
article: {
title: "Example GEO Optimization Guide",
summary: "A factual guide for marketing teams.",
body_markdown:
"Example Technology Co., Ltd. has eight years of GEO optimization experience.",
image_suggestions: [],
changed_sections: [],
requires_user_confirmation: [],
},
factCard: confirmedFactCard,
platform: "official_site",
sourceImages: [],
});
expect(report.checks[0]?.reason).toContain("行业");
expect(report.checks[0]?.suggested_fix).toContain("行业");
expect(report.checks[0]?.evidence).not.toContain("Article");
});
it("hard-fails incomplete company names, hallucinated numbers, industry drift, and conflicting years", () => { it("hard-fails incomplete company names, hallucinated numbers, industry drift, and conflicting years", () => {
const report = inspectQuality({ const report = inspectQuality({
article: { article: {
+33 -33
View File
@@ -55,9 +55,9 @@ function inspectRule(
aligned ? "pass" : "fail", aligned ? "pass" : "fail",
aligned ? factCard.target_industry : article.summary, aligned ? factCard.target_industry : article.summary,
aligned aligned
? "Article stays aligned with the confirmed industry." ? "文章内容与事实卡确认的目标行业一致。"
: "Article drifts from the confirmed industry.", : "文章内容偏离事实卡确认的目标行业。",
"Rewrite affected paragraphs around the confirmed industry.", "围绕事实卡确认的目标行业重写相关段落。",
aligned ? null : "body", aligned ? null : "body",
); );
} }
@@ -67,11 +67,11 @@ function inspectRule(
return check( return check(
ruleId, ruleId,
hasImages ? "pass" : "warn", hasImages ? "pass" : "warn",
hasImages ? "Image topics are available." : "No image descriptions supplied.", hasImages ? "已有可用于比对的图片主题。" : "未提供图片描述。",
hasImages hasImages
? "Image guidance can be compared with article sections." ? "图片建议可以和文章内容进行比对。"
: "Image-text confidence is low without image descriptions.", : "缺少图片描述时,图文匹配置信度较低。",
"Add image descriptions or review image placement manually.", "补充图片描述,或人工检查图片与正文的对应关系。",
null, null,
); );
} }
@@ -81,11 +81,11 @@ function inspectRule(
return check( return check(
ruleId, ruleId,
thirdPartyOfficial ? "warn" : "pass", thirdPartyOfficial ? "warn" : "pass",
thirdPartyOfficial ? "Third-party pronouns found." : "Voice matches platform.", thirdPartyOfficial ? "发现第三方代词。" : "表达视角符合平台要求。",
thirdPartyOfficial thirdPartyOfficial
? "Official-site content should avoid detached third-party voice." ? "官网文章应避免疏离的第三方叙述口吻。"
: "No obvious voice mismatch detected.", : "未发现明显的平台口吻不一致问题。",
"Rewrite in official brand voice.", "改写为更适合官网的品牌表达。",
thirdPartyOfficial ? "body" : null, thirdPartyOfficial ? "body" : null,
); );
} }
@@ -97,8 +97,8 @@ function inspectRule(
? "pass" ? "pass"
: "warn", : "warn",
article.summary, article.summary,
"Platform fit is based on the generated summary and structure.", "平台适配基于生成摘要与文章结构判断。",
"Review platform-specific framing.", "复核文章是否符合目标平台的表达方式和结构。",
null, null,
); );
} }
@@ -110,9 +110,9 @@ function inspectRule(
hasFullName ? "pass" : "fail", hasFullName ? "pass" : "fail",
hasFullName ? factCard.company_full_name : article.body_markdown, hasFullName ? factCard.company_full_name : article.body_markdown,
hasFullName hasFullName
? "Confirmed company full name is present." ? "文章中包含事实卡确认的公司全称。"
: "The confirmed company full name is missing or shortened.", : "文章缺少事实卡确认的公司全称,或使用了不完整简称。",
"Use the confirmed company full name at first mention.", "首次出现公司时使用事实卡确认的公司全称。",
hasFullName ? null : "body", hasFullName ? null : "body",
); );
} }
@@ -123,8 +123,8 @@ function inspectRule(
ruleId, ruleId,
badTitle ? "fail" : "pass", badTitle ? "fail" : "pass",
article.title, article.title,
badTitle ? "Title appears awkward or over-punctuated." : "Title reads naturally.", badTitle ? "标题存在语义不顺或标点堆砌问题。" : "标题表达自然清晰。",
"Rewrite title for clarity and grammar.", "重写标题,提升语义清晰度和语法质量。",
badTitle ? "title" : null, badTitle ? "title" : null,
); );
} }
@@ -134,11 +134,11 @@ function inspectRule(
return check( return check(
ruleId, ruleId,
longSentence ? "warn" : "pass", longSentence ? "warn" : "pass",
longSentence ? "Long sentence detected." : "Body structure is readable.", longSentence ? "发现过长句子。" : "正文结构可读。",
longSentence longSentence
? "Some sentences are too long for comfortable reading." ? "部分句子过长,影响阅读和理解。"
: "No severe body grammar issue detected.", : "未发现严重正文语法问题。",
"Split long sentences and clarify references.", "拆分长句,并明确指代关系。",
longSentence ? "body" : null, longSentence ? "body" : null,
); );
} }
@@ -150,11 +150,11 @@ function inspectRule(
unsupportedNumber || article.requires_user_confirmation.length > 0 ? "fail" : "pass", unsupportedNumber || article.requires_user_confirmation.length > 0 ? "fail" : "pass",
unsupportedNumber unsupportedNumber
? findUnsupportedNumbers(combined, factCard).join(", ") ? findUnsupportedNumbers(combined, factCard).join(", ")
: "No unsupported numeric claims found.", : "未发现未确认的数字类事实主张。",
unsupportedNumber unsupportedNumber
? "Numeric claims are not traceable to the confirmed fact card." ? "数字类主张无法追溯到已确认事实卡。"
: "Factual claims are traceable to the confirmed fact card.", : "事实主张可以追溯到已确认事实卡。",
"Remove or confirm unsupported claims.", "删除未确认主张,或先补充到事实卡并确认。",
unsupportedNumber ? "body" : null, unsupportedNumber ? "body" : null,
); );
} }
@@ -167,11 +167,11 @@ function inspectRule(
return check( return check(
ruleId, ruleId,
conflicts.length > 0 ? "fail" : "pass", conflicts.length > 0 ? "fail" : "pass",
conflicts.length > 0 ? conflicts.join(", ") : "No conflicting claims found.", conflicts.length > 0 ? conflicts.join(", ") : "未发现冲突事实。",
conflicts.length > 0 conflicts.length > 0
? "Experience years conflict with the confirmed fact card." ? "经验年限与事实卡确认内容冲突。"
: "Repeated claims are consistent.", : "重复出现的事实主张保持一致。",
"Normalize years, products, and service claims to confirmed facts.", "将年限、产品和服务表述统一为事实卡确认内容。",
conflicts.length > 0 ? "body" : null, conflicts.length > 0 ? "body" : null,
); );
} }
@@ -179,9 +179,9 @@ function inspectRule(
return check( return check(
ruleId, ruleId,
/\b(?:sensitive|forbidden)\b/i.test(combined) ? "warn" : "pass", /\b(?:sensitive|forbidden)\b/i.test(combined) ? "warn" : "pass",
"Context-sensitive wording scan complete.", "已完成语境敏感词检查。",
"Sensitive terms need context-aware review when present.", "涉及敏感或禁用表达时,需要结合上下文判断。",
"Review wording manually instead of deleting terms mechanically.", "人工复核相关表述,避免机械删除导致语义错误。",
null, null,
); );
} }