【指南】用Gemini从零构建ReAct代理

2024年10月14日 由 alex 发表 98 0

什么是人工智能代理?

人工智能代理是一种旨在感知环境、推理环境并执行行动以实现指定目标的系统。它将复杂的目标分解为易于管理的子任务,为每个子任务选择适当的工具(如搜索引擎、数据库查询、代码执行环境、API 调用和其他代理),并在分析观察结果的同时反复执行这些工具。代理根据中间结果调整策略,完善输入以优化工具的使用,并保持历史背景以避免重复无效的方法。在整个过程中,代理会平衡短期子任务与总体目标之间的关系,有效地汇总和综合结果,从而制定出全面的解决方案。


在这篇文章中,我们将重点介绍 ReAct,将其作为一种方法论,用于创建人工智能代理,使其能够在一组工具的帮助下执行相当复杂的任务。


ReAct 框架: 范式转变

ReAct 框架为人工智能代理引入了一个统一的架构,充分利用了 LLM 功能的最新进展。与将推理和行动分割开来的传统设计不同,ReAct 将这些功能集成到了一个单一、连贯的系统中。这种创新方法允许人工智能代理将思维过程和行动无缝结合起来,从而有可能开发出更高效、适应性更强的人工智能系统。这种模式转变有三个主要特点:

  1. 统一推理和行动: ReAct 代理利用 LLM 作为集中组件,同时对环境进行推理并确定适当的行动。这种统一使代理能够无缝处理观察结果、生成计划和执行行动,而无需单独的人工设计模块。通过整合推理和行动,代理可以更流畅地适应复杂多变的环境。
  2. 动态工具利用: ReAct 代理可以整合各种外部工具和应用程序接口,并根据当前环境和目标选择和使用它们。LLM 通过分析用户的任务和过去的观察结果来确定最适合利用的资源,从而为工具选择提供便利。这种动态集成使代理能够随时扩展其能力,根据需要访问搜索引擎、数据库、代码执行环境和其他实用工具,以实现其目标。
  3. 迭代式问题解决: 该框架使代理能够通过思考、行动和观察的迭代循环来解决复杂的任务。通过这种反馈循环,代理可以评估其行动的结果,根据效果改进策略,并相应地规划后续步骤。迭代过程由用户提供的提示引导,如果需要,还可以包括一些简短的示例,以更好地说明任务。LLM 利用当前和历史观察结果为决策提供信息。记忆组件可追溯交互历史,从而进一步增强了代理的适应能力和随时间推移的学习能力。


通过采用集成方法,ReAct 代理克服了传统架构的局限性,尤其是在需要灵活推理和自适应行为的场景中。在以 LLM 为中心的框架内将推理和行为融合在一起,可实现更复杂、更能感知上下文的问题解决能力。


ReAct 代理的核心是一个 LLM,如本文介绍的 Gemini Pro 1.5。这些模型充当了代理的 “大脑”,能够理解和生成自然语言、进行复杂的推理和决策、维护上下文和利用过去的观察结果,并为工具选择和执行生成结构化输出。


ReAct 框架将代理的思维过程和行动构建为一系列自然语言交互,从而使代理能够充分利用 LLM 的全部功能。这种方法有助于在复杂的问题解决场景中进行导航,使代理能够动态地适应新信息和新挑战,同时保持实现目标的连贯策略。


一、使用Gemini构建 ReAct Agent

既然我们已经奠定了基础,下面就让我们来探索一下使用 Gemini 作为我们所选择的 LLM 来构建 ReAct 代理的过程。


4


ReAct 代理设计概述

ReAct 代理将推理和行动结合在一个连续的循环中,为人工智能带来了一种新的方法。正如我们之前所了解的,传统的人工智能系统将决策与执行分离开来,而 ReAct 代理的设计则是将思考与行动融为一体。它们接收任务、分解任务、利用外部资源收集信息,并根据所学知识进行调整。这使它们非常适合处理需要不断调整并与外部环境互动的动态任务。上图是代理架构的 1000 英尺概览。让我们逐一分解:

  1. 输入: 代理首先接收自然语言任务。该任务进入核心语言模型(LLM),如 Gemini Pro,它将解释需要完成的任务。因此,核心语言模型充当了代理的 “大脑”,启动任务。任务由用户提供。在这里,代理的目标是利用手头可用的工具来解决任务。
  2. 推理: LLM 分析任务并将其分解为多个步骤。它计划采取哪些行动,并根据可用信息和工具决定如何解决问题。
  3. 外部环境行动: 在我们当前的设置中,代理可以访问两个主要环境--谷歌搜索和维基百科。使用通过应用程序接口连接的特定工具,它可以在谷歌上查找最新信息,或从维基百科上收集事实。代理所采取的每项行动都取决于它所确定的任务的最佳来源。通过连接到这些外部环境,代理可以快速找到相关信息或获得更多背景信息。
  4. 观察和记忆: 执行每项行动后,代理都会观察结果,并将相关信息保存在内存中。通过这种追踪,它可以跟踪过去的行动,并以以前的观察结果为基础,从而避免重复或丢失上下文。每一条新信息都会丰富代理对任务的理解,使未来的行动更有依据。
  5. 反馈回路: 代理在推理、行动和观察步骤中不断循环。每次收集到新信息后,它都会回到推理阶段,由 LLM 考虑更新后的知识。这种迭代循环有助于代理改进其方法,并与任务保持一致。推理循环可以根据结束条件或最大迭代次数进行限制。请注意,我们在这里利用的是记忆组件中过去的观察结果。
  6. 响应: 最后,一旦收集到足够多的信息并达成牢固的理解,代理就会根据其收集到的所有信息并经过多次循环改进后生成响应。同样,这可以完全由 LLM 决定,也可以基于一个结束条件,或者我们可能无法在有限的迭代次数内得出一个结果。


通过不断与外部信息源交互、存储观察结果并重新进行推理,ReAct 代理能以比传统模型更灵活、适应性更强的方式解决复杂问题。这种设计使它能够处理实时信息,应对不断变化的场景,并在最少人工干预的情况下产生高质量的结果。


第1步:设置环境

我们可以选择两种搜索环境: 谷歌搜索和维基百科搜索。谷歌搜索通过 SERP API 提供,而维基百科有自己的 API。我们的目标是提供工具--封装 API 调用逻辑的 Python 函数--接受查询(搜索词)并从这些环境中返回结果。


对于谷歌搜索,我们接收排名前 10 位的结果,并提供以下信息:排名、标题、链接和片段。Google 中的片段是出现在搜索结果中的网页的简短摘要或描述。片段旨在帮助用户了解网页内容并决定是否点击。虽然提供的信息极少,但片段往往包含了我们所需的清晰信息。


在维基百科中,我们可以获得标题和摘要--关于我们搜索的主题的一小段话。


这些搜索工具在 src/tools 目录下的 serp.py 和 wiki.py 中实现。


第2步:定义代理结构和支持类

要构建 ReAct 代理,我们需要定义几个协同工作的类和结构。让我们逐一探讨这些组件:


2.1 枚举和自定义类型

首先,我们将定义enum和自定义类型,以便在整个实现过程中使用。enum将映射工具选择,而自定义类型将捕捉每次迭代工具使用过程中的观察结果。ReAct 代理的完整代码位于 src/reactdirectory 下的 agent.py 中。


from enum import Enum, auto
from typing import Union, Callable
class Name(Enum):
    """Enumeration for tool names available to the agent."""
    WIKIPEDIA = auto()
    GOOGLE = auto()
    NONE = auto()
    def __str__(self) -> str:
        return self.name.lower()
Observation = Union[str, Exception]


2.2 消息和选择模型

接下来,我们将为信息和工具选择定义 Pydantic 模型:


from pydantic import BaseModel, Field
class Message(BaseModel):
    role: str = Field(..., description="The role of the message sender.")
    content: str = Field(..., description="The content of the message.")
class Choice(BaseModel):
    name: Name = Field(..., description="The name of the tool chosen.")
    reason: str = Field(..., description="The reason for choosing this tool.")


2.3 工具类

工具类封装了各个工具的功能:


class Tool:
    def __init__(self, name: Name, func: Callable[[str], str]):
            self.name = name
            self.func = func
        def use(self, query: str) -> Observation:
            try:
                return self.func(query)
            except Exception as e:
                logger.error(f"Error executing tool {self.name}: {e}")
                return str(e)


2.4 Agent 类

现在,让我们来了解一下代理类及其方法。该类定义了负责执行查询和处理工具交互的代理。


class Agent:
    def __init__(self, model: GenerativeModel) -> None:
        self.model = model
        self.tools: Dict[Name, Tool] = {}
        self.messages: List[Message] = []
        self.query = ""
        self.max_iterations = 5
        self.current_iteration = 0
        self.template = self.load_template()
    def load_template(self) -> str: ...
    def register(self, name: Name, func: Callable[[str], str]) -> None: ...
    def trace(self, role: str, content: str) -> None: ...
    def get_history(self) -> str: ...
    def think(self) -> None: ...
    def decide(self, response: str) -> None: ...
    def act(self, tool_name: Name, query: str) -> None: ...
    def execute(self, query: str) -> str: ...
    def ask_gemini(self, prompt: str) -> str: ...


这些类和结构共同创建了一个灵活而强大的 ReAct 代理。通过这样组织代码,我们为 ReAct 代理构建了一个模块化、可扩展的框架。这种结构允许轻松添加新工具、修改推理过程,以及与不同的 LLM 或外部服务集成。接下来,我们将深入研究最关键组件的实际实现——思考-行动-观察循环。


第3步:实现 “思考-行动-观察 ”循环

ReAct 代理的核心是其在迭代循环中思考、行动和观察的能力。下面的高级流程图说明了 ReAct 代理如何在这个循环中发挥作用。其核心部分是思考(推理)阶段、行动阶段(调用应用程序接口并通过使用工具访问环境),最后是观察阶段(收集结果)。如此循环往复,使代理不断改进,并朝着最初设定的共同目标前进。


5


think


think方法是 ReAct 代理认知循环的核心。它管理迭代跟踪,利用当前上下文动态构建提示,与Gemini语言模型交互,并记录代理的想法。它根据模型的响应调用决定方法,从而启动下一阶段的推理或行动。


def think(self) -> None:
    self.current_iteration += 1
    if self.current_iteration > self.max_iterations:
        logger.warning("Reached maximum iterations. Stopping.")
        return
    prompt = self.prompt_template.format(
        query=self.query, 
        history=self.get_history(),
        tools=', '.join([str(tool.name) for tool in self.tools.values()])
    )
    response = self.ask_gemini(prompt)
    self.trace("assistant", f"Thought: {response}")
    self.decide(response)


decide


Decidemethod 是 ReAct 代理决策过程中的另一个关键组件,紧随 thinkcomponent 之后。它解析来自语言模型(Gemini)的 JSON 响应,决定是使用特定工具采取行动,还是提供最终答案。如果需要采取行动,它就会调用 actmethod,并选择工具和输入。对于最终答案,它会记录结果。该方法包括错误处理功能,用于处理解析问题或意外的响应格式,如果出现问题,则返回到思维方法。这种方法确保了思考和行动的稳健循环,使代理能够通过在思考和工具使用之间无缝转换来处理复杂的查询,直到获得满意的答案或达到迭代限制。


def decide(self, response: str) -> None:
    try:
        parsed_response = json.loads(response.strip().strip('`').strip())
        if "action" in parsed_response:
            action = parsed_response["action"]
            tool_name = Name[action["name"].upper()]
            self.act(tool_name, action.get("input", self.query))
        elif "answer" in parsed_response:
            self.trace("assistant", f"Final Answer: {parsed_response['answer']}")
        else:
            raise ValueError("Invalid response format")
    except Exception as e:
        logger.error(f"Error processing response: {str(e)}")
        self.think()


Act


act方法完成了 ReAct 代理的认知周期,遵循thinkdecide阶段。它根据Gemini决策执行所选工具,将其应用于当前查询或细化输入。最重要的是,在每次行动后,该方法都会捕获并记录工具的输出,将其作为观察结果,通过跟踪方法将其整合到代理的记忆中。这些观察结果,包括当前的观察结果和过去迭代中积累的观察结果,都会被纳入下一个思考周期,使代理能够根据不断增长的信息库进行推理。


通过这种行动、观察和思考的迭代过程,ReAct 代理可以逐步积累知识,在进行复杂查询时做出越来越明智的决策。通过将每次行动与记录的观察结果联系起来,代理可以全面追踪其解决问题的过程,从而促进有效推理和透明决策。


def act(self, tool_name: Name, query: str) -> None:
    tool = self.tools.get(tool_name)
    if tool:
        result = tool.use(query)
        observation = f"Observation from {tool_name}: {result}"
        self.trace("system", observation)
        self.think()
    else:
        logger.error(f"No tool registered for choice: {tool_name}")
        self.think()


第4步·:制作提示模板

了解推理循环后,制作一个有效的 ReAct 提示模板至关重要。这种结构良好的提示会引导代理的行为和决策过程,成为交互的初始种子。ReAct 提示通常包括四个关键部分:i) 当前查询;ii) 任何先前的推理步骤和观察结果;iii) 可用工具;iv) 输出格式说明。这些说明包括工具选择的推理、从过去观察中获得的启示以及结束推理循环的指南。


提示可以是零镜头(不提供示例的说明),也可以是少镜头(包括推理和行动的示例)。在本练习中,我们采用的是零镜头方式。提示的目的是通过精心设计的指令,有效地指导模型采取类似 ReAct 的行为。它构建了代理的思维过程,鼓励代理分解问题、寻找信息并采取适当措施。通过整合这些元素,提示有助于采用结构化方法解决问题,使语言模型能够更有效地完成复杂任务。


我们用于构建 ReAct 代理的提示模板如下所示。请注意,除了括号内的工具名称外,其他所有内容都与流派无关,也与我们在此构建的任何特定内容无关。


You are a ReAct (Reasoning and Acting) agent tasked with answering the following query::
Query: {query}
Your goal is to reason about the query and decide on the best course of action to answer it accurately.
Previous reasoning steps and observations: {history}
Available tools: {tools}
Instructions:
1. Analyze the query, previous reasoning steps, and observations.
2. Decide on the next action: use a tool or provide a final answer.
3. Respond in the following JSON format:
If you need to use a tool:
{{
    "thought": "Your detailed reasoning about what to do next",
    "action": {{
        "name": "Tool name (wikipedia, google, or none)",
        "reason": "Explanation of why you chose this tool",
        "input": "Specific input for the tool, if different from the original query"
    }}
}}
If you have enough information to answer the query:
{{
    "thought": "Your final reasoning process",
    "answer": "Your comprehensive answer to the query"
}}
Remember:
- Be thorough in your reasoning.
- Use tools when you need more information.
- Always base your reasoning on the actual observations from tool use.
- If a tool returns no results or fails, acknowledge this and consider using a different tool or approach.
- Provide a final answer only when you're confident you have sufficient information.
- If you cannot find the necessary information after using available tools, admit that you don't have enough information to answer the query confidently.


二、方法比较: 传统代理与 ReAct 代理


为了了解 ReAct 框架的强大功能和灵活性,我们将其与传统的工具选择和查询处理方法进行了比较,发现了两者的主要区别。传统系统通常依赖于预定义的规则或模式匹配,例如将传记查询路由到维基百科,将位置查询路由到谷歌。而 ReAct 则利用语言模型,根据上下文动态分析和选择工具,具有明显的优势:


传统系统固有的僵化逻辑限制了灵活性。相比之下,ReAct 能流畅地适应各种查询,利用其推理能力做出多步骤决策。这种以上下文为导向的方法使 ReAct 能够保持对话历史记录,跟踪先前的交互,并制定更有效的响应。此外,与需要更新代码才能集成新工具的传统系统不同,ReAct 只需使用工具说明即可无缝集成新功能。它的自然语言理解能力还能增强错误处理能力,提供建设性建议,支持复杂的多步骤问题解决。


在这里,管理器根据前缀路由查询: /people 查询会转到维基百科以获取传记信息,而 /location 查询则会转到谷歌以获取基于位置的搜索。然而,这种结构迫使我们预先格式化查询并编码僵化的规则,从而造成了瓶颈。为了扩展功能,我们必须添加更多规则并修改代码--ReAct 通过动态解释上下文毫不费力地解决了这一限制。


class Manager:
    """
    Manages tool registration, selection, and execution.
    """
    def __init__(self) -> None:
        self.tools: Dict[Name, Tool] = {} 
    
    def register(self, name: Name, func: Callable[[str], str]) -> None:
        """
        Register a new tool.
        """
        self.tools[name] = Tool(name, func)
    
    def act(self, name: Name, query: str) -> Observation:
        """
        Retrieve and use a registered tool to process the given query.
        Parameters:
            name (Name): The name of the tool to use.
            query (str): The input query string.
        Returns:
            Observation: The result of the tool's execution or an error.
        """
        if name not in self.tools:
            raise ValueError(f"Tool {name} not registered")
        
        processed_query = query.split(' ', 1)[1] if ' ' in query else query
        return self.tools[name].use(processed_query)
    
    def choose(self, query: str) -> Choice:
        """
        Choose the appropriate tool based on the query prefix.
        """
        if query.startswith("/people"):
            return Choice(
                name=Name.WIKIPEDIA, 
                reason="Query starts with /people, using Wikipedia for biographical information."
            )
        elif query.startswith("/location"):
            return Choice(
                name=Name.GOOGLE, 
                reason="Query starts with /location, using Google for location-specific information."
            )
        else:
            raise ValueError("Unsupported query. Use /people or /location prefix.")


三、ReAct应用: 分析输出跟踪

为了更好地理解 ReAct 代理是如何运行的,让我们来看看真实交互的一些输出跟踪。我们将查看三个具有不同复杂性的查询示例。


示例 1:简化的三步查询

让我们从第一个示例开始,它展示了代理在回答一个需要当前世界知识的简单问题时的思维过程。代理使用提供的工具从环境中检索最新信息。问题是 克里斯蒂亚诺-罗纳尔多和梅西谁更年长?


想象一下人类是如何利用谷歌和维基百科来解决这个问题的。我们要么去维基百科获取两位球员的出生日期并进行比较,要么用谷歌做同样的事情。不过,我们必须把问题分成两个部分,每个部分针对一个球员,然后对两个部分进行相同类型的搜索,最后通过比较得出答案。让我们看看 ReAct 代理是如何模拟这一过程的。


如下所示,我们的 ReAct 代理通过三次迭代正确地解决了这个问题,让我们一窥它的思维过程。


迭代 1:初步分析和工具选择


Thought: {: {
 "thought": "The query asks for a comparison of age between Cristiano Ronaldo and Lionel Messi. To determine who is older, I need to find their birth dates and compare.",
 "action": {
  "name": "wikipedia",
  "reason": "Wikipedia is a reliable source for biographical information, including birth dates of famous individuals.",
  "input": "Cristiano Ronaldo"
 }
}
Action: Using wikipedia tool
Observation: [Wikipedia summary for Cristiano Ronaldo, including his birth date: February 5, 1985]


在第一次迭代中,代理:

  1. 分析查询并确定需要出生日期。
  2. 选择维基百科工具作为该信息的可靠来源。
  3. 决定先查找克里斯蒂亚诺-罗纳尔多。


迭代 2:收集其他信息


Thought: {: {
 "thought": "I have found Cristiano Ronaldo's birth date from Wikipedia: February 5, 1985. Now I need to find Lionel Messi's birth date to compare and determine who is older.",
 "action": {
  "name": "wikipedia",
  "reason": "Wikipedia is a reliable source for biographical information, and I need to find Lionel Messi's birth date to compare with Cristiano Ronaldo's.",
  "input": "Lionel Messi"
 }
}
Action: Using wikipedia tool
Observation: [Wikipedia summary for Lionel Messi, including his birth date: June 24, 1987]


在第二次迭代中,代理:

  1. 确认收集到的有关罗纳尔多的信息。
  2. 确定需要梅西的出生日期来完成对比。
  3. 再次选择维基百科作为适当的工具。


迭代 3:最终分析和答案


Thought: {: {
 "thought": "I now have Cristiano Ronaldo's birth date (February 5, 1985) and Lionel Messi's birth date (June 24, 1987) from Wikipedia. I can compare these dates to determine who is older.",
 "answer": "Cristiano Ronaldo is older than Lionel Messi. Ronaldo was born on February 5, 1985, while Messi was born on June 24, 1987."
}
Final Answer: Cristiano Ronaldo is older than Lionel Messi. Ronaldo was born on February 5, 1985, while Messi was born on June 24, 1987.


在最后一次迭代中,代理:

  1. 综合从两个维基百科搜索中收集到的信息。
  2. 比较出生日期,确定谁的年龄更大。
  3. 为原始查询提供简洁明了的答案。


这条跟踪记录展示了 ReAct 框架的几个关键方面:代理通过将问题分解成多个步骤来使用迭代推理,在做出最终决定之前收集必要的信息;它有效地利用工具(如维基百科工具)来收集相关数据;它根据已掌握的信息和仍然需要的信息来调整自己的行动,从而表现出适应性行为;它的决策过程是透明的,每个想法都清晰明了,展示了代理是如何做出决定和得出最终答案的。通过查看这一跟踪,我们可以看到 ReAct 代理是如何将推理和行动结合起来,以结构化、透明和有效的方式解决查询问题的。


示例 2:多步骤研究查询

现在,让我们用一个更具挑战性的查询来测试它: “现任国际足联世界杯冠军队队长出生国家的首都平均气温是多少?


这个查询包含几个未知数。要解决这个问题,我们需要将其分解为多个子任务,并按顺序解决。更重要的是,我们需要综合使用维基百科和谷歌等工具,并知道在什么时候使用哪种工具。让我们看看我们的代理是如何通过四次迭代来解决这个问题的。


迭代 1: 查询分解


Thought: To answer this query, I need to break it down into smaller steps:: To answer this query, I need to break it down into smaller steps:
1. Find out who the current FIFA World Cup champion team captain is.
2. Find out where they were born.
3. Determine the capital city of that country.
4. Look up the average temperature in that capital city.
Action: Use Wikipedia to search for "2022 FIFA World Cup"


解释:

  • 思考: 代理人将查询分成几个步骤:确定国际足联世界杯冠军队队长、确定他们的出生地、找到该国的首都,然后查询首都的平均气温。
  • 行动: 代理首先使用维基百科搜索 “2022 年国际足联世界杯”,以确定冠军队队长。
  • 观察: 维基百科搜索显示,梅西是赢得 2022 年国际足联世界杯的阿根廷队队长。


迭代 2:查找梅西的出生地


Thought: The Wikipedia summary tells us that Lionel Messi was the captain of the Argentinian team that won the 2022 FIFA World Cup. Now I need to find out where Lionel Messi was born.: The Wikipedia summary tells us that Lionel Messi was the captain of the Argentinian team that won the 2022 FIFA World Cup. Now I need to find out where Lionel Messi was born.
Action: Use Wikipedia to search for "Lionel Messi"


解释:

  • 思考: 在确定梅西是球队队长后,经纪人现在的重点是找到梅西的出生地,以回答下一步的询问。
  • 行动: 它在维基百科上搜索 “莱昂内尔-梅西”。
  • 观察:维基百科上关于梅西的页面证实他出生于阿根廷罗萨里奥。


迭代 3:首都和气温


Thought: The Wikipedia summary on Lionel Messi states that he was born in Rosario, Argentina. To answer the query, I need to find the average temperature in Buenos Aires, the capital of Argentina.: The Wikipedia summary on Lionel Messi states that he was born in Rosario, Argentina. To answer the query, I need to find the average temperature in Buenos Aires, the capital of Argentina.
Action: Use Google to search for "average temperature in Buenos Aires"


解释:

  • 思考: 得知梅西出生在阿根廷的罗萨里奥,特工确定布宜诺斯艾利斯是阿根廷的首都,它需要找到那里的平均气温。
  • 行动: 代理使用谷歌搜索 “布宜诺斯艾利斯的平均气温”。
  • 观察: 谷歌搜索结果提供了布宜诺斯艾利斯的平均气温数据,其中几个来源提到平均气温约为 18°C(64°F)。


迭代 4:最终答案


Thought: I have gathered all the necessary information. Lionel Messi, captain of the 2022 FIFA World Cup winning team, was born in Argentina. The capital of Argentina is Buenos Aires. Google search results show that the average temperature in Buenos Aires varies throughout the year, but several sources mention an average temperature of around 18°C (64°F).: I have gathered all the necessary information. Lionel Messi, captain of the 2022 FIFA World Cup winning team, was born in Argentina. The capital of Argentina is Buenos Aires. Google search results show that the average temperature in Buenos Aires varies throughout the year, but several sources mention an average temperature of around 18°C (64°F).
Answer: The average temperature in the capital city of the country where the current FIFA World Cup champion's team captain was born is approximately 18°C (64°F). This refers to Buenos Aires, Argentina, the birthplace of Lionel Messi.


解释:

  • 思考: 经纪人已经收集了所有必要的信息: 2022年世界杯冠军队队长梅西出生于阿根廷。首都是布宜诺斯艾利斯,平均气温约为 18°C(64°F)。
  • 行动: 它构建了一个回答,总结了这些发现。
  • 观察: 代理提供了一个完整的答案: “本届世界杯冠军队队长出生国家的首都平均气温约为 18°C(64°F),指的是梅西的出生地阿根廷布宜诺斯艾利斯"。


示例 3:比较分析查询

让我们考虑一个需要高级推理和批判性思维的更复杂的查询。这类问题没有明确的答案,而是涉及以分层方式探索多个子问题。我们的目标是对收集到的所有信息进行比较和综合,从而得出一个全面的结论。我们将研究基于 React 的代理在处理这类复杂的多层分析时的表现。


查询: “按 GDP 排名的前 5 个国家的国民菜肴中最常见的配料是什么?


迭代 1:初始查询和分解


{
  "thought": "To answer this query, I need to break it down into steps:\n1. Identify the top 5 countries by GDP.\n2. Determine the national dish of each country.\n3. Identify the ingredients in each national dish.\n4. Analyze the ingredients to find the most common one.\n\nI need to use a tool to get the information for steps 1 and 2.",
  "action": {
    "name": "google",
    "reason": "Google can provide a list of the top 5 countries by GDP and information about their national dishes.",
    "input": "top 5 countries by GDP and their national dishes"
  }
}


解释:

  • 思考: 代理人将查询分为四个逻辑步骤:确定 GDP 排名、发现各国菜肴、列出配料和分析共性。
  • 行动: 它选择 Google 来检索 GDP 高的国家和相应国家菜肴的列表。
  • 观察: 该工具返回了 GDP 高的国家的一般结果,但没有清晰、统一的国家菜肴列表。


迭代 2:完善搜索


{
  "thought": "The Google search results provided lists of countries by GDP and some information about national dishes, but not a clear and consistent pairing of the two for the top 5 countries. I need to refine my search to get more specific information.",
  "action": {
    "name": "google",
    "reason": "Google is still the best tool to find this information, but I need to be more specific in my query.",
    "input": "National dish of each of the top 5 countries by GDP"
  }
}


解释:

  • 思考: 代理注意到之前的搜索没有得到一个连贯的列表,决定改进查询,明确要求 “按国内生产总值排名前 5 位的国家的国家菜”。
  • 行动: 代理再次用这个改进后的搜索结果查询谷歌。
  • 观察: 谷歌仍然提供了有关各国菜肴和 GDP 排名的一般结果,但缺乏将每个国家与该国菜肴进行结构化配对的功能。


迭代 3:改用维基百科进行具体搜索


{
  "thought": "The Google search results still don't offer a clear and readily available list of national dishes for the top 5 economies. Wikipedia might have more detailed information about national dishes for each country. I can search for each country individually on Wikipedia and look for information about their national dish.",
  "action": {
    "name": "wikipedia",
    "reason": "Wikipedia is a more comprehensive source for information about specific topics like national dishes.",
    "input": "United States national dish"
  }
}


解释:

  • 思考: 代理人意识到谷歌的搜索结果不够具体,因此认为维基百科是一个更好的来源,可以从美国开始单独搜索每个国家的国菜。
  • 行动: 它在维基百科上查询 “美国国菜”。
  • 观察: 维基百科上并没有明确的美国 “国菜”,这说明这种方法可能无法在各国普遍推行。


迭代 4:将范围扩大到流行菜肴


{
  "thought": "The Wikipedia search for \"United States national dish\" returned no results. This suggests that a direct search for a single national dish for each country might not be the most effective approach. National dishes can be subjective and vary by region or cultural group. Instead of searching for a single dish, I should broaden my search to include popular or common dishes in each country. I can then analyze the ingredients of these dishes to identify any commonalities.",
  "action": {
    "name": "google",
    "reason": "Google is a better tool for broader searches and can provide a wider range of information about popular dishes in each country.",
    "input": "Popular dishes in the United States"
  }
}


解释:

  • 思考: 注意到维基百科对各国菜肴的限制,代理人扩大了搜索范围,改为搜索 “流行菜肴”。这种更广泛的方法应该能获得足够多的常见食品数据,即使单一的国家菜肴难以找到。
  • 行动: 它切换回 Google,使用更广泛的查询 “美国的流行菜肴”。
  • 观察: 谷歌成功地提供了一份美国流行菜肴列表,使代理能够编制一份有用的著名食物列表。


迭代 5:继续搜索各国的流行菜肴


{
  "thought": "The Google search provided a list of popular dishes in the United States. I can now repeat this process for the remaining top 4 countries by GDP (China, Japan, Germany, and India) to gather information about their popular dishes. Once I have a list of popular dishes for each country, I can then research common ingredients in those dishes to determine the most common one.",
  "action": {
    "name": "google",
    "reason": "Google is effective for finding lists of popular dishes in specific countries.",
    "input": "Popular dishes in China"
  }
}


解释:

  • 思考: 对美国的搜索结果感到满意后,代理人决定在中国、日本、德国和印度重复这一搜索过程,以收集各国的流行菜肴。
  • 行动: 它在谷歌上查询 “中国流行菜肴”,以收集中国的类似数据。
  • 观察: 谷歌提供了中国流行菜肴的相关结果,因此代理可以放心地对其余每个国家采用这种方法。


迭代 6:为日本收集菜肴


{
  "thought": "I've successfully found lists of popular dishes for the United States and China. I need to continue this process for Japan, Germany, and India to get a comprehensive list of popular dishes for all five countries. Once I have those lists, I can research common ingredients and identify the most frequent one.",
  "action": {
    "name": "google",
    "reason": "Google is effective for finding lists of popular dishes in specific countries.",
    "input": "Popular dishes in Japan"
  }
}


解释:

  • 思考: 有了美国和中国的数据后,特工转到日本,继续计划为每个 GDP 排名前五的国家汇编受欢迎的菜肴。
  • 行动: 它使用谷歌搜索 “日本的流行菜肴”。
  • 观察: 谷歌成功提供了日本流行菜肴列表,进一步支持了这种按国家收集烹饪数据的方法。


迭代 7:研究德国的流行菜肴


{
  "thought": "I now have lists of popular dishes for the United States, China, and Japan. I need to find similar lists for Germany and India to complete my data on the top 5 economies. I will use Google again as it has been effective for this purpose.",
  "action": {
    "name": "google",
    "reason": "Google is effective for finding lists of popular dishes in specific countries.",
    "input": "Popular dishes in Germany"
  }
}


解释:

  • 思考: 有了三个国家的数据后,特工转向德国,继续收集 GDP 排名前五的国家的流行菜肴。
  • 行动: 它在谷歌上查询 “德国流行菜肴”。
  • 观察: 谷歌返回了一份全面的德国菜肴列表,因此代理可以放心地继续执行其计划。


迭代 8:汇编印度的流行菜肴


{
  "thought": "I have successfully gathered lists of popular dishes for the United States, China, Japan, and Germany. I need to find a similar list for India to complete my data on the top 5 economies. I will use Google again as it has been effective for this purpose.",
  "action": {
    "name": "google",
    "reason": "Google is effective for finding lists of popular dishes in specific countries.",
    "input": "Popular dishes in India"
  }
}


解释:

  • 思考: 在完成了四个国家的名单后,印度是最后一个需要完善数据集的国家,即 GDP 排名前五的国家。
  • 行动: 代理用 “印度流行菜肴 ”查询谷歌。
  • 观察: 谷歌成功提供了印度流行菜肴列表,代理的数据收集工作完成。


迭代 9:计划分析成分


{
  "thought": "I have now gathered lists of popular dishes for all five countries: the United States, China, Japan, Germany, and India. My next step is to analyze these lists and identify common ingredients across the dishes. However, this task is too complex to perform without a tool. I need a way to efficiently research the ingredients of multiple dishes across different cuisines. Since there isn't a specific tool for this purpose, I will try using Google to research the ingredients of each dish individually and note any recurring patterns.",
  "action": {
    "name": "google",
    "reason": "While there isn't a perfect tool for this task, Google can be used to research the ingredients of each dish individually. I will manually analyze the results for commonalities.",
  "input": "Ingredients in [Dish Name]"
 }
}


解释:

  • 思考: 在确定了每个国家的流行菜肴后,该代理现在计划分析这些菜肴的配料,以找出共同点。由于认识到这一分析很复杂,特工决定逐一查找每道菜。
  • 行动: 该代理开始在 Google 上进行一系列搜索,重点是确定每道特定菜肴的配料。
  • 观察: 当谷歌搜索到每道菜的配料时,代理开始手动比较和分析这些配料,以找出任何共同的模式。


迭代 10:认识复杂性,关注共同主题


{
  "thought": "The last Google search returned a list of recipe websites and ingredient resources, but not specific ingredient lists for popular dishes. It seems that finding a single most common ingredient across such diverse cuisines will be difficult. Many ingredients like salt, sugar, and cooking oil are likely used in almost every dish but are not very insightful. Additionally, directly comparing ingredient frequency across vastly different cooking styles and cultural preferences might not be the best way to approach the query. \n\nInstead of focusing on a single ingredient, I can aim for a more general answer about common ingredient types or flavor profiles. To do this, I will research common ingredients and cooking styles for each country's cuisine and look for overarching themes or similarities.",
  "action": {
    "name": "google",
    "reason": "Google can provide information about common ingredients and cooking styles for each country's cuisine.",
    "input": "Common ingredients in [Country] cuisine" 
 }
}


解释:

  • 思考: 代理商意识到,由于烹饪风格和配料使用的多样性,确定单一的共同配料是不切实际的。取而代之的是,它将重点转移到确定这些菜系中常见的广泛配料类型或风味特征上。
  • 行动: 调整查询,收集各国常见配料和烹饪风格的一般数据。
  • 观察: 谷歌提供了有关一般烹饪概况和常见配料的信息,使代理能够总结总体主题,而不是查找单一配料。


结论

ReAct 框架为增强基于代理的系统的功能和适应性提供了多种可能性。未来的发展重点是整合处理图像、音频和视频等不同数据类型的能力,使代理能够解释更广泛的信息,从而做出更丰富的情境感知决策。将代理组织成分层的层次结构,其中主代理将专门任务委托给子代理,这将提高效率并细分任务。此外,通过共享观察结果、工具和资源,增强代理的协作能力,可以在复杂、多视角的环境中提高洞察力,支持凝聚力决策。高层代理可以动态地指导和协调其他代理,在多代理设置中协调行动,从而高效地处理复杂的多步骤任务。


文章来源:https://medium.com/google-cloud/building-react-agents-from-scratch-a-hands-on-guide-using-gemini-ffe4621d90ae
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
写评论取消
回复取消