给Agent输出加合规过滤怎么做
先说结论:别等大模型把脏东西吐出来才慌,得在它和用户之间架一层"出口闸"——命中敏感词就拦、就替换、就兜底回一句安全话术。我自己踩完坑给的最实用做法是:DFA 多模式匹配做识别,再配一套兜底文案做替换,最后留一个"模型也可能漏判"的二次复核。下面按 问题→分析→方案 三段讲,带步骤和我自己的踩坑。
一、问题:模型嘴上没把门,业务背锅
我做的是一个客服性质的问答小助手,给一个卖家居用品的客户用的。上线第三天就出事了——有个用户故意问"你们这破枕头是不是垃圾,竞品XX牌是不是更好",模型挺"老实"地把竞品名字、还有一句带情绪的评价原样夹在回答里发出去了。客户截图发我,问号脸。
问题不止这一个:
-
政治、涉黄涉暴这种红线词,模型大部分时候会拒答,但总有 5% 左右的边角 case 漏出来(尤其用户用谐音、拆字、空格分隔绕的时候);
-
竞品品牌名、内部代号、还没公开的活动价,这些不是"违法",但客户不想让 AI 嘴里出现;
-
模型偶尔会自己脑补一个客服热线号码,编得有鼻子有眼,这个比说脏话还麻烦。
说白了,大模型的"安全"是它训练时的安全,不等于你这个业务的合规。业务规则它根本不知道。
二、分析:拦在哪一层,怎么拦
我前后试过三个位置,记一下对比,省得你也绕:
|
拦截位置 |
做法 |
我的体感 |
|
改 Prompt |
在 system 里写"禁止提及竞品/脏话" |
能压一部分,但模型说漏就漏,不可控,靠概率不靠谱 |
|
模型自带安全开关 |
厂商的内容安全 API |
红线词管用,但管不了"竞品名"这种业务私有词 |
|
出口做后置过滤 |
回答生成完,发给用户前再过一道 |
最稳,规则在我手里,想加词就加词 |
结论很直接:Prompt 约束 + 出口后置过滤,两道一起上。Prompt 负责把大头压下去,出口过滤负责兜那漏网的 5%。
后置过滤的核心是个匹配效率问题。敏感词表我手上这份有 4000 多个词,一条回答几百字,要是拿正则一个个 if in 去撞,几千个词循环下来响应明显卡。所以识别得用 DFA(确定有限状态机) 把词表预编译成一棵字典树,一次扫描过完全文,O(n) 复杂度,n 是文本长度,跟词表多大基本无关。
三、方案:DFA 过滤 + 兜底替换,分四步
步骤 1:把敏感词建成 DFA 字典树
不用上来就装第三方库,Python 拿 dict 嵌套就能搭,几十行:
class SensitiveFilter:
def __init__(self, words):
self.root = {}
for w in words:
node = self.root
for ch in w:
node = node.setdefault(ch, {})
node['_end_'] = True # 词尾标记
def find_all(self, text):
hits = []
for i in range(len(text)):
node, j = self.root, i
while j < len(text) and text[j] in node:
node = node[text[j]]
if '_end_' in node:
hits.append((i, j, text[i:j+1]))
j += 1
return hits
词表我分了几类放不同文件:red.txt(红线,命中直接拦)、brand.txt(竞品/品牌,命中替换成"某品牌")、internal.txt(内部词,命中删掉)。分类的好处是不同类走不同兜底策略,下一步说。
步骤 2:命中后分级处理,别一刀切全拦
def guard(text, filters):
# 红线:整条回答作废,换安全话术
if filters['red'].find_all(text):
return "这个问题我不太方便回答,要不咱们聊点别的?"
# 品牌词:替换不删句
for s, e, w in filters['brand'].find_all(text):
text = text.replace(w, "某同类产品")
# 内部词:直接抹掉
for s, e, w in filters['internal'].find_all(text):
text = text.replace(w, "")
return text
兜底文案这块我吃过亏:第一版我让命中红线就回"您的问题包含敏感内容已被拦截",结果用户更炸毛,觉得被当坏人了。后来改成上面那种软话术,把球踢回对话流,投诉立刻少了。文案得当人话写,别像报警器。
步骤 3:堵谐音和拆字绕过
纯字符匹配挡不住"枕*头""枕 头"这种插符号、加空格的。处理也简单——匹配前先把文本里的特殊符号、多余空格洗掉,再过 DFA:
import re
def normalize(text):
return re.sub(r'[\s\*\.·\-_]+', '', text)
谐音(比如用拼音首字母、同音字代替)这层我没死磕,投入产出比太低,靠后面第 4 步的模型二次复核去捞,工程上认怂有时候是对的。
步骤 4:上一层"语义复核"兜最后的底
DFA 只认字面词,认不出"你们老板是不是跑路了"这种没有敏感词、但语义有风险的句子。这层我没自己训模型,直接挂了个现成大模型当二次裁判:把过滤后的回答连同一句"判断这段话是否涉及法律/政治/对公司的负面指控,只回 safe 或 risk"丢给它,返回 risk 就走兜底话术。
这套二次复核我是搭在一个零代码就能配智能体的平台上做的——拖个节点、挂上知识库、把上面这套过滤逻辑接成一个处理环节,不用我自己起服务部署模型。说实话第一次见到不写后端就能把"识别→复核→兜底"串成一条能跑的链路,我还挺意外的。缺点也有:这种语义复核每条多花一次模型调用,响应慢个三五百毫秒,高并发场景我只对"客服投诉类"会话开,普通寒暄不开,省点是点。
整套上线后,漏网率从我抽样的 5% 压到了千分之几,剩下的基本是谐音绕过那种长尾,认了。最大的体会是:合规这事别指望模型自觉,闸门攥在自己手里才踏实——DFA 管字面、语义复核管意思、兜底文案管体验,三层各干各的。你们给 Agent 做合规过滤是怎么处理谐音绕过的?评论区蹲一个更狠的方案。
(二次复核那层的模型我走的是讯飞星辰MaaS,现成 API 调,没自己部署算力,省了一台 GPU 机的钱。)
更多推荐




所有评论(0)