LangChain 简介
LangChain 是一个开源框架,设计用于开发和部署与语言模型(如大型语言模型LLM)交互的应用程序。它提供了一种简便的方法来构建基于自然语言处理(NLP)的系统,这些系统可以执行各种任务,例如问答、文本生成、文档检索等。LangChain 的主要目标是简化开发过程,使开发者能够快速地将强大的语言模型功能集成到他们的应用程序中。
LangChain 包含三个关键组件:
-
链(Chains):这是执行特定任务的模块化工作流。链条可以简单也可以复杂,取决于任务需求。它们可以是简单的问答机制,也可以是涉及多个步骤的复杂流程。
-
语言模型(Language Models):这些是预训练的模型,可以生成或修改文本。LangChain 支持多种语言模型,包括那些可以从外部服务调用的模型。
-
代理(Agents):代理是更高级的概念,它们使用链条来自主地执行任务。代理可以根据环境或输入数据的变化来决定使用哪些链条,从而实现一定程度的自动化决策。
LangChain 还支持与文档数据库的交互,这使得从结构化数据中提取信息变得容易,并且可以通过 API 调用来集成其他服务。开发者可以利用 LangChain 来创建聊天机器人、虚拟助手、自动化的写作工具以及其他依赖于语言理解和生成的应用程序。
LangChain 官方地址 https://python.langchain.com/v0.2/docs/introduction/
快速上手
创建一个文件,例如“chainlit_chat”
mkdir chainlit_chat
进入 chainlit_chat
文件夹下,执行命令创建python 虚拟环境空间(需要提前安装好python sdk
。 Chainlit
需要python>=3.8
。,具体操作,由于文章长度问题就不在叙述,自行百度),命令如下:
python -m venv .venv
- 这一步是避免python第三方库冲突,省事版可以跳过
.venv
是创建的虚拟空间文件夹可以自定义
接下来激活你创建虚拟空间,命令如下:
#linux or mac
source .venv/bin/activate
#windows
.venv\Scripts\activate
在项目根目录下创建requirements.txt
,内容如下:
langchain
chainlit
openai
chromadb
tiktoken
pymupdf
langchain_community
dashscope~=1.20.3
执行以下命令安装依赖:
pip install -r .\requirements.txt
- 安装后,项目根目录下会多出
.chainlit
和.files
文件夹和chainlit.md
文件
方案1
使用通义千问的openai
兼容接口和智谱清言的向量化open ai
接口实现知识库问答
在项目根目录下创建.env
环境变量,配置如下:
OPENAI_API_BASE="https://dashscope.aliyuncs.com/compatible-mode/v1"
OPENAI_API_KEY="ypur-api_key"
OPENAI_API_BASE
是 openai的api 的base地址,如果你能直接使用openai,这可以不设置,这里我用的是阿里的open ai兼容接口OPENAI_API_KEY
替换成你自己的API 密匙- 阿里模型接口地址 https://dashscope.console.aliyun.com/model
在项目根目录下创建app.py文件,代码如下:
from pathlib import Path
from typing import List
import chainlit as cl
from langchain.callbacks.base import BaseCallbackHandler
from langchain.prompts import ChatPromptTemplate
from langchain.schema import Document
from langchain.schema import StrOutputParser
from langchain.schema.runnable import Runnable, RunnablePassthrough, RunnableConfig
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import (
PyMuPDFLoader, CSVLoader, TextLoader, Docx2txtLoader
)
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_openai import ChatOpenAI
chunk_size = 1024
chunk_overlap = 100
FILE_STORAGE_PATH = "data_file"
embeddings_model = OpenAIEmbeddings(base_url='https://open.bigmodel.cn/api/paas/v4', api_key='your-pai-key',
model='embedding-3')
@cl.cache
def process_files(file_storage_path: str):
file_directory = Path(file_storage_path)
docs = [] # type: List[Document]
text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
for file_path in file_directory.glob("*.pdf"):
loader = PyMuPDFLoader(str(file_path))
docs += text_splitter.split_documents(loader.load())
for file_path in file_directory.glob("*.csv"):
loader = CSVLoader(str(file_path), encoding="UTF-8")
docs += text_splitter.split_documents(loader.load())
for file_path in file_directory.glob("*.txt"):
loader = TextLoader(str(file_path), encoding="UTF-8")
docs += text_splitter.split_documents(loader.load())
for file_path in file_directory.glob("*.doc"):
loader = Docx2txtLoader(str(file_path))
docs += text_splitter.split_documents(loader.load())
vector_store = Chroma.from_documents(docs, embeddings_model)
return vector_store
doc_search = process_files(FILE_STORAGE_PATH)
retriever = doc_search.as_retriever(search_kwargs={"k": 4})
model = ChatOpenAI(model_name="qwen-plus", streaming=True)
@cl.on_chat_start
async def on_chat_start():
template = """Answer the question based only on the following context:
{context}
Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
runnable = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
cl.user_session.set("runnable", runnable)
@cl.on_message
async def on_message(message: cl.Message):
runnable = cl.user_session.get("runnable") # type: Runnable
msg = cl.Message(content="")
class PostMessageHandler(BaseCallbackHandler):
"""
Callback handler for handling the retriever and LLM processes.
Used to post the sources of the retrieved documents as a Chainlit element.
"""
def __init__(self, msg: cl.Message):
BaseCallbackHandler.__init__(self)
self.msg = msg
self.sources = [] # To store unique pairs
def on_retriever_end(self, documents, *, run_id, parent_run_id, **kwargs):
for d in documents:
print(d)
source_page_pair = (d.page_content, d.metadata['row'])
self.sources.append(source_page_pair) # Add unique pairs to the set
async def on_llm_end(self, response, *, run_id, parent_run_id, **kwargs):
if len(self.sources):
source_names = []
for page_content, row in self.sources:
source_name = f"source_{row}"
source_names.append(source_name)
self.msg.elements.append(
cl.Text(content=page_content, name=source_name, display="side")
)
await self.msg.stream_token(f"\n**数据来源**: {', '.join(source_names)}")
async for chunk in runnable.astream(
message.content,
config=RunnableConfig(callbacks=[
cl.LangchainCallbackHandler(),
PostMessageHandler(msg)
]),
):
await msg.stream_token(chunk)
await msg.send()
- 代码中的向量需要替换成自己智谱清言的
api_key
。 网站地址: https://open.bigmodel.cn/
方案2
只使用通义千问的DashScope
模型服务灵积的接口
在项目根目录下创建.env
环境变量,配置如下:
DASHSCOPE_API_KEY="sk-api_key"
DASHSCOPE_API_KEY
是阿里dashscope的服务的APIkey,代码中使用DashScope的sdk实现,所以不需要配置base_url。默认就是阿里的base_url。- 阿里模型接口地址 https://dashscope.console.aliyun.com/model
在项目根目录下创建app.py文件,代码如下:
from pathlib import Path
from typing import List
import chainlit as cl
from langchain.callbacks.base import BaseCallbackHandler
from langchain.prompts import ChatPromptTemplate
from langchain.schema import Document
from langchain.schema import StrOutputParser
from langchain.schema.runnable import Runnable, RunnablePassthrough, RunnableConfig
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import (
PyMuPDFLoader, CSVLoader, TextLoader, Docx2txtLoader
)
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.llms import Tongyi
from langchain_community.vectorstores import Chroma
chunk_size = 1024
chunk_overlap = 100
FILE_STORAGE_PATH = "data_file"
embeddings_model = DashScopeEmbeddings()
@cl.cache
def process_files(file_storage_path: str):
file_directory = Path(file_storage_path)
docs = [] # type: List[Document]
text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
for file_path in file_directory.glob("*.pdf"):
loader = PyMuPDFLoader(str(file_path))
docs += text_splitter.split_documents(loader.load())
for file_path in file_directory.glob("*.csv"):
loader = CSVLoader(str(file_path), encoding="UTF-8")
docs += text_splitter.split_documents(loader.load())
for file_path in file_directory.glob("*.txt"):
loader = TextLoader(str(file_path), encoding="UTF-8")
docs += text_splitter.split_documents(loader.load())
for file_path in file_directory.glob("*.doc"):
loader = Docx2txtLoader(str(file_path))
docs += text_splitter.split_documents(loader.load())
vector_store = Chroma.from_documents(docs, embeddings_model)
return vector_store
doc_search = process_files(FILE_STORAGE_PATH)
retriever = doc_search.as_retriever(search_kwargs={"k": 4})
model = Tongyi(model='qwen-turbo')
@cl.on_chat_start
async def on_chat_start():
template = """Answer the question based only on the following context:
{context}
Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
runnable = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
cl.user_session.set("runnable", runnable)
@cl.on_message
async def on_message(message: cl.Message):
runnable = cl.user_session.get("runnable") # type: Runnable
msg = cl.Message(content="")
class PostMessageHandler(BaseCallbackHandler):
"""
Callback handler for handling the retriever and LLM processes.
Used to post the sources of the retrieved documents as a Chainlit element.
"""
def __init__(self, msg: cl.Message):
BaseCallbackHandler.__init__(self)
self.msg = msg
self.sources = [] # To store unique pairs
def on_retriever_end(self, documents, *, run_id, parent_run_id, **kwargs):
for d in documents:
print(d)
source_page_pair = (d.page_content, d.metadata['row'])
self.sources.append(source_page_pair) # Add unique pairs to the set
async def on_llm_end(self, response, *, run_id, parent_run_id, **kwargs):
if len(self.sources):
source_names = []
for page_content, row in self.sources:
source_name = f"source_{row}"
source_names.append(source_name)
self.msg.elements.append(
cl.Text(content=page_content, name=source_name, display="side")
)
await self.msg.stream_token(f"\n\n **数据来源**: {', '.join(source_names)}")
async for chunk in runnable.astream(
message.content,
config=RunnableConfig(callbacks=[
cl.LangchainCallbackHandler(),
PostMessageHandler(msg)
]),
):
await msg.stream_token(chunk)
await msg.send()
在项目根目录下创建data_file文件夹
将你的文件放到这里,代码中设置的支持,pdf、doc、csv 、txt格式的文件,后续可以根据自己的需求增加更多,langchain带有很多格式文件的加载器,可以自行修改代码。
运行应用程序
要启动 Chainlit
应用程序,请打开终端并导航到包含的目录app.py。然后运行以下命令:
chainlit run app.py -w
- 该
-w
标志告知Chainlit
启用自动重新加载,因此您无需在每次更改应用程序时重新启动服务器。您的聊天机器人 UI 现在应该可以通过http://localhost:8000访问。 - 自定义端口可以追加
--port 80
启动后界面如下:
相关文章推荐
《Chainlit快速实现AI对话应用的界面定制化教程》
《Chainlit接入FastGpt接口快速实现自定义用户聊天界面》
《使用 Xinference 部署本地模型》
《Fastgpt接入Whisper本地模型实现语音输入》
《Fastgpt部署和接入使用重排模型bge-reranker》
《Fastgpt部署接入 M3E和chatglm2-m3e文本向量模型》
《Fastgpt 无法启动或启动后无法正常使用的讨论(启动失败、用户未注册等问题这里)》
《vllm推理服务兼容openai服务API》
《vLLM模型推理引擎参数大全》
《解决vllm推理框架内在开启多显卡时报错问题》
《Ollama 在本地快速部署大型语言模型,可进行定制并创建属于您自己的模型》