背景与挑战

在金融类智能投顾系统中,用户可能会主动咨询一些违规或非法的敏感话题。例如,有用户询问“有没有某股票的内幕消息?”此类请求涉及内幕交易等违法内容,系统决不能直接回答。为了符合监管合规要求并保护系统安全,我们需要在用户提问的瞬间就检测出敏感词,并立即拦截该请求,返回预设的固定话术进行拒绝。这样的前置敏感词检测能够防止违规问题进入后续流程,避免浪费系统资源,并保证不给出任何不当回答。

本文将围绕这一场景,深入探讨 RAG(Retrieval-Augmented Generation,即检索增强生成)系统中全链路的敏感词防护策略。从敏感词检测应当嵌入的关键时间点比较入手,说明为何选择前置检测;接着介绍敏感词词表的设计与维护方法;然后阐述如何在 LangChain 等 RAG 框架中实现敏感词的快速拦截策略;最后给出代码示例,演示基于 LangChain 的拦截逻辑以及示范性的敏感词表结构。

检测的关键时间点分析

在 RAG 系统中,可以在不同阶段对用户的提问进行敏感内容检测。常见的四个关键时间点及其优劣对比如下:

  1. 用户输入阶段(前置检测):在用户提交问题的瞬间立即进行敏感词过滤。这种方式最大的优点是即时性,可以第一时间拦截违规提问,避免进入后续繁重的检索和生成流程,节省系统开销。同时,用户能够快速得到反馈(固定的拒答话术),提升交互效率。缺点是依赖预先定义的词表或检测模型,可能存在漏检(用户用非常规表述绕过词表)或误拦截的风险。但总体而言,前置检测可以将风险扼杀在源头,是最直接有效的方案 。本方案也是本文重点推荐的策略。
  2. 知识检索前检测:在接收到用户输入后、执行 RAG 检索之前进行检测。它与前置检测在流程上仅一步之隔,如果未在输入阶段拦截,可以在向向量数据库或检索库查询之前再做一次检查。这样做依然能避免不必要的后续LLM 调用和长文本处理,但相比输入即拦截稍晚了一些,可能已经进行了诸如查询预处理等操作。其优点和缺点与前置检测类似:能够阻止违规内容进入模型,但仍会浪费少量检索前的计算。如系统对用户提问做了改写或扩展查询,那么在改写后检索前检测可以补充前置策略的不足。然而,如果可以在更早的输入阶段检测,通常没有必要等到检索前才拦截。
  3. LLM生成前检测:在检索到相关知识文档后、发送给大型语言模型生成回答之前进行检测。此时系统已经完成了向量检索、文档获取等工作,再检查一次提问(或连同检索结果一并检查)是否包含敏感内容。这样做的劣势在于已经消耗了检索资源和时间,一旦此时发现敏感词并终止,前面的开销都浪费了。此外,如果敏感内容明确出现在用户提问中,完全可以更早拦截,没有必要拖到这一步。唯一的潜在好处是在某些极端情况下,检索到的内容使得本不明显的问题变得敏感(但这种情况较少见)。总体而言,将检测放在生成前并不是理想方案,因为用户已经等待了较长时间,而最终仍然得不到回答。
  4. 模型生成后审核(输出后审核):在 LLM 给出回答之后,对回答内容进行审核过滤。这属于后置审核,常用于补充前置策略,确保最终输出没有违规信息。对于聊天机器人而言,输出审核可以拦截模型试图生成的不良内容。然而,它作为唯一手段时问题很大:首先,模型可能已经产生了违规回答(即使不展示给用户也存在合规风险和不良内容留存);其次,用户等待完整回答后才被告知不能提供,这会非常影响体验。在我们的场景下,如果用户的问题本身就违规,应该直接拒绝,而不应让模型生成再去过滤。因此,输出后审核更多用于收尾保障(防止意外疏漏),而非处理已知违规的提问。

综上比较,前置检测在及时性、成本和安全性上都具有明显优势。一旦识别出用户输入触及内幕消息等敏感内容,系统会立即返回预先定义的拒答话术,既保护了合规安全,又避免了不必要的资源浪费。因此,我们明确推荐将敏感词检测放在用户提问输入阶段,实现即时拦截。当然,这并不排除在最终输出处再加一道审核保险,但前置拦截应作为第一道防线。

敏感词词表的设计与维护方法

要实现前置敏感词检测,一个高质量的敏感词词库至关重要。词库的设计需要考虑格式、匹配策略以及维护更新机制:

  • 词表格式与结构:敏感词库通常采用结构化的格式存储,便于程序快速加载和查询。可以使用文本文件(如 CSV/TSV 每行一个词)、JSON/YAML(包含分类标签等元数据),或数据库表等方式组织。每个词条往往不仅仅是一个词,还可以包含一些属性,例如类别、级别、备注说明等。举例来说,我们的金融场景可以在词表中为每个关键词标注类别(如“内幕交易”“非法证券咨询”等)和严重级别,以便区分对待。采用键值结构或多列数据结构有助于灵活扩展,例如:{“词”: “内幕消息”, “类别”: “违规咨询”, “级别”: “高”}。词表加载后通常会转换为方便查询的数据结构(比如哈希集合或前缀树),以支持高效匹配。
  • 分类标签与策略:为词表中的敏感词打上分类标签,可以帮助系统根据不同类别采取不同策略。例如,金融违法类(内幕消息、操纵市场等)一旦匹配直接拒答;不当言论类(辱骂、骚扰等)可以提示用户文明用语;隐私数据类(身份证号、银行卡号等)可以进行打码处理等。通过标签分类,系统可以对不同类别的敏感内容定制回应话术或处理逻辑。在我们讨论的智能投顾场景中,大多数涉及法律法规的敏感词(如内幕信息)属于最高优先级,通常直接触发拦截和固定回复。然而,分类仍有意义:它方便后续统计分析哪些类别的违规咨询最常出现,以及在日志中明确拦截原因。
  • 模糊匹配与正则支持:用户往往会想方设法绕过简单的敏感词匹配,例如使用同音字、变形拼写、符号替换甚至拼音等手段。为提高检出率,词库设计需要考虑模糊匹配策略。常见做法包括:加入同义词和常见变体(例如“内幕消息”的同义表述“内部消息”、“小道消息”等),以及利用正则表达式匹配模式来捕捉变化(例如忽略空格和特殊符号的干扰)。针对中文,可能需要考虑谐音字和拼音匹配,例如用户将“彩票”故意写作“啋票”或“采漂”来逃避过滤,这两者发音与“彩票”相同 。为了应对这种情况,可以在词库中引入对应的谐音变体,或在检测逻辑中先将文本转换为拼音序列再匹配。此外,正则表达式可以用于匹配模式而非固定字符串,例如检测形如“内部.*消息”的短语,以覆盖“内部消息”“内部重要消息”等各种措辞。通过模糊匹配和正则扩展,能够最大限度地提高敏感词过滤的召回率,减少漏网之鱼。当然,这也需要权衡性能开销,必要时可借助算法优化(如 AC 自动机)提升匹配效率 。
  • 词库的维护更新(人工 + 自动):敏感词列表不是一成不变的,而需要随着业务和违规手段的演进不断更新。一方面,需要有人工运营/安全团队定期审查词库,增删调整词条。例如监管机构可能颁布新的禁止事项,或者发现用户又创造了新的黑话,都需要及时加入词库 。人工维护可以保证权威性和准确性。另一方面,可以结合自动化手段辅助更新:利用模型或统计方法分析用户提问日志,发现疑似敏感但未被拦截的内容。例如,如果某段时间出现多次绕过词表的违规提问,可以通过文本分类模型将这些提问标记出来,供人工确认后加入词库。这种人工+自动结合的维护方式,可以保持词库的及时性和完备性。另外,维护流程上应做好版本管理和灰度发布,例如逐步在部分流量上验证新词库的效果,避免误伤正常用户。总之,只有持续的维护迭代,敏感词词表才能真正发挥长期作用。

工业界常用基于 Trie字典树/AC自动机 的算法来实现高性能的敏感词匹配服务。这种算法能够在构建含所有敏感词的自动机后,以近似线性的时间复杂度扫描输入文本,找出其中出现的所有敏感关键词。对于词库规模很大(数十万到上百万词条)的情况,AC自动机尤其适用。同时也可结合一些模糊匹配策略(例如将含多音字的词提前拆解为各变体插入自动机)。如果词库较小,直接遍历匹配也可接受,但随着词库增长,建议引入上述算法来保证检测的准确性和效率。

在 RAG 框架中实现快速拦截策略

明确了将敏感词检测前置的策略后,我们需要将其融入到 RAG 系统的架构中,实现一旦触发敏感词就快速失败(fast-fail),不再走后续检索和生成流程。下面以典型的 RAG 流程为例,说明拦截策略在系统中的位置和工作机制:

典型 RAG 问答流程: 用户输入问题 → 系统从知识库中检索相关文档 → 将文档与问题一同交给大语言模型生成答案 → 返回答案给用户。我们要在这个链路中加入敏感词拦截,可以这样改造:

  1. 插入前置拦截节点:在用户提问进入检索组件之前,增加一个敏感词检测节点。这个节点接收用户输入文本,利用前述的词表和匹配算法进行检测。
  2. 判断是否违规:如果检测节点发现输入中包含受保护的敏感词或命中违禁话题,则立即标记为违规。
  3. 触发快速失败:一旦判定违规,系统不再执行后续的向量检索、LLM 调用等步骤,而是直接走快速失败路径:生成一个预设的响应给用户。例如输出固定的话术:“很抱歉,您的提问涉及敏感信息,无法为您提供解答。”并结束流程。
  4. 正常路径继续:如果检测节点未发现敏感词,则认为输入安全,进入正常的 RAG 检索流程,获取相关知识,再进行答案生成。
  5. 输出及后续:对于前置拦截的情况,输出统一的拒答内容;对于正常回答的情况,模型生成后也可以通过最终的内容审核,以防生成中提及了不应提供的信息(这通常针对输出中的敏感、违规内容,属于另一层面的安全检查,不是本文重点)。

上述流程确保了当用户提出非法咨询时,系统会短路掉主要流程,既保障了响应及时,又避免了模型可能产生不良回答。从系统结构上看,这种实现相当于给 RAG pipeline 加了一层Gate(闸门)。只有输入通过 Gate(未命中敏感词),才会放行进入主干道;否则直接分流到安全出口。

在实际工程实现上,这种前置拦截有多种方式:

  • 外层条件判断:最简单的做法是在主链路调用前增加一个条件判断。例如,用伪代码表示:
if contains_sensitive(user_input):
    return PRESET_RESPONSE
else:
    result = RAG_chain(user_input)  # 正常执行检索和生成
    return result

这种方式清晰直观,也便于在任何框架中实现。对于使用 LangChain、Haystack 等现成框架的项目,也可以在调用链之前手动加这一判断逻辑。

  • 链路中集成拦截节点:如果希望将逻辑整合到 RAG 流程内部,可以自定义一个链(Chain)或工具(Tool)用于敏感词检测,然后将其插入到多步骤流程中。例如在 LangChain 中,可以创建一个自定义的 Callable(比如 RunnableLambda)用于检查输入并决定走向:当检测通过时,返回原始输入以供后续步骤使用;当检测不通过时,直接返回封装好的拒绝回答。这需要仔细设计以确保后续不会再处理已被拦截的请求。
  • 使用管道/中间件模式:有些框架支持在请求处理链中插入中间件函数。我们可以利用这种机制在进入主要逻辑前进行敏感词过滤。例如 LangChain 0.0.** 系列提供了 Runnable 序列功能,可以将多个步骤用管道拼接。我们可以将敏感词检测封装为一个 Runnable,在管道最前面执行 。如果结果显示不通过,则管道直接输出预设响应;如果通过,则将原始输入传递给下一个检索步骤。需要注意的是,在管道内实现“短路”需要让检测步骤能够控制后续执行流,可以通过抛出特定异常或者返回特殊标识来实现。

无论采用哪种集成方式,核心要点是在尽可能早的阶段完成安全校验,将不良输入拒之门外。同时要确保当拦截发生时,系统返回的固定话术正确无误且符合业务要求。对金融场景而言,这个话术通常经过合规团队审核,例如:“抱歉,根据监管要求,我无法回答您的提问。” 或 “很抱歉,您的问题涉及敏感信息,无法为您提供帮助。” 这样的回应既礼貌又坚定,既向用户传达了无法服务的原因,也避免透露任何多余信息。

值得一提的是,除了自建敏感词拦截机制,一些现有的大模型内容安全方案也可以在输入阶段发挥作用。例如 Meta 的 LlamaGuard 等工具,可以对用户输入进行有害内容检测 。这些通用方案可以作为辅助,但在金融合规场景下,仍需依赖精细定制的词表来识别特定违规咨询。因此综合运用多种手段才能构筑完善的安全防线。

代码实现示例

下面通过简化的代码片段,展示如何在 LangChain 框架中实现前置敏感词检测拦截,以及敏感词词表的组织示例。此示例假设我们已经有一个 RAG 问答链(如 RetrievalQA)以及相应的向量检索器和 LLM,重点在于插入敏感词过滤逻辑。

# 1. 定义敏感词列表和检测函数
sensitive_words = ["内幕消息", "内幕交易", "非法集资", "操纵市场", "洗钱"]  # 示例敏感词列表
# 为提高匹配准确度,可将列表中的每个词转换为正则模式或统一大小写等,这里简单直接匹配
def contains_sensitive(text: str) -> bool:
    """判断文本是否包含任一敏感词"""
    for w in sensitive_words:
        if w in text:
            return True
    return False

# 2. 构建 RAG QA 链(假设已初始化 retriever 和 llm)
from langchain.chains import RetrievalQA
qa_chain = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever)

# 3. 在提问时进行前置检测并实现 fast-fail 拦截
user_query = "请提供关于某股票的内幕消息"  # 用户输入示例
if contains_sensitive(user_query):
    response = "很抱歉,您的提问涉及敏感信息,无法提供解答。"  # 预设固定回应
else:
    # 未命中敏感词,执行正常的 RAG 流程
    response = qa_chain.run(user_query)

print(response)

上述代码首先定义了一个简单的敏感词列表和检测函数contains_sensitive。实际应用中,这个函数可以扩展为支持正则、大小写忽略、谐音转换等复杂匹配逻辑,并可从外部配置加载词库。然后,我们构建了一个 LangChain 提供的 RetrievalQA链来执行知识检索和问答。在真正调用链处理用户问题之前,我们用一个 if 判断检查 user_query 是否包含敏感词。如果是,则直接将 response 设置为预设的拒答话术,跳过 qa_chain 的执行;如果没有,则正常调用链获取回答。

在更高级的用法中,我们也可以利用 LangChain 的 RunnableLambda 或自定义 Chain 将上述逻辑封装,使之成为流水线的一部分。例如:

from langchain.schema import RunnableLambda, RunnableSequence

# 封装敏感词检测为一个 RunnableLambda
filter_runnable = RunnableLambda(lambda query:
    "很抱歉,您的提问涉及敏感信息,无法提供解答。" if contains_sensitive(query) else query
)

# 将检测步骤与 QA 链组合为一个流水线
pipeline = RunnableSequence([filter_runnable, qa_chain])
result = pipeline.invoke(user_query)

以上片段中,我们构建了一个 filter_runnable,当输入含敏感词时直接输出固定回复,否则原样返回输入。然后利用 RunnableSequence 将过滤和QA链串联成流水线。需要注意,如果 filter_runnable 返回的不是原始查询而是拒答字符串,那么紧随其后的 qa_chain 可能会把它当作新问题处理。因此,为真正实现短路,可以对 RunnableSequence 进行更复杂的定制,例如让过滤步骤返回一个特殊对象并在流水线中判断。但出于示范目的,此处简化处理,重点在于展示拦截逻辑的嵌入。

通过上述代码,我们实现了在用户提问阶段进行敏感词实时检测的完整链路。当用户输入诸如“内幕消息”等非法咨询时,系统会立即返回固定回复而不进行任何模型推理;当输入正常问题时,系统则按常规流程检索知识并生成答案。这种前置敏感词防护策略为金融场景的智能投顾系统筑起了一道坚实的安全屏障,在保障合规的同时也提升了系统效率和用户体验。

结语

在大型语言模型赋能的金融智能投顾应用中,内容安全与合规性是不容忽视的重点。本文通过“RAG 系统中的敏感词防护策略:从前置检测到即时拦截的全链路实践”这一主题,详细探讨了如何在系统中部署敏感词过滤机制以拦截违规提问。我们比较了不同阶段引入检测的优劣,阐明了前置检测的优势;介绍了高质量敏感词词表的设计和迭代维护方法,以应对不断翻新的规避技巧 ;并给出了在 LangChain 框架下落地快速失败拦截的实现思路与代码示例。

通过将敏感词检测前移,我们能够在毫秒级内响应不良提问,避免无谓的资源消耗和潜在的违规回答 。这一实践对于任何注重合规的对话式 AI 系统都具有参考意义。展望未来,我们可以进一步结合大模型的内容理解能力,探索智能化的敏感内容检测(例如利用小型分类模型识别隐晦提问意图),以及更细粒度的策略(比如根据违规严重程度决定是直接拒答还是给予警告)。但无论技术如何演进,建立在完善词库和明确规则基础上的前置拦截,依然会是守护 AI 应用安全的第一道也是最关键的一道防线。

Ge Yuxu • AI & Engineering

脱敏说明:本文所有出现的表名、字段名、接口地址、变量名、IP地址及示例数据等均非真实, 仅用于阐述技术思路与实现步骤,示例代码亦非公司真实代码。 示例方案亦非公司真实完整方案,仅为本人记忆总结,用于技术学习探讨。
    • 文中所示任何标识符并不对应实际生产环境中的名称或编号。
    • 示例 SQL、脚本、代码及数据等均为演示用途,不含真实业务数据,也不具备直接运行或复现的完整上下文。
    • 读者若需在实际项目中参考本文方案,请结合自身业务场景及数据安全规范,使用符合内部命名和权限控制的配置。

版权声明:本文版权归原作者所有,未经作者事先书面许可,任何单位或个人不得以任何方式复制、转载、摘编或用于商业用途。
    • 若需非商业性引用或转载本文内容,请务必注明出处并保持内容完整。
    • 对因商业使用、篡改或不当引用本文内容所产生的法律纠纷,作者保留追究法律责任的权利。

Copyright © 1989–Present Ge Yuxu. All Rights Reserved.