开头
如果你正在做电商智能客服,又恰好选了 LangChain 来搭 Agent,那这篇应该能帮你省掉不少弯路。上一篇我们把整体骨架立了起来——模型选型、Prompt 设计、基础工具注册——但骨架只是开始。真正让一个 Demo 变成能扛住真实流量、能处理复杂售后场景的“智能体”,靠的是对话状态管理、工具编排策略、异常兜底这些工程细节。
这篇文章是系列的第二篇,聚焦电商智能客服 Agent 从“能跑”到“好用”之间的那段路。我会直接讲架构设计的决策点、核心模块的代码实现、还有我们在生产环境里踩过的坑。适合已经跑通基础 Demo、想进一步把路由精准度、多轮记忆、任务拆分配置做扎实的开发者。纯新手也能看,但建议先有一篇的基础。
电商客服这个场景很特殊:它不像通用问答那样答完即走,每个用户进来都带着明确意图——查订单、退货、催发货、问优惠——而且对准确率要求极高。说错一个物流状态,可能就是一次客诉。所以智能体的设计重点不在于“能聊”,而在于“能办成事”,并且每一件事都要有迹可循。
1. 整体架构设计与思路拆解
1.1 为什么选 LangChain 的 Agent 而不是传统对话流程
很多人上来就问:电商客服用现成的对话平台不就行了吗?为什么还要自己用 LangChain 搭?这个问题的答案,其实取决于业务复杂度。
如果客服只做两件事——发欢迎语、转人工——那确实不需要 Agent。但电商场景的真实需求是:用户说“我上周买的那个蓝色耳机想换黑色的”,系统得先理解“上周买的”对应哪笔订单、哪个 SKU,再判断换货政策是否允许,最后还要确认库存。这是一条决策链路,不是一次问答匹配。
传统对话流程(Intent → Entity → Response)能处理“查订单”这种单一意图,但处理不了“多步推理 + 动态决策”的复合场景。LangChain 的 Agent 核心价值在于:它把大模型的推理能力、工具执行能力和状态管理统一起来,让系统能根据当前上下文动态决定下一步调用什么工具、传什么参数、什么时候该问用户、什么时候该直接执行。
我用过一个很直观的对比:传统方案像自动售货机,投币(输入)、掉货(输出),逻辑固定;Agent 方案更像一个柜员,他会根据你的话判断你要办什么业务,再决定是递表单还是直接操作后台,甚至可以追问你“请问是哪个订单号”。电商客服要的就是这种“柜员感”。
1.2 电商客服场景对 Agent 的特殊约束
通用 Agent 的设计思路可以直接搬,但电商场景有三条约束必须在架构层面就考虑进去,不然后期都是补丁:
第一条是准确性优先。用户问“发货了没”,你回答“已发货,物流正在揽收中”,这个信息必须是拉取真实物流接口得到的,不能靠模型生成。所以在工具设计上,凡是涉及订单、物流、售后状态的查询,都必须走工具函数返回真实数据,不允许模型凭空编造。语言模型可以做总结、做判断,但不能成为数据的源头。
第二条是话题切换与多轮记忆。用户在问完 A 订单的物流之后,紧接着说“那双鞋也想退了”——“那双鞋”指的是上一轮提到的商品,还是更早之前买的?如果没有一个结构化的会话状态管理机制,上下文很快就会混乱。LangChain 提供了多种记忆方案,但电商场景不能只把聊天记录拼接进 Prompt,那样既费 token 又容易丢失关键实体信息。我们用的是“摘要记忆 + 关键槽位记忆”的组合方案,这个后面详细说。
第三条是高并发下的成本与延迟控制。电商大促期间的流量是平时的几十倍,每个会话都走一次完整 ReAct 循环,token 消耗和响应延迟都会爆炸。所以我们引入了“意图预路由”机制:先用一个轻量分类器判断用户意图,只有高复杂度意图才进入全量 Agent 循环,简单查询走快速通道。这不只是省钱,更是保障服务可用性的关键。
2. 核心模块设计与关键决策
2.1 对话记忆模块:不能只靠拼接历史记录
先聊聊最容易被低估的模块——记忆。很多 LangChain 教程会教你用ConversationBufferMemory或ConversationSummaryMemory,直接把历史记录塞进 Prompt。这在 Demo 里没问题,但电商场景很快就翻车。
我见过一个真实案例:用户在上午问过订单 A 的退款进度,下午再次进线说“那个钱什么时候到”,如果系统只把历史记录拼进去,模型确实能知道“那个钱”是指订单 A 的退款,但等到第三个订单、第四个订单出现之后,这种模糊指代就开始频繁出错。用户说“那个黑色的呢?”天知道是哪个。
我们最后采用的方案是三层记忆结构:
第一层是ShortTermMemory:保留最近 3-5 轮对话原文,用于处理即时指代(比如“刚才说的那双鞋”)。第二层是SummaryMemory:每 5 轮对话触发一次摘要生成,把关键事实沉淀下来,防止历史过长导致 token 溢出。第三层是SlotMemory,这是电商场景独有的设计——把用户身份、当前关联订单号、正在处理的售后单、收货地址、商品 SKU 等业务关键字段,单独存储在结构化槽位中。
Slot 记忆的价值在于:模型不需要每次从聊天记录里去“猜”当前用户在处理哪个订单,槽位一查就知道。而且槽位可以直接传给工具函数做参数校验,减少模型传参出错的概率。
这个设计的一个副产品是:当系统需要转人工时,Slot 记忆可以序列化成上下文摘要,直接传给人工客服工作台。用户不需要重复描述问题,人工客服一眼就能看到“这个用户正在退一个什么订单、卡在哪个环节”。这个体验价值非常大。
2.2 工具注册与编排:电商 Agent 的“四肢”
电商客服 Agent 的工具集合,用我自己的项目经验来列,大概有这几类:
订单域:查询订单详情、查询物流轨迹、修改订单备注、取消订单、申请售后、查询退款进度。商品域:商品搜索、库存查询、优惠券查询、价格与促销信息查询。会员域:积分查询、优惠券列表、会员等级信息。人工兜底:创建工单、转人工会话。
每个工具的背后是一个真实的 API 或内部服务。LangChain 的@tool装饰器非常方便,但有几个细节值得注意:
工具的描述信息一定要写得足够清晰,而且要包含参数约束。模型依赖 tool description 来决定“什么时候该调用什么工具”,如果你写的是“查询订单”,模型可能在任何涉及订单的问题上都调用它,哪怕只是“我的订单能改地址吗”。我们把描述写成“仅当用户明确提及订单号或要求查询订单状态时调用;若用户仅询问规则或政策,请勿调用”,路由准确率明显上升。
工具的入参需要强校验。大模型生成的参数经常出现类型错误(把字符串传给 int 字段)、枚举越界、甚至直接幻觉一个不存在的订单号。我们在工具外层加了一层参数校验器,校验失败时不直接报错,而是生成一条清晰的追问消息:“请问您能提供下单时使用的手机号后四位吗?”这样保证系统在一个可控的闭环里运行。
工具编排策略上,强烈建议用create_openai_functions_agent或新的create_tool_calling_agent这类函数调用式 Agent,而不是最早的 ReAct 文本解析式。函数调用式 Agent 让模型输出结构化的调用指令,解析稳定性和成功率远高于让模型用文本格式 “Thought/Action/Action Input” 输出。我用 ReAct 试过线上流量,解析失败率在 5% 左右,迁移到 Function Calling 以后基本降到千分之一以下。
2.3 路由策略:轻量预分类 + 重量 Agent 决策
开头提到过,我们不能让每个用户请求都走完整 Agent 循环。一个很实际的优化是引入两级路由。
第一级是一个轻量意图分类器。候选意图集合需要根据业务线定制,我们当时定义的是:查订单、查物流、退换货、优惠活动、商品咨询、人工服务、闲聊寒暄和拒绝回答,共 8 类。这个分类器可以是一个小模型(比如基于嵌入的 KNN 或者一个蒸馏过的分类模型),也可以是规则 + 关键词的混合策略。它的输出不是最终答复,而是决定要不要让后面的 Agent 接管。
第二级:分类为“闲聊”或“拒绝回答”的请求,直接走固定话术库,绝不浪费大模型调用;分类为“查订单”“查物流”的简单单轮请求,走一个精简的“单一工具快速链”,也就是上下文里只有必要的工具描述和用户问题,不做多步规划;分类为高复杂度意图(退换货、投诉、多条件组合咨询)的请求,才进入完整的AgentExecutor。
这个路由设计听起来简单,但收益非常大。粗算一笔账:某次大促活动期间,完整 Agent 循环的平均 token 消耗约为 4200 tokens,响应时间约 4.8 秒;而经过路由后,约 55% 的请求走了快速通道,平均 token 消耗降到 800 左右,响应时间压到 1.2 秒。整体成本降低接近 60%,且高复杂度场景仍然保持完整推理能力。
3. 核心机制与实现细节
3.1 意图路由的实现与阈值调参
如果你的团队没有预算再训练一个分类模型,可以先用嵌入 + 相似度匹配的方案,实测效果也够用。具体做法是:
为每个意图准备 10-20 个典型用户问法作为种子样本,用OpenAIEmbeddings或国产模型的 embedding 接口向量化后存库。新用户消息进入时,也转成向量,与每个意图的种子向量集合计算余弦相似度,取最高分且超过阈值(0.82-0.85 区间)的意图作为预分类结果。
这里有一个踩过的坑:阈值设太高会导致很多请求被误判为“不确定”,最终全部打回完整 Agent 循环,路由优化形同虚设;阈值设太低则会把“查物流”误判成“投诉”,进入重链路后模型又被要求调用工具、来回多轮,延迟反而更差。建议上线前拿至少 2000 条真实客服对话做阈值网格搜索,找到准确率与召回率的平衡点。
另一个非常有效的优化是给种子样本做“动态补充”:每发现一条高置信度的新对话模式(用户表达属于某意图、但和种子样本相似度低于 0.7),就把它人工确认后加入样本库。这样可以持续适应促销季、新品上线带来的语言变化。我们运营了三个月,意图识别准确率从 86% 提到了 94% 左右。
3.2 订单、物流与售后三类核心工具链
订单、物流、售后是电商客服的三个主战场。分别说一下实现需要注意的点。
订单查询工具。核心是订单号的获取。用户很少直接给完整订单号,更多是“我 8 月份买的那件灰色卫衣”。这种情况不能直接调 API,需要先通过用户身份(如手机号/会员 ID)拉取订单列表,再结合对话里的商品属性(颜色、品类、时间描述)过滤。这个“意图识别 + 列表筛选”的组合操作,比强行让模型直接生成订单号要可靠得多。我们的 Prompt 里特别强调了一个原则:如果模型无法从上下文中确定唯一订单,必须向用户提出澄清问题,禁止猜测。
物流查询工具。注意“物流轨迹”和“物流摘要”是两个不同粒度的需求。“在哪儿了”和“什么时候到”是高频问题,我们用一个专门的物流摘要工具返回“包裹当前位置 + 预计送达时间 + 最近一条轨迹”,避免把一整串物流明细全塞进对话里。物流数据本身是第三方接口返回的,经常有数据延迟(比如揽收后 2 小时才有首条轨迹),工具层需要设计“数据未就绪”的返回分支,让模型理解这个状态,而不是反复重试。
售后申请工具。这是最容易出错的环节。售后申请涉及退款金额、退货地址、原因分类、凭证上传等多个字段,一个字段缺失就可能导致服务端校验失败。我们的做法是把售后申请拆成“预检查”和“正式提交”两步:先让工具把缺失字段列出来,模型据此追问用户补齐;全部齐了以后,再调用正式的提交接口。这样用户体验是“对话中完成申请”,后端又不会收到残缺请求。
三类工具链可以用下面的表来做总结:
| 工具类 | 高频输入 | 关键输出 | 典型坑位 |
|---|---|---|---|
| 订单查询 | 手机号、时间描述、商品属性 | 唯一订单确认 | 多订单时需要澄清 |
| 物流查询 | 订单号或物流单号 | 当前位置、预计时效 | 数据延迟导致空结果 |
| 售后申请 | 订单号、原因、凭证 | 售后单号 | 字段不全会校验失败 |
3.3 复杂任务的拆解与 Prompt 工程
电商客服里最难的其实是“投诉处理”这类复合型任务。用户可能一次性诉多个问题:“你们的物流也太慢了,而且送来的箱子是破的,我申请换货又说没库存,到底还能不能换了?”
这种消息,如果直接丢给 Agent 按顺序处理,大概率会漏掉部分诉求。我们使用的策略是“任务拆分 + 优先级排序”。
第一步,用提示词要求模型先做一个结构化解析,把用户消息拆成多个独立诉求。解析结果是一份 JSON 数组:每个元素是诉求类型、关键实体、状态(已满足/待处理)。第二步,模型根据诉求类型决定处理顺序:涉及退款、换货这类有明确服务承诺的先处理;涉及对物流时效的抱怨,先安抚再处理(安抚话术也被设计成一个工具函数);涉及新诉求(透明胶带残留、赠品缺失等)需要查询售后政策的,走知识库检索。第三步,让模型在最终回答之前,把自己的回答与诉求列表做一次“覆盖检查”,逐项确认没有遗漏。
这个“拆解—排序—覆盖检查”的循环,是我们实测下来投诉处理场景满意率提升最明显的一个改动。从最初的 71% 提到了 85% 以上,用户感受到的明显变化是:系统不再答非所问,也不会漏掉任何一个诉求点。
3.4 上下文窗口管理与 Token 精打细算
LangChain 应用在生产环境,Token 消耗是最容易被忽视的成本黑洞。电商客服场景中,基线 Prompt(系统角色设定、工具描述、历史摘要说明)加上工具定义,往往就已经有 2500-3500 tokens 了。如果再塞入过多历史记录、检索结果,一次完整调用很容易到 5000-7000 tokens。
我们总结出来的三层控制策略:
核心 Prompt 瘦身。系统 Prompt 只保留“角色定义 + 行为红黄线 + 输出格式”,所有业务规则不再写死在 Prompt 里,而是移到工具描述或知识库检索中。为什么要这样?因为业务规则经常变,直接改 Prompt 要么触发缓存失效,要么测试不充分导致回归。改成检索式,规则更新只需要改文档库,不用动代码。
工具描述按需注入。不要一次性把所有工具的描述都塞给模型。第一级路由已经知道用户意图,比如意图是“查物流”,就只注入订单相关和物流相关的 3-4 个工具描述,其余工具的 token 成本直接省掉。这个优化立竿见影,每轮调用平均省 800-1200 tokens。
分级记忆策略。短时记忆只保留最近 3-5 轮原文;更早的内容用摘要存;结构化槽位单独存储。摘要不是每轮都生成,而是当原文超过阈值时才触发一次。每次摘要生成本身也有成本,所以要控制频率。
这三点综合下来,同一个业务量下,每天的 token 消耗比第一版方案降低了超过一半。没有做量化裁剪,纯粹靠架构层面的取舍。
4. 常见问题与排查技巧实录
4.1 Agent 陷入重复调用工具的死循环
现象:模型一直在调用同一个工具,比如说“查询物流”,查询结果没有变化,它就再查一次,甚至查七八次,既不给出结论,也不请求用户补充信息。这在 LangChain 里表现为一个很长的工具调用序列,token 消耗飙升,最终可能触发max_iterations上限强制退出。
原因排查:最常见的原因是模型没有从工具返回中发现“条件不足”。比如物流接口返回的是“暂无轨迹信息”,模型的判断可能是“再查一次,也许有数据了”。尤其是接口返回的文案本身有歧义(比如“等待揽收”),模型会认为这是一个可以修正的状态,反复尝试。
解决办法:第一,把工具的返回结果结构化,用明确的状态字段表达“数据未就绪”,并在 Tool 描述里写清楚:“如果返回状态为 NO_DATA,请告知用户物流尚未揽收,需要进一步等待,切勿重复查询。”第二,为 Agent 设置一个更合理的max_iterations(我们一般设 5 步,包含工具调用和最终回复),并在 Prompt 里强调“如果你已经尝试过工具且结果没有变化,请基于现有结果生成回复”。第三,监测日志里如果发现同一工具连续调用超过 3 次,触发告警并强制停止,把该会话转人工。
这类问题用 LangSmith 或 Langfuse 这类可观测性工具会发现得更早。我们早期没有接 tracing,出了问题只能靠猜日志,节奏非常痛苦。后来每次工具调用、每轮模型回复都做了 trace,排查时间从小时级降到了分钟级。
4.2 多轮对话中的“张冠李戴”:指代消解失败
现象:用户第一轮问“A 订单什么时候发货”,第二轮说“那 B 订单呢”,第三轮又说“我的意思是两个都要改地址”。系统在处理第三轮时,可能把“两个”误解为 A 和 B,直接执行了重复改地址操作。
根因分析:纯靠大模型的指代消解能力是不可靠的。尤其是在多个实体(订单、商品、地址)同时存在时,模型倾向于从最近的上下文找指代对象,而不是从全局找。
我们的做法:在 SlotMemory 中维护一个“当前对话工作区”,里面记录所有被提到过的订单号、商品名、地址信息,并为每个实体附加“最后提及时间”。每轮工具调用前,先对用户消息做实体抽取(可以用正则、模型或一个轻量 NER 工具),把抽到的实体与工作区里的实体做对齐。如果用户说“那个”“这个”,就让模型在工作区候选中选择唯一项;选项超过一个的问澄清。
另外还有一个细节:模型传参加拿不准时,宁可让工具返回“多个候选订单,请用户确认”,也不要盲选最近的那个。一次错误的操作造成的用户信任损失,远比多一轮对话大得多。
4.3 大模型幻觉:虚构订单状态
现象:用户在问订单物流时,模型在没有调用工具的情况下,直接根据对话历史“推断”出一个状态结论,比如“您的订单预计明天送达”。这是所有 Agent 应用里必须零容忍的错误。
原因:模型以为自己知道,实际上是在编造。尤其当工具调用链路较长、用户等待时间较长时,模型可能在最后生成阶段“忘了”自己已经调用过工具,转而凭记忆生成答案。
解决办法:
第一,Prompt 里用强约束:“所有涉及订单、物流、余额、积分的陈述,必须以工具返回的数据为唯一依据。如果工具尚未调用或调用失败,只能回答‘系统暂时无法查询,请稍后重试’。”
第二,技术层面做一层“事实性校验钩子”:模型生成的最终回复里,凡是出现订单状态、金额、日期等实体字段,系统用正则和实体识别把这些字段抽取出来,与最近一次工具返回的真实数据进行比对,不一致则拦截回复、强制要求模型重新生成。
第三,在 Agent 的“最终回答”步骤前,加入一个黑名单指令词过滤,把“大概、可能、应该、我猜”这些不确定性词直接替换为确定口径的措辞(若工具数据充分)或转人工(若数据不足)。
这种“事后拦截”是我们最后的一道保险丝。因为模型推理过程永远有不确定性,我们不能赌它每次都执行工具,但我们可以保证它不可能输出一个与数据源矛盾的数。
4.4 常见问题速查表
| 现象 | 大概率原因 | 快速解法 |
|---|---|---|
| 模型反复调用同一工具 | 工具返回状态歧义,模型误以为可重试 | 结构化返回状态字段,Tool 描述增加禁止重试规则 |
| 用户指代错乱 | 多个实体在同一上下文中 | 引入 Slot 工作区实体对齐,模糊时强制澄清 |
| 回复出现了数据幻觉 | 模型未依赖工具结果 | 启用事实性校验钩子,非法字段拦截重生成 |
| 整体响应延迟过高 | 所有请求都走了全量 Agent 链路 | 增加意图预路由,简单意图走快速通道 |
| Token 消耗异常上涨 | 工具描述全量注入、历史记录过长 | 按意图动态注入工具、分级记忆 |
| 转人工时信息丢失 | 记忆没有对外暴露 | 将 SlotMemory 序列化成上下文摘要传给人工工作台 |
5. 从架构到生产的演进路线
5.1 灰度发布与线上评测
一个智能客服系统要上线,光离线评测是不够的。离线测试只能看“回答有没有逻辑”,而线上测试才能暴露性能、超时、上下文累积、真实用户跳跃话题这些复杂问题。
我们的做法是影子模式:把真实用户流量同时分流到新老两套系统,新系统(Agent 方案)的回答不直接发给用户,而是发给一个评测队列,由标注人员按“准确率、完整性、安全合规、语气自然度”四个维度打分。每批流量 1000 条,连续跑一周,直到评测合格率超过 90% 才正式切换。
为什么这么谨慎?因为客服系统一旦给了用户错误承诺(比如“明天必达”),产生的客诉成本远高于系统开发的成本。风险和收益不成正比时,宁可慢一点。
正式切换也要按流量比例放量:先 5%,观察 24 小时看有没有明显异常;再 20%,跑一周,拉通各项指标看有没有退化;确认没问题后,一次性切到 100%。这个节奏听起来保守,但对客服系统这种“信任敏感型”业务非常必要。
5.2 效果衡量指标
智能客服 Agent 的指标,不是单一的“用户满意度”,而是一套组合:
解决率:用户进线咨询的问题是否被彻底解决,没有再次进线。这个指标偏高,因为很多用户解决后直接离开,不点评价。显著率不高时,建议用“同一用户 24 小时内是否重复进线”作为代理指标。
转人工率:Agent 能独立处理的比例。理想情况下,我们的目标是把转人工率控制在 30% 以下,把简单重复性咨询都消化掉。
平均处理时长:从用户第一条消息到问题闭环的平均时长。注意“闭环”不等同于“对话结束”,可能是用户确认满意后主动结束,也可能是系统确认解决目标达成后主动结束。缩短这个指标的关键是减少多轮澄清的次数,这又回到前面提到的“槽位记忆”和“工具校验”设计。
安全违规率:回答中出现违规话术(包括不合适承诺、不符合售后政策的说法)的比例。这个指标任何一个百分点都不能放松。
整套指标用一张 Dashboard 每天监控,任何一条异常都拉日志复盘。当时我们设定了一个每日晨会检查仪表盘的习惯:昨天转人工率为什么突然高了?是某个工具接口异常,还是某类提问模式变了?这种持续运营打磨,比开发期的任何优化都更影响最终效果。
5.3 后续演进方向
电商智能客服 Agent 的第二阶段,我们的规划是在现有架构上做两个探索。
第一个方向是主动触达。现在的 Agent 都是被动响应,用户提问它才回答。未来可以在用户物流出现异常(比如滞留超过 48 小时)时,由 Agent 主动推送一条解释和解决方案,把“被动客服”变成“主动服务”。
第二个方向是多模态支持。现在的商品咨询还停留在文字描述,“那个红色的能用吗”模型只能猜测。未来可以结合图片输入,用户在对话中上传商品照片或截图,Agent 识别商品型号后做精准查询,这会进一步提升复杂售后场景的处理效率。
这些方向短期内不一定能做出来,但架构上提前留好了扩展位:工具注册机制是解耦的,多模态输入也只是新增一个工具的事。
结尾的实操建议
项目的核心代码和配置可以在模拟项目X的线上仓库中找到,命名规则和本次描述保持一致。想快速跑起来的话,建议先按 3.1 节的路由方案搭出最小闭环,再逐步添加工具链和记忆层。我自己踩过几次坑之后,最大的体会是:Agent 项目的复杂度不在“跑通”,而在“可控”——你愿意在多大比例的真实流量上信任它,这套系统才真正走到哪一步。
最后再分享一个小技巧:日志里除了记录模型输入输出和工具调用,建议把每轮会话的 Slot 状态也完整打印出来。排查问题时,你往往一眼就能看出是哪一步的槽位更新丢失导致了后续对话混乱。这个细节我们在第一版没做,后期补了好几天日志解析的功课,得不偿失。
这个内容后续还可以继续扩展成第三篇——如果反响够好的话,我会把多轮记忆的序列化协议、以及大促高并发压测的具体调参过程再单独拎出来详细展开。有在跑类似项目的朋友,欢迎一起交流踩坑心得。