Spring AI 流式对话系统:基于DFA算法的分级敏感词过滤与全链路合规设计

📅 2026/7/8 3:02:02
Spring AI 流式对话系统:基于DFA算法的分级敏感词过滤与全链路合规设计
Spring AI 流式对话系统基于DFA算法的分级敏感词过滤与全链路合规设计前言在AI对话类应用的落地过程中内容安全是不可绕过的核心环节。单一的“命中即拦截”策略会严重损伤用户体验而完全依赖第三方内容安全接口又会带来高昂的调用成本与响应延迟。本文基于 DFA 确定性有限自动机算法实现了一套分级敏感词初筛机制并与 Spring AI WebSocket 流式对话流程深度结合构建了“输入初筛分级处置 输出二次合规校验”的全链路安全方案在保障合规的同时兼顾了对话的流畅性与系统性能。一、整体架构设计思路整套合规体系围绕 AI 对话的完整生命周期设计分为三个核心阶段输入侧本地初筛严重违规内容直接拦截并返回提示终止流程同时记录审计日志一般违规内容自动替换为*号脱敏继续进入对话流程高风险疑似内容调用第三方内容安全服务做深度检测不通过则拦截模型流式交互通过 Spring AI 调用大模型以 WebSocket 分片推送的方式实现流式响应输出侧二次校验大模型生成完整回复后再次执行敏感词检测若命中严重违规则触发消息撤回机制该架构的核心优势在于绝大多数合规请求通过本地 DFA 算法毫秒级完成校验仅少量边缘 case 走第三方深度检测在合规性、成本、性能三者之间取得平衡。二、核心实现分级DFA敏感词过滤器2.1 风险等级枚举定义首先定义三级风险等级作为全链路处置的统一标准publicenumRiskLevel{/** * 严重违规直接拦截 */SEVERE,/** * 一般违规替换脱敏 */NORMAL,/** * 合规 */PASS}2.2 DFA 分级字典树过滤器DFA 算法的核心是通过字典树Trie结构存储敏感词将文本扫描的时间复杂度降低到 O(n)。我们在传统实现的基础上在词尾节点存储风险等级从而支持分级处置能力。importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importjavax.annotation.PostConstruct;importjava.util.*;Slf4jComponentpublicclassSensitiveWordFilter{/** * DFA 字典树根节点 */privatefinalMapCharacter,MaprootnewHashMap();/** * 敏感词替换符 */privatestaticfinalcharREPLACE_CHAR*;/** * 模拟敏感词库生产环境建议从数据库/配置中心动态加载 * key 敏感词value 对应风险等级 */privatestaticfinalMapString,RiskLevelSENSITIVE_WORD_MAPMap.of(严重违禁词1,RiskLevel.SEVERE,严重违禁词2,RiskLevel.SEVERE,普通违规词1,RiskLevel.NORMAL,普通违规词2,RiskLevel.NORMAL);/** * 服务启动时构建带等级标记的 DFA 字典树 */PostConstructpublicvoidinit(){buildTire(SENSITIVE_WORD_MAP);log.info(敏感词过滤器初始化完成共加载{}个敏感词,SENSITIVE_WORD_MAP.size());}/** * 构建带风险等级的字典树 */privatevoidbuildTire(MapString,RiskLevelwordLevelMap){root.clear();for(Map.EntryString,RiskLevelentry:wordLevelMap.entrySet()){Stringwordentry.getKey();RiskLevellevelentry.getValue();if(wordnull||word.isEmpty()){continue;}Mapcurrentroot;for(inti0;iword.length();i){charcword.charAt(i);Mapnext(Map)current.get(c);if(nextnull){nextnewHashMap();current.put(c,next);}currentnext;// 在词的结束位置存入风险等级替代传统的 isEnd 标记if(iword.length()-1){current.put(level,level.name());}}}}/** * 检测整段文本的最高风险等级 * 采用最长匹配原则命中严重违规时直接返回提前终止扫描 */publicRiskLevelgetMaxRiskLevel(Stringtext){if(textnull||text.isEmpty()){returnRiskLevel.PASS;}RiskLevelmaxLevelRiskLevel.PASS;for(inti0;itext.length();i){Mapcurrentroot;intji;RiskLevelmatchedLevelnull;while(jtext.length()){charctext.charAt(j);current(Map)current.get(c);if(currentnull){break;}// 匹配到敏感词结尾记录当前等级if(current.containsKey(level)){matchedLevelRiskLevel.valueOf((String)current.get(level));}j;}// 更新整段文本的最高风险等级if(matchedLevel!nullmatchedLevel.ordinal()maxLevel.ordinal()){maxLevelmatchedLevel;// 性能优化命中严重违规直接返回无需继续扫描if(maxLevelRiskLevel.SEVERE){returnRiskLevel.SEVERE;}}}returnmaxLevel;}/** * 快捷判断是否包含严重违规内容 */publicbooleanisSevereViolation(Stringtext){returnRiskLevel.SEVERE.equals(getMaxRiskLevel(text));}/** * 按最长匹配原则将所有敏感词替换为 * 号 */publicStringreplaceSensitiveWord(Stringtext){if(textnull||text.isEmpty()){returntext;}char[]charstext.toCharArray();for(inti0;ichars.length;i){Mapcurrentroot;intji;intmatchEndIndex-1;while(jchars.length){charcchars[j];current(Map)current.get(c);if(currentnull){break;}if(current.containsKey(level)){matchEndIndexj;// 记录最长匹配的结束位置}j;}// 命中敏感词将区间内所有字符替换if(matchEndIndex!-1){for(intki;kmatchEndIndex;k){chars[k]REPLACE_CHAR;}imatchEndIndex;// 跳过已匹配区间避免重复扫描}}returnnewString(chars);}/** * 动态刷新敏感词库无需重启服务 */publicvoidrefreshWordMap(MapString,RiskLevelnewWordMap){buildTire(newWordMap);log.info(敏感词库已刷新共{}个词,newWordMap.size());}} 设计要点采用最长匹配原则避免短词优先导致的漏判对严重违规场景做提前返回优化大幅提升高风险场景下的检测效率支持动态刷新词库适配运营侧快速迭代的合规需求。2.3 统一校验结果封装基于过滤器能力封装统一的输入校验入口返回标准化的处置结果publicFilterResultcheckInput(Stringtext){RiskLevellevelgetMaxRiskLevel(text);if(levelRiskLevel.SEVERE){returnFilterResult.block(您的内容包含违规信息无法发送);}if(levelRiskLevel.NORMAL){StringsafeTextreplaceSensitiveWord(text);returnFilterResult.passWithReplace(safeText);}returnFilterResult.pass();}三、Spring AI 流式会话中的全链路集成敏感词校验不是独立的工具模块需要深度嵌入对话的完整生命周期。以下是基于 WebSocket Spring AI 的流式会话服务实现覆盖输入校验、上下文管理、流式输出、输出校验、上下文持久化全流程。importlombok.RequiredArgsConstructor;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.web.socket.WebSocketSession;importreactor.core.publisher.Flux;importjava.util.List;Slf4jComponentRequiredArgsConstructorpublicclassAiSessionService{privatefinalStringRedisTemplateredisTemplate;privatefinalChatModelchatModel;privatefinalSensitiveWordServicesensitiveWordService;privatestaticfinalStringCONTEXT_KEYai:context:;privatestaticfinalintMAX_HISTORY_ROUND10;/** * WebSocket 流式对话主入口 */publicvoidstreamChat(LonguserId,WebSocketSessionsession,Stringcontent){// 1. 输入侧敏感词分级校验SensitiveWordService.FilterResultresultsensitiveWordService.checkInput(content);if(result.isBlocked()){WsMessageUtil.send(session,ai_error,result.getMsg());return;}StringsafeContentresult.getSafeContent();// 2. 从 Redis 加载历史上下文维护多轮对话能力ListMessagehistoryloadContext(userId);history.add(newUserMessage(safeContent));truncateHistory(history);// 超出最大轮次则截断旧消息// 3. 调用大模型流式分片返回PromptpromptnewPrompt(history);FluxChatResponsefluxchatModel.stream(prompt);StringBuilderfullAnswernewStringBuilder();flux.doOnNext(resp-{Stringchunkresp.getResult().getOutput().getContent();if(chunk!null){fullAnswer.append(chunk);// 逐字推送给前端实现打字机效果WsMessageUtil.send(session,ai_chunk,chunk);}}).doOnComplete(()-{// 4. 输出侧二次合规校验SensitiveWordService.FilterResultoutputResultsensitiveWordService.checkOutput(fullAnswer.toString());if(outputResult.isBlocked()){WsMessageUtil.send(session,ai_withdraw,outputResult.getMsg());return;}// 5. 校验通过将本轮对话存入 Redis 上下文history.add(newAssistantMessage(outputResult.getSafeContent()));saveContext(userId,history);WsMessageUtil.send(session,ai_end,);}).doOnError(e-{log.error(AI调用异常 userId:{},userId,e);WsMessageUtil.send(session,ai_error,生成失败请稍后重试);}).subscribe();}/** * 从 Redis 加载历史对话上下文 */privateListMessageloadContext(LonguserId){// 实现逻辑从 Redis Hash/String 中读取并反序列化为 Message 列表}/** * 保存对话上下文到 Redis */privatevoidsaveContext(LonguserId,ListMessagehistory){// 实现逻辑序列化后写入 Redis设置过期时间}/** * 截断超长历史控制 Token 消耗 */privatevoidtruncateHistory(ListMessagehistory){if(history.size()MAX_HISTORY_ROUND*2){history.subList(0,history.size()-MAX_HISTORY_ROUND*2).clear();}}} 关键设计输出侧校验放在流式生成完成后执行既保证了前端的打字机响应速度又确保最终留存的上下文内容合规若校验命中严重违规通过ai_withdraw指令通知前端撤回已展示的内容避免违规信息扩散。四、方案亮点与优化方向4.1 核心优势分级处置更灵活摒弃“一刀切”的拦截模式对轻微违规内容采用脱敏处理在合规前提下最大程度保留用户体验高性能低延迟DFA 算法本地毫秒级完成检测不依赖外部接口完全不会拖慢对话响应速度全链路覆盖输入、输出双端校验既管控用户侧违规输入也规避大模型生成风险内容可扩展性强词库支持动态刷新可无缝接入第三方内容安全接口作为二级检测能力4.2 后续优化方向增加白名单机制避免通用词汇被误拦截支持中英文混合匹配、大小写不敏感检测引入相似度算法规避变形敏感词谐音、拆字、加符号增加审计日志与命中统计支撑运营侧的词库迭代总结基于 DFA 算法的分级敏感词过滤方案是 AI 对话系统内容安全体系中性价比极高的第一道防线。它既能独立完成大部分场景的合规校验也可以作为第三方安全服务的前置初筛层有效降低调用成本。结合 Spring AI 的流式能力与 Redis 上下文管理最终可以构建出既安全合规、又具备流畅用户体验的智能对话系统。标签Java, Spring AI, DFA算法, 敏感词过滤, WebSocket流式对话