LangChain介绍及代码实践

一、简介

LangChian 作为一个大语言模型开发框架,是 LLM 应用架构的重要一环。那什么是 LLM 应用架构呢?其实就是指基于语言模型的应用程序设计和开发的架构。

LangChian 可以将 LLM 模型、向量数据库、交互层 Prompt、外部知识、外部工具整合到一起,进而可以自由构建 LLM 应用。

二、6大组件

LangChain 包含六部分组成,分别为:Models、Prompts、Indexes、Memory、Chains、Agents。
在这里插入图片描述

2.1 Models

LangChain本身不提供LLM,提供通用的接口访问LLM,可以很方便的更换底层的LLM以及自定义自己的LLM。主要有2大类的Models:

1)LLM:将文本字符串作为输入并返回文本字符串的模型,类似OpenAI的text-davinci-003

2)Chat Models:由语言模型支持但将聊天消息列表作为输入并返回聊天消息的模型。一般使用的ChatGPT以及Claude为Chat Models。

与模型交互的,基本上是通过给与Prompt的方式,LangChain通过PromptTemplate的方式方便我们构建以及复用Prompt。

2.1.1 LLM

LangChain支持各种大模型的使用,如
谷歌的 BERT
OpenAI 的 GPT-3
谷歌 LaMDA
谷歌 PaLM
Meta AI 的 LLaMA
OpenAI 的 GPT-4

借助 LangChain,与大语言模型的交互变得更加便捷。LangChain 提供的接口和功能有助于将 LLM 的强大能力轻松集成到你的工作应用程序中。LangChain 利用 asyncio 库为 LLM 提供异步支持。

对于需要同时并发调用多个 LLM 的网络绑定场景,LangChain 还提供了异步支持。通过释放处理请求的线程,服务器可以将其分配给其他任务,直到响应准备就绪,从而最大限度地提高资源利用率。

目前,LangChain 支持 OpenAI、PromptLayerOpenAI、ChatOpenAI 和 Anthropic 等模型的异步支持,但在未来的计划中将扩展对其他 LLM 的异步支持。你可以使用 agenerate 方法来异步调用 OpenAI LLM。此外,你还可以编写自定义的 LLM 包装器,而不仅限于 LangChain 所支持的模型。

2.1.2 Chat Models

LangChain 为使用聊天模型提供了一个标准接口。聊天模型是语言模型的一种变体。虽然聊天模型在内部使用语言模型,但它们所提供的接口略有不同。它们不是暴露一个 “输入文本,输出文本” 的 API,而是提供了一个以 “聊天消息” 作为输入和输出的接口。

聊天模型的接口是基于消息而不是原始文本。LangChain 目前支持的消息类型有 AIMessage、HumanMessage、SystemMessage 和 ChatMessage,其中 ChatMessage 接受一个任意的角色参数。大多数情况下,您只需要处理 HumanMessage、AIMessage 和 SystemMessage。

# 导入OpenAI的聊天模型,及消息类型
from langchain.chat_models import ChatOpenAI
from langchain.schema import (AIMessage,HumanMessage,SystemMessage
)# 初始化聊天对象
chat = ChatOpenAI(openai_api_key="")# 向聊天模型发问
chat([HumanMessage(content="Translate this sentence from English to French: I love programming.")])

OpenAI 聊天模式支持多个消息作为输入。这是一个系统和用户消息聊天模式的例子:

messages = [SystemMessage(content="You are a helpful assistant that translates English to French."),HumanMessage(content="I love programming.")
]
chat(messages)

批量处理,批量输出

batch_messages = [[SystemMessage(content="You are a helpful assistant that translates English to French."),HumanMessage(content="I love programming.")],[SystemMessage(content="You are a helpful assistant that translates English to French."),HumanMessage(content="I love artificial intelligence.")],
]
result = chat.generate(batch_messages)
result

LangChain 也很贴心的提供了缓存的功能。并且提供了两种缓存方案,内存缓存方案和数据库缓存方案,当然支持的数据库缓存方案有很多种。

# 导入聊天模型,SQLiteCache模块
import os
os.environ["OPENAI_API_KEY"] = 'your apikey'
import langchain
from langchain.chat_models import ChatOpenAI
from langchain.cache import SQLiteCache# 设置语言模型的缓存数据存储的地址
langchain.llm_cache = SQLiteCache(database_path=".langchain.db")# 加载 llm 模型
llm = ChatOpenAI()# 第一次向模型提问
result = llm.predict('tell me a joke')
print(result)# 第二次向模型提问同样的问题
result2 = llm.predict('tell me a joke')
print(result2)

2.2 Prompts(提示)

提示(prompt)是我们向系统提供的输入,以便根据我们的使用案例对答案进行精确或特定的调整。许多时候,我们希望得到的不仅仅是文本,还需要更结构化的信息。基于对比预训练和零样本学习的许多新的目标检测和分类算法都将提示作为有效的输入来进行结果预测。举例来说,OpenAI 的 CLIP 和 META 的 Grounding DINO 都使用提示作为预测的输入。

在 LangChain 中,我们可以根据需要设置提示模板,并将其与主链相连接以进行输出预测。此外,LangChain 还提供了输出解析器的功能,用于进一步精炼结果。输出解析器的作用是指导模型输出的格式化方式,和将输出解析为所需的格式。

在 LangChain 中,我们可以提供提示模板作为输入。模板指的是我们希望获得答案的具体格式或蓝图。LangChain 提供了预先设计好的提示模板,可以用于生成不同类型任务的提示。然而,在某些情况下,预设的模板可能无法满足你的需求。在这种情况下,我们可以使用自定义的提示模板。

from langchain.llms import OpenAI# 示例
def generate_text(features):prompt_template = "我想让你帮忙生成10条关于{}的文案"prompt = prompt_template.format(features)llm = OpenAI()response = llm.generate(prompt, max_tokens=100, temperature=0.8)text = [gen[0].text.strip() for gen in response.generations]return text features = "星空"text = generate_text(features)
print(text)

2.3 Chains(链)

链允许我们将多个组件组合在一起以创建一个单一的、连贯的任务。例如,我们可以创建一个链,它接受用户输入,使用 PromptTemplate 对其进行格式化,然后将格式化的响应传递给 LLM。另外我们也可以通过将多个链组合在一起,或者将链与其他组件组合来构建更复杂的链。

2.3.1. LLMChain

LLMChain 是一个简单的链,它围绕语言模型添加了一些功能。它在整个 LangChain 中广泛使用,包括在其他链和代理中。它接受一个提示模板,将其与用户输入进行格式化,并返回 LLM 的响应。

from langchain import PromptTemplate, OpenAI, LLMChainprompt_template = "What is a good name for a company that makes {product}?"llm = OpenAI(temperature=0)
llm_chain = LLMChain(llm=llm,prompt=PromptTemplate.from_template(prompt_template)
)
llm_chain("colorful socks")
2.3.2. SimpleSequentialChain

顺序链的最简单形式,其中每个步骤都有一个单一的输入/输出,并且一个步骤的输出是下一步的输入。
在这里插入图片描述

from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.chains import SimpleSequentialChain# 定义第一个chain
llm = OpenAI(temperature=.7)
template = """You are a playwright. Given the title of play, it is your job to write a synopsis for that title.Title: {title}
Playwright: This is a synopsis for the above play:"""
prompt_template = PromptTemplate(input_variables=["title"], template=template)
synopsis_chain = LLMChain(llm=llm, prompt=prompt_template)# 定义第二个chainllm = OpenAI(temperature=.7)
template = """You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.Play Synopsis:
{synopsis}
Review from a New York Times play critic of the above play:"""
prompt_template = PromptTemplate(input_variables=["synopsis"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template)# 通过简单顺序链组合两个LLMChain
overall_chain = SimpleSequentialChain(chains=[synopsis_chain, review_chain], verbose=True)# 执行顺序链
review = overall_chain.run("Tragedy at sunset on the beach")
2.3.3. SequentialChain

相比 SimpleSequentialChain 只允许有单个输入输出,它是一种更通用的顺序链形式,允许多个输入/输出。

特别重要的是: 我们如何命名输入/输出变量名称。在上面的示例中,我们不必考虑这一点,因为我们只是将一个链的输出直接作为输入传递给下一个链,但在这里我们确实需要担心这一点,因为我们有多个输入。

# 这是一个 LLMChain,根据戏剧的标题和设定的时代,生成一个简介。
llm = OpenAI(temperature=.7)
template = """You are a playwright. Given the title of play and the era it is set in, it is your job to write a synopsis for that title.
# 这里定义了两个输入变量title和era,并定义一个输出变量:synopsis
Title: {title}
Era: {era}
Playwright: This is a synopsis for the above play:"""
prompt_template = PromptTemplate(input_variables=["title", "era"], template=template)
synopsis_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="synopsis")# 这是一个 LLMChain,根据剧情简介撰写一篇戏剧评论。
llm = OpenAI(temperature=.7)
template = """You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.
# 定义了一个输入变量:synopsis,输出变量:review
Play Synopsis:
{synopsis}
Review from a New York Times play critic of the above play:"""
prompt_template = PromptTemplate(input_variables=["synopsis"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="review")overall_chain({"title":"Tragedy at sunset on the beach", "era": "Victorian England"})

2.4. Indexes(索引)

索引是指对文档进行结构化的方法,以便 LLM 能够更好的与之交互。该组件主要包括:Document Loaders(文档加载器)、Text Splitters(文本拆分器)、VectorStores(向量存储器)以及 Retrievers(检索器)。

2.4.1. Document Loaders

指定源进行加载数据的。将特定格式的数据,转换为文本。如 CSV、File Directory、HTML、

JSON、Markdown、PDF。另外使用相关接口处理本地知识,或者在线知识。如 AirbyteJSON

Airtable、Alibaba Cloud MaxCompute、wikipedia、BiliBili、GitHub、GitBook 等等。

2.4.2. Text Splitters

由于模型对输入的字符长度有限制,我们在碰到很长的文本时,需要把文本分割成多个小的文本片段。

文本分割最简单的方式是按照字符长度进行分割,但是这会带来很多问题,比如说如果文本是一段代码,一个函数被分割到两段之后就成了没有意义的字符,所以整体的原则是把语义相关的文本片段放在一起。

LangChain 中最基本的文本分割器是 CharacterTextSplitter ,它按照指定的分隔符(默认“\n\n”)进行分割,并且考虑文本片段的最大长度。

from langchain.text_splitter import CharacterTextSplitter# 初始字符串
state_of_the_union = "..."text_splitter = CharacterTextSplitter(separator = "\\n\\n",chunk_size = 1000,chunk_overlap  = 200,length_function = len,
)texts = text_splitter.create_documents([state_of_the_union])

LangChain 还支持多个高级文本分割器
在这里插入图片描述

2.4.3. VectorStores

存储提取的文本向量,包括 Faiss、Milvus、Pinecone、Chroma 等。如下是 LangChain 集成的向量数据库。
在这里插入图片描述

2.4.4. Retrievers

检索器是一种便于模型查询的存储数据的方式,LangChain 约定检索器组件至少有一个方法 get_relevant_texts,这个方法接收查询字符串,返回一组文档。

from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
from langchain.document_loaders import TextLoader
from langchain.indexes import VectorstoreIndexCreator
loader = TextLoader('../state_of_the_union.txt', encoding='utf8')# 对加载的内容进行索引
index = VectorstoreIndexCreator().from_loaders([loader])query = "What did the president say about Ketanji Brown Jackson"# 通过query的方式找到语义检索的结果
index.query(query)

2.5 Memory(记忆)

一般在与 LLM 的交互过程中,模型是无法记住之前对话的历史消息的,无法实现跨越上下文的流畅对话应用。ChatGPT 提供了短期的 Memory(记忆),在每个交互 session 的问答里 ChatGPT 都能记住这个对话的上文(通过每次请求时把之前的问答 token 传给 OpenAI 来实现),但在新的交互 session 里 ChatGPT 就没有之前 session 的记忆。 LangChain 提供了多种不同的记忆形式,开发者可以选择存储完整记忆、仅保留最后几轮对话记忆或是限制存储的 token 数等。除此之外,开发者也可以选择将对话历史存储在向量数据库中,或是将某些特定实体的信息记忆起来。

第一次发送:

import openaiopenai.ChatCompletion.create(model="gpt-3.5-turbo",messages=[{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": "Hello"},]
)

第二次发送就要带上我们第一次的记录:

import openaiopenai.ChatCompletion.create(model="gpt-3.5-turbo",messages=[{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": "Hello"},{"role": "assistant", "content": "Hello, how can I help you?"},{"role": "user", "content": "who is more stylish Pikachu or Neo"},]
)

langchain 提供了不同的 Memory 组件完成内容记忆,如下是目前提供的组件。

2.5.1. ConversationBufferMemory

该组件类似我们上面的描述,只不过它会将聊天内容记录在内存中,而不需要每次再手动拼接聊天记录。

2.5.2. ConversationBufferWindowMemory

相比较第一个记忆组件,该组件增加了一个窗口参数,会保存最近K轮的聊天内容。

2.5.3. ConversationTokenBufferMemory

在内存中保留最近交互的缓冲区,并使用 token 长度而不是交互次数来确定何时刷新交互。

2.5.4. ConversationSummaryMemory

相比第一个记忆组件,该组件只会存储一个用户和机器人之间的聊天内容的摘要。

2.5.5. ConversationSummaryBufferMemory

结合了上面两个思路,存储一个用户和机器人之间的聊天内容的摘要并使用 token 长度来确定何时刷新交互。

2.5.6. VectorStoreRetrieverMemory

它是将所有之前的对话通过向量的方式存储到 VectorDB(向量数据库)中,在每一轮新的对话中,会根据用户的输入信息,匹配向量数据库中最相似的 K 组对话。

2.6. Agents(代理)

一些应用程序需要根据用户输入灵活地调用 LLM 和其他工具的链。代理接口为这样的应用程序提供了灵活性。代理可以访问一套工具,并根据用户输入确定要使用哪些工具。我们可以简单的理解为他可以动态的帮我们选择和调用 chain 或者已有的工具。代理主要有两种类型 Action agents 和 Plan-and-execute agents。

2.6.1. Action agents

行为代理: 在每个时间步,使用所有先前动作的输出来决定下一个动作。下图展示了行为代理执行的流程。
在这里插入图片描述

2.6.2. Plan-and-execute agents

预先决定完整的操作顺序,然后执行所有操作而不更新计划,下面是其流程。

● 接收用户输入

● 计划要采取的完整步骤顺序

● 按顺序执行步骤,将过去步骤的输出作为未来步骤的输入传递

三、构建本地知识库问答机器人

导入os,设置环境变量。导入OpenAI嵌入模型、Chroma向量数据库、文本分割器、OpenAI模型、向量数据库数据查询模块及文件夹文档加载器


import os
os.environ["OPENAI_API_KEY"] = '你的api key'
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain import OpenAI,VectorDBQA
from langchain.document_loaders import DirectoryLoader# 获取当前脚本所在的目录
base_dir = os.path.dirname(os.path.abspath(__file__))# 构建doc.txt文件的路径
doc_Directory = os.path.join(base_dir, 'static')# 加载文件夹中的所有txt类型的文件
loader = DirectoryLoader(doc_Directory, glob='**/*.txt')# 将数据转成 document 对象,每个文件会作为一个 document
documents = loader.load()# 初始化加载器
text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)# 切割加载的 document
split_docs = text_splitter.split_documents(documents)# 初始化 openai 的 embeddings 对象
embeddings = OpenAIEmbeddings()# 持久化数据
docsearch = Chroma.from_documents(split_docs, embeddings, persist_directory="D:/vector_store")
docsearch.persist()# 从已有文件中加载数据
docsearch = Chroma(persist_directory="D:/vector_store", embedding_function=embeddings)# 创建问答对象
qa = VectorDBQA.from_chain_type(llm=OpenAI(), chain_type="stuff", vectorstore=docsearch,return_source_documents=True)# 进行问答
result = qa({"query": "中国面积多大?"})

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

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

相关文章

与云栖的浪漫邂逅:记一段寻找云端之美的旅程

云端之旅 2023 年的云栖大会如约而至,这次云栖大会也是阿里新任掌门蔡老板当任阿里巴巴董事局主席以来的第一次。大会与以往有很多不一样的地方,其中 AIGC 更是本届大会的重点议题!你会感叹,阿里还是猛啊! 我逛了下展…

高校教务系统登录页面JS分析——天津大学

高校教务系统密码加密逻辑及JS逆向 本文将介绍天津大学教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文,你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习,勿用于非法用途。 一、密…

优先队列----数据结构

概念 不知道你玩过英雄联盟吗?英雄联盟里面的防御塔会攻击离自己最近的小兵,但是如果有炮车兵在塔内,防御塔会优先攻击炮车(因为炮车的威胁性更大),只有没有兵线在塔内时,防御塔才会攻击英雄。…

Azure机器学习 - 在 Azure 机器学习中上传、访问和浏览数据

目录 一、环境准备二、设置内核三、下载使用的数据四、创建工作区的句柄五、将数据上传到云存储空间六、访问笔记本中的数据七、创建新版本的数据资产八、清理资源 机器学习项目的开始阶段通常涉及到探索性数据分析 (EDA)、数据预处理(清理、特征工程)以…

Android 10.0 framework关于systemUI定制之导航栏透明背景的功能实现

1.概述 在10.0的系统产品定制化开发中,在对于系统原生SystemUI的导航栏背景在沉浸式导航栏的 情况下默认是会随着背景颜色的变化而改变的,在一些特定背景下导航栏的背景也是会改变的,所以由于产品开发需要 要求需要设置导航栏背景为透明的,所以就需要在Activity创建的时候…

【贝叶斯回归】【第 1 部分】--pyro库应用

Bayesian Regression - Introduction (Part 1) — Pyro Tutorials 1.8.6 documentation 一、说明 我们很熟悉线性回归的问题,然而,一些问题看似不似线性问题,但是,用贝叶斯回归却可以解决。本文使用土地平整度和国家GDP的关系数据…

10.30寄存器,寄存器堆

寄存器 8位环形移位寄存器 module shift_regist (input wire clk,input wire rstn,input wire [7:0]D,output reg [7:0]Q ); always (posedge clk or negedge rstn) beginif(!rstn)Q<8b000000;elseQ<{D[6:0],D[7]} ; end endmodule //shift_regist 输入有时钟…

gRPC源码剖析-Builder模式

一、Builder模式 1、定义 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的的表示。 2、适用场景 当创建复杂对象的算法应独立于该对象的组成部分以及它们的装配方式时。 当构造过程必须允许被构造的对象有不同的表示时。 说人话&#xff1a…

Ubuntu 使用 nginx 搭建 https 文件服务器

Ubuntu 使用 nginx 搭建 https 文件服务器 搭建步骤安装 nginx生成证书修改 config重启 nginx 搭建步骤 安装 nginx生成证书修改 config重启 nginx 安装 nginx apt 安装&#xff1a; sudo apt-get install nginx生成证书 使用 openssl 生成证书&#xff1a; 到对应的路径…

Android广播BroadcastReceiver

BroadcastReceiver组件 BroadcastReceiver是Android中的一个组件&#xff0c;用于接收和处理系统广播或应用内广播。它可以监听系统事件或应用内自定义的广播&#xff0c;并在接收到广播时执行相应的操作。 广播是一种用于在应用组件之间传递消息的机制。通过发送广播&#x…

C++STL---Vector、List所要掌握的基本知识

绪论​ 拼着一切代价&#xff0c;奔你的前程。 ——巴尔扎克&#xff1b;本章主要围绕vector和list的使用&#xff0c;以及容器底层迭代器失效问题&#xff0c;同时会有对原码的分析和模拟实现其底层类函数。​​​​话不多说安全带系好&#xff0c;发车啦&#xff08;建议电脑…

数据结构(三):栈及面试常考的算法

一、栈介绍 1、定义 栈也是一种数据呈线性排列的数据结构&#xff0c;不过在这种结构中&#xff0c;我们只能访问最新添加的数据。从栈顶放入元素的操作叫入栈&#xff0c;取出元素叫出栈。 2、优缺点及使用场景 优点&#xff1a;高效的操作、简单易用、空间效率高等 缺点&…