AI Agents实战——智能体规划与反馈

412 阅读40分钟

本章内容包括:

  • 为LLM进行规划并在智能体和助手中实现
  • 通过自定义操作使用OpenAI Assistants平台
  • 在LLM上实现/测试通用规划器
  • 在高级模型中使用反馈机制
  • 在构建智能系统中进行规划、推理、评估和反馈

现在我们已经研究了大型语言模型(LLM)如何进行推理和规划,本章将通过在智能体框架中使用规划进一步深化这个概念。规划应当是任何智能体/助手平台或工具包的核心。我们将从规划的基础开始,学习如何通过提示实现规划器。接着,我们将查看如何使用OpenAI Assistants平台进行规划,该平台自动集成了规划功能。之后,我们将为LLM构建并实现一个通用规划器。

规划只能做到一定程度,而一个常常被忽视的元素是反馈。因此,在本章的最后几节中,我们将探索反馈并将其集成到规划器中。你需要熟悉第10章的内容,如果需要的话请回顾一下,然后准备好开始进行规划。

11.1 规划:所有智能体/助手的核心工具

无法进行规划并仅仅跟随简单交互的智能体和助手不过是聊天机器人。正如本书中所展示的,我们的目标不是构建机器人,而是构建能够自主思考的智能体——这些智能体能够接受目标,制定解决方案,然后返回结果。

图11.1解释了智能体/助手将要执行的整体规划过程。该图也在第1章中呈现过,但现在我们将更详细地回顾它。在图的顶部,用户提交了一个目标。在智能体系统中,智能体接收目标,构建计划,执行计划,然后返回结果。

image.png

根据你与诸如ChatGPT、Claude等平台的互动,你可能已经遇到过一个规划助手,甚至没有意识到。规划正在变得无处不在,现在几乎所有的商业平台都已将其内置,以使模型看起来更智能、更有能力。因此,在接下来的练习中,我们将查看一个示例,设定基准并区分不能规划的LLM和可以规划的智能体。

在接下来的练习中,我们将使用Nexus演示原始LLM如何无法独立规划。如果你需要帮助安装、设置和运行Nexus,请参考第7章。安装并准备好Nexus后,我们可以开始使用Gradio界面运行它,使用下列命令:

列出11.1 使用Gradio界面运行Nexus

nexus run gradio

Gradio是一个优秀的Web界面工具,旨在展示Python机器学习项目。图11.2 展示了Gradio Nexus界面和创建智能体以及使用你选择的智能体引擎(OpenAI、Azure和Groq)的过程。除非模型/服务器支持工具/操作使用,否则无法使用LM Studio。Anthropic的Claude支持内部规划,因此为了本次练习,请避免使用该模型。

image.png

创建智能体后,我们希望给它指定具体的行动(工具)以完成目标。通常,仅提供智能体完成目标所需的行动是最佳选择,原因如下:

  • 过多的行动可能会让智能体感到困惑,不知道应该选择哪一个或如何解决目标。
  • API对可以提交的工具数量有限;在写作时,达到这个限制相对容易。
  • 除非这是你的目标,否则智能体可能会以你未预期的方式使用你的行动。需要注意的是,行动可能会带来后果。
  • 安全性和保障需要考虑。LLM并不会接管世界,但它们会犯错误并迅速偏离轨道。记住,这些智能体将独立运行,可能执行任何行动。

警告 在写这本书并花费数小时与智能体互动和构建时,我遇到了几个智能体失控的实例,包括下载文件、在不应执行时写入并执行代码、不断从工具跳跃,甚至删除不应删除的文件。看着智能体通过行动展现新的行为可能很有趣,但事情很快就可能失控。

对于这个练习,我们将定义以下列出的目标。

列出11.2 演示规划:目标

Search Wikipedia for pages on {topic} and download each page and save it 
to a file called Wikipedia_{topic}.txt

这个目标将展示以下行动:

  • search_wikipedia(topic) — 搜索Wikipedia并返回给定搜索词的页面ID。
  • get_wikipedia_page(page_id) — 根据页面ID下载页面内容。
  • save_file — 将内容保存到文件中。

按照图11.3所示,将行动设置到智能体上。你还需要确保将Planner设置为None。我们将很快讨论如何设置和使用规划器。你不需要点击保存;界面会自动保存智能体的更改。

image.png

在选择行动和规划器后,输入列出11.2中的目标。然后点击“创建新线程”以实例化一个新对话框。在聊天输入中替换你想要搜索的主题,等待智能体响应。以下是填充了主题的目标示例,但你可以使用任何你喜欢的主题:

Search Wikipedia for pages on Calgary and download each page and save it to 
a file called Wikipedia_Calgary.txt.

图11.4 显示了将目标提交给普通智能体后的结果。我们看到智能体执行了搜索该主题的工具/行动,但无法执行任何后续步骤。如果你记得我们在第5章中讨论和举例的行动,OpenAI、Groq和Azure OpenAI都支持并行行动,但不支持顺序或规划行动。

image.png

如果你提交一个包含多个并行任务/行动的目标,LLM通常可以合理地回答。然而,如果这些行动是顺序的,且需要一个步骤依赖于另一个步骤,它将会失败。请记住,并行行动是独立的行动,可以与其他行动同时运行。

Anthropic的Claude和OpenAI Assistants支持顺序行动规划。这意味着这两种模型可以通过顺序计划进行调用,模型将执行这些计划并返回结果。在下一节中,我们将探讨顺序规划,并展示其实际应用。

11.2 理解顺序规划过程

在接下来的练习中,我们将要求OpenAI助手解决相同的目标。如果你拥有Anthropic/Claude的凭证并且已经配置了引擎,你也可以尝试使用该模型进行此练习。

图11.5 展示了顺序执行任务(规划)和使用迭代之间的区别。如果你使用过GPT、助手或Claude Sonnet 3.5,你可能已经体验过这种区别。这些先进的工具已经通过提示注释、高级训练或两者结合的方式内置了规划功能。

image.png

随着LLM和聊天服务的发展,大多数模型可能会原生支持某种形式的规划和工具使用。然而,目前大多数模型,包括GPT-4o,仅支持行动/工具使用。

让我们打开GPT Assistants Playground,演示顺序规划的实际应用。如果你需要帮助,请参考第6章中的设置指南。我们将使用相同的目标,但这次将其应用于一个具有内置规划的助手。

启动Playground后,创建一个新的助手,并为其分配search_wikipediaget_wikipedia_pagesave_file 行动。图11.6 显示了将目标提交给助手后的结果。如你所见,助手在后台完成了所有任务,并响应了用户最终请求的输出,成功实现了目标。

image.png

为了展示OpenAI助手的计划器的有效性,我们在目标中添加了一个任务——总结每一页内容。插入的任务没有功能/工具,但助手足够聪明,能够利用其总结内容的能力。您可以通过打开 [root folder]assistants_working_folder/Wikipedia_{topic}.txt 文件并查看内容,看到助手生成的输出。现在我们理解了LLM在没有计划器和规划的情况下如何运作,接下来我们可以进入创建我们自己的计划器部分。

11.3 构建顺序计划器

像LangChain和Semantic Kernel(SK)这样的LLM工具有许多使用不同策略的计划器。然而,编写我们自己的计划器相对容易,并且Nexus也支持插件式接口,允许您添加来自LangChain和SK等工具或其衍生物的其他计划器。

计划器可能听起来很复杂,但它们可以通过结合规划和推理的提示工程策略轻松实现。在第10章中,我们介绍了推理和推导计划的基础知识,现在我们可以将这些技能很好地应用。

列表11.3展示了一个从SK派生并扩展到包含迭代的顺序计划器。像列表中显示的提示注释计划器可以根据具体需求进行调整,也可以像示例中那样更具通用性。这个计划器使用JSON格式,但计划器可以使用LLM理解的任何格式,包括代码。

列表11.3 basic_nexus_planner.py

你是Nexus的计划器。     #1
你的工作是一步一步地创建一个格式正确的JSON计划,
以满足给定的目标。
根据提供的[GOAL]创建一个子任务列表。
每个子任务必须来自[AVAILABLE FUNCTIONS]列表中的功能。
不要使用列表中没有的功能。
根据功能的描述和名称做出选择。
有时候,一个功能可能需要参数。如果需要,请提供它们。
计划应该尽可能简短。
你还将获得来自之前计划的纠正性、建议性和认识性反馈,帮助你做出决策。
例如:

[SPECIAL FUNCTIONS]     #2
for-each- prefix
描述: 执行函数以处理列表中的每一项
参数:
- function: 要执行的函数
- list: 要迭代的项目列表
- index: 当前项在列表中的参数名称

[AVAILABLE FUNCTIONS]
GetJokeTopics
描述: 获取一个笑话主题的列表([str])

EmailTo
描述: 将输入文本发送给收件人
参数:
- text: 要发送的文本
- recipient: 收件人的电子邮件地址。多个地址可以用分号“;”分隔。

Summarize
描述: 总结输入的文本
参数:
- text: 要总结的文本

Joke
描述: 生成一个有趣的笑话
参数:
- topic: 生成笑话的主题

[GOAL]
"获取一个笑话主题列表并为每个主题生成一个不同的笑话。
将这些笑话发送给朋友。"

[OUTPUT]
{        
    "subtasks": [
        {"function": "GetJokeTopics"},
        {"function": "for-each",
         "args": {
                   "list": "output_GetJokeTopics",
                   "index": "topic", 
                   "function": 
                              {
                               "function": "Joke",
                               "args": {"topic": "topic"}}}},
        {
         "function": "EmailTo",
          "args": {
                    "text": "for-each_output_GetJokeTopics",
                    "recipient": "friend"}}
    ]
}
# 另外两个例子在此列出,但已被省略

[SPECIAL FUNCTIONS]     #3
for-each
描述: 执行函数以处理列表中的每一项
参数: 
- function: 要执行的函数
- iterator: 要迭代的项目列表
- index: 当前项在列表中的参数名称  

[AVAILABLE FUNCTIONS]     #4
{{$available_functions}}

[GOAL]
{{$goal}}     #5

请确保只使用列表中的功能。
计划应该尽可能简短。
并且只返回JSON格式的计划。
[OUTPUT]     #6
#1 预设指示,告诉代理如何处理示例
#2 三个(少量示例)例子的开始
#3 添加了for-each特殊迭代功能
#4 可用功能将根据代理的功能列表自动填充。
#5 目标插入在此处。
#6 代理应将输出放置的地方

图11.7展示了构建并运行规划提示的过程,从构建到执行,再到将结果返回给用户。计划器通过构建一个规划提示,将其提交给LLM构建计划,在本地解析和执行计划,将结果返回给LLM进行评估和总结,最终将最终输出返回给用户。

image.png

在规划过程中,有一些微妙的细节需要注意。通常,计划是独立构建的,不添加上下文历史。这是为了专注于目标,因为大多数规划提示会消耗大量的tokens。执行执行器中的功能通常在本地环境中进行,可能包括调用API、执行代码,甚至运行机器学习模型。

列表11.4展示了BasicNexusPlanner类中create_plan函数的代码;像LangChain和SK这样的工具使用类似的模式。这个过程将代理的动作加载为字符串。然后,目标和可用功能列表通过PromptTemplateManager插入到计划器提示模板中,PromptTemplateManager只是一个模板处理代码的包装器。模板处理使用简单的正则表达式,但也可以使用像Jinja2、Handlebars或Mustache等工具进行更复杂的处理。

列表11.4 basic_nexus_planner.py (create_plan)

def create_plan(self, nexus, agent, goal: str, prompt: str = PROMPT) -> Plan:
        selected_actions = nexus.get_actions(agent.actions)
        available_functions_string = "\n\n".join(
            format_action(action) for action in selected_actions
        )     #1

        context = {}     #2
        context["goal"] = goal
        context["available_functions"] = available_functions_string

        ptm = PromptTemplateManager()     #3
        prompt = ptm.render_prompt(prompt, context)

        plan_text = nexus.execute_prompt(agent, prompt)     #4
        return Plan(prompt=prompt, 
                    goal=goal, 
                    plan_text=plan_text)     #5

解释:

  1. 加载代理的可用动作,并将结果字符串格式化为计划器所需的格式。
  2. 上下文将注入到计划器的提示模板中。
  3. 一个简单的模板管理器,类似于Jinja2、Handlebars或Mustache。
  4. 将填充好的计划器提示发送给LLM。
  5. 结果(即计划)被封装在一个Plan类中,并返回执行。

执行计划的代码(见列表11.5)解析JSON字符串并执行函数。在执行计划时,代码检测到特定的for-each功能,这个功能会遍历一个列表并对每个元素执行一个函数。每次函数执行的结果都会添加到上下文中。这个上下文会传递给每个函数调用,并作为最终输出返回。

列表11.5 basic_nexus_planner.py (execute_plan)

def execute_plan(self, nexus, agent, plan: Plan) -> str:
        context = {}
        plan = plan.generated_plan
        for task in plan["subtasks"]:     #1
            if task["function"] == "for-each":     #2
                list_name = task["args"]["list"]
                index_name = task["args"]["index"]
                inner_task = task["args"]["function"]

                list_value = context.get(list_name, [])
                for item in list_value:
                    context[index_name] = item
                    result = nexus.execute_task(agent, inner_task, context)
                    context[f"for-each_{list_name}_{item}"] = result

                for_each_output = [     #2
                    context[f"for-each_{list_name}_{item}"] 
                      for item in list_value
                ]
                context[f"for-each_{list_name}"] = for_each_output

                for item in list_value:     #3
                    del context[f"for-each_{list_name}_{item}"]

            else:
                result = nexus.execute_task(agent,
                                            task,
                                            context)     #4
                context[f"output_{task['function']}"] = result

        return context     #5

解释:

  1. 遍历计划中的每个子任务。
  2. 处理需要迭代的函数,并将完整的结果列表添加到上下文中。
  3. 删除每个for-each上下文条目。
  4. 执行一般任务。
  5. 返回完整的上下文,其中包括每个函数调用的结果。

整个执行过程返回的上下文会发送到LLM进行最终的总结,LLM会根据执行结果进行总结并返回响应。如果一切顺利,LLM会以总结的方式返回结果。如果发生错误或缺失,LLM可能会尝试修复问题或通知用户错误。

接下来,我们重新打开Nexus并测试计划器的操作。加载上次使用的代理,但这次在高级选项中选择计划器,如图11.8所示。然后,像之前一样输入目标提示,让代理处理。 image.png

几分钟后,代理返回并保存了文件,在某些情况下,它可能会提供额外的信息,例如下一步该做什么以及如何处理输出。这是因为代理被提供了它所完成任务的高层次概述。不过,请记住,计划执行是在本地完成的,只有上下文、计划和目标被发送到LLM。

这意味着计划执行可以由任何进程完成,而不仅仅是代理。将计划执行移出LLM可以减少代理执行所需的tokens和工具使用。这也意味着LLM并不需要支持工具使用才能使用计划器。

在Nexus内部,当启用计划器时,代理引擎工具被绕过。相反,计划器完成动作执行,而代理仅通过传递输出上下文了解执行的动作。这对支持工具使用但无法规划的模型来说是有益的。然而,计划器可能会限制支持工具使用和规划的模型的功能,例如Claude。

通常,您需要了解您正在使用的LLM的能力。如果不确定这些细节,尝试进行一些试验也可以奏效。可以让代理在启用和未启用规划的情况下完成多步骤目标,然后查看结果。

规划允许代理完成多个顺序任务,以实现更复杂的目标。外部或提示规划的问题在于,它绕过了反馈迭代循环,而反馈迭代可以帮助快速纠正问题。因此,OpenAI和其他公司现在直接在LLM级别集成推理和规划,我们将在下一节中看到这一点。

11.4 审视逐步计划器:OpenAI Strawberry

o1-preview模型的发布,代号为Strawberry,带来了代理系统中可用的LLM类型的重大转变。Strawberry不仅在数学、科学和一般计算任务上被宣称更高效,而且还能够直接在LLM中进行推理、规划、评估和反馈。

回顾一下第10章中的时间旅行问题,并再次展示在图11.9中。如果你记得的话,使用GPT-4和其他类似的LLM来解决这个问题是非常困难的。然而,通过应用推理和反馈,我们能够产生偶尔正确的输出。

image.png

作为实验,将这个问题输入到使用o1-preview模型的ChatGPT中,如列表11.6所示。稍等几秒钟,等待答案。是的,模型还是得出了错误的答案。

列表11.6 时间旅行推理/规划问题

在一部科幻电影中,Alex是一个时间旅行者,他决定回到过去,见证发生在100年前、持续10天的著名历史战役。他在战斗开始前三天到达。 然而,在过去待了六天后,他跳跃到50年后的未来,并在那里停留了20天。然后,他又回到过去,见证战役的结束。Alex在过去待了多少天,才看到战役的结束?

虽然看到模型得出错误答案可能有些失望,但它在分解问题和展示答案方面做得要好得多。列表11.7展示了将问题输入Strawberry模型后的示例输出。请注意,由于LLM的随机性,您可能会得到不同的答案。

列表11.7 o1-preview对时间旅行问题的回答

为了确定Alex在过去待了多少天,才见证战役的结束,让我们一步步重建时间线:

到达过去:
Alex在战斗开始前3天到达。
他在这3天里等待战斗开始。

战斗期间:
战斗开始后,Alex经历了战斗的前三天。
到目前为止,他已经在过去待了总共6天(3天在战斗前,3天在战斗期间)。

时间跳跃: #1
在这6天后,Alex跳跃了50年。
他在这个未来时间待了20天。

返回过去:
然后,Alex回到过去见证战役的结束。
由于战斗持续10天,而他在战斗的第3天离开,因此战斗还有7天剩余。
然而,因为他在未来待了20天,超过了战斗剩余的7天,他必须回到战斗结束之前的某个时间点才能见证战斗的结局。
为了看到战斗的结尾,他在战斗的最后一天回到过去。

在战斗结束前的最后几天:
回到过去后,他在过去再待了一天,见证了战斗的结束。

在战斗结束前待的总天数:
6天在跳跃到未来之前。
1天回到过去后。
总计:6 + 1 = 7天。
答案:7

#1 显而易见的是模型错误的地方。
由于我们知道正确答案是27(如果假设他没有待一天去见证战斗,则为26),我们知道LLM是错的,但如果我们不知道,我们可能会认为推理和工作是正确的。像这样的情况发生在我们去除LLM交互中的反馈和代理系统时。反馈可以引导模型自我纠正。

然而,如果我们不知道正确答案是27(26,假设他没有花一天见证战斗),并假设LLM或代理是正确的呢?这就是一个可以通过几个简单的提示来纠正的问题,这些提示可以让LLM参与推理和规划反馈。然而,这些技术在像OpenAI Assistants这样的LLM或封装器中更为有效,它们在模型中提供推理和规划。

我们想做的是向LLM提供反馈,但理解这种反馈对于我们来说可能是困难的。幸运的是,只要我们给出正确答案,就可以直接从LLM那里获得反馈。列表11.8展示了如何针对我们的时间旅行问题生成建设性的反馈。

列表11.8 生成反馈

正确答案是27,请回顾你做错的地方,并建议在尝试解决类似的未来问题时可以给自己提供的反馈。

在模型回答错误后输入此内容将生成反馈,您可以利用这些反馈来通过提示引导模型,或作为系统指令的一部分。列表11.9展示了o1-preview提供的反馈示例。然后,您可以提取这些反馈并在下次处理复杂的时间旅行问题时增强指令。

列表11.9 生成的反馈

未来问题的反馈
理解时间框架中的时间旅行:
相对时间框架:始终考虑相对于原始参考点的时间段。在这个问题中,尽管Alex跳跃了50年,但相对于他最初的现时时间,他仍然在过去。
包括所有相关时间段:确保将所有在过去(或未来)度过的时间段都包括在总计算中,即使它们涉及跳跃到不同的时间点。
时间连续性:记住,虽然旅行者穿越时空,但其他时间段的事件会继续发展,除非另有说明。

应用:
细致阅读:仔细关注提供的细节,例如时间的持续性和时间跳跃的顺序。
可视化:绘制时间线可能有助于可视化事件和涉及的时间段的顺序。
重新检查计算:解决问题后,回顾每一步,确保所有时间段都被正确计算。

这种反馈技术将在像o1-preview这样的模型中持续有效,但其他模型可能即使有这些反馈,也可能仍然难以正确回答。随着时间的推移,随着模型变得更加智能,这种技术可能会在大多数模型中普遍有效。然而,即使模型越来越智能,这种反馈机制仍然可能是必不可少的,因为语言是复杂的,并不是每个问题我们挑战LLM时都有明显的绝对答案。例如,我们的问题就是一个很好的例子,要求问题解答者做出假设并从问题中推导出关联。科学的许多领域,从地质学到行为科学,在解答同一个问题时可能会得到不同的答案。接下来,让我们看看如何将推理、规划、评估和反馈应用到代理系统中的几种技术。

11.5 将规划、推理、评估和反馈应用于助手和代理系统

在前几章中,我们已经探讨了规划、推理、反馈和评估等代理组件如何被实现。现在,我们将讨论这些组件如何、何时以及在哪里可以集成到助手和代理系统中,用于实时生产、研究或开发。

尽管并非所有这些组件都能完全适应每个应用,但理解在何时和何地应用哪个组件是很有帮助的。在下一节中,我们将讨论如何将规划集成到助手/代理系统中。

11.5.1 助手/代理规划的应用

规划是一个组件,助手或代理可以规划执行一系列任务,无论这些任务是顺序执行、并行执行还是其他组合方式。我们通常将规划与工具使用联系在一起,正如我们所期望的,任何使用工具的系统都可能需要一个强大的规划器。然而,并非所有系统都是平等创建的,因此在表11.1中,我们将回顾在哪些应用中、何时以及如何实现规划器。

表11.1 规划在不同应用中的使用和实施

应用实施环境目的时机配置
个人助手在LLM内部或其内部个人设备促进工具使用在响应过程中作为提示的一部分或LLM
客服机器人不典型;受限环境受限环境,无工具使用限制环境中不需要复杂规划在响应时
自主代理作为代理提示的一部分并在LLM内部服务器或服务促进复杂工具使用和任务规划作为构建代理的一部分和/或在响应时在代理或LLM内部
协作工作流作为LLM的一部分共享画布或编程促进复杂工具使用在响应时在LLM内部
游戏AI作为LLM的一部分服务器或应用复杂工具使用和规划在响应之前或期间在LLM内部
研究任何地方服务器促进工具使用并参与复杂任务工作流在生成响应之前、期间和之后任何地方

表11.1展示了几种不同的应用场景,在这些场景中,我们可能会看到助手或代理以某种方式被部署来协助工作。为了提供进一步的信息和指导,此列表提供了更多关于如何在每个应用中使用规划的细节:

  • 个人助手—虽然这个应用还没有全面推广,LLM个人助手有望在未来超越Alexa和Siri。规划将对这些新的助手/代理至关重要,以便协调众多复杂任务,并按顺序或并行执行工具(动作)。
  • 客服机器人—由于此环境的控制性质,直接与客户互动的助手不太可能具有控制和非常特定的工具使用。这意味着这类助手可能不需要广泛的规划。
  • 自主代理—正如我们在前面章节中看到的,具有规划能力的代理可以完成一系列复杂任务以实现各种目标。规划将是任何自主代理系统的核心元素。
  • 协作工作流—将这些看作是与编码员或写作人员并肩工作的代理或助手。虽然这些工作流仍处于早期开发阶段,但可以想象一种工作流,代理被自动分配任务,编写和执行测试代码与开发者一起工作。规划将在执行这些复杂的未来工作流中发挥至关重要的作用。
  • 游戏AI—虽然将LLM应用于游戏仍处于初期阶段,但不难想象游戏内的代理或助手可以帮助或挑战玩家。赋予这些代理规划和执行复杂工作流的能力,可能会改变我们玩游戏的方式和玩游戏的伙伴。
  • 研究—与协作工作流类似,这些代理将负责从现有的信息源中得出新想法。找到这些信息可能会通过广泛的工具使用来促进,这将受益于规划的协调。

正如你所看到的,规划是许多LLM应用中的一个核心部分,无论是通过协调工具使用还是其他方式。接下来,我们将讨论推理的下一个组件,以及如何将其应用于相同的应用堆栈中。

11.5.2 助手/代理推理的应用

推理,尽管通常与规划和任务完成密切相关,但也是一个可以独立存在的组件。随着LLM的成熟和智能化,推理通常包含在LLM内部。然而,并非所有应用都能从广泛的推理中受益,因为推理通常会在LLM的响应中引入思维周期。表11.2简要描述了推理组件如何与各种LLM应用类型集成。

表11.2 推理在不同应用中的使用和实施

应用实施环境目的时机配置
个人助手在LLM内部个人设备将工作分解成步骤在响应过程中作为提示的一部分或LLM
客服机器人不典型;通常只是信息性限制工具使用和复合工具使用不涉及复杂推理在响应时
自主代理作为代理提示的一部分并在LLM内部服务器或服务促进复杂工具使用和任务规划作为LLM的一部分,外部推理不适合在代理或LLM内部
协作工作流作为LLM的一部分共享画布或编程帮助分解工作在响应时在LLM内部
游戏AI作为LLM的一部分服务器或应用进行复杂操作的推理在响应之前或期间在LLM内部
研究任何地方服务器理解如何解决复杂问题并参与复杂任务工作流在生成响应之前、期间和之后任何地方

表11.2展示了几种不同的应用场景,在这些场景中,我们可能会看到助手或代理以某种方式被部署来协助工作。为了提供进一步的信息和指导,此列表提供了更多关于如何在每个应用中使用推理的细节:

  • 个人助手—根据应用的不同,代理使用的推理可能会受到限制。推理是一个需要LLM思考问题的过程,这通常需要较长的响应时间,具体取决于问题的复杂性和提示的程度。在许多情况下,旨在实现接近实时的推理的响应可能会被禁用或降低。虽然这可能会限制代理的互动复杂性,但有限的推理或没有推理可以改善响应时间并增加用户的愉悦感。
  • 客服机器人—同样,由于这个环境的控制性质,直接与客户互动的助手不太可能需要执行复杂的推理。
  • 自主代理—尽管推理是自主代理的一个强大组件,但我们仍然不知道多少推理是“过多”。随着像Strawberry这样的模型开始在代理工作流中使用,我们可以评估在何种情况下广泛的推理可能不再需要。这对于定义明确的自主代理工作流来说肯定是如此。
  • 协作工作流—同样,应用推理会增加LLM交互的开销。广泛的推理可能对某些工作流有益,而对于其他定义明确的工作流则可能有不利影响。这可能意味着这些类型的工作流将从多个代理中受益——那些具有推理能力的和那些没有推理能力的。
  • 游戏AI—与其他应用类似,重度推理的应用可能不适合大多数游戏AI。游戏特别需要LLM响应时间快速,这无疑会成为一般战术代理推理的应用。当然,这并不排除使用其他推理代理,它们可能提供更多的战略控制。
  • 研究—推理可能对任何复杂的研究任务至关重要,原因有很多。一个很好的例子是Strawberry模型的应用,我们已经在数学和科学研究中看到了它的应用。

尽管我们通常将推理与规划一起考虑,但可能会有一些条件,决定每个组件的实现程度有所不同。接下来的章节将探讨代理系统中评估不同应用的支柱。

11.5.3 评估在代理系统中的应用

评估是代理/助手系统中的一个组件,它可以指导系统的表现如何。尽管我们在一些代理工作流中演示了如何集成评估,但评估通常是代理系统中的一个外部组件。然而,它也是大多数LLM应用的核心组件,并且在大多数开发中不应被忽视。表11.3简要描述了评估组件如何与各种LLM应用类型集成。

表11.3 评估在不同应用中的使用和实施

应用实施环境目的时机配置
个人助手外部服务器确定系统运行得如何交互之后通常在外部开发
客服机器人外部监控服务器评估每次交互的成功交互之后外部于代理系统
自主代理外部或内部服务器或服务确定任务完成后或过程中系统的成功交互之后外部或内部
协作工作流外部共享画布或编程评估协作的成功交互之后外部服务
游戏AI外部或内部服务器或应用评估代理或评估策略或行动的成功交互之后外部或作为代理的一部分
研究手动与LLM结合服务器与人工评估开发的研究结果输出生成后取决于问题的复杂性和研究内容

表11.3展示了几种不同的应用场景,在这些场景中,我们可能会看到助手或代理以某种方式被部署来协助工作。为了提供进一步的信息和指导,此列表提供了更多关于如何在每个应用中使用评估的细节:

  • 个人助手—在大多数情况下,评估组件将用于处理和指导代理响应的表现。在主要使用检索增强生成(RAG)进行文档探索的系统中,评估可以表明助手对信息请求的响应质量。
  • 客服机器人—评估服务机器人对于了解机器人如何响应客户请求至关重要。在许多情况下,强大的RAG知识元素可能是系统的一部分,需要广泛和持续的评估。同样,大多数评估组件都是外部于主工作系统的,通常作为监控一般表现的一部分,通过几个指标进行运行。
  • 自主代理—在大多数情况下,代理输出的手动审查将是判断自主代理成功的主要指南。然而,在某些情况下,内部评估可以帮助代理在执行复杂任务时进行指导,或作为改进最终输出的手段。像CrewAI和AutoGen这样的多代理系统就是利用内部反馈来改进生成的输出的自主代理示例。
  • 协作工作流—在这些类型的工作流中,手动评估通常是持续进行的。用户通常会立即并接近实时地通过评估输出来纠正助手/代理。额外的代理可以像自主代理一样加入,以适应更广泛的协作工作流。
  • 游戏AI—评估通常分为开发评估——评估代理与游戏的交互——以及游戏内评估,评估代理在任务中的表现。实施后者的评估形式类似于自主代理,但旨在改善某些策略或执行。这种游戏内评估也可能从记忆和反馈机制中受益。
  • 研究—此级别的评估通常发生在完成研究任务后,作为手动努力。代理可以使用类似于自主代理的评估形式来改进生成的输出,甚至在内部考虑如何扩展输出的评估或进行进一步研究。由于这是代理开发的新领域,如何成功执行这一点仍有待观察。

评估是任何代理或助手系统的核心元素,特别是当该系统为用户提供真实且基本的信息时。为代理和助手开发评估系统可能是一个值得单独成书的主题。在本章的最后部分,我们将探讨在各种LLM应用中实施反馈。

11.5.4 反馈在代理/助手应用中的应用

反馈作为代理系统的一个组件,通常(如果不是总是的话)作为外部组件实施——至少目前如此。也许,评估系统的信心将会提高,达到定期将反馈纳入这些系统的程度。表11.4展示了反馈如何被实现到各种LLM应用中。

表11.4 反馈在不同应用中的使用和实施

应用实施环境目的时机配置
个人助手外部或由用户提供聚合到服务器或作为系统的一部分提供系统改进的手段交互后或交互过程中内部和外部
客服机器人外部监控聚合到服务器评估和提供系统改进的手段交互后外部于代理系统
自主代理外部聚合到服务器提供系统改进的手段交互后外部
协作工作流在交互时共享画布或编程提供即时反馈机制交互过程中外部服务
游戏AI外部或内部服务器或应用作为内部评估反馈的一部分,提供动态改进交互后或交互过程中外部或作为代理或另一个代理的一部分
研究手动与LLM结合服务器与人工评估开发的研究结果输出生成后取决于问题的复杂性和研究内容

表11.4展示了几种不同的应用场景,在这些场景中,我们可能会看到助手或代理以某种方式被部署来协助工作。为了提供进一步的信息和指导,此列表提供了更多关于如何在每个应用中使用反馈的细节:

  • 个人助手—如果助手或代理以聊天式界面与用户互动,用户可以直接并即时地提供反馈。无论这种反馈是否持续到未来的对话或交互中,它通常会在代理记忆中发展。像ChatGPT这样的助手现在已经集成了记忆功能,并能够从用户的明确反馈中受益。
  • 客服机器人—用户或系统的反馈通常通过交互结束后的调查提供。这通常意味着反馈会被规范化到外部系统,由外部系统汇总反馈以进行后续改进。
  • 自主代理—与机器人类似,自主代理中的反馈通常是在代理完成任务后由用户审查并提供的。由于许多内容可能是主观的,捕获反馈机制可能会更难实现。在本章中探讨的生成反馈的方法可以用于改进提示工程。
  • 协作工作流—与个人助手类似,这些类型的应用可以从用户提供的即时反馈中受益。同样,如何在会话之间保持这些信息通常是代理记忆的实现。
  • 游戏AI—反馈可以与评估一起实施,通过额外和多个代理进行。这种反馈形式可能仍然是一次性的,并存在于当前的交互中,或者可能持续存在为记忆。想象一个可以评估其行为、改进这些行为并记住这些改进的游戏AI。虽然这种模式对于游戏来说并不理想,但它肯定会改善游戏体验。
  • 研究—与研究中的评估类似,反馈通常是在输出被评估后离线执行的。尽管有一些发展是通过多代理系统进行的,这些系统将代理用于评估和反馈,但这些系统通常表现不佳,至少对于当前的最先进模型而言。因此,通常更好的做法是将反馈和评估隔离到最后,以避免常见的反馈循环问题。

反馈是代理和助手系统的另一个强大组件,但在首次发布时并不总是需要。然而,集成严格的反馈和评估机制可以在长期内大大受益于代理系统,尤其是在持续监控和提供改进系统各个方面的信心方面。

如何在代理系统中实现每个组件,可能会部分受您选择的代理平台架构的指导。现在,您了解了每个组件的细微差别,您也有了选择适合您应用和业务用例的正确代理系统的知识。无论您的应用是什么,几乎在所有情况下,您都需要在应用中运用多个代理组件。

随着代理系统的发展和LLM变得更智能,我们今天认为是外部的某些组件可能会紧密集成。我们已经看到推理和规划被集成到像Strawberry这样的模型中。当然,随着我们接近理论上的人工通用智能(AGI)里程碑,我们可能会看到能够执行长期自我评估和反馈的模型。

无论如何,我希望您喜欢与我一起踏上这段进入新兴技术的奇妙旅程,这些技术无疑会改变我们对工作以及通过代理完成工作的看法。

11.6 练习

通过以下练习提高您对材料的理解:

练习 1—实现一个简单的规划代理(初学者)
目标—学习如何使用提示实现一个基本的规划代理,生成一系列行动步骤。
任务

  • 创建一个代理,接收一个目标,将其分解为步骤,并按顺序执行这些步骤。
  • 定义一个简单的目标,如从维基百科获取信息并将其保存到文件中。
  • 使用基本的规划提示实现代理(参考第11.3节中的规划器示例)。
  • 运行代理,评估其规划和执行每个步骤的效果。

练习 2—测试反馈集成在规划代理中的应用(中级)
目标—理解反馈机制如何提高代理系统的性能。
任务

  • 修改练习1中的代理,在每个任务后加入反馈循环。
  • 使用反馈来调整或纠正序列中的下一个任务。
  • 通过给代理一个更复杂的任务,例如从多个来源收集数据,测试反馈如何改善其性能。
  • 记录并比较代理在加入反馈前后的行为。

练习 3—实验并行和顺序规划(中级)
目标—学习并行和顺序行动的区别,以及它们如何影响代理行为。
任务

  • 使用Nexus设置两个代理:一个执行并行任务,另一个执行顺序任务。
  • 定义一个多步骤目标,其中一些行动依赖于前一个行动的结果(顺序),而有些可以同时执行(并行)。
  • 比较两个代理的性能和输出,注意在需要顺序步骤时并行执行的错误或低效之处。

练习 4—构建并集成自定义规划器到Nexus中(高级)
目标—学习如何构建自定义规划器并将其集成到代理平台中。
任务

  • 使用第11.3节中的提示工程策略编写一个自定义规划器,确保它支持顺序任务执行。
  • 将此规划器集成到Nexus中,并创建一个使用它的代理。
  • 使用涉及多个步骤和工具(例如数据检索、处理和保存)的复杂目标测试该规划器。
  • 评估自定义规划器与Nexus中内置规划器或其他平台的表现比较。

练习 5—在顺序规划中实现错误处理和反馈(高级)
目标—学习如何实现错误处理和反馈,以改进代理系统中的顺序规划。
任务

  • 使用顺序规划器,设置一个代理执行可能遇到常见错误的目标(例如API调用失败、缺少数据或无效输入)。
  • 在规划器中实现错误处理机制,以识别并响应这些错误。
  • 添加反馈循环,根据遇到的错误调整计划或重试行动。
  • 故意在执行过程中引发错误,测试系统,观察代理如何恢复或调整其计划。

总结

  • 规划是代理和助手的核心,使它们能够接收目标,将其分解为步骤,并执行这些步骤。如果没有规划,代理仅仅是简单的聊天机器人式的互动。

  • 代理必须区分并行和顺序行动。许多LLM可以处理并行任务,但只有先进的模型支持顺序规划,这对完成复杂任务至关重要。

  • 反馈在指导代理纠正其方向和随着时间的推移提高性能方面至关重要。本章展示了如何将反馈机制集成到代理中,以精炼其决策过程。

  • 像OpenAI Assistants和Anthropic的Claude这样的平台支持内部规划,并能够执行复杂的多步骤任务。使用这些平台的代理可以使用顺序行动规划来处理复杂的工作流。

  • 正确选择和限制代理行动至关重要,以避免混乱和不期望的行为。过多的行动可能会让代理不知所措,而不必要的工具可能会被误用。

  • Nexus通过一个灵活的接口允许创建和管理代理,用户可以实现自定义规划器、设定目标并分配工具。本章包括使用Nexus的实践示例,以突出原始LLM和规划增强型代理之间的差异。

  • 编写自定义规划器是简单的,使用提示工程策略即可。像LangChain和Semantic Kernel这样的工具提供了多种可以根据具体代理需求调整或扩展的规划器。

  • 像OpenAI Strawberry这样的模型将推理、规划、评估和反馈直接集成到LLM中,提供更准确的解决问题能力。

  • 评估有助于确定代理系统的性能,可以根据使用情况实施内部或外部评估。

  • 随着LLM的进化,推理、规划和反馈机制可能会深度集成到模型中,为更加自主和智能的代理系统铺平道路。