使用 MCP 与 A2A 设计多智能体 AI 系统——理解 AI 智能体如何工作

在第 1 章中,我们探讨了 GenAI 与智能体系统的基础概念,包括用于设计它们的各种架构。在本章中,我们将更近一步看看 AI 智能体究竟是如何运作的。我们会考察智能体 AI 的关键组件与子系统,例如核心智能体循环、记忆、目标制定、状态管理、规划/推理、工具使用以及反馈机制。

为了便于理解,本章会引入智能体系统中的多种概念或组件,并讨论它们各自的作用。随后,我们将讨论这些组件在一个开源智能体 AI 框架 AutoGen 中是如何实现的。

需要再次强调:本书聚焦的智能体 AI 系统中,所有决策、规划与推理都由大语言模型(LLM)完成。也存在其他形式的智能体 AI 系统,其中这类逻辑可能以不同方式实现,例如通过规则引擎或传统编程,或通过检索增强生成(RAG)。外部 RAG 也是一种常见模式:AI 系统会解释模型的回复,并从外部来源(如数据库或知识库)拉取相关数据,以提供更准确、更贴合上下文的答案,或将数据再反馈给 LLM。不过我们稍后会看到,即使是 RAG,也可以简单到只是由 LLM 自己控制的一次工具调用。

在本章中,我们将涵盖以下主要内容:

  • 理解智能体循环:感知、思考、行动
  • AutoGen 中智能体循环的工作方式
  • 用记忆、目标与状态管理长对话
  • 规划与推理机制
  • 工具使用与环境交互
  • 智能体评估与反馈回路

理解智能体循环:感知、思考、行动

智能体循环(agent loop)是定义 AI 智能体如何运作的核心周期。它与其他领域中的许多控制回路非常相似,比如机器人学或控制系统。一些著名的控制回路包括 OODA(Observe, Orient, Decide, Act)循环(en.wikipedia.org/wiki/OODA_l...)以及 Kubernetes 的调谐/协调(reconciliation)循环(pkg.go.dev/sigs.k8s.io...)。

一个智能体循环由一个会话工作流和三个阶段组成:感知(sensing)、思考(thinking)与行动(acting)。我们来看看它们如何运作。

会话工作流

智能体循环围绕 LLM 展开。在循环的每一步中,智能体都会与 LLM 交互:要么发送包含新信息的消息(用户输入、传感器数据等),要么接收来自 LLM 的消息。

图 2.1:智能体的会话工作流

如果收到的消息是最终回复,那么智能体处理结束,循环终止,并把回复返回给调用方(用户、其他系统或更高层智能体)。但如果收到的消息是 LLM 指示智能体执行某个动作(通常是一次工具调用),那么智能体会执行该动作,然后把动作结果再发回给 LLM,继续循环。

智能体所使用的 LLM API 通常负责指示某个回复是否为最终回复。

接下来我们讨论智能体循环的各个阶段。

智能体循环的三个阶段

智能体循环可分为三个主要阶段:感知、思考与行动。下面我们在"与 LLM 交换消息"的语境下描述它们如何工作。

感知(Sensing)

AI 智能体通过多种感知机制来感知环境:它们收集数据,观察环境状态及其变化,并可使用 API、物理传感器或访问数据存储。但感知阶段的触发条件始终是:LLM 发出的工具调用消息

图 2.2:智能体循环的感知阶段

用户、AI 框架或其他智能体可能以系统提示词、初始用户消息(用户不一定是人类)以及一组工具来调用智能体。然而,这些初始信息总是先被发送给 LLM;LLM 可能会决定通过发送工具调用消息来触发感知。随后智能体执行工具调用------可能是从 API 或物理传感器读取数据,也可能是利用 RAG------并把收集到的数据返回给 LLM。

思考(Thinking)

这里有个"反直觉"的点------思考阶段并不是智能体循环中的一个独立阶段。所有思考都由 LLM 完成:无论是规划、推理、分析之前工具调用的结果、决定调用哪些工具,当然也包括组织最终回复。智能体循环只是与 LLM 交换消息的通道。每当智能体循环向 LLM 发送消息时,思考就发生一次。

图 2.3:每次智能体发送消息都会触发 LLM 思考

要记住:这种"纯粹由 LLM 负责思考"的方式并不是公理式必然。

也可能存在以混合模式运行的智能体 AI 系统:它们会拦截中间动作(例如工具调用结果),然后在不把所有思考都交给 LLM 的前提下,自行处理并决定下一步。但这类系统的"思考"发生在智能体 AI 框架之上。

图 2.4:智能体 AI 系统的混合模式

运行智能体循环的 AI 智能体框架本身仍然是消息交换通道,但它可能提供一个钩子:把每次工具调用的结果发给"使用该框架的上层 AI 系统"。然后,上层系统中具备领域知识的专用代码可以通过处理工具响应、直接修改上下文,或甚至在 LLM 还没给出最终回复前就终止循环,来控制或提示后续与 LLM 的交互。

智能体本身从不进行思考。 思考要么由 LLM(位于智能体循环之下)完成,要么由 AI 系统(位于智能体循环之上)完成。智能体框架与智能体不会自行进行任何思考。

行动(Acting)

行动与感知非常相似。实际上,从智能体循环的视角看,两者无法区分:智能体循环把消息发给 LLM,LLM 回一个工具调用消息,智能体 AI 框架执行该工具调用并把结果发回 LLM。所谓行动,只是"对外部世界产生影响的工具调用"。它可能是调用 API、访问数据库,甚至是在真实世界中执行物理动作(如移动机械臂)。

感知与行动的区别在于它们对外部世界的影响:感知只是观察世界状态;行动会改变世界状态。

图 2.5:智能体循环的行动阶段

关键点是:智能体循环通过发送工具调用并接收结果来促进这种交互;它并不关心工具调用的性质到底是感知还是行动。行动类工具调用的结果通常是成功或错误消息,并被发回 LLM。如果该行动是长期动作,响应可能是一个 action ID,LLM 之后可以再查询其进度与状态更新。和往常一样,LLM 会根据行动工具调用的响应决定如何继续。

如果响应是成功状态,LLM 可能直接返回最终回复并终止智能体循环;如果失败,它可能会重试或采取恢复动作。

在多智能体系统中,一个最重要的"行动"就是启动另一个拥有自己智能体循环的智能体。我们将在本书后面(第 8 章)深入讨论这一点。另一种多智能体协作是与另一个已在运行、且并非由当前智能体启动的智能体进行通信。根据对方智能体做什么,这种交互可以被视为感知或行动:如果它只是返回信息,那就是感知;如果它在世界中执行了某种动作,那就是行动。若你此刻觉得有点绕,别担心,我们会在本书第 3 部分把多智能体系统的所有细节与微妙之处逐一拆开。

接下来,我们将看看这种智能体循环概念如何在微软一个流行的智能体框架 AutoGen(github.com/microsoft/a...)中实现。

AutoGen 因其模块化、干净的设计而广为人知:它为智能体、对话提供抽象,并具备强大的工具集成能力,因此非常适合作为概念演示的选择。

AutoGen 中智能体循环的工作方式

AutoGen 通过 AssistantAgent 类及其配套基础设施实现智能体循环的三个阶段(感知、思考、行动),并紧贴我们所描述的模式:由 LLM 通过工具调用驱动整个循环。

在本节中,我们将带你走读 AutoGen 的核心实现,用以说明感知、思考与行动在实践中如何工作。

AutoGen 的核心实现

智能体循环主要在 tool_agent/_caller_loop.py 模块中实现(github.com/microsoft/a...),该模块负责 LLM 交互与工具执行的持续循环。它属于 AutoGen 的 core layer(deepwiki.com/microsoft/A...)。

请看图 2.6,它展示了 AutoGen 智能体循环的工作方式:

图 2.6:AutoGen 智能体循环概览

该循环的运作方式如下:

首先,为一个 tool agent 启动 caller loop:tool_agent_caller_loop()。该函数以交替方式向 tool agent 与 model client 发送消息,直到 model client 不再生成工具调用。其参数如下:

  • tool_agent_id (AgentId):tool agent 的 ID
  • input_messages (List[LLMMessage]):输入消息列表
  • model_client (ChatCompletionClient):用于调用模型 API 的 model client
  • tool_schema (List[Tool | ToolSchema]):模型可用的工具列表

它返回 List[LLMMessage]------caller loop 中生成的输出消息列表。

下面是 tool_agent_caller_loop 的函数签名实现:

python 复制代码
from ..tools import Tool, ToolSchema
from ._tool_agent import ToolException

async def tool_agent_caller_loop(
    caller: BaseAgent | AgentRuntime,
    tool_agent_id: AgentId,
    model_client: ChatCompletionClient,
    input_messages: List[LLMMessage],
    tool_schema: List[ToolSchema] | List[Tool],
    cancellation_token: CancellationToken | None = None,
    caller_source: str = "assistant",
) -> List[LLMMessage]:

AutoGen 中的感知阶段(Sensing)

AutoGen 通过 LLM 触发的工具调用来实现感知。当 LLM 决定收集信息时,它会生成 FunctionCall 对象,智能体执行这些调用,然后把结果发回 LLM。

以下代码展示了这一部分:

ini 复制代码
generated_messages: List[LLMMessage] = []

# Get a response from the model.
response = await model_client.create(input_messages,
    tools=tool_schema, cancellation_token=cancellation_token,)

# Add the response to the generated messages.
generated_messages.append(
    AssistantMessage(
        content=response.content,
        source=caller_source
    )
)

# Keep iterating until the model stops generating tool calls.
while (
    isinstance(response.content, list)
    and all(
        isinstance(item, FunctionCall) 
        for item in response.content
    )
):

    # Execute functions called by the model by sending messages to the tool agent.
    results: List[FunctionExecutionResult | BaseException] = await asyncio.gather(
        *[
            caller.send_message(
                message=call, recipient=tool_agent_id,
                cancellation_token=cancellation_token,
            )
            for call in response.content
        ],
        return_exceptions=True
    )

    # Combine the results into a single response and handle exceptions.
    function_results: List[FunctionExecutionResult] = []

    for result in results:
        if isinstance(result, FunctionExecutionResult):
            function_results.append(result)

        elif isinstance(result, ToolException):
            function_results.append(
                FunctionExecutionResult(
                    content=f"Error: {result}",
                    call_id=result.call_id,
                )
            )

        elif isinstance(result, BaseException):
            raise result  # Unexpected exception.

    generated_messages.append(
        FunctionExecutionResultMessage(content=function_results)
    )

AutoGen 中的思考阶段(Thinking)

AutoGen 也印证了:思考完全发生在 LLM 内部,而不是 AI 框架侧的独立阶段。智能体循环充当通道,通过持续调用 model client 来推进交互。循环会在 LLM 持续返回工具调用时继续运行;所有推理、规划与决策都委托给 LLM 本身:

ini 复制代码
# Query the model again with the new response.
response = await model_client.create(
    input_messages + generated_messages, tools=tool_schema,
    cancellation_token=cancellation_token)
generated_messages.append(AssistantMessage(
    content=response.content, source=caller_source))
return generated_messages

AutoGen 中的行动阶段(Acting)

行动与感知以完全相同的方式实现------都通过工具执行。智能体"对工具调用的性质并不知情",正如前面所说:无论工具是读取数据(感知)还是执行动作(行动),实现方式一致。对于更复杂的工具交互,AutoGen 提供了专门的 tool_agent_caller_loop 函数(github.com/microsoft/a...),

该函数展示了核心闭环:发送消息给 LLM → 接收工具调用 → 执行工具 → 发送结果回 LLM → 重复,直到不再有工具调用。

AutoGen 通过 handoffs 支持上述多智能体协作:一个智能体可以启动或与另一个智能体通信。它通过 HandoffMessagegithub.com/microsoft/a...)与 Swarm 模式来实现,使智能体能够把任务委派给拥有自身智能体循环的专门化智能体。

  • HandoffMessage:用于将对话转交给另一个智能体的消息。
  • Swarm pattern:一种协调策略,由多个智能体共同贡献以解决任务。

为了让智能体系统能够保留上下文,它需要访问某些信息以支撑其行动路径------这就是记忆、目标与状态发挥作用的地方。接下来我们将讨论它们。

用记忆、目标与状态管理长对话

智能体 AI 系统会在可能很长的一段时间里执行复杂的多步骤任务,并以同步或异步方式与人类、其他系统以及其他 AI 智能体交互。正如你现在已经非常清楚的那样,所有"思考"都由 LLM 完成,但 LLM 本身没有记忆 (尽管像 ChatGPT 这样的聊天机器人在 LLM 之上提供了长期记忆;见 help.openai.com/en/articles...)。这意味着每次调用 LLM 时,我们都需要把所有相关信息提供给它,这通常不仅包括最新的用户消息或工具调用响应,也包括迄今为止整个会话的全部历史。一些 LLM 提供商提供有状态的长期记忆,并将其动态注入到 LLM 的上下文中。

我们这里聚焦于一次对智能体的单次调用:其中恰好包含一条系统消息、一条用户消息(也就是 prompt)、零条或多条工具消息,以及 LLM 产生的一条最终回复消息。

智能体的一般指令写在系统消息中,它通常是一段很长的文本,描述智能体的能力、角色、初始知识、输出规范与示例。例如,一个 Python 编码智能体的系统消息可能是这样的:

你是一名 Python 编程专家与助手。你编写干净、地道、文档完善的 Python 代码,并在被要求时清晰且简洁地解释你的选择。

你可以分析需求、调试代码、重构现有实现,并提出架构与风格改进建议。

始终假设用户具备技术能力,但可能不了解 Python 标准库或惯用法的全部细节。

如果问题含糊,请在继续之前提出澄清问题。

写代码时,优先考虑清晰而非炫技,并在合适处添加简短注释。

除非另有指示,否则把 Python 代码输出在三重反引号中。

只有在明确允许或普遍接受(如 requests、pandas、numpy)时才可使用第三方库。

遵循最佳实践,例如编写单元测试,并按 PEP-257 规范为代码撰写文档。

与该智能体的一段长对话可能会被拆分为多次智能体循环调用,每次都有自己的最终回复消息。对话线程由 AI 系统保持:它会把前一段会话的所有消息喂给下一段会话。

例如,用户可能提出"用 Python 给我做一个计算器",智能体可能以最终回复消息的形式提出一个澄清问题:"你更喜欢 CLI 程序还是 GUI 应用?"控制该流程的 AI 系统会把问题展示给用户,用户选择 CLI。此时,一次新的智能体调用开始:它带着第一段会话的全部历史,以及用户"选择 CLI"的信息,作为新的初始用户消息发给智能体。新智能体因为已知道用户偏好 CLI,就可以开始通过工具调用迭代生成并写入代码与测试,直到满意后返回最终回复,例如:"完成。CLI 程序名为 calc.py,单元测试在 calc_test.py。"

图 2.7:多会话对话

下面我们看看这在 LLM 的上下文窗口里是如何表示的。

上下文窗口

语言模型的上下文窗口(context window)指的是模型在一次调用中能够处理并进行推理的全部信息量。这不仅包括文本,也包括其他输入模态,例如图像、音频、结构化数据或 embeddings------取决于模型能力。上下文窗口实际上充当了模型的工作记忆,决定它能"看到"并用于生成回复的信息范围。

RAG 的 embeddings 可能会被直接注入上下文窗口,而不受所有模态都要经过的编码流程影响。

现在,我们迄今讨论过的所有高层概念------例如智能体记忆、目标与状态------最终都必须被表示为 token,并且必须塞进每一次对 LLM 的 API 请求的上下文窗口里。

当 LLM 形成回复时,它会依赖训练中学到的知识,这些知识编码在模型权重中。但上下文窗口里的所有信息,会在模型生成回复后立刻被"遗忘"。注意:LLM 的运营方可能会为审计、未来训练或其他目的存储这些信息,但 模型本身不会从上下文窗口中学习或保留任何信息。跨多次智能体循环调用保留信息的唯一方式,是在 AI 框架或 AI 系统层面把信息存储下来,并在下一次调用时再把它喂回上下文窗口。

LLM(例如 GPT-4o 这类模型)并不管理用户画像。LLM 提供商(如 OpenAI)可能会存储用户相关信息,并在交互式聊天会话中(例如 ChatGPT)自动把它注入上下文窗口。在这种情况下,ChatGPT 扮演了管理记忆的 AI 系统角色。

而在使用 AI 框架的智能体系统中,与 LLM 的交互通常通过专用 API key 进行,并不存在用户级别的 profile。

更大的上下文窗口能带来更复杂的行为:更长的对话、更丰富的输入、更完整地回溯前序步骤进行推理,或对复杂工件进行更细致引用。更小的窗口会限制模型维持连续性、整合上下文或处理深度嵌套任务的能力。

但是,即便上下文窗口很大,面对长对话或输入输出规模很大的复杂任务,你仍然可能"装不下"。每个模型都有自己的上下文窗口大小,选模型时必须考虑这一点。在这种情况下,你可能需要截断对话历史、对其做摘要,或以某种形式压缩以适配上下文窗口。在多智能体系统中,你也可以采用分治:每个智能体负责一个特定子任务,从而能把模型的整个上下文窗口专门用于它自己的子任务;然后由更高层智能体汇总各子智能体的结果(结果通常更小)。我们会在第 9 章讨论这种场景。

为什么 LLM 没有记忆?

那为什么 LLM 不能把所有东西都记住?为什么每次都必须把上下文喂给它?答案很复杂也很微妙。首先,在 LLM 自身内部加入记忆组件并没有好处。我们真正讨论的是:LLM 提供商维护每个用户对话的状态(所有消息历史),这样用户每次请求只需要发送最新消息。

LLM 提供商可以把最新消息追加到用户保存的会话里,再把它作为短暂的上下文窗口喂给 LLM。于是问题变成:为什么 LLM 提供商不能给用户提供这种服务?理论上,它能节省大量带宽,因为每次请求不需要发送完整历史,只要发送新增消息的 delta 即可。

但在规模化场景下,你希望同时并行运行多个 LLM 副本,以处理海量请求。如果每个 LLM 实例都带自己的记忆,那么你就需要在用户请求与某个特定 LLM 实例之间维持会话粘性(session affinity)。此外,用户连续两条消息之间可能有延迟;当 LLM 提供商管理会话记忆时,即便没有新消息,它也必须维护状态。这在长会话场景下会迅速累积成本。

来看一个例子:10,000 个用户在 5 分钟内总共向 LLM 提供商发送了 10TB 数据。为了服务后续消息,提供商必须存储这些数据。把这些数据放在哪里就很复杂(内存、数据库、分布式缓存)。当用户再发一条消息时,提供商还需要取回该用户对话上下文并注入 LLM,这同样复杂,并可能引入多种故障模式与性能问题。

最后还有会话终止问题。LLM 提供商有两种选择:要么永久保存会话状态,让用户随时继续对话;要么基于某些启发式规则决定何时清除会话记忆。总体而言,为了提升可扩展性、降低延迟并最小化成本,LLM 提供商更倾向于保持 LLM 无状态,把会话状态交给 AI 框架或 AI 系统来管理。

像 ChatGPT、Perplexity 和 Claude.ai 这样的聊天机器人看起来有长期记忆,并能继续之前的对话。下面是 Claude.ai 展示历史对话列表的一个示例。

图 2.8:Claude.ai 服务的对话存储示例

这其实是管理聊天机器人的 AI 系统特性,而不是 LLM 本身的特性。Claude.ai 是一个基于 Anthropic 模型提供交互式聊天的 Web 服务:它把所有对话存入持久化存储,并向用户展示历史对话列表。当用户选择某条对话时,Claude.ai 会取回存储的对话,并把它作为初始消息喂给所选 LLM(例如 Claude Sonnet 4)。LLM 本身始终只有一个作用于当前调用的短暂上下文窗口。一些 LLM 提供商(例如 OpenAI)已经开始在 ChatGPT memory 中试验跨所有对话的全局长期记忆(openai.com/index/memor...)。系统会把所有过往对话的摘要(用户可选择加入或退出长期记忆)喂给每次调用,于是看起来就像模型记得你过去的所有互动。

AutoGen 中的记忆

作为现实实现,我们来看看 AutoGen 框架(github.com/microsoft/a...)里记忆是怎么做的。

令人意外的是,AutoGen 在其架构的 core 层并不提供记忆设施。记忆是在更高一层的 chat agents 抽象中支持的。共有三类记忆:

简单顺序列表记忆(Simple sequential list memory) :AutoGen 为简单的顺序列表记忆定义了一个抽象记忆协议,提供诸如 add()query()update_context()clear()close() 等操作。例如,一个返回当前天气的智能体可以用记忆保存用户的温度单位偏好("英制" vs "公制"),并据此响应用户查询。在与 LLM 交互时,记忆内容会被加入上下文窗口。

基于向量的 RAG 记忆(Vector-based RAG memory) :AutoGen 支持更复杂的记忆存储,同样实现记忆协议。AutoGen 开箱即用地提供了一个扩展:在 ChromaDBVectorMemory 类中实现基于向量的 RAG 记忆(github.com/microsoft/a...)。

以任务为中心的快速记忆(实验性)(Task-centric fast memory) :实验性的"任务中心快速记忆"由 MemoryController 实现。它不使用记忆协议,并且仍是 AutoGen 内部的一个活跃研究项目。其目标包括:

  • 通过在上下文窗口限制之外快速且持续学习,更有效地完成通用任务
  • 记住用户提供的指导、纠正、计划与示范
  • 通过智能体自身经验学习,并快速适应变化的环境
  • 避免在与过去相似的任务上重复犯错

这些记忆能力都被用于 AssistantAgent 类中,它是 AutoGen 中实现聊天智能体的主要类。

接下来,我们将把注意力转向 LLM 的规划与推理机制。

规划与推理机制

如你所记得的,智能体 AI 系统中的所有规划与推理都由 LLM 完成。不过,AI 框架仍然可以通过结构化与 LLM 的交互方式,来促进"可编排的规划与推理"。在本节中,我们将考察智能体 AI 系统中不同的规划与推理方法,包括单智能体与多智能体规划,以及如何使用推理模型。

使用推理模型

LLM 提供商如今推出了专门面向复杂多步骤推理任务的推理模型(见下图):

图 2.9:使用推理模型

这意味着在一次请求中,这些模型可能执行多个推理步骤,并使用内部工具(如网页搜索、代码执行),而不需要与 AI 框架或用户交互。例如,OpenAI 的 o 系列模型(o3、o4-mini)会花更多时间(也消耗更多 token),实际上运行它们自己的内部智能体循环。这些模型在必要时仍可能调用外部工具,但 LLM 侧的思考阶段会更"重"。从 AI 框架的视角看,使用推理模型与使用常规 LLM 并没有太大区别;但需要管理用户对性能、时延与成本的预期。

单智能体规划与推理

单智能体规划与推理是智能体 AI 系统中最简单的一种形式。下图展示了这种方式:

图 2.10:单智能体推理

智能体循环以一条系统消息、一条用户消息以及一组工具被调用。智能体既不会与其他智能体通信,也不会启动子智能体。在每一次对 LLM 的请求中,会话的完整历史都会被发送给 LLM,由 LLM 决定如何继续。最终形成的计划或分析会以最终回复消息的形式返回给用户。智能体可能调用工具来收集有助于制定计划或对输入数据进行推理的信息。

多智能体规划与推理

在多智能体系统中,规划与推理会更复杂。顶层智能体可能会调用额外的子智能体,或与已经在运行的智能体通信。注意:调用子智能体或与另一个智能体通信的决策,本身就是由顶层智能体所调用的 LLM 规划出来的结果------它会产生一次工具调用,而该工具执行的结果就是启动子智能体或向另一个智能体发送消息。

多智能体系统中的智能体交互会引入单智能体场景不存在的多层交互。当父智能体把工作委派给子智能体时,它必须谨慎地构造子智能体的上下文,包括系统提示词、初始目标、可用工具,以及从父智能体会话中抽取的任何相关背景信息。

图 2.11:多智能体推理

父智能体还需要决定:是同步等待子智能体完成,还是在异步继续处理其他任务的同时监控子智能体进度。当智能体在执行中需要交换信息时,通信协议就变得至关重要------智能体可能需要协商、协调共享资源、解决冲突,或汇总来自多个并行子智能体的结果。此外,错误处理与恢复策略也会更复杂:某个智能体的失败可能在系统中级联传播,迫使父智能体检测失败、用不同参数重试,或动态把任务重新分配给其他智能体。单个智能体的上下文窗口限制也会给信息共享带来挑战:智能体必须决定彼此传递哪些信息,这可能需要对长对话历史进行摘要或选择性过滤。

工具使用与环境交互

管理工具是 AI 框架的主要职责。LLM 通过规划与推理驱动一切,但对许多任务来说,智能体必须与环境交互,而这依赖于可用工具。智能体向 LLM 提供的工具列表定义了智能体的能力边界。

智能体可以非常有效地提升生产力:自动化琐碎工作、汇总邮件、在工作环境中执行常规检查等。

以一个 AI 软件工程师智能体为例。为了真正有效,它需要工具来完成以下任务:

  • 列出文件与目录
  • 读取、创建、编辑与删除文件
  • 运行单元测试与集成测试
  • 在沙箱环境中运行代码(例如 Docker 容器)
  • 运行本地 Web 服务,在浏览器中测试代码
  • 运行代码 linter 与 formatter、代码风格检查器、代码分析器、代码安全扫描器
  • 创建 Git 分支、提交代码、推送到远程仓库

对普通用户而言,AI 智能体也能帮助完成日常活动,例如按饮食需求做餐食计划并下单、按计划周期准备并购买食材,或完成需要多步骤的复杂任务,如度假规划。

以一个 AI 度假规划智能体为例。为了有效,它需要工具来完成以下任务:

  • 搜索航班,并在不同航司与日期间对比价格
  • 查找并预订住宿(酒店、短租、公寓、青旅等)
  • 调研目的地景点、餐厅与活动
  • 查询不同国家的签证要求与旅行限制
  • 获取实时天气预报与季节性旅行信息
  • 计算旅行预算并按类别追踪开支
  • 进行餐厅订位,预订 tour 或体验项目
  • 访问用户日历以确定可用出行日期与时长约束
  • 发送确认邮件,并创建包含预订信息的行程文档

无论 LLM 多强,如果没有与环境交互的工具,它都无法成为一个有效的软件工程师或度假规划师。AI 软件工程师最多只能根据 prompt 生成代码------这很有用,但只是软件开发生命周期的一部分。比如,如果度假规划智能体没有权限完成预订,它可以推荐度假方案,却无法协助实际的预订与预约。

下面我们将更深入讨论工具在智能体 AI 系统中如何实现、声明与使用,重点以 AutoGen 为例。

用户自定义工具如何实现?

智能体 AI 系统里的工具分为两类:内置工具与用户自定义工具。内置工具如 web_searchcode_executionfile_searchcomputer_use 等由 LLM 提供商实现。用户自定义工具则是可以用任何编程语言实现的函数,通常使用与智能体 AI 框架相同的语言实现。不过,它们也可以用不同语言实现,并以 Docker 容器或内部 API 的形式暴露给智能体 AI 框架。

下面是一个用 Python 实现的"把两个数相加"的用户自定义工具示例:

python 复制代码
def add_numbers(a: int, b: int) -> int:
    """
    Adds two numbers and returns the result.

    Args:
        a (int): The first number.
        b (int): The second number.

    Returns:
        int: The sum of the two numbers.
    """
    return a + b

没错,它就是一个普通的 Python 函数。智能体 AI 框架会把这个函数暴露为一个工具。我们会在第 3、4、5 章详细展开。

工具如何声明?

OpenAI、Anthropic、Mistral、Google 等 LLM 提供商都提供与模型交互的 API;许多其他提供商也提供与 OpenAI 兼容的 API。这些 API 非常相似,并允许你在每次调用中声明一份独立的工具列表。

下面是 OpenAI API 中经典的工具声明示例:

arduino 复制代码
curl https://api.openai.com/v1/responses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-4.1",
    "input": "What is the weather like in Paris today?",
    "tools": [
      {
        "type": "function",
        "name": "get_weather",
        "description": "Get current temperature for a given location.",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "City and country e.g. Bogotá, Colombia"
            }
          },
          "required": [
            "location"
          ],
          "additionalProperties": false
        }
      }
    ]
  }'

AI 智能体拥有明确的作用域以及一组用于完成任务的工具。智能体 AI 框架代表某个特定智能体向 LLM 发送的每一次请求,都会包含该智能体的工具列表;不同智能体可以拥有不同工具。接收到工具集合的 LLM 可能会通过工具消息的形式请求调用某个工具;AI 框架负责处理该工具消息、执行被请求的工具,并把结果返回给 LLM。

LLM 如何决定用哪个工具?

这里存在多层抽象。智能体 AI 框架通常支持多个 LLM 提供商,每个提供商都有自己的 API 与工具声明格式。框架会抽象掉这些差异,并提供统一接口用于声明工具。当框架向某个具体提供商发送请求时,它会把请求(包括工具列表)翻译成该提供商 API 期望的格式。

图 2.12:智能体框架调用 get_weather 工具

当 LLM 提供商收到包含工具列表的结构化请求时,它会生成类似下面这样的文本表示:

你可以调用以下工具:

  1. search_web(query: string) ------ 在网上搜索信息。
  2. calculate(expression: string) ------ 计算数学表达式。
  3. get_weather(location: string) ------ 获取某地当前天气。

这段文本会被转换成 token 流并输入 LLM。LLM 并不知道也不关心 API 请求的结构(那是给开发者看的)。LLM 在训练时见过大量包含工具调用及其用法的文本样例;当它接收到带工具列表的文本请求时,就能基于训练识别工具及其参数。随后它会根据输入查询与可用工具决定使用哪个工具,并生成一个结构化的工具调用消息,由提供商 API 层接收并格式化为带工具调用的响应,例如:

json 复制代码
{
  "tool_calls": [
    {
      "name": "get_weather",
      "arguments": {
        "location": "Paris, France"
      }
    }
  ]
}

该响应会被发送回智能体 AI 框架;框架执行工具调用并把结果回传给 LLM,从而继续智能体循环。

下面我们看看 AutoGen 是如何使用工具的。

AutoGen 中的工具使用

AutoGen 提供了一个用于在 AI 智能体内定义与使用工具的完整框架。它同时支持内置工具与用户自定义工具,使开发者能轻松扩展智能体能力。由于 AutoGen 兼容多个 LLM 提供商及其 API,它定义了与提供商无关的数据结构来表示工具,然后在需要时再转换为各提供商的格式。

我们来看一个 AutoGen 的下棋智能体示例,看看它们如何使用工具。黑棋智能体拥有如下工具:

css 复制代码
black_tools: List[Tool] = [
    FunctionTool(
        get_legal_moves_black,
        name="get_legal_moves",
        description="Get legal moves.",
    ),
    FunctionTool(
        make_move_black,
        name="make_move",
        description="Make a move.",
    ),
    FunctionTool(
        get_board_text,
        name="get_board",
        description="Get the current board state.",
    ),
]

每个 function tool 都是一个 Python 函数/方法,例如:

python 复制代码
async def chess_game(
    runtime: AgentRuntime,
    model_client: ChatCompletionClient,  # type: ignore
) -> None:
    """
    Create agents for a chess game and return the group chat.
    """

    # Create the board.
    board = Board()

    # Create tools for each player.
    def get_legal_moves_black() -> str:
        return get_legal_moves(board, "black")

然后这些工具会在 ToolAgent 中注册(github.com/microsoft/a...):

ini 复制代码
# Register the agents.
await ToolAgent.register(
    runtime,
    "PlayerBlackToolAgent",
    lambda: ToolAgent(
        description="Tool agent for chess game.",
        tools=black_tools,
    ),
)

工具注册完成后,LLM 就获得了在回复消息中调用这些工具的能力。

ToolAgent 是 AutoGen core 中的一种专门化智能体:它接收 FunctionCall 消息,用提供的参数执行对应工具,并返回 FunctionExecutionResult 消息。它通常作为专用的工具执行器,被其他智能体用来委派函数调用,同时提供当工具不存在、参数无效或执行失败时的错误处理。

随后会注册黑棋玩家智能体(白棋智能体也类似)。注意其中"作为黑方下棋"以及"使用提供的工具"的指令:

ini 复制代码
await PlayerAgent.register(
    runtime,
    "PlayerBlack",
    lambda: PlayerAgent(
        description="Player playing black.",
        instructions=(
            "You are a chess player and you play as black. "
            "Use the tool 'get_board' and 'get_legal_moves' to get the legal moves "
            "and 'make_move' to make a move."
        ),
        model_client=model_client,
        model_context=BufferedChatCompletionContext(buffer_size=10),
        tool_schema=[tool.schema for tool in black_tools],
        tool_agent_type="PlayerBlackToolAgent",
    ),
)

AutoGen 的智能体循环会把工具列表随请求发送给 LLM,然后由 LLM 在合适时机决定用哪个工具。这里的思路是:当 LLM 决定下一步时,预期它需要先获取当前棋盘状态。get_legal_moves() 工具从某种意义上说可能并非必需------如果我们期望 LLM 足够懂棋、能够选出最优下一步,那么它也应该足够聪明,只选择合法走法。

智能体评估与反馈回路

智能体 AI 系统的一个关键方面,是能够评估智能体的表现,并提供一条改进路径。这在多智能体系统中尤为重要:智能体可能相互协作或相互竞争,而微小的错误与不精确会不断累积,导致结果不佳,甚至整个工作流失败。评估智能体有多种方式,包括:人类反馈、自动化评估 以及行业基准

有些评估方法可以在系统运行过程中自动完成;而那些需要人类参与或耗时过长的方法,则可在离线环境中用于提升未来智能体的表现。

下面我们分别更细致地看看这些方法。

人类反馈

人类反馈在塑造智能体 AI 系统的行为与质量方面起着关键作用。不同于依赖预定义规则或指标的自动化评估,人类反馈能够捕捉诸如"有用性、语气、清晰度、用户满意度"等主观要素。这在创意写作、决策制定或用户协助等开放式任务中尤其有价值------因为在这些场景里,刚性的评估标准往往不够用。用户可以通过对回复打分、标记错误动作或选择更偏好的输出等方式提供直接反馈;也可以从用户行为中收集隐式反馈,例如用户是否继续与智能体互动,或是否提前放弃任务。

无论哪种形式,智能体 AI 框架通常都不提供收集人类反馈的机制。AI 系统的开发者需要在应用层构建人类反馈支持。

在更结构化的环境中,反馈可以来自领域专家:他们按特定标准评估智能体表现。例如,对于编码助手智能体,开发者可以评估生成代码的质量、解释是否有用、调试步骤是否正确。这样的专家反馈可用于改进提示词、策划更好的示例,并调整智能体工具集。注意,这与用于训练 LLM、使其更符合人类偏好的"基于人类反馈的强化学习(RLHF)"流水线不同。这里的重点是提升 AI 智能体 的表现,而不是底层 LLM。本质上,你完全可以构建一个自我改进的 AI 系统:它收集人类反馈、分析反馈,并用它来改进自己或其他 AI 智能体。

人类反馈在多智能体系统中尤其重要,因为这里会出现协调、委派与沟通等动态。在这种情况下,反馈不只是评估单个智能体,还会评估智能体之间协作或分工的效果。标注者或终端用户可以指出沟通断裂、目标不一致或任务执行低效等问题。这些反馈有助于识别系统性问题,并为重新配置智能体以获得更好的协同提供数据。尽管人类反馈比自动化方法更慢、也更耗资源,但它对于评估细微行为、并在真实部署中建立信任,是不可或缺的:

图 2.13:人类反馈

人类反馈与其他评估方法(如 A/B 测试)配合效果很好,也可用于交叉验证自动化评估结果。

自动化评估

自动化评估方法提供了一种无需人类介入、可规模化评估智能体表现的方式。当任务的预期结果非常明确时,自动化评估效果最好。比如,如果让智能体按某种格式生成结果(例如 CSV 文件),就很容易验证输出是否确实是 CSV。

我们也可以利用 LLM 技术,让 LLM 去评估 AI 智能体的输出是否符合其原始目标。在层级式多智能体系统里,这种评估还可以在不同层级上进行;并且它带来了"在线即时纠偏"的可能性。

图 2.14:对智能体系统的评估

例如,一个父智能体启动多个子智能体来完成子任务,然后使用一个"评估智能体"去评估子任务结果;若不满意,它可以重新运行子智能体,可能改用不同的提示词或配置。这类在线评估的结果应被记录下来,用于提升未来智能体表现,从而减少线上临时修复问题的需求。

智能体 AI 的评估涉及:用完成率、轨迹质量、工具准确性等指标评估端到端任务成功;同时通过 traces 与仿真拆解并评估规划、推理与自我纠错等组件。这个过程会使用基准(例如 SWE-Bench、TRAIL)、LLM-as-judge 打分,以及生产监控来保证可靠性,并通过分层的人类与自动化检查对失败进行迭代修复。

以下是一些用于评估智能体 AI 系统的标准指标与评分:

任务表现指标(Task performance metrics)

  • 成功率(Success rate) :成功完成任务的百分比
  • 任务完成时间(Task completion time) :完成任务的平均耗时
  • 准确率(Accuracy) :输出/决策的正确性
  • Precision/Recall/F1:用于分类与检索任务

智能体行为指标(Agent behavior metrics)

  • 工具使用效率(Tool usage efficiency) :智能体使用可用工具的有效程度
  • 规划质量(Planning quality) :生成计划的连贯性与最优性
  • 推理深度(Reasoning depth) :多步骤推理链的质量
  • 适应性(Adaptability) :条件变化时的表现

多智能体协同指标(Multi-agent coordination metrics)

  • 沟通效率(Communication efficiency) :智能体交互的质量与必要性
  • 任务分配效率(Task allocation efficiency) :任务分配是否合理
  • 协同开销(Coordination overhead) :沟通成本与收益的对比
  • 达成一致耗时(Consensus time) :协作任务中达成一致所需时间

安全与对齐指标(Safety and alignment metrics)

  • 无害性(Harmlessness) :避免产生有害输出
  • 真实性(Truthfulness) :事实准确与诚实
  • 有用性(Helpfulness) :是否有效满足用户需求
  • 对齐分数(Alignment score) :遵循预期目标而非"奖励投机"(reward hacking)

鲁棒性指标(Robustness metrics)

  • 对抗鲁棒性(Adversarial robustness) :在攻击场景下的表现
  • 分布漂移容忍度(Distribution shift tolerance) :面对分布外数据的表现
  • 错误恢复(Error recovery) :从失败中恢复的能力
  • 幻觉率(Hallucination rate) :生成虚假信息的频率

效率指标(Efficiency metrics)

  • 计算成本(Computational cost) :每个任务消耗的资源
  • Token 效率(Token efficiency) :每消耗一个 token 的输出质量
  • 时延(Latency) :实时应用的响应时间
  • 可扩展性(Scalability) :系统规模扩大时的性能退化程度

我们会在第 10 章更深入地讨论评估指标。

实现全部评估指标是一项巨大工程,可能需要很多年的工程投入。参见:www.getmaxim.ai/articles/ev... 。下面我们再看看行业基准。

行业基准

行业基准是标准化测试或任务,用来评估智能体。这些基准提供了一种客观方式来衡量单个 AI 智能体或完整智能体系统的表现。把你的智能体系统与 SOTA(state of the art)对比,可以帮助判断是否还有很多"低垂果实",或表现是否已接近最优。但基准往往为特定任务或领域设计,与你的用例的重叠可能有限。以下是一些常见行业基准:

  • AgentBench :AgentBench(github.com/THUDM/Agent...)是一个综合性基准,用于评估基于 LLM 的智能体在多种任务上的通用能力。它聚焦真实世界的智能体任务,如网页浏览、数学解题与具身决策,以测试推理、记忆与工具使用能力。
  • ToolBench :ToolBench(github.com/OpenBMB/Too...)提供一套数据集与评估协议,用于训练与评估能够调用外部工具的 LLM。它强调工具使用能力,衡量智能体调用 API 或函数以完成超越文本生成的复杂任务的能力。
  • WebArena :WebArena(webarena.dev/)是一个真实、开放式的 Web 环境,用于评估智能体导航并与动态网站交互的能力。它提供基于浏览器的基准场景,用于衡量目标完成度、规划能力以及应对噪声或意外布局的能力。
  • Mind2Web :Mind2Web(osu-nlp-group.github.io/Mind2Web/)用于评估智能体在网站上执行用户指定任务的能力,强调类人的推理与规划。它模拟多样的 Web 任务,并提供目标、子目标与推理链标注,以评估可解释性与鲁棒性。

基准有助于在特定领域与任务上比较智能体系统,但更重要的是理解用户与客户的目标与意图。例如,你的系统可能在某个基准上得分较低,但运行更快、成本更低------这可能反而是更好的价值主张。

总结

在本章中,我们深入剖析了由大语言模型驱动的智能体 AI 系统的运行流。我们引入了核心智能体循环------感知、思考、行动------并展示它在 AutoGen 等框架中如何运作:LLM 通过工具调用来决定智能体行为。我们探讨了智能体如何在 LLM 上下文窗口限制下使用记忆、目标与状态管理,以及这些责任如何由外围的 AI 框架或系统承载。

我们还研究了规划与推理机制在单智能体与多智能体系统中的重要性。随着专门化推理模型的出现以及智能体工作流复杂度增长,对交互进行结构化、对任务进行拆解,成为获得鲁棒行为的关键。随后我们把注意力转向工具使用------它是智能体能力的重要使能因素。毕竟,即便最聪明的 LLM,也只能在其工具所及范围内观察或影响外部世界。

最后,我们讨论了评估与反馈。这些机制让智能体 AI 系统能够随时间不断改进。我们覆盖了人类与自动化反馈策略,并回顾了用于跨系统客观对比的标准化基准。这些实践不仅对提升单个智能体性能很重要,也对调试、合规、可信性与长期系统演进至关重要。

总体而言,本章描述的概念与模式构成了现代智能体 AI 的运行骨架。随着领域成熟,这些组件会持续变得更复杂,但核心原则------清晰的智能体循环、透明的工具使用、具备上下文意识的推理、精确的记忆管理以及系统化评估------仍将是基础。

在下一章中,我们将把视角下沉到代码层面,极其详细地审视一个简单但完整的智能体 AI 系统的实现:它作为一个 AI Kubernetes 工程师运行。

相关推荐
Baihai_IDP5 小时前
回头看 RLHF、PPO、DPO、GRPO 与 RLVR 的发展路径
人工智能·llm·强化学习
智泊AI5 小时前
一口气讲清:AI Agent 八大核心概念,建议收藏!
llm
Sailing5 小时前
LLM 调用从 60s 卡死降到 3s!彻底绕过 tiktoken 网络阻塞(LangChain.js 必看)
前端·langchain·llm
松灵机器人21 小时前
【养个小龙虾】让小龙虾帮我控制七轴臂
agent
阿里云大数据AI技术21 小时前
用 SQL 调大模型?Hologres + 百炼,让数据开发直接“对话”AI
sql·llm
量子位21 小时前
这届MWC真成了中国AI主场,小米直接把AI从对话框里拽出来接管物理世界了
llm·aigc
AI探索者1 天前
LangGraph 记忆机制:基于 Checkpointer 的状态持久化
llm
over6971 天前
从 LLM 到全栈 Agent:MCP 协议 × RAG 技术如何重构 AI 的“做事能力”
面试·llm·mcp
用户12039112947261 天前
从MCP到RAG:Agent的开发之路
agent·mcp