AI 文本审核 API:一段中文文本判风险等级、命中标签与处置建议
UGC 内容、评论、私信、商品描述、客服话术,只要允许用户自由输入,就绕不开内容风控。关键词库维护到几万条仍然漏判误判,"免费日入过万" 换个说法就绕过,"傻X" 中间插个符号就绕过。text.moderation 把这件事做成一个 GET / POST 都能调的接口:一段中文文本进去,返回风险等级(none / low / medium / high)+ 命中标签明细 + 置信度 + 处置建议 ,并且审核服务不可用时不收费。
api.xujian.techV:xujian_cq
接口速览
| 关键事实 | 说明 |
|---|---|
| 接口地址 | https://api.xujian.tech/openapi/text/moderation |
| 接口编码 | text.moderation |
| 请求方式 | GET(content 放 Query)或 POST(JSON {"content":"..."}),二者等价 |
| 鉴权方式 | 请求头 X-API-Key,不做签名、时间戳或加密 |
| 入参 | 只有 content 一个字段,最长 1800 字符 |
| 返回核心字段 | riskLevel(风险等级)/ labels(命中标签数组)/ suggest(处置建议) |
| 标签体系 | 11 类违规标签 + nonLabel(未检测出风险) |
| 计费方式 | 按次计费,0.01 元/次,先预鉴权、给出审核结论后再扣费 |
| 不计费场景 | content 为空、超过 1800 字符、审核服务不可用、返回结果无法解析 |
| 建议超时 | 60 秒以上(含语义判定耗时) |
| 单次耗时 | 通常 1 ~ 数秒,costMs 为真实耗时 |
注意:"未检测出风险" 属于一次成功的审核,正常计费。合规结论本身有价值,判定为"可以放行"也是一次有效调用。
一、哪些业务需要这一步
| 场景 | 具体用法 |
|---|---|
| 社区 / 论坛发帖 | 发布前先审,命中 high 直接拦截,medium 转人工 |
| 评论与弹幕 | 高并发短文本,建议先审后放,low 可抽样复核 |
| 私信 / 即时通讯 | 拦截诈骗导流、联系方式导流,防止用户被骗 |
| 商品标题与详情 | 拦截违禁品交易、极限词与医疗夸大宣传 |
| 用户昵称 / 签名 / 头像文案 | 注册与修改时校验,避免涉政、辱骂类昵称上线 |
| 客服与工单 | 识别用户辱骂、威胁,优先转人工处理 |
| 搜索词与反馈 | 拦截恶意灌入的违规内容 |
| 招聘与简历 | 识别歧视性表述与隐私泄露(身份证、手机号、住址) |
| 内容平台审核后台 | 作为机审层,与人工复核形成两级队列 |
| 数据采集清洗 | 入库前批量过滤掉明显违规文本 |
二、请求参数
2.1 请求头
| 参数名 | 必填 | 说明 |
|---|---|---|
X-API-Key |
是 | 开发者 API Key,缺失或无效直接返回失败(不计费) |
Content-Type |
否 | POST JSON 时填 application/json;GET 与表单提交可不传 |
2.2 业务参数
| 参数名 | 位置 | 类型 | 示例 | 说明 |
|---|---|---|---|---|
content |
GET Query / POST JSON body | String | 加我微信 xxx666,带你日入过万 | 待审核文本,唯一入参,最长 1800 字符 |
长度口径:服务端会先把换行、制表符替换为空格、把连续空格压缩成一个,再 trim 后计算长度。也就是说长度按"清洗后"的文本算,一段带大量换行的文本不会因为换行符被误判为超长。
三、返回字段
3.1 顶层与 data
| 字段 | 类型 | 说明 |
|---|---|---|
code |
int | 0 成功,非 0 失败(统一为 500) |
msg |
String | 成功为 success,失败为具体原因 |
data |
Object | 业务数据,失败时为 null |
data 字段:
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
riskLevel |
String | high | 风险等级:none 未检测到风险 / low 低风险 / medium 中风险 / high 高风险 |
riskLevelText |
String | 高风险 | 等级中文名,直接展示用 |
suggest |
String | 建议拦截并转人工核实 | 处置建议,可直接进审核后台的"处理意见"栏 |
summary |
String | 疑似诈骗与广告导流内容 | 一句话结论,最多 100 字 |
labels |
Array | 见下 | 命中标签明细,最多 10 条,按危险程度从高到低排列 |
apiCode |
String | text.moderation | 接口编码 |
apiName |
String | AI文本审核服务 | 接口名称 |
chargeType |
String | PER_CALL | 计费类型 |
balance |
BigDecimal | 99.9900 | 扣费后的账户余额(元) |
costMs |
Long | 860 | 本次调用耗时(毫秒) |
3.2 labels 元素
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
label |
String | fraud | 标签编码,见标签表 |
labelName |
String | 诈骗欺诈 | 标签中文名 |
description |
String | 疑似返利诈骗内容 | 本次命中的具体说明,20 字以内 |
confidence |
BigDecimal | 92.00 | 置信度 0 ~ 100,保留 2 位小数;可能为 null(把握不足时不给分值) |
3.3 标签体系(对外承诺的完整取值)
| 编码 | 中文名 | 风险等级 | 典型内容 |
|---|---|---|---|
political |
涉政敏感 | high | 危害国家安全、分裂国家、损害国家形象与领导人不当表述 |
pornography |
色情低俗 | high | 淫秽描写、招嫖、性交易暗示 |
violence |
暴恐极端 | high | 暴力血腥、恐怖袭击、极端主义宣扬 |
gambling |
赌博相关 | high | 赌博平台推广、赌局邀约 |
drug |
毒品违法 | high | 毒品交易、吸食引导 |
fraud |
诈骗欺诈 | high | 刷单返利、冒充公检法、钓鱼链接 |
prohibited |
违禁品交易 | high | 枪支弹药、管制刀具、野生动物交易 |
abuse |
攻击辱骂 | medium | 人身攻击、侮辱诽谤、歧视性言论 |
ad |
广告导流 | medium | 营销推广、联系方式导流、二维码拉群 |
privacy |
隐私泄露 | medium | 他人身份证号、手机号、住址、银行卡号 |
other |
其他违规 | low | 不属于以上任何一类但确实不宜发布 |
nonLabel |
未检测出风险 | none | 占位标签 :没有命中任何违规时,labels 会返回这一条 |
风险等级不是逐个标签自评出来的,而是服务端取命中标签中的最高等级统一推导,保证同一段文本多次调用得到的等级口径稳定、可复现。
3.4 处置建议对照表
| riskLevel | riskLevelText | suggest |
|---|---|---|
| none | 未检测到风险 | 建议直接放行 |
| low | 低风险 | 建议放行;高召回场景可抽样转人工复核 |
| medium | 中风险 | 建议人工复核后再决定是否放行 |
| high | 高风险 | 建议拦截并转人工核实 |
四、调用示例
4.1 curl(GET)
bash
curl -s -G "https://api.xujian.tech/openapi/text/moderation" \
-H "X-API-Key: 你的APIKey" \
--data-urlencode "content=加我微信 xxx666,带你日入过万,内部渠道稳赚不赔"
4.2 curl(POST JSON,推荐用于含换行、引号的长文本)
bash
curl -s -X POST "https://api.xujian.tech/openapi/text/moderation" \
-H "X-API-Key: 你的APIKey" \
-H "Content-Type: application/json" \
-d '{"content":"加我微信 xxx666,带你日入过万,内部渠道稳赚不赔"}'
4.3 Java(Hutool)
java
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
public class TextModerationClient {
private static final String API_URL = "https://api.xujian.tech/openapi/text/moderation";
/**
* 审核一段文本
*
* @param apiKey 开发者 API Key
* @param content 待审核文本,最长 1800 字符
* @return data 节点;服务不可用时返回 null,且不扣费
*/
public static JSONObject moderate(String apiKey, String content) {
JSONObject json = JSONUtil.parseObj(
HttpRequest.post(API_URL)
.header("X-API-Key", apiKey)
// 段落文本交给 JSON,避免换行、&、= 等字符破坏 Query String
.body(JSONUtil.createObj().set("content", content).toString())
.timeout(60000)
.execute().body());
if (json.getInt("code") == null || json.getInt("code") != 0) {
System.out.println("审核失败(不收费):" + json.getStr("msg"));
return null;
}
return json.getJSONObject("data");
}
public static void main(String[] args) {
JSONObject data = moderate("你的APIKey", "你这个傻东西,脑子有问题吧,滚出这个群");
if (data == null) {
return;
}
System.out.println(data.getStr("riskLevel") + " / " + data.getStr("suggest"));
data.getJSONArray("labels").forEach(item -> {
JSONObject label = (JSONObject) item;
System.out.printf("命中:%s(%s) %s%n",
label.getStr("labelName"), label.getStr("label"), label.getStr("description"));
});
}
}
4.4 Python
python
import requests
def moderate(api_key: str, content: str):
"""返回 data 节点;服务不可用或参数非法返回 None,且不扣费"""
resp = requests.post(
"https://api.xujian.tech/openapi/text/moderation",
json={"content": content},
headers={"X-API-Key": api_key},
timeout=60,
)
result = resp.json()
if result.get("code") != 0:
print("审核失败(不收费):", result.get("msg"))
return None
return result["data"]
if __name__ == "__main__":
data = moderate("你的APIKey", "加我微信 xxx666,带你日入过万,内部渠道稳赚不赔")
if data:
print(data["riskLevel"], data["riskLevelText"], data["suggest"])
for item in data["labels"]:
print(item["label"], item["labelName"], item["confidence"])
4.5 JavaScript
javascript
async function moderate(apiKey, content) {
const resp = await fetch("https://api.xujian.tech/openapi/text/moderation", {
method: "POST",
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
body: JSON.stringify({ content })
});
const result = await resp.json();
if (result.code !== 0) {
throw new Error(result.msg);
}
return result.data;
}
五、返回示例
命中两个标签(诈骗 + 广告导流):
json
{
"code": 0,
"msg": "success",
"data": {
"riskLevel": "high",
"riskLevelText": "高风险",
"suggest": "建议拦截并转人工核实",
"summary": "疑似诈骗与广告导流内容",
"labels": [
{"label": "fraud", "labelName": "诈骗欺诈", "description": "疑似返利诈骗内容", "confidence": 92.00},
{"label": "ad", "labelName": "广告导流", "description": "疑似联系方式导流", "confidence": 88.00}
],
"apiCode": "text.moderation",
"apiName": "AI文本审核服务",
"chargeType": "PER_CALL",
"balance": 99.9800,
"costMs": 860
}
}
完全正常的文本(同样算一次成功调用,正常计费):
json
{
"code": 0,
"msg": "success",
"data": {
"riskLevel": "none",
"riskLevelText": "未检测到风险",
"suggest": "建议直接放行",
"summary": "正常文本,未检测出风险",
"labels": [
{"label": "nonLabel", "labelName": "未检测出风险", "description": "未检测出风险", "confidence": null}
],
"apiCode": "text.moderation",
"apiName": "AI文本审核服务",
"chargeType": "PER_CALL",
"balance": 99.9900,
"costMs": 720
}
}
超长文本(不收费):
json
{
"code": 500,
"msg": "content 长度不能超过 1800 个字符",
"data": null
}
六、可直接复用的两段代码
6.1 按风险等级分流到"放行 / 复核 / 拦截"三个队列
python
QUEUE = {"none": "pass", "low": "pass", "medium": "review", "high": "block"}
def route(data: dict) -> str:
"""审核结论 → 处理队列;labels 为空或只有 nonLabel 时按 none 处理"""
return QUEUE.get(data["riskLevel"], "review")
def hit_labels(data: dict) -> list:
"""只取真实命中的标签,过滤掉 nonLabel 占位"""
return [i["label"] for i in data["labels"] if i["label"] != "nonLabel"]
6.2 长文本按句拆分后批量审核(规避 1800 字符限制)
python
def split_and_moderate(api_key: str, text: str, size: int = 1500):
"""长文按标点切成 <= size 的片段,返回最高风险等级与全部命中标签"""
import re
chunks, buf = [], ""
for sentence in re.split(r"(?<=[。!?;\n])", text):
if len(buf) + len(sentence) > size:
chunks.append(buf)
buf = ""
buf += sentence
if buf:
chunks.append(buf)
level_rank = {"none": 0, "low": 1, "medium": 2, "high": 3}
top, labels = "none", []
for chunk in chunks:
data = moderate(api_key, chunk)
if not data:
continue
if level_rank[data["riskLevel"]] > level_rank[top]:
top = data["riskLevel"]
labels += hit_labels(data)
return top, labels
长文拆句会按片段数计费,建议只对确实需要全文审核的正文(如详情页、长帖)这么做,评论类短文本直接单次调用。
七、实践建议
- 超过 1800 字符先切分。超长直接失败且不计费,长文用第六节的拆句方案。
- 判定口径以
riskLevel为准 。suggest是给审核员看的建议文案,业务分流请读等级字段。 confidence可能为null。把握不足时不给分值,代码里要做判空,别直接float(confidence)。nonLabel不是命中。它只是"未检测出风险"的占位,过滤标签时要排除。- 不要拿它当唯一防线。高风险内容(尤其涉政、暴恐)建议与人工复核、关键词黑名单组合使用。
- 超时设 60 秒以上。语义判定比字符串匹配慢,别用查询类接口的 5 秒超时。
- 结果可以缓存。同一段文本(去空格后哈希)的结论在短期内不会变,缓存 Key 用文本哈希可省掉重复调用。
- 别做高频轮询。0.01 元/次虽然便宜,但评论流里每条都调、并且重复调用同一条,量起来也很可观。
- 审核结论要留痕 。把
riskLevel + labels + suggest + costMs一起写进自己的审核日志,便于事后追责与策略复盘。
八、错误码与排查
| code | msg | 是否扣费 | 处理建议 |
|---|---|---|---|
| 0 | success | 是(0.01 元) | 读取 data,按 riskLevel 分流 |
| 500 | 缺少请求头 X-API-Key | 否 | 请求头补 X-API-Key |
| 500 | API Key 无效 / API Key 已停用 | 否 | 到控制台核对 Key 状态 |
| 500 | 客户不存在或已停用 | 否 | 联系平台开通 |
| 500 | 接口不存在或已停用 | 否 | 确认接口编码 text.moderation |
| 500 | 余额不足,请先充值 | 否 | 预鉴权阶段就被拦下,不产生费用 |
| 500 | content 不能为空(GET 传 Query 参数,POST 传 JSON {"content":"..."}) | 否 | 检查参数位置:GET 只能走 Query |
| 500 | content 长度不能超过 1800 个字符 | 否 | 截断或按句拆分后重试 |
| 500 | 文本审核服务暂时不可用(...),本次调用不计费 | 否 | 稍后重试;连续失败可提交工单 |
所有失败场景统一返回
code=500,是否扣费以上表为准。判断"到底扣没扣费"最可靠的依据是响应data.balance是否减少。
九、计费与接入
| 项目 | 说明 |
|---|---|
| 单价 | 0.01 元/次 |
| 计费方式 | 按次计费,调用前先预鉴权(余额不足直接拒绝);给出审核结论后才扣费 |
| 计费判定 | 正常返回审核结论即计一次,包括"未检测出风险" |
| 不计费场景 | content 为空、超过 1800 字符、审核服务不可用、返回结果无法解析;鉴权失败与余额不足同样不扣费 |
| 建议超时 | 60 秒以上(含语义判定) |
接入流程:注册开发者账号 → 控制台创建 API Key → 请求头带上 X-API-Key 即可调用,无需签名或加密。控制台可查看调用量、扣费流水与余额。
服务站点:
api.xujian.tech(纯文本域名,不做跳转)。接口试用、数据与充值咨询可在控制台提交工单,或联系 Vxujian_cq。
十、小结
自建内容风控的成本不在"调用",而在"维护":词库要人跟、规则要人调、误判要人赔。做成接口之后,这段文本合规与否变成一分钱一次的判断,并且判定不出结论时不收费。
使用时要记住:
- 一个入参、四个等级 :
content进去,none / low / medium / high出来,按等级分流即可; - 读
riskLevel而不是summary:等级是服务端统一推导的稳定口径,文案只是说明; - "没风险"也计费:它是一次有效的合规判定,高频重复调用仍然要花钱;
- 1800 字符是硬上限:长文先拆句,别指望一次塞进去。
配套接口:vehicle.license(0.1 元/次)用于识别机动车行驶证,二者组合即可覆盖"文字 + 证照"两类主流 UGC 材料的机审环节。