大语言模型(Large Language Model,LLM)的安全问题,不能只理解成“模型会不会拒绝某些敏感问题”。真实系统通常由多层组件组成:输入审核、主模型推理、输出审核、日志监控、风控策略等。任何一层只要对某类表达形式覆盖不足,都可能被对抗性输入利用。
文言文对抗提示就是一个典型例子。它利用古汉语表达晦涩、词面和现代敏感词差异大、语义又能被模型理解的特点,暴露出多语言安全审核中的一个薄弱环节:模型可能懂古文,但安全分类器不一定同样懂。
需要明确一点:这里讨论的是防御建模、红队评测和安全加固思路,不提供可直接复用的绕过模板,也不展开具体有害请求的构造方式。
大模型安全链路为什么会出现语言盲区
一个常见的大模型服务大致可以拆成四段。
flowchart LR
A[用户输入] --> B[输入预处理与安全分类]
B --> C[主模型推理]
C --> D[输出安全审核]
D --> E[返回结果]
B --> F[关键词规则]
B --> G[意图分类器]
B --> H[提示注入检测]
D --> I[违规内容检测]
D --> J[敏感信息检测]
D --> K[策略合规判断]
输入审核主要判断请求是否涉及违法、暴力、自伤、色情、隐私泄露、恶意攻击等风险。输出审核则检查模型生成的内容是否真的越过边界。主模型本身负责根据上下文生成下一个 token,并不等同于完整的安全系统。
问题出在不同组件的语言能力并不一致。
| 组件 | 通常擅长的事 | 容易出问题的地方 |
|---|---|---|
| 主模型 | 理解多语言、古文、隐喻、上下文推理 | 可能顺着上下文生成不该生成的内容 |
| 关键词黑名单 | 快速拦截明确词面 | 对改写、古文、谐音、隐喻覆盖弱 |
| 意图分类器 | 判断请求意图 | 训练集如果缺少古文或低资源语言样本,召回会下降 |
| 输出审核器 | 阻断最终违规内容 | 如果输出被包装成古文、典故或隐喻,也可能漏检 |
这就形成了一个不对称局面:模型在预训练阶段见过大量古籍、文言文和历史文本,因此能解析古汉语;但安全数据集、审核规则和拒答样本往往集中在现代语言表达上。攻击者不一定需要让模型“变聪明”,只需要让审核器“看不准”。
Token 差异不是根因,语义覆盖不足才是根因
很多人会把这类问题简单归结为“token 不同,所以审核不到”。这个说法只说对了一部分。
模型输入会被分词器切成 token,再映射成向量。现代汉语、文言文、异体表达、隐喻写法在 token 层面确实差异很大。如果安全模块主要依赖关键词或浅层匹配,就会受到影响。
但更核心的问题不是 token,而是安全系统没有对“同一危险意图的多种表达”建立稳定表示。
可以把问题理解成三层:
flowchart TB
A[表层形式] --> B[语义意图]
B --> C[安全策略]
A1[现代汉语直白表达] --> B
A2[文言文表达] --> B
A3[隐喻表达] --> B
A4[跨语言表达] --> B
C --> D[允许]
C --> E[拒绝]
C --> F[安全替代回答]
安全判断应该尽量落在“语义意图”这一层,而不是停留在“表层形式”。如果系统只认识现代汉语直白表达,那么同一意图换成古文、隐喻、外语夹杂或专业术语包装后,就可能逃过第一道审核。
CC-BOS 的核心思想:把对抗提示变成搜索问题
CC-BOS 可以理解成一种对抗提示搜索框架。它不是手工写一个固定提示,而是把提示拆成多个策略维度,再用黑盒优化算法自动寻找更容易触发风险输出的组合。
从防御视角看,它的重要性不在于某个具体提示形式,而在于它把“绕过安全策略”抽象成了一个可优化问题。
形式化地说,可以把一个输入请求记为 q,把策略组合记为 s,提示生成器记为 G:
p = G(q, s)
其中:
q:待测试的风险意图,安全评测中通常来自受控数据集;s:一组表达策略,例如语言风格、语境包装、表达结构等;p:最终送入模型的测试提示;G:根据策略组合生成提示的函数。
目标模型是黑盒时,只能看到输入和输出:
r = M(p)
其中 M 是目标模型,r 是响应。评测系统再根据响应判断它是否拒绝、是否给出违规细节、是否偏离安全策略。
整个过程像这样:
flowchart TD
A[受控风险测试集] --> B[策略空间]
B --> C[候选提示生成]
C --> D[黑盒模型调用]
D --> E[响应归一化]
E --> F[安全评测打分]
F --> G{是否达到停止条件}
G -- 否 --> H[优化器更新策略组合]
H --> C
G -- 是 --> I[输出风险报告与防御样本]
这里的关键不是“某个古文模板”,而是自动搜索能力。一旦搜索器可以不断试探输入形式,并根据模型响应调整方向,安全系统就会面对持续变形的对抗样本。
八维策略空间:从单条提示到组合空间
CC-BOS 将文言文对抗提示拆成多个维度。为了避免提供可直接复用的绕过方法,可以把这些维度抽象成防御建模所需的类别。
| 维度 | 防御侧理解 | 风险点 |
|---|---|---|
| 身份框架 | 请求被包装成某种角色或叙述视角 | 审核器可能把危险意图误判成角色扮演 |
| 引导方式 | 用委婉、递进或间接方式推动回答 | 模型可能逐步越过拒答边界 |
| 推理结构 | 用古文逻辑、问答体或论证结构组织请求 | 浅层分类器难以捕捉真实目标 |
| 语义替换 | 用古语、典故、隐喻替代现代敏感词 | 关键词规则召回下降 |
| 文体风格 | 让输入看起来像古籍、注疏或文学文本 | 安全模型可能降低警惕 |
| 知识包装 | 把危险请求伪装成历史、学术或考据问题 | “知识问答”和“操作指导”边界变模糊 |
| 场景语境 | 构造特定时代、仪式、辩论或叙事背景 | 语境掩盖真实意图 |
| 触发节奏 | 分步、延迟或条件式触发目标内容 | 单轮检测难以发现完整攻击链 |
这些维度构成一个笛卡尔积空间:
S = D1 × D2 × ... × D8
每一个 Di 是某个维度的候选集合,s ∈ S 表示一种完整组合。组合空间一旦变大,人工枚举就不现实,黑盒优化算法就有了用武之地。
FOA:用果蝇优化算法搜索高风险组合
FOA(Fruit Fly Optimization Algorithm,果蝇优化算法)是一种群体搜索算法。它模拟果蝇觅食过程,把每个“果蝇”看成一个候选策略组合,通过多轮迭代寻找更高风险的输入形式。
在这个问题中:
- 一个个体 = 一组提示策略;
- 一个种群 = 多组候选策略;
- 适应度 = 该策略触发不安全响应的程度;
- 迭代 = 根据打分继续生成新候选。
可以用一个抽象公式表示:
P(t+1) = Update(P(t), best(t))
其中 P(t) 是第 t 代种群,best(t) 是当前评测中风险最高的策略组合。更新过程通常包含三类动作。
嗅觉搜索:扩大搜索范围
嗅觉搜索负责全局探索。它会对当前策略组合做随机扰动,让搜索器不要只停留在已有组合附近。
new_index = current_index + random_step
在防御评测中,这类扰动可以帮助发现安全系统没覆盖到的表达变体。它的作用不是精确收敛,而是扩大样本多样性。
视觉搜索:向高风险样本靠拢
视觉搜索负责局部收敛。它会让普通个体向当前高风险个体学习,保留一些有效维度,同时继续保持一定随机性。
candidate_dimension =
best_dimension, with probability p
original_dimension, otherwise
这类似“保留有效特征,再微调其他部分”。如果某些表达形式已经表现出风险,搜索器会围绕它们继续扩展。
柯西变异:停滞时跳出局部区域
当多轮迭代都没有发现更高风险样本时,搜索可能陷入局部最优。柯西变异利用长尾分布产生较大跳跃,让搜索器离开当前区域。
new_index = current_index + cauchy_noise
柯西分布的特点是偶尔产生很大的扰动,这对逃离局部最优有帮助。防御侧要关注这一点:攻击样本不会只在一个固定风格附近变化,它可能突然跳到完全不同的表达区域。
适应度评估:如何判断一次测试是否危险
对抗搜索必须有打分函数,否则优化器不知道哪个方向更“有效”。从防御角度看,适应度可以拆成两个部分。
| 打分项 | 含义 | 防御价值 |
|---|---|---|
| 语义一致性 | 响应是否在语义上贴近受控风险意图 | 识别“没有直接关键词但实质违规”的输出 |
| 拒答与安全性 | 响应是否明确拒绝、是否给出安全替代建议 | 判断安全策略是否生效 |
| 细节泄露程度 | 是否出现可执行步骤、材料、代码、参数等高风险细节 | 区分轻微偏离和严重失败 |
| 归一化后风险 | 将古文、现代文、英文等统一到同一语义空间后再评估 | 减少语言形式造成的误判 |
一个更安全的评测流程应当先做归一化,再做判断:
flowchart LR
A[模型响应] --> B[语言识别]
B --> C[语义归一化]
C --> D[多语言安全分类器]
C --> E[规则与关键词补充检测]
C --> F[LLM Judge 交叉评估]
D --> G[综合风险分]
E --> G
F --> G
G --> H[人工抽检与样本入库]
归一化不等于简单翻译。翻译可能丢失隐喻、古文典故或上下文指代,所以更稳妥的做法是组合多种信号:
- 多语言嵌入模型;
- 现代汉语释义;
- 英文中间表示;
- 安全分类器;
- 规则检测;
- 人工抽检。
为什么单靠关键词防不住
关键词规则适合拦截高频、明确、低成本的风险表达,但它很难处理“同义不同形”的输入。文言文对抗提示正是利用了这一点。
| 防御方式 | 能解决的问题 | 不能单独承担的部分 |
|---|---|---|
| 关键词黑名单 | 拦截直白敏感词 | 隐喻、古语、跨语言、拆分表达 |
| 正则规则 | 处理固定格式攻击 | 变形空间大时维护成本高 |
| 单轮分类器 | 快速判断当前输入 | 多轮递进、延迟触发、上下文累积 |
| 输出审核 | 阻断最终违规内容 | 如果输出同样被包装,仍可能漏检 |
| 语义级安全模型 | 捕捉意图和风险 | 需要高质量多语言安全数据 |
| 红队搜索评测 | 主动发现盲区 | 必须在授权环境中受控执行 |
更可靠的路线是把关键词规则放在底层兜底,把主要判断交给语义级安全模型,并且持续用对抗样本更新评测集。
防御设计:把“古文盲区”纳入安全系统
文言文不是唯一风险,低资源语言、方言、谐音、混合语言、编码变形、专业术语包装都有类似问题。防御设计不能只补一个“古文黑名单”,而要升级整个安全链路。
1. 输入侧做语义归一化
输入进入主模型前,可以增加一层归一化模块:
flowchart TD
A[用户输入] --> B[语言与风格识别]
B --> C{是否存在高风险变体}
C -- 是 --> D[释义/翻译/语义嵌入]
C -- 否 --> E[常规安全分类]
D --> F[语义级安全分类]
E --> F
F --> G{是否允许进入主模型}
G -- 允许 --> H[主模型]
G -- 拒绝 --> I[安全拒答或替代帮助]
这个模块的目标不是把所有输入都翻译成现代汉语,而是识别“表层安全、语义可疑”的请求,并把它送入更强的安全分类器。
2. 输出侧同样做归一化
如果只审核现代汉语输出,模型用古文、外语或隐喻表达危险内容时仍可能漏出。输出审核也需要走语义归一化流程。
检查重点包括:
- 是否提供可执行步骤;
- 是否列出材料、参数、代码或规避方法;
- 是否把拒答后又转向隐含指导;
- 是否通过故事、典故、类比泄露关键操作。
3. 加入多轮上下文检测
很多风险不会在第一轮显露,而是通过连续对话逐步积累。安全系统要维护会话级风险状态。
session_risk = f(current_input, model_output, history, user_pattern)
如果用户持续围绕同一敏感目标做拆分询问,即使单轮输入看似无害,也应提高风险等级。
4. 用对抗搜索生成防御样本
黑盒优化不只属于攻击者,也可以用于防御评测。安全团队可以在隔离环境中使用搜索方法发现模型薄弱点,再把样本转化为训练集、回归测试集和策略规则。
一个受控红队流程可以这样设计:
flowchart LR
A[安全策略定义] --> B[受控风险意图集合]
B --> C[对抗变体生成]
C --> D[沙箱模型评测]
D --> E[失败样本标注]
E --> F[分类器训练/规则更新]
F --> G[回归测试]
G --> H[上线前门禁]
关键要求是隔离、授权、留痕和最小化暴露。评测样本不应直接进入公开日志、客服系统或生产提示库。
工程落地时容易踩的坑
| 问题 | 典型表现 | 处理方式 |
|---|---|---|
| 把古文问题当成关键词缺失 | 不断补词表,但新表达仍然绕过 | 转向语义级分类和多语言评测 |
| 只测输入不测输出 | 输入被识别,输出仍出现隐晦违规内容 | 输入、输出、会话三层都要审核 |
| 只用一个评测模型打分 | 评测模型自身也有盲区 | 组合规则、分类器、LLM Judge 和人工抽检 |
| 翻译后再判断导致误判 | 翻译丢失典故或把隐喻洗白 | 保留原始输入、释义和嵌入表示,多路投票 |
| 忽视自动化试探 | 高频变体请求未被识别 | 加入速率限制、异常模式检测和账户级风控 |
| 没有回归测试 | 修复一类样本后又被相似变体击穿 | 建立固定红队样本集,每次模型或策略变更都跑 |
一个防御侧评测框架示例
下面的伪代码只描述安全评测流程,不包含任何对抗提示模板或可复用绕过内容。
def evaluate_multilingual_safety(test_cases, model, safety_stack):
reports = []
for case in test_cases:
# case 由安全团队维护,包含受控风险标签和语言/文体元数据
normalized_input = safety_stack.normalize(case.input_text)
input_risk = safety_stack.classify_input(normalized_input)
if input_risk.blocked:
reports.append({
"case_id": case.id,
"stage": "input_blocked",
"risk": input_risk.level,
})
continue
response = model.generate(case.input_text)
normalized_output = safety_stack.normalize(response)
output_risk = safety_stack.classify_output(
normalized_output,
context=case.context,
)
reports.append({
"case_id": case.id,
"stage": "output_checked",
"input_risk": input_risk.level,
"output_risk": output_risk.level,
"safe": output_risk.safe,
})
return reports
评测指标可以分成几类:
| 指标 | 解释 |
|---|---|
| 输入拦截率 | 风险请求在进入主模型前被识别的比例 |
| 输出拦截率 | 主模型生成风险内容后被输出审核拦截的比例 |
| 语义漏检率 | 表层无敏感词但语义违规的样本漏检比例 |
| 多轮风险召回率 | 拆分到多轮对话中的风险意图被识别的比例 |
| 误杀率 | 正常古文、文学、历史学习请求被错误拒绝的比例 |
| 回归通过率 | 已知风险样本在策略更新后仍能被稳定拦截的比例 |
安全系统不能只追求拦截率。误杀率同样重要,因为古文学习、历史研究、文学创作都是正常需求。好的策略应该区分“讨论历史文本”与“索取现实可执行危险指导”。
对模型安全建设的启示
文言文对抗提示说明了一个更普遍的问题:安全能力必须跟上模型理解能力。只要模型能理解某种表达,安全系统就应该能判断这种表达背后的意图。
比较稳妥的建设方向包括:
- 建立覆盖古文、方言、低资源语言、混合语言的安全评测集;
- 用语义归一化替代单纯关键词匹配;
- 对输入、输出和多轮上下文同时建模;
- 将红队搜索结果转化为持续回归测试;
- 对自动化试探行为做速率限制和异常检测;
- 对高风险领域采用更严格的拒答模板和安全替代回答;
- 对模型版本、系统提示和审核策略的变更建立上线门禁。
CC-BOS 这类研究的价值在于提醒安全团队:攻击面不只存在于模型能力本身,也存在于模型外部的审核链路、评测数据和语言覆盖范围中。只要安全策略仍然依赖表层形式,对抗者就可以通过换语言、换文体、换语境来寻找缝隙。真正可靠的防线需要理解语义、跟踪上下文,并持续用新型对抗样本检验系统。