大家好,我是【同学小张】。持续学习,持续干货输出,关注我,跟我一起学AI大模型技能。
今天这篇文章我们来学习一下LangChain中的核心思想,也可以说是最核心的价值所在:Chain模块和LCEL语言。
Chain(链)应该是LangChain的核心思想和价值了。
Chain(链)指的是调用序列——无论是对LLM、工具还是数据预处理步骤。目前LangChain内的主要表现形式和实现方式是使用LCEL(LangChain Expression Language,LangChain声明式语言)。
看了上面的介绍可能还是比较懵,下面我们以一个例子来看,LangChain中的链。
0. 从一个例子开始了解LangChain的Chain是什么
import os # 加载 .env 到环境变量 from dotenv import load_dotenv, find_dotenv _ = load_dotenv(find_dotenv()) from langchain_openai import ChatOpenAI llm = ChatOpenAI() # 默认是gpt-3.5-turbo from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate prompt_template = """ 我的名字叫【{name}】,我的个人介绍是【{description}】。 请根据我的名字和介绍,帮我想一段有吸引力的自我介绍的句子,以此来吸引读者关注和点赞我的账号。 """ prompt = ChatPromptTemplate.from_template(prompt_template) output_parser = StrOutputParser() chain = prompt | llm | output_parser response = chain.invoke({"name": "同学小张", "description": "热爱AI,持续学习,持续干货输出"}) print(response)
运行结果:
看到代码中的chain = prompt | llm | output_parser
这一行了吗?这就是Chain,也是LCEL。|
符号类似于unix管道操作符,它将不同的组件链接在一起,将一个组件的输出作为下一个组件的输入。在以上示例代码中,用户输入传给prompt,prompt组装结果传给llm,llm结果传给output_parser。
1. 用LCEL重写RAG流程
下面我会从我的思考步骤和探索过程,来一步步将RAG用LCEL写出来。
1.1 先将Prompt和llm连起来
因为我们之前已经用LangChain写过RAG,基本元素都有了,所以最简单的,我们先把 prompt模板 和 llm 连起来,prompt模板的输出给llm作为输入(注释中的第5步和第6步)。
import os # 加载 .env 到环境变量 from dotenv import load_dotenv, find_dotenv _ = load_dotenv(find_dotenv()) from langchain_openai import ChatOpenAI llm = ChatOpenAI() # 默认是gpt-3.5-turbo ## 1. 文档加载 from langchain.document_loaders import PyPDFLoader loader = PyPDFLoader("D:\GitHub\LEARN_LLM\RAG\如何向 ChatGPT 提问以获得高质量答案:提示技巧工程完全指南.pdf") pages = loader.load_and_split() ## 2. 文档切分 from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=200, chunk_overlap=100, length_function=len, add_start_index=True, ) paragraphs = [] for page in pages: paragraphs.extend(text_splitter.create_documents([page.page_content])) ## 3. 文档向量化,向量数据库存储 from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma db = Chroma.from_documents(paragraphs, OpenAIEmbeddings()) ## 4. 向量检索 retriever = db.as_retriever() docs = retriever.get_relevant_documents("什么是角色提示?") for doc in docs: print(f"{doc.page_content}\n-------\n") ## 5. 组装Prompt模板 prompt_template = """ 你是一个问答机器人。 你的任务是根据下述给定的已知信息回答用户问题。 确保你的回复完全依据下述已知信息。不要编造答案。 如果下述已知信息不足以回答用户的问题,请直接回复"我无法回答您的问题"。 已知信息: {info} 用户问: {question} 请用中文回答用户问题。 """ from langchain.prompts import PromptTemplate template = PromptTemplate.from_template(prompt_template) # prompt = template.format(info=docs[0].page_content, question='什么是角色提示?') ## 6. 执行chain chain = template | llm response = chain.invoke({"info": docs[0].page_content, "question": "什么是角色提示?"}) ## 给template的输入,多个变量,invoke以字典形式 print(response.content)
测试运行可以正常运行和输出结果。然后继续往前连接。
1.2 将retriever加进来
Prompt模板需要的是检索得到的文档块和用户提问。
retriver的输入是用户query。而用户query也需要跨过retriver,直接放到prompt中。
这块代码如下(比较难理解):
...... 其它代码不变 ...... ## 4. 向量检索 retriever = db.as_retriever() ...... 其它代码不变 ...... from langchain_core.runnables import RunnableParallel, RunnablePassthrough setup_and_retrieval = RunnableParallel( {"info": retriever, "question": RunnablePassthrough()} ) ## 6. 执行chain chain = setup_and_retrieval | template | llm response = chain.invoke("什么是角色提示?") ## 给retriver的输入,以字符串形式 print(response.content)
首先应该重点关注下chain.invoke
的输入形式的变化,一个是字典,一个是字符串。
然后,主要是setup_and_retrieval
比较难以理解,RunnableParallel
和RunnablePassthrough
都是新词儿~
RunnablePassthrough
是LangChain框架中的一个组件,它允许将输入数据不经修改地传递给下一个步骤,这通常与RunnableParallel
一起使用。所以,setup_and_retrieval的意思:
- info字段接收retriver的输出
- question接收用户的输入,将用户的输入不经修改地传递过来。
这样,我们就把RAG的流程串了起来(前面向量数据库的创建和数据灌入是离线步骤,与这个完全分离开的,不用放到本次的chain里面)。
2. 为什么要有LCEL
LCEL将各个模块和接口统一,并封装了流支持、异步支持、并行执行、重试和回退、访问中间结果等,极大地方便了应用各模块的开发。
LCEL的意义包括:
- 定义依赖关系:LCEL提供了一种清晰的语言结构,可以用来明确定义模块之间的依赖关系,包括模块的输入、输出和传递规则等。
- 解耦模块:通过LCEL,可以将模块之间的依赖关系明确地表达出来,有助于解耦模块,使得系统更易于维护和扩展。
- 可视化依赖:LCEL可以用于生成依赖关系图,帮助开发人员更直观地了解模块之间的依赖关系,从而更好地进行系统设计和优化。
- 规范化描述:LCEL提供了一种规范化的描述方式,有助于团队成员之间更好地沟通和理解模块之间的依赖关系,减少误解和歧义。
更多好处可参考官方说明:
https://python.langchain.com/docs/expression_language/why
3. 总结
本文我们通过例子来了解了LangChain中的核心思想:Chain和其特有的语法 LCEL。然后通过LCEL重写了RAG流程。最后简单了解了一下LCEL的意义和优点。相信大家通过本文会对Chain和LCEL有一个比较直观的认知。
如果觉得本文对你有帮助,麻烦点个赞和关注呗 ~~~
- 大家好,我是同学小张
- 欢迎 点赞 + 关注 👏,促使我持续学习,持续干货输出。
- +v: jasper_8017 一起交流💬,一起进步💪。
- 微信公众号也可搜【同学小张】 🙏
- 踩坑不易,感谢关注和围观
本站文章一览: