[译文] Google AI Agents 白皮书

本文适合对于 AI Agents 没有什么概念,正准备开发 Agents 的新手进行知识科普,虽然有些地方使用了google 提供的能力,整体还算比较中立。

[译文] Google AI Agents 白皮书

编者按:本文适合对于 AI Agents 没有什么概念,正准备开发 Agents 的新手进行知识科普,虽然有些地方使用了google 提供的能力,整体还算比较中立。

Agents

作者: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

目录

  • 引言
  • 什么是代理?
    • 模型
    • 工具
    • 编排层
    • 代理与模型的区别
    • 认知架构:代理如何运作
  • 工具:连接外部世界的关键
    • 扩展
      • 示例扩展
    • 函数
      • 使用场景
      • 函数示例代码
    • 数据存储
      • 实现与应用
    • 工具回顾
  • 通过目标学习提升模型性能
  • 使用 LangChain 快速构建代理
  • 使用 Vertex AI 代理进行生产应用
  • 总结
  • 尾注
结合推理、逻辑以及对外部信息的访问,这些能力与生成式 AI 模型相连,诞生了代理的概念。

引言

人类非常擅长处理复杂的模式识别任务。然而,他们常常需要依赖书籍、谷歌搜索或计算器等工具来补充已有的知识,然后得出结论。同样,生成式 AI 模型也可以通过使用工具来访问实时信息或建议现实中的操作。例如,模型可以利用数据库检索工具来访问特定信息,比如客户的购买历史,从而生成个性化的购物推荐。此外,根据用户的查询,模型可以进行各种 API 调用,比如发送邮件回复给同事或代表用户完成一笔金融交易。为了实现这些功能,模型不仅需要具备访问一组外部工具的能力,还需要能够自主规划和执行任务。这种结合了推理、逻辑和外部信息访问的能力,与生成式 AI 模型相结合,就引出了代理(agent)的概念。代理是一种超越单一生成式 AI 模型能力的程序。本白皮书将详细探讨这些及相关的内容。

什么是代理?

从最基础的角度来说,生成式 AI 代理可以定义为一种通过观察世界并使用其可用工具采取行动以实现目标的应用程序。代理是自主的,可以在没有人类干预的情况下独立运行,尤其是在明确提供了目标或任务的情况下。此外,代理在实现目标的过程中还可以表现出主动性。即使在没有明确的指令下,代理也可以推断出下一步应该做什么,以达成最终目标。尽管 AI 中的代理概念非常广泛且强大,本白皮书重点探讨的是生成式 AI 模型当前能够构建的特定类型的代理。

为了理解代理的内部工作原理,我们首先需要介绍驱动代理行为、行动和决策的核心组件。这些组件的组合可以描述为一种认知架构,通过不同组件的搭配可以实现多种架构。关注核心功能,代理的认知架构有以下三个关键组件,如图 1 所示。

图 1. 通用代理架构及其组件

模型

在代理的范围内,模型是指将作为代理流程中心决策者的语言模型(LM)。代理所使用的模型可以是一个或多个任何规模的语言模型(小型/大型),这些模型能够遵循基于指令的推理和逻辑框架,如 ReAct、Chain-of-Thought 或 Tree-of-Thoughts。根据代理架构的具体需求,模型可以是通用的、多模态的,或经过微调的。为了获得最佳的生产效果,应选择最符合目标应用的模型,并优先选择在与计划使用的工具相关数据特征上经过训练的模型。需要注意的是,模型通常不会针对代理的具体配置设置(如工具选择、编排/推理设置)进行训练。然而,可以通过为模型提供示例来进一步优化其任务能力,这些示例展示了代理在不同情境中使用特定工具或推理步骤的能力。

工具

尽管基础模型在文本和图像生成方面表现令人印象深刻,但它们无法与外部世界交互的局限性仍然存在。工具弥补了这一缺陷,使代理能够与外部数据和服务交互,并解锁基础模型无法单独完成的更多操作。工具形式多样,复杂程度不一,但通常与常见的 Web API 方法(如 GET、POST、PATCH 和 DELETE)一致。例如,一个工具可以更新数据库中的客户信息,或获取天气数据以优化旅行推荐。有了工具,代理能够访问并处理现实世界中的信息。这使代理能够支持更复杂的系统,如检索增强生成(RAG),显著扩展了代理的能力,使其远超基础模型本身。我们将在后文更详细地讨论工具,但最重要的是,工具连接了代理的内部能力与外部世界,解锁了更多可能性。

编排层

编排层描述了一个循环过程,控制代理如何获取信息、执行内部推理,并利用这些推理来决定下一步行动或决策。一般而言,这种循环会持续进行,直到代理达成目标或到达终点。根据代理及其任务的不同,编排层的复杂性可能有很大差异。一些循环可能只是简单的计算和决策规则,而另一些则可能包含链式逻辑、额外的机器学习算法或其他概率推理技术。在后续认知架构部分,我们将深入讨论代理编排层的详细实现。

代理 vs. 模型

以下表格有助于更清楚地理解代理与模型的区别:

模型 代理
知识局限于其训练数据中可用的信息。 通过工具与外部系统的连接扩展知识。
基于用户查询进行单次推理/预测。模型通常不具备管理会话历史或连续上下文的能力(如聊天历史),除非对其进行了明确实现。 管理会话历史(如聊天历史),支持基于用户查询和编排层决策的多轮推理/预测。在这种情况下,“轮次”定义为交互系统与代理之间的一次交互(如 1 次事件/查询与 1 次代理响应)。
没有内置工具实现。 工具原生集成在代理架构中。
没有内置逻辑层实现。用户可以构造简单问题的提示,或者使用推理框架(如 CoT、ReAct 等)构建复杂提示来指导模型的预测。 内置认知架构,使用推理框架(如 CoT、ReAct 或其他预构建代理框架,如 LangChain)。

认知架构:代理如何运作

想象一下,厨房里有一位忙碌的厨师。他的目标是为餐厅顾客制作美味的菜肴,而这需要经历一系列的规划、执行和调整循环。

  • 他们收集信息,例如顾客的订单,以及储藏室和冰箱中的食材。
  • 他们进行内部推理,基于刚刚收集的信息思考可以制作哪些菜品和风味组合。
  • 他们采取行动制作菜肴:切菜、混合调料、煎烤肉类。

在整个过程中,厨师会根据需要进行调整,例如食材不足或接收到顾客的反馈,从而优化计划,并利用之前的经验决定下一步行动。这种信息摄取、规划、执行和调整的循环,描述了厨师为了达成目标所使用的一种独特认知架构。

与厨师类似,代理也可以使用认知架构,通过反复处理信息、做出明智的决策,并根据之前的输出优化下一步行动来实现最终目标。在代理的认知架构核心中,编排层负责维护记忆、状态、推理和规划。它利用快速发展的提示工程领域及相关框架来指导推理和规划,从而使代理更高效地与环境交互并完成任务。在提示工程框架和语言模型任务规划领域的研究正在迅速发展,产生了许多有前景的方法。以下是目前几种较为流行的框架和推理技术(非完整列表):

  • ReAct:一种提示工程框架,向语言模型提供思考和行动策略,用于处理用户查询,可包含上下文示例,也可不包含。ReAct 提示已被证明优于多个当前最优基线,并提高了大型语言模型的可互操作性和可信度。
  • Chain-of-Thought (CoT):一种通过中间步骤实现推理能力的提示工程框架。CoT 包括多种子技术,如自一致性、主动提示和多模态 CoT,这些技术在不同应用场景中各有优劣。
  • Tree-of-Thoughts (ToT):一种适用于探索或战略性前瞻任务的提示工程框架。它是对 Chain-of-Thought 提示的推广,允许模型探索作为问题解决中间步骤的多条思维链。

代理可以利用上述推理技术之一或其他技术,根据用户请求选择最佳行动。例如,假设一个代理被编程为使用 ReAct 框架来选择正确的操作和工具处理用户查询,事件序列可能如下:

  1. 用户向代理发送查询。
  2. 代理启动 ReAct 流程。
  3. 代理向模型提供一个提示,要求其生成下一个 ReAct 步骤及其相应输出:
    • 问题 (Question):用户查询中的输入问题,包含在提示中。
    • 思考 (Thought):模型关于下一步行动的思考。
    • 行动 (Action):模型决定下一步要采取的行动:
      • 这是工具选择的地方,例如,行动可能是以下之一:[Flights(航班)、Search(搜索)、Code(代码)、None(无工具选择)]。
    • 行动输入 (Action input):模型决定提供给工具的输入(如果有)。
    • 观察 (Observation):行动/行动输入序列的结果。
      • 这一“思考/行动/行动输入/观察”过程可以根据需要重复多次。
    • 最终答案 (Final answer):模型提供给用户查询的最终答案。
  4. ReAct 循环结束,代理向用户提供最终答案。
图 2. 在编排层中使用 ReAct 推理的代理示例

如图 2 所示,模型、工具和代理配置协同工作,为用户的原始查询提供基于事实的简明响应。虽然模型可以基于其先前的知识猜测答案(产生幻觉),但它选择使用工具(如 Flights)搜索实时的外部信息。这些附加信息被提供给模型,使其能够基于真实的事实数据做出更明智的决策,并将这些信息总结后返回给用户。

总结

代理的响应质量直接取决于模型对任务的推理和行动能力,包括选择合适工具的能力以及工具定义的质量。就像厨师使用新鲜食材制作菜肴并关注顾客反馈一样,代理依赖于稳健的推理和可靠的信息来提供最佳结果。在下一节中,我们将深入探讨代理如何与新鲜数据连接。

工具:连接外部世界的钥匙

尽管语言模型在信息处理方面表现出色,但它们缺乏直接感知和影响现实世界的能力。这限制了它们在需要与外部系统或数据交互的场景中的实用性。换句话说,语言模型的能力取决于其训练数据。然而,无论向模型输入多少数据,它们依然缺乏与外部世界交互的基本能力。那么,我们如何让模型能够与外部系统实时、上下文相关地交互呢?函数(Functions)扩展(Extensions)数据存储(Data Stores)插件(Plugins) 是实现这一关键能力的几种方式。

尽管名称各异,但“工具”就是连接基础模型与外部世界的桥梁。这一连接外部系统和数据的能力使代理能够执行更多种类的任务,并以更高的准确性和可靠性完成任务。例如,工具可以使代理调整智能家居设置、更新日历、从数据库获取用户信息,或根据特定指令发送电子邮件。截至本白皮书发布日期,Google 模型能够交互的三种主要工具类型是:扩展、函数和数据存储。通过为代理配备工具,我们为其打开了理解和作用于世界的大门,为各种新应用和可能性提供了广阔的空间。

扩展 (Extensions)

要理解扩展,可以将其视为在 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 提供了一些开箱即用的扩展,可以快速导入到您的项目中,并以最少的配置进行使用。例如,代码解释器扩展(Code Interpreter Extension)可以根据自然语言描述生成并运行 Python 代码,如代码片段 1 所示。

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 = """Write a python method to invert a binary tree in O(n) time."""

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

print("Generated Code:")
pprint.pprint({response['generated_code']})

# 以上代码将生成以下代码:

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):
    """
    Inverts a binary tree.
    Args:
        root: The root of the binary tree.
    Returns:
        The root of the inverted binary tree.
    """
    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)

代码片段 1:代码解释器扩展(Code Interpreter Extension)可以生成并运行 Python 代码。

总结来说,扩展提供了一种方式,使代理能够以多种方式感知、交互并影响外部世界。扩展的选择和调用通过示例引导,所有示例均作为扩展配置的一部分定义。

函数(Functions)

在软件工程中,函数是定义为完成特定任务的独立代码模块,可以根据需要重复使用。当软件开发人员编写程序时,他们通常会创建多个函数来完成不同的任务,并定义调用函数 A 或函数 B 的逻辑,以及期望的输入和输出。

在代理的世界中,函数的工作方式非常相似,但我们可以将软件开发人员替换为模型。模型可以根据函数的规范决定何时使用已知的函数,以及函数需要哪些参数。函数与扩展之间存在以下显著区别:

  1. 模型输出函数及其参数,但不会进行实时 API 调用
  2. 函数在客户端执行,而扩展在代理端执行

以 Google Flights 为例,一个简单的函数设置可能如图 7 所示。

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

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

对于函数,实际 API 端点的调用逻辑和执行被从代理端转移到客户端应用程序,如图 8 和图 9 所示。这为开发者提供了对应用程序中数据流的更细粒度控制。开发者选择函数而非扩展的原因可能包括:

  • API 调用需要在应用程序堆栈的其他层次进行(例如中间件系统、前端框架等),而不是直接通过代理架构完成。
  • 安全或身份验证限制,防止代理直接调用 API(例如,API 不对互联网开放,或代理基础设施无法访问)。
  • 时间或操作顺序限制,导致代理无法实时进行 API 调用(例如批量操作或需要人工审核的情况)。
  • 需要对 API 响应进行额外的数据转换逻辑,而代理无法完成。例如,一个 API 端点可能不提供限制返回结果数量的筛选机制。使用客户端函数可以对这些结果进行进一步的转换。
  • 开发者希望在无需部署额外基础设施的情况下迭代代理开发(例如函数调用可以充当 API 的“模拟”)。

虽然这两种方法的内部架构差异较小(如图 8 所示),但函数调用带来的额外控制和对外部基础设施的解耦,使其对开发者具有吸引力。

图 8. 客户端与代理端的控制范围:扩展与函数调用的对比

使用场景

模型可以调用函数来处理复杂的客户端执行流程,特别是在代理开发者不希望语言模型管理 API 调用(如使用扩展的情况)时。例如,考虑一个代理被训练为旅行顾问,与想预订度假旅行的用户交互。目标是让代理生成城市列表,供我们的中间件应用程序使用,以下载用户旅行计划的图片、数据等内容。用户可能会说:

“我想和家人一起滑雪旅行,但我不确定该去哪里。”

在模型的典型输出中,可能会显示如下内容:

“当然,这里是一些适合家庭滑雪旅行的城市:”

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

尽管上述输出包含我们需要的数据(城市名称),但格式并不便于解析。通过函数调用,我们可以教模型以结构化格式(如 JSON)输出数据,这对其他系统解析更为便利。基于相同的用户输入提示,函数调用的示例 JSON 输出可能如代码片段 5 所示。

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

代码片段 5:函数调用生成的 JSON 有效负载,用于显示城市列表和用户偏好。

此 JSON 有效负载由模型生成,然后发送到客户端服务器,供我们根据需要进行处理。在上述特定情况下,我们将调用 Google Places API,利用模型提供的城市信息查找图片,并将其作为格式化的富内容返回给用户。图 9 展示了这一交互的详细步骤。

图 9. 函数调用生命周期的序列图

图 9 示例的结果是,模型用于填充客户端 UI 调用 Google Places API 所需的参数。客户端 UI 使用模型返回的函数参数管理实际的 API 调用。这只是函数调用的一个使用场景,其他可能的场景还包括:

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

函数的一个关键点是,它旨在为开发者提供对 API 调用执行以及整个应用程序数据流的更大控制。例如,在图 9 中,开发者选择不将 API 信息返回代理,因为这对代理的后续操作并不重要。然而,根据应用程序的架构,将外部 API 调用数据返回代理可能是合理的,这样可以影响后续推理、逻辑和操作选择。最终,应用开发者应根据具体应用场景做出合适的选择。

函数示例代码

为了实现上述滑雪度假场景的输出,我们将为 gemini-1.5-flash-001 模型构建相关组件。

首先,我们定义一个简单的 Python 方法 display_cities

def display_cities(cities: list[str], preferences: Optional[str] = None):
    """根据用户的搜索查询和偏好提供城市列表。
    参数:
        preferences (str): 用户的搜索偏好,如滑雪、海滩、餐厅、烧烤等。
        cities (list[str]): 推荐给用户的城市列表。
    返回值:
        list[str]: 推荐给用户的城市列表。
    """
    return cities

代码片段 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"Function Name: {res.candidates[0].content.parts[0].function_call.name}")
print(f"Function Args: {res.candidates[0].content.parts[0].function_call.args}")

# 输出
> Function Name: display_cities
> Function Args: {'preferences': 'skiing', 'cities': ['Aspen', 'Vail', 'Park City']}

代码片段 7:构建工具,将用户查询传递给模型并触发函数调用。

总结来说,函数提供了一个简单的框架,开发者可以通过它对数据流和系统执行进行细粒度控制,同时利用代理/模型生成关键的输入。开发者可以选择是否将代理“留在循环中”(例如返回外部数据),也可以根据具体的应用架构需求省略这些步骤。

数据存储(Data Stores)

想象一下,语言模型就像一个包含其训练数据的大型图书馆。但与不断引入新书的图书馆不同,这个图书馆是静态的,只包含其初始训练数据。这带来了一个挑战,因为现实世界的知识是不断变化的。数据存储(Data Stores) 通过提供对更多动态和最新信息的访问,解决了这一限制,从而确保模型的响应保持真实且相关。

考虑一个常见场景,开发者可能需要向模型提供少量额外数据,例如电子表格或 PDF 文件。

图 10:代理如何与结构化和非结构化数据交互?

数据存储允许开发者以原始格式将额外数据提供给代理,省去了耗时的数据转换、模型重训练或微调的过程。数据存储将输入文档转换为一组向量数据库嵌入,代理可以利用这些嵌入来提取信息,从而补充其下一步的行动或响应。

图 11:数据存储将代理连接到各种类型的实时数据源。

实现与应用

在生成式 AI 代理的上下文中,数据存储通常以向量数据库的形式实现,开发者希望代理在运行时可以访问这些数据。虽然这里不会深入探讨向量数据库,但关键点是它以向量嵌入的形式存储数据,这是一种数据的高维向量或数学表示。最近,数据存储与语言模型结合的最常见应用是基于检索增强生成(Retrieval Augmented Generation, RAG) 的应用。这些应用通过使模型访问多种格式的数据,扩展了其知识的广度和深度,例如:

  • 网站内容
  • PDF、Word 文档、CSV、电子表格等格式的结构化数据
  • HTML、PDF、TXT 等格式的非结构化数据
图 12:代理与数据存储之间的“一对多”关系,可表示多种预索引数据类型。


每次用户请求和代理响应的底层流程通常如图 13 所示。

  1. 用户查询被发送到嵌入模型以生成查询嵌入。
  2. 查询嵌入通过匹配算法(如 SCaNN)与向量数据库的内容匹配。
  3. 从向量数据库中检索匹配内容并以文本格式返回给代理。
  4. 代理接收用户查询和检索内容,然后制定响应或采取行动。
  5. 最终响应发送给用户。
图 13:基于 RAG 应用的用户请求和代理响应生命周期。

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

图 14 展示了使用 RAG 和 ReAct 推理/规划的代理示例交互。

图 14:基于 RAG 的示例应用,结合 ReAct 推理/规划。


工具回顾

总结来说,扩展、函数和数据存储构成了代理在运行时可使用的几种工具类型。每种工具都有其独特的用途,可以根据代理开发者的需求单独或组合使用。

工具类型 扩展(Extensions) 函数调用(Function Calling) 数据存储(Data Stores)
执行 代理端执行 客户端执行 代理端执行
使用场景 - 开发者希望代理控制与 API 端点的交互 - 适用于利用原生预构建扩展(如 Vertex Search、Code Interpreter 等) - 多跳规划与 API 调用(即下一步操作依赖于前一步操作/API 调用的输出) - 安全或身份验证限制,防止代理直接调用 API - 时间约束或操作顺序限制,防止代理实时调用 API(如批量操作、人工审核等) - API 不对外开放,或 Google 系统无法访问 开发者希望实现基于 RAG 的应用,使用以下数据类型: - 预索引域名和 URL 的网站内容 - PDF、Word 文档、CSV、电子表格等格式的结构化数据 - 关系型/非关系型数据库 - HTML、PDF、TXT 等格式的非结构化数据

通过目标学习提升模型性能

使用模型的一个关键方面是它们在生成输出时选择正确工具的能力,尤其是在生产环境中大规模使用工具时。尽管一般训练可以帮助模型发展这一技能,但现实场景常常需要超出训练数据范围的知识。可以通过以下方法实现目标学习:

  • 上下文学习(In-context Learning):为模型提供提示、工具和少量示例,使其能够“即时学习”如何在特定任务中使用这些工具。ReAct 框架是自然语言上下文学习的一个示例。
  • 基于检索的上下文学习:通过从外部内存中检索最相关的信息、工具和示例,动态填充模型提示。例如,Vertex AI 扩展中的“示例存储(Example Store)”或前文提到的数据存储 RAG 架构。
  • 基于微调的学习(Fine-tuning Based Learning):在推理之前使用更大的特定示例数据集对模型进行训练,帮助模型在接收用户查询前理解何时及如何应用某些工具。

通过将这些技术组合到代理框架中,我们可以利用各种优势,减少其弱点,从而构建更强大且适应性更高的解决方案。

使用 LangChain 快速构建代理

为提供实际可执行的代理示例,我们将使用 LangChain 和 LangGraph 库构建一个快速原型。这些开源库允许用户通过将逻辑、推理和工具调用“链接”起来,构建自定义代理。以下代码展示了一个多阶段查询的示例。

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 进行查询。"""
    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()

代码片段 8:基于 LangChain 和 LangGraph 构建的代理示例。

输出示例如下:

=============================== 人类消息 ================================
上周德克萨斯大学长角牛橄榄球队与谁比赛?对方球队的体育场地址是什么?
================================= AI 消息 ================================
工具调用:search
参数:
	query: 德克萨斯大学长角牛橄榄球队赛程
================================ 工具消息 ================================
名称:search
{...结果: "NCAA 一级橄榄球赛,佐治亚州,日期..."}
================================= AI 消息 ================================
德克萨斯大学长角牛橄榄球队上周对阵佐治亚斗牛犬队。
工具调用:places
参数:
	query: 佐治亚斗牛犬队体育场
================================ 工具消息 ================================
名称:places
{...桑福德体育场地址: 100 Sanford...}
================================= AI 消息 ================================
佐治亚斗牛犬队体育场的地址是 100 Sanford Dr, Athens, GA 30602, USA。

代码片段 9:程序输出示例。

尽管这是一个相对简单的代理示例,但它展示了模型、编排层和工具协同工作的基本组件。

使用 Vertex AI 代理进行生产应用

在生产环境中,Google 的 Vertex AI 平台通过提供全面管理的环境,简化了代理开发。开发者可以快速定义代理的目标、任务指令、工具、子代理及示例,构建理想的系统行为。平台还提供一套开发工具,用于测试、评估、性能测量、调试和改进代理质量,帮助开发者专注于代理的构建与优化。

下图展示了基于 Vertex AI 平台构建的代理架构示例,涵盖了生产就绪应用所需的各种组件。

图 15:基于 Vertex AI 平台构建的端到端代理架构示例。

您可以从我们的官方文档中试用此预构建代理架构示例。

总结

在本白皮书中,我们讨论了生成式 AI 代理的基础构建模块、组成方式以及通过认知架构实现它们的有效方法。以下是本白皮书的一些关键要点:

  1. 代理通过利用工具扩展了语言模型的能力,能够访问实时信息、建议现实世界中的操作,并自主规划和执行复杂任务。代理可以利用一个或多个语言模型来决定何时以及如何在不同状态间转换,并使用外部工具完成模型单独无法完成的复杂任务。
  2. 代理运行的核心是编排层,这是一种认知架构,用于组织推理、规划、决策并指导其行动。各种推理技术(如 ReAct、Chain-of-Thought 和 Tree-of-Thoughts)为编排层提供了框架,使其能够摄取信息、执行内部推理,并生成明智的决策或响应。
  3. 工具(如扩展、函数和数据存储)是代理连接外部世界的钥匙,允许代理与外部系统交互并访问其训练数据之外的知识。扩展为代理和外部 API 之间提供了桥梁,使代理能够执行 API 调用并检索实时信息;函数通过分工协作为开发者提供了更精细的控制,允许代理生成函数参数并在客户端执行;数据存储为代理提供了访问结构化或非结构化数据的能力,支持基于数据的应用。

代理的未来充满令人兴奋的进步,我们目前只是触及了可能性的表面。随着工具变得更加复杂和推理能力的增强,代理将能够解决越来越复杂的问题。此外,“代理链式调用”(agent chaining)的战略方法将继续获得关注。通过结合多个擅长特定领域或任务的专业代理,我们可以创建“混合代理专家”的方法,在各个行业和问题领域中实现卓越的结果。

需要记住的是,构建复杂的代理架构需要采用迭代方法。实验和优化是解决特定业务场景和组织需求的关键。由于其架构基础模型的生成特性,没有两个代理是完全相同的。然而,通过利用这些基础组件的优势,我们可以创建具有深远影响的应用程序,扩展语言模型的能力并带来实际价值。

参考文献

  1. Shafran, I., Cao, Y. 等,2022,"ReAct: Synergizing Reasoning and Acting in Language Models"。访问地址:https://arxiv.org/abs/2210.03629
  2. Wei, J., Wang, X. 等,2023,"Chain-of-Thought Prompting Elicits Reasoning in Large Language Models"。访问地址:https://arxiv.org/pdf/2201.11903.pdf
  3. Wang, X. 等,2022,"Self-Consistency Improves Chain of Thought Reasoning in Language Models"。访问地址:https://arxiv.org/abs/2203.11171
  4. Diao, S. 等,2023,"Active Prompting with Chain-of-Thought for Large Language Models"。访问地址:https://arxiv.org/pdf/2302.12246.pdf
  5. Zhang, H. 等,2023,"Multimodal Chain-of-Thought Reasoning in Language Models"。访问地址:https://arxiv.org/abs/2302.00923
  6. Yao, S. 等,2023,"Tree of Thoughts: Deliberate Problem Solving with Large Language Models"。访问地址:https://arxiv.org/abs/2305.10601
  7. Long, X.,2023,"Large Language Model Guided Tree-of-Thought"。访问地址:https://arxiv.org/abs/2305.08291
  8. Google. "Google Gemini Application"。访问地址:http://gemini.google.com
  9. Swagger. "OpenAPI Specification"。访问地址:https://swagger.io/specification/
  10. Xie, M.,2022,"How does in-context learning work? A framework for understanding the differences from traditional supervised learning"。访问地址:https://ai.stanford.edu/blog/understanding-incontext/
  11. Google Research. "ScaNN (Scalable Nearest Neighbors)"。访问地址:https://github.com/google-research/google-research/tree/master/scann
  12. LangChain. "LangChain"。访问地址:https://python.langchain.com/v0.2/docs/introduction/