在之前的文章中,我们已经领略了AI Agent 使用 ReAct 模式的强大之处。简单回顾一下,它以“思考(Thought)→行动(Action)→观察(Observation)”的循环工作,直到 Agent 得出一个最终的答案,这个答案可能是正确的。虽然 ReAct Agent 功能强大,并且提供了强大的逐步推理能力,但它们更适合于简单的、基于工具的任务;在处理复杂的、多步骤的任务时,它们存在一些局限性。本文将深入探讨如何利用 LangChain 中的 Plan and Execute 模式来解决这些问题,特别是在处理复杂的多步骤任务时,Plan and Execute Agent 如何展现出其优越性。
ReAct Agent 的局限性:短视、高成本与缺乏重规划能力
ReAct Agent 虽然在简单的任务中表现出色,但在处理复杂任务时,其局限性逐渐显现。主要表现在以下几个方面:
- 短视思维(Short-term thinking):ReAct Agent 无法全面了解需求的整体情况,而是每次只决定一步。这意味着如果任务变得复杂或耗时,可能会导致不准确。
- 过多的 LLM 调用(Too many LLM calls):每次行动后,Agent 都需要暂停并再次思考,这导致对主 LLM 的调用次数增加,从而导致执行速度变慢和成本增加。
- 重规划困难(Re-planning):如果在执行过程中出现错误,ReAct Agent 没有完整计划的记忆。它们无法智能地恢复或中途调整策略。
让我们考虑以下示例:
“预订一张从纽约到巴黎的机票,预订一家酒店,并安排机场接送——所有这些都在特定的预算和特定的日期内完成。每个步骤都取决于前一个步骤的结果(例如,航班时间会影响酒店入住时间和接送时间)。”
ReAct Agent 在这里可能会遇到困难,因为该任务涉及多个相关的步骤。如果第一次航班搜索失败会发生什么?Agent 可能会陷入推理循环,反复尝试寻找航班,而不考虑其他策略或调整总体计划。更糟糕的是,如果机票已预订,但后来发生了变化(例如,由于可用性或时间安排),Agent 需要回溯并更新所有相关的任务,例如酒店入住和机场接送。
为了处理所有这些回退场景,我们可能需要将每种可能性预先添加到提示中,这既困难又混乱。这正是 Plan and Execute 模式旨在解决的问题。
Plan and Execute Agent:结构化复杂任务的利器
Plan and Execute Agent 通过将复杂任务分解为更小的子任务,并预先计划每个步骤,从而克服了上述 ReAct Agent 的局限性。这种方法的核心在于将任务分解为两个不同的阶段:规划(Plan) 和 执行(Execute)。
-
规划阶段(Plan):在此阶段,Agent 首先接收到复杂的查询,然后利用大型语言模型(LLM)将该查询分解为一系列有序的子任务。这个过程类似于创建一个待办事项清单,其中每个子任务都是实现最终目标的必要步骤。例如,对于上述预订旅行的例子,规划阶段可能会生成以下步骤:
- 搜索7月20日从纽约到巴黎的预算低于700美元的航班。
- 找到合适的航班后,预订7月20日的航班。
- 搜索巴黎7月20日晚上的酒店,与航班到达时间相符。
- 预订一家在预订航班后剩余预算范围内的酒店。
- 研究并预订巴黎7月20日到达的机场接送服务。
- 确保机票、酒店和机场接送的总费用不超过1200美元的总预算。
- 确认行程的所有预订和安排。
-
执行阶段(Execute):在规划阶段完成之后,Agent 进入执行阶段,逐步执行每个子任务。值得注意的是,执行阶段并不是简单的循环,而是由一个运行完整的 ReAct 模式的 Agent 来执行每个步骤。这意味着每个子任务都经过“思考→行动→观察”的循环,从而确保任务的准确性和可靠性。此外,为了降低成本和延迟,可以使用较小的、特定于任务的 LLM 来执行每个子任务,从而优化资源利用率。
因此,我们可以将 Plan and Execute 模式概括为:Plan-and-Execute = Planner + ReAct-based Executor。
LangChain 实现:代码示例与解析
以下是 LangChain 中 Plan and Execute Agent 的实现示例:
from langchain_experimental.plan_and_execute import (
PlanAndExecute,
load_chat_planner,
load_agent_executor,
)
planner = load_chat_planner(llm)
executor = load_agent_executor(llm=llm, tools=tools, verbose=True)
agent = PlanAndExecute(planner=planner, executor=executor, verbose=True)
task = (
"Book a flight from New York to Paris on July 20 for under $700, "
"then book a hotel that matches the flight arrival, "
"and finally arrange an airport pickup. "
"Total budget is $1200."
)
result = agent.invoke({"input": task})
代码解释:
- 导入必要的模块:首先,我们从
langchain_experimental.plan_and_execute
模块中导入PlanAndExecute
、load_chat_planner
和load_agent_executor
等类。 - 加载规划器(Planner):
load_chat_planner(llm)
函数用于加载规划器。规划器的作用是将复杂任务分解为一系列子任务。llm
变量代表大型语言模型,它负责理解任务并生成计划。 - 加载执行器(Executor):
load_agent_executor(llm=llm, tools=tools, verbose=True)
函数用于加载执行器。执行器负责执行规划器生成的每个子任务。llm
变量再次代表大型语言模型,tools
变量代表 Agent 可以使用的工具,verbose=True
表示输出详细的执行过程。 - 创建 PlanAndExecute Agent:
PlanAndExecute(planner=planner, executor=executor, verbose=True)
创建一个 Plan and Execute Agent 实例,并将规划器和执行器传递给它。 - 定义任务:
task
变量定义了要执行的复杂任务,即预订机票、酒店和机场接送。 - 执行任务:
agent.invoke({"input": task})
调用 Agent 的invoke
方法来执行任务。{"input": task}
将任务作为输入传递给 Agent。
运行这段代码后,你可以观察到 Agent 如何逐步执行每个子任务,最终完成整个旅行预订。你会看到类似下面的输出:
> Entering new PlanAndExecute chain…
steps=[Step(value='Search for flights from New York to Paris on July 20 within the budget of $700.'), Step(value='Once a suitable flight is found, book the flight for July 20.'), Step(value='Search for hotels in Paris for the night of July 20 that match the flight arrival time.'), Step(value='Book a hotel that fits within the remaining budget after booking the flight.'), Step(value='Research and book an airport pickup service in Paris for the arrival on July 20.'), Step(value='Ensure that the total cost of the flight, hotel, and airport pickup does not exceed the total budget of $1200.'), Step(value="Confirm all bookings and arrangements for the trip.\nGiven the above steps taken, please respond to the user's original question. \n")]
这段输出展示了 Agent 规划的步骤,执行器将会按照这些步骤逐一执行。
优势与应用场景:超越 ReAct Agent
与传统的 ReAct Agent 相比,Plan and Execute Agent 具有以下显著优势:
- 更好的可控性:通过预先规划任务步骤,Plan and Execute Agent 能够更好地控制任务的执行流程,避免 ReAct Agent 可能出现的盲目探索和错误。
- 更高的效率:通过将任务分解为更小的子任务,并使用特定于任务的 LLM 来执行每个子任务,Plan and Execute Agent 可以更有效地利用资源,降低成本和延迟。
- 更强的鲁棒性:当任务执行过程中出现错误时,Plan and Execute Agent 可以根据预先制定的计划进行调整和恢复,从而提高任务的成功率。
Plan and Execute Agent 适用于各种复杂的、多步骤的任务,例如:
- 旅行规划:如上述示例所示,Plan and Execute Agent 可以用于预订机票、酒店和机场接送,从而为用户提供一站式旅行规划服务。
- 数据分析:Plan and Execute Agent 可以用于从多个数据源收集数据,进行数据清洗、转换和分析,最终生成报告。例如,可以创建一个 Agent,首先从不同的在线数据库(例如,财务报表、社交媒体趋势、行业新闻)收集特定公司的信息。然后,Agent 将计划对这些数据进行清洗和标准化,以确保一致性和准确性。下一步可能涉及使用数据分析工具来识别关键趋势和模式,例如收入增长、客户情绪变化或竞争对手活动。最后,Agent 将计划根据这些分析生成一份全面的报告,重点介绍关键发现和见解。
- 软件开发:Plan and Execute Agent 可以用于自动化软件开发过程中的各个环节,例如需求分析、代码生成、测试和部署。设想一个 Agent 被赋予开发一个简单的Web应用程序的任务。首先,Agent 会计划分析任务需求,确定所需的功能和技术堆栈。然后,Agent 会计划生成必要的代码结构,例如HTML、CSS和JavaScript文件,并可能使用代码生成工具来加速此过程。接下来,Agent 将计划实施应用程序的功能,例如用户身份验证、数据处理和用户界面交互。在此之后,Agent 将计划进行测试以确保代码的质量和功能的正确性。最后,Agent 将计划把应用程序部署到服务器,使其可以公开访问。
安全考量与最佳实践
尽管 Plan and Execute Agent 具有诸多优点,但在实际应用中,我们也需要关注一些安全问题和最佳实践:
- 输入验证:确保对用户输入进行严格的验证,防止恶意输入导致 Agent 执行不安全的操作。
- 权限控制:限制 Agent 可以访问的资源和工具,防止 Agent 滥用权限。
- 监控与审计:对 Agent 的执行过程进行监控和审计,及时发现和处理潜在的安全风险。
- 规划验证:在执行之前,对规划的步骤进行验证,确保计划的合理性和安全性。
例如,在使用 Agent 访问数据库时,应该使用最小权限原则,只授予 Agent 执行必要操作的权限,避免 Agent 意外或恶意地修改或删除数据。此外,应该定期审查 Agent 的访问日志,以便及时发现异常行为。
结论
Plan and Execute Agent 通过将复杂任务分解为更小的子任务,并预先计划每个步骤,从而克服了传统 ReAct Agent 的局限性。这种方法提高了任务的可控性、效率和鲁棒性,使其成为处理复杂任务的有力工具。随着大型语言模型技术的不断发展,Plan and Execute Agent 将在更多领域发挥重要作用,为我们带来更智能、更高效的自动化解决方案。相对于 ReAct Agent 常常在没有预定义策略的情况下进行推理和行动,Plan and Execute Agent 基于初始输入大纲出一个计划,然后孤立地执行每个步骤,从而为复杂任务带来可靠的结果。掌握 Plan and Execute 模式,无疑将为你在 Agent 开发领域打开新的大门。