为 Foundry 托管代理开启 A2A 端点和 Agent Card
最近在给 Foundry 上的托管代理配置对外能力时,碰到了一个绕不开的问题:怎么让别的 agent 框架也能发现我这个代理、跟它对话。翻了一圈 Microsoft Foundry 的文档,发现官方已经把 Agent-to-Agent(A2A)协议接进了托管代理里,跨框架、跨技术栈的代理之间能直接打通。原文在这儿:Enabling A2A endpoint and Agent Card for a Hosted Agent(原文作者 srisatyakrishna5,发布于 2026 年 8 月 24 日)。我把自己跑通的过程和踩的坑整理了一下。
写代理这件事,本来就不该让你把身份验证、入口管理、A2A 传输、发现端点这些平台该干的活儿再重新造一遍轮子。用 Foundry 托管代理的话,这些能力是继承来的,专心写业务逻辑就行。
这篇笔记记录的是从"本地跑着一个容器化代理"到"在 Foundry 上有一个可被发现的 A2A 端点"最短的一条路。核心是怎么打开 A2A 支持,让别的代理通过 agent card 发现你、并用标准的 Agent-to-Agent 协议跟你对话。
重要提示 托管代理这块的接口还在变。把你的 Azure Developer CLI(azd)扩展和 SDK 版本钉死,字段名对着你环境里装的 schema 核对一遍再用。
最终会得到什么
跑完这一套,你手上会有:
- 一个监听 8088 端口的容器
- 一个原生的
POST /invocations端点 - 一个作为 A2A 桥接层的
POST /responses端点 - 一个受 Entra 保护的 Foundry 端点
- 一张能被其他代理拉取到的 agent card
我这边用的例子是一个请求校验代理:接收结构化 JSON,返回 approve、reject 或 needs_human_review 这样的判定结果。换成别的业务场景,思路是一样的。
核心思路:两个互相独立的配置面
托管代理的部署其实是两个相关但彼此独立的配置面拼起来的:
| 配置面 | 定义的内容 | 配置方式 |
|---|---|---|
| 控制面(Control plane) | 代理版本、镜像或源码构建方式、环境、资源、支持的协议版本 | azure.yaml 和 azd |
| 数据面(Data plane) | 线上端点、身份验证、启用的协议、发布的 agent card | agentEndpoint 和端点更新命令 |
如果协议只在代理版本里声明了,端点那边没配,调用方照样用不了;反过来也一样,两边都得声明。我一开始就漏了端点这一步,调了半天以为代码有问题,后来才发现是配置面没对齐。
请求路径大概长这样:
A2A client
| JSON-RPC over HTTPS with an Entra bearer token
v
Foundry data plane
| validates identity, terminates A2A, and bridges to Responses
v
Your container on port 8088
|-- GET /readiness
|-- POST /invocations
|-- POST /responses
这张图其实在提醒一件事:不要在 Foundry 托管的容器里自己加一套应用层的 A2A 路由,这层传输 Foundry 已经包了。
先把运行时契约搭好
配置 azure.yaml 之前,容器得先满足托管运行时的几个要求:
- 用纯 HTTP 在 8088 端口上服务,TLS 在上游终止
- 提供一个快速、轻量的
GET /readiness检查 - 处理
SIGTERM,把正在处理的请求跑完 - 发出 OpenTelemetry 信号,方便平台侧做关联
Azure 的 agent server 包已经把托管相关的部分封装好了,一组典型依赖大致是:
dependencies = [
"azure-ai-agentserver-core==2.0.0",
"azure-ai-agentserver-invocations==1.0.0",
"azure-ai-agentserver-responses==1.0.0b9",
"agent-framework-core==1.13.0",
"agent-framework-foundry==1.10.4",
]
readiness 检查要保持"轻"
不要把下游依赖(模型、数据库、参考数据服务)的探活塞进 /readiness。这些服务临时抖一下不代表容器本身出了问题,平台不该因为这个把一个明明还能正常处理请求的容器重启掉。
深度的运维诊断另开一个端点,比如 GET /health。
让 Invocations 和 Responses 共用一个宿主
Invocations 是直接、原生的协议;Responses 是 Foundry 用来接 A2A 流量的"聊天形状"协议,相当于一座桥。
把两个协议宿主组合起来,指向同一个服务:
|
|
共用一个 service 很关键:同一个请求,不管是从直接调用进来还是从 A2A 进来,最后给出的判定结果、发现项、关联数据和错误结构都应该是一样的。
原生的 invocation handler 可以写得很朴素:
|
|
失败信息统一放进一个稳定的结构里返回,别把堆栈、内部异常文字或者下游依赖的原始报文直接甩给调用方:
|
|
配置托管代理
下面是 azure.yaml 的关键结构。环境相关的值放到 azd 环境变量或者基础设施输出里,别写死在应用代码里做分支判断。
|
|
Remote Build 这个特性能让 Foundry 直接从你发布的源码构建镜像。Dockerfile 还是留着,本地复现,或者以后需要自定义基础镜像、装系统包的时候用得上。
注意
azure.yaml是要提交到源码仓库的。名称、URL、调优参数写在里面没问题,但密钥这类东西还是放 Key Vault,启动时通过托管身份去取。
通过端点打开 A2A
A2A 是在数据面的端点上启用的。等版本层的协议声明完了,再加上 agent card 和端点配置。
其他代理和那些由模型驱动的规划器,靠这张卡片判断要不要调用你的代理。写它的时候按"路由用"的思路来,别写成营销文案。输入是什么、输出是什么、终态结果、模态和限制,说清楚就行。
一段好用的卡片描述,基本上就是快速回答三个问题:
- 这个代理接受什么样的信息?
- 它返回什么?
- 什么情况下该选它?
|
|
这里有个不对称的地方,我第一次看也没留意:
responses和invocations写在代理版本里,因为容器自己实现了这两个协议。a2a写在端点里,因为 A2A 传输是 Foundry 提供并对外暴露的,不是容器自己实现的。responses必须先启用,因为它是 A2A 的桥接目标。
部署完代理版本之后,再应用端点配置:
|
|
跨平台边界保留关联 ID
Foundry 会把 x-client- 前缀的头转发进容器。统一用 x-client-correlation-id,按下面这个优先级来解析:
x-client-correlation-id头- W3C
traceparent头里的 trace ID - 都没有的话,新生成一个 UUID
同时把平台的这些标识也绑到日志和审计记录里:
| 请求头 | 审计/日志字段 |
|---|---|
x-agent-session-id |
agent_session_id |
x-agent-user-id |
agent_user_id |
x-agent-foundry-call-id |
foundry_call_id |
关联 ID 要在响应头和响应体里都回显一遍,往下游调用继续传,也要写进审计存储里。调用方拿到的报告、容器里的 trace、审计记录、平台侧的日志,最好都能靠这一个值串起来,出问题排查的时候能省不少事。
部署成功不代表配置生效了,端点协议有没有真正应用上,得单独验证,我踩过这个坑:部署脚本跑完显示成功,结果调用方还是拿不到 A2A 协议,最后发现是忘了单独跑端点更新命令。
检查线上的协议
|
|
确认端点报出来的协议里同时有 a2a 和 responses。
拉取发布出去的 agent card
|
|
如果拿到 404,八成是端点更新没生效。要是卡片能拉到但调用失败,先去确认端点的协议列表里有没有 responses。
用 A2A 协议暴露代理的时候,有几个限制得留意:
- 只支持文本模态,文件数据和其它非文本模态都不支持。
- 不支持流式响应(server-sent events)。
- 走 A2A 进来的请求依赖 responses 协议,没实现 responses 协议的代理没法暴露成 A2A 端点。
流式响应这条我一开始没太在意,本来想给校验结果加个实时进度提示,试了才发现走不通,只能退回成一次性返回结果。这种限制早知道比晚知道省事。
排查清单
托管的 A2A 部署跑不通的时候,我一般按这个顺序挨个查:
- 容器是不是监听在 8088 端口、用的纯 HTTP。
- 版本层的两个协议是不是都声明了。
- 端点是不是启用了
responses、invocations、a2a这三个。 - 端点更新命令是不是在部署完代理版本之后才跑的。
- agent card 的 URL 能不能正常返回。
- 调用方的工作负载身份是不是有对应的 Foundry 权限。
- 去掉可能的 Markdown 代码块围栏之后,JSON 文本能不能正常解析。
- 关联头是不是都用了
x-client-前缀。 - readiness 是不是还保持轻量、快速。
参考资料:
- 本文作者:BeanHsiang
- 本文链接:https://beanhsiang.github.io/post/2026-08-25-enabling-a2a-endpoint-and-agent-card-for-a-hosted-agent/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。