最近 Anthropic 把电商场景的 Agent 架构和生产实践指南一起放出来了,还开源了一个叫 commerce-agents 的参考实现。如果你正在做客服机器人、订单助手、导购 Agent,或者被老板要求"用 Agent 把电商流程跑通",这套东西值得从头到尾过一遍。它不是那种只给概念的白皮书,而是直接给你一个能跑起来的项目骨架,再加上一份讲清楚"为什么这么设计"的实践指南。
这篇文章我会按自己的理解拆解这套架构,结合我实际跑 commerce-agents 和调 Claude API 的经验,把里面最关键的设计思路、生产环境要注意的坑、以及从零搭一个电商 Agent 的步骤都讲清楚。不管你是刚接触 Agent 开发,还是已经在做 LLM 应用,应该都能从中拿到一些可以直接用的东西。
1. 电商场景为什么需要一套专门的 Agent 架构,而不是拿通用框架硬套
1.1 电商 Agent 和通用 Chatbot 的本质差异
很多人觉得 Agent 就是"Chatbot 加个工具调用",这个理解在 demo 阶段够用,但放到电商场景里会出大问题。
普通聊天机器人,比如做一个百科问答、文档助手,用户问一句模型答一句,答错了损失也不大。但电商 Agent 面对的是真实订单、真实库存、真实退款流程,它的每一个动作都涉及钱和用户的信任。用户问"我这个订单能不能退",Agent 如果乱答一句"可以退",后面客服就要花十倍精力去收拾烂摊子。
所以电商 Agent 的第一个特殊性是:它必须把"对话能力"和"业务系统的约束"绑在一起。模型不能自由发挥,它只能在一套预设好的动作空间里做选择——查订单、查库存、创建售后单、升级人工,每一步都要有明确的边界和校验规则。
第二个特殊性是流程长且状态多。一个订单查询不是一次 API 调用就结束的:用户可能先问订单状态,再问物流,再问退货政策,最后真的发起退货。这个过程里 Agent 要记住用户的身份、订单号、之前聊到哪一步,还要保持上下文一致。通用 Chatbot 那种"对话一轮就忘"的架构根本扛不住。
第三个特殊性是错误容忍度极低。电商系统有库存系统、支付系统、ERP、CRM,每个系统都可能超时、返回错误、数据不一致。Agent 必须能处理这些异常,不能因为下游系统报错就直接跟用户说"我不知道"。
1.2 commerce-agents 想解决的真实痛点
Anthropic 这套 commerce-agents 参考实现,本质上是在回答一个问题:如果我们把电商后端能力全部暴露给大模型,应该用什么样的架构来保证它既灵活又可控?
它没有选择"一个大模型 + 一堆 function calling"的极简方案,而是采用了一个更务实的多 Agent 编排方案——顶层有一个 Supervisor(主管)Agent,下面挂着多个专门负责具体领域的子 Agent。每个子 Agent 负责一块独立的业务域,比如订单、商品、售后、物流,它们各自有各自的工具和 Prompt,由 Supervisor 根据用户意图决定派活给谁。
这个设计的逻辑很像公司组织架构:你不可能让一个员工同时精通销售、财务、客服,但你可以设一个前台接待,根据客户的问题转给对应部门。Supervisor 就是那个前台,子 Agent 就是各个部门。这样做的最大好处是隔离复杂性和故障域——售后 Agent 崩了不会影响商品推荐 Agent,改一个领域的 Prompt 也不用担心影响到其他领域。
另外,commerce-agents 还把很多生产环境的细节做了工程化处理,比如 Agent 之间的消息传递格式、错误信息如何格式化返回给模型、如何记录整个对话的轨迹用于调试。这些都是自己从零搭 Agent 时最容易忽略、但上线后最致命的东西。
2. 拆开 commerce-agents:参考实现里的 Agent 分工与协作方式
2.1 顶层编排:Supervisor 如何调度下游子 Agent
我拿到 commerce-agents 之后,最先看的就是它顶层那个编排逻辑。它把整个电商交互流程抽象成了一个树状结构:根节点是 Supervisor,子节点是各个业务 Agent,业务 Agent 下面再挂工具。
Supervisor 的职责非常单一:理解用户当前这句话想干什么,然后决定是直接回复用户,还是调用某个子 Agent,或者同时调用多个子 Agent。它不做具体的业务查询,不碰订单数据,也不管库存,只做路由和汇总。
这样做有一个明显的好处:Supervisor 的 Prompt 可以保持很短。你不需要把所有的业务规则塞给一个模型,它只需要知道"有哪些子 Agent、各自负责什么、什么情况下该转给谁"。模型面对的选择越少,判断就越准,这是我在实际使用中感受特别深的一点。如果你把所有业务都塞在一个 Prompt 里,模型很容易混淆边界,该查库存的时候跑去查订单。
子 Agent 返回结果也不是直接把话说给用户听,而是返回一组结构化数据,比如:
{ "status": "success", "agent": "order_agent", "data": { "order_id": "ORD-20250115-001", "status": "shipped", "carrier": "SF", "tracking_number": "SF1234567890" } }Supervisor 拿到的是一份结果,再由它决定怎么组织语言回复用户。这个设计把"决策"和"表达"解耦了,子 Agent 只负责把事办对,Supervisor 负责把话说好。对于电商这种经常要对接多个系统的场景,这个解耦能省掉大量 prompt 调试时间。
2.2 核心 Agent 的角色定位与工具边界
commerce-agents 里默认划分了几个典型角色,每个角色的工具集都做了严格区隔。我整理了一下,基本上覆盖了电商最常见的几个场景:
| Agent 角色 | 核心职责 | 典型工具 |
|---|---|---|
| Order Agent | 查询订单状态、修改订单、取消订单 | get_order, update_order, cancel_order |
| Product Agent | 商品搜索、详情查询、库存校验 | search_products, get_product_details, check_inventory |
| Customer Service Agent | 处理售后、退款、投诉 | create_return, refund_order, escalate_to_human |
| Cart Agent | 购物车增删改查 | get_cart, add_to_cart, remove_from_cart |
这里有个设计细节值得单独说:每个 Agent 只知道自己该知道的事。Order Agent 不配置商品搜索工具,Product Agent 不配置退款工具。这不是功能上的限制,而是安全上的设计——工具边界越清晰,模型就越不可能做越权操作。你想想,如果所有工具都堆在一个 Agent 里,模型在上下文较长的时候真的可能出现"用户说退款,它却去查了别人订单"的混乱情况。
还有一个我比较欣赏的点是它对"转人工"的处理。Customer Service Agent 有个 escalate_to_human 工具,这不是摆设,而是整个架构的保险丝。当 Agent 发现自己处理不了、或者用户情绪激烈、或者退款金额超过阈值时,它可以选择转人工,并且会生成一份摘要交给人工客服,让人工不用重新问一遍用户。
2.3 工具定义与数据模型:让模型和业务系统说同一种语言
跑通 commerce-agents 之后你会发现,它花在工具定义上的心思一点都不比 Prompt 少。每个工具的参数都尽量用清晰的 JSON Schema 描述,枚举值、必填项、格式限制都写得很明确。
为什么要这么做?因为大模型本质上是一个"模式匹配器",它看到的信息越规范,犯错的概率就越低。比如订单状态字段,如果你的系统里既有"已发货"又有"4",模型可能就蒙了;但如果你在工具定义里明确写死:
{ "order_status": { "type": "string", "enum": ["pending", "paid", "shipped", "completed", "cancelled"] } }模型就知道这个字段只能取这几个值,它就不会自己发明一个"已配送完成"出来。
另外,commerce-agents 里对数据的返回格式也做了约束。它倾向于让工具返回"更接近业务原貌的结构化数据",而不是直接返回一段写好的话。这个思路和很多人的直觉不太一样——你可能会想,既然模型这么强,直接让它读数据库原始记录不就行了?但实际上,数据库里的字段命名、状态码、关联关系对模型来说是很噪声的,它需要花精力去理解数据结构,就更容易出错。正确的做法是在工具层做一次翻译,把数据库的原始数据翻译成模型熟悉的业务语言,再返回给它。
3. 从 Demo 到生产:API 调用、错误处理与可观测性
3.1 连接与鉴权:把 403、网关路由错误一次讲清楚
Demo 跑通只是开始,真正上线的时候,你首先会遇到的就是各种 API 连接问题。我在好几个项目里都见过同一种报错:unable to connect to anthropic services或者failed to connect to api.anthropic.com: status 403。
很多第一次用 Claude API 的人看到 403 就以为是网络不行,其实 403 的含义非常明确:服务器收到了你的请求,但拒绝执行。这跟连不上是两码事。我建议遇到 403 先按下面这个顺序排查:
- API Key 是否有效:检查环境变量是否真的被读取到了,有些框架部署后环境变量没生效,代码里拿到的还是空字符串。这是我遇到最多的情况。
- 账号权限是否足够:你的账号是否开通了对应模型的访问权限。有些新注册的账号,或者用企业组织账号的时候,模型访问权限需要在 Console 里手动开启。
- 网关路由是否正确:如果在用网关或者模型路由服务,注意看有没有类似
expected a gateway model route的报错。通常是你配置的模型名称和网关里实际注册的路由名称对不上。这个在团队共用网关时特别常见,A 项目组删了某个路由,B 项目组还在用,直接就 403。 - 地区限制:API 使用有区域限制,需要确认你的部署服务器所在区域是否在支持范围内。
如果是unable to connect这一类的连接错误,那才需要查网络链路:确认服务器能不能访问外网、DNS 解析是否正常、安全组和防火墙有没有放行目标域名和端口。注意这里有个很多人忽略的点:有些云厂商的防火墙默认只放行 80/443,但你如果用 SDK 配置了非标准端口,就会被墙掉。
3.2 重试、超时与幂等:交易场景不能靠赌
连接问题排查完之后,下一个要考虑的是可靠性。电商场景里,Agent 后面接的都是真实业务系统,任何一个调用都可能超时或失败。如果你不做重试和超时控制,用户就会遇到"Agent 转圈圈转半天,最后说不知道"的情况。
我整理了一套比较稳妥的策略:
| 策略 | 推荐配置 | 说明 |
|---|---|---|
| 超时时间 | 单次工具调用 10~15 秒 | 太短容易误判,太长用户等不起 |
| 重试次数 | 3 次,指数退避 | 第一次 1 秒,第二次 2 秒,第三次 4 秒 |
| 幂等键 | 每个用户请求生成唯一 request_id | 防止用户重复点击导致下单重复 |
| 兜底话术 | 固定模板 | 明确告诉用户"系统繁忙,请稍后再试"或转人工 |
这里面幂等是最容易被忽略的。电商场景里,一个"创建订单"操作如果因为超时而重试,可能会创建出两个订单。所以 commerce-agents 这类参考实现里,非常强调工具操作的幂等性设计——要么业务系统支持幂等键去重,要么 Agent 在调用敏感操作前先跟用户确认一次。
还有一个很多初学者不知道的技巧:工具结果返回给模型之前做一次"健康检查"。比如查订单返回了空数据,你先判断是"订单真不存在"还是"下游系统报错了",然后针对两种情况给模型不同的提示。如果你直接把异常堆栈丢给模型,模型可能会一本正经地把错误信息当成订单内容,编一个不存在的订单状态出来。这种"一本正经地胡说八道"在生产环境是最吓人的。
3.3 可观测性:给 Agent 装上黑匣子
Agent 应用上线后最大的挑战是:你根本不知道它为什么这么回答。传统服务出问题看日志就行,Agent 应用出问题你得从头回溯:用户说了什么、模型当时怎么想的、调了哪个工具、工具返回了什么、最后一句话怎么生成的。少了任何一个环节都很难定位问题。
所以我在做 Agent 生产化的时候一定会加一层完整的 trace 记录。commerce-agents 里也有类似的思路,它把每个 Agent 的执行过程都记录成结构化事件,包括:
- 用户的原始输入
- Supervisor 的路由决策
- 调用的子 Agent 名称
- 子 Agent 调用的工具、传入参数
- 工具的原始返回和格式化后的结果
- 模型最终生成的回复
- 整个链路各节点的耗时
拿到这些 trace,你就能回答最关键的三个问题:慢在哪?错在哪?为什么错?我在实际排障中遇到过很多次,Agent 答错了,看它自己的思考过程完全正常,但一查 trace 发现是上游工具返回的库存数据本身就是脏数据。如果没有 trace,这个问题查一整天都不一定能定位到。
4. 电商 Agent 的评估方法和上线节奏
4.1 离线评估:用历史工单和数据模拟用户
很多人对 Agent 的评估还停留在"拉几个同事来聊聊天,觉得差不多就上"。这个做法在内容生成类场景可能凑合,但在电商场景是绝对不行的。你没法保证上线后用户不会问出测试时从来没覆盖过的问题。
我建议的做法是搭建一个离线评测集,从历史客服工单里抽几百条真实用户问题作为测试用例。每条用例标注好正确答案或者期望行为,然后跑一个自动化评测脚本,用模型来判断 Agent 的回答是否正确完成了任务。
比如你这样设计测试用例:
| 用例 | 用户输入 | 期望行为 |
|---|---|---|
| 订单查询 | "我上周买的 iPhone 壳发货了吗" | 正确识别用户身份,调用查单工具,返回物流信息 |
| 退货咨询 | "鞋子尺码不合适能不能换" | 先查订单,再调用售后政策,给出换货流程 |
| 超纲问题 | "你觉得我该不该分手" | 不调用任何工具,礼貌地转人工或不作答 |
用历史数据做评测集有个好处:你能非常直观地看到每次改动 Prompt 或工具之后,Agent 的行为是变好还是变坏。我在实际项目里专门建了一个 CI 流程,每次改过 Agent 代码都自动跑一遍评测集,分数下降就不允许合并。这比靠人肉眼回归测试高效太多了。
4.2 在线灰度:先人工兜底,再逐步放量
再好的离线评测也没法覆盖所有线上 case,所以上线一定要走灰度。但 Agent 的灰度和普通功能灰度有个本质区别:你不能直接给一部分用户开 Agent,然后就不管了。在 Agent 能力还没完全稳定之前,任何一次糟糕的交互都可能让用户流失。
比较稳妥的策略是三层灰度:
- 影子模式:Agent 在后台跑,但不对用户展示结果,它的输出被发送给人工客服做对比。这个阶段是为了收集数据,验证 Agent 在真实流量下的表现。
- 辅助模式:Agent 的回答先经过人工审核,审核通过了才发给用户。这个阶段你能发现很多离线评测发现不了的问题,比如用户会追问、会情绪化表达、会发送无关内容。
- 自动模式:只有前两个阶段跑稳了,才让 Agent 直接面对用户。但即使在这个阶段,也要保留"一键转人工"的兜底机制,给用户随时退出对话的入口。
这个过程看起来很慢,但它能让你避开一个巨坑:Agent 上线后才发现某个高频场景完全没覆盖,导致客服投诉量爆炸。宁可前面慢一点,也不要上线后焦头烂额。
4.3 成本与延迟的平衡
电商场景对成本和延迟的敏感度,比很多 To B 场景都要高。用户问一句"发货了没",如果你让 Agent 来回调用 4、5 次模型,每次都要等一两秒,用户早就失去了耐心。
我在做成本优化时主要会用这几招:
- 缓存常见请求:对于"退货政策是什么""运费谁出"这类静态知识类问题,完全可以走缓存,不需要每次都让模型跑一遍。用 embedding 做语义匹配,命中了直接返回预设答案。
- 减少无效轮次:Supervisor 如果能确定当前问题只涉及一个子 Agent,就应该一次调用完成,而不是把所有子 Agent 都跑一遍。commerce-agents 这种"先路由再干活"的架构,天然就能节省这部分开销。
- 用小模型处理简单任务:不是所有任务都需要最强的模型。比如用户只是想查一下订单状态,你可以用一个更快更便宜的模型来做意图识别和简单回复,只有在需要复杂推理时才路由到大模型。
- 控制上下文长度:对话历史只截取最近几轮,不要无限制堆积。上下文越长,每次调用的 token 成本越高,响应也越慢。很多 Agent 项目的成本失控,就是因为历史消息没有做裁剪。
5. 动手实践:基于 commerce-agents 做一个订单查询与退货引导 Agent
5.1 环境准备与最小依赖
说再多理论和架构,不动手跑一遍都是空的。下面我用一个最小示例,帮你理解 commerce-agents 的核心工作流。这个示例不用真的接电商系统,我用一个 mock 的服务来模拟订单数据,你替换成真实 API 就能用。
环境准备比较简单,只需要 Python 3.10+ 和 Anthropic SDK:
pip install anthropic然后准备一个配置文件,把 API 相关的环境变量放好:
export ANTHROPIC_API_KEY="your-api-key"这个示例里我会用claude-sonnet模型,它在工具调用上的表现比较稳定,成本也比旗舰模型低,适合做订单这类高频、逻辑相对固定的任务。
5.2 定义工具与 Agent 工作流
整个 Agent 的核心逻辑就是:接收用户输入,根据意图选择工具,执行工具,把结果组织成回复。下面我写一个基于 SDK 的简化版本,模拟"查订单 + 引导退货"两个动作。
import json from anthropic import Anthropic client = Anthropic() # 模拟订单数据,实际项目里换成真实数据库或 API 调用 MOCK_ORDERS = { "ORD-001": {"status": "shipped", "item": "iPhone 15 保护壳", "can_return": True}, "ORD-002": {"status": "delivered", "item": "机械键盘", "can_return": False}, } TOOLS = [ { "name": "get_order_status", "description": "根据订单号查询订单状态和是否支持退货", "input_schema": { "type": "object", "properties": { "order_id": {"type": "string", "description": "订单号,格式如 ORD-001"} }, "required": ["order_id"] } } ] def get_order_status(order_id: str) -> dict: order = MOCK_ORDERS.get(order_id) if not order: return {"error": "order_not_found", "message": "没有找到该订单"} return order def run_agent(user_input: str): messages = [{"role": "user", "content": user_input}] response = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, tools=TOOLS, messages=messages ) # 检查模型是否请求调用工具 for block in response.content: if block.type == "tool_use": tool_name = block.name tool_args = block.input result = None if tool_name == "get_order_status": result = get_order_status(**tool_args) # 把工具结果回传给模型,让它生成最终回复 messages.append({ "role": "assistant", "content": [block] }) messages.append({ "role": "user", "content": [ { "type": "tool_result", "tool_use_id": block.id, "content": json.dumps(result) } ] }) final_response = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, tools=TOOLS, messages=messages ) for final_block in final_response.content: if final_block.type == "text": print("Agent:", final_block.text) return # 模型没有调用工具,直接输出文本 for block in response.content: if block.type == "text": print("Agent:", block.text) if __name__ == "__main__": run_agent("帮我查一下 ORD-001 订单还能不能退货?")这个示例能跑通核心闭环:模型发现用户提供了订单号,就调用get_order_status工具,拿到结果后再基于真实数据组织回答。它比直接让模型"凭记忆回答"安全得多,因为订单数据是真实的、可校验的。
5.3 常见坑:工具返回格式不一致、上下文溢出、模型乱猜
这个 demo 跑通之后,你还需要处理几个我在实际开发里反复踩过的坑。
第一个坑:工具返回格式不一致。有时候你的工具返回一个 Python dict,有时候返回一个字符串,有时候又返回一个对象,模型面对这些不同格式的返回内容时,解析能力会下降。最好的做法是统一让所有工具都返回 JSON 字符串,并且在返回数据里加上明确的success或error标记。这样模型每次面对的都是同一种结构,它的稳定性会明显提升。
第二个坑:上下文溢出。如果用户在一个会话里连续追问好几个订单,工具返回的结果会一直堆积在对话历史里,很快就把上下文占满。解决办法是把对话历史压缩:只保留最近两三轮的消息,再往前的历史用摘要替代。你可以让模型每隔几轮生成一个简短的对话摘要,然后把旧消息替换成这段摘要。
第三个坑:模型乱猜订单号。用户说"我买的那个手机壳发货了没",这时候模型不知道用户的订单号是什么,它很可能会自己编一个出来。这在大模型应用里真的很常见,模型为了完成任务会"脑补"缺失信息。解决思路是:在意图识别阶段就判断用户是否提供了必要参数,如果没有,就明确向用户追问,绝对不能自己编。你可以通过在工具描述里加上一条约束:
如果用户没有明确提供订单号,不要调用本工具,先向用户询问订单号。一句话就能避免大量幻觉问题。
6. 我在实际跑这套架构时的一些体会
最后随便聊几句自己的感受。commerce-agents 这套参考实现,我最大的体会是它把"Agent 工程化"这件事落地得很扎实。它不是给你一个花哨的 demo,而是把多 Agent 编排、工具边界、错误处理、可观测性这些真正决定一个 Agent 项目能不能上线的东西都考虑进去了。
如果你现在正准备做一个电商 Agent,我的建议是从它的架构开始,不要自己拍脑袋从零设计。先照着参考实现跑通,再去改业务工具,会比你自己摸索省非常多的时间。特别是 Supervisor + 子 Agent 的拆分方式,我觉得不止电商,很多业务域都适用。
还有一个小技巧:调试 Agent 的时候,不要只看最终回答好不好,要多看它每一步调用了什么工具、传了什么参数。我用 trace 工具排查的时候发现,至少一半的"回答错误"其实不是模型的问题,而是工具调用链路上某个环节传了脏数据。把这个环节管好了,Agent 的正确率会有一个非常明显的提升。
从电商这个切口进去理解 Agent 架构,你会发现自己后面做其他场景也会顺手很多。这套东西值得好好用起来。