AI客服稳健性实践:风险分层与状态管理的关键设计 从标题入手风险分层和状态管理这两个词往往是经历过线上事故的团队才会反复琢磨的东西。前两年我深度参与过一个偏行业侧的智能客服重构项目踩过不少坑也沉淀了些自己觉得还算通用的方法论这里统一梳理一下。整篇文章会围绕风险分层、状态管理、动态工具路由和可观测性这几块展开重点讲讲“为什么这么做”以及“方案落地的细节”。1. 一开始我们为什么会被“稳健”两字卡住脖子先还原一个很典型的线上事故现场。某天凌晨大促刚开始用户咨询量瞬间拉高智能客服在第一轮应答里表现得还不错但当用户追问“刚才你说可以退差价具体怎么申请”时系统突然给出了一段牛头不对马嘴的通用话术。原因不在语法模型而在于会话上下文丢失了用户之前的诉求没有被正确存储在会话状态里。更让人头疼的是同一时刻还有一小批用户问到了某个刚下架的活动商品模型在没有风险判断的情况下直接按历史知识库内容答复把已失效的规则说出去了。这类问题暴露了两个事实第一单纯堆大模型参数不能解决业务约束问题第二上下文状态管理不是一个“顺手做做”的模块而是整个客服系统能持续稳定输出的地基。当时我梳理了一下系统的整体调用链路发现状态保存散落在各业务方有时放在进程内存里有时直接塞进缓存超时时间还不一致模型拿到手的数据经常是残缺的。至于风险控制几乎等于没有系统对回答内容的合规性、时效性和业务限定几乎不加校验。所以“稳健AI客服”这个题目不是我随便选的角度它本质上是一套组合工程方案需要把风险识别、状态存储、策略路由、人工兜底看成一个大闭环去设计。下面我按我们当时落地的主线逐步展开。2. 风险分层给AI的回答加一道分级闸门2.1 为什么“一刀切”式风控会误伤最开始时团队追求“所有回答都必须经过强一致性校验”每个问题都要求模型能溯源到某个内部知识库文档命中不了就转人工。结果系统变得很僵硬大量常规问候、闲聊、简单查单问题也走了高成本链路响应时间拉长用户体验直线下降。后来我们做了调整风险分层不是把所有回答都堵死而是把回答可能引发的后果按等级区分开不同等级使用不同强度的校验策略。这里我把回答内容划分为三个风险等级低风险不涉及用户个人数据、不涉及价格承诺、不涉及规则承诺比如“你们的营业时间是几点”“怎么联系人工”。中风险涉及一般性业务规则说明比如退换货期限、优惠券使用范围但不承诺用户一定能获得额外利益。高风险涉及价格调整、赔偿、权限变更、个人敏感信息操作比如“我给你补30元优惠券”“可以帮你改收货地址”。等级划分完成后每个等级对应不同的模型温度参数、知识库检索范围、兜底策略。核心思路是低风险要求速度快高风险要求必须把准确率拉满。2.2 用内容走查器做二次判断光靠模型自己对输出做分类不够可靠必须有一个独立的校验层。我们实现了一个轻量的规则引擎本质上是多条可配置的走查器链每条走查器干一件事敏感信息走查识别是否引用了身份证号、完整手机号、地址等字段一旦出现就用脱敏逻辑替换后再返回。承诺性句式走查判断是否存在“保证”“一定”“肯定”“最晚X天内到账”这类强承诺表达命中后必须附带风控审批编号才允许直接返回。时效性走查把回答中提到的时间信息与当前时间比对防止模型引用已过期活动。知识库引用存证要求高风险回答必须携带知识库原文编号否则不允许下发。这套走查器不在任何大模型里面写死而是独立成一个判断模块任何策略都能通过规则中心热更新。这带来的好处非常明显运营同学在活动期修改规则时不需要等算法重新部署模型直接改规则配置即可生效。2.3 风险分层与工具调用的关系低、中、高三级的划分不只影响话术输出还约束了模型可调用工具的范围。高风险的意图下模型可以直接调用订单查询、积分变更、优惠券发放的接口中风险意图下只允许查知识库和业务卡片信息不允许做任何写操作。在工程层面我们对每个工具都标注了风险等级和可调用条件模型申请调用工具时工具网关会校验当前会话的风险等级。这里有一个容易踩坑的地方模型在上下文里能看到风险等级标识符但它有可能忽略这个标识直接尝试调用工具。所以工具调用校验不能只放在提示词层必须在执行层也拦一道。我们的实现是在工具网关注入一个前置钩子函数每次调用前都从会话服务拉取当前风险级别比对工具白名单不匹配直接返回一个系统级拦截原因同时把拦截原因回填到模型让它“换个安全方式表达”。3. 状态管理让多轮对话不说“失忆”的话3.1 会话状态的存储选型AI客服的状态管理和普通接口的状态不太一样普通接口状态是“这一次请求要做什么”AI客服的会话状态是“过去几轮用户和系统之间发生了什么以及当前对话进行到哪一步”。我们在重构前出现过好几次会话串号的问题核心原因是之前用进程内全局Map存上下文多实例部署时负载均衡把不同请求分到了不同节点导致上下文偶尔错乱。后面我们统一把会话状态挪到了独立的存储服务里按照会话ID做分片。数据结构上没有用散乱的JSON直接塞而是采用三级分层基础元数据用户ID、会话ID、进入渠道、当前风险等级、最新意图标签。共享上下文包含用户槽位信息历史比如“用户是否已登录”“是否说自己是海外的会员”“人工坐席是否已经接入”。技能域数据对应不同业务子模块独立保存比如订单子域只存订单上下文优惠券子域只存券码上下文。这样拆的好处是模型每次读取状态时不必把所有字段都拼进提示词而是按需加载提示词不会过度膨胀成本控制更可控。3.2 状态机的流转与超时多轮对话必须建模成有限状态机。我们在会话服务内部维护了这样一个状态集合初始用户刚进入会话。意图识别中模型正在判断用户需要什么帮助。信息采集需要用户补充若干关键字段。工具执行中正在调用订单或优惠券接口。风险复核触发了高危操作等待另行确认。完成问题处理完毕问答闭环。有一次我们线上遇到了“用户信息始终采集不齐”的问题用户在对话中来回跳题状态机一直卡在信息采集阶段导致后续的兜底逻辑无法触发。后面给状态流转加了一个轮次计数器同一状态下超过3轮无进展就强制切换成“转人工引导”状态同时清理掉无用的临时槽位。这个机制上线后会话卡死率直接降了一大截。3.3 记忆的分层持久化状态里还有一部分数据是会被多轮会话复用的比如用户的高频诉求偏好。为了兼顾性能和准确性我们分了短期记忆和长期档案两层。短期记忆存当前会话内最近6轮的关键实体和诉求长期档案存用户历史投诉偏好、渠道偏好、常用物流地址。长期档案的更新不是每次都写而是由专门的记忆聚合任务在会话结束后异步更新这样避免每次对话都要重写数据库。模型接入记忆时通过提示词组装模块定期拉取记忆服务。这里有一个很真实的经验千万不要把用户历史全量塞进提示词否则模型容易抓到很久以前的意图反而干扰当前轮次判断。我们的策略是“只取当前意图域相关”的历史记忆比如当前识别出是物流投诉域就只带这个域相关的历史联系方式、历史投诉次数别的域一概不取。4. 风险分层与状态管理必须联动否则都是白搭4.1 把状态和风险对齐成一套矩阵如果风险分层和状态管理各自为政那系统还是会出现漏洞。举个例子用户状态已经在“工具执行中”了但风险等级还停留在“低风险”模型就可能以低风险身份去调用了高风险的写操作。所以必须把两者绑定成一个矩阵关系。我们在会话状态中心单独维护了一个字段当前可用动作集这个字段由“风险等级”和“状态机当前节点”两个维度共同决定。矩阵设计大概长这样低风险 信息采集阶段可用动作仅限查询型工具和知识库检索。中风险 工具执行阶段允许读取业务系统数据禁止写操作。高风险 风险复核阶段必须等待用户显式确认确认后放行写操作否则只能输出“我帮你转人工处理”。这个矩阵进一步在代码层固化成了一个配置表模型返回的每个动作都会被翻译成“可执行动作编码”工具网关只认编码。这样即使模型在措辞上出现了偏差网关也能根据矩阵拉回正确的行为边界。4.2 共享上下文的自动遮蔽策略联动过程中我们踩过比较揪心的坑是上下文里的敏感数据被带进后续轮次。比如用户前一秒刚刚问过“我的身份证号是不是可以改”模型回答时复述了身份证号结果后一轮用户只是问“那地址呢”模型又把身份证号带进了回答草稿。解决方式是在共享上下文写入时增加一个脱敏映射层把敏感字段转成一个占位符比如“用户证件号已识别”模型只能看到这个占位符调用具体工具时再按需从加密存储里还原。整个还原链路需要校验会话指纹防止占据位符被强行猜测。4.3 超时和并发的状态覆盖防护有一次线上并发压力测试多条请求同时推进同一个会话状态因为处理时没有加分布式锁后处理的请求把先处理的槽位信息覆盖了用户明明说“我地址是杭州市西湖区”下一轮追问时系统却回复“我没有获取到你的地址信息”。这个问题本质是会话状态读改写三个操作没有原子化。我们引入了基于会话ID的分布式细粒度锁所有状态更新必须通过锁内事务执行并且版本号自增模型提交新状态时携带上一版版本号版本不匹配就拒绝更新让用户重新补充一次关键槽位。虽然这会让用户体验显得稍微“笨一点”但至少不会出现噪声性错误。5. 可观测性的工程落地别等用户投诉才知道出问题5.1 动作日志和状态迁移轨迹稳定不是靠感觉靠的是日志能完整还原每一次对话的内部动作。我们把每一次模型调用、工具调用、状态迁移、风险拦截都记录成统一结构的动作事件包含会话ID当前状态节点当前风险等级调用工具名称输入输出摘要耗时和返回码这些事件落到检索后端后可以方便地搜索“某个会话卡在哪个节点”“某个高风险调用被哪个闸门拦截”这类问题。团队内部建了一张看板专门监控高风险拦截占比、状态迁移成功率、状态采集超时率等指标。5.2 慢会话的追踪与采样客服系统的会话链路比较长一次会话可能包含多轮问答。如果只是单次接口调用快但整体会话迟迟不完结一样是体验问题。我们额外加了一层“慢会话追踪器”当一个会话持续超过5分钟且没有进入完成状态就触发一次内部诊断把当前状态机节点、最后5轮对话摘要、工具调用序列全部快照下来推送给开发同学。通过这个机制我们好几次提前发现了状态机循环的问题还没等用户大规模投诉就已经修复了。5.3 风险拦截的误杀率监控风控做多了容易误伤正常用户。我们的走查器里有一些规则比较严格比如“只要出现时间词就要求二次确认”结果很多用户只是随口问“明天能送到吗”也被卡了一道确认流程。后来在规则中心增加了误伤率的实时计算定期把“被拦截但用户最终没有投诉”的会话样本捞出来重新标注逐步收敛规则阈值。经过大约三周的调优高风险拦截准确率提到了一个可以接受的水平低风险场景几乎不再出现拦截提示。6. 落地这套方案的几点体会整个重构过程里我最深刻的体会是“稳健不是直接让模型变得聪明而是让模型不能轻易闯祸”。这里把几个关键经验再强调一遍风险分层一定要从产品层定义清楚不能只让算法自己判断什么算高风险。规则中心必须能让运营直接配置否则活动一变全链路都要返工。状态管理的存储选型尽量独立不要和业务库耦合过深。我们在重构后将会话状态单独拆分之后排查问题的速度明显加快。模型能调用什么工具必须是执行层强制校验不能指望提示词约束够用。提示词只是第一道防线工具网关才是真正的闸门。可观测性的动作日志结构一定要从第一版就想清楚否则后面查问题会面对各种格式混乱的日志光是清洗数据就够喝一壶的。人机协作的边界要明显。当风险等级达到高等级或者状态机进入风险复核后系统应该坚定地引导到人工处理不要反复用AI话术拖延。再补充一个可以直接落地的细节在做风险等级判定时我们并没有完全依赖大模型给自己打分而是让模型先输出一个“判断依据文本”再让规则引擎对这个依据文本做关键词和句式校验两边一致才确认等级。这一步虽然多了几十毫秒耗时但显著减少了模型自我认知偏差导致的风险分级失误。最后聊聊后续可以扩展的方向。目前这套方案主要还是面向文本会话如果把风险分层和状态管理的能力复用到语音客服场景还需要额外处理ASR转写带来的置信度问题。另一个方向是引入离线评估数据集把历史会话里风险误判的样本做成回归测试集每次调整风控规则或提示词时先跑一遍回归确保旧问题不复发。工程上稳健没有终点但把基础骨架搭对之后后续迭代会轻松很多。