MoEngage 如何用 AgentCore Gateway 将 400 多个 API 变成智能体工具
How MoEngage turned over 400 APIs into agent tools with AgentCore Gateway
MoEngage 利用 Amazon Bedrock AgentCore Gateway 将约 400 个内部 REST API 转化为权限受限的 MCP 智能体工具,估算节省了六个月的平台工程工作量,新 API 接入时间从数天缩短到数小时。
本文由 MoEngage 的 Nikhil Kumar 和 Shakir Khan 共同撰写。
当工程团队开始构建 AI 智能体时,这些智能体所需的能力通常已经以内部 API 的形式存在。活动创建、细分查询、发送统计。挑战不在于构建新功能,而在于让智能体能够安全地访问数百个私有 API,同时不将它们暴露给公共互联网,不让模型控制身份,也不用数百个工具定义淹没上下文窗口。
MoEngage 是一个智能化的客户互动平台,超过 1,350 个消费品牌使用它跨推送、电子邮件、应用内、SMS、WhatsApp 和 RCS 提供个性化体验,它面临的正是这个问题。该产品背后约有 400 个内部 REST API,由 25 个工程团队构建,运行在横跨七个 AWS 区域的私有虚拟私有云(VPC)中。MoEngage 对这些 API 进行区域隔离以满足数据驻留承诺,且不将其暴露给公共互联网。智能体所需的能力早已在生产环境中运行。如何安全地让它可访问,才是真正的难点。
在本文中,我们将介绍 MoEngage 如何使用 Amazon Bedrock AgentCore 的一项能力 AgentCore Gateway,将这些私有 API 转化为带权限范围限制的智能体工具。这一方案为 MoEngage 节省了预计六个月的平台工程工作量,现在接入一个新 API 只需数小时而非数天。没有产品团队编写 Model Context Protocol(MCP)服务器代码,没有为每次集成单独配置网络,整个路径中也不存在任何公共端点。
为什么 20 个 MCP 服务器是错误的答案
Model Context Protocol(MCP)为智能体提供了一种发现和调用工具的标准方式。采用它的直接做法是让每个团队暴露自己的 MCP 服务器。MoEngage 对此进行了成本评估并予以否决。
超过 20 个 MCP 服务器意味着要监控、打补丁并保持协议兼容的部署超过 20 个,而且 MCP 还在不断演进。跨领域的智能体——也就是那些能读取活动、检查其细分并查看发送统计的有用智能体——需要连接多个服务器并协调它们。而且每个团队都需要掌握与其实际产品工作无关的 MCP 专业知识。
因此 MoEngage 确立了一条影响后续一切工作的原则:各个工程团队不实现 MCP。团队构建 REST API 并声明哪些操作应成为工具。AI 平台团队端到端地拥有 MCP 层。团队的责任止于一份描述清晰的 OpenAPI 规范。
这一原则立即产生了三项要求:一个智能体只需连接一次而非逐服务连接的中央工具界面、由服务端强制实施而非依赖约定的按智能体授权,以及无需为每个新 API 重新构建的私有连接。
从零开始构建所有这些也是一种选择。自定义中央 MCP 服务器可以解决 20 个服务器的问题,但会让 MoEngage 还要构建工具生成流水线、策略文件格式、用于重命名的工具别名注册表以及熔断器。全是自定义代码,全由一个团队负责,全都是无法让 MoEngage 的智能体更聪明或让客户的营销活动更好的非差异化工作。
塑造架构的四个约束
在评估任何方案之前,MoEngage 定义了四个约束。四个约束必须同时满足。只解决其中两个却破坏第三和第四个的方案是不可行的。
绝不暴露公网端点。按照政策,MoEngage 的内部 API 都是 VPC 私有的。任何需要暴露它们的方案,即使背后加了额外的身份验证,都是行不通的。传统变通方法是针对每个集成做网络配置:这里架一个 NAT 网关,那里放一个反向代理,第三个再建一条 VPN 隧道。乘以七个区域,运维面比智能体的集合增长得更快。每新增一个 API 都意味着又一次网络变更请求、又一轮安全评审、又一套需要维护的基础设施。
身份不能由智能体决定。每次内部 API 调用都携带租户、用户、角色和数据库上下文。如果这些上下文是大型语言模型(LLM)可见的工具参数,模型可能会弄错它,或者被诱导修改它。MoEngage 需要让身份在结构上处于模型接触不到的地方,由调用服务注入,而不作为模型可以设置或覆盖的内容暴露出来。
一个 400 个工具的目录塞不进上下文窗口。每个工具的 JSON schema 大约占 50 个 token,整个系统的完整 tools/list 每次连接都要传输超过 20,000 个 token。这笔 token 预算在智能体做任何有用的事之前就被花掉了,还挤占了智能体真正需要用来对任务进行推理的上下文。客户端过滤(ADK 或 MCP SDK 中的 tool_filter)解决不了这个问题。完整目录已经越过了网络,而按客户端约定过滤并不是授权边界。本不应看到写操作的智能体从一开始就不应该收到它们。
正是最后这条约束排除了那些显而易见的替代方案。自建一个集中式 MCP 服务器能解决 20 个服务器的问题,但 MoEngage 仍需构建工具生成流水线、策略文件格式、用于重命名的工具别名注册表以及熔断器。全都是定制代码,全归一个团队所有,却没有一样能让 MoEngage 的产品变得更好。
AgentCore Gateway 如何对应这些约束
Amazon Bedrock AgentCore Gateway 是一个全托管的 MCP 服务器。你把 OpenAPI 规范注册为目标,网关据此生成 MCP 工具,然后把调用路由到后端服务。它有四个特性与 MoEngage 的约束直接对应。
输入 OpenAPI,输出 MCP 工具。无需构建或运维 MCP 服务器,也没有需要与 API 保持同步的独立产物。各团队已经维护的规范就是工具定义。每个操作都变成一个名为 Target___operationId 的工具,例如 CampaignsAPI___searchCampaigns。规范变了,工具就变。团队新增一个端点,工具就出现。团队删除一个端点,工具就消失。无需部署,无需协议专业知识,无需与 AI Platform 团队协调。
Cedar 策略在目录序列化之前进行评估。AgentCore 策略引擎使用部分评估按主体过滤 tools/list。每个智能体以自己的 IAM 角色进行身份验证,其 Cedar 策略决定它可以看到和调用哪些工具。这正是让 400 个工具变得可控的原因。这不是事后在客户端过滤,而是在构建响应之前在服务端限定范围。
例如,一个活动 QA 机器人获得对活动和细分工具的读取权限,但不能调用写操作:
permit(
principal == Agent::"campaign-qa-bot-v1",
action == Action::"tools/call",
resource in ToolGroup::"campaigns-read"
);
forbid(
principal == Agent::"campaign-qa-bot-v1",
action == Action::"tools/call",
resource in ToolGroup::"write-actions"
);机器人在其 tools/list 响应中看不到写工具。客户端没有任何东西需要过滤,因为这些工具根本没有被发送。
通过 Amazon VPC Lattice 进行托管出站。网关通过 AgentCore 托管出站而非 MoEngage 自建并运维的网络来访问其内部服务。入站方向,网关只能通过带私有 DNS 的 VPC 接口端点访问,没有公共路径。接入新 API 只需添加一个目标,而不需要改动网络拓扑。正是这一特性打破了乘以七个区域的难题。网络只需存在一份,每个 API 都能使用它。
语义搜索作为网关能力。借助 x_amz_bedrock_agentcore_search 能力,智能体可以通过描述任务来查找工具,而不必说出工具名称。这消除了自建发现层的需要,也顺带消除了重命名的脆弱性:如果 get_segment_user_count 改名为 get_segment_size,智能体搜索segment user count 依然能找到它,因为匹配基于描述而非标识符。
实际运行中的架构
从智能体到内部 API 的请求流程很短,每一层都有明确的职责。智能体服务通过 VPC 接口端点连接到 AgentCore Gateway,后者评估 Cedar 策略,并通过 Amazon VPC Lattice 上的托管出站将工具调用路由到内部 API。
下图展示了各组件如何连接。
图 1:MoEngage AgentCore Gateway 架构
运行在 Amazon Elastic Compute Cloud(Amazon EC2)或容器中的智能体服务调用 MCP 客户端。客户端可以是官方 MCP SDK、LangChain、Google ADK 或 MoEngage 自己的智能体运行器。客户端使用该服务已有的 AWS Identity and Access Management(IAM)角色以 SigV4 对请求进行签名。请求通过 HTTPS 经由带私有 DNS 的 VPC 接口端点传输,没有公共跳数。AgentCore Gateway 接收请求并评估 Cedar 策略,确定该主体可以看到或调用什么,然后通过 Amazon VPC Lattice 上的托管出站将工具调用路由到内部 API。
MoEngage 做出了两个值得说明的身份验证决策。
第一,内部网关选择 SigV4 而非 JWT。这里的智能体是已经拥有 IAM 角色的服务。使用 SigV4,它们可以用已有的凭证签名,无需签发、轮换或存储任何新东西。MoEngage 独立的公共 MCP 服务器(为来自 Claude 等客户端的人类身份验证会话提供服务)则在 AgentCore runtime 上使用 OIDC JWT。那是另一个具有不同需求的接口面。
第二,身份信息以标头传递,而不是作为工具参数。租户、用户、角色和数据库上下文由客户端库从一个小的 Identity 对象转换为 X-MOE-* 个标头。模型看不到它们,也无法设置它们。一个对构建类似系统的人很重要的细节:由于 SigV4 签名占用了 Authorization 标头,调用方转发的 bearer 令牌必须放到别处。MoEngage 将其作为一个专用的带 MoEngage 前缀的标头来传递。标头传递也有上限,因此标头集合是经过深思熟虑的,而不是逐渐累积的。
发现和执行有不同的身份要求。列出和搜索工具不携带租户身份,只有 tools/call 才携带。这种分离使发现保持低成本,并使审计轨迹毫无歧义:每个携带身份的请求都是一次真实的数据访问。
即使通过 Cedar 将智能体限定在目录的一个子集中,这个子集也可能很大。MoEngage 的客户端库向模型暴露了一个单一的元工具 search_tools。模型描述自己的需求,网关的语义搜索返回候选结果,只有这些候选结果在该轮次的剩余部分中变为可调用。完整目录不会进入上下文。智能体的上下文在开始时只包含一个工具定义,搜索后也只包含少量相关定义,而不是几百个它永远不会用到的定义。
接入一个新 API
团队将一个 API 暴露为智能体工具,只需在其现有的 OpenAPI 规范中添加一个代码块:
paths:
/campaigns:
get:
operationId: listCampaigns
description: |
Returns a paginated list of campaigns. Supports filtering by
status, channel, date range, and tags.
x-moe-mcp:
expose:
external: false
internal: true
tool_description: |
Find campaigns by status or channel. Returns campaign ID, name,
status, and channel. Use the returned campaign_id with
get_campaign for full details, or send_campaign to launch it.
param_overrides:
status:
description: |
Filter by campaign status. Use 'active' for running
campaigns, 'scheduled' for upcoming. Omit for all.该代码块中的四个设计选择值得解释一下。
暴露是显式的选择性加入。没有 x-moe-mcp 代码块的端点就不是一个工具。持续集成(CI)不会对其缺失发出警告,因为对大多数端点来说,缺失才是正确状态。缓存预热路由和迁移触发器不是智能体工具,任何东西都不应该引导团队把它们变成智能体工具。通过 internal 和 external 标志,团队可以将某个操作暴露给内部智能体、公共 MCP 服务器,或同时暴露给两者,各自独立。
工具描述不是 API 描述。API 描述告诉阅读文档的开发者某个端点返回什么。工具描述则告诉模型何时使用这个工具以及传入什么参数。MoEngage 发现,原样复用 API 描述会导致糟糕的工具选择。模型会根据实现细节(“返回一个分页列表”)而不是意图(“按状态或渠道查找营销活动”)来选择工具。tool_description 和 param_overrides 字段的存在正是为了让团队有一个地方编写面向模型的文本,而无需更改面向开发者的文档。
CI 流水线处理从规范到可运行工具的一切。当团队推送规范更改时,流水线会验证 x-moe-mcp 代码块,向 AgentCore Gateway 注册目标,并应用 Cedar 策略。无需手动步骤,无需向 AI 平台团队提交工单。一个从未考虑过 MCP 的团队,只需等待流水线运行完成,就能让一个操作作为智能体工具可用。
客户端库有两种模式。插件模式将 AgentCore Gateway MCP 服务器封装在 LangChain、Google ADK 或兼容 MCP 的框架中,作为工具提供者。智能体运行器模式则为想要独立智能体而不采用框架的团队运行完整的智能体循环。
MoEngage 目前的运行情况
MoEngage 的 AgentCore Gateway 部署目前运行在七个 AWS 区域。该网关向 10 个内部智能体提供大约 400 个已注册的 API 操作。没有任何产品团队编写过 MCP 服务器代码。没有任何产品团队为智能体集成配置过网络。没有任何产品团队实现过 Cedar 策略。这些都由 AI 平台团队负责,接入一个新 API 的成本只是一条规范注释和一次 CI 运行。AgentCore 功能可用性因区域而异。有关支持的区域,请参阅 Amazon Bedrock AgentCore 文档。
该系统已在生产环境中运行了三个月。这些智能体覆盖了营销团队成员日常接触的整个工作流。一个活动质检智能体在发布前验证定向规则和内容。一个投递健康智能体监控各渠道的发送率和退信模式。一个细分构建智能体将自然语言的受众描述转换为细分查询。每个智能体都使用自己的 IAM 角色进行身份验证,只能看到其 Cedar 策略允许的工具,并通过描述任务来发现工具,而不是记住工具名称。MoEngage 估计,通过免除构建和运维 20 个独立 MCP 服务器以及在 7 个区域配置每 API 网络的工作,这种方法节省了大约六个月的工程工作量。现在接入一个新 API 只需数小时的规格标注,而不再是数天的网络配置和安全审查。
搜索优先的模式改变了 MoEngage 对智能体设计的思考方式。新的智能体从零硬编码工具引用开始。它们描述意图,接收候选工具,然后调用它们找到的工具。当某个团队重命名一个操作或发布一个新操作时,现有的智能体无需更改代码即可自动适配。
结论
MoEngage 方法背后的关键洞察是一种关注点分离,它契合工程组织的实际运作方式。产品团队拥有 API 并对其进行良好的描述。平台团队负责协议层、策略层和连接层。AgentCore Gateway 通过消除中间那些无差异的工作让这种分工变得切实可行:无需 MCP 服务器代码,无需每 API 网络,也无需把客户端过滤伪装成授权。
其结果是一个这样的智能体系统:添加新工具只需修改规格,限制访问只需修改策略,而且两者都不需要另一个团队的参与。
要开始使用 AgentCore Gateway,请参阅 Amazon Bedrock AgentCore 文档。有关 Cedar 策略编写,请参阅 Cedar 策略语言参考。有关私有连接模式,请参阅 Amazon VPC Lattice 文档。
要讨论为您的组织采用类似的架构,请联系您的 AWS 客户团队或联系 AWS。
关于作者
来源: AWS Machine Learning Blog · aws.amazon.com
