老胡
发布于 2026-10-13 / 2 阅读
0
0

第六集 把"另一个智能体"当成工具来使 —— A2A Agent 作为函数工具 人话教材

配套代码:dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTool/Program.cs
参考风格:MAF 快速上手系列「用 Executor 与 Edge 编排流程」那集的讲法
适用人群:刚接触 Microsoft Agent Framework(MAF)/ A2A,不想被一堆术语劝退的同学


一、先讲人话:这集到底在干啥?

前面玩的都是"一个智能体自己闷头干"。这集要升级了——

让一个智能体去"召唤"另一个智能体来帮忙,而且召唤的方式,跟调用一个普通函数 / 工具一模一样。

这就是 Program.cs 干的事:它启动一个主智能体,然后把这个主智能体连到一个跑在别处的 A2A 智能体上,让主智能体把那个远程智能体**当成自己的一个"工具"**来用。

打个最土的比方你就懂了:

  • 主智能体 = 你(项目经理,负责跟老板/用户对话)
  • 远程 A2A 智能体 = 隔壁的专家老王(活儿干得溜,但在另一间办公室)
  • Agent Card(智能体名片) = 老王的工位牌 / 简历(写着他叫啥、擅长啥、怎么找到他)
  • AsAIFunction() = 你把老王"接线"到你桌上的呼叫按钮上,按一下,活儿就派给他了,回来的结果也直接显示在屏幕上

一句话总结本集灵魂:用 A2A 协议,把一个远程智能体"伪装"成本地的一个函数工具。


二、核心概念(先认脸,不背定义)

  1. A2A 协议(Agent-to-Agent)
    一套"智能体之间互相打交道"的标准协议。有了它,不管老王的智能体是用什么框架写的、跑在哪台机器上,只要他支持 A2A,你就能跟他对话。相当于两个不同国家的人,约定好都说"普通话"。

  2. Agent Card(智能体名片)
    远程智能体的"电子简历"。里面写明了:他叫什么名字、能干什么(skills)、服务地址在哪。这是 A2A 规定的"自我介绍标准格式"。

  3. A2ACardResolver(名片快递员)
    你给它一个网址(远程智能体的 host),它跑过去把那张 Agent Card 取回来。

  4. AsAIAgent()(翻译官)
    把取回来的 Agent Card 变成 MAF 框架里能直接用的 AIAgent 对象。到这一步,远程老王在你代码里已经是一个"可对话的智能体"了。

  5. AsAIFunction()(变形术)★★★ 本集最关键的魔法
    把上面的 AIAgent 再包一层,伪装成主智能体眼里的"一个函数工具"。这一步让主智能体以为自己只是在调一个普通 tool,实际上背后是跨网络去叫了另一个智能体。

  6. 主智能体 + AIProjectClient
    主智能体跑在 Azure AI Foundry 上(由大模型驱动),它手里拿着工具箱,其中之一就是我们刚包装好的"远程 A2A 智能体"。它自己决定:用户的问题到底要不要叫老王来答。


三、代码逐行拆解(配人话注释)

// ① 先问清楚三件事:在哪、用啥模型、老王住哪
var endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
    ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
var model = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
var a2aAgentHost = Environment.GetEnvironmentVariable("A2A_AGENT_HOST")
    ?? throw new InvalidOperationException("A2A_AGENT_HOST is not set.");

白话:先确认三个配置——endpoint(我在哪个 Foundry 项目)、model(用哪个大模型)、a2aAgentHost(要呼叫的老王住在哪个网址)。后两个缺了就直接报错罢工。

// ② 派快递员去拿老王的"名片"
A2ACardResolver agentCardResolver = new(new Uri(a2aAgentHost));
AgentCard agentCard = await agentCardResolver.GetAgentCardAsync();

白话:把老王的网址交给 A2ACardResolver,它跑过去把 Agent Card(名片)取回来。这张名片里写着老王会干啥、怎么联系他。

// ③ 把名片翻译成 MAF 能用的"智能体对象"
AIAgent a2aAgent = agentCard.AsAIAgent();

白话:AsAIAgent() 就是翻译官——把标准名片变成框架里能直接对话的 AIAgent。注意:到这儿它还是"另一个智能体",还没挂到主智能体上。

// ④ 创建主智能体,并把远程 A2A 智能体当成"一个工具"塞给它 ★重头戏
AIAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
    .AsAIAgent(
        model: model,
        instructions: "You are a helpful assistant. Use the available tools to answer the user's questions.",
        tools: [a2aAgent.AsAIFunction()]   // ← 把老王包成工具,放进主智能体的工具箱
    );

白话逐个拆:

  • AIProjectClient + DefaultAzureCredential:连上 Azure Foundry(用你当前的登录身份做鉴权)。
  • AsAIAgent(...):把主智能体创建出来,给它一句指令"你是个好帮手,用工具回答问题"。
  • tools: [a2aAgent.AsAIFunction()]:这就是灵魂一行。把"远程老王智能体"用 AsAIFunction() 包一下,放进了主智能体的工具箱。从此主智能体调老王,就跟调一个普通函数没两样。
// ⑤ 开工
Console.WriteLine(await agent.RunAsync("What can you help me with?"));

白话:问主智能体一句"你能帮啥"。主智能体会自己判断:这个问题要不要叫老王(远程 A2A 智能体)来答。要的话,它就通过 A2A 协议把任务派过去,把老王的回答收回来,整理好还给你。


四、一张图看懂数据怎么流

   用户提问 "What can you help me with?"
            │
            ▼
   ┌─────────────────────┐
   │   主智能体 (Foundry) │  ← 大模型驱动,负责思考和决策
   │   工具箱里有:        │
   │   [A2A 老王工具]     │
   └─────────┬───────────┘
             │  (判断:这题得问老王)
             │  a2aAgent.AsAIFunction()  ← 伪装成普通工具调用
             ▼
   ┌─────────────────────┐
   │  远程 A2A 智能体(老王)│  ← 通过 A2A 协议跨网络通信
   │  (另一台机器/另一框架)│
   └─────────┬───────────┘
             │  返回结果
             ▼
   主智能体整理答案 → 打印给你

注意:主智能体自己决定要不要叫老王。它不是无脑转发,而是"觉得这个工具能帮上忙"时才去调。这就是 LLM 驱动的 tool calling。


五、把"工作流"那集的思路接上来(对比着学,记忆更牢)

那篇讲 Executor / Edge 的文章里,咱们说**"工作流 = 做什么(Executor) + 怎么流(Edge)"**。本集的"主智能体调远程 A2A 智能体"其实是一脉相承的:

那集(工作流)这集(A2A 当工具)
把一串处理步骤(每个 Executor)用 Edge 串成流水线把"调用另一个智能体"这件事,包装成了一个工具(Tool)
每个 Executor 干一件本地的小活这个"工具"背后是跨网络去叫另一个智能体

将来如果你愿意,完全可以把它塞进工作流的某个 Executor 里——也就是说:

"流水线里的某一步处理"不再是本地代码,而是"去叫另一个智能体来干"。

这就是把单智能体 → 多智能体协作,或者把本地步骤 → 远程协作的桥梁。AsAIFunction() 就是那座桥。

一句话记忆:AsAIFunction() 是"把别的智能体伪装成本地工具"的魔法;A2A 是"两个智能体跨网络通信"的协议。


六、动手改造(建议你自己跑一遍才叫真懂)

前置条件

  1. 一个能跑的远程 A2A 智能体(比如同系列 A2AAgent 那个 sample,先把它起在本地某个端口,例如 http://localhost:8000)。
  2. 一个 Azure AI Foundry 项目,记下它的 endpoint。
  3. 在自己的 shell 里设好环境变量:
$env:FOUNDRY_PROJECT_ENDPOINT = "https://你的foundry.projects.ai.com"
$env:FOUNDRY_MODEL            = "gpt-5.4-mini"
$env:A2A_AGENT_HOST           = "http://localhost:8000"   # 远程 A2A 智能体的地址

然后 dotnet run 就能看到主智能体把问题派给老王、再把答案带回来的全过程。

改造练习

  • 练习 1(看路由):把最后那句提问换成更具体的,比如 "帮我查一下今天北京天气"(前提是老王智能体支持这类技能),观察主智能体是否会去呼叫它。
  • 练习 2(改人设):把 instructions 从 "You are a helpful assistant..." 改得更"强势"一点,比如要求"必须先调用工具、再总结",看看主智能体行为有啥差异。
  • 练习 3(进阶·接回工作流):回忆 Executor 那集,把 a2aAgent.AsAIFunction() 当作一个工具,放进某个 Executor 的 HandleAsync 内部——让"流水线某一步"实际去召唤远程智能体。这一步做完,你就把两集串起来了。

七、教学要点小结(背这 6 条就够了)

  1. A2A = 智能体之间的标准通信协议,跨框架、跨机器都能聊。
  2. Agent Card = 远程智能体的"简历/名片",A2ACardResolver 负责去取它。
  3. AsAIAgent() = 把名片变成本地可用的智能体对象(翻译官)。
  4. AsAIFunction() = 把远程智能体"伪装"成主智能体手里的普通工具,这是本集的灵魂一行。
  5. 主智能体跑在 Azure Foundry(大模型驱动),它自己决定什么时候该去呼叫远程老王(LLM 驱动的 tool calling)。
  6. DefaultAzureCredential 适合本地开发;上线请换更具体的凭据(如 ManagedIdentityCredential),避免鉴权探活慢、还有潜在安全风险。

一句话收尾:这集教会你的,是"让智能体不再单打独斗"的第一招——把队友(另一个智能体)变成自己手边随手可按的工具。


评论