← 全部文章

面向红队测试与安全研究的无审查 LLM API

2026-08-20 · 2 分钟阅读 · red-teaming, security

如果你搭建过自动化红队测试框架,你一定已经见过这种失败模式:攻击者模型拒绝生成攻击,评判模型拒绝阅读它本该打分的输出,于是你的攻击成功率悄然下滑。没有任何报错。你测到的不是目标变得更安全——而是你的工具变得更谨慎。无审查 LLM API 存在的意义,就是把这个变量从测量中剔除。

这不是在论证护栏不好,而是在论证护栏该放在哪里。消费级聊天产品上的护栏是在履行它的职责。同样的护栏放进评估循环里,就成了横亘在你和你想计算的那个数字之间的一个不受控的混杂变量。

拒答是假阴性,而且它看起来像成功

这个问题之所以如此棘手,是因为拒答不是错误。你得到 HTTP 200。你得到 finish_reason: "stop"。你得到格式良好、能干净解析的 JSON。里面的字符串是"我无法帮你做这个",而你的流水线把它写进数据集的某一列、某个标签字段或报告的某一节,然后继续往下走。

具体来说,在受影响最严重的三个地方:

攻击者模型。 在迭代式越狱搜索中——PAIR、TAP、GCG 风格的优化,任何攻击者在环的方法——一次拒答就会终止搜索的那个分支。你报告的攻击成功率成了攻击者意愿的函数,而不是目标鲁棒性的函数。把攻击者换成一个不那么谨慎的,你给目标模型交付的"改进"就烟消云散了。

评判与打分模型。 用 LLM 作为评判者给有害输出打分,要求评判者真正去读那些有害输出。当它拒答时,你得到的是一个无法解析的分数。大多数框架会丢掉那一行。被丢掉的行并不是随机缺失的——它们恰恰集中在你最需要打分的那些严重输出上,于是你的汇总结果偏向安全。

分析工作。 问一个主流模型某段反编译的例程做了什么,它常常会因为出现了恶意软件样子的 token 而拒答,而不是基于你的实际请求。任务是防御性的;分类器却因词汇而触发。在日志中分诊漏洞利用流量、标注钓鱼邮件语料来训练检测器、或者撰写 CTF 发现,都是同样的故事。

因为拒答是无声的,团队最终会去写拒答检测器——而这本身就是另一个尚未解决的问题:

python
REFUSAL_MARKERS = ("i can't", "i cannot", "i'm unable", "i won't", "as an ai")

def looks_like_refusal(text: str) -> bool:
    """Substring heuristic. Good enough to alarm on, not good enough to trust:
    it misses polite deflections that never use the phrase, and it fires on any
    completion that quotes a refusal. Both error directions corrupt a dataset."""
    return any(m in text[:200].lower() for m in REFUSAL_MARKERS)

为一个本可以从源头消除的问题去构建一个不可靠的检测器,是一笔错误的交易。

调用 API

unbleep 使用 OpenAI Chat Completions schema,所以你手头的任何客户端都能用。改掉 base URL,改掉密钥,SDK 保持不变。

python
import json
import os

from openai import OpenAI

client = OpenAI(
    base_url="https://unbleep.ai/v1",
    api_key=os.environ["UNBLEEP_API_KEY"],
)

TRIAGE = """You are a malware analyst writing notes for a detection engineer.
Given a decompiled routine, describe what it does, the observable artefacts it
would leave on a host, and where a defender could detect it. Reply as JSON with
keys: behaviour, artefacts, detection_surface, confidence."""


def triage(decompiled: str) -> dict:
    """One sample in, one structured verdict out — no refusal branch to handle."""
    resp = client.chat.completions.create(
        model="unbleep",
        messages=[
            {"role": "system", "content": TRIAGE},
            {"role": "user", "content": decompiled},
        ],
        response_format={"type": "json_object"},
        temperature=0.2,
    )
    return json.loads(resp.choices[0].message.content)

用 curl 的等价写法——这里是给钓鱼邮件语料打标签,为检测器构建训练数据:

bash
curl https://unbleep.ai/v1/chat/completions \
  -H "Authorization: Bearer $UNBLEEP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "unbleep",
    "messages": [
      {"role": "system", "content": "Label each email for a phishing detector. Answer with one word: phishing or benign."},
      {"role": "user", "content": "Subject: Payroll update required\n\nYour direct deposit is on hold. Confirm at hxxp://payroll-verify.example.com"}
    ],
    "temperature": 0,
    "max_tokens": 4
  }'

在打标签的循环里,temperature: 0 和一个紧凑的 max_tokens 比平时更重要——你要的是标签,而不是一段解释标签的文字。

选择档位

三个模型,全部无审查,区别在于上下文和价格:

unbleep 和 unbleep-high 是推理档位:它们在作答前会先思考,并在 content 旁边的 reasoning_content 字段中返回推理轨迹。对于评判模型来说,这条轨迹确实有用——它告诉你某一行为什么得到了那个分数,而这正是你审计分歧时需要的。它同样按输出计费,所以在不需要它的任务上请发送 "thinking": false。unbleep-mini 直接作答,不返回轨迹。

由你掌控的治理

从模型中移除拒答,并不意味着从你的流水线中移除监督。可选的 policy 参数在我们这一侧按请求执行治理:

python
resp = client.chat.completions.create(
    model="unbleep",
    messages=[...],
    extra_body={"policy": "research"},  # recorded on the usage row; answers exactly like off
)

off 是默认的无过滤基线。research 的回答与 off 完全一致——该级别会记录在用量行上,这样你可以在自己的报表中把评估流量和基线流量区分开。它不施加任何额外筛查。strict 会将消息文本与我们由运营方维护的服务黑名单进行比对,命中即返回 422。这份名单对所有选择启用它的人都是相同的——没有按账户配置的黑名单——所以请把它当作兜底措施,而不是你的生产内容策略。

在把流水线指向这里之前,有一件事需要先想清楚:我们会存储你发送的内容和返回的内容。 我们接受并转发给模型的每一次调用,其请求体、响应内容和源 IP 都会被写入我们的数据库并保留 30 天,用于滥用调查、支持和账单争议。正文在 64 KB 处截断。源 IP 的保留时间超过 30 天——它还会被写入为该次调用计费的用量行,而那一行是我们会保留更久的财务记录。唯一不被存储的,是被 strict 在到达模型之前就拒绝的请求:它会出现在你的用量历史中,但不含内容。除此之外没有任何例外,也没有关闭它的设置。如果你的语料敏感到在你的边界之外存放一份 30 天的副本都成问题——活体恶意软件、客户数据、受 NDA 约束的项目——那这就是一个真实的约束,而隐私政策是你应该在开始之前、而不是之后阅读的页面。

错误遵循 OpenAI 信封格式,所以现有的处理器可以工作:401 密钥无效,402 额度用尽,422 被策略拦截,429 触发速率限制,5xx 可重试。每个响应都携带 x-ratelimit-* 响应头,这样批处理作业可以自行控制节奏,而无需猜测。

无审查 LLM API 不会给你什么

无审查模型是一把更锋利的工具,而不是一把更放任的工具。它会回答那些基础模型被调教成拒绝的问题,包括那些你本不该问的问题,而且它作答时的自信与它对待其他一切问题时别无二致——这也意味着,在一个有护栏的模型会因为无知而拒答的地方,这个模型可能会直接编造。请让一个人为输出结果负责。可接受使用政策明确说明了这个 API 的用途,以及它绝不允许用于的那一小部分事情;授权与意图才是判断标准,而不是词汇。合法使用是你的责任。

获取 API 密钥,别再测量你工具的谨慎程度了。