【翻译】Google 智能体白皮书

505 阅读37分钟

智能体

本文来自 Google,由 向量脉络 AI 工作流翻译。

作者:Julia Wiesinger,Patrick Marlow 和 Vladimir Vuskovic

致谢

审阅者和贡献者

  • Evan Huang
  • Emily Xue
  • Olcan Sercinoglu
  • Sebastian Riedel
  • Satinder Baveja
  • Antonio Gulli
  • Anant Nawalgaria

策展人和编辑

  • Antonio Gulli
  • Anant Nawalgaria
  • Grace Mollison

技术撰稿人

  • Joey Haymaker

设计师

  • Michael Lanning

目录

  • 简介 4
  • 什么是智能体? 5     - 模型 6     - 工具 7     - 编排层 7     - 智能体 vs. 模型 8     - 认知架构:智能体如何运作 8
  • 工具:我们通往外部世界的钥匙 12     - 扩展 13         - 示例扩展 15     - 函数 18         - 用例 21         - 函数示例代码 24     - 数据存储 27         - 实现和应用 28     - 工具回顾 32
  • 通过有针对性的学习增强模型性能 33
  • 使用 LangChain 快速启动智能体 35
  • 使用 Vertex AI 智能体的生产应用 38
  • 总结 40
  • 尾注 42

将推理、逻辑和对外部信息的访问结合在一起,所有这些都连接到一个生成式 AI 模型,便引出了智能体的概念。

简介

人类非常擅长处理混乱的模式识别任务。然而,他们常常依赖工具——如书籍、谷歌搜索或计算器——来补充他们已有的知识,然后得出结论。就像人类一样,生成式 AI 模型也可以被训练使用工具来访问实时信息或提出实际行动建议。例如,模型可以利用数据库检索工具来访问特定信息,如客户的购买历史,以便生成量身定制的购物推荐。或者,根据用户的查询,模型可以进行各种 API 调用,向同事发送电子邮件回复或代表您完成金融交易。为此,模型不仅必须能够访问一组外部工具,还需要能够以自我导向的方式计划和执行任何任务。这种将推理、逻辑和对外部信息的访问结合在一起,所有这些都连接到一个生成式 AI 模型,便引出了智能体的概念,或者说是一个超越生成式 AI 模型独立能力的程序。本白皮书将更详细地深入探讨所有这些以及相关的方面。

什么是智能体?

从最根本的形式来看,生成式人工智能智能体可以被定义为一个应用程序,它试图通过观察世界并利用其拥有的工具对其进行操作来实现目标。智能体是自主的,可以独立于人为干预而行动,尤其是在被赋予适当的目标或旨在实现的目标时。智能体也可以在实现目标的方法上具有主动性。即使在没有人类明确指令的情况下,智能体也可以推理接下来应该做什么以实现其最终目标。虽然人工智能中的智能体概念非常普遍且强大,但本文档侧重于在发布时生成式人工智能模型能够构建的特定类型的智能体。

为了理解智能体的内部运作,让我们首先介绍驱动智能体的行为、动作和决策制定的基本组件。这些组件的组合可以被描述为认知架构,并且可以通过混合和匹配这些组件来实现许多这样的架构。专注于核心功能,如图 1 所示,智能体的认知架构中有三个基本组件。

图 1. 通用智能体架构和组件

模型

在智能体的范围内,模型指的是将用作智能体流程集中决策者的语言模型 (LM)。智能体使用的模型可以是一个或多个任何大小(小型/大型)的 LM,这些 LM 能够遵循基于指令的推理和逻辑框架,如 ReAct、思维链或思维树。模型可以是通用的、多模态的,或者根据您特定智能体架构的需求进行微调。为了获得最佳的生产结果,您应该利用最适合您期望的最终应用程序的模型,理想情况下,该模型已经使用与您计划在认知架构中使用的工具相关的数据签名进行了训练。重要的是要注意,模型通常不使用智能体的特定配置设置(即工具选择、编排/推理设置)进行训练。但是,可以通过向其提供展示智能体能力的示例来进一步改进模型以适应智能体的任务,包括智能体在各种上下文中使用特定工具或推理步骤的实例。

工具

基础模型,尽管其在文本和图像生成方面表现出色,但仍然受限于其无法与外部世界交互的能力。工具弥合了这一差距,使智能体能够与外部数据和服务进行交互,同时解锁了超出底层模型本身能力的更广泛的操作。工具可以采用多种形式,并具有不同的复杂程度,但通常与常见的 Web API 方法(如 GET、POST、PATCH 和 DELETE)保持一致。例如,一个工具可以更新数据库中的客户信息,或者获取天气数据以影响智能体向用户提供的旅行建议。借助工具,智能体可以访问和处理真实世界的信息。这使它们能够支持更专业的系统,如检索增强生成 (RAG),这大大扩展了智能体的能力,超越了基础模型自身所能实现的范围。我们将在下面更详细地讨论工具,但最重要的是要理解,工具弥合了智能体的内部能力与外部世界之间的差距,从而开启了更广泛的可能性。

编排层

编排层描述了一个循环过程,该过程控制着智能体如何接收信息、执行一些内部推理,并使用该推理来告知其下一个动作或决策。通常,这个循环将持续进行,直到智能体达到其目标或停止点。编排层的复杂性可能因智能体及其执行的任务而异。有些循环可以是简单的带有决策规则的计算,而另一些循环可能包含链式逻辑、涉及额外的机器学习算法,或实现其他概率推理技术。我们将在认知架构部分讨论更多关于智能体编排层详细实现的信息。

智能体 vs. 模型

为了更清楚地理解智能体和模型之间的区别,请参考以下图表:

模型智能体
知识仅限于其训练数据中可用的内容。知识通过工具与外部系统连接来扩展。
基于用户查询的单次推理/预测。除非为模型显式实现,否则不管理会话历史或连续上下文。(即聊天历史)管理会话历史(即聊天历史),以便基于用户查询和编排层中做出的决策进行多轮推理/预测。在此上下文中,"轮次"定义为交互系统和智能体之间的交互。(即 1 个传入事件/查询和 1 个智能体响应)
非原生工具实现。工具在智能体架构中原生实现。
未实现原生逻辑层。用户可以将提示形成简单问题,或使用推理框架(CoT、ReAct 等)来形成复杂的提示,以指导模型进行预测。使用推理框架(如 CoT、ReAct)或其他预构建的智能体框架(如 LangChain)的原生认知架构。

认知架构:智能体如何运作

想象一下一个繁忙厨房里的厨师。他们的目标是为餐厅顾客制作美味佳肴,这涉及到计划、执行和调整的循环。

  • 他们收集信息,例如顾客的订单以及食品储藏室和冰箱里的食材。
  • 他们根据刚刚收集的信息,对可以制作的菜肴和风味进行内部推理。
  • 他们采取行动来制作菜肴:切菜、混合香料、煎肉。

在过程的每个阶段,厨师都会根据需要进行调整,随着食材的消耗或收到顾客的反馈,改进他们的计划,并使用先前结果的集合来确定下一步的行动计划。这种信息摄入、计划、执行和调整的循环描述了厨师为实现目标而采用的独特的认知架构。

就像厨师一样,智能体可以使用认知架构,通过迭代处理信息、做出明智的决策以及根据先前的输出来改进下一步的行动,从而达到最终目标。智能体认知架构的核心是编排层,负责维护记忆、状态、推理和计划。它利用快速发展的提示工程领域和相关框架来指导推理和计划,使智能体能够更有效地与其环境互动并完成任务。提示工程框架和语言模型任务规划领域的研究正在迅速发展,产生了各种有希望的方法。虽然这不是一个详尽的列表,但以下是本文发布时一些最流行的框架和推理技术:

  • ReAct,一种提示工程框架,为语言模型提供了一种思维过程策略,以推理并根据用户查询采取行动,无论是否有上下文示例。ReAct 提示已显示出优于多个 SOTA 基线,并提高了 LLM 的人机互操作性和可信度。
  • 思维链 (CoT),一种提示工程框架,通过中间步骤实现推理能力。CoT 有多种子技术,包括自洽性、主动提示和多模态 CoT,它们各自具有优缺点,具体取决于特定应用。
  • 思维树 (ToT),一种提示工程框架,非常适合探索或战略前瞻任务。它概括了思维链提示,并允许模型探索各种思维链,这些思维链可以作为使用语言模型解决一般问题的中间步骤。

智能体可以利用上述推理技术之一,或许多其他技术,来选择给定用户请求的最佳下一步行动。例如,让我们考虑一个被编程为使用 ReAct 框架来为用户查询选择正确行动和工具的智能体。事件的顺序可能如下所示:

  1. 用户查询: 用户向智能体发送查询。
  2. 智能体启动 ReAct 序列: 智能体开始 ReAct 序列。
  3. 智能体生成 ReAct 步骤: 智能体向模型提供提示,要求其生成下一个 ReAct 步骤及其对应的输出:     *   a. 问题: 来自用户查询的输入问题,随提示一起提供。     *   b. 思考: 模型关于下一步应该做什么的想法。     *   c. 行动: 模型关于下一步采取什么行动的决定。         *   i. 工具选择: 这是工具选择可能发生的地方。         *   ii. 示例: 行动可以是 [航班, 搜索, 代码, 无] 之一,其中前 3 个代表模型可以选择的已知工具,最后一个代表“不选择工具”。     *   d. 行动输入: 模型关于向工具提供什么输入(如果有)的决定。     *   e. 观察: 行动/行动输入序列的结果。         *   i. 循环: 这个思考/行动/行动输入/观察可以根据需要重复 N 次。     *   f. 最终答案: 模型为原始用户查询提供的最终答案。
  4. ReAct 循环结束: ReAct 循环结束,并将最终答案提供给用户。

图 2. 在编排层中使用 ReAct 推理的示例智能体

如图 2 所示,模型、工具和智能体配置协同工作,根据用户的原始查询向用户提供基于事实的简洁响应。虽然模型可以根据其先前的知识猜测(幻觉)答案,但它却使用工具(航班)来搜索实时外部信息。此附加信息提供给模型,使其能够根据真实的事实数据做出更明智的决策,并将此信息总结返回给用户。

总而言之,智能体响应的质量可以直接与模型推理和执行各种任务的能力相关联,包括选择正确工具的能力以及工具的定义程度。就像厨师用新鲜食材烹制菜肴并关注客户反馈一样,智能体依靠健全的推理和可靠的信息来提供最佳结果。在下一节中,我们将深入探讨智能体连接新鲜数据的各种方式。

工具:我们通往外部世界的钥匙

虽然语言模型擅长处理信息,但它们缺乏直接感知和影响现实世界的能力。这限制了它们在需要与外部系统或数据交互的情况下的实用性。这意味着,在某种意义上,一个语言模型的好坏完全取决于它从训练数据中学到的东西。但是,无论我们向模型投入多少数据,它们仍然缺乏与外部世界交互的基本能力。那么,我们如何才能使我们的模型能够与外部系统进行实时的、上下文感知的交互呢?函数、扩展、数据存储和插件都是为模型提供这种关键能力的方式。

虽然它们有许多名称,但工具是连接我们的基础模型和外部世界的桥梁。这种与外部系统和数据的连接使我们的智能体能够执行更广泛的任务,并且以更高的准确性和可靠性来执行。例如,工具可以使智能体能够调整智能家居设置、更新日历、从数据库中获取用户信息或根据特定指令发送电子邮件。

截至本文发布之日,谷歌模型能够与之交互的主要工具有三种:扩展、函数和数据存储。通过为智能体配备工具,我们为它们解锁了巨大的潜力,不仅可以理解世界,还可以对其采取行动,从而为无数新的应用和可能性打开了大门。

扩展

理解扩展最简单的方式是将其视为在 API 和智能体之间架起一座标准化桥梁,使智能体能够无缝执行 API,而无需考虑其底层实现。假设你构建了一个智能体,目标是帮助用户预订航班。你知道你想使用 Google Flights API 来检索航班信息,但你不确定如何让你的智能体调用这个 API 端点。

图 3. 智能体如何与外部 API 交互?

一种方法是实现自定义代码,该代码将接收传入的用户查询,解析查询以获取相关信息,然后进行 API 调用。例如,在航班预订用例中,用户可能会说“我想预订从奥斯汀到苏黎世的航班”。在这种情况下,我们的自定义代码解决方案需要从用户查询中提取“奥斯汀”和“苏黎世”作为相关实体,然后再尝试进行 API 调用。但是,如果用户说“我想预订飞往苏黎世的航班”,而没有提供出发城市,该怎么办?如果没有所需的数据,API 调用将失败,并且需要实现更多代码才能捕获此类边缘和极端情况。这种方法不具有可扩展性,并且在任何超出已实现自定义代码范围的情况下都容易崩溃。

一种更具弹性的方法是使用扩展。扩展通过以下方式弥合智能体和 API 之间的差距:

  1. 使用示例教导智能体如何使用 API 端点。
  2. 教导智能体成功调用 API 端点所需的参数或参数。

图 4. 扩展将智能体连接到外部 API

扩展可以独立于智能体进行制作,但应作为智能体配置的一部分提供。智能体在运行时使用模型和示例来决定哪个扩展(如果有)适合解决用户的查询。这突出了扩展的一个关键优势,即其内置的示例类型,允许智能体动态选择最适合任务的扩展。

图 5. 智能体、扩展和 API 之间的一对多关系

将其视为软件开发人员在解决用户问题和提供解决方案时决定使用哪些 API 端点的方式。如果用户想要预订航班,开发人员可能会使用 Google Flights API。如果用户想知道离他们最近的咖啡店在哪里,开发人员可能会使用 Google Maps API。同样,智能体/模型堆栈使用一组已知的扩展来决定哪个最适合用户的查询。如果您想了解扩展的实际应用,可以在 Gemini 应用程序中尝试它们,方法是转到“设置”>“扩展”,然后启用您想要测试的任何扩展。例如,您可以启用 Google Flights 扩展,然后询问 Gemini “显示下周五从奥斯汀飞往苏黎世的航班”。

示例扩展

为了简化扩展的使用,Google 提供了一些开箱即用的扩展,可以快速导入到您的项目中,并以最少的配置使用。例如,代码解释器扩展(如代码片段 1 所示)允许您从自然语言描述生成和运行 Python 代码。

智能体

import vertexai
import pprint  


PROJECT_ID = "YOUR_PROJECT_ID"
REGION = "us-central1"  


vertexai.init(project=PROJECT_ID, location=REGION)  


from vertexai.preview.extensions import Extension


extension_code_interpreter = Extension.from_hub("code_interpreter")
CODE_QUERY = """写一个python方法,用O(n)的时间复杂度反转一个二叉树。"""


response = extension_code_interpreter.execute(
    operation_id = "generate_and_execute",
    operation_params = {"query": CODE_QUERY}
)  


print("生成的代码:")
pprint.pprint({response['generated_code']})  


# 上面的代码片段将生成以下代码。

生成的代码:

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right  


def invert_binary_tree(root):
    """
    反转二叉树。
    Args:
        root: 二叉树的根节点。
    Returns: 反转后的二叉树的根节点。
    """
    if not root:
        return None
    
    # 递归地交换左右子节点
    root.left, root.right = invert_binary_tree(root.right), invert_binary_tree(root.left)
    return root  
 
# 示例用法:
# 构建一个示例二叉树
root = TreeNode(4)   
root.left = TreeNode(2)   
root.right = TreeNode(7)   
root.left.left = TreeNode(1)   
root.left.right = TreeNode(3)   
root.right.left = TreeNode(6)   
root.right.right = TreeNode(9)  


# 反转二叉树
inverted_root = invert_binary_tree(root)

Snippet 1. 代码解释器扩展可以生成并运行 Python 代码

总而言之,扩展为智能体提供了以多种方式感知、交互和影响外部世界的方法。这些扩展的选择和调用由示例指导,所有示例都定义为扩展配置的一部分。

函数

在软件工程领域,函数被定义为完成特定任务且可以根据需要重复使用的自包含代码模块。当软件开发人员编写程序时,他们通常会创建许多函数来执行各种任务。他们还会定义何时调用函数A与函数B的逻辑,以及预期的输入和输出。

函数在智能体领域的工作方式非常相似,但我们可以用模型代替软件开发人员。模型可以接收一组已知的函数,并根据其规范决定何时使用每个函数以及函数需要哪些参数。函数与扩展(Extensions)在几个方面有所不同,最显著的是:

  1. 模型输出一个函数及其参数,但不进行实时的API调用。
  2. 函数在客户端执行,而扩展在智能体端执行。

再次使用我们的Google Flights示例,一个简单的函数设置可能如图7所示。

图7. 函数如何与外部API交互?

请注意,这里的主要区别在于函数和智能体都不直接与Google Flights API交互。那么API调用实际上是如何发生的呢?

使用函数,调用实际API端点的逻辑和执行被从智能体卸载回客户端应用程序,如下面的图8图9所示。这为开发人员提供了对应用程序中数据流的更精细的控制。开发人员可能选择使用函数而不是扩展的原因有很多,但一些常见的用例是:

  • API调用需要在应用程序堆栈的另一层进行,而不是直接在智能体架构流程中(例如,中间件系统,前端框架等)。
  • 安全或身份验证限制阻止智能体直接调用API(例如,API未暴露于互联网,或智能体基础设施无法访问)。
  • 时间或操作顺序的约束阻止智能体实时进行API调用。(即批量操作,人工审核等)
  • 需要对智能体无法执行的API响应应用额外的数据转换逻辑。例如,考虑一个API端点,它不提供限制返回结果数量的筛选机制。在客户端使用函数为开发人员提供了进行这些转换的额外机会。
  • 开发人员希望在不为API端点部署额外基础设施的情况下迭代智能体开发(即,函数调用可以像API的“存根”一样)。

虽然如图 8 所示,两种方法在内部架构上的差异很微妙,但额外的控制和与外部基础设施的解耦依赖使得函数调用对于开发者来说是一个更具吸引力的选择。

图 8. 区分扩展和函数调用的客户端与智能体端控制

用例

模型可以被用来调用函数,以便为最终用户处理复杂的客户端执行流程,在这种情况下,智能体开发者可能不希望语言模型来管理 API 的执行(就像扩展的情况一样)。让我们考虑以下示例,其中一个智能体被训练为旅行礼宾,与想要预订度假旅行的用户互动。目标是让智能体生成一个城市列表,我们可以在中间件应用程序中使用这些城市列表来为用户的旅行计划下载图像、数据等。用户可能会说这样的话:

我想和家人去滑雪旅行,但我不知道去哪里。

在典型的模型提示中,输出可能如下所示:

当然,这里有一些您可以考虑用于家庭滑雪旅行的城市列表:

  • 科罗拉多州克雷斯特德比特,美国
  • 加拿大不列颠哥伦比亚省惠斯勒
  • 瑞士采尔马特

虽然上面的输出包含我们需要的数据(城市名称),但格式对于解析来说并不理想。 通过函数调用,我们可以教模型以结构化的样式(如 JSON)格式化此输出,这更方便其他系统解析。 给定用户相同的输入提示,来自函数的 JSON 输出示例可能如代码片段 5 所示。

function_call {
    name: "display_cities"
    args: {
        "cities": ["Crested Butte", "Whistler", "Zermatt"],
        "preferences": "skiing"
    }
}

代码片段 5. 用于显示城市列表和用户偏好的示例函数调用有效负载

这个 JSON 有效负载由模型生成,然后发送到我们的客户端服务器,以执行我们希望的任何操作。在这个特定案例中,我们将调用 Google Places API 来获取模型提供的城市,查找图像,然后将它们作为格式化的富内容提供给我们的用户。请参考图 9 中的时序图,其中详细展示了上述交互的步骤。

图 9. 显示函数调用生命周期的时序图

图 9 中的示例结果是,利用模型来“填补空白”,提供客户端 UI 调用 Google Places API 所需的参数。客户端 UI 使用模型在返回的函数中提供的参数来管理实际的 API 调用。这只是函数调用的一个用例,但还有许多其他场景需要考虑,例如:

  • 您希望语言模型建议您可以在代码中使用的函数,但您不想在代码中包含凭据。由于函数调用不会运行该函数,因此您无需在函数信息中包含凭据。
  • 您正在运行可能需要几秒钟以上的异步操作。这些场景非常适合函数调用,因为它是一个异步操作。
  • 您希望在与生成函数调用及其参数的系统不同的设备上运行函数。

关于函数,要记住的一个关键事项是,它们旨在为开发人员提供对 API 调用执行以及整个应用程序数据流的更多控制。在图 9 的示例中,开发人员选择不将 API 信息返回给智能体,因为它与智能体可能采取的未来操作无关。但是,根据应用程序的架构,将外部 API 调用数据返回给智能体以影响未来的推理、逻辑和操作选择可能是有意义的。最终,由应用程序开发人员来选择适合特定应用程序的内容。

函数示例代码

为了从我们的滑雪度假场景中实现上述输出,让我们构建每个组件,以便与我们的 gemini-1.5-flash-001 模型一起工作。

首先,我们将 display_cities 函数定义为一个简单的 Python 方法。

def display_cities(cities: list[str], preferences: Optional[str] = None):
    """根据用户的搜索查询和偏好提供城市列表。


    Args:
        preferences (str): 用户搜索的偏好,如滑雪、海滩、餐厅、烧烤等。
        cities (list[str]): 推荐给用户的城市列表。


    Returns:
        list[str]: 推荐给用户的城市列表。
    """
    return cities

Snippet 6. 用于显示城市列表的示例 Python 方法。

接下来,我们将实例化我们的模型,构建工具(Tool),然后将用户的查询和工具传递给模型。执行下面的代码将产生代码片段底部所示的输出。

from vertexai.generative_models import GenerativeModel, Tool, FunctionDeclaration


model = GenerativeModel("gemini-1.5-flash-001")


display_cities_function = FunctionDeclaration.from_func(display_cities)
tool = Tool(function_declarations=[display_cities_function])


message = "我想和家人一起去滑雪旅行,但我不知道去哪里。"


res = model.generate_content(message, tools=[tool])


print(f"函数名称: {res.candidates[0].content.parts[0].function_call.name}")
print(f"函数参数: {res.candidates[0].content.parts[0].function_call.args}")

函数名称: display_cities 函数参数: {'preferences': 'skiing', 'cities': ['Aspen', 'Vail', 'Park City']}

代码片段 7. 构建工具,将其与用户查询一起发送给模型,并允许函数调用发生

总而言之,函数提供了一个直接的框架,使应用程序开发人员能够对数据流和系统执行进行精细控制,同时有效地利用智能体/模型进行关键的输入生成。开发人员可以根据特定的应用程序架构需求,有选择地决定是否通过返回外部数据来保持智能体“在循环中”,或者省略它。

数据存储

将语言模型想象成一个巨大的图书馆,其中包含其训练数据。但与不断获取新书的图书馆不同,这个图书馆是静态的,只保留了最初训练时获得的知识。这就带来了一个挑战,因为现实世界的知识在不断发展。数据存储通过提供对更动态和最新的信息的访问来解决这个限制,并确保模型的响应仍然基于事实和相关性。

考虑一个常见的场景,开发人员可能需要向模型提供少量额外的数据,可能是以电子表格或PDF的形式。

图 10. 智能体如何与结构化和非结构化数据交互?

数据存储允许开发人员以其原始格式向智能体提供额外的数据,从而消除了耗时的数据转换、模型再训练或微调的需要。数据存储将传入的文档转换为一组向量数据库嵌入,智能体可以使用这些嵌入来提取所需的信息,以补充其下一个动作或对用户的响应。

图 11. 数据存储将智能体连接到各种类型的新实时数据源。

实现与应用

在生成式人工智能智能体的背景下,数据存储通常被实现为向量数据库,开发者希望智能体在运行时能够访问该数据库。虽然我们在此不会深入探讨向量数据库,但需要理解的关键点是,它们以向量嵌入的形式存储数据,这是一种高维向量或所提供数据的数学表示。最近,数据存储与语言模型结合使用的最显著的例子之一是基于检索增强生成(RAG)的应用程序的实现。这些应用程序旨在通过让模型访问各种格式的数据,例如:

  • 网站内容
  • 诸如 PDF、Word 文档、CSV、电子表格等格式的结构化数据
  • 诸如 HTML、PDF、TXT 等格式的非结构化数据

图 12. 智能体和数据存储之间的一对多关系,可以表示各种类型的预索引数据

每个用户请求和智能体响应循环的底层过程通常如图 13 所示建模。

  1. 用户查询被发送到嵌入模型以生成查询的嵌入
  2. 然后使用诸如 SCaNN 之类的匹配算法,将查询嵌入与向量数据库的内容进行匹配
  3. 匹配的内容以文本格式从向量数据库中检索出来,并发送回智能体
  4. 智能体接收用户查询和检索到的内容,然后制定响应或操作
  5. 向用户发送最终响应

图 13. 基于 RAG 的应用程序中用户请求和智能体响应的生命周期

最终的结果是一个应用程序,它允许智能体通过向量搜索将用户的查询与已知的数据存储进行匹配,检索原始内容,并将其提供给编排层和模型以进行进一步处理。下一个操作可能是向用户提供最终答案,或者执行额外的向量搜索以进一步细化结果。

图 14 显示了一个使用 RAG 和 ReAct 推理/规划的智能体的示例交互。

图 14. 使用 ReAct 推理/规划的示例 RAG 应用程序

工具回顾

总而言之,扩展、函数调用和数据存储构成了智能体在运行时可使用的几种不同的工具类型。每种工具都有其自身的用途,智能体开发者可以自行决定是组合使用还是独立使用。

扩展函数调用数据存储
执行智能体端执行客户端执行智能体端执行 开发者希望
用例开发者希望智能体控制与API端点的交互。当利用原生预构建的扩展(例如,Vertex Search、代码解释器等)时很有用。多跳规划和API调用(例如,下一个智能体动作取决于上一个动作/API调用的输出)安全或身份验证限制阻止智能体直接调用API。时间限制或操作顺序限制阻止智能体实时进行API调用。(例如,批量操作、人工审核等)未暴露于互联网或Google系统无法访问的API使用以下任何数据类型实现检索增强生成(RAG):来自预索引域和URL的网站内容 PDF、Word文档、CSV、电子表格等格式的结构化数据 关系/非关系数据库 HTML、PDF、TXT等格式的非结构化数据

通过目标学习增强模型性能

有效使用模型的关键在于模型在生成输出时选择正确工具的能力,尤其是在生产环境中大规模使用工具时。虽然通用训练有助于模型发展这种技能,但现实场景通常需要超出训练数据的知识。可以将此想象为基本烹饪技能和掌握特定菜系之间的差异。两者都需要基础的烹饪知识,但后者需要有针对性的学习才能获得更细致的结果。

为了帮助模型获得这种类型的特定知识,有几种方法:

  • 上下文学习:这种方法在推理时为通用模型提供提示、工具和少样本示例,使其能够“即时”学习如何以及何时将这些工具用于特定任务。ReAct 框架是自然语言中这种方法的一个例子。
  • 基于检索的上下文学习:这种技术通过从外部存储器中检索最相关的信息、工具和相关示例,动态地填充模型提示。这方面的一个例子是 Vertex AI 扩展中的“示例存储”或之前提到的基于 RAG 的数据存储架构。
  • 基于微调的学习:这种方法涉及在推理之前使用更大的特定示例数据集来训练模型。这有助于模型在接收任何用户查询之前了解何时以及如何应用某些工具。

为了提供关于每种目标学习方法的更多见解,让我们回顾一下我们的烹饪类比。

  • 想象一下,一位厨师收到了一份特定的食谱(提示)、一些关键食材(相关工具)和一些来自客户的示例菜肴(少样本示例)。基于这些有限的信息和厨师的通用烹饪知识,他们需要“即时”弄清楚如何准备一道最符合食谱和客户偏好的菜肴。这就是上下文学习。
  • 现在,让我们想象一下,我们的厨师在一个储备充足的厨房(外部数据存储)里,里面装满了各种食材和食谱(示例和工具)。厨师现在能够从食品储藏室中动态选择食材和食谱,并更好地与客户的食谱和偏好保持一致。这使厨师能够利用现有和新的知识创造出更明智和更精致的菜肴。这就是基于检索的上下文学习。
  • 最后,让我们想象一下,我们把厨师送回学校学习新的菜系或一系列菜系(在更大的特定示例数据集上进行预训练)。这使得厨师能够以更深入的理解来处理未来未见过的客户食谱。如果我们希望厨师擅长特定的菜系(知识领域),这种方法是完美的。这就是基于微调的学习。

这些方法在速度、成本和延迟方面各有优缺点。然而,通过在智能体框架中结合这些技术,我们可以利用各种优势并最大限度地减少其弱点,从而实现更强大和适应性更强的解决方案。

使用 LangChain 快速启动智能体

为了提供一个实际可执行的智能体示例,我们将使用 LangChain 和 LangGraph 库构建一个快速原型。这些流行的开源库允许用户通过“链接”逻辑、推理和工具调用的序列来构建客户智能体,以回答用户的查询。我们将使用我们的 gemini-1.5-flash-001 模型和一些简单的工具来回答用户在代码片段 8 中看到的多阶段查询。

我们正在使用的工具是 SerpAPI(用于 Google 搜索)和 Google Places API。在代码片段 8 中执行我们的程序后,您可以在代码片段 9 中看到示例输出。

from langgraph.prebuilt import create_react_agent
from langchain_core.tools import tool
from langchain_community.utilities import SerpAPIWrapper
from langchain_community.tools import GooglePlacesTool


os.environ["SERPAPI_API_KEY"] = "XXXXX"
os.environ["GPLACES_API_KEY"] = "XXXXX"


@tool  
def search(query: str):
    """使用 SerpAPI 运行 Google 搜索。"""
    search = SerpAPIWrapper()
    return search.run(query)  


@tool  
def places(query: str):
    """使用 Google Places API 运行 Google Places 查询。"""
    places = GooglePlacesTool()
    return places.run(query)  


model = ChatVertexAI(model="gemini-1.5-flash-001")
tools = [search, places]  


query = "德克萨斯长角牛队上周的足球比赛对手是谁?对方球队的体育场地址是什么?"  


agent = create_react_agent(model, tools)
input = {"messages": [("human", query)]}


for s in agent.stream(input, stream_mode="values"):
    message = s["messages"][-1]
    if isinstance(message, tuple):
        print(message)
    else:
        message.pretty_print()

Snippet 8. 基于 LangChain 和 LangGraph 的示例智能体,带有工具

==================== Human Message ====================
德克萨斯长角牛队上周的橄榄球比赛对手是谁?对方球队的体育场地址是什么?
==================== Ai Message ====================
工具调用:搜索
参数:
    query: 德克萨斯长角牛队橄榄球赛程
==================== Tool Message ====================
名称:搜索
{...结果:“NCAA一级橄榄球赛,佐治亚州,日期...”}
==================== Ai Message ====================
德克萨斯长角牛队上周与佐治亚斗牛犬队比赛。
工具调用:地点
参数:
    query: 佐治亚斗牛犬队体育场
==================== Tool Message ====================
名称:地点
{...桑福德体育场地址:100 Sanford...}
==================== Ai Message ====================
佐治亚斗牛犬队体育场的地址是美国佐治亚州雅典市桑福德大道100号,邮编30602。

Snippet 9. 我们程序在代码片段 8 中的输出

虽然这是一个相当简单的智能体示例,但它展示了模型、编排和工具所有组件协同工作以实现特定目标的基本要素。在最后一部分,我们将探讨这些组件如何在谷歌规模的托管产品(如 Vertex AI 智能体和生成式剧本)中结合在一起。

基于 Vertex AI 智能体的生产应用

虽然这份白皮书探讨了智能体的核心组件,但构建生产级应用需要将它们与用户界面、评估框架和持续改进机制等其他工具集成。Google 的 Vertex AI 平台通过提供一个完全托管的环境简化了这一过程,该环境包含了前面提到的所有基本要素。借助自然语言界面,开发人员可以快速定义其智能体的关键要素 - 目标、任务指令、工具、用于任务委派的子智能体和示例 - 以轻松构建所需的系统行为。此外,该平台还提供了一套开发工具,用于测试、评估、衡量智能体性能、调试和提高已开发智能体的整体质量。这使得开发人员可以专注于构建和改进他们的智能体,而基础设施、部署和维护的复杂性则由平台本身管理。

在图 15 中,我们提供了一个基于 Vertex AI 平台构建的智能体示例架构,该架构使用了 Vertex Agent Builder、Vertex Extensions、Vertex Function Calling 和 Vertex Example Store 等多种功能。该架构包含了生产就绪应用所需的许多组件。

图 15. 基于 Vertex AI 平台构建的端到端智能体示例架构

您可以从我们的官方文档中尝试这个预构建的智能体架构示例。

总结

在这份白皮书中,我们讨论了生成式人工智能智能体的基础构建模块、它们的构成以及以认知架构形式实现它们的有效方法。这份白皮书的一些主要结论包括:

  1. 智能体通过利用工具访问实时信息、建议现实世界的行动以及自主规划和执行复杂任务,扩展了语言模型的能力。智能体可以利用一个或多个语言模型来决定何时以及如何转换状态,并使用外部工具来完成模型自身难以或不可能完成的任何数量的复杂任务。
  2. 智能体运行的核心是编排层,这是一种认知架构,它构建了推理、规划、决策并指导其行动。诸如ReAct、思维链和思维树等各种推理技术为编排层提供了一个框架,用于接收信息、执行内部推理并生成明智的决策或响应。
  3. 诸如扩展、函数和数据存储之类的工具是智能体通往外部世界的钥匙,使它们能够与外部系统交互并访问超出其训练数据范围的知识。扩展提供了智能体和外部 API 之间的桥梁,从而能够执行 API 调用并检索实时信息。函数通过分工为开发人员提供了更细致的控制,允许智能体生成可在客户端执行的函数参数。数据存储为智能体提供了对结构化或非结构化数据的访问,从而支持数据驱动的应用程序。

智能体的未来充满令人兴奋的进步,我们才刚刚开始触及可能性的表面。随着工具变得更加复杂,推理能力得到增强,智能体将被赋予解决日益复杂问题的能力。此外,“智能体链”的战略方法将继续获得发展势头。通过结合专门的智能体(每个智能体都擅长特定的领域或任务),我们可以创建一种“智能体专家混合”方法,能够在各个行业和问题领域提供卓越的成果。

重要的是要记住,构建复杂的智能体架构需要迭代的方法。实验和改进是为特定业务案例和组织需求找到解决方案的关键。由于支撑其架构的基础模型的生成性质,没有两个智能体是相同的。然而,通过利用这些基础组件中每一个的优势,我们可以创建有影响力的应用程序,扩展语言模型的功能并驱动现实世界的价值。

尾注

  1. Shafran, I., Cao, Y. 等, 2022, 'ReAct: 语言模型中推理和行动的协同作用'。 可在以下网址获取:arxiv.org/abs/2210.03…
  2. Wei, J., Wang, X. 等, 2023, '思维链提示引发大型语言模型的推理'。 可在以下网址获取:arxiv.org/pdf/2201.11…
  3. Wang, X. 等, 2022, '自洽性改进了语言模型中的思维链推理'。 可在以下网址获取:arxiv.org/abs/2203.11…
  4. Diao, S. 等, 2023, '使用思维链进行大型语言模型的积极提示'。 可在以下网址获取:arxiv.org/pdf/2302.12…
  5. Zhang, H. 等, 2023, '语言模型中的多模态思维链推理'。 可在以下网址获取:arxiv.org/abs/2302.00…
  6. Yao, S. 等, 2023, '思维树:使用大型语言模型进行深思熟虑的问题解决'。 可在以下网址获取:arxiv.org/abs/2305.10…
  7. Long, X., 2023, '大型语言模型引导的思维树'。 可在以下网址获取:arxiv.org/abs/2305.08…
  8. Google. 'Google Gemini 应用程序'。 可在以下网址获取:gemini.google.com。
  9. Swagger. 'OpenAPI 规范'。 可在以下网址获取:swagger.io/specificati…
  10. Xie, M., 2022, '上下文学习是如何工作的?一个理解其与传统监督学习差异的框架'。 可在以下网址获取:ai.stanford.edu/blog/unders…
  11. Google Research. 'ScaNN (可扩展最近邻)'。 可在以下网址获取:github.com/google-rese…
  12. LangChain. 'LangChain'。 可在以下网址获取:python.langchain.com/v0.2/docs/i…