首页 / 文章 / 通过脑与手的类比理解人工智能智能体

通过脑与手的类比理解人工智能智能体

通过将智能体架构类比为点餐过程,了解大型语言模型、工具以及工具执行器之间的交互方式,进而构建一个最简的智能体实现。

1513 词

概述

你可能已经听说过诸如“工具调用”之类的术语,或者认为智能体不过是配备了工具的大型语言模型。本文不会直接给出定义,而是通过一个大家熟悉的日常场景来解释智能体内部实际发生的过程。在讲解过程中,你将了解大型语言模型、工具以及所谓的工具执行器是如何相互配合的。本文是为那些希望真正理解智能体运作机制而非仅仅重复相关术语的开发者而写的,后续还会指导你构建一个最简单的智能体,以便在实践中看到其实现方式。

类比

想象一下你通过外卖应用点餐的场景。通过分析这一普通行为,就能明白你的大脑和手指是如何与应用程序协同完成点餐的,之后我们会把每个步骤对应到智能体的相关术语中。

首先,你的大脑指示手指打开美食订购应用以便浏览附近的餐厅。你的手指点击图标,屏幕上就会出现餐厅列表。

接着,你的大脑浏览屏幕上显示的餐厅名称,并指引手指点击某家特定餐厅以查看其菜单。之后,大脑会浏览菜单中的菜品,并指示手指点击所需菜肴旁边的“添加”按钮,将其加入购物车。

完成这些操作后,大脑会检查购物车里的所有内容,确保没有遗漏,然后指示手指点击“下单”

最终,当大脑看到订单确认页面时,就会意识到目标已经达成,此时大脑与手指之间的来回交互也就结束了。

智能体语言中的类比

注意在整个过程中有一个重要点:大脑本身从未直接执行过任何操作——它只是解读传入的信息,并告诉手指下一步该做什么。这几乎完全对应于人工智能智能体的运作方式。大语言模型扮演着大脑的角色;浏览餐厅、打开菜单、将菜品加入购物车以及下订单等操作则是大脑知道如何使用的工具;而手指则相当于工具的执行者,即实际执行操作的组件。

当为特定目的构建智能体时,需要定义一组工具并将该列表交给作为决策核心的LLM。(此后,->用于表示将控制权传递给下一步。)当用户给智能体分配任务时,流程如下:LLM会查看当前步骤或剩余工作,挑选最相关的工具——将其交给工具执行器,要求其运行并反馈结果——LLM读取该结果并判断是否满足用户需求。如果满足,则流程终止;如果不满足,LLM会根据最新结果选择下一个合适的工具,循环继续,直至确定任务已完成且无需再调用任何工具。

实现方式

在概念确立之后,下一步就是构建一个小型工作代理,使其能够根据用户的请求下食品订单。接下来的代码片段展示了该代理的实际执行路径,文末还提供了完整代码库的链接。

工具

get_restaurants() {
    // In production, replace with an API call such as GET /api/v1/restaurants
  return list of restaurants;
}

get_menu(restaurantName) {
    // In production, replace with an API call such as GET /api/v1/restaurants/${restaurantName or restaurantId}/menu
  return menuItems;
}

add_to_cart(sessionId, menuItemId) {
  // In production, replace with an API call such as POST /api/v1/cart
  return updatedCart;
}

place_order(sessionId) {
  // In production, replace with an API call such as POST /api/v1/order
  return orderDetails;
}

请注意,这些工具只不过是你在日常应用代码中会编写的普通函数而已——其中并未嵌入任何特殊的代理框架或针对大语言模型的专用逻辑。在真正的生产环境中,每个工具还会配有名称、描述以及定义好的输入结构,用以说明它期望接收的数据类型。大语言模型正是依靠这些名称和描述来判断哪个工具最能满足用户的请求。

工具执行器

toolExecutor(toolCall) {
  const tool = toolNameMap[toolCall.name];

  return tool.execute(toolCall.arguments);
}

工具执行器本身只是另一个普通函数。它的toolCall参数包含了关于被调用工具的信息——即工具名称及其参数——而这些信息会因所调用的工具不同而有所变化,可能是餐厅名称、菜单项ID或其他内容。关键在于,当大语言模型告诉执行器要运行哪个工具时,它会提供精确且结构化的详细信息,例如工具名称get_menu以及参数{restaurantName: "Spicy Pizza"}。无需从大语言模型的原始文本输出中手动提取或解析这些信息。

Agent

// Give all the tools to the LLM
llm = OpenAILLM.bindTools([get_restaurants, get_menu, add_to_cart, place_order])

// Take the user query to run the agent loop
reactAgent(userInput) {
  while (true) {
    response = llm(userInput);

    if (response.isFinalAnswer) {
      return response.answer;
    }

    result = toolExecutor(response.toolCall);

    userInput = response + result;
  }
}

从伪代码中需要注意的事项

  1. reactAgent这一名称指的是ReAct(推理与行动)提示模式,即大语言模型会决定调用哪个工具,观察该调用的结果,然后判断是否需要运行其他工具,或者任务是否已完成从而可以终止循环。
  2. 需要注意while(true)循环。这正是智能体与在Java或Python等语言中通常使用的条件逻辑不同的地方。无需在if-else分支或for循环中硬编码函数调用,而是将一组工具提供给大语言模型——每个工具都有名称和描述——让大语言模型在运行时自行决定下一步调用哪个工具、传递什么参数,以及何时任务完成并退出循环。
  • 不过,实际的生产系统并不会依赖原始的 while(true) 循环;此处使用它只是为了用简单代码展示底层的智能体行为。在实践中,应选用 LangGraph 这类的编排框架。即便使用此类框架,通常也会限制递归深度,以避免无限调用大语言模型——无限制的循环可能会导致错误、资源浪费以及不必要的成本。
  • 伪代码中的 response.isFinalAnswer 检查表明智能体已经通过推理得出完整答案,无需再调用其他工具。一旦满足该条件,智能体就会向用户返回总结后的回复,而不会再次触发工具调用。
  • 你可能还注意到了那行代码 userInput = response + result,其中组合后的输出会在下一次迭代中通过 response = llm(userInput) 的方式再次输入到大语言模型中。这是因为每次对大语言模型的调用都是无状态的——它不会保留任何关于之前轮次的记忆,即便是在同一会话或用户交互中也是如此。因此,每当某个工具执行完毕后,你就需要重新发送完整的对话历史:描述智能体应如何行为的系统提示、用户的原始查询、AI之前建议使用工具的响应,以及该工具产生的结果。大语言模型随后会处理这一整串信息,来判断是否已经达成目标,或者还需要进行更多的工具调用。
  • 大语言模型与工具执行序列图

    下图展示了大语言模型如何选择下一个工具、工具执行器如何运行该工具,以及这一循环如何不断重复,直到大语言模型判定任务已完成且无需再执行任何工具。

    所描述的流程清晰地体现了这种来回交互:大语言模型提出调用请求,执行器负责执行该请求,结果再反馈给大语言模型,大语言模型要么提出新的调用请求,要么给出最终答案从而结束循环。

    GitHub代码

    如果您想亲自运行此示例并观察代理程序的循环过程,可访问该仓库获取示例代码。

    相关阅读

  • 大规模 AI 智能体的渐进式工具发现 — 阐述了为何庞大的工具目录会降低 AI 智能体的性能,以及如何通过清单文件和即时生成的架构实现渐进式发现来解决这一问题。
  • 通过 OpenAI 兼容的端点调用 Claude、GPT 和 Gemini — 了解 Anthropic 与 Gemini 的 OpenAI 兼容层支持哪些功能、会在何处隐式放弃某些特性,以及何时通过单一网关路由这三种模型更为合理。