【AI大模型应用开发】【LangChain系列】4. 从Chain到LCEL:探索和实战LangChain的巧妙设计

大家好,我是【同学小张】。持续学习,关注我,跟我一起学AI大模型技能。

今天这篇文章我们来学习一下LangChain中的核心思想,也可以说是最核心的价值所在:Chain模块和LCEL语言。

Chain(链)应该是LangChain的核心思想和价值了。

Chain(链)指的是调用序列------无论是对LLM、工具还是数据预处理步骤。目前LangChain内的主要表现形式和实现方式是使用LCEL(LangChain Expression Language,LangChain声明式语言)。

看了上面的介绍可能还是比较懵,下面我们以一个例子来看,LangChain中的链。

0. 从一个例子开始了解LangChain的Chain是什么

python 复制代码
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步)。

bash 复制代码
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中。

这块代码如下(比较难理解):

python 复制代码
...... 其它代码不变 ......

## 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提供了一种规范化的描述方式,有助于团队成员之间更好地沟通和理解模块之间的依赖关系,减少误解和歧义。

更多好处可参考官方说明: python.langchain.com/docs/expres...

3. 总结

本文我们通过例子来了解了LangChain中的核心思想:Chain和其特有的语法 LCEL。然后通过LCEL重写了RAG流程。最后简单了解了一下LCEL的意义和优点。相信大家通过本文会对Chain和LCEL有一个比较直观的认知。

如果觉得本文对你有帮助,麻烦点个赞和关注呗 ~~~


  • 大家好,我是同学小张
  • 欢迎 点赞 + 关注 👏,促使我持续学习 ,持续干货输出。
  • +v: jasper_8017 一起交流💬,一起进步💪。
  • 微信公众号也可搜【同学小张】 🙏
  • 踩坑不易,感谢关注和围观

本站文章一览:

相关推荐
智圣新创011 分钟前
教育新基建下高校数据治理决策转型:智圣新创决策中台全域建设效能提升路径
大数据·人工智能
Tokenge2 分钟前
什么是CodeBuddy ,和WorkBuddy 有什么区别!
人工智能
桃西西呀3 分钟前
Spring AI Alibaba 之七:我没写一行 tracing 埋点,Agent 每一步却被 Micrometer 看得清清楚楚
人工智能·spring·llm
桃西西呀4 分钟前
Spring AI Alibaba 之八:我让 Agent 直接执行 shell 命令,它却读到了目录里的密钥,沙箱到底防住了什么
人工智能·spring·llm
Hi202402174 分钟前
Vortex GPGPU Warp 调度与指令执行流程解读
人工智能·gpgpu
9i编程6 分钟前
9. 教 AI 上班:带出我的数字同事 —— 部署到本机再大回归:组织架构又错,被我叫停先改树
人工智能·openai·ai编程
遇码8 分钟前
system prompt 里拼了时间和知识大纲,每轮缓存都被击穿:静态/动态分离的提示词装配方法
人工智能·ai·大模型
a932011 分钟前
基准真值也会错:我的 AI 审计管线漏报 12 题,先别急着调模型
人工智能
俊哥V12 分钟前
每日 AI 研究简报 · 2026-10-10
人工智能·ai
程序人生88814 分钟前
PDF 转 Word 版式错乱、表格丢失?DocConverter Web 格式互转引擎的保真实践
前端·人工智能·opencv·机器学习·pdf·word