决策模型做内容安全检测:从踩坑到上线

接口参考:

https://help.aliyun.com/zh/model-studio/decision-model-api?spm=a2c4g.11186623.0.i0#是非判断示例-h4

20 条标注样本实测:违规拦截率 100%、漏放率 0%、误拦率 0%、平均 35~59ms/条。

代码:content_safety.py / content_safety.mjs,报告:内容安全检测报告.md

一、为什么不用普通大模型做审核

用 qwen 做内容审核的典型做法是:给一段 system prompt,让它输出 JSON 判定。问题是它要"生成"------生成就有 token 成本、有延迟、有格式不稳定的风险,还得写解析兜底。

百炼的决策模型 (decision-model-preview)是另一条路:一次请求带上若干"问题",一次前向返回所有问题的判定和概率分布,不生成任何文本。输出长度固定为 0,所以延迟与内容长度基本无关,实测服务端 40~100ms。

它支持三种问题类型:

类型 语义 返回
noul 是非判断 P(yes),0~1
score 有序量表 等级期望值(可落在两级之间,如 1.43)+ 完整概率分布
choice 多选一 选项名 + 置信度 + 概率分布

二、第一个坑:文档里的域名调不通

文档写的是:

复制代码
https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1/systemone

实际踩坑记录(都是真跑的):

请求 结果
https://dashscope.aliyuncs.com/compatible-mode/v1/systemone ❌ 404(公共域名根本没这条路由)
https://cn-beijing.maas.aliyuncs.com/... ❌ 连接失败(只有 SOA 记录)
https://default.cn-beijing.maas.aliyuncs.com/... ❌ 400 Workspace endpoint is invalid.
https://ws-test.cn-beijing.maas.aliyuncs.com/... ❌ 403 Workspace endpoint access denied.
.../chat/completions + model=decision-model-preview ❌ 403 access_denied(决策模型不走 chat 协议)
https://trial.cn-beijing.maas.aliyuncs.com/compatible-mode/v1/systemone ✅ 200

结论:用试用域名 trial.cn-beijing.maas.aliyuncs.com 即可,不需要 WorkspaceId 。业务空间专属域名要控制台「业务空间管理」里的空间 ID,新加坡地域换成 trial.ap-southeast-1.maas.aliyuncs.com。

三、最小可用示例

python 复制代码
import os, requests

resp = requests.post(
    "https://trial.cn-beijing.maas.aliyuncs.com/compatible-mode/v1/systemone",
    headers={"Authorization": f"Bearer {os.environ['QWEN_LLM_API_KEY']}",
             "Content-Type": "application/json"},
    json={
        "model": "decision-model-preview",
        "state": "这个傻X客服,赶紧退钱!再不处理我就去举报你们。",
        "questions": {
            "abuse":  {"type": "noul",   "instructions": "这段内容是否在辱骂、人身攻击?"},
            "risk":   {"type": "score",  "instructions": "整体有害程度?",
                       "criteria": ["无风险", "低风险", "中风险", "高风险"]},
            "action": {"type": "choice", "instructions": "该怎么处置?",
                       "criteria": {"pass": "放行", "review": "转人工", "block": "拦截"}},
        },
    },
    timeout=30,
)
print(resp.json()["answers"])

真实返回:

json 复制代码
{
  "answers": {
    "abuse":  {"type": "noul", "noul": 0.87},
    "risk":   {"type": "score", "score": 1.95, "confidence": 0.65,
               "probabilities": {"0": 0.15, "1": 0.2, "2": 0.21, "3": 0.44}},
    "action": {"type": "choice", "choice": "review", "confidence": 0.49,
               "probabilities": {"pass": 0.03, "review": 0.66, "block": 0.31}}
  },
  "usage": {"input_tokens": 86},
  "latency_ms": 51.5
}

三个问题、一次请求、51ms、86 个输入 token,没有任何输出 token。注意 action 只给了 review、置信度 0.49 ------ 这就是后面要讲的核心问题。

(上面这段是可运行文件 blog_demo_min.py,python blog_demo_min.py 直接出结果。)

四、把审核需求变成 10 个问题

内容审核要的不是"像不像违规",而是可解释的分类 + 明确的处置。所以拆成 10 个问题一起问:

  • 1 个 choice:处置动作(pass / review / block)
  • 1 个 score:整体风险等级 0~3
  • 8 个 noul:涉政敏感、色情低俗、暴力恐怖、违法犯罪、辱骂攻击、垃圾营销、隐私泄露、自杀自残

实测 10 个问题全量一次请求:888 输入 token,服务端 98ms。问题数不设上限(建议 ≤16),延迟随问题数近线性增长,所以别一个类目发一次请求。

五、最大的坑:三个信号单独用都会翻车

这是踩得最深的地方。以下全是原始返回:

样本 action risk noul 只看它会怎样
售后咨询 review 置信 0.97 0.13 8 类全 0 正常售后被误转人工
小说里的战争描写 pass 置信 1.0 0.35 violence=0.83 正常文学被误拦
出售改装手枪和弹药 pass 置信 0.95 0.12 illegal=0.54 卖枪直接漏放
泄露他人身份证号 pass 0.03 privacy=0.95 隐私泄露漏放

原因各不相同:

  • action 是"整体处置倾向",但对投诉、差评、抱怨类文本有强烈的 review 偏置;
  • risk 是"整体有害程度",对明确的违法交易反而给分很低;
  • noul 判的是**"是否涉及"**,不是"是否违规" ------ 小说写战争,确实"涉及"暴力。

结论:不要相信任何一个单一信号。

六、两道解法

1. 改问题措辞(效果立竿见影)

action 的 instructions 里明说:

严格按标准判定:不要因为文本是投诉、差评、抱怨或情绪化表达就选 review;这些只要不含违规内容就选 pass。review 仅用于确有违规嫌疑但证据不足的情况。

noul 从"是否涉及 "改成"是否在传播、宣扬、教唆或促成":

仅仅是提及、讨论、批评、新闻报道、文学影视描写、反诈与安全提示不算违规,一律判否。

改完后:售后样本 review(0.97) → pass(0.94),文学样本 violence 0.83 → 0。提示词在决策模型上同样重要,甚至更重要------它的每个问题都是一次独立判断,措辞直接决定判定尺度。

2. 投票融合

python 复制代码
BLOCK_THRESHOLD, REVIEW_THRESHOLD = 0.5, 0.2
SEVERE = ("politics", "porn", "violence", "illegal", "selfharm")   # 高危类目

severe_hit = any(cat[c] >= BLOCK_THRESHOLD for c in SEVERE)
max_cat = max(cat.values())

if (risk >= 2.5
    or (severe_hit and risk >= 0.5)          # 高危类目单信号 + 风险不为 0 → 拦截(补"卖枪")
    or (max_cat >= 0.9 and (risk >= 1.5 or action == "block"))   # 双信号共振
    or pii_block):                           # 身份证号/银行卡号
    return "block"
if action in ("block", "review") or risk >= 1.5 or max_cat >= BLOCK_THRESHOLD or pii:
    # 纠偏:模型只因文本"像投诉"就给 review,且类目与风险均无违规信号 → 放行
    return "pass" if (max_cat < REVIEW_THRESHOLD and risk < 1) else "review"
return "pass"

两条原则:

  1. block 必须多信号共振 ,唯一的例外是"高危类目命中 + 风险不为 0"(涉政/色情/暴恐/违法/自残),专治上面那个 risk=0.12 的卖枪样本;
  2. 单一信号最多升到 review 转人工,绝不静默放行。审核系统里,漏放比误拦贵得多。

七、结构化信息交给正则,别交给模型

泄露身份证号那条,模型给的是 risk=0.03、action=pass,只有 privacy 类目到了 0.95。指望模型挡住隐私泄露不可靠,正则才是对的工具:

python 复制代码
_PII = [
    ("idcard",   "身份证号", "block",  r"(?<!\d)\d{17}[\dXx](?!\d)|(?<!\d)\d{15}(?!\d)"),
    ("bankcard", "银行卡号", "block",  r"(?<!\d)\d{16,19}(?!\d)"),
    ("phone",    "手机号",   "review", r"(?<!\d)1[3-9]\d{9}(?!\d)"),
    ("email",    "邮箱",     "review", r"[\w.+-]+@[\w-]+\.[A-Za-z]{2,}"),
]

小坑:18 位身份证号同时也满足"16-19 位银行卡",要记录已命中区间去重,否则一条文本报两个类目。

八、别忘了平台自带的兜底

涉政那条样本,请求还没到模型就被百炼网关拒了:

复制代码
HTTP 400  Input data may contain inappropriate content.

这不是调用失败,是平台已经判定违规。必须把它当成"拦截"结论,而不是降级重试或报错------否则最危险的那类内容反而会被漏放:

python 复制代码
if "inappropriate content" in msg.lower():
    return {"action": "block", "reason": "平台输入审核拒绝", ...}

九、实测结果

20 条标注样本:10 条正常/边界(含反诈提示、时政新闻、小说战争描写、文明差评、维权投诉)+ 10 条覆盖全部 8 类违规。

指标 决策模型 qwen-flash 降级
违规拦截率 100% 100%
违规漏放率 0% 0%
正常放行率 100% 100%
误拦率 0% 0%
平均耗时 35~59ms/条 ~280ms/条

十、上线的几个建议

  1. 三道闸:平台输入审核(免费兜底)→ 决策模型判 10 个问题 → 投票融合分流。
  2. 失败方向要保守 :超时/异常按 review;平台 400 inappropriate content 按 block。
  3. 降级要自动 :决策模型不可用时无感切到 qwen-flash(字段一致),并把路由类失败(400/403/404)在进程内缓存,避免批量时每条都白跑一次。
  4. 留痕 :落库 request_id、8 类概率、riskLevel、命中的信号,便于回溯和调阈值。
  5. 缓存:按文本哈希缓存判定结果,审核场景重复率很高。

=代码=

bash 复制代码
# -*- coding: utf-8 -*-
"""博客里那段"最小可用示例"的原样验证,确保贴出去就能跑。"""
import json
import os

import requests

resp = requests.post(
    "https://trial.cn-beijing.maas.aliyuncs.com/compatible-mode/v1/systemone",
    headers={"Authorization": f"Bearer {os.environ['QWEN_LLM_API_KEY']}",
             "Content-Type": "application/json"},
    json={
        "model": "decision-model-preview",
        "state": "这个傻X客服,赶紧退钱!再不处理我就去举报你们。",
        "questions": {
            "abuse": {"type": "noul", "instructions": "这段内容是否在辱骂、人身攻击?"},
            "risk": {"type": "score", "instructions": "整体有害程度?",
                     "criteria": ["无风险", "低风险", "中风险", "高风险"]},
            "action": {"type": "choice", "instructions": "该怎么处置?",
                       "criteria": {"pass": "放行", "review": "转人工", "block": "拦截"}},
        },
    },
    timeout=30,
)
print("HTTP", resp.status_code)
print(json.dumps(resp.json(), ensure_ascii=False, indent=2))
相关推荐
摇滚侠3 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 12
java·数据库·笔记
川石课堂软件测试3 小时前
学习软件测试需掌握的一些知识
数据库·python·sql·功能测试·网络协议·mysql·http
2501_937767164 小时前
自建 SQL 数据库定时备份踩坑复盘与落地思路
数据库·sql
silvamux_5 小时前
10款模型写种草笔记:可发布性、返工来源与成本数据缺口复盘
java·数据库·笔记
I Am a robert girl7 小时前
从 GameHorizon 拆解多时间尺度游戏评测:数据管线、离线基准与在线回放的三层设计
数据库·游戏·多时间尺度·数据管线·游戏评测·离线基准·在线回放
安教安全员通关宝7 小时前
2026年吉林安全员C3证考试题库+答案+解析
安全
躺柒7 小时前
读数据安全架构设计与实践17安全架构案例(下)
数据库·安全·零信任·安全架构·防火墙·无边界
HackTwoHub7 小时前
双通道红队 C2,MCP 接入 AI,全自动渗透实战利器,具备自动提权、进程伪装、无文件上线能力,载荷 AES 加密
安全·web安全·网络安全·系统安全·密码学·可信计算技术·安全架构
SelectDB7 小时前
实时更新排错现场:导入了却查到旧数据,和 FINAL 越加越慢的两条路
大数据·数据库·数据分析
学页官方8 小时前
【安全实操】教培系统上线前的安全检查清单(20条)
安全