第1788篇:AI内容审核系统——自动检测违规内容的分类器设计
第1788篇:AI内容审核系统——自动检测违规内容的分类器设计
做面向C端的AI产品,内容安全是绕不过去的坎。
有人会说:我用的是大模型,模型自己有安全对齐,还需要额外做内容审核吗?
我的答案是:必须做。原因有三:
第一,任何大模型都不能保证100%的安全输出,越狱(jailbreak)技术持续演进,模型的安全对齐会被绕过。
第二,业务层面的违规定义和模型的训练目标不完全一致。模型不会阻止所有平台政策违规的内容,只阻止通用意义上的有害内容。
第三,内容审核是合规的强制要求,不是"可选项"。《网络安全法》、《网络信息内容生态治理规定》都有明确规定。
今天这篇,我们系统地讲AI内容审核分类器的设计与实现。
一、内容审核的分类体系
先把需要检测的内容类别梳理清楚。这个体系设计好了,后面的工作才有方向。
一级分类(强制检测)
| 类别 | 说明 | 处理方式 |
|---|---|---|
| 政治违规 | 危害国家安全、分裂国家等 | 立即拦截,上报 |
| 违法犯罪 | 诈骗、赌博、毒品等 | 立即拦截,上报 |
| 色情内容 | 淫秽色情 | 立即拦截 |
| 暴力内容 | 极端暴力、恐怖主义 | 立即拦截 |
| 未成年人保护 | 涉及未成年人的不当内容 | 立即拦截,高优上报 |
二级分类(平台策略)
| 类别 | 说明 | 处理方式 |
|---|---|---|
| 仇恨言论 | 基于种族、性别等的歧视 | 拦截或过滤 |
| 骚扰内容 | 人身攻击、网络暴力 | 拦截或警告 |
| 误导信息 | 虚假新闻、健康谣言 | 标注警示 |
| 隐私侵犯 | 个人信息泄露 | 脱敏或拦截 |
| 广告营销 | 未经授权的商业推广 | 过滤或标注 |
| 版权侵权 | 大量复制他人内容 | 过滤或标注 |
二、多层次分类器架构
单一分类器很难满足精度和性能的双重要求。工业界通常用多层次、级联的架构:
这个架构的核心思想:
- 第一层规则引擎处理大量明确违规,成本极低
- 第二层ML分类器处理模糊案例,平衡精度和速度
- 第三层LLM只处理高复杂度或高风险的内容,保证最终防线的质量
三、规则引擎实现
@Service
@Slf4j
public class ContentRuleEngine {
@Autowired
private KeywordDictionary keywordDictionary;
@Autowired
private RegexRuleRepository regexRuleRepo;
/**
* AhoCorasick多模式匹配,O(n)时间复杂度
* 比逐词遍历快得多,适合大型关键词库
*/
private AhoCorasickAutomaton buildAutomaton(List<String> keywords) {
AhoCorasickAutomaton automaton = new AhoCorasickAutomaton();
for (String keyword : keywords) {
automaton.addPattern(keyword);
}
automaton.build();
return automaton;
}
@PostConstruct
public void initAutomata() {
// 按危险等级分别构建自动机
this.criticalAutomaton = buildAutomaton(
keywordDictionary.getByLevel(KeywordLevel.CRITICAL)
);
this.highAutomaton = buildAutomaton(
keywordDictionary.getByLevel(KeywordLevel.HIGH)
);
this.mediumAutomaton = buildAutomaton(
keywordDictionary.getByLevel(KeywordLevel.MEDIUM)
);
}
private AhoCorasickAutomaton criticalAutomaton;
private AhoCorasickAutomaton highAutomaton;
private AhoCorasickAutomaton mediumAutomaton;
/**
* 规则引擎检测
*/
public RuleCheckResult check(String content) {
RuleCheckResult result = new RuleCheckResult();
// 文本预处理:繁简转换、全半角统一、常见变体处理
String normalizedContent = normalizeText(content);
// 关键词匹配
List<String> criticalMatches = criticalAutomaton.findAll(normalizedContent);
if (!criticalMatches.isEmpty()) {
result.setViolation(true);
result.setLevel(ViolationLevel.CRITICAL);
result.setMatchedKeywords(criticalMatches);
result.setHitRule("CRITICAL_KEYWORD");
return result;
}
List<String> highMatches = highAutomaton.findAll(normalizedContent);
if (!highMatches.isEmpty()) {
result.setViolation(true);
result.setLevel(ViolationLevel.HIGH);
result.setMatchedKeywords(highMatches);
result.setHitRule("HIGH_KEYWORD");
return result;
}
// 正则规则检测(针对结构化违规内容,如手机号变形、银行卡号等)
List<RegexRule> regexRules = regexRuleRepo.findActive();
for (RegexRule rule : regexRules) {
Pattern pattern = patternCache.computeIfAbsent(rule.getRuleId(),
k -> Pattern.compile(rule.getPattern()));
Matcher matcher = pattern.matcher(normalizedContent);
if (matcher.find()) {
result.setViolation(true);
result.setLevel(rule.getViolationLevel());
result.setHitRule(rule.getRuleId());
result.setMatchedText(matcher.group());
return result;
}
}
result.setViolation(false);
return result;
}
/**
* 文本标准化
* 对抗常见的规避手段:加空格、全角字符、繁体字、同音字替换
*/
private String normalizeText(String text) {
if (text == null) return "";
String normalized = text;
// 全角转半角
normalized = fullWidthToHalfWidth(normalized);
// 繁体转简体
normalized = traditionalToSimplified(normalized);
// 移除零宽字符(常被用于插入字符破坏关键词匹配)
normalized = normalized.replaceAll("[\u200B-\u200F\uFEFF]", "");
// 移除重复的空格和换行
normalized = normalized.replaceAll("\\s+", " ");
// 常见同音字替换(如"涩图"→"色图")
normalized = applyHomophoneMapping(normalized);
// 移除常见的规避字符(如"色·情"→"色情")
normalized = normalized.replaceAll("[·。,、]", "");
return normalized.trim();
}
}四、ML分类器——轻量文本分类模型
第二层用轻量ML分类器,推理延迟要控制在50ms以内。
@Service
@Slf4j
public class MLContentClassifier {
@Autowired
private OnnxModelService onnxModelService; // 使用ONNX运行时,Java侧直接推理
@Autowired
private TokenizerService tokenizer;
private static final int MAX_SEQ_LENGTH = 128;
/**
* 使用ONNX Runtime运行BERT-tiny分类器
* BERT-tiny: 2层, 128维, 推理约10-20ms
*/
public ClassifierResult classify(String content) {
// 分词
long[] inputIds = tokenizer.tokenize(content, MAX_SEQ_LENGTH);
long[] attentionMask = buildAttentionMask(inputIds);
// ONNX推理
OnnxTensor inputIdsTensor = OnnxTensor.createTensor(
environment,
new long[][]{inputIds}
);
OnnxTensor attentionMaskTensor = OnnxTensor.createTensor(
environment,
new long[][]{attentionMask}
);
Map<String, OnnxTensor> inputs = Map.of(
"input_ids", inputIdsTensor,
"attention_mask", attentionMaskTensor
);
long startTime = System.currentTimeMillis();
try (OrtSession.Result result = onnxModelService.run(inputs)) {
float[][] logits = (float[][]) result.get("logits").getValue();
float[] probabilities = softmax(logits[0]);
long latency = System.currentTimeMillis() - startTime;
metrics.recordHistogram("classifier.latency_ms", latency);
// 找出概率最高的类别
int maxIndex = 0;
for (int i = 1; i < probabilities.length; i++) {
if (probabilities[i] > probabilities[maxIndex]) {
maxIndex = i;
}
}
ContentCategory predictedCategory = ContentCategory.values()[maxIndex];
float confidence = probabilities[maxIndex];
return ClassifierResult.builder()
.category(predictedCategory)
.confidence(confidence)
.allProbabilities(buildProbabilityMap(probabilities))
.latencyMs(latency)
.isViolation(predictedCategory != ContentCategory.SAFE)
.requiresHumanReview(confidence < 0.85 && predictedCategory != ContentCategory.SAFE)
.build();
}
}
/**
* 批量分类(用于内容抽检)
*/
public List<ClassifierResult> classifyBatch(List<String> contents) {
if (contents.isEmpty()) return Collections.emptyList();
int batchSize = Math.min(contents.size(), 32); // 批量上限32
List<ClassifierResult> results = new ArrayList<>();
for (int i = 0; i < contents.size(); i += batchSize) {
List<String> batch = contents.subList(i, Math.min(i + batchSize, contents.size()));
results.addAll(classifyBatchInternal(batch));
}
return results;
}
private float[] softmax(float[] logits) {
float maxLogit = Float.NEGATIVE_INFINITY;
for (float logit : logits) {
maxLogit = Math.max(maxLogit, logit);
}
float sum = 0;
float[] expLogits = new float[logits.length];
for (int i = 0; i < logits.length; i++) {
expLogits[i] = (float) Math.exp(logits[i] - maxLogit);
sum += expLogits[i];
}
for (int i = 0; i < expLogits.length; i++) {
expLogits[i] /= sum;
}
return expLogits;
}
}五、LLM语义审核——处理高复杂度内容
对于规则和ML分类器都难以处理的内容(语境依赖、隐喻、长文本等),用LLM做最终判断。
@Service
@Slf4j
public class LlmSemanticModerator {
@Autowired
private AiModelClient moderationModelClient;
private static final String MODERATION_SYSTEM_PROMPT = """
你是一个专业的内容安全审核助手。你的任务是判断给定的内容是否违反以下规则:
1. 政治违规:危害国家安全、分裂国家、颠覆政权的内容
2. 暴力内容:煽动暴力、恐怖主义、极端主义内容
3. 色情淫秽:色情、淫秽、不雅内容
4. 违法信息:诈骗、赌博、毒品、传销等违法内容
5. 仇恨言论:基于种族、性别、宗教等的歧视性内容
6. 隐私侵犯:泄露个人隐私、人肉搜索等内容
请以JSON格式回复,包含以下字段:
{
"is_violation": true/false,
"violation_categories": ["类别1", "类别2"],
"confidence": 0.0-1.0,
"reasoning": "简短说明",
"severity": "CRITICAL/HIGH/MEDIUM/LOW"
}
只输出JSON,不要有其他文字。
""";
/**
* LLM语义审核
*/
public LlmModerationResult moderate(String content, String context) {
String prompt = buildModerationPrompt(content, context);
long startTime = System.currentTimeMillis();
String response = moderationModelClient.generate(
MODERATION_SYSTEM_PROMPT,
prompt,
GenerationParams.builder()
.temperature(0.1) // 低温度,确保稳定输出
.maxTokens(200)
.build()
);
long latency = System.currentTimeMillis() - startTime;
try {
LlmModerationResult result = parseJsonResponse(response);
result.setLatencyMs(latency);
return result;
} catch (JsonParseException e) {
log.error("LLM审核结果解析失败 response={}", response, e);
// 解析失败时,保守处理:标记为需要人工审核
return LlmModerationResult.parseFailure(content, latency);
}
}
/**
* 构建审核Prompt,包含必要的上下文
*/
private String buildModerationPrompt(String content, String context) {
StringBuilder sb = new StringBuilder();
if (context != null && !context.isEmpty()) {
sb.append("对话上下文:\n").append(context).append("\n\n");
}
sb.append("待审核内容:\n").append(content);
return sb.toString();
}
}六、人工审核队列
ML分类器置信度不够高的内容需要进入人工审核队列。
@Service
@Slf4j
public class HumanModerationQueueService {
@Autowired
private ModerationQueueRepository queueRepository;
@Autowired
private ModeratorAssignmentService assignmentService;
@Autowired
private NotificationService notificationService;
/**
* 提交到人工审核队列
*/
public String submitForHumanReview(
String contentId,
String content,
ClassifierResult mlResult,
String contentContext) {
ModerationTask task = new ModerationTask();
task.setTaskId(UUID.randomUUID().toString());
task.setContentId(contentId);
task.setContent(content);
task.setContext(contentContext);
task.setMlPrediction(mlResult.getCategory());
task.setMlConfidence(mlResult.getConfidence());
task.setPriority(computePriority(mlResult));
task.setStatus(ModerationTask.Status.PENDING);
task.setSubmittedAt(Instant.now());
task.setDeadlineAt(computeDeadline(mlResult));
queueRepository.save(task);
// 分配给审核员
Moderator assignedModerator = assignmentService.assign(task);
task.setAssignedModerator(assignedModerator.getModeratorId());
task.setStatus(ModerationTask.Status.ASSIGNED);
queueRepository.save(task);
// 通知审核员
notificationService.notifyModerator(assignedModerator, task);
log.info("内容已提交人工审核 taskId={} contentId={} priority={}",
task.getTaskId(), contentId, task.getPriority());
return task.getTaskId();
}
/**
* 审核员提交结果
*/
@Transactional
public void submitModerationDecision(
String taskId,
String moderatorId,
boolean isViolation,
String violationCategory,
String notes) {
ModerationTask task = queueRepository.findById(taskId)
.orElseThrow(() -> new TaskNotFoundException(taskId));
// 验证是当前分配的审核员在操作
if (!task.getAssignedModerator().equals(moderatorId)) {
throw new UnauthorizedModeratorException("该任务未分配给当前审核员");
}
task.setViolationConfirmed(isViolation);
task.setViolationCategory(violationCategory);
task.setModeratorNotes(notes);
task.setCompletedAt(Instant.now());
task.setStatus(ModerationTask.Status.COMPLETED);
queueRepository.save(task);
// 将结果记录到训练数据集(用于模型改进)
trainingDataCollector.recordModerationDecision(task);
// 如果违规,执行相应处理
if (isViolation) {
contentActionService.takeAction(task.getContentId(), violationCategory);
}
log.info("人工审核完成 taskId={} moderatorId={} isViolation={}",
taskId, moderatorId, isViolation);
}
/**
* 计算优先级
* ML置信度高的违规案例优先处理(确认后影响更大)
* 高风险类别优先处理
*/
private int computePriority(ClassifierResult mlResult) {
int priority = 5; // 默认中等优先级
// 高置信度违规提高优先级
if (mlResult.getConfidence() > 0.8) priority -= 2;
// 高风险类别提高优先级
if (mlResult.getCategory() == ContentCategory.POLITICAL ||
mlResult.getCategory() == ContentCategory.CSAM) {
priority -= 3;
}
return Math.max(1, priority); // 优先级最小为1(最高)
}
private Instant computeDeadline(ClassifierResult mlResult) {
// 高风险内容需要在2小时内审核完成
if (mlResult.getCategory() == ContentCategory.POLITICAL ||
mlResult.getCategory() == ContentCategory.CSAM) {
return Instant.now().plus(Duration.ofHours(2));
}
// 普通内容24小时内处理
return Instant.now().plus(Duration.ofHours(24));
}
}七、内容审核的监控与运营
@Component
@Slf4j
public class ContentModerationMetricsService {
@Autowired
private ModerationAuditRepository auditRepo;
@Autowired
private AlertService alertService;
/**
* 计算各阶段的过滤率
* 如果某个阶段的过滤率突然变化,可能意味着:
* - 关键词库需要更新
* - ML模型出现了漂移
* - 出现了新型违规模式
*/
@Scheduled(fixedRate = 900000) // 每15分钟
public void computeFilterRates() {
Instant since = Instant.now().minus(Duration.ofMinutes(15));
long totalRequests = auditRepo.countByCheckedAtAfter(since);
if (totalRequests == 0) return;
long ruleBlocked = auditRepo.countByCheckedAtAfterAndBlockedByLayer(since, "RULE");
long mlBlocked = auditRepo.countByCheckedAtAfterAndBlockedByLayer(since, "ML");
long llmBlocked = auditRepo.countByCheckedAtAfterAndBlockedByLayer(since, "LLM");
long humanBlocked = auditRepo.countByCheckedAtAfterAndBlockedByLayer(since, "HUMAN");
double ruleRate = (double) ruleBlocked / totalRequests;
double mlRate = (double) mlBlocked / totalRequests;
double llmRate = (double) llmBlocked / totalRequests;
double humanRate = (double) humanBlocked / totalRequests;
// 记录指标
metricsService.recordGauge("moderation.rule_filter_rate", ruleRate);
metricsService.recordGauge("moderation.ml_filter_rate", mlRate);
metricsService.recordGauge("moderation.llm_filter_rate", llmRate);
metricsService.recordGauge("moderation.human_filter_rate", humanRate);
// 检测异常(规则过滤率突然大幅上升可能意味着新型攻击)
if (ruleRate > historicalRuleRate * 3) {
alertService.sendAlert(AlertLevel.HIGH,
String.format("规则层过滤率异常上升 current=%.2f%% historical=%.2f%%",
ruleRate * 100, historicalRuleRate * 100));
}
}
/**
* ML分类器准确率监控
* 通过与人工审核结果对比,评估ML分类器的准确率
*/
@Scheduled(cron = "0 0 6 * * ?")
public void computeClassifierAccuracy() {
// 获取过去7天有人工审核结论的ML分类结果
List<ModerationAudit> reviewedCases = auditRepo
.findByHumanReviewCompletedAndCheckedAtAfter(
true,
Instant.now().minus(Duration.ofDays(7))
);
if (reviewedCases.size() < 100) {
log.info("样本量不足,跳过分类器准确率计算 sampleSize={}", reviewedCases.size());
return;
}
// 计算精确率、召回率、F1
long truePositives = reviewedCases.stream()
.filter(c -> c.isMlPredictedViolation() && c.isHumanConfirmedViolation())
.count();
long falsePositives = reviewedCases.stream()
.filter(c -> c.isMlPredictedViolation() && !c.isHumanConfirmedViolation())
.count();
long falseNegatives = reviewedCases.stream()
.filter(c -> !c.isMlPredictedViolation() && c.isHumanConfirmedViolation())
.count();
double precision = truePositives > 0 ?
(double) truePositives / (truePositives + falsePositives) : 0;
double recall = truePositives > 0 ?
(double) truePositives / (truePositives + falseNegatives) : 0;
double f1 = (precision + recall) > 0 ?
2 * precision * recall / (precision + recall) : 0;
log.info("ML分类器性能 precision={:.3f} recall={:.3f} f1={:.3f} sampleSize={}",
precision, recall, f1, reviewedCases.size());
// F1低于阈值时告警,可能需要重新训练模型
if (f1 < 0.85) {
alertService.sendAlert(AlertLevel.MEDIUM,
String.format("ML分类器F1分数下降 f1=%.3f 低于阈值0.85,可能需要重训练", f1));
}
}
}八、分类器的持续学习
人工审核的结果是宝贵的训练数据,要利用起来持续改进模型。
@Service
@Slf4j
public class OnlineLearningService {
@Autowired
private ModerationTaskRepository taskRepo;
@Autowired
private TrainingDataRepository trainingDataRepo;
@Autowired
private ModelRetrainingTrigger retrainingTrigger;
/**
* 收集人工审核数据用于模型改进
*/
@EventListener
public void onModerationCompleted(ModerationCompletedEvent event) {
ModerationTask task = event.getTask();
// 只使用ML预测错误的案例(这些是最有价值的学习样本)
boolean mlWrong = task.isMlPredictedViolation() != task.isViolationConfirmed();
TrainingDataPoint dataPoint = new TrainingDataPoint();
dataPoint.setContent(task.getContent());
dataPoint.setLabel(task.isViolationConfirmed() ?
task.getViolationCategory() : "SAFE");
dataPoint.setSource("HUMAN_REVIEW");
dataPoint.setIsMLError(mlWrong);
dataPoint.setMLPrediction(task.getMlPrediction().name());
dataPoint.setMLConfidence(task.getMlConfidence());
dataPoint.setCreatedAt(Instant.now());
trainingDataRepo.save(dataPoint);
// 当积累了足够的错误案例,触发重训练
long errorCount = trainingDataRepo.countByIsMLErrorAndCreatedAtAfter(
true, Instant.now().minus(Duration.ofDays(7))
);
if (errorCount >= 500) {
log.info("积累了足够的错误案例,触发模型重训练 errorCount={}", errorCount);
retrainingTrigger.triggerRetraining("weekly_error_threshold_reached");
}
}
/**
* 生成用于模型评估的测试集
* 保证测试集随时间更新,反映最新的违规模式
*/
@Scheduled(cron = "0 0 2 * * MON") // 每周一凌晨2点
public void refreshEvaluationSet() {
// 从近期人工审核数据中采样
List<TrainingDataPoint> recentData = trainingDataRepo
.findBySourceAndCreatedAtAfter(
"HUMAN_REVIEW",
Instant.now().minus(Duration.ofDays(30))
);
// 按类别均衡采样
Map<String, List<TrainingDataPoint>> byLabel = recentData.stream()
.collect(Collectors.groupingBy(TrainingDataPoint::getLabel));
List<TrainingDataPoint> evaluationSet = new ArrayList<>();
int samplesPerClass = 100;
for (List<TrainingDataPoint> classData : byLabel.values()) {
List<TrainingDataPoint> sampled = sampleWithoutReplacement(classData, samplesPerClass);
evaluationSet.addAll(sampled);
}
Collections.shuffle(evaluationSet);
// 保存到评估集
evaluationSetRepository.saveAll(evaluationSet.stream()
.map(EvaluationSample::from)
.collect(Collectors.toList()));
log.info("评估集已刷新 totalSamples={}", evaluationSet.size());
}
}九、流式内容的审核
对于流式输出(Stream),内容审核需要特殊处理。
@Service
@Slf4j
public class StreamingContentModerator {
@Autowired
private ContentRuleEngine ruleEngine;
private static final int WINDOW_SIZE = 200; // 滑动窗口大小(字符数)
/**
* 对流式输出做滑动窗口检测
* 防止违规内容被分割在多个chunk之间逃过检测
*/
public Flux<String> moderateStream(Flux<String> contentStream, String sessionId) {
return contentStream
.scan(new StreamingModerationState(), (state, chunk) -> {
state.appendChunk(chunk);
// 维护滑动窗口
String window = state.getWindow(WINDOW_SIZE);
// 对窗口内容做规则检测
RuleCheckResult ruleResult = ruleEngine.check(window);
if (ruleResult.isViolation() &&
ruleResult.getLevel() == ViolationLevel.CRITICAL) {
state.setViolationDetected(true);
state.setViolationKeyword(ruleResult.getMatchedKeywords().get(0));
log.warn("流式输出检测到违规内容 sessionId={} keyword={}",
sessionId, ruleResult.getMatchedKeywords().get(0));
}
return state;
})
.filter(state -> !state.isViolationDetected())
.map(StreamingModerationState::getLatestChunk)
.onErrorResume(ViolationDetectedException.class, e -> {
log.error("流式内容违规,中断输出 sessionId={}", sessionId, e);
// 返回安全的错误信息
return Flux.just("抱歉,由于内容安全原因,此回复已被中断。");
});
}
/**
* 对完整的流式输出做最终审核
* 用于检测多个chunk合并后才能识别的违规内容
*/
public ContentModerationResult finalStreamReview(
String sessionId, String completedContent) {
// 对完整输出做完整的三层审核
return fullContentModeration(completedContent, sessionId);
}
}十、踩坑经验
坑1:关键词库不做版本管理
关键词库被多人维护,某天有人不小心删掉了一批重要词,导致大量违规内容通过了第一层过滤,直到人工抽检才发现。
教训:关键词库要像代码一样管理——版本控制、审批流程、变更日志、回滚能力。
坑2:流式输出的检测窗口太小
刚开始流式检测的窗口只有50个字符,被人发现可以把违规内容分成几段来规避检测(每段都不超过阈值,但合在一起就违规了)。窗口扩大到200字符后明显改善,但还是没能完全解决,最终加上了全文最终审核。
坑3:ML模型更新引入了新偏差
重新训练了ML分类器后,发现某个正常类别的误判率从2%飙升到了15%,大量正常用户的输入被误拦截,投诉量暴增。
教训:模型更新前要做A/B测试,灰度发布,并且要监控各类别的误判率,而不只是整体精确率。
坑4:人工审核员的标注不一致
不同审核员对相同内容的判断有差异,导致训练数据质量参差不齐。
解决方案:引入"二次确认"机制,置信度低的案例由两个审核员独立审核,结果不一致时进入升级流程。同时定期做审核员校准培训。
坑5:上下文丢失导致误判
"我要杀了他"在某些上下文下是愤怒的夸张表达,在某些上下文下是真实威胁。ML分类器没有利用上下文,导致前一种情况被大量误判。
解决方案:把对话上下文(最近3-5轮)作为分类器输入的一部分,同时在LLM审核时明确要求结合上下文判断。
十一、小结
AI内容审核系统是内容平台的安全地基。多层次架构是行业通行方案:规则引擎负责速度,ML分类器负责通用性,LLM负责语义深度,人工审核负责兜底。
几个关键指标需要长期监控:
- 各层过滤率(异常变化意味着问题)
- ML分类器的精确率和召回率(通过与人审对比计算)
- 人工审核队列积压量(积压意味着响应超时)
- 用户申诉量(误判的间接指标)
内容审核没有完美解,只有持续迭代。随着违规手段的演进,审核能力也需要不断升级。
