June 23, 2023・31 min・Lilian Weng
使用大型语言模型(LLM)作为其核心控制器构建代理是一个很酷的概念。例如 AutoGPT 、GPT-Engineer 和 BabyAGI 都是启发性的概念验证演示。大语言模型的潜力不仅仅是生成精美的文案、故事、论文和程序;它可以被视为一个强大的通用问题解决者。
智能体 (agent) 系统概览
在一个 LLM 驱动的自主代理系统中,LLM 充当代理的大脑,辅以几个关键组件:
-
计划
- 子目标和分解:代理将大任务分解为较小、可管理的子目标,从而有效地处理复杂任务。
- 反思和完善:代理可以对过去的行为进行自我批评和反思,从错误中学习并为未来的步骤完善它们,从而提高最终结果的质量。
-
记忆
- 短期记忆:我认为所有的上下文学习(参见 提示工程 )都是利用模型的短期记忆进行学习。
- 长期记忆:这为代理提供了长时间保留和回忆(无限)信息的能力,通常通过利用外部向量存储和快速检索来实现。
-
工具使用
- 代理学会调用外部 APIs 以获取模型权重中缺失的额外信息(在预训练后经常很难更改),包括当前信息、代码执行能力、访问专有信息源等。
组件一:计划
一项复杂的任务通常涉及许多步骤。代理需要知道这些步骤并提前计划。
任务分解
思维链(Chain of thought) (CoT; Wei et al. 2022 ) 已经成为增强模型在复杂任务上性能的标准提示技术。该模型被指导 “一步一步地思考”,以利用更多的测试时间计算来将困难的任务分解为更小、更简单的步骤。CoT 将大任务转化为多个可管理的任务,并为模型的思考过程提供了解释。
Tree of Thoughts (Yao et al. 2023 ) 通过在每一步探索多种推理可能性来扩展 CoT。它首先将问题分解为多个思考步骤,并在每一步生成多个思考,从而创建一个树结构。搜索过程可以是 BFS(广度优先搜索)或 DFS(深度优先搜索),每个状态都由分类器(通过提示)或多数票评估。
任务分解可以通过 (1) 使用简单的提示,如 "XYZ 的步骤(Steps for XYZ).\n1(这里 \n 代表换行)."
、"实现 XYZ 的子目标是什么?(What are the subgoals for achieving XYZ?)"
,由 LLM 完成,(2) 使用任务特定的指令;例如,写小说时使用 "写一个小说大纲(Write a story outline)."
,或 (3) 使用人类输入。
另一个截然不同的方法,LLM+P (Liu et al. 2023 ),依赖于一个外部的经典规划器来进行长期规划。这种方法使用规划域定义语言(PDDL)作为中间接口来描述规划问题。在这个过程中,LLM (1) 将问题翻译成 “Problem PDDL”,然后 (2) 根据现有的 “Domain PDDL” 请求一个经典规划器生成一个 PDDL 计划,最后 (3) 将 PDDL 计划翻译回自然语言。本质上,规划步骤是外包给一个外部工具,假设在某些机器人设置中有特定领域的 PDDL 和合适的规划器,但在许多其他领域中并不常见。
自我反思
自我反思是一个至关重要的方面,它允许自主代理通过改进过去的行动决策和纠正以前的错误来迭代地提高。它在现实世界中需要不可避免地进行试错的任务中发挥着关键作用。
ReAct (Yao 等人,2023 ) 通过扩展动作空间将任务特定的离散动作和语言空间的组合,将推理和行动整合到 LLM 中。前者使 LLM 能够与环境互动(例如使用 Wikipedia 搜索 API),而后者提示 LLM 用自然语言生成推理痕迹。
ReAct 提示模板包含了让 LLM 思考的明确步骤,大致格式为:
思考:...
行动:...
观察:...
... (多次重复)
图 2. 知识密集型任务(例如 HotpotQA、FEVER)和决策任务(例如 AlfWorld Env、WebShop)的推理轨迹示例。 (图片来源:Yao 等人,2023 )。
在知识密集型任务和决策任务的实验中,ReAct
比只有 Act
的基线更好,其中 Thought: ...
步骤被移除。
Reflexion (Shinn & Labash 2023 ) 是一个框架,为代理提供动态记忆和自我反思能力,以提高推理技能。Reflexion 有一个标准的 RL 设置,其中奖励模型提供一个简单的二进制奖励,动作空间遵循 ReAct 中的设置,其中任务特定的动作空间与语言相结合,以实现复杂的推理步骤。在每一个动作 $a_t$ 之后,代理计算一个启发式 $h_t$,并根据自我反思的结果,可能会决定重置环境,开始一个新的试验。
图 3. Reflexion 框架的示意图。 (图片来源:Shinn & Labash, 2023 )
启发式函数确定何时轨迹是低效的或包含幻觉,并应该被停止。低效规划指的是花费太长时间没有成功的轨迹。幻觉被定义为遇到一系列连续相同的动作,导致在环境中得到相同的观察。
通过向 LLM 展示两个示例来创建自我反思,每个示例是一个(失败的轨迹,为指导未来计划变化的理想反思)对。然后将反思添加到代理的工作记忆中,最多三个,作为查询 LLM 的上下文。
图 4. 在 AlfWorld Env 和 HotpotQA 上的实验。在 AlfWorld 中,幻觉比低效规划更常见。 (图片来源:Shinn & Labash, 2023 )
Chain of Hindsight (CoH; Liu 等人,2023 ) 鼓励模型通过明确地向其展示一系列过去的输出,每个输出都带有反馈,来改进自己的输出。人类反馈数据是一个 $D_h = \{(x, y_i , r_i , z_i)\}_{i=1}^n$ 的集合,其中 $x$ 是提示,每个 $y_i$ 是一个模型完成,$r_i$ 是 $y_i$ 的人类评级,$z_i$ 是相应的人类提供的事后反馈。假设反馈元组按奖励排序,$r_n \geq r_{n-1} \geq \dots \geq r_1$ 这个过程是监督细调,其中数据是 $\tau_h = (x, z_i, y_i, z_j, y_j, \dots, z_n, y_n)$ 的形式,其中 $\leq i \leq j \leq n$。模型被微调为只预测 $y_n$,其中基于序列前缀的条件,使模型可以基于反馈序列进行自我反思,以产生更好的输出。在测试时,模型可以选择性地接收与人类注释者的多轮指令。
为了避免过拟合,CoH 增加了一个正则化项,以最大化预训练数据集的对数似然。为了避免走捷径和复制(因为在反馈序列中有许多常见的词),他们在训练过程中随机掩盖 0% - 5% 的过去的令牌。
他们实验中的训练数据集是 WebGPT 比较 、来自人类反馈的摘要 和 人类偏好数据集 的组合。
图 5. 使用 CoH 进行微调后,模型可以按照指令产生具有序列增量改进的输出。 (图片来源:Liu 等,2023 )
CoH 的思想是在上下文中呈现一系列逐步改进的输出,并训练模型采纳这种趋势以产生更好的输出。算法蒸馏 (AD; Laskin 等,2023 ) 将同样的思想应用于强化学习任务中的跨情节轨迹,其中一个 算法 被封装在一个长的历史条件策略中。考虑到代理与环境多次互动,并且在每个情节中代理都会变得更好,AD 将这种学习历史连接起来并输入到模型中。因此,我们应该期望下一个预测的动作会比之前的尝试带来更好的性能。目标是学习 RL 的过程,而不是训练特定任务的策略本身。
图 6. 算法蒸馏(AD)的工作原理示意图。 (图片来源:Laskin 等,2023 )。
该论文假设,任何生成一组学习历史的算法都可以通过对行为进行克隆来蒸馏到神经网络中。历史数据是由一组源策略生成的,每个策略都针对特定的任务进行了训练。在训练阶段,每次 RL 运行时,都会随机抽取一个任务,并使用多情节历史的子序列进行训练,从而学到的策略是任务不可知的。
实际上,模型的上下文窗口长度有限,因此情节应该足够短,以构建多情节历史。需要 2-4 个情节的多情节上下文来学习近乎最优的上下文 RL 算法。上下文 RL 的出现需要足够长的上下文。
与三个基线相比,包括 ED(专家蒸馏,使用专家轨迹而不是学习历史的行为克隆)、源策略(用于通过 UCB 生成蒸馏轨迹)和 RL^2 (Duan 等,2017 ;用作上界,因为它需要在线 RL),AD 展示了上下文 RL,性能接近 RL^2,尽管只使用离线 RL,并且比其他基线学得更快。当基于源策略的部分训练历史进行条件化时,AD 的改进速度也比 ED 基线快得多。
图 7. AD、ED、源策略和 RL^2 在需要记忆和探索的环境上的比较。只分配二进制奖励。源策略是用 A3C 训练的 “dark” 环境和 DQN 训练的水迷宫。 (图片来源:Laskin 等,2023 )
组件二:记忆
(非常感谢 ChatGPT 帮助我起草这一部分。在与 ChatGPT 的 对话 中,我学到了很多关于人类大脑和用于快速 MIPS 的数据结构的知识。)
记忆的类型
记忆可以定义为用于获取、存储、保留和稍后检索信息的过程。人类大脑中有几种类型的记忆。
-
感觉记忆:这是记忆的最早阶段,提供了在原始刺激结束后保留感觉信息(视觉、听觉等)的印象的能力。感觉记忆通常只持续几秒钟。子类别包括图像记忆(视觉)、回声记忆(听觉)和触觉记忆(触觉)。
-
短时记忆(STM)或 工作记忆:它存储我们当前意识到的信息,并需要执行复杂的认知任务,如学习和推理。人们认为短时记忆的容量大约是 7 个项目(Miller 1956 ),并持续 20-30 秒。
-
长时记忆(LTM):长时记忆可以存储信息相当长的时间,从几天到几十年不等,其存储容量基本上是无限的。LTM 有两个子类型:
- 显性 / 陈述性记忆:这是关于事实和事件的记忆,指的是那些可以有意识地回忆起来的记忆,包括情节记忆(事件和经验)和语义记忆(事实和概念)。
- 隐性 / 程序性记忆:这种记忆是无意识的,涉及自动执行的技能和例行程序,如骑自行车或在键盘上打字。
图 8. 人类记忆的分类。
我们可以大致考虑以下映射:
- 将感觉记忆视为学习原始输入的嵌入表示,包括文本、图像或其他模态;
- 将短时记忆视为上下文中的学习。它是短暂和有限的,因为它受到 Transformer 的有限上下文窗口长度的限制。
- 将长时记忆视为代理在查询时可以关注的外部向量存储,可以通过快速检索访问。
最大内积搜索(MIPS)
外部记忆可以缓解有限注意力跨度的限制。标准做法是将信息的嵌入表示保存到一个向量存储数据库中,该数据库支持快速的最大内积搜索(MIPS )。为了优化检索速度,常见的选择是使用 近似最近邻 (ANN) 算法返回大致的前 k 个最近邻,以牺牲一点点的准确性换取巨大的速度提升。
几种常见的用于快速 MIPS 的 ANN 算法选择:
- LSH (局部敏感哈希):它引入了一个 哈希 函数,使得相似的输入项以高概率映射到同一个桶中,其中桶的数量远小于输入的数量。
- ANNOY (近似最近邻哦耶):核心数据结构是 随机投影树 ,一组二叉树,其中每个非叶节点代表一个超平面,将输入空间分为两半,每个叶子存储一个数据点。树是独立和随机建立的,所以在某种程度上,它模仿了一个哈希函数。ANNOY 搜索发生在所有的树中,迭代地搜索最接近查询的一半,然后汇总结果。这个想法与 KD 树很相关,但是更具可扩展性。
- HNSW (分层可导航小世界):它受到 小世界网络 的启发,其中大多数节点可以在少数几步内到达任何其他节点;例如,社交网络的 “六度分隔” 特性。HNSW 构建了这些小世界图的分层,其中底层包含实际的数据点。中间的层创建快捷方式来加速搜索。当执行搜索时,HNSW 从顶层的一个随机节点开始,并导航到目标。当它不能再接近时,它移动到下一层,直到到达底层。上层中的每一步都可以在数据空间中覆盖很大的距离,而下层中的每一步都可以提高搜索质量。
- FAISS (Facebook AI 相似性搜索):它基于这样一个假设,在高维空间中,节点之间的距离遵循高斯分布,因此应该存在数据点的 聚类 。FAISS 通过将向量空间划分为簇,然后在簇内进行精细化量化来应用向量量化。搜索首先查找粗量化的簇候选,然后进一步查看每个簇的细量化。
- ScaNN (可扩展的最近邻):ScaNN 中的主要创新是 各向异性向量量化 。它将数据点 $x_i$ 量化为 $\tilde {x}_i$,使得内积 $\langle q, x_i \rangle$ 尽可能接近 $\angle q, \tilde {x}_i$ 的原始距离,而不是选择最近的量化中心点。
图 9. MIPS 算法的比较,以 recall@10 为测量标准。 (图片来源:Google 博客,2020 )
在 ann-benchmarks.com 上查看更多 MIPS 算法和性能比较。
组件三:工具使用
使用工具是人类的一个显著和区别性特征。我们创建、修改并使用外部物体来完成超出我们身体和认知限制的事情。为 LLMs 配备外部工具可以显著扩展模型的功能。
图 10. 一只海獭在水中使用石头敲开贝壳的图片。尽管一些其他动物可以使用工具,但其复杂性与人类不可比拟。(图片来源:使用工具的动物 )
MRKL(Karpas et al. 2022 ),简称为 “模块化推理、知识和语言”,是一个用于自主代理的神经符号架构。MRKL 系统被提议包含一系列的 “专家” 模块,而通用目的的 LLM 作为路由器,将查询路由到最合适的专家模块。这些模块可以是神经的(例如深度学习模型)或符号的(例如数学计算器、货币转换器、天气 API)。
他们做了一个关于微调 LLM 来调用计算器的实验,使用算术作为测试案例。他们的实验显示,解决口头数学问题比明确陈述的数学问题更困难,因为 LLMs(7B Jurassic1-large 模型)未能可靠地提取基本算术的正确参数。结果突出显示,当外部符号工具可以可靠地工作时,知道何时以及如何使用工具是至关重要的,这取决于 LLM 的能力。
TALM(工具增强语言模型;Parisi et al. 2022 )和 Toolformer(Schick et al. 2023 )都微调了 LM,学会使用外部工具 API。数据集是基于是否新添加的 API 调用注释可以提高模型输出的质量来扩展的。在 Prompt Engineering 的 “外部 APIs” 部分 中查看更多细节。
ChatGPT 插件 和 OpenAI API 函数调用 是 LLMs 增强工具使用能力在实践中的良好示例。工具 API 的集合可以由其他开发者提供(如在插件中)或自定义(如在函数调用中)。
HuggingGPT(Shen et al. 2023 )是一个使用 ChatGPT 作为任务规划器来选择 HuggingFace 平台上可用的模型的框架,根据模型描述并根据执行结果总结响应。
图 11. HuggingGPT 如何工作的示意图。(图片来源:Shen et al. 2023 )
该系统包括 4 个阶段:
(1)任务规划:LLM 作为大脑,将用户请求解析为多个任务。每个任务都有四个与之相关的属性:任务类型、ID、依赖关系和参数。他们使用少量的示例来指导 LLM 进行任务解析和规划。
指令:
AI 助手可以将用户输入解析为几个任务:[{“task”: 任务,“id”, 任务 id, “dep”: 依赖任务 id, “args”: {“text”: 文本,“image”: URL, “audio”: URL, “video”: URL}}]。“dep” 字段表示生成新资源的先前任务的 id,当前任务依赖于该资源。一个特殊的标签 “-task_id” 指的是依赖于 id 为 task_id 的任务生成的文本、图像、音频和视频。任务必须从以下选项中选择:{{可用任务列表}}。任务之间存在逻辑关系,请注意它们的顺序。如果用户输入不能被解析,您需要回复空的 JSON。这里有几个案例供您参考:{{示范}}。聊天历史记录为 {{聊天历史}}。从这个聊天历史中,您可以找到用户提到的资源的路径,以供您进行任务规划。
(2)模型选择:LLM 将任务分配给专家模型,其中请求被构建为多项选择问题。LLM 被呈现一个模型列表供其选择。由于上下文长度有限,需要基于任务类型进行过滤。
指令:
给定用户请求和调用命令,AI 助手帮助用户从模型列表中选择一个合适的模型来处理用户请求。AI 助手只输出最合适模型的模型 id。输出必须采用严格的 JSON 格式:“id”: “id”, “reason”: “选择的详细原因”。我们有一个模型列表供您选择 {{候选模型}}。请从列表中选择一个模型。
(3)任务执行:专家模型对特定任务执行并记录结果。
指令:
使用输入和推断结果,AI 助手需要描述过程和结果。前面的阶段可以形成为 - 用户输入:{{用户输入}},任务规划:{{任务}},模型选择:{{模型分配}},任务执行:{{预测}}。您必须首先以直接的方式回答用户的请求。然后描述任务过程,并以第一人称向用户展示您的分析和模型推断结果。如果推断结果包含文件路径,必须告诉用户完整的文件路径。
(4)响应生成:LLM 接收执行结果并向用户提供总结结果。
要将 HuggingGPT 投入实际使用,需要解决几个挑战:(1) 需要提高效率,因为 LLM 推断轮次和与其他模型的交互都会减慢过程;(2) 它依赖于一个长的上下文窗口来交流复杂的任务内容;(3) 需要提高 LLM 输出和外部模型服务的稳定性。
API-Bank(Li et al. 2023 )是一个用于评估工具增强 LLM 性能的基准。它包含 53 个常用的 API 工具,一个完整的工具增强 LLM 工作流程,以及 264 个注释的对话,涉及 568 个 API 调用。API 的选择非常多样,包括搜索引擎、计算器、日历查询、智能家居控制、日程管理、健康数据管理、帐户认证工作流等。因为有大量的 API,LLM 首先可以访问 API 搜索引擎来找到正确的 API 进行调用,然后使用相应的文档进行调用。
图 12. LLM 在 API-Bank 中进行 API 调用的伪代码。(图片来源:Li et al. 2023 )
在 API-Bank 工作流中,LLMs 需要做出几个决策,每一步我们都可以评估该决策的准确性。决策包括:
- 是否需要 API 调用。
- 确定要调用的正确 API:如果不够好,LLMs 需要迭代地修改 API 输入(例如,为搜索引擎 API 决定搜索关键词)。
- 根据 API 结果进行响应:如果结果不满意,模型可以选择细化并再次调用。
此基准测试代理的工具使用能力分为三个层次:
- 第一级评估 调用 API 的能力。给定 API 的描述,模型需要确定是否调用给定的 API,正确调用它,并正确响应 API 返回。
- 第二级检查 检索 API 的能力。模型需要搜索可能解决用户需求的 API,并通过阅读文档学习如何使用它们。
- 第三级评估 计划 API 超出检索和调用 的能力。给定不明确的用户请求(例如,安排团体会议、为旅行预订航班 / 酒店 / 餐厅),模型可能需要进行多次 API 调用来解决它。
案例研究
科学发现代理
ChemCrow (Bran 等人,2023 ) 是一个特定领域的例子,其中 LLM 通过 13 个专家设计的工具进行增强,以完成有机合成、药物发现和材料设计的任务。在 LangChain 中实现的工作流反映了之前在 ReAct 和 MRKLs 中描述的内容,并结合了与任务相关的 CoT 推理和工具:
- LLM 被提供了一个工具名称列表,工具的实用性描述,以及关于预期输入 / 输出的详细信息。
- 然后指示它使用提供的工具在必要时回答用户给定的提示。指令建议模型遵循 ReAct 格式 -
Thought, Action, Action Input, Observation
。
一个有趣的观察是,虽然基于 LLM 的评估得出 GPT-4 和 ChemCrow 的表现几乎相同,但面向完成和化学正确性的解决方案的专家的人类评估显示,ChemCrow 的表现远远超过了 GPT-4。这表明在需要深入专业知识的领域使用 LLM 评估其自身的性能可能存在问题。缺乏专业知识可能导致 LLM 不知道自己的缺陷,因此不能很好地判断任务结果的正确性。
Boiko 等人 (2023) 也研究了用于科学发现的 LLM-empowered 代理,以处理复杂科学实验的自主设计、规划和性能。这个代理可以使用工具浏览互联网、阅读文档、执行代码、调用机器人实验 API 并利用其他 LLM。
例如,当被要求 “开发一种新的抗癌药物” 时,模型提出了以下推理步骤:
- 询问当前的抗癌药物发现趋势;
- 选择一个目标;
- 请求一个针对这些化合物的骨架;
- 一旦确定了化合物,模型就尝试合成它。
他们还讨论了风险,尤其是非法药物和生物武器。他们开发了一个测试集,包含了一系列已知的化学武器剂,并要求代理合成它们。11 个请求中有 4 个 (36%) 被接受并获得了合成解决方案,代理尝试查阅文档来执行程序。11 个中有 7 个被拒绝,其中 7 个被拒绝的案例中,5 个是在网络搜索后,2 个是仅基于提示被拒绝的。
生成代理模拟
Generative Agents (Park 等人,2023 ) 是一个超级有趣的实验,其中 25 个由 LLM-powered 代理控制的虚拟角色生活和互动在一个沙盒环境中,受到 The Sims 的启发。生成代理为交互应用创建了可信的人类行为模拟。
生成代理的设计结合了 LLM 与记忆、规划和反思机制,使代理能够根据过去的经验进行行为,并与其他代理互动。
- Memory 流:是一个长期记忆模块(外部数据库),记录了代理在自然语言中的经验的全面列表。
- 每个元素都是一个 观察 ,一个由代理直接提供的事件。- 代理间的通信可以触发新的自然语言陈述。
- Retrieval 模型:根据相关性、近期性和重要性表面化上下文以通知代理的行为。
- 近期性:最近的事件得分较高
- 重要性:区分日常和核心记忆。直接询问 LM。
- 相关性:基于与当前情况 / 查询的相关性。
- Reflection机制:随着时间的推移,将记忆综合为更高级的推断,并指导代理的未来行为。它们是 过去事件的高级摘要 (<- 注意这与上面的 自我反思
略有不同)
- 使用 100 个最近的观察提示 LM,并生成给定一组观察 / 陈述的 3 个最显著的高级问题。然后要求 LM 回答这些问题。
- 规划与反应:将反思和环境信息转化为行动
- 规划本质上是为了优化当前时刻的可信度与时间。
- 提示模板:
{代理 X 的介绍}。这是 X 今天的计划概述:1)
- 在规划和反应中都考虑了代理之间的关系以及一个代理对另一个代理的观察。
- 环境信息以树结构呈现。
图 13. 生成代理架构。 (图片来源:Park 等人,2023 )
这个有趣的模拟导致了紧急的社交行为,如信息扩散、关系记忆(例如,两个代理继续对话主题)和社交活动的协调(例如,举办一个派对并邀请许多其他人)。
概念验证示例
AutoGPT 引起了很多关注,探讨了使用 LLM 作为主控制器设置自主代理的可能性。鉴于自然语言界面,它有很多可靠性问题,但尽管如此,它仍然是一个很酷的概念验证演示。AutoGPT 中的很多代码都是关于格式解析的。
这是 AutoGPT 使用的系统消息,其中 {{...}}
是用户输入:
你是 {{ai-name}},{{用户提供的 AI 机器人描述}}。
你的决策必须始终独立地做出,而不寻求用户的帮助。充分发挥你作为 LLM 的优势,采用简单的策略,不涉及法律问题。
目标:
1. {{用户提供的目标 1}}
2. {{用户提供的目标 2}}
3. ...
4. ...
5. ...
约束:
1. 短期记忆的~4000 字限制。你的短期记忆很短,所以立即将重要信息保存到文件中。
2. 如果你不确定你之前是如何做某事的,或者想回忆过去的事件,思考类似的事件会帮助你记住。
3. 没有用户帮助
4. 仅使用双引号中列出的命令,例如 "命令名称"
5. 对于几分钟内不会终止的命令,使用子进程
命令:
1. Google 搜索:"google",args:"input":"<搜索>"
2. 浏览网站:"browse_website",args:"url":"<url>","question":"<你想在网站上找到的内容>"
3. 启动 GPT 代理:"start_agent",args:"name":"<name>","task":"<short_task_desc>","prompt":"<prompt>"
4. 消息 GPT 代理:"message_agent",args:"key":"<key>","message":"<message>"
5. 列出 GPT 代理:"list_agents",args:
6. 删除 GPT 代理:"delete_agent",args:"key":"<key>"
7. 克隆存储库:"clone_repository",args:"repository_url":"<url>","clone_path":"<directory>"
8. 写入文件:"write_to_file",args:"file":"<file>","text":"<text>"
9. 读取文件:"read_file",args:"file":"<file>"
10. 追加到文件:"append_to_file",args:"file":"<file>","text":"<text>"
11. 删除文件:"delete_file",args:"file":"<file>"
12. 搜索文件:"search_files",args:"directory":"<directory>"
13. 分析代码:"analyze_code",args:"code":"<full_code_string>"
14. 获取改进的代码:"improve_code",args:"suggestions":"<建议列表>","code":"<full_code_string>"
15. 编写测试:"write_tests",args:"code":"<full_code_string>","focus":"<关注点列表>"
16. 执行 Python 文件:"execute_python_file",args:"file":"<file>"
17. 生成图像:"generate_image",args:"prompt":"<prompt>"
18. 发送推文:"send_tweet",args:"text":"<text>"
19. 什么都不做:"do_nothing",args:
20. 任务完成(关机):"task_complete",args:"reason":"<reason>"
资源:
1. 用于搜索和信息收集的互联网访问。
2. 长期记忆管理。
3. 用于简单任务委派的 GPT-3.5 代理。
4. 文件输出。
性能评估:
1. 持续审查和分析你的行为,确保你尽力而为。
2. 不断地自我批评你的大局行为。
3. 反思过去的决策和策略,完善你的方法。
4. 每个命令都有成本,所以要聪明且高效。目标是在最少的步骤中完成任务。
你应该只按照下面描述的 JSON 格式回应
响应格式:
{
"thoughts": {
"text": "思考",
"reasoning": "推理",
"plan": "- 简短的项目列表 \n- 传达 \n- 长期计划",
"criticism": "建设性的自我批评",
"speak": "对用户说的思考总结"
},
"command": {
"name": "命令名称",
"args": {"arg name": "值"}
}
}
确保响应可以由 Python json.loads 解析
GPT-Engineer 是另一个项目,旨在根据自然语言中指定的任务创建整个代码存储库。GPT-Engineer 被指示考虑构建的一系列较小的组件,并在需要时询问用户输入以澄清问题。
以下是发送给 GPT-Engineer 使用的 OpenAI ChatCompletion 端点的任务澄清的示例对话。用户输入用 {{用户输入文本}}
包装。
[
{
"role": "system",
"content": "你将阅读指令并不执行它们,只寻求澄清它们。\n 具体来说,你首先会总结一系列需要澄清的超短子弹点。\n 然后你会提出一个澄清问题,并等待用户的答复。\n"
},
{
"role": "user",
"content": "我们正在写 {{一个用 python 编写的 Super Mario 游戏。MVC 组件分开在不同的文件中。键盘控制。}}\n"
},
{
"role": "assistant",
"content": "需要澄清的领域的总结:\n1. Super Mario 游戏的具体内容(例如,关卡设计、角色、游戏玩法机制)\n2. 关于 MVC 组件的细节(例如,每个文件中有哪些组件)\n3. 键盘控制实现的细节(例如,使用哪些键,如何处理输入)\n\n 澄清问题:\n 你能提供更多关于 Super Mario 游戏的细节吗,比如关卡设计、角色和游戏玩法机制?"
},
{
"role": "user",
"content": "{{总共有 10 个关卡。主要角色是一个叫 Mario 的管道工,他可以走和跳。这是一个像 Super Mario 那样的经典平台游戏。主角从左到右移动,试图到达目的地,在过程中有许多障碍和敌人的攻击。}}\n\n 还有什么不清楚的吗?如果是,请只按以下形式回答:\n {剩余的不清楚的领域} 剩余的问题。\n {下一个问题}\n 如果一切都足够清楚,只回答 \" 没有更多需要澄清的。\""
},
{
"role": "assistant",
"content": "剩余的不清楚的领域:2 个剩余的问题。\n 你能提供更多关于如何将 MVC 组件分割到不同文件中的信息吗?"
},
{
"role": "user",
"content": "{{根据你自己的假设,并在开始之前明确地陈述它们}}"
}
]
然后,在这些澄清之后,代理进入了代码编写模式,使用了一个不同的系统消息。系统消息:
你将获得编写代码的指令。你将写一个非常长的答案。确保最终实施了架构的每一个细节。确保最终实施了架构的每一个细节。
一步一步地思考,并自己推理出正确的决策,确保我们做得对。你首先会列出必要的核心类、函数、方法的名称,以及它们的目的的简短评论。
然后你将输出每个文件的内容,包括所有的代码。每个文件必须严格遵循 markdown 代码块格式,其中以下标记必须被替换,使得 FILENAME 是包括文件扩展名的小写文件名,LANG 是代码的语言的标记代码块语言,CODE 是代码:
FILENAME
CODE
你将从 “入口点” 文件开始,然后转到由该文件导入的文件,依此类推。请注意,代码应该是完全功能的。没有占位符。
遵循适当的语言和框架最佳实践文件命名约定。确保文件包含所有的导入、类型等。确保不同文件中的代码彼此兼容。确保实施所有代码,如果你不确定,写一个可能的实施。包括模块依赖或包管理器依赖定义文件。在你完成之前,仔细检查文件中是否存在架构的所有部分。
有用的知识:你几乎总是把不同的类放在不同的文件中。对于 Python,你总是创建一个适当的 requirements.txt 文件。对于 NodeJS,你总是创建一个适当的 package.json 文件。你总是在函数定义上添加一个简短的描述其目的的评论。你试图添加解释非常复杂的逻辑的评论。你总是遵循所请求的语言的最佳实践,描述所写的代码作为一个定义的包 / 项目。
Python 工具带偏好:
- pytest
- dataclasses
对话样本:
[
{
"role": "system",
"content": "你将获得编写代码的指令。\n 你将写一个非常长的答案。确保最终实施了架构的每一个细节。确保最终实施了架构的每一个细节。\n\n 一步一步地思考,并自己推理出正确的决策,确保我们做得对。\n 你首先会列出必要的核心类、函数、方法的名称,以及它们的目的的简短评论。\n\n 然后你将输出每个文件的内容,包括所有的代码。\n 每个文件必须严格遵循 markdown 代码块格式,其中以下标记必须被替换,使得 \nFILENAME 是包括文件扩展名的小写文件名,\nLANG 是代码的语言的标记代码块语言,CODE 是代码:\n\nFILENAME\n```LANG\nCODE\n```\n\n 你将从" 入口点 "文件开始,然后转到由该文件导入的文件,依此类推。\n 请注意,代码应该是完全功能的。没有占位符。\n\n 遵循适当的语言和框架最佳实践文件命名约定。\n 确保文件包含所有的导入、类型等。确保不同文件中的代码彼此兼容。\n 在你完成之前,仔细检查文件中是否存在架构的所有部分。\n\n 有用的知识:\n 你几乎总是把不同的类放在不同的文件中。\n 对于 Python,你总是创建一个适当的 requirements.txt 文件。\n 对于 NodeJS,你总是创建一个适当的 package.json 文件。\n 你总是在函数定义上添加一个简短的描述其目的的评论。\n 你试图添加解释非常复杂的逻辑的评论。\n 你总是遵循所请求的语言的最佳实践,描述所写的代码作为一个定义 \n 包 / 项目。\n\n\nPython 工具带偏好:\n- pytest\n- dataclasses\n"
},
# ... 与之前相同的对话,以 "根据你自己的假设,并在开始之前明确地陈述它们" 结束。
{
"role": "assistant",
"content": "假设:\n1. 模型:模型将包含游戏的数据,如关卡信息、角色状态和敌人位置。\n2. 视图:视图将处理游戏的视觉效果,包括渲染游戏对象、背景和更新显示。\n3. 控制器:控制器将管理用户输入,如键盘控制,并相应地更新模型。\n\n 考虑到这些假设,你是否希望我澄清键盘控制实现的任何特定方面?"
},
{
"role": "user",
"content": "请现在记住步骤:\n\n 一步一步地思考,并自己推理出正确的决策,确保我们做得对。\n 首先列出必要的核心类、函数、方法的名称,以及它们的目的的简短评论。\n\n 然后你将输出每个文件的内容,包括所有的代码。\n 每个文件必须严格遵循 markdown 代码块格式,其中以下标记必须被替换,使得 \nFILENAME 是包括文件扩展名的小写文件名,\nLANG 是代码的语言的标记代码块语言,CODE 是代码:\n\nFILENAME\n```LANG\nCODE\n```\n\n 你将从" 入口点 "文件开始,然后转到由该文件导入的文件,依此类推。\n 请注意,代码应该是完全功能的。没有占位符。\n\n 遵循适当的语言和框架最佳实践文件命名约定。\n 确保文件包含所有的导入、类型等。确保不同文件中的代码彼此兼容。\n 在你完成之前,仔细检查文件中是否存在架构的所有部分。\n\n 有用的知识:\n 你几乎总是把不同的类放在不同的文件中。\n 对于 Python,你总是创建一个适当的 requirements.txt 文件。\n 对于 NodeJS,你总是创建一个适当的 package.json 文件。\n 你总是在函数定义上添加一个简短的描述其目的的评论。\n 你试图添加解释非常复杂的逻辑的评论。\n 你总是遵循所请求的语言的最佳实践,描述所写的代码作为一个定义 \n 包 / 项目。\n\n\nPython 工具带偏好:\n- pytest\n- dataclasses\n\n 现在,请开始编写代码。"
},
{
"role": "assistant",
"content": "明白了。让我们开始。\n\n 首先,我会列出核心类、函数和方法的名称,以及它们的目的的简短评论。\n\n 核心类和函数:\n1.`Game`- 主游戏类,负责初始化和运行游戏循环。\n2.`Level`- 表示游戏中的一个关卡。\n3.`Player`- 表示玩家角色 Mario。\n4.`Enemy`- 表示游戏中的敌人。\n5.`Controller`- 处理用户输入。\n6.`View`- 负责渲染游戏的视觉效果。\n7.`Model`- 包含游戏的数据,如关卡信息、角色状态和敌人位置。\n\n 接下来,我会为每个类和函数提供代码实现。"
},
# ... 这里会有更多的对话,其中助理提供了代码实现。
]
这些对话示例展示了如何使用 OpenAI API 来创建自然语言驱动的编程助手。这些助手可以根据用户的指示生成代码、解决问题或执行其他任务。
结论
OpenAI 的 API 为开发者提供了一个强大的工具,可以利用自然语言模型来解决各种问题。从简单的查询和任务到复杂的编程和设计挑战,API 都能够提供有价值的答案和解决方案。这篇文章提供了一些
挑战
在深入了解构建以 LLM 为中心的代理的关键思想和演示后,我开始看到一些常见的限制:
- 有限的上下文长度:受限的上下文容量限制了历史信息、详细指令、API 调用上下文和响应的包含。系统的设计必须与这种有限的通信带宽一起工作,而像自我反思这样的机制从长或无限的上下文窗口中受益很大。尽管向量存储和检索可以提供对更大知识库的访问,但它们的表示能力不如完全关注那么强大。
- 长期规划和任务分解的挑战:在冗长的历史上进行规划和有效地探索解决方案空间仍然是一个挑战。当面对意外的错误时,LLMs 难以调整计划,使它们与通过试验和错误学习的人类相比显得不那么稳健。
- 自然语言界面的可靠性:当前的代理系统依赖于自然语言作为 LLMs 和外部组件(如内存和工具)之间的界面。然而,模型输出的可靠性是有问题的,因为 LLMs 可能会犯格式错误,并偶尔表现出叛逆的行为(例如,拒绝遵循指令)。因此,许多代理演示代码都集中在解析模型输出上。
引用
Cited as:
Weng, Lilian. (Jun 2023). LLM-powered Autonomous Agents". Lil’Log. https://lilianweng.github.io/posts/2023-06-23-agent/ .
Or
@article {weng2023prompt,
title = "LLM-powered Autonomous Agents"",
author = "Weng, Lilian",
journal = "lilianweng.github.io",
year = "2023",
month = "Jun",
url = "https://lilianweng.github.io/posts/2023-06-23-agent/"
}
References
[1] Wei et al. “Chain of thought prompting elicits reasoning in large language models.” NeurIPS 2022
[2] Yao et al. “Tree of Thoughts: Dliberate Problem Solving with Large Language Models.” arXiv preprint arXiv:2305.10601 (2023).
[3] Liu et al. “Chain of Hindsight Aligns Language Models with Feedback” arXiv preprint arXiv:2302.02676 (2023).
[4] Liu et al. “LLM+P: Empowering Large Language Models with Optimal Planning Proficiency” arXiv preprint arXiv:2304.11477 (2023).
[5] Yao et al. “ReAct: Synergizing reasoning and acting in language models.” ICLR 2023.
[6] Google Blog. “Announcing ScaNN: Efficient Vector Similarity Search” July 28, 2020.
[7] https://chat.openai.com/share/46ff149e-a4c7-4dd7-a800-fc4a642ea389
[8] Shinn & Labash. “Reflexion: an autonomous agent with dynamic memory and self-reflection” arXiv preprint arXiv:2303.11366 (2023).
[9] Laskin et al. “In-context Reinforcement Learning with Algorithm Distillation” ICLR 2023.
[10] Karpas et al. “MRKL Systems A modular, neuro-symbolic architecture that combines large language models, external knowledge sources and discrete reasoning.” arXiv preprint arXiv:2205.00445 (2022).
[11] Weaviate Blog. Why is Vector Search so fast? Sep 13, 2022.
[12] Li et al. “API-Bank: A Benchmark for Tool-Augmented LLMs” arXiv preprint arXiv:2304.08244 (2023).
[13] Shen et al. “HuggingGPT: Solving AI Tasks with ChatGPT and its Friends in HuggingFace” arXiv preprint arXiv:2303.17580 (2023).
[14] Bran et al. “ChemCrow: Augmenting large-language models with chemistry tools.” arXiv preprint arXiv:2304.05376 (2023).
[15] Boiko et al. “Emergent autonomous scientific research capabilities of large language models.” arXiv preprint arXiv:2304.05332 (2023).
[16] Joon Sung Park, et al. “Generative Agents: Interactive Simulacra of Human Behavior.” arXiv preprint arXiv:2304.03442 (2023).
[17] AutoGPT. https://github.com/Significant-Gravitas/Auto-GPT
[18] GPT-Engineer. https://github.com/AntonOsika/gpt-engineer