AI智能代理实战——构建自主助理

112 阅读33分钟

本章内容包括:

  • 机器人和人工智能应用中的行为树
  • GPT 助理游乐场(GPT Assistants Playground)及创建助理与动作
  • 自主控制代理行为树
  • 通过代理行为树模拟对话式多代理系统
  • 使用反向推理构建复杂系统的行为树

在了解了动作如何扩展代理的能力后,我们将探讨行为树如何指导代理系统。首先,我们会理解行为树的基本概念,以及它们如何控制机器人和游戏中的人工智能。

随后,我们回到代理动作,考察如何在 OpenAI 助理平台上利用 GPT 助理游乐场项目实现动作。接着,介绍如何使用 OpenAI 助理构建自主代理行为树(ABT)。然后,探讨自主代理需要的控制与保护措施,以及控制屏障函数的应用。

本章最后将展示如何使用 AgentOps 平台监控我们的自主行为驱动代理系统。本章内容丰富且富有挑战,接下来让我们先从行为树的介绍开始。

6.1 行为树介绍

行为树是一种历史悠久的模式,广泛应用于机器人和游戏中的人工智能控制。Rodney A. Brooks 在1986年发表的论文《一种用于移动机器人的稳健分层控制系统》中首次提出了该概念,为今天行为树和节点结构的使用奠定了基础。

如果你玩过带有非玩家角色(NPC)的电脑游戏,或与高级机器人系统交互过,你可能已经见识过行为树的实际运作。图 6.1 展示了一个简单的行为树结构,包含主要节点类型:选择器(selector)或回退(fallback)节点、序列(sequence)节点、动作(action)节点和条件(condition)节点。

image.png

表 6.1 介绍了本书中将要探讨的主要行为树节点的功能和用途。行为树中还有其他节点类型,甚至可以自定义节点,但目前我们重点关注表中列出的节点。

表 6.1 行为树中的主要节点

节点目的功能描述类型
选择器(回退)选择第一个成功完成的子节点。因其总是回退到最后一个成功执行的节点,故称回退节点。按顺序调用子节点,遇到第一个成功的子节点即停止执行。若有子节点成功,则返回成功;否则返回失败。复合节点
序列按顺序执行所有子节点,直到有一个失败或全部成功完成。无论子节点失败还是成功,均依次调用所有子节点。若全部成功,则返回成功;只要有一个失败,则返回失败。复合节点
条件用成功或失败控制流程,非布尔逻辑。条件成立时返回成功,否则失败。根据条件判断返回成功或失败。任务节点
动作执行动作的节点。执行操作,成功返回成功,失败返回失败。任务节点
装饰器控制子节点的执行,判断节点是否值得或安全执行,类似条件控制。控制子节点执行,装饰器可作为控制屏障函数阻止或防止不期望的行为。装饰器节点
并行并行执行所有子节点,成功或失败由达到成功阈值的子节点数量决定。无论子节点状态如何,均并行执行所有子节点。复合节点

表 6.1 中的主要节点能够满足许多应用场景的需求。然而,初次理解行为树可能有一定难度,直到真正使用它们,才能体会其背后的复杂性。接下来,我们将在下一节更详细地探讨行为树的执行过程。

6.1.1 理解行为树的执行

理解行为树的执行方式对设计和实现行为树至关重要。与计算机科学中多数概念不同,行为树以成功和失败作为运作基础。行为树中的节点执行时,会返回“成功”或“失败”,这一点对条件节点和选择器节点同样适用。

行为树按照自上而下、从左到右的顺序执行。图 6.2 展示了行为树的执行流程以及节点失败或成功时的处理情况。示例中,行为树控制的 AI 拥有一个苹果但没有梨。在第一个序列节点中,一个条件判断 AI 是否有苹果。由于 AI 没有苹果,该序列中止,回退到选择器节点。选择器随后选择下一个子节点,即另一个序列节点,判断 AI 是否有梨。AI 有梨,因此执行吃苹果的动作。

image.png

行为树可以在宏观或微观层面对 AI 系统的执行进行控制。在机器人领域,行为树通常设计为微观层级运行,每个动作或条件都是一个小事件,比如检测苹果。而在游戏中,行为树也可以控制更宏观的系统,比如 NPC,每个动作可能是多个事件的组合,比如攻击玩家。

对于代理系统,行为树支持在你选择的层级对代理或助理进行控制。我们将探索如何在任务层级控制代理,后续章节将涉及规划层级。毕竟,借助大型语言模型(LLM)的能力,代理甚至可以自行构建行为树。

当然,还有多种 AI 控制方式可以用来控制代理系统。下一节将对这些不同系统进行探讨,并与行为树进行比较。

6.1.2 关于行为树的选择

许多其他 AI 控制系统各有优劣,值得在代理系统控制中探讨。它们可以展示行为树的优势,也为特定场景提供替代方案。行为树是一种优秀的模式,但并非唯一,了解其他模式也很重要。

表 6.2 其他 AI 控制系统对比
控制名称描述缺点是否适合代理 AI 控制
有限状态机(FSM)用状态和事件/条件触发的转移来建模 AI。复杂度增大时难以管理;不具备良好扩展性。不实用,难以扩展。
决策树用树状结构表达决策及其可能后果。复杂场景容易过拟合,泛化能力不足。可结合行为树改进。
效用系统基于当前情境评估和选择最佳动作。需精心设计效用函数以平衡优先级。可纳入行为树体系。
规则系统一系列 if-then 规则定义 AI 行为。规则繁多时难管理,且可能冲突。与 LLM 驱动代理结合不佳。
规划系统使用规划算法生成实现目标的动作序列。计算量大,需丰富领域知识。代理可自行实现,后续章节详述。
行为克隆通过模仿专家示范学习策略。泛化到未见情况时表现欠佳。可集成至行为树或特定任务。
分层任务网络(HTN)将任务分解为层级结构中的子任务。管理和设计复杂,适合非常大的任务。适合大型代理系统。
黑板系统通过共享黑板促进子系统间协作解决问题。实现和管理子系统间通信困难。可用聊天/线程实现类似模式。
遗传算法(GA)受自然选择启发的优化算法。计算量大,未必找到最优解。有潜力,可用于优化行为树。
  • a 复杂代理系统中不实用
  • b 可纳入行为树或已有类似实现
  • c 通常用于任务或动作/条件层级
  • d 应用到代理时实现复杂

本书后续章节将进一步探讨表 6.2 中部分模式。整体来看,许多模式可在行为树基础上增强或集成。虽然 FSM 等适合小型实验,但缺乏行为树的可扩展性。

行为树的优势

行为树作为 AI 控制系统,具备多项优势,包括可扩展性。以下是使用行为树的其他显著好处:

  • 模块化与复用性
    行为树支持模块化设计,节点可在不同部分或项目中复用,提升可维护性,缩短开发周期。
  • 可扩展性
    随系统复杂度增长,行为树比 FSM 更优雅地支持新增行为。层级化任务组织使得管理大规模行为更简单。
  • 灵活性与可扩展性
    行为树框架灵活,可无缝新增节点(动作、条件、装饰器),方便适应新需求或修改现有行为。
  • 调试与可视化
    行为树结构清晰直观,有助于调试和理解决策流程。许多工具提供图形化编辑器,便于定位和修复问题。
  • 决策逻辑解耦
    行为树将决策与执行逻辑分离,高层策略与底层动作清晰分工,简化设计,便于局部修改和测试。

既然行为树优势明显,接下来我们考虑如何用代码实现。下一节将介绍如何使用 Python 及 py_trees 库构建简单行为树。

6.1.3 使用 Python 和 py_trees 运行行为树

行为树历史悠久,广泛应用于多种技术,创建示例演示非常简单。最便捷的方法是让 ChatGPT 或你喜欢的 AI 聊天工具帮忙生成代码。列表 6.1 展示了使用提示生成的代码示例,示例基于图 6.1 的行为树,稍作简单命名和参数修正。

注意:本章所有代码均可通过下载 GPT Assistants Playground 项目获得,网址:mng.bz/Ea0q

列表 6.1 first_btree.py
import py_trees

class HasApple(py_trees.behaviour.Behaviour):      #1
    def __init__(self, name):
        super(HasApple, self).__init__(name)

    def update(self):        
        if True:  
            return py_trees.common.Status.SUCCESS
        else:
            return py_trees.common.Status.FAILURE
# 省略其他类…

has_apple = HasApple(name="Has apple")      #2
eat_apple = EatApple(name="Eat apple")      #2
sequence_1 = py_trees.composites.Sequence(name="Sequence 1", memory=True)
sequence_1.add_children([has_apple, eat_apple])                              #3

has_pear = HasPear(name="Has pear")         #4
eat_pear = EatPear(name="Eat pear")         #4
sequence_2 = py_trees.composites.Sequence(name="Sequence 2", memory=True)
sequence_2.add_children([has_pear, eat_pear])               #3                

root = py_trees.composites.Selector(name="Selector", memory=True)
root.add_children([sequence_1, sequence_2])          #3                       

behavior_tree = py_trees.trees.BehaviourTree(root)     #5

py_trees.logging.level = py_trees.logging.Level.DEBUG   
for i in range(1, 4):                                                      #6
    print("\n------------------ Tick {0} ------------------".format(i))
    behavior_tree.tick()                                                  #6

### 输出开始
------------------ Tick 1 ------------------
[DEBUG] Selector             : Selector.tick()
[DEBUG] Selector             : Selector.tick() [!RUNNING->reset current_child]
[DEBUG] Sequence 1           : Sequence.tick()
[DEBUG] Has apple            : HasApple.tick()
[DEBUG] Has apple            : HasApple.stop(Status.INVALID->Status.SUCCESS)
[DEBUG] Eat apple            : EatApple.tick()
Eating apple
[DEBUG] Eat apple            : EatApple.stop(Status.INVALID->Status.SUCCESS)
[DEBUG] Sequence 1           : Sequence.stop()[Status.INVALID->Status.SUCCESS]
  • #1 创建实现动作或条件的类
  • #2 创建动作和条件节点
  • #3 将节点添加到对应父节点
  • #4 创建动作和条件节点
  • #5 创建完整行为树
  • #6 对行为树执行一次“滴答”操作

列表 6.1 中代码即图 6.1 行为树的代码表示。你可以直接运行,也可以修改条件返回值后再次运行,或者通过移除根选择器中的序列节点来调整行为树结构。

现在我们对行为树有了基本了解,接下来可以着手代理和助理的相关工作。在此之前,我们先了解一个辅助工具,帮助我们为 OpenAI 助理构建首个自主代理行为树(ABT)。

6.2 探索 GPT 助理游乐场

为了本书的开发,创建了多个 GitHub 项目来涵盖构建代理和助理的各个方面。其中一个项目是 GPT 助理游乐场(GPT Assistants Playground),它使用 Gradio 构建界面,模仿 OpenAI 助理游乐场,但增加了多个额外功能。

该游乐场项目既作为教学工具,也作为演示辅助。在项目中,Python 代码使用 OpenAI 助理 API 创建聊天界面和代理系统,用以构建和驱动助理。项目还提供了丰富的助理动作集合,并且支持轻松添加自定义动作。

6.2.1 安装与运行游乐场

以下列表展示了如何在终端安装并运行游乐场项目。目前该项目尚无 PyPI 包可供安装。

列表 6.2 安装 GPT 助理游乐场
# 切换到工作文件夹并创建新的 Python 虚拟环境
git clone https://github.com/cxbxmxcx/GPTAssistantsPlayground     #1
cd GPTAssistantsPlayground      #2
pip install -r requirements.txt      #3
  • #1 从 GitHub 拉取源码
  • #2 切换到项目源码文件夹
  • #3 安装依赖

你可以通过终端或 Visual Studio Code(VS Code)运行应用,后者能提供更多控制。在运行应用前,需要通过命令行或创建 .env 文件设置 OpenAI API 密钥,这一步我们前面已经多次演示。

列表 6.3 运行 GPT 助理游乐场示例
export OPENAI_API_KEY="your-api-key"      #1
python main.py     #2
  • #1 将 API 密钥设置为环境变量
  • #2 通过终端或 VS Code 运行应用

打开浏览器访问终端显示的地址(通常是 http://127.0.0.1:7860)即可看到界面,如图6.3 所示。如果你已经定义了 OpenAI 助理,它们会显示在“选择助理”下拉菜单中。

image.png

如果你还没有定义过助理,可以创建一个,并选择你所需的各种选项和指令。如果你用过 OpenAI Playground,那么你已经体验过类似的界面。


GPT 与助理的区别

OpenAI 将 GPT 定义为你可以在 ChatGPT 界面内运行和使用的助理。而助理则只能通过 API 访问,且通常需要自定义代码。运行助理时,费用根据模型的 Token 使用量和任何特殊工具(如代码解释器和文件)计算;而 GPT 运行在 ChatGPT 内,费用由账户费用覆盖。

创建本地版本 Playground 的目的既是为了演示代码结构,也是为了提供以下额外功能:

  • 动作(自定义动作)
    创建自己的动作可以让你为助理添加任意功能。如我们后续会见到的,Playground 非常便于快速创建自定义动作。
  • 代码运行器
    API 自带代码解释器,但成本较高(每次运行约 0.03 美元),不允许安装自定义模块,不能交互式运行代码,且执行较慢。Playground 允许你在本地隔离的虚拟环境中运行 Python 代码。虽然安全性不如使用 Docker 镜像,但执行时具有窗口化和进程外运行的优势,优于其他平台。
  • 透明性与日志记录
    Playground 提供全面的日志捕获,甚至可以展示助理如何使用内部和外部工具/动作,这有助于你了解助理背后的运行状况。

接下来几节将详细介绍这些功能。我们先从使用和调用动作开始。

6.2.2 使用与构建自定义动作

动作和工具是赋能代理与助理的基石。没有工具,代理不过是无功能的聊天机器人。正如第三章所示,OpenAI 平台在许多工具模式的建立中处于领先地位。

Playground 提供了多个可以通过界面附加到助理的自定义动作。在接下来的练习中,我们将构建一个简单的助理,并附加几个自定义动作,看看能实现什么功能。

图 6.4 展示了扩展的“动作”折叠面板,列出了许多可用的自定义动作。你可以从终端或调试器启动 Playground,创建一个新的助理,然后选择图中显示的动作。选完后,向下滚动页面,点击“添加助理”以添加该助理。助理必须先创建后才能使用。

image.png

创建助理后,你可以让它列出所有可用的助理。列出助理时,还会显示调用助理所需的 ID。你也可以调用其他助理,让它们完成各自专长领域的任务。

添加自定义动作非常简单,只需把代码写到文件里,并放到正确的文件夹中即可。打开主项目文件夹中的 playground/assistant_actions 文件夹,你会看到多个定义不同动作的文件。用 VS Code 打开 file_actions.py 文件,如列表 6.4 所示。

列表 6.4 playground/assistant_actions/file_actions.py
import os

from playground.actions_manager import agent_action

OUTPUT_FOLDER = "assistant_outputs"


@agent_action     #1
def save_file(filename, content):      #2
    """
    Save content to a file.      #3

    :param filename: The name of the file including extension.
    :param content: The content to save in the file.
    """
    file_path = os.path.join(OUTPUT_FOLDER, filename)
    with open(file_path, "w", encoding="utf-8") as file:
        file.write(content)
    print(f"File '{filename}' saved successfully.")      #4
  • #1 该装饰器自动将函数添加为一个动作
  • #2 给函数起个清晰、符合功能的名字
  • #3 说明函数用途,助理会根据说明判断该函数,因此文档要写清楚
  • #4 通常返回成功或失败消息

你可以通过将文件放入 assistant_actions 文件夹,并用 agent_action 装饰器装饰函数,添加任何你想要的自定义动作。只要给函数起好名字并写好使用说明即可。Playground 启动时,会加载该文件夹里所有正确装饰且有文档说明的动作。

就是这么简单。你可以根据需要添加多个自定义动作。下一节我们将介绍一个特殊的自定义动作,让助理能在本地运行代码。

6.2.3 安装助理数据库

要运行本章的多个示例,你需要安装助理数据库。幸运的是,这可以通过界面轻松完成,甚至只需向代理发出请求即可。以下安装流程摘自 GPT Assistants Playground 的 README:

  • 创建一个新助理,或者使用已有助理。
  • 给助理添加 create_manager_assistant 动作(位于动作列表中)。
  • 让助理创建管理助理(例如对助理说:“please create the manager assistant”),并确保命名为“Manager Assistant”。
  • 刷新浏览器,重新加载助理选择列表。
  • 选择新的“Manager Assistant”。该助理带有安装来自 assistants.db SQLite 数据库中助理的指令和动作。
  • 与“Manager Assistant”对话,获取可安装助理列表,或者直接让它安装所有可用助理。

6.2.4 让助理在本地运行代码

让代理和助理生成并执行可执行代码非常强大。与代码解释器不同,本地运行代码提供了大量快速迭代和调试的机会。我们之前在 AutoGen 中也见过,代理可以持续运行代码,直到符合预期。

在 Playground 中,选择自定义动作 run_code 非常简单,如图 6.5 所示。同时,你还需要选择 run_shell_command 动作,因为它允许助理使用 pip 安装任何需要的模块。

image.png

你现在可以让助理帮你生成并运行代码,以确保代码能够正常工作。试着添加这些自定义动作,然后让助理生成并运行代码,如图 6.6 所示。如果代码没有按预期运行,可以告诉助理你遇到的问题。

image.png

同样,Playground 中运行的 Python 代码会在项目子文件夹内创建一个新的虚拟环境。这个系统对于不涉及操作系统级别代码或底层代码的情况运行良好。如果你需要更健壮的方案,AutoGen 是一个不错的选择,它使用 Docker 容器来运行隔离的代码。

为助理添加运行代码或其他任务的动作可能会让助理看起来像个黑箱。幸运的是,OpenAI 助理 API 允许你监听事件,查看助理在幕后执行的操作。下一节我们将展示这是什么样的体验。

6.2.5 通过日志调查助理运行过程

OpenAI 在助理 API 中新增了一个功能,允许你监听通过工具/动作链调用的事件和操作。这个功能已经集成到 Playground 中,当助理调用另一个助理时,会捕获其动作和工具使用情况。

我们可以通过让助理使用一个工具,然后打开日志来尝试这一功能。一个很好的示例是,给助理提供代码解释器工具,然后让它绘制一个方程图。图 6.7 展示了这个练习的示例。

image.png

通常,当启用助理的代码解释器工具时,你不会直接看到代码生成或执行过程。该功能允许你实时查看助理使用的所有工具和动作。这不仅是一个极好的诊断工具,还能让你深入了解大型语言模型(LLM)的功能。

由于相关代码庞大且可能频繁变动,我们这里没有详细回顾。不过,如果你计划使用助理 API,这个项目是一个不错的起点。有了 Playground 的介绍,我们可以继续进入下一节,探索代理行为树(ABT)。

6.3 介绍代理行为树(Agentic Behavior Trees)

代理行为树(ABT)是在助理和代理系统上实现行为树的方式。与普通行为树的主要区别在于,ABT 使用提示(prompt)来指导动作和条件。由于提示可能返回大量随机结果,这类树也可被称为随机行为树(stochastic behavior trees),确实存在。为简化起见,我们将专指用于控制代理的行为树,称之为代理行为树。

接下来,我们将进行一个练习,创建一个 ABT。完成的行为树会用 Python 编写,但需要配置多个助理。我们将介绍如何使用助理本身来管理助理。

6.3.1 用助理管理助理

幸运的是,Playground 可以帮助我们快速管理和创建助理。我们先安装管理助理(Manager Assistant),然后安装预定义的助理。以下步骤展示如何安装管理助理:

  1. 在浏览器打开 Playground,创建一个新的简单助理或选择已有助理。如果需要新助理,先创建并选中它。
  2. 选中助理后,展开“动作”(Actions)折叠面板,选择 create_manager_assistant 动作。无需保存,界面会自动更新助理。
  3. 在聊天界面输入:“Please create the manager assistant.”
  4. 几秒后,助理会提示完成。刷新浏览器,确认管理助理已可用。如果未显示,尝试重启 Gradio 应用。

管理助理类似管理员,拥有所有权限。与其交互时,请确保请求具体明确。管理助理激活后,你可以按照以下步骤安装本书使用的其他助理:

  • 选择管理助理。如果你修改过它,可以随时删除并重新安装。虽可同时存在多个管理助理,但不建议这样做。
  • 在聊天框输入:“Please list all the installable assistants.” 询问可安装助理列表。
  • 识别想安装的助理后,输入:“Please install the Python Coding Assistant.” 等指令让管理助理安装它。

你可以使用 Playground 管理和安装任何可用助理,还可以让管理助理将所有助理定义保存为 JSON 文件:

Please save all the assistants as JSON to a file called assistants.json.

管理助理能访问所有动作,应谨慎使用。设计助理时,最好保持目标明确,限制动作只包含必要内容。这样不仅避免赋予 AI 过多决策权,也防止因幻觉导致错误。

在接下来的练习中,你可能需要安装所需助理。也可让管理助理安装所有可用助理。无论如何,下一节我们将用助理创建一个 ABT。

6.3.2 构建编程挑战 ABT

编程挑战是测试和评估代理及助理系统的良好基准。挑战和基准测试能量化代理或代理系统的表现。我们在第四章已使用 AutoGen 和 CrewAI 应用编程挑战于多平台代理。

这次,我们进一步关注 Edabit 网站(edabit.com)上Python 编程挑战,难度从初学者到专家不等。考虑到 GPT-4o 和其他模型是出色的编码者,我们将选择专家级别的编程挑战。请看下一节示例,思考你的解决方案。

列表 6.5 Edabit 挑战:Plant the Grass(种草)

作者:AniXDownLoe

你将获得一个表示田地的矩阵 g,以及两个数字 x, y 表示坐标。

矩阵中有三种字符:

- x 表示岩石。  
- o 表示泥土。  
- + 表示长满草的区域。

你需要模拟草从坐标 (x, y) 开始生长。  
草可以向上下左右四个方向扩散。  
草只能长在泥土上,不能穿过岩石。

返回模拟后的矩阵。

示例:

simulate_grass(["xxxxxxx","xooooox","xxxxoox""xoooxxx""xxxxxxx"], 1, 1) → ["xxxxxxx","x+++++x","xxxx++x""xoooxxx""xxxxxxx"]

备注:矩阵边缘总是岩石。

你可以选择任何编程挑战或练习,但请注意:

  • 挑战应能通过量化断言(通过/失败)测试。
  • 避免在挑战中弹出窗口,比如构建游戏、网站或其他界面。未来可能支持完整界面测试,但目前只输出文本。
  • 尽量避免长时间运行的挑战,初期保持挑战简短精炼。

通常,每个挑战都会附带一组测试或断言以确认解法有效。Edabit 上的挑战通常提供了完善的测试集。下列表展示了该挑战配套的测试用例。

列表 6.6 Plant the Grass 测试用例
Test.assert_equals(simulate_grass(
["xxxxxxx","xooooox","xxxxoox","xoooxxx","xxxxxxx"],
 1, 1), 
["xxxxxxx","x+++++x","xxxx++x","xoooxxx","xxxxxxx"])

Test.assert_equals(simulate_grass(
["xxxxxxx","xoxooox","xxoooox","xooxxxx","xoxooox","xoxooox","xxxxxxx"],
 2, 3), ["xxxxxxx","xox+++x","xx++++x","x++xxxx","x+xooox","x+xooox","xxxxxxx"])

Test.assert_equals(simulate_grass(
["xxxxxx","xoxoox","xxooox","xoooox","xoooox","xxxxxx"], 
1, 1), 
["xxxxxx","x+xoox","xxooox","xoooox","xoooox","xxxxxx"])

Test.assert_equals(simulate_grass(
["xxxxx","xooox","xooox","xooox","xxxxx"], 
1, 1),
["xxxxx","x+++x","x+++x","x+++x","xxxxx"])

Test.assert_equals(simulate_grass(
["xxxxxx","xxxxox","xxooox","xoooxx","xooxxx","xooxxx","xxooox","xxxoxx","xxxxxx"], 
4, 1),
["xxxxxx","xxxx+x","xx+++x","x+++xx","x++xxx","x++xxx","xx+++x","xxx+xx","xxxxxx"])

Test.assert_equals(simulate_grass(
["xxxxxxxxxxx", "xoxooooooox", "xoxoxxxxxox", "xoxoxoooxox", "xoxoxoxoxox", "xoxoxoxoxox", "xoxoxxxoxox", "xoxoooooxox", "xoxxxxxxxox", "xooooooooox", "xxxxxxxxxxx"], 1, 1), 
["xxxxxxxxxxx", "x+x+++++++x", "x+x+xxxxx+x", "x+x+x+++x+x", "x+x+x+x+x+x", "x+x+x+x+x+x", "x+x+xxx+x+x", "x+x+++++x+x", "x+xxxxxxx+x", "x+++++++++x", "xxxxxxxxxxx"])

这些测试将作为两步验证的一部分,用于确认解法的有效性。我们将直接使用这些测试和挑战,进一步检验 AI。

图 6.8 展示了一个简单行为树的结构,该树将用于解决各种编程挑战。你会发现,该 ABT 对动作和条件分别使用不同的助理。第一步由 Python 编码助理(称为 Hacker)生成解决方案,随后由编程挑战裁判(称为 Judge)进行审查,裁判输出经改进的解法,最后由另一名 Python 编码助理(称为 Verifier)验证代码正确性。

image.png

图 6.8 还展示了每个代理在哪个线程上对话。助理使用消息线程,类似 Slack 或 Discord 的频道,所有在该线程对话的助理都能看到所有消息。对于这个 ABT,我们为 Hacker 和 Judge 共享一个主对话线程,而 Verifier 则在一个独立的线程工作。将 Verifier 放在独立线程中可以避免被解决方案过程中的大量信息干扰。

现在,用代码构建 ABT 就是结合 py_trees 包和 Playground API 函数的问题。列表 6.7 展示了创建每个动作/条件节点及给助理下达指令的代码片段。

列表 6.7 agentic_btree_coding_challenge.py
root = py_trees.composites.Sequence("RootSequence", memory=True)

thread = api.create_thread()     #1
challenge = textwrap.dedent("""
 #2
""")
judge_test_cases = textwrap.dedent("""
 #3
""")

hacker = create_assistant_action_on_thread(   
    thread=thread,      #4
    action_name="Hacker",
    assistant_name="Python Coding Assistant",
    assistant_instructions=textwrap.dedent(f"""
    Challenge goal: 
    {challenge}      #5
    Solve the challenge and output the 
final solution to a file called solution.py        
    """),
)
root.add_child(hacker)

judge = create_assistant_action_on_thread(    
    thread=thread,      #6
    action_name="Judge solution",
    assistant_name="Coding Challenge Judge",
    assistant_instructions=textwrap.dedent(
        f"""
    Challenge goal: 
    {challenge}      #7
    Load the solution from the file solution.py.
    Then confirm is a solution to the challenge 
and test it with the following test cases:
    {judge_test_cases}      #8
    Run the code for the solution and confirm it passes all the test cases.
    If the solution passes all tests save the solution to a file called 
judged_solution.py
    """,
    ),
)
root.add_child(judge)

# verifier operates on a different thread, essentially in closed room
verifier = create_assistant_condition(     #9
    condition_name="Verify solution",
    assistant_name="Python Coding Assistant",
    assistant_instructions=textwrap.dedent(
        f"""
    Challenge goal: 
    {challenge}      #10
    Load the file called judged_solution.py and 
verify that the solution is correct by running the code and confirm it passes 
all the test cases:
    {judge_test_cases}      #11
    If the solution is correct, return only the single word SUCCESS, otherwise 
return the single word FAILURE.
    """,
    ),
)
root.add_child(verifier)

tree = py_trees.trees.BehaviourTree(root)

while True:
    tree.tick()
    time.sleep(20)      #12
    if root.status == py_trees.common.Status.SUCCESS:    #13
        break
  • #1 创建一个消息线程,由 Hacker 和 Judge 共享
  • #2 挑战内容,如示例列表 6.5 所示
  • #3 测试用例,如示例列表 6.6 所示
  • #4 创建消息线程,由 Hacker 和 Judge 共享
  • #5 挑战内容
  • #6 创建消息线程,由 Hacker 和 Judge 共享
  • #7 挑战内容
  • #8 测试用例
  • #9 创建一个新的消息线程(Verifier 独立线程)
  • #10 挑战内容
  • #11 测试用例
  • #12 可根据需要调整休眠时间,用于限制发送给 LLM 的消息频率
  • #13 直到验证成功,流程才会结束

在 VS Code 中加载此文件或通过命令行运行即可执行 ABT。关注终端输出,观察助理如何一步步完成行为树中的任务。

如果验证步骤失败,流程将按树的逻辑继续执行。即使是这个简单示例,你也能快速创建许多变体,甚至可以为挑战设计多个 Hacker,协同分析和解决问题。

这个示例主要依赖 Playground 代码,使用了辅助函数 create_assistant_conditioncreate_assistant_action_on_thread。这些代码利用了几个类将 py_trees 行为树代码与 Playground 中封装的 OpenAI 助理代码集成起来。想了解底层细节可查看项目代码。

6.3.3 会话式 AI 系统与其他方法比较

我们在第四章通过 AutoGen 已经看过会话式多代理系统。ABT 可以结合多线程对话和其他方法(比如文件共享)来工作。助理和代理间传递文件有助于减少冗余且嘈杂的对话内容;而会话系统则利于产生潜在的新兴行为。二者结合能提升控制能力和解决方案效果。

列表 6.7 的简单方案可扩展以处理更多现实中的编程挑战,甚至构建完整的编程 ABT。下一节我们将构建另一个 ABT,解决不同的问题。

6.3.4 发布 YouTube 视频到 X(前 Twitter)

本节练习将设计一个 ABT,实现以下功能:

  • 在 YouTube 搜索指定主题的视频并返回最新视频列表。
  • 下载所有搜索视频的字幕。
  • 汇总字幕内容。
  • 审核汇总字幕并选出一条视频进行 X 平台发布(前称 Twitter)。
  • 撰写精彩且吸引人的短文(不超过 280 字)。
  • 审核短文后发布到 X。

图 6.9 展示了该 ABT 由不同助理组成。练习中根节点为序列节点,每个助理负责不同动作。为简化起见,每个助理交互均在新线程中进行,将每个助理的互动隔离开来,方便调试异常。

image.png

6.3.5 运行所需的 X(Twitter)账号配置

如果你打算运行本练习中的代码,必须将你的 X 账号凭证添加到 .env 文件中。.env.default 文件中有凭证格式示例,如列表 6.8 所示。你不必非要输入真实凭证,这意味着最后一步发布推文时会失败,但你仍然可以查看生成的文件(youtube_twitter_post.txt)内容。

列表 6.8 凭证配置示例
X_EMAIL = "在此填写 Twitter 邮箱"
X_USERNAME = "在此填写 Twitter 用户名"
X_PASSWORD = "在此填写 Twitter 密码"

YouTube 搜索与垃圾信息问题

如果你计划真正运行此练习并让其发布到你的 X 账号,请注意 YouTube 存在一定的垃圾信息问题。助理们已被配置尽量避免视频垃圾信息,但仍可能有部分内容漏网。构建一个能够筛选视频、避开垃圾信息的有效 ABT 有实际应用价值。


列表 6.9 创建助理动作的代码示例

该 ABT 使用三个不同的助理,每个助理有自己独特的任务指令。注意,每个助理的角色说明都是唯一的。你可以通过 Playground 查看每个助理的详细指令。

root = py_trees.composites.Sequence("RootSequence", memory=True)

search_term = "GPT Agents"
search_youtube_action = create_assistant_action(
    action_name=f"Search YouTube({search_term})",
    assistant_name="YouTube Researcher v2",
    assistant_instructions=f"""
    Search Term: {search_term}
    Use the query "{search_term}" to search for videos on YouTube.
    then for each video download the transcript and summarize it 
for relevance to {search_term}
    be sure to include a link to each of the videos,
    and then save all summarizations to a file called youtube_transcripts.txt
    If you encounter any errors, please return just the word FAILURE.
    """,
)
root.add_child(search_youtube_action)

write_post_action = create_assistant_action(
    action_name="Write Post",
    assistant_name="Twitter Post Writer",
    assistant_instructions="""
    Load the file called youtube_transcripts.txt,
    analyze the contents for references to search term at the top and 
then select
    the most exciting and relevant video related to: 
    educational, entertaining, or informative, to post on Twitter.
    Then write a Twitter post that is relevant to the video,
    and include a link to the video, along
    with exciting highlights or mentions, 
    and save it to a file called youtube_twitter_post.txt.
    If you encounter any errors, please return just the word FAILURE.
    """,
)
root.add_child(write_post_action)

post_action = create_assistant_action(
    action_name="Post",
    assistant_name="Social Media Assistant",
    assistant_instructions="""
    Load the file called youtube_twitter_post.txt and post the content 
to Twitter.
    If the content is empty please do not post anything.
    If you encounter any errors, please return just the word FAILURE.
    """,
)
root.add_child(post_action)

所需助理 — “YouTube Researcher v2”,“Twitter Post Writer”,以及“Social Media Assistant” — 通过 Playground 安装这些助理。

按常规方式运行代码,几分钟后,你将在 assistants_output 文件夹看到一条新生成的帖子。图 6.10 展示了使用该 ABT 生成的帖子示例。

如果你一天运行多条以上的帖子,很可能会导致你的 X 账号被封禁。如果已正确配置了 X 凭证,生成的帖子将出现在你的时间线上。

image.png

该 ABT 仅作为演示用途,不适合生产环境或长期使用。演示的主要功能包括展示搜索与数据加载、摘要与过滤、内容生成,以及多个自定义动作和与 API 的集成。

6.4 构建会话式自主多代理系统

多代理系统中的会话机制能够驱动反馈、推理和涌现行为等功能。使用 ABT 将助理/代理隔离管理,能够有效控制结构化流程,正如我们在 YouTube 发布示例中所见。然而,我们同样不希望错过跨代理/助理会话所带来的优势。

幸运的是,Playground 提供了将助理隔离或组合到不同会话线程中的方法。图 6.11 展示了助理如何以多种方式被隔离或混合到各个线程中。将隔离机制与会话结合,能够兼顾两种模式的优势。

image.png

我们将通过一个简单但实用的练习来展示会话模式的有效性。在下一个练习中,我们会使用两个助理在同一个 ABT(代理行为树)中,二者在同一消息线程上进行对话。下列代码展示了该行为树的构建及相关助理。

列表 6.10 agentic_conversation_btree.py
root = py_trees.composites.Sequence("RootSequence", memory=True)
bug_file = """
# 代码未显示
"""

thread = api.create_thread()     #1

debug_code = create_assistant_action_on_thread(     #2
    thread=thread,
    action_name="Debug code",
    assistant_name="Python Debugger",
    assistant_instructions=textwrap.dedent(f"""    
    这里有一段包含错误的代码:
    {bug_file}
    运行代码以识别并修复错误。
    确保测试代码,确保无错误且不抛出异常。
    """),
)
root.add_child(debug_code)

verify = create_assistant_condition_on_thread(     #3
    thread=thread,
    condition_name="Verify",
    assistant_name="Python Coding Assistant",
    assistant_instructions=textwrap.dedent(
        """
    验证解决方案是否修复了错误且不存在其他问题。
    确认运行代码时不抛出异常。
    如果解决方案正确,回复 SUCCESS;否则回复 FAILURE。
    如果满意解决方案,将代码保存为 fixed_bug.py 文件。
    """,
    ),
)
root.add_child(verify)
tree = py_trees.trees.BehaviourTree(root)

while True:
    tree.tick()    
    if root.status == py_trees.common.Status.SUCCESS:
        break    #4
    time.sleep(20)
  • #1 创建一个消息线程,供助理共享和对话
  • #2 创建调试代码动作,由专用助理执行
  • #3 创建验证条件,用于测试代码是否修复
  • #4 该行为树会持续运行,直到根序列成功完成

该行为树包含三个节点:根序列、调试代码动作和验证修复条件。根序列节点意味着两个助理会依次工作,直到都返回成功。两个助理在同一线程上对话,但以持续反馈的方式被控制。

在 VS Code 中加载此文件并运行,或者直接从命令行执行。示例代码中包含几个小错误,助理们将会合作修复。ABT 成功运行后,可以打开 assistants_output/fixed_bug.py 文件,验证结果是否正确。

我们已经看到几个 ABT 的实际应用,并理解了使用隔离(silos)与会话的细微差别。接下来,我们将学习一些构建自己 ABT 的技巧。

6.5 使用反向链式推理构建 ABT

反向链式推理(back chaining)是逻辑和推理中的一种方法,通常用于通过从目标向后工作来构建行为树。本节将使用反向链式推理过程来构造一个 ABT,以实现特定目标。具体步骤如下:

  1. 确定目标行为:明确你希望代理执行的行为。
  2. 确定所需动作:识别达到目标行为所需的动作。
  3. 确定条件:明确每个动作成功所需满足的条件。
  4. 确定通信模式:确定助理之间如何传递信息,是隔离在不同线程还是共享同一线程,或者两者结合更优。
  5. 构建行为树:从目标行为开始递归构建行为树,添加动作和条件节点,直到所有条件与已知状态或事实相连。

行为树通常使用黑板模式(blackboard)在节点间通信。黑板类似于 py_trees 中的键值存储,用于保存信息并在节点间共享,还支持访问控制等功能。

我们之前使用文件通信是因为其简单和透明。但随着代理系统对信息和格式需求增加,黑板系统需要变得更复杂或与文件存储方案整合。

让我们通过反向链式推理来构建一个 ABT。我们可以针对多种目标,但这里选择一个有趣的元目标:构建一个帮助创建助理的 ABT。首先将目标陈述为:

“创建一个可以帮助我完成{任务}的助理。”

  • 所需动作(逆向)

    • 创建助理
    • 验证助理
    • 测试助理
    • 给助理命名
    • 给助理相关指令
  • 识别条件

    • 验证助理
  • 确定通信模式

    • 为了趣味性,所有助理都运行在同一消息线程中。
  • 构建行为树(动作和条件倒序):

    • (动作)给助理相关指令,帮助其完成指定任务。
    • (动作)给助理命名。
    • (动作)测试助理。
    • (条件)验证助理。
    • (动作)创建助理。

当然,构建树的简单方法是让 ChatGPT 或其他能力强大的模型来生成。下一列表展示了 ChatGPT 生成的树形结构。你也可以自行设计并加入其它元素。

列表 6.11 构建助理的 ABT
Root

├── Sequence
    ├── Action: 给助理相关指令,帮助用户完成指定任务
    ├── Action: 给助理命名
    ├── Action: 测试助理
    ├── Condition: 验证助理
    └── Action: 创建助理

从这里开始,我们可以遍历每个动作和条件节点,确定助理所需的指令,包括工具和自定义动作(可能需要自行开发)。初次尝试时,保持指令通用。理想情况下,尽量减少助理数量。

确定了助理、工具和动作及对应任务后,可以尝试进一步泛化设计,考虑哪些动作可以合并、减少助理数量。但务必保持合理的任务划分,例如测试和验证最好由不同助理完成。

总结

  • 行为树是一种强大且可扩展的人工智能控制模式,最早由 Rodney A. Brooks 在机器人领域提出。因其模块化和可复用性,行为树被广泛应用于游戏和机器人领域。

  • 行为树的主要节点包括选择器(selector)、序列(sequence)、条件(condition)、动作(action)、装饰器(decorator)和并行节点(parallel)。选择器类似“或”逻辑块,序列节点按顺序执行子节点,条件节点用于测试状态,动作节点执行具体任务,装饰器节点作为包装器控制执行,而并行节点允许多个子节点同时执行。

  • 理解行为树的执行流程对于设计、构建和运行行为树,确保决策路径明确至关重要。

  • 行为树的优势包括模块化、可扩展性、灵活性、易于调试以及决策逻辑的解耦,使其非常适合复杂的 AI 系统。

  • 在 Python 中设置和运行简单的行为树,需要正确命名和文档化自定义节点。

  • GPT Assistants Playground 项目基于 Gradio,模拟了 OpenAI Assistants Playground,增加了教学和演示自主行为树(ABT)的功能。

  • GPT Assistants Playground 支持创建和管理自定义动作,这是构建多功能助理的重要基础。

  • ABT 通过提示引导助理执行动作和判断条件,从而控制代理和助理,利用大型语言模型(LLM)的能力构建动态自主系统。

  • 反向链式推理(back chaining)是一种从目标行为向后构建行为树的方法,包括识别所需动作、条件、通信模式,然后逐步构建行为树。

  • 代理系统在实体间通信时,既能受益于隔离(siloed)模式,也能从会话(conversational)模式中获益。将两者结合能同时利用结构化流程和涌现行为的优势。