构建大型语言模型代理:关键组件与开发指南

2023年12月01日 由 daydream 发表 469 0

开发大型语言模型 (LLM) 代理应用程序时,你需要四个关键组件:代理核心、记忆模块、代理工具和规划模块。无论你是设计问答代理、多模态代理还是代理群体,你都可以考虑从开源到生产就绪的多种实施框架。


微信截图_20231201112915


对于首次尝试开发LLM代理的人来说,本文提供以下内容:

  • 开发者生态系统概览,包括可用框架和推荐阅读材料,以便快速了解LLM代理
  • 用于构建你的第一个LLM驱动代理的初学者教程


代理的开发者生态系统概览


你可能已经阅读过有关LangChain或LLaMa-Index代理的文章。目前有几个实施框架可用:

  • LangChain代理
  • LLaMaIndex代理
  • HayStack代理
  • AutoGen
  • AgentVerse
  • ChatDev
  • 生成代理


那么我应该推荐哪一个呢?答案是,“这取决于具体情况”。


单个代理框架


社区构建了几个框架,以推进LLM应用程序开发生态系统,为你提供开发代理的便捷途径。一些流行的框架包括LangChain、LlamaIndex和Haystack。这些框架提供了通用的代理类、连接器、记忆模块特性,第三方工具的访问以及数据检索和摄取机制。


选择哪个框架在很大程度上取决于你的管道细节和需求。在必须构建具有有向无环图(DAG)流程或具有独特属性的复杂代理的情况下,这些框架为提示和一般架构提供了一个很好的参考点,用于你自己的自定义实现。


多代理框架


你可能会问,“多代理框架有什么不同?”简短的答案是一个“世界”类。为了管理多个代理,你必须构建世界,或者说是他们相互交互的环境,以及他们与用户和环境中的工具互动。这里的挑战是,对于每个应用程序,世界都将不同。你需要的是一个专门用于构建仿真环境的工具箱,该工具箱可以管理世界状态,并拥有代理的通用类。你还需要一个通信协议来管理代理之间的流量。OSS框架的选择取决于你正在构建的应用程序类型以及所需的定制程度。


构建代理的推荐阅读清单


你可以使用大量资源和材料来激发你对代理可能性的思考,但以下资源是了解代理整体精神的绝佳起点:


  • AutoGPT:这个GitHub项目是最早真正构建的代理之一,展示了代理所能提供的功能。查看一般架构和项目中使用的提示技术可能非常有帮助。
  • Voyager:NVIDIA研究的这个项目触及了自我改进代理的概念,这些代理学会使用新工具或在没有任何外部干预的情况下构建工具。
  • OlaGPT:OlaGPT这样的代理概念框架是一个很好的起点,用来激发关于如何超越基本的四个模块代理的想法。
  • MRKL系统:结合大型语言模型、外部知识来源和离散推理的模块化、神经符号架构:此论文首次提出了使用工具与语言模型一起执行复杂任务的核心机制。
  • 生成代理:人类行为的互动模拟:这是最早构建真正的代理群体解决方案之一,由多个代理相互交互组成,以分散的方式进行。


如果你正在寻找更多阅读材料,我发现Awesome LLM-Powered Agent列表很有用。


教程:构建一个问答代理


在这个教程中,你将构建一个问答 (QA) 代理,以帮助你与你的数据对话。


为了展示一个相当简单的代理如何应对相当困难的挑战,你将构建一个能够从财报电话会议中挖掘信息的代理。你可以查看财报电话的文字记录。图1展示了财报电话的一般结构,以便你了解本教程使用的文件。


微信截图_20231201132010


到本帖结束时,你构建的代理将能回答如下复杂和多层次的问题:


  • 2024年第一季度和第二季度之间的收入增长了多少?
  • 2024财年第二季度的主要要点是什么?


微信截图_20231201132459


正如本系列的第一部分所描述的,有四个代理组件:

  • 工具
  • 规划模块
  • 内存模块
  • 代理核心


工具


为了构建一个LLM代理,你需要以下工具:

  • RAG pipeline:如果没有RAG,你就无法解决与数据对话的问题。因此,你需要的一个工具是RAG pipeline。在本文中,假设RAG pipeline对基本或原子问题有100%的准确性。
  • 数学工具:你还需要一个数学工具来进行任何类型的分析。为了简化本文,使用LLM来回答数学问题,但对于生产应用程序,推荐像WolframAlpha这样的工具。


规划模块


有了这个LLM代理,你将能够回答诸如“2024年第一季度和第二季度之间的收入增长了多少?”这样的问题。这本质上将一个问题分成了三个:

  • 第一季度的收入是多少?
  • 第二季度的收入是多少?
  • 两者之间的差异是什么?

 

答案是你必须构建一个问题分解模块:


decomp_template = """GENERAL INSTRUCTIONS
You are a domain expert. Your task is to break down a complex question into simpler
sub-parts.
 
USER QUESTION
{{user_question}}
 
ANSWER FORMAT
{"sub-questions":["<FILL>"]}""


如你所见,分解模块提示LLM将问题分解成较不复杂的部分。图3展示了答案的样子。


微信截图_20231201132700


内存模块


接下来,你必须构建一个内存模块,以跟踪所有被问到的问题,或者只是保留所有子问题及其答案的列表。


class Ledger:
    def __init__(self):
        self.question_trace = []
        self.answer_trace = []


你用由两个列表组成的简单分类账来做到这一点:一个用来跟踪所有问题,一个用来跟踪所有答案。这有助于代理记住它已经回答了哪些问题,还有哪些问题尚未回答。


评估心理模型


在你构建代理核心之前,评估你现在拥有的内容:

  • 用于搜索和进行数学计算的工具
  • 用于分解问题的规划器
  • 用于记录所问问题的内存模块


此时,您可以将它们整合起来,看看它是否作为一个心理模型能工作(图4)。


template = """GENERAL INSTRUCTIONS
Your task is to answer questions. If you cannot answer the question, request a
helper or use a tool. Fill with Nil where no tool or helper is required.
 
AVAILABLE TOOLS
- Search Tool
- Math Tool
 
AVAILABLE HELPERS
- Decomposition: Breaks Complex Questions down into simpler subparts
 
CONTEXTUAL INFORMATION
<No previous questions asked>
 
QUESTION
How much did the revenue grow between Q1 of 2024 and Q2 of 2024?
 
ANSWER FORMAT
{"Tool_Request": "<Fill>", "Helper_Request "<Fill>"}"""


图4显示了LLM收到的答案。


微信截图_20231201132820


你可以看到LLM请求使用搜索工具,这是一个合乎逻辑的步骤,因为答案可能就在语料库中。尽管如此,你知道没有任何文字记录包含答案。在下一步中(图5),你提供来自RAG pipeline的输入,答案是不可用的,所以代理随后决定将问题分解成更简单的子部分。


微信截图_20231201132844


通过这个练习,你验证了逻辑核心机制是健全的。LLM根据需要选择工具和帮手。


现在,剩下的就是将这些整齐地封装在一个Python函数中,看起来可能是如下代码示例:


def agent_core(question):
    answer_dict = prompt_core_llm(question, memory)
    update_memory()
    if answer_dict[tools]:
        execute_tool()
        update_memory()
    if answer_dict[planner]:
        questions = execute_planner()
        update_memory()
    if no_new_questions and no tool request:
        return generate_final_answer(memory)


代理核心


你刚才看到了一个代理核心的例子,那么剩下的是什么呢?嗯,代理核心不仅仅是把所有部件拼凑在一起。你必须定义代理应该执行其流程的机制。本质上有三个主要选择:

  • 线性求解器
  • 单线程递归求解器
  • 多线程递归求解器


线性求解器


这是我之前讨论的那种执行类型。有一个单一线性解决方案链,代理可以使用工具和进行一级规划。虽然这是一个简单的设置,但真正复杂和细腻的问题通常需要分层思考。


单线程递归求解器


你还可以构建一个递归求解器,它构建一个问题和答案树,直到原始问题被回答。这棵树以深度优先遍历方式解决。以下代码示例显示了逻辑:


def Agent_Core(Question, Context):
    Action = LLM(Context + Question)
 
    if Action == "Decomposition":
        Sub Questions = LLM(Question)
        Agent_Core(Sub Question, Context)
 
    if Action == "Search Tool":
        Answer = RAG_Pipeline(Question)
        Context = Context + Answer
        Agent_Core(Question, Context)
 
    if Action == "Gen Final Answer”:
        return LLM(Context)
 
    if Action == "<Another Tool>":
        <Execute Another Tool>


多线程递归求解器


与其迭代地解决树,不如为树上的每个节点启动并行执行线程。这种方法增加了执行复杂性,但由于LLM调用可以并行处理,因此带来大量的延迟优势。


接下来是什么?


祝贺你!你现在已经具备了构建相当复杂的代理所需的知识!下一步是将前面讨论的原则适应到你的问题中。


文章来源:https://developer.nvidia.com/blog/building-your-first-llm-agent-application/
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
写评论取消
回复取消