做 UGC 产品时,内容审核通常不是一个独立功能,而是一条会穿过评论、私信、投稿、资料修改和运营后台的公共能力。对我这次的项目来说,我需要的不是一份"能识别敏感词"的演示接口,而是一层能直接进入发布链路的文本风控能力。
我最后接的是内容审核 API。相比把接口说明逐项翻一遍,我更想把这篇文章写成一次真实的接入实战:它在项目里解决了什么、我为什么选它、以及我怎么把原始返回整理成业务里真正能用的审核结果。
这个接口能帮我拦住哪些文本问题
我这次接内容审核接口,目标很明确:把原本分散在各个入口的文本判断收成一套统一服务。评论区、帖子发布、简介编辑、客服输入框,这些地方表面上是不同页面,本质上都在处理"用户提交一段文本,然后系统决定放行、提醒还是拦截"。
这个接口对我最有用的地方,在于它不只是给一个 pass/fail,而是把审核结果拆成了风险等级、命中类别、命中明细和可选脱敏结果。这样我就不用自己再去拼"拦截逻辑"和"提示文案"。
评论和帖子发布最适合先接
像评论区、动态发布、社区发帖这种场景,最在意的是响应要快,结果要清楚。接口返回 safe、low、medium、high 这类风险等级后,我就能把"直接通过""提示修改""进入人工复核"这几条路径拆得很自然。
运营后台也很适合复用
除了前台提交入口,我还把这类结果用在后台审核页。运营同学看到的不是一大段原文,而是命中类别、命中词和脱敏文本,处理效率会高很多。
我为什么直接选了它
我用的是 Apizero 的内容审核 API,接口地址是 v1.apizero.cn/api/content...,文档我直接参考的是它的市场页说明:apizero.cn/marketplace...。
我最后选它,主要是因为它很适合做服务层收敛。
风险分级比单纯拦截更好接业务
很多审核接口只返回"通过"或者"不通过",接起来当然简单,但业务层很快就会遇到问题:轻微风险和高风险要不要走同一条路径?提示文案要不要区分?运营后台要不要留痕?这个接口把 risk_level 和 categories 直接带出来,我在业务里就能做细一点的路由。
脱敏结果很适合前台回显
它支持返回 masked_text,这对前台交互很实用。我的做法不是简单地提示"审核未通过",而是把脱敏后的结果回给用户,让用户一眼就知道哪部分内容需要改。
命中明细能直接进入后台审核页
像 details 里这类"命中了哪类问题、通过什么方式识别、命中了哪些词"的信息,很适合直接进入运营后台。这样审核页不用再做太多解释,信息已经足够清楚。
我的接入做法
我没有让前端直接请求第三方接口,而是在后端包了一层统一的审核函数。这样做的目的很简单:所有文本入口都走同一套审核结果结构,后面我要补日志、加白名单或者换审核策略时,不需要改每个页面。
我的服务层主要做四件事:
- 接收文本
- 请求审核接口
- 整理风险结果
- 返回业务动作
调用流程
先看我在项目里采用的调用链路:
这个流程不长,但职责很清楚。前端只负责提交文本,后端负责把第三方审核结果转成业务里的动作,比如放行、提醒或者拦截。这样评论区、发帖页和后台录入都能复用同一个入口。
代码示例
下面这段代码,就是我在首版里保留的核心封装:
ts
import axios from "axios";
const client = axios.create({
baseURL: "https://v1.apizero.cn/api",
timeout: 5000,
});
export async function moderateText(text: string) {
try {
const { data } = await client.post("/content-moderation", { text });
if (data?.code !== 0 || !data?.data) {
throw new Error(data?.msg || "request failed");
}
const x = data.data;
return {
pass: !!x.is_pass,
riskLevel: x.risk_level || "safe",
categories: x.categories || [],
maskedText: x.masked_text || text,
action: x.risk_level === "high"
? "reject"
: x.risk_level === "medium"
? "review"
: "pass",
};
} catch (err) {
console.error("moderateText failed:", err);
return {
pass: false,
riskLevel: "high",
categories: ["审核失败"],
maskedText: text,
action: "review",
};
}
}
moderateText("你这个xx,xx吧").then((res) => {
console.log(res);
});
这段代码我刻意控制得很短。它已经包含了请求函数、超时、错误处理和调用示例,复制过去就能跑。真实项目里我会再补审计日志和入口级缓存,但首版接入时,先把审核结果和业务动作对上更重要。
返回结果我只保留了业务真正会用的部分
这个接口返回的信息不算少,但我没有把所有字段都往上透传。我的原则很简单:业务真正需要什么,我就保留什么;先把结构收紧,后面每个入口接起来都会更轻。
第一组是审核结论
我固定保留 is_pass 和 risk_level。这两个字段已经足够支撑大部分业务动作:通过、提示修改、进入复核、直接拦截。
第二组是命中类别
我会保留 categories,因为它能直接决定提示文案和后台标签。比如广告、联系方式、引流和谩骂,在前台和后台的处理策略通常不会完全一样。
第三组是脱敏结果
我会保留 masked_text,因为它最适合拿来做前台回显。用户不是只想知道"没通过",而是更想知道"哪部分应该改"。
至于 details,我不会在前台接口里全部透出,但会保留给后台审核页和内部日志。这样既能保证前台结构简洁,也不会丢掉定位问题的依据。
两个做完之后会明显顺手的小细节
我把审核结果直接映射成业务动作
如果只把第三方返回原样抛给上层,页面还要自己判断 low、medium、high 分别怎么处理,逻辑很快就会散。我的做法是服务层直接给出 pass、review、reject 这类动作,让调用方只关心流程,不关心第三方字段细节。
我把脱敏文本用于前台提示
很多时候用户不是故意提交问题文本,而是不知道系统会拦哪些表达。把 masked_text 回给前端之后,提示会更具体,用户修改成本也更低。这个细节做完之后,评论和发帖入口的交互会顺很多。
什么场景适合用它
如果你做的是评论系统、社区发帖、表单提交、简介编辑、客服消息或者运营后台录入,这类接口都很适合直接接。因为它解决的不是某一个页面的问题,而是一整类文本入口的治理问题。
不过它也有边界。我的理解是,这类审核接口很适合做"发布前拦截"和"后台辅助判断",但不适合单独承担完整的内容治理策略。真正的内容治理,通常还要结合账号历史、举报记录、上下文内容和人工复核一起看。
另外,如果你的业务只处理极短、极低风险的内部文本,这类能力就未必需要在每个入口都上。它更适合那些已经明显存在 UGC 治理需求,或者已经进入规模化运营阶段的产品。用在这些场景里,它的价值会非常直接。
4. 后端封装代码
ts
import axios from "axios";
const client = axios.create({
baseURL: "https://v1.apizero.cn/api",
timeout: 5000,
});
export async function moderateText(text: string) {
try {
const { data } = await client.post("/content-moderation", { text });
if (data?.code !== 0 || !data?.data) {
throw new Error(data?.msg || "request failed");
}
const x = data.data;
return {
pass: !!x.is_pass,
riskLevel: x.risk_level || "safe",
categories: x.categories || [],
maskedText: x.masked_text || text,
action: x.risk_level === "high" ? "reject" : x.risk_level === "medium" ? "review" : "pass",
};
} catch (err) {
console.error("moderateText failed:", err);
return {
pass: false,
riskLevel: "high",
categories: ["审核失败"],
maskedText: text,
action: "review",
};
}
}
moderateText("你这个xxx,xxx吧").then((res) => {
console.log(res);
});