文章

LLM 输出内容安全生产实战:用分级审核与灰度阈值降低误拦截

本文系统讲解大模型内容安全在生产环境中的分级审核、阈值灰度、人工复核和误拦截治理,帮助团队在降低风险的同时保留正常用户体验。

LLM 输出内容安全生产实战:用分级审核与灰度阈值降低误拦截

背景问题

很多团队接入大模型后,会先关注效果、成本和延迟,最后才补内容安全。这种顺序容易导致两个问题:第一,模型上线后才发现某些回答不适合直接展示;第二,安全策略一旦收紧,又会把正常用户请求误拦截,造成体验下降。

内容安全不是简单地在接口前加一个”是否违规”的布尔判断。更接近真实生产问题的是:哪些内容必须阻断,哪些内容可以降级回答,哪些内容要转人工复核,哪些内容只需要审计留痕;同时,系统还要区分输入风险、输出风险、用户年龄、业务类型、地区合规和历史行为。

OpenAI 的 Moderation 文档说明,moderation 模型可以检测文本和图片中的有害内容,并将结果用于过滤内容、路由到人工复核或对提交被标记内容的账户进行干预。Azure AI Content Safety 也强调,它面向应用中的用户生成内容和 AI 生成内容,提供文本和图像检测能力。AWS Bedrock Guardrails 和 Google Model Armor 则进一步把内容过滤、敏感信息保护、输入/输出拦截、审计和策略执行放到了生成式 AI 应用保护链路中。

本文不讨论”如何写一个完美的安全提示词”,而是讨论生产系统里更可落地的内容安全工程:分级审核、阈值灰度、误拦截治理、人工复核和上线检查

核心原理

1. 从二分类改成风险分级

最常见的错误是把内容安全结果压缩成一个字段:blocked: true | false。这样做实现简单,但不利于治理。

生产系统更适合保留三类信息:

  • category:命中的风险类别,例如骚扰、仇恨、成人、暴力、自伤、违法建议、敏感信息等。
  • severity:风险等级,例如 lowmediumhighcritical
  • confidence 或 score:分类器对该判断的置信度。

这样做的价值是把”检测”和”执行”拆开。检测模型只负责判断可能风险,业务策略负责决定动作。低风险内容可以提示用户调整表达;中风险内容可以改写或局部遮罩;高风险内容可以拒绝;边界样本可以进入人工复核。

风险等级置信度区间建议动作
low< 0.40仅记录日志,正常返回
medium0.40 - 0.70安全改写或局部遮罩
high0.70 - 0.90转人工复核队列
critical> 0.90直接阻断

2. 输入和输出都要检查

只检查用户输入不够,因为模型输出也可能引入新的风险。比如用户提出一个边界请求,输入本身不明显违规,但模型补全时扩展出了不适合展示的细节。Google Model Armor 的架构说明中也明确包含两个检查点:先检查 prompt,再检查模型生成的 response;如果需要,可以对 prompt 或 response 进行清洗后再继续。

建议将链路拆成四个检查位置:

  1. pre-input check:用户输入进入模型前。
  2. context check:系统拼接历史上下文、工具结果、检索内容后。
  3. post-output check:模型回答返回用户前。
  4. feedback check:用户举报、人工复核和申诉后。

其中第三个位置最容易被忽略,但对生产系统最关键。

3. 阈值应该按场景灰度,而不是一次性全局启用

内容安全分类器会产生误判。OpenAI 文档也提醒,moderation endpoint 的底层模型会持续升级,因此依赖 category_scores 的自定义策略可能需要随时间重新校准。这意味着阈值不能写死在代码里,也不应该所有业务共用一个全局阈值。

更稳妥的方式是建立策略矩阵:

policy_version: content-safety-2026-07-03
rules:
  - scene: public_chat
    category: violence
    threshold:
      block: 0.86
      review: 0.62
      log_only: 0.40

  - scene: customer_support
    category: self_harm
    threshold:
      block: 0.92
      review: 0.55
      safe_completion: 0.30

  - scene: internal_knowledge_base
    category: pii
    threshold:
      redact: 0.50
      review: 0.75

同一个风险类别,在不同业务场景中可以有不同执行动作。例如公开社区内容应更保守,内部知识库问答可以优先脱敏和审计,而客服场景可能需要把部分高风险对话转人工。

工程落地

1. 推荐架构

一个可落地的内容安全链路通常包含以下模块:

  • Policy Service:保存策略版本、阈值、场景、动作和灰度比例。
  • Moderation Adapter:对接 OpenAI Moderation、Azure AI Content Safety、Bedrock Guardrails、Model Armor 或自研分类器。
  • Decision Engine:把分类结果映射成 allowblockredactrewritereviewlog_only 等动作。
  • Review Queue:承接边界样本、用户申诉和人工复核结果。
  • Audit Store:记录策略版本、模型版本、分类分数、执行动作和最终结果。
  • Metrics Dashboard:观察拦截率、误拦截率、复核通过率、用户申诉率和各类别趋势。

关键点是:不要让业务服务直接写死分类器阈值。业务服务只传入场景、用户类型、输入、输出和上下文摘要,由策略服务返回最终动作。

2. 决策引擎示例

下面是一个简化的 TypeScript 伪代码,用于说明检测结果如何转成执行动作。

type ModerationResult = {
  category: string;
  score: number;
  severity?: "low" | "medium" | "high" | "critical";
};

type SafetyAction = "allow" | "log_only" | "redact" | "review" | "block";

type PolicyRule = {
  scene: string;
  category: string;
  logOnlyScore: number;
  reviewScore: number;
  blockScore: number;
  redactScore?: number;
};

function decideAction(
  scene: string,
  results: ModerationResult[],
  rules: PolicyRule[]
): SafetyAction {
  let finalAction: SafetyAction = "allow";
  for (const result of results) {
    const rule = rules.find(
      r => r.scene === scene && r.category === result.category
    );
    if (!rule) continue;
    if (result.score >= rule.blockScore) return "block";
    if (result.score >= rule.reviewScore)
      finalAction = maxAction(finalAction, "review");
    else if (rule.redactScore && result.score >= rule.redactScore)
      finalAction = maxAction(finalAction, "redact");
    else if (result.score >= rule.logOnlyScore)
      finalAction = maxAction(finalAction, "log_only");
  }
  return finalAction;
}

function maxAction(a: SafetyAction, b: SafetyAction): SafetyAction {
  const order: SafetyAction[] = ["allow", "log_only", "redact", "review", "block"];
  return order.indexOf(a) >= order.indexOf(b) ? a : b;
}

这段代码只是骨架。真实系统还需要考虑用户年龄、地区政策、产品线、请求来源、模型类型、历史违规次数、是否命中敏感业务流程等因素。

3. 审计日志必须记录策略版本

内容安全问题排查时,团队常常只看到”某条消息被拦截”,却不知道为什么拦截。建议每次决策至少记录:

字段说明
request_id / conversation_id请求或会话标识
policy_version当前生效的策略版本号
moderation_provider审核服务提供商
moderation_model审核模型版本
input_check_result输入检查结果
output_check_result输出检查结果
action最终执行动作
threshold_snapshot决策时刻的阈值快照
reviewer_result人工复核结果
appeal_result用户申诉结果
created_at时间戳

其中 policy_versionthreshold_snapshot 很重要。没有这两个字段,后续很难回答”这个用户当时为什么被拦截""是否因为策略升级导致拦截率上升""旧版本策略是否需要回滚”。

4. 人工复核不是兜底,而是训练数据来源

人工复核队列不应该只作为客服补救手段。它更重要的价值是形成持续校准数据。

建议把复核样本分为四类:

  1. false positive:误拦截,后续应调低阈值或增加白名单上下文。
  2. false negative:漏拦截,后续应收紧阈值或补充规则。
  3. policy ambiguous:政策不清,需要业务和法务明确边界。
  4. model uncertain:分类器分数波动,需要多模型交叉验证或人工优先。

这类标注数据可以进入安全回归集,但不要把它和普通模型效果评估混在一起。内容安全回归集应按照风险类别、语言、地域、场景和用户群体分层维护。

适用场景

内容安全网关最适合以下场景:

  • 面向公众开放的 AI 聊天、搜索、问答或写作产品。
  • 企业客服、金融、医疗、教育等对输出边界敏感的场景。
  • 支持用户上传图片、文档、网页、长文本并让模型生成总结的系统。
  • 多模型平台或内部 LLM 平台,需要统一内容安全策略。
  • 有人工运营、申诉、审计或合规要求的应用。

如果只是内部研发测试工具,也至少应保留 log_only 模式。Google Model Armor 文档中提到的 Inspect only 模式很适合灰度阶段:先记录潜在违规和命中分布,不立即阻断流量,等团队理解误拦截率后再切换到主动阻断。

常见误区

误区一:只要接入一个内容安全 API 就完成治理

API 只能提供检测结果,不能替你定义业务政策。真正的难点在于:哪些内容允许、哪些内容改写、哪些内容转人工、哪些内容必须拦截。

误区二:阈值越严格越安全

阈值过严会制造大量误拦截。误拦截会让用户绕过系统、换说法、投诉,甚至导致正常业务无法完成。内容安全不是简单追求拦截率,而是平衡风险、用户体验和人工成本。

误区三:所有场景共用一套规则

公开社区、内部知识库、客服工单、儿童产品、金融建议和代码助手的风险边界并不相同。统一规则容易在低风险场景过度拦截,在高风险场景又不够严格。

误区四:忽略多语言和隐晦表达

内容安全分类器在不同语言、方言、隐喻和群体相关表达上可能表现不一致。公开研究也提示,商业内容审核 API 可能同时存在过度审核和审核不足的问题。因此上线前需要准备多语言、多地区、多表达方式的测试集。

上线检查清单

  • 是否同时检查用户输入、拼接上下文和模型输出。
  • 是否将检测结果和执行策略解耦。
  • 是否保存 policy_versionthreshold_snapshotmoderation_model
  • 是否支持 log_onlyreviewredactblock 等多种动作。
  • 是否能按业务场景、地区、用户类型和产品线配置阈值。
  • 是否建立人工复核和申诉流程。
  • 是否统计误拦截率、漏拦截率、复核通过率和用户投诉率。
  • 是否支持策略灰度、回滚和审计导出。
  • 是否对图片、文档、工具返回内容和多轮上下文做边界说明。
  • 是否明确哪些内容会被记录,记录多久,由谁可访问。

FAQ

内容安全应该放在网关层还是业务服务层?

建议放在统一策略层或平台层,但要允许业务传入场景参数。完全放在业务服务里容易形成规则碎片;完全放在网关里又可能不了解业务语义。更好的方式是平台提供统一检测、审计和策略执行,业务侧提供场景、用户类型和风险边界。

输出内容被拦截后应该直接返回错误吗?

不建议总是返回硬错误。可以按风险等级选择安全改写、局部遮罩、解释性拒绝、转人工、重新生成或仅提示用户修改输入。直接返回错误虽然简单,但容易破坏体验,也不利于收集可复核样本。

参考资料

  1. OpenAI Moderation Guide: https://developers.openai.com/api/docs/guides/moderation
  2. OpenAI Safety Best Practices: https://developers.openai.com/api/docs/guides/safety-best-practices
  3. Azure AI Content Safety Overview: https://learn.microsoft.com/en-us/azure/ai-services/content-safety/overview
  4. Amazon Bedrock Guardrails: https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html
  5. Google Cloud Model Armor Overview: https://docs.cloud.google.com/model-armor/overview
  6. BingoGuard: LLM Content Moderation Tools with Risk Levels: https://arxiv.org/abs/2503.06550
  7. Lost in Moderation: How Commercial Content Moderation APIs Over- and Under-Moderate Group-Targeted Hate Speech and Linguistic Variations: https://arxiv.org/abs/2503.01623

常见问题

内容安全是否只需要在用户输入前做一次检测?
不够。生产系统通常需要同时检查输入和输出,因为风险可能来自用户请求、模型补全、工具返回内容或多轮对话上下文。
为什么不能把所有高风险分类都直接拦截?
不同业务的风险承受能力不同,分类器也可能误判。更稳妥的做法是按风险等级、用户场景和置信度配置拦截、改写、人工复核或仅记录。
内容安全阈值上线后还需要调整吗?
需要。模型、用户群、语言分布和业务政策都会变化,应通过灰度、抽样复核、申诉数据和误拦截率持续校准。
误拦截率应该怎么衡量?
至少要结合三类数据:人工复核推翻比例、用户申诉成功比例、灰度样本抽检结果。对高价值业务流程,还应记录被拦截后是否导致用户放弃操作。