为企业 MCP 设计可组合工具:从理论到实践

发布: (2025年12月24日 GMT+8 01:35)
14 分钟阅读
原文: Dev.to

Source: Dev.to

企业 MCP:从 “API 网关” 到基于可组合技能的工具设计

在我之前的文章中,我讨论了 企业 MCP 实施中最大的差距并不是协议本身——而是围绕它的架构决策。具体来说,团队常常把 MCP 当作 “LLM 的 API 网关”,但实际上应该考虑可组合的工具设计。

今天,我想向大家展示可组合、基于技能的工具设计在实践中到底是什么样子。

酒店运营案例研究

Hotel Operations Case Study

让我们从酒店管理系统的真实场景开始。前台员工说:

“Beth Gibbs 正在退房,她说她房间的马桶坏了。”

这段简单的交互需要:

  • 处理退房(付款、收据、房间状态)
  • 提交维修请求(保持房间上下文)
  • 更新库存和可用性
  • 将请求路由到合适的维修团队

你会如何为此设计 MCP 工具?

The Naïve Approach

Many (if not most) teams start by exposing existing APIs as MCP tools:

[
  "get_guest_by_email",
  "get_booking_by_guest",
  "get_room_by_booking",
  "create_payment_intent",
  "charge_payment_method",
  "send_receipt_email",
  "update_booking_status",
  "update_room_status",
  "create_case",
  "assign_case_to_contact",
  "set_case_priority"
]

An agent now has to orchestrate 11+ API calls in the correct sequence, handle potential failures at each step, and maintain state throughout. The result? Slow, error‑prone, and terrible user experiences.

许多(如果不是大多数)团队会先将现有 API 公开为 MCP 工具:

[
  "get_guest_by_email",
  "get_booking_by_guest",
  "get_room_by_booking",
  "create_payment_intent",
  "charge_payment_method",
  "send_receipt_email",
  "update_booking_status",
  "update_room_status",
  "create_case",
  "assign_case_to_contact",
  "set_case_priority"
]

现在,代理需要按正确的顺序编排 11+ 个 API 调用,处理每一步可能出现的失败,并在整个过程中保持状态。结果如何?速度慢、容易出错,且用户体验 糟糕透顶

组合式方法

如果我们改为围绕 用户意图 设计工具会怎样?调用可能看起来像这样:

[
  "process_guest_checkout",
  "submit_maintenance_request"
]

两个工具。一次自然的对话。复杂性并没有消失——它只是转移到了应有的位置。

Source:

可组合、基于技能的工具设计的九大模式

在实现生产级 MCP 系统后,这些模式决定了优雅架构与脆弱架构的区别。

1. 接受业务标识符,而非系统 ID

错误示例

{
  "contact_id": "003Dn00000QX9fKIAT",
  "booking_id": "a0G8d000002kQoFEAU",
  "room_id":   "a0I8d000001pRmXEAU"
}

正确示例

{
  "guest_email": "beth.gibbs@email.com",
  "room_number": "302"
}

让后端将可读的业务标识符解析为内部 ID。 代理不应需要了解你的数据库模式。

提示: 这适用于 所有 工具参数——不仅仅是主实体。当更新关系(例如,将案件重新分配到其他房间)时,仍然使用业务标识符:

{
  "idempotency_token": "550e8400-e29b-41d4-a716-446655440000",
  "room_number": "402",               // 后端解析为 room_id
  "guest_email": "new.guest@example.com" // 后端解析为 contact_id
}

代理永远不应为获取另一个操作的 ID 而调用 get_room_by_numberget_guest_by_email。每个工具参数都应使用业务标识符,ID 解析全部由后端内部完成。

2. 在工具设计中内置幂等性

所有创建或修改资源的工具都应接受 幂等性令牌

{
  "idempotency_token": "550e8400-e29b-41d4-a716-446655440000",
  "guest_email": "beth.gibbs@email.com",
  "description": "Toilet broken in room 302"
}

当代理重试(它一定会)时,后端识别重复请求并返回原始结果。幂等性是后端的责任,而非代理的责任。

额外收益: 对于跨多个系统的操作(例如跨支付处理和 CRM 更新的退房流程),幂等性令牌可以实现 saga‑pattern 编排。如果支付成功但 CRM 更新失败,后端可以使用该令牌协调补偿事务(如退款),而无需代理介入。

3. 原子化协调状态转换

当客人办理入住时,需要同时完成多件事:

  • 预订状态:Reserved → Checked In
  • 房间状态:Available → Occupied
  • 商机阶段:Pending → Active

这些不应是代理必须协调的三个独立工具。一个工具(check_in_guest)应原子化地编排整个状态转换。

4. 在工具设计中嵌入授权

不要暴露无限制的搜索工具:

[
  "search_all_cases",
  "search_all_rooms",
  "search_all_bookings"
]

而是设计具备适当作用域的工具:

[
  "search_cases_on_behalf_of_guest(guest_email)",
  "search_rooms_on_behalf_of_guest(guest_email)",
  "search_rooms_on_behalf_of_staff(floor_filter, status_filter)"
]

工具接口本身即编码了 可以看到 什么。授权变为声明式而非命令式。

5. 提供智能默认值

尽可能降低代理的认知负荷:

{
  "guest_email": "required",
  "check_in_date": "defaults to today",
  "number_of_guests": "defaults to 1",
  "status_filter": "defaults to 'Open'"
}

代理只需指定真正可变的字段。

6. 记录前置条件和错误

(原文片段已截断 – 请务必为每个工具列出明确的必备前置条件,并提供简洁的错误处理指南。)

7. 支持带有明确语义的部分更新

更新操作应易于推理:

{
  "external_id": "required",
  "check_in_date": "optional – only changes if provided",
  "room_number": "optional – only changes if provided",
  "guest_email": "optional – only changes if provided"
}

“仅提供需要更改的字段——其余保持不变。”
这比强迫代理执行读‑改‑写要简单得多。

8. 创建防御性组合助手

So

Source:

我的操作需要前置条件。与其强迫代理执行 检查‑然后‑创建

# idempotent helper
create_contact_if_not_found(email, first_name, last_name)

此帮助函数可以安全地被编排工具调用,以确保前置条件存在。

9. 为自然语言模式设计

倾听人们实际的说法:

自然语言工具名称
“为 Beth Gibbs 办理入住”check_in_guest
“302 房间的厕所坏了”submit_maintenance_request
“把预订改到 402 房间manage_bookings

工具名称和参数应与用户自然使用的语言保持一致。

TL;DR

  • 围绕用户意图设计工具,而不是低层 API 调用。
  • 使用业务标识符,让后端解析内部 ID。
  • 使每个变更工具具备幂等性,并能够进行原子状态更改。
  • 在工具签名中直接编码授权和合理的默认值。

遵循这些模式,你将把 MCP 从脆弱的“胶水层”转变为强大、可组合的技能集,使 LLM 代理能够像真正的领域专家一样行动。 🚀

r 模式

工具描述应引导代理成功

Check‑in 工具:

“验证客人/预订的前提条件,检查房间空余情况,执行状态转换。返回预订和房间详情或错误代码(404:未找到客人/预订,409:存在多个预订或房间不可用)。”

当代理事先了解失败模式时,它可以优雅地处理这些情况,或在尝试操作之前提出澄清性问题。

可组合工具背后的架构

这些九个模式源自一个统一的架构原则:让 LLM 处理意图,让后端处理执行。

  • LLMs – 为理解人类交流而优化的概率系统。
  • Backends – 为可靠的状态管理和事务一致性而优化的确定性系统。

当你模糊这条边界——让 LLM 编排多步操作,或让后端解析自然语言——就会得到既不可靠不智能的系统。

模式映射

模式组实现功能
1, 5, 9 – 业务标识符、智能默认值、自然语言对齐LLM 处理人类概念;系统层面的细节交给后端。
2, 3, 6 – 幂等性、原子转移、错误模式后端保证可靠性;LLM 无需考虑重试或故障恢复。
4, 7, 8 – 授权范围、部分更新、防御性助手工具接口编码业务规则;后端验证并强制执行约束。

架构收益

当后端负责编排(良好设计):

  • 单一实现,经过测试和验证
  • 事务一致性得到保证
  • 可观察的状态转变
  • 可在不同接口间复用(Web、移动、MCP)

当 LLM 负责编排(糟糕设计):

  • 逻辑分散在对话中
  • 非确定性协调
  • 不透明的失败(难以调试)
  • 上下文膨胀(例如,工具超过 50 个,每个任务调用超过 6 次)

实际影响

在构建 Dewy Resort 应用时,我们逐步用基于技能的架构设计替换了直接的 API 调用和 API 工具包装器。下面的基准测试展示了差异。

指标组合设计前组合设计后
平均响应时间8–12 秒2–4 秒
成功率73 %94 %
工具数量4712
每次交互的平均工具调用次数6.21.8
用户反馈“它能工作,但速度慢,有时会混乱。”“它就是能工作。”

企业 MCP 工具设计实现清单

在为生产系统设计 MCP 工具时,请自问:

身份与解析

  • 工具是否接受业务标识符(电子邮件、姓名、号码)?
  • 后端是否处理 ID 解析?

安全与可靠性

  • 创建工具是否需要幂等性令牌?
  • 状态转换是否原子化?
  • 是否在操作前验证前置条件?

授权与访问

  • 工具是否在接口中编码授权范围?
  • 搜索工具是否限定在适当的上下文中?

认知负荷

  • 工具是否提供合理的默认值?
  • 工具名称是否与自然语言保持一致?
  • 描述是否记录错误模式?

灵活性

  • 更新操作是否支持部分更新?
  • 代理是否可以使用业务标识符修改关系?

更广泛的模式

这些模式适用于您在构建与企业系统和流程交互的 AI 代理的任何场景。

DomainIntent‑driven example
Healthcare“为该患者安排后续随访” → 协调预约预订、通知、记录更新。
Finance“提交这份费用报告” → 处理验证、审批流转、会计分录。
Retail“处理此退货” → 协调库存、退款、客户通知。

问题始终相同: 您是围绕用户意图设计工具,还是围绕 API 操作设计工具?

结论

Enterprise MCP 为您提供工具互操作性的基础。可组合、基于技能的设计是您在此基础上构建有用东西的方式。

该协议无法拯救您免于糟糕的架构,但良好的架构——围绕用户意图组合的工具——可以。

以意图驱动的架构:从好奇到生产级

停止包装 API。开始组合技能。

你的用户会感谢你。你的代理会感谢你。(好吧,你的代理可能不会。)但你的运维团队一定会感谢你。

你在 MCP 工具设计方面有什么经验?

我很想了解你发现了哪些模式。留下评论或在 LinkedIn 上联系——我们分享的模式越多,大家构建更好 AI 系统的速度就越快。

本文基于 Beyond Basic MCP: Why Enterprise AI Needs Composable Architecture 撰写,文中我探讨了使 MCP 在生产环境中有用的架构原则。

Back to Blog

相关文章

阅读更多 »