LangChain大模型应用开发指南-传统编程范式思维的应用
上节课,我带领小伙伴们完成了baichuan2量化模型的OpenAI标准接口封装,并完成LangChain对大模型的调用与测试。没有看过的小伙伴可以点击链接查看:
今天我们将正式开始LangChain大模型应用开发课程。
组件总览
上图为LangChain的组件与架构图,基本完整描述了LangChain的组件与抽象层以及它们之间的相关联系。主要包括以下六个部分,基于同传统软件开发的比较,我将其分为两个类型:
-
传统编程思想的AI工程化应用
Chains:链,用于组合组件的结构化方式
Agents:代理,用于执行特定任务的组件
Callbacks:回调,用于在链中插入自定义逻辑的组件 -
AI大模型衍生的新能力
css
Model I/O:模型输入输出,用于与语言模型交互的组件
Retrieval:检索,用于从外部来源获取上下文的组件
Memory:记忆,用于存储和更新上下文的组件
作为LangChain大模型应用开发指南第一课,我将介绍LangChain的六大核心组件和概念解析,帮助大家从全局的角度了解LangChain在AI应用开发流程中的能力边界,并将在最后给出一个使用到了以上所有相关组件和概念的实际使用示例。
受限于文章篇幅,本篇文章主要从传统编程出发,讲解LangChain应用开发中与传统IT应用开发相同的编程范式,本次课程整体提纲如下:
Chains
做过传统软件开发的小伙伴,对链这个名词肯定不陌生,比如Java设计模式里的责任链。LangChain中的Chain也便是大家熟知的链,是LangChain中用于组合组件的结构化方式,它可以让你按照一定的顺序和逻辑来执行不同的任务。你可以把它想象成语言模型进行流水线作业,每个环节都有一个特定的目标和功能。Chains有以下四种类型:
类型 | 作用 |
---|---|
LLMChain | 用于在语言模型周围添加一些功能的简单Chain,它由一个PromptTemplate和一个语言模型(LLM或chat model)组成,它可以使用输入键值和内存键值(如果有的话)来格式化PromptTemplate,然后将格式化后的字符串传递给语言模型,并返回语言模型的输出 |
RouterChain | 用于创建一个动态选择下一个要使用的Chain的Chain的范式,它由两个组件组成:RouterChain本身(负责选择下一个要调用的Chain)和destination_chains(RouterChain可以路由到的Chain) |
SequentialChain | 用于将多个Chain连接起来,形成一个执行一些特定场景的管道的Chain,它允许将一个调用的输出作为另一个调用的输入 |
TransformChain | 用于创建一个通用的转换Chain,它可以对输入进行一些预处理或后处理,然后将其传递给另一个Chain |
Chains是LangChain中完成业务逻辑拆解和组装的核心组件,它可以让你灵活地构建复杂的应用程序。你可以使用不同类型的Chains来实现大模型、现有数据等各种各样基础能力的组装与能力搭建。
- 如下代码是使用Chain整合提示词模板,OpenAI接口,输出文本格式化解析器的一个简单示例
python
from langchain.chat_models import ChatOpenAI
from langchain.prompts.chat import ChatPromptTemplate
from langchain.schema import BaseOutputParser
class CommaSeparatedListOutputParser(BaseOutputParser):
"""Parse the output of an LLM call to a comma-separated list."""
def parse(self, text: str):
"""Parse the output of an LLM call."""
return text.strip().split(", ")
template = """You are a helpful assistant who generates comma separated lists.
A user will pass in a category, and you should generate 5 objects in that category in a comma separated list.
ONLY return a comma separated list, and nothing more."""
human_template = "{text}"
chat_prompt = ChatPromptTemplate.from_messages([
("system", template),
("human", human_template),
])
chain = chat_prompt | ChatOpenAI() | CommaSeparatedListOutputParser()
chain.invoke({"text": "colors"})
# >> ['red', 'blue', 'green', 'yellow', 'orange']
Agents
和责任链一样,Java设计模式还有一个设计模式叫代理模式。LangChain中Agents的概念与传统的代理模式基本一致,是通过对稳定的核心能力进行封装,通过Agents代理提供访问入口,既保证了对多样化业务能力需求的支持,也能保持核心能力的稳定性。
LangChain中Agents与传统代理模式的区别就在于,其代理的核心能力便是AI大模型。通过Agents,我们可以让语言模型具有主动性和智能性。你可以把它想象成语言模型的打工人,它可以根据你的需求和指示来完成各种各样的工作。LangChain基于对AI应用开发的总结抽象,主要提供以下六种代理类型:
类型 | 作用 |
---|---|
Zero-shot ReAct | 用于根据工具的描述来决定使用哪个工具的Agent,它可以使用任意数量的工具,但要求每个工具都有一个描述 |
Structured input ReAct | 用于使用多输入工具的Agent,它可以根据工具的参数模式来创建一个结构化的动作输入,这对于更复杂的工具使用,如在浏览器中精确地导航,很有用 |
OpenAI Functions | 用于与一些特定的OpenAI模型(如gpt-3.5-turbo-0613和gpt-4-0613)协作的Agent,这些模型已经被显式地微调过,以便检测何时应该调用一个函数,并返回应该传递给函数的输入 |
Conversational | 用于在对话场景中使用的Agent,它的提示被设计成使Agent有助于和对话,它使用ReAct框架来决定使用哪个工具,并使用内存来记住之前的对话交互 |
Self-ask with search | 用于利用一个名为Intermediate Answer的工具的Agent,这个工具应该能够查找问题的事实答案,这个Agent相当于原始的自问自答与搜索论文,其中提供了一个Google搜索API作为工具 |
ReAct document store | 用于与文档存储交互的Agent,它使用ReAct框架,并且必须提供两个工具:一个Search工具和一个Lookup工具(它们必须被准确地命名为这样),Search工具应该搜索一个文档,而Lookup工具应该在最近找到的文档中查找一个术语,这个Agent相当于原始的ReAct论文,特别是Wikipedia示例 |
Agents是LangChain工程化封装与能力复用核心手段,可以动态的帮我们选择和调用 Chain 或者已有的工具,实现面向多种应用场景的智能适配。
- 如下代码是使用Self-ask with search完成查询链
javascript
from langchain.llms import OpenAI
from langchain.utilities import SerpAPIWrapper
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
llm = OpenAI(temperature=0)
search = SerpAPIWrapper()
tools = [
Tool(
name="Intermediate Answer",
func=search.run,
description="useful for when you need to ask with search",
)
]
Callbacks
回调这个概念不用多说,各种编程语言的回调函数就是具体的应用。 LangChain中Callbacks是用于在链中插入自定义逻辑的组件,它可以让你对语言模型的行为进行控制和调整和调整。你可以把它理解成餐厅排队的叫号,当排队到你的时候就会触发提醒你用餐。同样在LangChain中,它可以在链中的任何位置执行你定义的函数或代码。。LangChain基于对AI应用开发的总结抽象,主要提供以下七种回调类型:
类型 | 作用 |
---|---|
Async callbacks | 用于在异步模式下执行回调函数的类,它可以让您在不阻塞主线程的情况下,监听和处理LLM应用的各个阶段的事件 |
Custom callback handlers | 用于自定义回调函数的类,它可以让您实现一些特定的逻辑,如验证,过滤,转换等,以及定义回调函数在哪些事件上被触发 |
Callbacks for custom chains | 用于为自定义的Chain添加回调函数的类,它可以让您在Chain的开始,结束,或每个组件的调用之前或之后执行一些操作 |
Logging to file | 用于将LLM应用的日志记录到文件中的类,它可以让您指定日志文件的路径,格式,级别等,并在每次LLM应用运行时自动写入日志信息 |
Multiple callback handlers | 用于同时使用多个回调函数的类,它可以让您将不同类型或功能的回调函数组合起来,并按照一定的顺序执行 |
Tags | 用于给回调函数添加标签的类,它可以让您根据标签来筛选或分组回调函数,并在一些特定的场景中使用它们 |
Token counting | 用于统计LLM应用使用的令牌数量的类,它可以让您监控和控制LLM应用的消耗,并在每次LLM应用运行时自动更新令牌计数 |
Callbacks是LangChain中实现自定义逻辑插入的入口,它可以让你的任务链达到合适场景时执行你自定义的功能和效果。
- 如下是LangChain内置的标准输出的回调演示
ini
from langchain.callbacks import StdOutCallbackHandler
from langchain.chains import LLMChain
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
handler = StdOutCallbackHandler()
llm = OpenAI()
prompt = PromptTemplate.from_template("1 + {number} = ")
# Constructor callback: First, let's explicitly set the StdOutCallbackHandler when initializing our chain
chain = LLMChain(llm=llm, prompt=prompt, callbacks=[handler])
chain.run(number=2)
# Use verbose flag: Then, let's use the `verbose` flag to achieve the same result
chain = LLMChain(llm=llm, prompt=prompt, verbose=True)
chain.run(number=2)
# Request callbacks: Finally, let's use the request `callbacks` to achieve the same result
chain = LLMChain(llm=llm, prompt=prompt)
chain.run(number=2, callbacks=[handler])
总结
本文以传统应用编程设计模式和思维为入口和对比对象,介绍了LangcChain中的Chain、Agent、Callback三大核心概念,并整理了LangcChain为众多开发者内置的能力与工具。
通过本文的指导,读者可以迁移在传统应用编程中累积的思维方式和经验,快速入门掌握AI应用编程。
下一节课我们将着重讲解,LangChain架构在传统应用开发基础上通过AI大模型衍生出的新能力,请大家持续关注。