用通俗易懂的方式讲解大模型:LangChain Agent 原理解析

LangChain 是一个基于 LLM(大型语言模型)的编程框架,旨在帮助开发人员使用 LLM 构建端到端的应用程序。它提供了一套工具、组件和接口,可以简化创建由 LLM 和聊天模型提供支持的应用程序的过程。

LangChain 由几大组件构成,包括 Models,Prompts,Chains,Memory 和 Agent 等,而 Agent 是其中重要的组成部分,如果把 LLM 比做大脑的话,那 Agent 就是给大脑加上手和脚。今天就来带大家重点了解一下 Agent 以及它的工作原理。

什么是 LangChain Agent

在 LangChain 中,Agent 是一个代理,接收用户的输入,采取相应的行动然后返回行动的结果。Agent 可以看作是一个自带路由消费 Chains 的代理,基于 MRKL 和 ReAct 的基本原理,Agent 可以使用工具和自然语言处理问题。官方也提供了对应的 Agent,包括 OpenAI Functions Agent、Plan-and-execute Agent、Self Ask With Search 类 AutoGPT 的 Agent 等。Agent 的作用是代表用户或其他系统完成任务,例如数据收集、数据处理、决策支持等。Agent 可以是自主的,具备一定程度的智能和自适应性,以便在不同的情境中执行任务。我们今天主要了解基于 ReAct 原理来实现的 Agent。

ReAct

ReAct 是一个结合了推理和行动的语言模型。虽然 LLM 在语言理解和交互决策制定方面展现出了令人印象深刻的能力,但它们的推理(例如链式思考提示)和行动(例如行动计划生成)的能力主要被视为两个独立的主题。ReAct 的目标是探索如何使用 LLM 以交错的方式生成推理痕迹和特定任务的行动,从而在两者之间实现更大的协同作用。

想象一下,你有一个智能助手机器人,名叫小明。你给小明一个任务:去厨房为你做一杯咖啡。小明不仅要完成这个任务,还要告诉你他是如何一步步完成的。

没有 ReAct 的小明:

  1. 小明直接跑到厨房。

  2. 你听到了一些声音,但不知道小明在做什么。

  3. 过了一会儿,小明回来给你一杯咖啡。

这样的问题是,你不知道小明是怎么做咖啡的,他是否加了糖或奶,或者他是否在过程中遇到了任何问题。

有 ReAct 的小明:

  1. 小明告诉你:“我现在去厨房。”

  2. 小明再说:“我找到了咖啡粉和咖啡机。”

  3. “我现在开始煮咖啡。”

  4. “咖啡煮好了,我要加点糖和奶。”

  5. “好了,咖啡做好了,我现在给你拿过去。”

这次,你完全知道小明是怎么做咖啡的,知道他的每一个步骤和决策。

ReAct 就是这样的原理。它不仅执行任务(行动),还会告诉你它是如何思考和决策的(推理)。这样,你不仅知道任务完成了,还知道为什么这样做,如果有问题,也更容易找出原因。

更多关于 ReAct 的内容可以查看这篇文章[1]。

自定义 LLM Agent

LangChain 在官方网站上提供了关于如何创建自定义 LLM Agent[2]的例子,在官网的示例中,我们除了看到自定义 LLM Agent 外,还有一个自定义 Agent,这两者的区别就是自定义 LLM Agent 使用了 LLM 来解析用户输入,判断使用何种工具,而自定义 Agent 则是直接自行判断工具的使用,这种方式只能用于简单的场景,而自定义 LLM Agent 可以用于更复杂的场景。

在官方示例中,对其实现原理做了一些简单的描述,介绍了其组成部分和流程等,但如果是刚开始了解 Agent 的同学看起来可能会一知半解,所以我们今天主要结合其中的示例代码来了解自定义 LLM Agent 的实现原理。

通俗易懂讲解大模型系列

  • 用通俗易懂的方式讲解大模型:HugggingFace 推理 API、推理端点和推理空间使用详解
  • 用通俗易懂的方式讲解大模型:使用 LangChain 封装自定义的 LLM,太棒了
  • 用通俗易懂的方式讲解大模型:使用 FastChat 部署 LLM 的体验太爽了
  • 用通俗易懂的方式讲解大模型:基于 Langchain 和 ChatChat 部署本地知识库问答系统
  • 用通俗易懂的方式讲解大模型:使用 Docker 部署大模型的训练环境
  • 用通俗易懂的方式讲解大模型:在 Ubuntu 22 上安装 CUDA、Nvidia 显卡驱动、PyTorch等大模型基础环境
  • 用通俗易懂的方式讲解大模型:Llama2 部署讲解及试用方式
  • 用通俗易懂的方式讲解大模型:LangChain 知识库检索常见问题及解决方案
  • 用通俗易懂的方式讲解大模型:基于 LangChain 和 ChatGLM2 打造自有知识库问答系统
  • 用通俗易懂的方式讲解大模型:代码大模型盘点及优劣分析
  • 用通俗易懂的方式讲解大模型:Prompt 提示词在开发中的使用

技术交流

建了大模型技术交流群! 想要学习、技术交流、获取如下原版资料的同学,可以直接加微信号:mlc2060。加的时候备注一下:研究方向 +学校/公司+CSDN,即可。然后就可以拉你进群了。

方式①、微信搜索公众号:机器学习社区,后台回复:加群
方式②、添加微信号:mlc2060,备注:来自CSDN + 技术交流

在这里插入图片描述

提示词模板

要实现 Agent,我们需要先定义一套基于 ReAct 的提示词模板,示例中的 Agent 就是基于 ReAct 原理来实现的,为了方便理解,我们将官方的英文提示词模板换成中文和去掉一些没必要的内容,修改后的提示词模板内容如下:

template = """尽你所能回答以下问题,你可以使用以下工具:{tools}请按照以下格式:问题:你必须回答的输入问题
思考:你应该始终考虑该怎么做
行动:要采取的行动,应该是[{tool_names}]中的一个
行动输入:行动的输入
观察:行动的结果
... (这个思考/行动/行动输入/观察可以重复N次)
思考:我现在知道最终答案了
最终答案:对原始输入问题的最终答案开始吧!问题:{input}
{agent_scratchpad}"""

简单介绍一下这个提示词模板,首先提示词模板中会引用到一些工具,可以看到模板中有 2 个变量:toolstool_names,tools 变量是一个列表,包含了所有的工具,列表中的每个元素包含了工具的名称和描述,而 tool_names 变量是工具名称的列表,传入具体的工具后,会生成对应的工具列表,比如我们有如下 2 个工具,解析后如下所示:

尽你所能回答以下问题,你可以使用以下工具:search: 实时联网搜索的工具
math: 数学计算的工具请按照以下格式:
......
行动:要采取的行动,应该是[search, math]中的一个
......

这样 LLM 在执行任务时就知道要使用哪些工具,以及在提取信息时可以提取到正确的工具名。

模板中下面的思考/行动/行动输入/观察就是标准的 ReAct 流程,思考是指思考如何解决问题,行动是具体的工具,行动输入是工具用到的参数,观察是工具执行完成后得到的结果,这个流程可以重复多次,直到最终得到最终答案。

模板最后还有 2 个变量:inputagent_scratchpad,input 是用户输入的问题,agent_scratchpad 是之前的思考过程(下面解析代码时会讲),包括了思考、行动、行动输入和观察等,这个变量在 Agent 执行过程中会被更新,代入具体的值后,模板会生成如下的提示词:

......
问题:北京的天气怎么样
思考: 我们需要通过 search 工具查找北京天气。
行动: search
行动输入: "北京天气"
观察: 6日(今天). 多云转晴. 32/22. <3级
思考:

从问题下面一句到最后结束就是agent_scratchpad的值。

构造提示词

准备好提示词模板后,我们就可以构造提示词了,构造提示词的官方示例代码如下:

# Set up a prompt template
class CustomPromptTemplate(StringPromptTemplate):# The template to usetemplate: str# The list of tools availabletools: List[Tool]def format(self, **kwargs) -> str:# Get the intermediate steps (AgentAction, Observation tuples)# Format them in a particular wayintermediate_steps = kwargs.pop("intermediate_steps")thoughts = ""for action, observation in intermediate_steps:thoughts += action.logthoughts += f"\nObservation: {observation}\nThought: "# Set the agent_scratchpad variable to that valuekwargs["agent_scratchpad"] = thoughts# Create a tools variable from the list of tools providedkwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])# Create a list of tool names for the tools providedkwargs["tool_names"] = ", ".join([tool.name for tool in self.tools])return self.template.format(**kwargs)prompt = CustomPromptTemplate(template=template,tools=tools,# This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically# This includes the `intermediate_steps` variable because that is neededinput_variables=["input", "intermediate_steps"]
)

首先我们需要定义一个类,继承自StringPromptTemplate,然后实现format方法,这个方法的作用是将提示词模板中的变量代入具体的值,然后返回提示词。其中intermediate_steps是中间 Agent 思考的步骤,每个步骤是一个元组,包含了AgentAction(行为和行为输入)和Observation(观察)的值,这个变量不会直接传递到 LLM,所以它不会在提示词中出现,但提示词模板会将它转换为agent_scratchpad变量,这个变量也就是我们在上面提到的agent_scratchpad,这个变量的值是 Agent 思考的过程,包括了思考、行动、行动输入和观察等。

format 方法最后会设置模板中的toolstool_names变量,这两个变量的值是提示词模板中的工具列表,这个列表是根据tools变量生成的,包含了所有的工具,列表中的每个元素包含了工具的名称和描述,而tool_names变量是工具名称的列表。

最后我们需要创建一个CustomPromptTemplate对象,传入提示词模板和工具列表,这个对象就是我们最终要传入 LLM 的提示词。

工具解析

接下来是输出结果的解析,其中分为 2 个部分,一个是工具的解析,一个是结果的解析,我们来看下官方的示例代码:

class CustomOutputParser(AgentOutputParser):def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:# 暂时不看结果解析的代码# Parse out the action and action inputregex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"match = re.search(regex, llm_output, re.DOTALL)if not match:raise OutputParserException(f"Could not parse LLM output: `{llm_output}`")action = match.group(1).strip()action_input = match.group(2)# Return the action and action inputreturn AgentAction(tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output)

parse 方法前半部分是关于结果解析的,我们待会再讲,我们先看后面的代码,这是对工具的解析。

代码使用了一个正则表达式来解析 LLM 关于思考过程的输出结果,一般思考过程的输出结果是这样的格式:

思考: 我们需要通过 search 工具查找北京天气。
行动: search
行动输入: "北京天气"

根据我们的提示词模板,LLM 会智能地返回我们制定好的格式,有思考、行动、行动输入几项内容,我们主要通过这个正则表达式来获取到行动行动输入这两项的值(也就是工具名称和工具所需参数),这样 Agent 就知道该如何使用工具了。可以看到代码中 action(工具)获取的是正则结果中的第一个分组的值,而 action_input(工具参数)获取的是第二个分组的值。最后将这 2 个值封装成一个 AgentAction 对象返回。

但需要注意的是,虽然我们设置好了提示词模板,但如果 LLM 不够智能的话,返回的结果可能会和我们预期的不一样,比如 LLM 可能返回这样的结果:

思考: 我们需要通过 search 工具查找北京天气。
行动: 我需要使用search工具来查询
行动输入: "北京天气"

可以看到行动的值不是我们预期的 search,而是一句话,这样通过正则表达式获取的工具名就是我需要使用search工具来查询,然后 Agent 会拿这个工具名去匹配工具,然后去调用,发现没有这个工具,就会报错。所以如果我们发现 LLM 有时候返回的结果不符合我们预期时,我们需要通过其他方式来解析出工具的名称,比如去掉一些无用的内容,只保留工具名称,这样才能保证 Agent 能够正常使用工具。

结果解析

我们再说下结果的解析,就是上面代码中 parse方法的前半部分:

class CustomOutputParser(AgentOutputParser):def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:# Check if Agent should finishif "Final Answer:" in llm_output:return AgentFinish(# Return values is generally always a dictionary with a single `output` key# It is not recommended to try anything else at the moment :)return_values={"output": llm_output.split("Final Answer:")[-1].strip()},log=llm_output,)

示例代码中判断 LLM 的输出是否有包含最终答案:,如果有的话,就说明 LLM 已经得到了最终的答案,这时就可以返回最终的答案了,这个格式也是我们在提示词模板中定义的:

"""
思考:我现在知道最终答案了
最终答案:对原始输入问题的最终答案
"""

这个最终的答案是 LLM 输出结果中的最后一句话,我们可以通过split方法来获取到最后一句话,然后返回一个ActionFinish对象,这个对象包含了最终的答案,解析出来的结果如下:

# 思考过程
思考:我现在知道最终答案了
最终答案: 北京的天气情况如下:6日(今天)多云转晴,温度在32/22℃,风力小于3# 最终结果
北京的天气情况如下:6日(今天)多云转晴,温度在32/22℃,风力小于3

跟工具解析一样,我们在结果解析时也需要注意 LLM 返回的结果是否符合我们预期,比如有时候 LLM 会输出这样的结果:

思考:我现在知道最终答案了
北京的天气情况如下:6日(今天)多云转晴,温度在32/22℃,风力小于3

可以看到 LLM 的输出没有包含最终结果:关键字,这样示例代码中的 if 逻辑就不生效了,会导致解析逻辑进到工具解析那一部分去,然后引发错误。所以我们在解析结果时,要提高我们解析程序的健壮性,以满足不同的 LLM 输出结果,或者调整我们的提示词模板,让 LLM 返回的结果更加准确。

中断提示

最后一步是创建 Agent,示例代码如下:

llm = OpenAI(temperature=0)
# LLM chain consisting of the LLM and a prompt
llm_chain = LLMChain(llm=llm, prompt=prompt)tool_names = [tool.name for tool in tools]
agent = LLMSingleActionAgent(llm_chain=llm_chain,output_parser=output_parser,stop=["\nObservation:"],allowed_tools=tool_names
)

这里重点关注的是 Agent 中的stop参数,我们知道一般 LLM 都会长篇大论,说一大堆废话,我们希望 LLM 在返回了我们需要的信息后就停止输出,这里就需要用到stop参数,这个参数是一个列表,列表中的每个元素都是一个字符串,代表了 LLM 输出中的某一句话,当 LLM 输出中包含了这句话时,LLM 就会停止输出,这样我们就可以只获取到我们需要的信息了,这里我们使用观察关键字来停止 LLM 的输出。

总结

自定义 LLM Agent 的示例代码我们已经介绍完了,最后我们再讲下来 Agent 中使用的 LLM。在官方示例中,LLM 用的是 OpenAI,也就是gpt-3.5这个模型,但如果想达到更好的效果的话,推荐使用 OpenAI 的gpt-4模型,它是目前最好的 LLM,如果使用的 LLM 比较差,就容易出现刚才我们提到 LLM 返回的结果不符合我们预期的情况。

有人希望通过一些开源的 LLM 来实现 ReAct Agent,但实际开发过程中会发现开源低参数(比如一些 6B、7B 的 LLM)的 LLM 对于提示词的理解会非常差,根本不会按照提示词模板的格式来输出,这样就会导致我们的 Agent 无法正常工作,所以如果想要实现一个好的 Agent,还是需要使用好的 LLM,目前看来使用gpt-3.5模型是最低要求。

关注我,一起学习各种人工智能和 AIGC 新技术,欢迎交流,如果你有什么想问想说的,欢迎在评论区留言。

参考:

[1]这篇文章: https://react-lm.github.io/

[2]创建自定义 LLM Agent: https://python.langchain.com/docs/modules/agents/how_to/custom_llm_agent

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/311010.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s搭建(五、k8s可视化管理工具Dashboard配置)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

【基础】【Python网络爬虫】【8.Selenium入门】selenium配置、环境安装、浏览器驱动下载(附大量案例代码)(建议收藏)

Python网络爬虫基础 Selenium 入门1. 动态网页&静态网页动态网页JavaScriptJQueryAjaxHTML 动态网页处理方法 2. Selenium 工作原理3. Selenium 配置环境安装浏览器驱动下载配置浏览器驱动 selenium 快速上手 4. Driver对象的常用方法及属性5. 元素提取6. 元素对象的方法及…

【基础】【Python网络爬虫】【5.数据解析】bs4、Xpath、Parsel模块、正则表达式(附大量案例代码)(建议收藏)

Python网络爬虫基础 数据解析1. 为何数据解析2. 常见的数据类型结构化数据半结构化数据非结构化数据 3. 爬虫项目实现步骤 数据解析模块1. Bs4环境安装bs4解析流程案例 - bs4碧血剑文本爬取 2. Xpath环境安装xpath解析的编码流程xpath表达式如何理解&#xff1f;案例 - 简历模板…

《深入理解C++11:C++11新特性解析与应用》笔记六

第六章 提高性能及操作硬件的能力 6.1 常量表达式 6.1.1 运行时常量性与编译时常量性 大多数情况下&#xff0c;const描述的是运行时常量性&#xff0c;也即是运行时数据的不可更改性。但有时候我们需要的却是编译时的常量性&#xff0c;这是const关键字无法保证的。例如&am…

SparkStreaming与Kafka整合

1.3 SparkStreaming与Kafka整合 1.3.1 整合简述 kafka是做消息的缓存&#xff0c;数据和业务隔离操作的消息队列&#xff0c;而sparkstreaming是一款准实时流式计算框架&#xff0c;所以二者的整合&#xff0c;是大势所趋。 ​ 二者的整合&#xff0c;有主要的两大版本。 kaf…

Java流程控制语句(if语句,switch语句,for循环,while循环,do...while循环,三种循环的区别)

文章目录 第一章 流程控制语句1.1 流程控制语句分类1.2 顺序结构 第二章 判断语句&#xff1a;if语句2.1 if语句格式1练习1&#xff1a;老丈人选女婿练习2&#xff1a;考试奖励第一种格式的细节&#xff1a; 2.2 if语句格式2练习1&#xff1a;吃饭练习2&#xff1a;影院选座 2.…

MATLAB中./和/,.*和*,.^和^的区别

MATLAB中./和/&#xff0c;.*和*&#xff0c;.^ 和^ 的区别 MATLAB中./和/&#xff0c;.*和*&#xff0c;.^ 和^ 的区别./ 和 / 的区别.//实验实验结果 .* 和 * 的区别.**实验实验结果 .^ 和^ 的区别.^n^n实验运行结果 MATLAB中./和/&#xff0c;.和&#xff0c;.^ 和^ 的区别 …

文件描述符

文件描述符 2.1 文件描述符 文件描述符&#xff08;File Descriptor&#xff09;是在Unix-like操作系统中用于标识和访问文件或I/O设备的抽象概念。它是一个非负整数&#xff0c;用于**唯一标识一个打开的文件、套接字&#xff08;socket&#xff09;或其他类型的I/O资源。**…

一个项目,用十款数据库?

大家好&#xff0c;我是豆小匠。 关于数据库&#xff0c;大学的时候只知道MySQL&#xff0c;学习深入点也就是用到了Redis、MongoDB等非关系型数据库。 然而&#xff0c;工作中用到的数据库实在太多&#xff0c;每种数据库都有自身的优势和局限性。所以在这里梳理下日常常用数…

运动轨迹仿真

重型运载火箭轨迹/总体参数一体化优化方法 硬核推导火箭运动方程&#xff0c;并用python仿真实现 rocket-simulation:MATLAB-火箭仿真软件 MatRockSim:Matlab 火箭飞行模拟器 【开源】飞鹰一号探空火箭——箭体设计、制造回顾与仿真对比 偏航角&#xff0c;滚动角&#xf…

迅为RK3588开发板RTMP推流之视频监控之搭建 RTMP 媒流体服务器

1.安装 nginxrtmp 运行所要用到的库和依赖环境 apt-get update apt-get install build-essential libpcre3 libpcre3-dev libssl-dev zlib1g-dev openssl 2. 下 载 nginx-1.20.2 源 码 ( 下 载 地 址 &#xff1a; http://nginx.org/download/nginx-1.20.2.tar.gz) 和nginx-…

搭建FTP服务器与计算机端口介绍

FTP介绍 FTP&#xff08;File Transfer Protocol&#xff09;是一种用于在计算机网络上进行文件传输的协议。它允许用户通过客户端与服务器进行通信&#xff0c;从服务器下载文件或将文件上传到服务器。 FTP使用客户端-服务器模型。用户使用FTP客户端软件连接到FTP服务器&…