- Transformer基础
- 常用的大语言模型
- 行业大语言模型
- 大语言模型评估
- 数据构造与清洗
- 分词器构造
- 大语言模型的微调
- 基于PEFT的LLaMA模型微调
- 基于人类反馈的强化学习框架
- 前沿偏好对齐方法
- 基于DPO的偏好对齐实战
- GPTs初体验
- GPTs的初阶使用
- GPTs的高阶使用
- 公开数据集
- 主流方法
- Text2SQL任务实战
- 角色扮演
- 角色扮演实战测试
- 基于Baichuan的角色扮演模型微调
- 对话要素抽取
- 对话要素抽取实战测试
- 基于Qwen的对话要素抽取模型微调
- Agent概述
- Agent的主要模块
- Agent的行为决策机制
- 主流Agent框架
- 基于知识库问答
- 向量数据库
- 基于知识库的大型语言模型问答实战
- AutoGPT概述
- LangChain概述
- 使用LangChain构建AutoGPT
- 运行AutoGPT
截止到2023年12月,AutoGPT项目已经获得了开源社区GitHub的15.4万以上的星标,成为开发者社区炙手可热的LLM工具框架。它的核心吸引力在于开发者能够使用它创建一系列功能各异的自主代理,这些代理被广泛展示在其Arena平台上。那么,是什么驱动了AutoGPT的发展,使其成为构建自主代理的理想选择呢?下面通过使用LangChain框架构建一个AutoGPT代理,我们可以更深入地理解这个代理工具背后的原理。
1. 构建
在使用LangChain构建AutoGPT代理的过程中,首先需要定义代理的目标和任务。接下来,通过编程实现这些任务,将它们转化为可由AutoGPT理解和执行的格式。
Lang-Chain框架提供了一套工具和API,以便开发者可以轻松地与LLM及其他外部服务进行交互。
为了使用智能Agent,需要一个LLM的API密钥。本节将介绍如何获取OpenAI的API密钥。
1)访问OpenAI官网,并在屏幕右上角单击用户名。
·单击“查看API密钥”。
·单击“创建新的密钥”。
·输入一个合适的名称,然后单击“创建密钥”以获得密钥。
2)为了使用Google搜索API,需要获取一个Google搜索API的密钥。获取步骤如下:
·访问Google Cloud平台并登录Google账户。
·在Google Cloud控制台中创建一个新的项目。
·在项目中导航到“API和服务”部分。
·搜索“Google搜索API”,并启用此API。
·在API页面单击“创建凭证”以生成新的API密钥。
·保存这个密钥以供API调用使用。
3)创建一个名为autogpt_langchain的文件夹,内部的文件结构如下。
·agent.py:用于构建AutoGPT类。
·output_parser.py:用于解析响应结果。
·prompt_generator.py:用于生成提示词模板的组合。
·prompt.py:用于提示词模板的构建。
·weather_agent.py:用于运行一个具有搜索和读写文件的天气报告Agent。
2. 规划和任务分解
要实现AutoGPT的规划和任务分解功能,最重要的是掌握AutoGPT的提示词模板技术。
AutoGPT通过构建提示词模板来指导LLM规划和任务分解。构建方法遵循的是“思维链”(Chain of Thought,CoT)和“思维树”(Tree of Thought,ToT)原则,它要求模型逐步思考,从而鼓励它利用自身的推理能力进行有效的规划。
虽然AutoGPT并不直接利用像LLM+P这样的外部规划器,但它对于明确目标设定和工具整合的重视,可以被看作在LLM中实现更强大规划能力的一个重要步骤。通过这种方式,AutoGPT不仅为Agent提供了明确的指导,还帮助它将复杂的任务分解为更简单、更具可操作性的小步骤,从而推进任务的完成。
1)规划提示词模板,选择提示词策略为思维链,要求模型逐步思考后做目标规划和任务分解。我们先创建一个新的文件,名为prompt.py,专门用于为AutoGPT类生成提示词模板。在这个文件中,我们可以先导入所需要的类和方法,然后定义一个名为AutoGPT-Prompt的类,它负责管理和处理与AutoGPT相关的提示词模板和方法,该类的属性值ai_role、ai_name和tools用于设置Agent的角色、名称和配置的工具能力。
这个AutoGPTPrompt类继承自LangChain的BaseChatPromptTemplate和BaseModel。
在这个类中,我们首先定义一个名为construct_full_prompt的方法。这个方法的主要目的是构建完整的提示词,它将根据特定的目标和工具设定明确的方向和步骤,为AutoGPT类生成适当的提示词模板,类似于我们在写文章时确定主题。接着,它根据任务的目标,细化出需要完成的各个具体步骤,就像我们在写作时列出搜集资料、撰写提纲等小任务一样。
当我们调用这个方法,并传入一系列的目标(goals)时,它可以生成如下结构的提示词模板:
这个提示词模板清楚地定义了Agent的角色和名称,并提出了一些基本的操作指南,例如独立做决策和采取简单的策略等。紧接着,它列出了Agent需要完成的具体目标和可用工具的清单。这种有结构的提示方式不仅有助于Agent更好地理解其任务,还能够通过明确地列出步骤和工具,帮助Agent有效地规划和执行任务。
2)根据这个提示词模板的设计思路来实现construct_full_prompt方法:
在函数内部使用了一个get_prompt方法。该方法的目的是为Agent所配置的工具(tools)构建一个包含多种工具约束、命令、资源和性能评估的提示词模板。这个方法在LangChain框架中已经有实现代码,我们可以直接导入。
3)将提示词转化为符合LLM聊天模型要求的消息列表格式。为此,我们创建一个名为format_messages的函数。这个函数将处理多种输入,并将它们格式化为LLM的聊天模型端口要求的信息列表格式(包含System、AI、User三种角色信息的列表)。
首先实现的功能是初始化基础提示词和时间信息,也就是利用
construct_full_prompt函数和提供的目标,创建一个base_prompt信息,为Agent设定总体背景和目标。接着,创建一个time_prompt信息,告知Agent当前的时间和日期。
在完成以上步骤之后,程序具备了为LLM创建提示词模板以及处理LLM响应的能力。这些步骤都是为了准备Agent的运行环境。通过construct_full_prompt函数构造一个名为AutoGPTPrompt的类,该类能够利用LLM的理解和分析能力来生成高效的提示词模板。接着,format_messages函数用于将LLM的输入转换成聊天模型所需的消息
列表格式。最后解析LLM的输出响应。这整个流程为Agent的顺利运行提供了必要的技术支持和基础设施。
在整个AutoGPT的开发过程中,深入理解和掌握提示词模板技术至关重要。通过精心设计的提示词模板,AutoGPT可以自主地分析和解决问题,这体现了其在任务处理和决策制定上的自足性和独立性。
3. 输出解析
将LLM的输出(通常是字符串形式)转换为代码或可执行命令是一项复杂的任务。这涉及开发结构化的响应和输出解析器,这些解析器能够处理并修正输出中的错误,这对于将代理的决策转化为具体行动至关重要。
鉴于输出解析的重要性,同时为了构造AutoGPT专属的提示词模板,我们必须规范LLM的输出,否则AutoGPT程序很容易遇到错误并中断。由于我们设计的AutoGPT是自主型的,没有人类用户的参与,如果无法将语言模型的输出解析为程序能够理解的语言,它就无法完成自主运行的任务。在提示词中,我们要求LLM的响应格式按照以下提示词模板进行输出。
为了更高效地将AI生成的文本转换为实际的操作命令,我们需要定制一个LLM模型 输出器AutoGPTOutputParser类。该类旨在解析由AutoGPT生成的输出文本,并将其转换为可操作的命令。其关键功能如下。
1)初始化设置和导入模块。新建一个output_parser.py文件,导入所需的模块,数据格式使用的是LangChain框架已经封装好的BaseOutputParser。定义一个AutoGPTAction类,用于约定解析的数据格式。再定义一个
BaseAutoGPTOutputParser类,该类继承了LangChain框架的输出解析BaseOutputParser类,并且写入parse方法拓展该输出解析的功能。
2)新建preprocess_json_input函数。通常在尝试解析JSON时,特别是在处理由LLM(如GPT-3或GPT-4)生成的输出时,这些输出可能包含不规则的反斜杠,从而导致标准JSON解析器无法正确解析。编写一个函数处理这样的问题,可以增加将文本成功解析为JSON的可能性,从而增强代码的可靠性。
3)异常处理。定义AutoGPTOutputParser类,该类最重要的方法是parse,parse方法内首先尝试将文本内容解析为JSON对象。若此过程失败,它会对文本进行预处理后再次尝试解析。这一步骤体现了处理LLM输出的不确定性,因为输出可能不总是有效的JSON格式。
4)命令提取。这是parse方法的第二个任务,如果成功解析出JSON对象,代码则尝试从中提取命令名称和参数。这与AutoGPT的预期输出格式相符,即假定LLM的输出是一个包含命令名称和参数的JSON对象。
5)错误处理。如果无法解析命令,或者命令结构不完整(例如缺少关键组成部分或类型错误),代码将生成一个标记错误的AutoGPTAction对象。这一机制确保了在解析失败的情况下,能够妥善处理错误,避免程序中断。
LangChain框架已经实现了具备上述功能的AutoGPTOutputParser类,我们可以直接从框架中导入这个类。这样一来,开发者在使用AutoGPT时就无须从头编写复杂的输出解析逻辑,而是可以直接利用LangChain提供的这个类来处理和解析LLM的输出。
4. 程序的核心AutoGPT类
当我们的Agent拥有了规划和任务分解的提示词模板后,下一步是将这些提示词发送给LLM,然后通过输出解析器解析结果,并执行任务。为此,我们需要定义AutoGPT类。实例化这个类后,我们能够创建一个基于LLM模型的对话Agent,这个Agent能够根据用户输入和目标执行各种任务。
首先创建Agent.py文件,然后定义AutoGPT类,AutoGPT类是程序的核心,我们按照以下步骤,实现AutoGPT类。
(1)定义类
写清楚AutoGPT类的用途是使用AutoGPT交互的Agent类。
(2)定义属性
AutoGPT类拥有多个关键属性,包括代理名称(ai_name)、记忆组件(memory)、LLM链组件(chain)、输出解析器组件(output_parser)、可用工具列表 (tools)、反馈工具(feedback_tool)和聊天历史记录存储(chat_history_memory)。构造函数接受这些属性作为参数,以实现定制化的代理配置。
(3)定义run()方法
AutoGPT类的核心功能体现在其run()方法中,该方法负责代理的运行逻辑。当调用run()方法时,它接受目标列表和初始用户输入作为参数。该函数的逻辑比较复杂,可以拆解成以下几个步骤进行解释:
1)初始化。将用户初始输入设置为“Determine which next command to use,and respond using the format specified above:”,设置循环计数器loop_count为0。
2)交互循环。进入一个无限循环,直到遇到终止条件为止。在循环体内执行以下操作:向LLM发送当前目标、消息、记忆以及用户输入,并获取回复;将回复存储在assistant_reply变量中;打印LLM的回复;将LLM的回复和用户输入分别添加到聊天历史记录中。
3)交互循环内部,解析结果,并进行不同的处理。使用输出解析器output_parser解析存储了LLM回复的助手回复assistant_reply,提取命令名称和参数,并存储在action变量中。
·如果命令名称是FINISH_NAME,则表示用户想要结束对话,返回LLM回复中action变量的response的值。
·如果命令名称存在于可用tools列表中,则传递action.args参数且使用该工具执行run命令,并获取结果,并将结果存储在observation变量中。
·如果命令名称是ERROR,则表示LLM遇到错误,解析错误信息并打印。
·如果命令名称未知,则提示用户使用指定格式的命令。
4)在交互循环内部,对程序是否循环做判断,并且更新程序的记忆组件。
首先是添加反馈到记忆组件中,将反馈工具获取到的反馈信息添加到memory_to_add变量中。
其次是添加一个新文档保存到记忆组件中。该文档包含存储在memory_to_add变量中的信息,也就意味着该文档的信息包括助手回复、上一次操作的结果和用户反馈(如果有)。通过将此文档添加到记忆组件中,代理可以稍后在对话中访问它。这允许代理更好地理解上下文并对未来的用户输入做出更准确的响应。
最后是添加系统消息到聊天历史记忆组件中。该消息chat_history_memory包含上一次操作的结果。result变量是action变量的子属性,它包含命令执行的结果。具体来说,result可以是以下3种类型:命令执行输出、错误消息、未知命令提示。这可以帮助用户跟踪对话的进展,并了解命令执行的结果。
总之,添加记忆组件的作用是确保代理的记忆和聊天历史记录在每次用户交互后都更新了相关信息。这允许代理从过去的对话中学习并为未来的用户输入提供更好的响应。
最后,创建from_llm_and_tools方法用于创建一个AutoGPT实例,并返回该实例。
AutoGPTPrompt实例用于为LLM提供提示,其中包括Agent的名称、角色、可用工具列表以及一些输入变量。LLMChain实例将LLM与AutoGPTPrompt连接起来,并负责将用户输入传递给LLM模型,并返回LLM的回复。HumanInputRun实例用于在每次用户交互之后收集用户的反馈。
5. 工具能力配置
在构建AutoGPT类的代理时,选择并设置合适的工具是至关重要的。Agent通过使用一系列特定的工具来执行任务。这些工具的种类繁多,包括网络搜索工具SerpAPIWrapper、文件管理工具WriteFileTool和ReadFileTool等。代理通过配置这些工具,能够高效地处理各种数据和请求。LangChain框架提供了大量常用的工具和工具包组件,使得代理的构建和运行更加高效和灵活。
例如,以下是一些在LangChain框架中可用的常见工具及其配置方法:
在这个代码示例中,我们定义了3种工具:一个用于网络搜索的工具
(SerpAPIWrapper),一个用于写文件的工具(WriteFileTool)和一个用于读文件的工具(ReadFileTool)。在实例化AutoGPT类时,设置tools属性为代码中的tools列表,可以完成工具的配置。通过这样的配置,代理可以在执行任务时灵活地使用这些工具,例如搜索网络上的信息、读取和写入文件等。因此,为AutoGPT代理准备一套有效的工具集合是实现其高效运行的关键。这些工具不仅增强了代理的功能性,也为其在处理复杂任务时提供了必要的支持。
工具配置会对Agent的行为产生重大影响,主要体现在以下几个方面:
1)工具信息整合。这个特点主要表现在之前定义的construct_full_prompt函数上,该函数调用get_prompt(self.tools),生成基于可用工具的提示词部分。这一部分可能提供了特定于每个工具的指示或信息,指导Agent如何有效地使用它们。
2)命令识别和执行。Agent在调用运行函数run()时,接收其响应并进行解析。
这个过程识别出Agent提到的命令名称和参数。如果命令名称与tools中的工具名称匹配,则检索相应的工具对象。
3)任务委派和观察。tools工具对象根据解析出的参数运行指定动作。这可能涉及各种功能,取决于工具的目的,例如访问外部资源、执行计算或与外部系统交互。
工具执行的结果会生成一个观察结果,并被纳入对话历史。这个观察结果可以是通过搜索引擎检索到的数据,也可以是工具执行的特定任务的结果。工具执行的结果(observation)被捕获并记录在对话历史中。这个观察结果可以是从搜索引擎获取的信息、执行特定任务的结果,也可以是任何其他由工具产生的输出。通过记录这些观察结果,AutoGPT类能够跟踪Agent的行为和决策过程,提供关于执行任务的实时反馈。我们可以在命令识别和执行的代码片段中增加观察的逻辑,正常情况下,observation变量存储的是工具执行的结果;其他异常情况下,则需要分多种错误类型进行处理,例如参数验证失败(ValidationError)或其他类型的错误。
这时,代码会捕获异常并生成相应的观察结果,这有助于后续的错误处理和调试。最后将观察的结果通过f-string拼接为result变量。
3)动态指导和适应性。工具本质上为Agent提供了一套功能,可以利用这些功能来实现其目标。与每个工具相关的提示词模板和指令指导Agent的决策制定和动作选择。通过融合工具执行的反馈和观察结果,Agent可以适应其方法并完善其策略,从而可以更有效地完成任务。
通过给Agent配置工具能力,赋予了Agent做出明智决策、执行多样化动作并从经验中学习的能力,最终呈现出了一个更加灵活和智能的系统。
6. 为Agent配置记忆
在探讨如何为Agent配置记忆能力之前,让我们先了解一些关于人类大脑中记忆类型的背景知识。这有助于我们更好地理解为何这种类型的记忆对于增强AutoGPT的能力是必要的。
人类大脑的记忆可以分为感官记忆、短期记忆和长期记忆几种类型。感官记忆是对感觉信息的短暂保留,例如视觉(图标记忆)、听觉(回声记忆)和触觉(触觉记忆)。短期记忆或工作记忆则存储当前我们意识到的信息,它对于执行复杂的认知任务如学习和推理至关重要。长期记忆则可以长时间存储信息,分为显性/陈述性记忆(关于事实和事件的记忆,可以有意识地回忆)和隐性/程序性记忆(涉及自动执行的技能和例程,如骑自行车或打字)。
理解这些记忆类型如何在大脑中工作,可以帮助我们更好地设计和实现AutoGPT的记忆系统。在AutoGPT中,我们通常会尝试模拟这些记忆类型,以增强Agent的记忆能力。
1)模拟长期记忆。AutoGPT依靠AutoGPTMemory类实现长期记忆功能。AutoGPTMemory类中的retriever属性是LangChain框架提供的一个VectorStoreRetriever对象。这个对象连接到一个向量存储服务,该服务用于存储和检索信息。这种类型的存储通常用于模拟长期记忆。在人类的长期记忆中,信息可以存储很长时间,并且容量几乎是无限的。在AutoGPTMemory类中,模拟长期记忆是通过使用向量存储来实现的,向量存储能够保存大量的信息,并且可以快速检索这些信息。当需要查询
与聊天相关的信息时,retriever通过检索与当前查询相关的文档,模拟了长期记忆中的信息检索过程。
2)模拟短期记忆。短期记忆(或工作记忆)是指当前我们意识到并需要处理的信息。在AutoGPTMemory类中,模拟短期记忆是通过维护聊天历史的最后几条消息来实现的。例如AutoGPT以返回最近的10条历史消息来实现短期记忆。而且短期记忆的内容需要不断更新,以反映最新的交互和上下文。
综上所述,AutoGPT通过其向量存储和聊天历史管理机制,有效地模拟了人类的长期记忆和短期记忆。长期记忆的模拟使得模型能够访问和利用大量的历史信息,而短期记忆的模拟使得模型能够有效地处理当前的交互和上下文。这两种记忆的结合使得AutoGPT成为一个拥有短期和长期记忆的Agent,提高了AutoGPT在处理和存储与聊天相关信息时的效率和准确性。
下面将探讨如何为Agent配置一个AutoGPTMemory类,这个类旨在增强AutoGPT的记忆能力,使其能更有效地处理和存储与聊天相关的信息。我们将从类的定义开始,逐步解释每个部分的作用和实现方法。
1)导入所需模块,定义类和成员变量。新建memory.py文件,这里定义了一个名为AutoGPTMemory的类,它继承自BaseChatMemory。这意味着AutoGPTMemory将拥有BaseChatMemory的所有属性和方法,并且可以扩展或修改这些属性和方法。
retriever是一个VectorStoreRetriever对象,用于连接到一个向量存储服务,这个服务用于存储和检索与聊天相关的信息。
2)定义内存变量,这个属性返回一个字符串列表,代表内存中要使用的变量名。
在这个例子中,我们有两个变量:chat_history和relevant_context。这两个变量用于存储聊天历史和相关的上下文信息。
3)获取提示输入键,这个方法用于从输入中获取适合用作提示的键。如果内部变量input_key是None,它将调用get_prompt_input_key函数(在导入模块时已导入)并传入输入和内存变量。
4)定义load_memory_variables方法用于加载和返回与当前聊天相关的内存变量。
首先,确定要用于查询的输入键,然后使用这个键从输入中检索查询。从
retriever获取相关文档,并将这些文档以及最近的10条聊天消息存储在返回的字典中。
通过AutoGPTMemory类的设计,AutoGPT能够根据聊天的上下文和历史来做出更加准确的响应。
5)构建好这个类后,我们将这个类所具备的记忆能力赋能到用于构造LLM提示词模板的AutoGPTPrompt类中。在AutoGPTPrompt类中,调用AutoGPTMemory是为了整合长期记忆和短期记忆数据,以便在构建提示时使用。AutoGPTMemory在这个过程中的主要作用体现在以下几个方面:
第一,在AutoGPTPrompt类,定义的format_messages方法中,AutoGPTMemory通过调用memory.get_relevant_documents(str(previous_messages[-10:]))获取与最近的10条消息相关的文档。这些文档随后被加工成relevant_memory。
第二,AutoGPTMemory类同时负责管理和提供聊天历史信息,这模拟了短期记忆的功能。在format_messages方法中,通过previous_messages[-10:]获取的最近10条消息被用来代表当前聊天上下文。这些消息被整合到提示中,以提供当前交互的直接背景,帮助生成更准确的响应。在代码中,实例化类后,从传递的参数
kwargs["memory"]中提取到memory对象,即为VectorStoreRetriever对象,包含程序的聊天历史信息,存放在向量数据库中。我们在Agent运行之前,即为AutoGPT类实例化之前,会选择一个向量数据库,通过LangChain框架提供的组件集成为向量服务。(代码中选择的是免费的FAISS,而AutoGPT的作者使用的是收费的
Pinecone)。在项目中新建一个weather_agent.py文件,这是程序的主程序,提供一个写天气报告的Agent服务,加入代码中。我们将向量数据库支撑的记忆能力,在AutoGPT实例化的时候,通过参数memory传递给AutoGPT。从此,这个Agent就拥有了记忆能力。
6)添加长期记忆。添加记忆分为以下步骤。
①输入记忆。当AutoGPT的Agent运行后,需要将用户的输入添加到内存中。这是输入记忆阶段的关键操作。
②Agent运行逻辑。设计的AutoGPT是完全自主运行的,不涉及用户交互。用户的输入被设置为特定的命令:Determine which next command to use,and respond using the format specified above。
③处理循环和响应。在运行循环中,AutoGPT使用LLMChain发起请求,并将获得的响应保存在assistant_reply变量中。
在这个代码示例中,AutoGPT类的run方法负责处理用户输入和AI的响应,并将它们添加到聊天历史和向量数据库中。通过上述步骤,AutoGPT类能够将用户的输入(User角色)和AI的响应(AI角色)作为文档保存到向量数据库中,以便后续的提取和搜索。
7)添加短期记忆。LangChain框架提供了一个用于存储历史聊天记录的内存功能,这可以通过ChatMessageHistory类实现。该功能主要用于在Agent自主运行时捕捉和保存System、User和AI之间的对话。使用这个功能的方法如下:
①导入ChatMessageHistory。
②实例化时作为成员变量。在定义AutoGPT类时,将ChatMessageHistory实例化为一个成员变量。如果在构造函数中没有提供具体的chat_history_memory实例,会默认创建一个新的实例。
③运行时添加消息。在AutoGPT类的run方法或相关处理函数中,使用add_message方法将用户的输入(User角色)和AI的响应(AI角色)添加到chat_history_memory对象中。这样,Agent就能够保留对话历史,但需要注意的是,这种方式并不是长期的持久化存储,而是程序运行时的临时存储。
通过这种方式,AutoGPT类不仅能处理当前的对话内容,还能参考过往的对话历史来生成更加相关和连贯的响应。
在整个过程中,AutoGPTMemory和ChatMessageHistory的作用是确保AutoGPTPrompt 可以访问并利用所有必要的上下文信息来构建高质量的提示,这包括从长期记忆中
检索相关的历史信息和从短期记忆中获取最近的交互数据。通过这种方式,
AutoGPTPrompt能够生成更加精确和个性化的响应。