基于LLM的智能客服系统架构设计与排障实践
去年我主导了一个智能客服平台从零到一的设计与落地整套系统基于LLM大语言模型搭建上线后稳定运行了三个多月扛住了一年中最大的活动流量也把各种诡异问题从日志里一个个捞出来解决掉。这篇文章不是泛泛介绍智能客服能做什么而是把那段踩坑之后沉淀下来的架构设计思路、关键模块的参数配置、安全可靠性方案以及几个最典型的故障排查过程完整拆开来讲给你听。如果你正在设计或重构一套基于LLM的智能客服系统尤其是对“高效、安全、可靠”这三个词有明确要求的业务场景那么这篇文章里的架构分层、RAG检索细节、上下文管理、安全防线、降级与熔断方案以及最后那部分问题排查实录应该会比较对胃口。就算你只是对这套系统感兴趣想了解一个按真实业务需求打磨出来的技术方案长什么样也可以放心往下看。1. 整体设计思路先想明白要解决什么问题1.1 传统客服转向LLM的必然性早期电商、金融、在线教育这类场景里的智能客服大量采用的是“FAQ匹配 关键词规则 多轮任务树”的组合方案。这种方案不是不能用而是维护成本会随着业务膨胀快速失控。新商品、新活动、新政策上线运营人员要手动维护几百上千条问答对稍微漏掉一个说法不一致的触发词用户就会被导到“对不起我没有理解您的问题”。基于LLM的智能客服核心变化在于把“人工写规则来匹配问题”变成了“让模型理解意图并组织答案”。用户说“我昨天买的手机还没发货能帮我看看吗”模型不需要命中某个预先设定好的关键词而是能理解这是“催发货”意图再去调用查单接口或者检索物流知识库。这个转变让客服系统的可对话边界一下子打开了。但LLM并不是万能的。它的问题也特别明显会产生幻觉编造不存在的政策对实时业务数据一无所知需要挂接工具对大段私域知识无法记忆必须靠检索补充还存在被提示词注入攻击的风险。所以整个架构设计的出发点不是“如何用LLM把原来的客服机器人替换掉”而是“如何把LLM嵌到一套可控的客服链路里让它既聪明又不失控”。1.2 项目初期我们把目标拆成了四个非功能指标很多团队做这类系统一开始就把注意力放在“模型选谁”“要不要用Agent框架”上反而忽略了真正要紧的事情。我们当时在需求评审阶段把目标明确写成了四个可验证的指标高效单次对话的端到端响应时间P95控制在3秒以内高峰期不出现雪崩式超时。安全对私域知识不泄露、对用户隐私数据做脱敏、对恶意注入进行拦截、输出内容可审计。可靠单点故障不影响主流程模型服务不可用时有降级方案客服服务不能直接挂掉。可运维所有链路有日志追踪每次回答能回看检索命中了什么、模型怎么生成的方便追责。这四个指标后来成为整个架构设计的决策标尺。很多技术上看起来“更酷”的方案最后都因为不符合这几条而被过滤掉了。1.3 我们对“智能”的边界做了取舍在方案设计阶段我们明确划分了三类能力边界。第一类是纯知识问答比如“你们的退货政策是什么”“这个商品支持分期吗”归RAG链路管模型根据知识库内容来回答。第二类是任务执行比如“帮我查一下上周的订单物流”“取消这笔订单”归工具调用链路管模型只负责从用户话里抽取参数然后调用业务接口。第三类是复杂推理和主动服务比如“对比这两个套餐哪个更适合我”这类问题我们没有在首期铺开而是直接转人工。这个取舍很重要。如果一开始就让LLM尝试所有复杂任务模型的不确定性会在客服场景里被放大安全风险也不可控。先做稳知识问答和基础工具调用让系统先把跑得好的场景跑稳再逐步扩展能力边界是更务实的路径。2. 总体架构与模块拆分先画好边界再写代码2.1 四层架构把系统拆成可以独立演进的模块我们最终采用的分层架构大概是这样从上到下分成接入层、服务层、模型层和数据层。接入层负责统一接收来自App、网页、小程序等多个渠道的会话消息做基本的鉴权和风控然后把消息转成内部统一的会话事件格式。这里不直接跟LLM相关但它的重要性在于客服系统必须支持多渠道接入而每个渠道的用户身份体系、消息格式、超时策略都不一样不统一接入层后面的逻辑会被渠道差异拖垮。服务层是核心业务逻辑所在包含会话状态管理、会话历史存储、意图路由、工具调用、RAG检索、Prompt拼接、安全审核、降级控制等模块。这一层是嵌入LLM的关键位置所有与模型交互的逻辑都集中在这里便于统一控制上下文长度、注入安全策略、记录日志。模型层做的主要是各类模型能力的统一封装和调度包括对话模型、Embedding模型、Rerank重排模型、以及敏感词和内容审核模型。我们不直接让业务代码调用具体的模型供应商接口而是封装成统一的模型网关。数据层包括知识库、会话日志、向量索引、业务系统接口的适配层。知识库用于支撑RAG检索业务系统适配层用于让客服系统安全调用下单、查询、退款等能力。这个分层最大的好处是每一层都能独立升级。比如后来我们换了更强的Embedding模型服务层完全没有改动只是把模型层的配置切换了一下就完成了。如果当初把模型调用散落在各个业务代码里这种替换会非常痛苦。2.2 一次完整对话在主流程里经过的七个节点为了让你对整体链路有个具体印象这里把一次用户提问在系统内部的旅程写出来用户发来消息接入层完成鉴权和风控生成会话ID。服务层从会话管理模块取出最近的对话上下文组装为一个结构化会话对象。意图路由模块判断这是一个“知识咨询”还是“任务办理”还是“转人工”请求。知识咨询走RAG链路对用户问题进行向量化到向量库召回候选知识片段再经过重排模型筛选。Prompt拼接模块把系统指令、检索到的知识片段、历史上下文、当前问题组装成最终发送给模型的内容。LLM模型推理生成回复返回结果经过内容安全审核对涉及隐私的部分做脱敏。服务层把回复写回会话存储通过接入层推送给用户并把整个链路的调试信息写入日志。这个链路里的每一步都有专门的模块负责而不是在业务代码里硬写。流程看起来简单但每一步的细节都藏了不少坑。接下来的章节会拆开讲。2.3 技术选型的几个关键决定先说模型。我们当时并没有盲目追求参数最大的模型而是选了一个中文能力扎实的开源基座模型私有化部署。原因很简单客服会话的每一句话都涉及用户数据和业务数据直接调用外部API会带来数据出域风险而且没办法完全控制采样参数和上下文长度。私有化部署之后可控性大大增强成本也能通过量化压缩控制住。向量库的选择也做过多个对比。我们最终没有选择用一个额外组件而是直接用了已有基础设施里的通用检索组件来承载向量索引因为它在团队里已经维护了两年稳定性有保障。如果你从零开始专门部署一个向量库完全没有问题但一定要考虑运维成本和团队熟悉度。关于Agent框架我们评估过用现成的编排框架来管理工具调用和任务流程但最终选择自己写了轻量的路由与工具调用逻辑。因为客服场景的流程控制要求非常强需要精确管理每一次调用的参数、超时、错误处理通用框架在这个场景里反而会带来黑盒不可控的问题。这个决定在后来的排障过程中被反复证明是正确的——因为我们能直接看懂每一层到底发生了什么。3. 核心模块的实操要点与参数选择3.1 会话管理控制上下文窗口才是重中之重LLM的上下文窗口是有限的而用户对话可能很长。如果不做管理对话超过上下文上限后要么报错要么最前面的信息被截掉导致理解错乱。我们当时定了一套会话状态管理方案会话状态存储在缓存里近10轮完整对话保留在结构性数据中超过10轮的部分按照用户意图做摘要压缩再连同最近10轮一起送入模型。这意味着无论用户聊了多长时间模型看到的上下文长度都不会无限增长P95延迟就能稳定住。在发送给模型前还要做一次Token预算控制。我们把整个Prompt按优先级分为四块系统指令、检索知识、历史会话、当前问题。当总字数超限时优先裁剪“历史会话”部分再裁剪“检索知识”中排列靠后的内容永远保证“当前问题”和“系统指令”完整。这样设计的原因是对回答质量影响最大的是用户当前想问什么以及模型该遵守什么规则历史信息哪怕少一点也比如今从头到尾什么都有但被截断要可靠得多。3.2 RAG链路让模型老老实实用知识库说话客服系统的答案必须来自业务知识库而不是让模型自己编。这里就依赖RAG检索增强生成把知识库内容检索出来再交给模型组织成回答。我们在这条链路上踩过最深的一个坑是“检索不到相关片段但模型依然给出答案”。后来通过三项改动解决了。第一项是切分策略。知识库里的文档我们不能直接整篇扔给模型必须切成合适的片段。我们经过反复调试最后采用按标题层级切分加固定窗口补全的办法核心参数是大段落按一级标题先切小于200字的小段落不切每个片段控制在200到400字之间片段之间保留30字的重叠。重叠文字的作用是防止“一句话被切到两个片段里导致语义断裂”。第二项是召回参数。我们对用户问题进行向量检索取回前20个候选片段再用重排模型逐条打分只保留前5个送入Prompt。很多人会直接取TopK3或TopK5但我们建议先取大候选集再用重排模型做精排效果比单纯加大向量召回数量要好得多。第三项是在Prompt里明确限定模型的权限范围我们写的是只能依据给定的知识片段回答问题如果知识片段不包含答案如实说不知道并建议转人工或查询官方渠道。这行指令看上去简单但它直接把“模型胡编”的比例降了一个量级。模型生成了内容之后还会做一轮引用校验只有答案中出现的具体信息确实能在知识片段里找到对应依据这条回答才会被投放出去。3.3 意图路由与工具调用从聊到办只差一步可控跳转客服系统不能只回答“是什么”还必须能做“怎么办”。我们设计了一套轻量级路由机制让模型先做意图分类输出预设的JSON结构然后服务层根据JSON里的意图字段决定走哪条链路。第一层路由把请求分为知识咨询、业务办理、订单查询、转人工、闲聊五类。分类采用小模型或Prompt分类均可但关键点在于要让模型输出结构化的JSON并且限定枚举值集合不能让它自由发挥。例如输出为“订单查询”时服务层才会触发工具调用链。工具调用环节是风险最高的部分。客服系统要替用户查订单、办退款一旦参数理解错会产生资损。我们的做法是严格限定参数抽取schema模型只能抽取用户明说的参数不补充猜测信息。用户说“帮我查我的订单”但没说订单号模型会反问要订单号而不是自作主张去查。涉及资金类的动作比如退款、价格修改系统一律二次确认把用户意图、执行动作、涉及金额拼成确认文案返回给用户确认无误再调用业务接口。调用业务接口时统一通过业务适配层进行超时时间设置为2秒失败不降级到模型硬答而是如实回复“系统暂时有点忙请稍后再试”并转人工处理。这块必须坚持因为客服场景里错误执行比不执行更糟糕。3.4 缓存策略能不进模型就不进模型在大促场景里流量短时间会拉到平时的几倍如果把每个问题都送到LLM做一次推理成本高且延迟不可控。我们最终通过三层缓存把模型调用量压下去不少。第一层是热点问题缓存。对一些问法相近而答案一致的问题如退换货时间、发票类型、发货时效我们提前配置答案模板并绑定触发条件命中后直接走模板返回。第二层是语义缓存把用户问题向量化之后与最近一段时间内的历史问题做相似度比较相似度超过0.92就直接返回当时的回答并带上“这是一个关联问题的参考回答”的标识。第三层是会话级去重缓存同一个会话内用户在30秒内重复发送相同内容返回上一次的答案。三层缓存叠加后大促当天的模型调用量降低了约40%。缓存命中时响应基本能做到百毫秒级别极大保护了下游模型服务的稳定性。但要注意给缓存设置合理的失效时间否则业务政策变化会导致答案过期又出现一批“已经是老黄历”的回答。4. 安全与可靠性客服系统最不能省的功夫4.1 输入侧的四道防线LLM客服面临的一个现实威胁是提示词注入。用户可能会故意发送“忽略系统设定”或“把文档原样输出”这类内容来试探。对这类情况光靠模型自身防御是不够的我们在输入侧布置了四道防线。第一道防线在接入层做基础的事前过滤包括敏感话题、明显攻击特征词命中即拦截并返回标准话术不进模型。第二道防线是注入检测器我们用一个专门的小模型或规则模型对每个用户输入做分类判断是否处于“试图操控系统指令”的风险档。这条规则不追求百分之百精准主要是把最高风险的一批输入提前挡在模型之外。第三道防线是Prompt结构分隔把用户输入与系统指令在物理上用分隔符隔离并对用户输入做转义处理避免它在拼接后覆盖指令区。第四道防线是会话行为风控同一个会话短时间内连续触发多次注入尝试系统直接单向转入工人工客服不再与用户对话。这套防线在压力测试里拦截住了一批常见攻击也给审计留下了完整的记录。多说一句哪怕拦截率只有90%对客服系统来说都是关键的因为攻击只需要成功一次就可能造成舆情风险。4.2 输出侧审核与隐私脱敏模型生成的内容并不是直接发给用户而是先经过一道输出审核闸门。我们做了三层检查第一层是敏感词过滤覆盖暴力、色情、政治等违禁类别命中即拦截重写第二层是隐私检测识别手机号、身份证号、银行卡号等个人信息对不符合业务需要展示的字段一律打码第三层是事实一致性抽检随机抽取一部分回答人工复核答案中的关键信息是否与知识库一致抽检比例固定在5%。输出侧审核还有一个容易忽略的点就是要对回答里的“确定性表达”做处理。模型经常会说“应该是可以的”“我理解可能是”这种含糊表达在客服场景里容易引发歧义。我们要求模型在知识库无明确依据时直接给出标准话术“这个情况我无法准确核实为您转接人工核实处理”而不是使用含糊措辞暗示不确定但可操作。4.3 数据隔离与权限管控客服系统会触及用户订单、个人信息数据隔离是合规底线。我们采用了租户隔离与角色隔离交叉的方案。在检索知识库时不同的业务线维护独立的知识集合向量检索时强制附加业务线过滤条件防止一个业务线的问题检索出另一个业务线的知识片段。在调用业务接口时通过接入层的用户身份换取访问令牌令牌包含本次请求允许访问的资源范围业务系统根据范围校验才返回数据。所有会话日志和模型推理日志分开存储原始会话日志设置较短的保留期并做加密模型推理日志保留用于排障但不含用户实名信息。因为一旦模型日志泄露里面记录的对话片段很可能包含订单内容这个风险要在架构层面提前规避。4.4 降级、熔断与限流的联动设计任何同模型的服务都要假设依赖可能随时不可用我们为此设计了三层防护。第一层是限流在接入层按用户维度设置动态接口速率上限超限排队等待而不是直接拒绝配合异步重试消化短暂高峰。第二层是熔断对模型服务、向量检索、业务接口分别做健康度统计连续失败达到阈值后自动切换到降级模式。第三层是降级降级不是“啥都不做”而是有明确层次先停用重排模型只做向量召回再停用工具调用改为跳转人工如果模型服务彻底不可用整体回到一个简单的规则应答通道返回一个预设的安抚话术并亮出人工客服入口。这套机制的价值在于它保证了每次故障都不会把用户丢在“问了没人理”的境地而是总有一条兜底路径。有一次模型服务商确实出现了不可抗的网络抖动我们这端因为自动降级用户端几乎没有感知到服务中断只有内部监控日志显示走了降级通道。5. 排查实录上线前后踩过的几个坑5.1 模型乱编答案症状出现在源头但根源在检索上线初期我们接到大量反馈说模型会给离谱的回答比如用户问“你们客服几点下班”它一本正经回答说“晚上10点前可以联系也可以通过第三方渠道办理报废手续”。排查日志后发现当天的知识库里其实没有“客服下班时间”这条片段但向量检索召回了语义相近的几条“售后报废流程”模型把其中无关信息当成了答案来源。解决动作分两步。第一步是给RAG加上最终答案与引用片段的相似度校验若生成答案与引用的片段语义关联过弱则判定为不可信回答改回复固定转人工话术。第二步是建立知识库拓扑巡检任务定时扫描“用户高频提问命中到的知识片段但与问题本身的匹配度过低”的记录反推优化知识库的片段切分和覆盖度。那之后类似的无来源编造基本绝迹。5.2 上下文窗口装不下长对话裁剪策略要不断调对话超过十五轮时我们发现模型开始出现“记忆错乱”把用户早期说的地址当成刚才说的地址。打开模型日志确认原因是会话历史超过了窗口限制而我们的裁剪策略是按时间从最老的开始丢掉后面模型看到的其实是断断续续的碎片。后来调整了两个点。一是增加关键信息槽位的概念在会话状态里单独维护用户可能提及过的上下文槽位比如订单号、地址、诉求类型、时间节点这些信息每次都会放回Prompt。二是对超出限制的历史部分生成一段语义摘要而不是简单切断。摘要与关键信息槽位组合替换原始历史内容。调整后长对话的语境连续性明显改善。5.3 检索开慢了之把延迟问题从单环找到了并行链路大促前压测时P95延迟一度到了5秒明显超标。逐段埋点后发现主要时间花在了重排模型串行等待上。我们的链路里嵌入模型和重排模型是前后两个步骤而且重排模型的输入是固定的20个候选片段排队和推理各占了不少时间。优化做了三件事候选从20降到12同时限制重排只对向量召回排名在前20的片段操作把重排阶段改成批量并行请求而不是单条逐条推理最后给重排设置了硬性超时300毫秒一旦超时就直接跳过重排用第一次召回的Top5作为结果。优化后P95降到1.8秒左右达到了预设目标而且稳定性得到了明显提升。5.4 高峰期的限流配合该让用户有点耐心排队还有一次是切流当天流量突然翻了三倍虽然服务没挂但部分用户出现了长时间等待。排查结果是接入层限流策略里的等待机制配置过保守队列太短导致大量请求在等待后被拒。调整方案是把有限的等待时间分块比如等待超过3秒就返回“当前咨询人数较多已为您安排请继续等待”的提示同时保持请求在队列内异步轮询处理。配上一个前端自动重发按钮用户不需要手动刷新体感上好了很多。这个案例也说明限流不能只考虑服务端保护还必须考虑用户体验的反馈节奏否则用户会不断重试反而加剧系统压力。6. 一点经验沉淀与后续演进方向6.1 架构设计上我比较坚持的几条原则整个项目做完我自己沉淀了几个比较坚持的原则。第一是对动态能力保持约束LLM适合做开放理解不适合做不可控执行。所有会引发真实业务动作的环节都必须有明确的schema校验和硬性二次确认绝不能出现“模型自由调用接口”。第二是检索质量就是回答质量与其反复调Prompt不如先把知识库拆好、索引配好、召回与重排节奏调好RAG链路里检索的优先级比生成高得多。第三是任何依赖都要有降级路径这是服务可靠性的底牌模型挂了、向量库延迟了、业务接口超时了都必须有明确的自救行为而不是任由用户经历漫长的等待后失败。这些原则现在回头想救活了我们很多次。宁可代码多写几行降级分支也不要在大促当天面对一个查不了原因的系统干瞪眼。6.2 后续想继续尝试的方向这套架构首期跑顺后我们规划了几个明显的扩展点。一个是主动服务质量让模型能识别用户的情绪状态出现不满时更早转人工并同步历史摘要另一个是复杂多步任务编排比如“帮我对比三个套餐并推荐办理方案”这类任务单靠现在的基础链路还撑不住需要引入更稳健的规划和校验机制。我个人的体会是基于LLM的智能客服是一个不断往“让人更省心”的方向演进的事情但每一次能力外沿的扩张都必须配一轮更细的安全和可靠性加固。如果你正在搭类似的系统建议先从知识问答和受控工具调用这两个场景切入把链路、日志、降级都打磨顺了再去看更酷的Agent能力。毕竟对于客服平台来说稳定、安全、不胡说永远比什么都能聊重要得多。