从 Tool Calling 到 MCP:电商客服 Agent 中业务工具标准化接入的思考

一、为什么关注 Agent 的工具接入

在大模型应用落地中,RAG 主要解决的是“让模型基于外部知识回答问题”,比如商品说明、活动规则、售后政策、平台规则等知识类内容。

但是在真实客服场景里,用户的问题往往不只是“问知识”,还会涉及具体业务动作,例如:

  • 查询订单状态
  • 查询物流轨迹
  • 判断是否满足价保条件
  • 创建售后工单
  • 查询退款进度
  • 触发人工审核

这类问题仅靠 RAG 是不够的,因为它需要访问业务系统,甚至需要执行某些操作。因此在 Agent 系统中,工具调用就成了非常关键的一层。

最早做 Agent 工具调用时,常见方式是通过 Function Calling 或 Tool Calling,把订单查询、物流查询、售后工单等接口包装成工具,让模型根据用户意图决定是否调用。

这种方式可以跑通业务流程,但随着工具数量增加,也会遇到一些工程问题:

  • 不同工具的参数格式不统一
  • 不同接口的返回结构不一致
  • Agent 侧需要维护较多工具调用逻辑
  • 工具异常、超时、重试、降级处理分散
  • 多 Agent 共用工具时容易出现重复封装

因此,工具接入层需要逐步从“能调用”走向“标准化、可维护、可扩展”。

二、Tool Calling 在客服 Agent 中的基本流程

以电商客服场景为例,一个典型的用户问题是:

我的订单什么时候发货?

系统大致会经历下面几个步骤:

  1. 接收用户问题
  2. 判断问题属于订单物流类意图
  3. 提取订单号、用户 ID 等必要参数
  4. 调用订单查询工具
  5. 调用物流查询工具
  6. 根据工具返回结果生成回复
  7. 如果工具异常,则进入重试或人工兜底

如果使用 LangGraph 编排,可以把这个过程拆成多个节点:

用户输入 ↓ 意图识别节点 ↓ 槽位提取节点 ↓ 订单服务 Agent ↓ 工具调用节点 ↓ 结果整理节点 ↓ 回复生成节点

在这个流程里,LangGraph 主要负责流程编排和状态流转,Tool Calling 主要负责让模型决定是否调用工具,以及调用哪个工具。

例如订单查询工具可以抽象成:

defquery_order(order_id:str,user_id:str)->dict:""" 根据订单号和用户ID查询订单状态。 """pass

物流查询工具可以抽象成:

defquery_logistics(order_id:str)->dict:""" 根据订单号查询物流轨迹。 """pass

售后工单工具可以抽象成:

defcreate_after_sale_ticket(order_id:str,reason:str,user_id:str)->dict:""" 创建售后工单。 """pass

这种方式比较直观,适合早期 PoC 或工具数量较少的场景。

三、直接使用 Tool Calling 会遇到的问题

当系统从单个 Agent 发展到多个 Agent 后,工具调用会变复杂。

例如客服系统中可能会拆出三个专项 Agent:

  • 商品咨询 Agent:负责商品参数、活动规则、使用说明
  • 订单服务 Agent:负责订单状态、物流轨迹、发货时效
  • 售后处理 Agent:负责退款、退货、价保、凭证补充

这三个 Agent 都可能需要调用工具。

比如:

商品咨询 Agent 可能需要查询商品信息 订单服务 Agent 可能需要查询订单和物流 售后处理 Agent 可能需要查询订单、物流、售后政策和工单状态

如果每个 Agent 都直接维护自己的工具调用逻辑,后期会出现几个问题。

1. 工具定义重复

订单查询工具可能同时被订单 Agent 和售后 Agent 使用。如果两个 Agent 各自封装一遍,后期接口字段变化时,就需要多处修改。

2. 参数校验分散

有些工具必须校验订单号、用户 ID、手机号后四位、售后类型等字段。如果校验逻辑散落在不同 Agent 中,很容易出现遗漏。

3. 返回格式不统一

有的接口返回status,有的接口返回code,有的接口返回success。如果不统一,Agent 后续处理会比较麻烦。

4. 异常处理不集中

工具调用可能出现接口超时、参数缺失、订单不存在、重复提交等问题。如果每个 Agent 单独处理,会导致重试、降级、人工兜底逻辑不一致。

5. 后续扩展成本高

当业务新增“发票查询”“优惠券查询”“工单催办”等工具时,如果没有统一的工具接入规范,系统会越来越难维护。

四、MCP 适合解决什么问题

MCP 可以理解为 Agent 和外部工具、数据源之间的一层标准化协议。

它不是替代 LangGraph,也不是替代 RAG,而是更偏向解决:

Agent 如何以统一方式发现工具、理解工具、调用工具。

在客服 Agent 场景里,可以把订单、物流、售后等能力统一封装成 MCP Server 暴露的工具。

例如:

订单 MCP Server ├── query_order ├── query_logistics └── query_invoice 售后 MCP Server ├── query_after_sale_policy ├── create_after_sale_ticket ├── query_ticket_status └── submit_refund_request

Agent 侧通过 MCP Client 获取这些工具,并根据工具描述完成调用。

这样一来,Agent 不需要直接关心底层接口来自哪个系统,也不需要在每个 Agent 中重复维护工具定义。

更清晰的职责划分是:

LangGraph:负责流程编排、状态流转、条件路由、中断恢复 MCP:负责业务工具的标准化暴露和调用 FastAPI:负责统一入口和底层业务接口封装 Redis:负责热问缓存、会话状态和幂等控制 RAG:负责政策、规则、商品说明等知识检索 LLM:负责意图理解、工具选择和回复生成

五、一个客服 Agent 的工具调用链路

假设用户问:

我这个订单物流一直没更新,可以帮我看看吗?

系统可以这样处理:

1. FastAPI 接收用户请求 2. Orchestrator 判断用户意图属于订单物流问题 3. LangGraph 将请求路由到订单服务 Agent 4. Agent 从上下文中提取订单号 5. 如果订单号缺失,先进行槽位追问 6. 订单号完整后,通过 MCP Client 调用物流查询工具 7. MCP Server 调用底层物流接口 8. 工具返回物流轨迹和当前状态 9. LangGraph 将结果写入 State 10. 如果物流异常,进入售后处理 Agent 或人工审核流程 11. 最终生成面向用户的回复

这里面最关键的是:

  • Orchestrator 负责判断去哪一个 Agent
  • Agent 负责完成当前任务
  • MCP 负责调用业务工具
  • State 负责保存上下文
  • Checkpointer 负责中断后的恢复

如果用户问题升级为:

物流一直没动,我想申请退款。

流程就会从订单服务 Agent 转到售后处理 Agent:

订单物流查询 ↓ 判断物流异常 ↓ 路由到售后处理 Agent ↓ 检索售后政策 ↓ 调用订单和工单工具 ↓ 风控规则判断 ↓ 低风险自动处理 / 高风险转人工审核

这类流程就不是简单 RAG 能完成的,而是需要 Agent 编排、工具调用、状态管理和风控规则共同配合。

六、MCP 和 FastAPI 的关系

很多人容易把 MCP 和 FastAPI 混在一起。

我的理解是:

FastAPI 更偏业务服务接口。 MCP 更偏 Agent 工具协议。

也就是说,底层业务系统可以仍然使用 FastAPI 提供接口,例如:

GET /api/orders/{order_id} GET /api/logistics/{order_id} POST /api/after-sale/tickets

而 MCP Server 可以在这些接口之上再封装一层,把它们变成 Agent 更容易理解和调用的工具:

query_order query_logistics create_after_sale_ticket

这样做的好处是,底层系统仍然保持原有接口设计,Agent 层则通过统一工具协议访问这些能力。

简单理解:

FastAPI 面向业务系统和普通服务调用。 MCP 面向 Agent 的工具发现和工具调用。

七、工具调用中的几个工程细节

在真实业务里,工具调用不能只考虑“调通”,还要考虑异常和稳定性。

1. 参数校验

比如售后工单创建工具,至少需要校验:

  • 用户 ID 是否存在
  • 订单号是否合法
  • 订单是否属于当前用户
  • 是否超过售后期限
  • 是否已经存在同类工单
  • 退款原因是否在允许范围内

否则模型生成的参数可能会导致错误调用。

2. 幂等控制

用户可能会重复点击,网络也可能重试。如果没有幂等控制,可能会重复创建工单。

可以使用:

用户ID + 订单号 + 售后类型

作为幂等键,避免重复提交。

3. 超时重试

业务接口可能会超时,因此工具调用需要设置超时时间和重试策略。

常见方式是:

最多重试 3 次 每次重试间隔递增 失败后返回降级结果 必要时转人工处理

4. 返回结构统一

工具返回结果最好统一成类似结构:

{"success":true,"code":"OK","message":"查询成功","data":{},"trace_id":"xxx"}

这样 Agent 后续处理会更稳定,也方便日志排查。

5. 状态持久化

对于需要人工审核的流程,不能只依赖内存状态。

可以将当前会话的 State 保存到 Redis 中,包括:

  • 用户意图
  • 订单上下文
  • 已完成节点
  • 工具调用结果
  • 风控标签
  • 人工审核状态

人工审核完成后,再从 Checkpointer 恢复流程继续执行。

八、RAG、Tool Calling 和 MCP 的区别

在客服系统里,这三者经常同时出现,但解决的问题不一样。

能力主要解决的问题典型场景
RAG查知识、找依据、减少幻觉售后政策、活动规则、商品说明
Tool Calling让模型决定调用哪个工具查询订单、查询物流、创建工单
MCP标准化暴露和调用工具多 Agent 共用订单、物流、售后工具

可以简单记成:

RAG 解决“回答依据从哪里来”。 Tool Calling 解决“模型什么时候调用工具”。 MCP 解决“工具如何标准化接入 Agent”。

九、一个更完整的工程流程

结合 LangGraph、RAG、MCP 和业务工具,一个客服 Agent 系统可以抽象成下面的流程:

用户输入 ↓ FastAPI 统一入口 ↓ 参数校验 / 安全拦截 / 会话初始化 ↓ Orchestrator 意图识别 ↓ LangGraph State 更新 ↓ 条件路由 ├── 商品咨询 Agent │ └── FAQ / RAG 检索商品知识 │ ├── 订单服务 Agent │ └── MCP 调用订单、物流工具 │ └── 售后处理 Agent ├── RAG 检索售后政策 └── MCP 调用订单、工单、退款工具 ↓ 风控规则判断 ↓ 低风险自动回复 / 高风险人工审核 ↓ Checkpointer 保存状态 ↓ 生成最终回复

这个架构中,每一层职责比较清晰:

  • FastAPI:负责接收请求和接口服务
  • Orchestrator:负责统一调度
  • LangGraph:负责流程状态和节点编排
  • RAG:负责知识检索
  • MCP:负责工具接入
  • Redis:负责缓存和状态保存
  • 风控规则:负责业务边界控制

十、总结

在大模型应用开发中,RAG 解决的是知识增强问题,Agent 解决的是任务执行问题,而 MCP 更偏向解决工具接入标准化问题。

对于电商客服这类业务场景,用户问题往往会同时涉及知识查询、订单状态、物流轨迹、售后规则和人工审核。如果只使用 RAG,系统只能回答知识类问题;如果只使用 Tool Calling,随着工具数量增加,维护成本会逐渐变高。

因此比较合理的设计是:

用 LangGraph 管流程, 用 RAG 查知识, 用 MCP 接工具, 用 Redis 存状态, 用风控规则控边界。

这样既能保留大模型的理解和生成能力,也能让系统更贴近真实业务流程,减少工具调用混乱、状态丢失和异常不可控的问题。

对我来说,MCP 更像是 Agent 工程化过程中的一层“工具协议抽象”。它不一定是所有项目一开始就必须引入的组件,但当系统中存在多个 Agent、多个业务工具、多个外部数据源时,MCP 的价值会更加明显。