在智能体中使用A2A工具:让大模型自己决定“该找谁帮忙“

在智能体中使用A2A工具:让大模型自己决定"该找谁帮忙"

写在前面

前面三篇文章把A2A的基础都讲了------设计动机、原生SDK实战、HelloAgents封装。但有一个问题还没深入聊:A2A连接建立之后,大模型到底是怎么决定"这个任务我该自己做,还是该委托给其他Agent"的?

这是A2A在智能体中真正落地的关键。协议只是通道,真正的"智能"体现在决策上------Agent要能判断自己的能力边界,知道什么时候该求助,求助该找谁。

这篇文章我就来聊聊这个话题。我会从最核心的思路讲起,然后给一个完整的代码例子,最后分享一些让Agent协作更稳定的技巧。

一、核心思路:把其他Agent变成"工具"

要理解A2A在智能体中的使用方式,首先要转变一个观念:其他Agent对你的大模型来说,就是一个"工具"。

跟MCP工具一样,A2A Agent也可以被封装成大模型可调用的Function。大模型看到这个Function的描述,判断"这个任务需要调用它",然后输出一个工具调用请求,你的代码执行这个请求------通过A2A协议把任务发给目标Agent,等结果回来,再塞给大模型。

这个思路跟MCP工具调用完全一样,区别只在于:

  • MCP工具是"死"的,你传什么参数它返回什么结果
  • A2A Agent是"活"的,它有自己的大模型,能理解任务、多轮对话、自主处理

但对调用方的大模型来说,它不需要知道这些区别。它只需要知道:"有一个叫'机票助手'的工具,输入目的地和日期,能返回机票信息。"至于是不是Agent在背后处理,它不关心。

这种设计的好处是:你不需要为A2A单独设计一套决策机制,直接复用大模型已有的Function Calling能力就行。

二、怎么把A2A Agent注册为LLM工具

具体怎么做呢?我以OpenAI的Function Calling格式为例,看看怎么把一个A2A Agent包装成工具:

python 复制代码
class A2ATool:
    """把A2A Agent包装成大模型可调用的工具"""
    
    def __init__(self, agent_client, tool_name, tool_description):
        self.client = agent_client  # A2AClient实例
        self.tool_name = tool_name
        self.tool_description = tool_description
    
    def to_llm_tool(self):
        """转换成大模型的Function Calling格式"""
        return {
            "type": "function",
            "function": {
                "name": self.tool_name,
                "description": self.tool_description,
                "parameters": {
                    "type": "object",
                    "properties": {
                        "task": {
                            "type": "string",
                            "description": "要委托的任务描述,尽量详细"
                        },
                        "context": {
                            "type": "string",
                            "description": "相关上下文信息(可选)"
                        }
                    },
                    "required": ["task"]
                }
            }
        }
    
    async def execute(self, arguments):
        """执行工具调用:通过A2A把任务发给目标Agent"""
        task_input = {
            "task": arguments.get("task", ""),
            "context": arguments.get("context", "")
        }
        result = await self.client.create_task_and_wait(
            input=task_input,
            timeout=60
        )
        if result.success:
            return str(result.output)
        else:
            return f"任务失败:{result.error}"

然后在你的Agent里,把所有可用的A2A Agent都注册成工具:

python 复制代码
class MyAgent:
    def __init__(self):
        self.a2a_tools = []
        self.llm_tools = []
    
    async def register_agent(self, agent_url, tool_name, description):
        """注册一个A2A Agent为工具"""
        client = await A2AClient.connect(agent_url)
        tool = A2ATool(client, tool_name, description)
        self.a2a_tools.append(tool)
        self.llm_tools.append(tool.to_llm_tool())
    
    async def setup(self):
        # 注册机票Agent
        await self.register_agent(
            agent_url="http://localhost:8001",
            tool_name="book_flight",
            description="查询和预订机票。输入:目的地、日期、舱位偏好。输出:航班信息或预订结果。"
        )
        # 注册酒店Agent
        await self.register_agent(
            agent_url="http://localhost:8002",
            tool_name="book_hotel",
            description="查询和预订酒店。输入:城市、入住日期、离店日期、价格偏好。输出:酒店列表或预订结果。"
        )

这样一来,大模型在做决策的时候,就能看到book_flight和book_hotel两个工具。它会根据用户的问题,自主决定调用哪个、怎么传参数。

三、大模型的决策过程:什么时候该委托?

把Agent注册成工具之后,大模型的决策过程其实跟普通工具调用是一样的。但因为A2A Agent比较"重"(调用成本高、耗时长),你需要在工具描述上做一些引导,让大模型做出更合理的决策。

我总结了几个引导大模型正确决策的技巧:

技巧1:在描述里写清楚"什么时候该用"。

不要只写"查询机票",要写"当用户需要查询或预订机票时使用,不要用于查询酒店或火车票"。明确的使用场景能大幅减少误调用。

技巧2:写清楚"什么时候不该用"。

比如"仅在用户明确要求查询实时机票信息时使用,不要用于回答一般性的航空知识问题"。排除场景跟包含场景同样重要。

技巧3:说明输入要求。

"调用前必须确认用户的目的地和出发日期,如果用户没说,先询问用户,不要猜测。"这样能避免大模型在信息不全的情况下贸然调用。

技巧4:说明输出格式。

"返回结果包含航班号、时间、价格,直接转述给用户即可,不需要额外处理。"让大模型知道拿到结果后该怎么处理。

我做过对比,加上这些引导之后,大模型的工具调用准确率提升了差不多30%。以前经常出现"用户问天气它调机票工具"的情况,加了描述之后基本没有了。

四、多Agent协作的完整流程

我们来看一个完整的多Agent协作流程。假设用户说"帮我安排一下下周去上海的出差,订机票和酒店"。

整个流程是这样的:

第1步:大模型分析任务

大模型收到用户请求,判断需要调用两个工具:book_flight和book_hotel。它可能先调用机票,也可能两个并行调用(取决于大模型的能力)。

第2步:委托给机票Agent

大模型输出工具调用:

json 复制代码
{
  "name": "book_flight",
  "arguments": {
    "task": "查询下周从北京到上海的机票,用户偏好上午的航班,经济舱",
    "context": "用户是商务出差,公司报销,预算不是问题"
  }
}

你的代码通过A2A把这个任务发给机票Agent。机票Agent收到任务后,可能会:

  • 调用自己的MCP工具(航班查询API)
  • 如果信息不全,可能会通过A2A回复要求补充信息
  • 处理完之后返回结果

第3步:委托给酒店Agent

类似地,大模型调用book_hotel工具,把酒店预订任务委托给酒店Agent。

第4步:汇总结果

两个Agent都返回结果后,大模型把结果整理成自然语言,回复给用户:

"已为您查询到以下方案:

航班:国航CA1501,下周一08:30起飞,票价580元

酒店:如家酒店(人民广场店),标准间299元/晚

需要我帮您预订吗?"

第5步:后续交互

如果用户说"就订这个",大模型会再次调用对应的Agent工具,完成预订。

整个过程中,主Agent不需要知道机票和酒店是怎么查的,它只需要负责"委托"和"汇总"。具体的执行由各个专业Agent完成。这就是多Agent协作的精髓------分工明确,各司其职。

五、一个完整的代码例子

光说不练假把式,我写一个简化但完整的例子,把上面的流程落地:

python 复制代码
import asyncio
import json
from helloagents import A2AClient

class A2ATool:
    def __init__(self, client, name, description):
        self.client = client
        self.name = name
        self.description = description
    
    def to_function(self):
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": {
                    "type": "object",
                    "properties": {
                        "task": {"type": "string", "description": "任务描述"}
                    },
                    "required": ["task"]
                }
            }
        }
    
    async def run(self, task):
        result = await self.client.create_task_and_wait(
            input={"task": task}, timeout=60
        )
        return json.dumps(result.output, ensure_ascii=False) if result.success else f"失败:{result.error}"

class TravelAgent:
    def __init__(self, llm_client):
        self.llm = llm_client
        self.tools = []
        self.tool_map = {}
    
    async def register_agent(self, url, name, desc):
        client = await A2AClient.connect(url)
        tool = A2ATool(client, name, desc)
        self.tools.append(tool.to_function())
        self.tool_map[name] = tool
    
    async def chat(self, user_message):
        messages = [
            {"role": "system", "content": """你是一个旅行助手。
你可以调用机票和酒店工具来帮助用户。
调用工具前确认用户需求明确,信息不全时先询问。
拿到结果后用自然语言总结给用户。"""},
            {"role": "user", "content": user_message}
        ]
        
        while True:
            response = await self.llm.chat(messages, tools=self.tools)
            
            if not response.tool_calls:
                return response.content
            
            for call in response.tool_calls:
                tool_name = call.function.name
                args = json.loads(call.function.arguments)
                tool = self.tool_map[tool_name]
                
                print(f"[委托任务] {tool_name}: {args['task']}")
                result = await tool.run(args["task"])
                print(f"[收到结果] {result[:100]}...")
                
                messages.append({"role": "assistant", "content": None, "tool_calls": [call]})
                messages.append({"role": "tool", "tool_call_id": call.id, "content": result})

# 使用示例
async def main():
    # 假设llm_client是你自己封装的大模型客户端
    agent = TravelAgent(llm_client)
    
    # 注册两个A2A Agent
    await agent.register_agent(
        "http://localhost:8001",
        "flight_assistant",
        "查询和预订机票。当用户需要机票信息时调用,输入任务描述,返回航班信息。"
    )
    await agent.register_agent(
        "http://localhost:8002",
        "hotel_assistant",
        "查询和预订酒店。当用户需要酒店信息时调用,输入任务描述,返回酒店信息。"
    )
    
    # 跟用户对话
    reply = await agent.chat("帮我查下周三从北京到上海的机票,顺便推荐一家酒店")
    print(f"\n最终回复:{reply}")

if __name__ == "__main__":
    asyncio.run(main())

这个例子虽然简化了大模型客户端的部分,但核心逻辑是完整的------注册Agent工具、大模型决策调用、A2A委托执行、结果汇总。你可以把llm_client换成你实际用的大模型API。

六、处理协作中的异常

多Agent协作比单Agent复杂,出错的地方也更多。我总结了几种常见异常和处理方式:

异常1:目标Agent不在线。

A2AClient连接失败,或者连接上了但任务超时。处理方式:重试2-3次,还是失败的话告诉用户"该服务暂时不可用",并提供替代方案(比如建议用户手动查询)。

异常2:Agent返回结果格式不对。

你期望返回JSON,结果Agent回了一段自然语言。处理方式:在工具描述里明确要求返回格式,同时在代码里做容错------如果解析失败,把原始文本传给大模型,让它自己理解。

异常3:Agent之间产生分歧。

比如机票Agent说"这个时间没有航班",酒店Agent说"这个日期酒店满房",两个结果矛盾。处理方式:把两个结果都给大模型,让它判断怎么跟用户解释,或者尝试调整方案。

异常4:循环调用。

A委托B,B又委托A,形成死循环。处理方式:给任务加一个"深度"参数,每委托一次深度+1,超过阈值就终止。我一般设最大深度为3层。

异常5:部分成功部分失败。

机票查到了,酒店没查到。处理方式:把成功的部分先告诉用户,失败的部分说明原因并询问是否需要换个方式。

这些异常处理看起来琐碎,但在生产环境中非常重要。多Agent系统的稳定性,很大程度上取决于异常处理做得好不好。

七、几个让协作更顺畅的技巧

最后分享几个我在实践中总结的技巧:

技巧1:给每个Agent设定清晰的"能力边界"。

在System Prompt里明确告诉主Agent:"机票Agent只负责机票,酒店Agent只负责酒店,不要让它们做超出能力范围的事。"边界清晰,协作才不会乱。

技巧2:控制同时协作的Agent数量。

不要一次让大模型调用五六个Agent,信息太多它处理不过来。我一般控制在2-3个并行,更多的话分批处理。

技巧3:在Agent之间传递结构化信息。

不要让Agent A返回一大段自然语言,Agent B再去解析。尽量用结构化的JSON传递关键信息,减少理解偏差。

技巧4:记录协作日志。

把每次Agent调用的输入、输出、耗时都记下来。出问题的时候,日志是最好的排查工具。我还会定期分析日志,看看哪些Agent经常被调用、哪些经常失败、哪些从来没被用过。

技巧5:给用户"可见性"。

多Agent协作的时候,用户可能等很久。最好在界面上显示"正在查询机票...""正在查询酒店...",让用户知道系统在干活,而不是卡死了。

写在最后

把A2A用在智能体里,本质上就是"把其他Agent当成工具来用"。这个思路简单,但真正做好不容易------你需要精心设计工具描述、处理各种异常、优化协作流程。

我的体会是:不要一开始就搞很复杂的多Agent系统。先从两个Agent的协作开始,跑通流程、踩完坑,再逐步增加Agent数量。多Agent的复杂度是指数级增长的,Agent越多,调试和维护越困难。

A2A协议给了我们Agent间通信的标准,但"怎么让Agent协作得好"是一个工程问题,需要在实践中不断调优。希望这篇文章里的经验能帮你少走点弯路。

这篇也是10.3节A2A协议实战的最后一篇。从设计动机到原生实战,再到框架封装和智能体集成,A2A的核心内容我们都过了一遍。接下来会进入10.4节,聊聊ANP协议------Agent网络中的服务发现。

你们在做多Agent协作的时候,遇到过什么有意思的问题?或者有什么好用的技巧?欢迎在评论区交流。


下一篇预告:ANP协议目标。我们会开始学习ANP协议,看看它是怎么解决Agent网络中的服务发现和组织问题的。

相关推荐
用户3126874877201 小时前
Gemini 4 Argon 技术拆解:100 万输出 token 的"长活"模型,谷歌押注的三个战场
agent
xiaoqiMikko1 小时前
fastjson 1.2.84 的补丁关掉了一个缓存:addAccept 包前缀白名单下,反序列化慢了 13~28 倍
java·后端·性能优化
崎岖Qiu1 小时前
MCP 的架构、协议分层与通信过程
后端·ai·架构·agent·mcp
Henry-SAP2 小时前
SAP PP主数据关联业务视角全解析
后端
Percy_kk2 小时前
AI Agent 开发实践:从手写 ReAct 到框架选型
agent·设计
对空六课2 小时前
指标口径生效时间的版本查询实现
大数据·服务器·前端·算法·数据分析
对空六课2 小时前
RFM分层边界变化的回溯计算
大数据·服务器·前端·数据库·人工智能·算法·数据分析
小羊没烦恼!2 小时前
如何在IIS6,7中部署ASP.NET网站?
数据库·后端·python·web安全·asp.net
IT_陈寒2 小时前
Vite打包的诡异行为,让我差点提交了个错误版本
前端·人工智能·后端