给大模型输出做合规过滤兜底

给大模型输出做合规过滤兜底 先把结论摆这儿别指望模型本身百分百守规矩真正稳的做法是在输出侧再加一层过滤兜底改写——命中风险词就拦拦了别甩个 500 给用户换一句得体的话顶上去。下面按 风险 → 怎么过滤 → 取舍 三段讲,都是我自己踩过的。一、风险:它平时挺乖,偏在你不看的时候翻车我做的是个客服性质的问答助手,内部用,回答商品和售后问题。上线前我自己测了大概两三百条,挺乖。结果上线第四天,运营群里甩来一张截图——有个用户绕着问了句带情绪的话,模型居然顺着对方的脏话回了半句,语气还挺到位。我当时有点惊到。明明系统提示里写了礼貌、不带攻击性,怎么就破了?后来翻日志才搞明白几件事:模型的安全是概率性的,不是开关。temperature 调高一点、上下文一长、用户换个刁钻问法,它就可能滑出去。越是开放域、越是带 RAG 检索的,越容易出岔子。我那个助手挂了私有知识库,有份老文档里残留了一段早就该删的内部话术,被检索命中后直接念出来了。单测覆盖不到。你测的是正常路径,真正捅娄子的是那些你根本想不到的输入。所以我的态度变了:模型输出不可信,得当成来自外部的不可信数据来对待。二、怎么过滤:拦 兜底改写,两步光拦不够。命中了直接回个该内容不可显示,用户体验稀烂,还显得你心虚。我最后落的是两层:先检测,命中了走兜底改写,把话重新说一遍。第一层,关键词 正则做粗筛,快、便宜、挡掉八成低级问题:import re BLOCK_PATTERNS [ r(脏话词A|脏话词B), # 实际维护成词表,别硬编码 r\d{17}[\dxX], # 身份证号这类 PII r(内部售价|成本价), # 业务敏感 ] def has_risk(text: str) - bool: return any(re.search(p, text) for p in BLOCK_PATTERNS)第二层,正则挡不住的语义风险(阴阳怪气、诱导、暗示性内容),粗筛之后再过一道语义判断。命中风险后,不是直接拒,而是把原回答 一句改写指令再喂给模型,让它换个说法:def safe_reply(user_q, raw_answer): if has_risk(raw_answer): rewrite_prompt ( f下面这段回复可能不合规,请在保留有用信息的前提下, f用礼貌、中立的语气重写,删掉任何敏感或攻击性内容:\n{raw_answer} ) rewritten call_llm(rewrite_prompt) if has_risk(rewritten): # 改写还命中,就兜最后一句 return 这个问题我暂时不太方便回答,要不换个方式问问? return rewritten return raw_answer逻辑就这么直白。检测的脏活、改写调的模型,我没自己搭——直接用了一个零代码就能拖配智能体的平台,把检测节点、改写节点、兜底回复几个块拖到画布上连起来,知识库也挂上去。说实话第一版兜底文案太干,像机器人念稿,我又回去把那句话改活了点。层干啥优点缺点关键词/正则挡明显违禁词、PII快、便宜、可解释漏语义、易绕过语义检测抓阴阳怪气、诱导覆盖广慢、要花钱兜底改写命中后换句话体验不崩多一次调用三、取舍:别追求干净到没人味这套上去之后,翻车基本没了,但代价得认:慢。一旦命中走改写,那一条要多一次模型调用,响应从一秒多飙到三四秒。我的做法是只对高风险分类才触发改写,普通回答不碰。误杀。词表写狠了,杀价砍一刀这种正常表达也被拦。得留人工复核口子,持续养词表,别指望一劳永逸。它只干杂活。过滤兜底解决的是别出事,不解决答得好。模型答得好不好,还是得靠提示词和知识库本身。我现在的心态是:合规过滤是底线,不是亮点。它存在的意义就是让你半夜睡得着,而不是让回答多惊艳。能拦住那 1% 的尴尬,值了。你们的模型有没有在线上突然蹦出过让你冒冷汗的一句?评论区聊聊,我也想知道别人都踩过啥坑。(模型这块我直接调的讯飞星辰 MaaS,现成大模型 API,没自己部署算力。)