ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

啃 LangChain 第四章 LCEL:管道符 | 背后的世界,信息的交通

啃 LangChain 第四章 LCEL:管道符 | 背后的世界,信息的交通 为什么写这篇前三章模型、Message、结构化输出学完觉得自己入门了。结果一翻到第四章LCELLangChain Expression Language整个人比较蒙。prompt | llm | StrOutputParser()这行代码视频里老师敲得飞快我跟着敲也能跑。但让我讲讲|到底干了什么、RunnablePassthrough为什么存在、assign里的lambda x到底 x 是什么——全答不上来。这章是 LangChain 最抽象的部分也是后续所有高级功能RAG、Agent、记忆的地基。我手写了三张笔记反复啃了啃现在可以能用人话讲清楚了。、一、LCEL 是什么一个符号五种组件LCEL LangChain Expression Language核心就是管道符|。它把数据不断被处理、传递的过程写成一行管道高复用、不断输出。数据 → 处理A → 处理B → 处理C → 输出LangChain 里有 5 个核心组件名字都带Runnable可运行组件作用类比Runnable所有可上链对象的基类水管接口RunnableLambda把普通函数变成链上的一节把任意水泵接进水管RunnablePassthrough输入原样输出assign可加字段直通管 可加旁路RunnableParallel一个输入同时分发给多个并行处理分水器invoke/batch/stream任何链的调用方式开水龙头的三种姿势一句话所有能上链的东西都实现了 Runnable 接口所以能用|连起来。二、基础管道prompt | llm | StrOutputParser()这是最常见的链也是理解后续一切的起点。fromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.output_parsersimportStrOutputParser promptChatPromptTemplate.from_messages([(system,你是一个翻译。),(human,把这句话翻译成英文{text}),])chainprompt|llm|StrOutputParser()chain.invoke({text:你好})数据流转重点dict → [prompt] → PromptValue → [llm] → AIMessage → [StrOutputParser] → str每一节都是一个 Runnable上一节的输出是下一节的输入你传{text: 你好}dictprompt把 dict 填进模板产出PromptValue填好的消息llm调模型产出AIMessage模型回复StrOutputParser把 AIMessage 里的文本抽出来产出str|的本质把左边的输出作为右边的输入。三、RunnableLambda把普通函数接进水管链上的每一节必须是 Runnable。那普通函数怎么上链用RunnableLambda包一层。fromlangchain_core.runnablesimportRunnableLambdadef加粗(text:str)-str:returnf**{text}**chainprompt|llm|StrOutputParser()|RunnableLambda(加粗)print(chain.invoke({text:你好}))# 输出会被加粗适用场景链的中间需要自定义处理逻辑。比如对模型输出做后处理清洗、格式化、加标签。我手写笔记里特别标了一句可实现自定义的逻辑处理。这就是 RunnableLambda 存在的意义——让你不写子类也能往链里塞逻辑。四、RunnablePassthrough透传 assign 加字段这是我卡最久的一个概念。单独的 RunnablePassthrough价值不大RunnablePassthrough().invoke(hello)# 原样输出 hello输入啥输出啥单独用没意义。真正的威力在.assign()。assign在字典上动态加字段fromlangchain_core.runnablesimportRunnablePassthrough addRunnablePassthrough.assign(contextlambdax:检索到的文档,questionlambdax:问题,)add.invoke({question:什么是 RAG})# {question: 什么是 RAG, context: 检索到的文档, question2: 问题}assign 的 key 是你定义的字段名value 是函数接收原字典返回新值。为什么需要 assign核心问题我一开始最大的疑问是prompt 里的{context}不也是变量吗为什么必须用 assign 加字段答案是prompt 的{}只是空位它自己不会产生内容必须有人把值塞进去。如果值是用户手动提供的 → 不需要 assign直接chain.invoke({context: ..., question: ...})也没问题如果值需要系统自动生成比如检索文档→ 必须用 assign在管道运行时自动算出来在我看来有一种一个内部消息内部消化的感觉这就是 RAG 的核心用户只问问题系统自动检索文档assign 负责把检索结果塞进字典prompt 才能填空。chain(RunnablePassthrough.assign(contextlambdax:retriever.invoke(x[question])# 自动检索)|prompt|llm)chain.invoke({question:什么是 RAG})# context 由管道自动检索不用用户手动给易错点我亲手踩过assign 的 key 必须和 prompt 的{变量}对上——少了报错多了忽略value 必须是函数lambda不是写死的字符串——写死字符串是退化用法真实场景是动态计算lambda 的输入 x 是原字典——你可以用x[question]取到用户传的问题五、RunnableParallel并行分支基本用法fromlangchain_core.runnablesimportRunnableParallel parallelRunnableParallel(jokejoke_chain,poempoem_chain)outparallel.invoke({question:月亮})# {joke: 笑话内容, poem: 诗的内容}invoke 的输入字典会完整传给每一条链每条链各取所需joke_chain 收到完整字典只用questionpoem_chain 同样收到完整字典只用question多余的字段忽略缺少的字段报错那条链报错整个 invoke 失败字典简写等价写法# 写法1RunnableParallel 类parallel1RunnableParallel(jokejoke_chain,poempoem_chain)# 写法2直接写字典语法糖完全等价parallel2{joke:joke_chain,poem:poem_chain}LangChain 内部会自动把字典转成 RunnableParallel。日常基本都用字典简写。并行结果汇总并行返回的是字典key 是你起的子链名。要把结果拼成一段文字用 RunnableLambdadefcombine(d:dict)-str:returnf笑话{d[joke]}\n诗{d[poem]}finalparallel|RunnableLambda(combine)print(final.invoke({question:月亮}))并行结果是字典用自己定义的键名取出来。六、本章总结一张图理清RunnableLambda → 普通函数上链 RunnablePassthrough.assign → 字典动态加字段RAG 核心 RunnableParallel → 一个输入走多条链返回字典三者组合就能写出复杂的链chain({context:retriever,question:RunnablePassthrough()}# 并行检索 透传|prompt|llm|StrOutputParser())这行代码是 RAG 的经典写法拆开看就是 RunnableParallel RunnablePassthrough 基础管道的组合。写在最后学完这章最大的感受抽象的东西一定要配具体场景理解而且自己用笔去引导自己的思路也会更好RunnablePassthrough.assign单看定义我完全不知道它有什么用。直到写到 RAG 的代码——“用户只问问题系统自动检索文档塞给模型”——才突然明白哦assign 就是干这个的。如果你也在卡这章我的建议是别死磕概念先把prompt | llm | parser跑通然后去看 RAG 的最小实现回来再看 assign 和 parallel很多地方会突然通。以及自己感觉现在的自学和高中有不一样的感受高中方向太明确也可以说太窄了现在自己学的内容很适合主动发散不必追求太耀眼的结果思维在颅骨内一次次反射也是一种各位可以尝试的感受不稳会断但没有失控感。抽象概念理解起来费力是正常的硬啃效率低。把它放到具体场景里成本会降一半。关于作者平顶山学院 2026 级计算机专业新生AI 工程方向自学者。爬虫起步正在啃 LangChain目标做出能落地的 AI 系统。我的代码仓库Gitee | 个人博客small-mo.netlify.app
返回列表