在Langchain中,“链”(Chains)指的是一个概念上的组件或模块,它能够处理输入并产生输出。这个概念类似于编程中的函数或者工作流中的步骤,其中每个链都可以执行特定的任务,如文本生成、问答、翻译等。链条的设计目的是为了提供一种方式来组织和连接不同的自然语言处理(NLP)任务,使得这些任务可以以有序的方式相互作用,从而构建出更加复杂的AI应用。
LangChain中的链就是通过将多个语言模型组件(比如提示词模板、模型等) 按照一定逻辑顺序连接起来,像一条链条一样,让数据能依次经过这些组件,最终得到想要的结果。
打个比方,就好比在厨房做饭,一道复杂的菜可能需要先切菜(相当于一个组件对数据初步处理)、然后炒菜(另一个组件进行进一步加工)、最后装盘(再一个组件呈现结果),这几个步骤连成一个链条,一步步完成整个烹饪过程。在 LangChain 里,链也是这样,把不同功能的组件组合起来,协同完成一个复杂的任务,比如生成一个完整的分析报告、进行复杂的问答等。
from langchain_openai import ChatOpenAI
chat_model = ChatOpenAI(
openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
base_url="https://api.siliconflow.cn/v1",
model="Qwen/Qwen2.5-7B-Instruct"
)
from langchain.prompts.chat import SystemMessagePromptTemplate, HumanMessagePromptTemplate, ChatPromptTemplate
# 创建一个系统消息,定义机器人角色
system_msg = SystemMessagePromptTemplate.from_template(
"你是一个有用的机器人。"
)
# 创建一个人类消息
human_msg = HumanMessagePromptTemplate.from_template(
"{user_question}"
)
# 将模板结合成一个完整的聊天提示
chat_prompt = ChatPromptTemplate.from_messages([
system_msg,
human_msg
])
from langchain.chains.llm import LLMChain
# # 在旧版本中
# # 创建一个LLMChain
# llm_chain = LLMChain(llm=chat_model, prompt=chat_prompt, verbose=True)
# # 测试
# response = llm_chain("你好")
# print(response)
# 新版本
# runnableSequence 链
chain = chat_prompt | chat_model
print(chain)
response = chain.invoke({"user_question": "你好"}).content
print(response)
运行效果:
first=ChatPromptTemplate(input_variables=['user_question'], input_types={}, partial_variables={}, messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], input_types={}, partial_variables={}, template='你是一个有用的机器人。'), additional_kwargs={}), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['user_question'], input_types={}, partial_variables={}, template='{user_question}'), additional_kwargs={})]) middle=[] last=ChatOpenAI(client=<openai.resources.chat.completions.completions.Completions object at 0x000000001E932F50>, async_client=<openai.resources.chat.completions.completions.AsyncCompletions object at 0x000000001EDEC710>, root_client=<openai.OpenAI object at 0x000000001E2ECDD0>, root_async_client=<openai.AsyncOpenAI object at 0x000000001E9E5CD0>, model_name='Qwen/Qwen2.5-7B-Instruct', model_kwargs={}, openai_api_key=SecretStr('**********'), openai_api_base='https://api.siliconflow.cn/v1')
你好!很高兴见到你。有什么我可以帮助你的吗?
我们可以观察调试信息了解LLMChain的过程,输入的提示词先被格式化为完整的提示词然后输入LLM结束链得到答案。因此链的作用就是将Prompt和LLM链接起来,构成一个完整的应用。
检索链是Langchain中的一种特殊类型的链,主要用于从大量的文档数据集中检索相关信息,并且通常与向量数据库(如Chroma、Pinecone、Faiss等)结合使用。检索链可以帮助我们在处理如知识库查询、文档搜索等场景时,更有效地找到相关的文档片段,并且利用这些文档片段来生成准确的回答。
检索增强是指将检索技术与生成式模型相结合的一种方法。在传统的生成式模型中,所有的知识都是基于模型在训练阶段学到的信息。而在RAG中,当模型接收到一个查询时,它不仅依赖于自身的知识,还会从外部数据源(如文档、数据库或其他形式的知识库)检索相关信息来辅助生成答案。这种方法特别适用于需要提供精确信息的场景,比如法律咨询、医疗诊断等领域。
词嵌入是自然语言处理(NLP)领域的一个重要概念,指的是将文本中的词汇或短语映射到多维向量空间的技术。这些向量不仅捕捉了词语的意义,还反映了词语之间的语义关系。在检索增强的上下文中,词嵌入用于将文档转换成向量形式,以便能够进行高效的相似度比较。向量数据库存储这些向量,并且可以快速地根据新的查询向量来检索出最相似的文档。
下面是一个简单的检索链的例子:
首先安装依赖:
pip install sentence-transformers==3.3.0
pip install faiss-cpu==1.9.0
pip install langchain-huggingface==0.1.2
sentence-transformers 是一个Python库,它提供了预训练的模型来计算句子、段落或短文本的嵌入向量(embeddings)。这些嵌入向量可以用来比较文本之间的相似度,进行聚类分析,或者作为其他机器学习任务的输入特征。这个库基于Transformers架构,如BERT、RoBERTa等,并且已经针对特定任务进行了微调,使得用户能够轻松地获取高质量的语义表示。
faiss-cpu 是Facebook Research开发的一个高效相似度搜索库的CPU版本。Faiss旨在帮助开发者快速地从大量的向量中找到与查询向量最相似的一组向量。这在推荐系统、图像检索、文档相似度匹配等领域有着广泛的应用。Faiss支持多种距离度量方法,包括余弦相似度、欧氏距离等,并且优化了索引结构以提高搜索速度。这两个库经常一起使用,首先,使用 sentence-transformers 将文本转换为嵌入向量;然后,利用 faiss-cpu 构建高效的索引结构,以便快速地在大量文本嵌入中进行相似性搜索。这样的组合不仅提高了处理效率,也简化了开发流程。例如,在构建一个问答系统时,可以先将所有可能的问题转化为嵌入,再通过Faiss建立索引,当用户提出新问题时,系统可以通过查找最相似的问题来提供答案。
首先,我们需要创建一个向量存储,这通常涉及到将文档转换成嵌入向量,并存储到数据库中。需要在魔搭下载Embedding模型:
from modelscope.hub.snapshot_download import snapshot_download
emb_model_dir = snapshot_download('AI-ModelScope/bge-large-zhv1.5',cache_dir='models')
完整代码如下:
from langchain_openai import ChatOpenAI
chat_model = ChatOpenAI(
openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
base_url="https://api.siliconflow.cn/v1",
model="Qwen/Qwen2.5-7B-Instruct"
)
from langchain_community.document_loaders import TextLoader
loader = TextLoader("黑悟空.txt", encoding="utf-8")
docs = loader.load()
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 块大小200字为一组,每组之间20字重叠
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=20)
chunks = text_splitter.split_documents(docs) # 分割成块
from langchain_huggingface import HuggingFaceEmbeddings
embedding = HuggingFaceEmbeddings(model_name="models/AI-ModelScope/bge-large-zh-v1___5")
# 构建faiss向量存储
from langchain_community.vectorstores import FAISS
vs = FAISS.from_documents(chunks, embedding) # 将文本块转换为向量并且存储到FAISS
retriever = vs.as_retriever() # 创建检索器用于从数据库中获取相关信息
from langchain.chains import RetrievalQA
from langchain.prompts import SystemMessagePromptTemplate, HumanMessagePromptTemplate, ChatPromptTemplate
system_msg = SystemMessagePromptTemplate.from_template(
"根据以下已知的内容回答用户问题。\n 已知消息{context}"
)
huamn_msg = HumanMessagePromptTemplate.from_template(
"用户问题:{question}"
)
chat_prompt = ChatPromptTemplate.from_messages([
system_msg, huamn_msg
])
qa = RetrievalQA.from_chain_type(llm=chat_model,
chain_type="stuff",
retriever=retriever,
chain_type_kwargs={"prompt": chat_prompt})
user_question = "黑熊精自称为?"
related_docs = retriever.invoke(user_question)
# # 打印相关文档的内容
for i, doc in enumerate(related_docs):
print(f"文档 {i + 1}")
print(doc.page_content)
print("--" * 40)
print(qa.invoke(user_question))
运行效果:
文档 1
金池一路坐到观音禅院的长老(毕竟学过长生术,活的最久),受人顶礼膜拜,欲望也随之膨胀。
黑熊精手下有一个是苍狼精,起了个道号叫凌虚子。还有一个是白花蛇怪,一般称呼为白衣秀士。二人虽都和黑熊精结拜,但二者各有派别。凌虚子管着一群狼妖,而白衣秀士则管着一群蛇妖。
--------------------------------------------------------------------------------
文档 2
黑熊精见这招不合适,又扶持了一个够强的狼妖,给他起名灵虚子(和凌虚子音同字不同,可能也是黑熊精没啥文化)。但这灵虚子是从狮驼国跑过来的外来户,而且修炼法门过于血腥暴力,引起黑风山本地狼妖的不满,并最终差点引起灵虚子对本地狼妖的大屠杀。不过最终被新任蛇妖统领白衣秀士阻止。
--------------------------------------------------------------------------------
文档 3
黑熊精对这些事有种无力感,于是重建观音禅院,想用复活凌虚子的法术复活金池长老,以解寂寞。没想到金池的魂魄依然惦记着他生前藏着的财物,没有复活到肉身之上,而是复活到了财物上,成了一个精神不正常的大头怪物。
这期间还发生了一件小事:
黑风山的土地遇到一个老道士。老道士和土地相谈甚欢,然后教了土地定身法和聚形散气等技能。
--------------------------------------------------------------------------------
文档 4
第一章
在西天取经的几百年前,黑风山上有一只黑熊精占山为王,自称黑风大王。
有一天,黑熊精碰到了一个小和尚。他觉得这个小和尚蛮有趣,于是给了他一些金银财宝,又教给他一些长生的法门。这个小和尚就是后来的金池长老,二人从此结缘。
在这之后,金池也给黑熊精讲一些佛法,黑熊精也有点兴趣,二人也算是有共同话题的朋友。
--------------------------------------------------------------------------------
{'query': '黑熊精自称为?', 'result': '黑风大王'}
通过Langchain LCEL表达式可以轻松自定义链。例如我们构造一条简单链接受用户的输入并给出回答。
from langchain_openai import ChatOpenAI
chat_model = ChatOpenAI(
openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
base_url="https://api.siliconflow.cn/v1",
model="Qwen/Qwen2.5-7B-Instruct"
)
# 创建聊天模板,包含占位符topic
from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("说出一句包含{topic}的诗句")
# 创建一个字符串输出解析器
from langchain_core.output_parsers import StrOutputParser
output_parser = StrOutputParser()
# 构造一个链,依次包含提示模板、语言模型,输出解析器
chain = prompt | chat_model | output_parser
print(chain.invoke({"topic": "花"}))
运行效果:
花开堪折直须折,莫待无花空折枝。
from langchain_openai import ChatOpenAI
chat_model = ChatOpenAI(
openai_api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
base_url="https://api.siliconflow.cn/v1",
model="Qwen/Qwen2.5-7B-Instruct"
)
# 创建聊天模板,包含占位符topic
from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("""
接下来的四字成语必须以上一个成语“{pre_cy}”的最后一个字为开头。
例如:上一个成语是“兴高采烈”,那么下一个成语应该是以“烈”开头的成语。
请给出成语“{pre_cy}”接下来的接龙成语:
""")
# 创建一个字符串输出解析器
from langchain_core.output_parsers import StrOutputParser
output_parser = StrOutputParser()
# 构造一个链,依次包含提示模板、语言模型,输出解析器
chain = prompt | chat_model | output_parser
while True:
cy = input("给出成语:")
init_cy = cy
print("AI回答:", chain.invoke({"pre_cy": init_cy}))
运行效果:
给出成语:白日做梦
AI回答: 梦笔生花
给出成语:花开富贵
AI回答: 好的,根据您的要求,以“花开富贵”的最后一个字“贵”为开头的四字成语是:“贵人多忘”。
给出成语: