当前位置: 首页 > news >正文

LCEL表达式语言,LangChain的管道式组合让代码更灵活

LCEL表达式语言,LangChain的管道式组合让代码更灵活

上一篇用LCEL写了三种链,管道符一路接到底,代码跑得也顺。但有个问题一直悬在那,管道符到底干了什么,RunnablePassthrough和RunnableLambda这些组件什么时候该用,itemgetter和bind又是怎么回事。这篇就把LCEL表达式语言掰开揉碎讲一遍,把这些零件的脾气摸清楚。


LCEL是什么

LCEL全称LangChain表达式语言,英文叫LangChain Expression Language。它是一套写法约定,让你用管道符把Runnable组件串起来,像搭积木一样拼出完整流程。

为什么要搞这么一套东西。早期LangChain写链要继承一堆类,LLMChain、SequentialChain、RouterChain,每个类参数不同,组合起来代码又长又乱。LCEL把这些全统一成一个接口,所有组件都是Runnable,都能用竖线连接。你只要记住一个符号,就是竖线。

竖线左边组件的输出,自动变成竖线右边组件的输入。和Unix管道一个道理,cat file | grep keyword,数据从左往右流。这种写法组合性强,想加一步就在中间插一个管道,想换掉某一步就替换那一段,其余代码一行不动。


管道符背后发生了什么

每个Runnable组件都实现了__or__方法,这是管道符的底层机制。你写a | b,Python实际调用的是a.__or__(b),返回一个新的Runnable,内部记录了a和b的顺序。invoke的时候先跑a,把结果喂给b。

这样设计有个好处,整条链本身也是一个Runnable对象,你能对它做任何Runnable能做的事。invoke同步调用,stream流式输出,batch批量处理,异步版ainvoke、astream、abatch都有。拼好一条链之后,同步切异步只要换个方法名,链本身一行不用动。我第一次发现这事的时候挺惊喜,之前为了上异步还重写过整条流程。

stream这个方法做聊天界面时特别实用。用户等不及整段输出,stream能把模型生成的字一个一个往外吐,前端接上就是打字机效果,等待感弱很多。batch也顺手,一坨输入丢进去批量跑,比循环调invoke快不少。


RunnablePassthrough,原样传递

RunnablePassthrough干的事情很简单,把输入原封不动传给输出。听着像废话,拼链的时候却特别有用。

RAG场景里,检索器需要用户的原始问题,提示词模板又需要检索到的文档,两个地方都要拿到输入。这时候就得让原始问题一路透传下去,RunnablePassthrough就是干这个的。

fromlangchain_core.runnablesimportRunnablePassthrough# 输入什么就输出什么print(RunnablePassthrough().invoke({"question":"什么是LCEL"}))# {'question': '什么是LCEL'}

它还有个assign方法,能在保留原输入的基础上追加新字段。下面RAG示例里你会看到它的用法,拿来做字段拼接很顺手。


RunnableLambda,把普通函数塞进流水线

RunnableLambda把任意Python函数包装成Runnable,这样你的自定义逻辑也能用管道符串起来。

fromlangchain_core.runnablesimportRunnableLambdadefto_upper(text):returntext.upper()chain=RunnableLambda(to_upper)print(chain.invoke("hello"))# HELLO

我踩过一个坑。函数接收的参数得和上一步输出对得上。上一步输出字符串,你的函数就得接收字符串,上一步输出字典,你的函数就得接收字典。类型不匹配报错信息有时候挺绕的,新手容易卡半天。建议写函数前先想清楚上一步吐出来的是什么。其实新版本LCEL有个贴心设计,你直接把普通函数写进管道里,它会自动包成RunnableLambda,retriever | format_docs这种写法能直接跑,不用显式套一层。


RunnableParallel和itemgetter,并行与取值

RunnableParallel让多个组件同时跑,结果按key拼成一个字典返回。RAG里检索和问题改写可以并行,能省时间。

fromlangchain_core.runnablesimportRunnableParallel parallel=RunnableParallel({"original":RunnablePassthrough(),"upper":RunnableLambda(lambdax:x.upper()),})print(parallel.invoke("hi"))# {'original': 'hi', 'upper': 'HI'}

LCEL还有个偷懒写法,直接传字典就等于RunnableParallel,不用显式写类名。上一篇顺序链里就用了这个,{"outline": outline_chain},字典本身就是并行执行。刚开始我看到字典出现在管道左边还愣了一下,搞懂之后就觉得很自然。

itemgetter是Python标准库operator里的函数,经常和RunnableParallel配合,从字典里取某个字段,比写lambda简洁。

fromoperatorimportitemgetter prompt=ChatPromptTemplate.from_template("回答问题{question}")chain=({"question":itemgetter("topic"),"context":RunnablePassthrough()}|prompt|model)

itemgetter(“topic”)会从输入字典里取出topic字段,省得写lambda x: x["topic"]。字段一多,itemgetter写法干净不少。


bind,给组件绑定参数

bind方法给Runnable预先绑定一些参数,调用时自动带上。最常见的用途是给模型绑定工具,或者绑定停止词。

fromlangchain_core.toolsimporttool@tooldefsearch(query:str)->str:"""搜索网络内容"""returnf"搜索结果:{query}"model=ChatOpenAI(model="gpt-4o-mini")# 把工具绑定到模型上model_with_tools=model.bind_tools([search])chain=prompt|model_with_tools|StrOutputParser()

bind之后这个模型对象就带着工具了,每次调用都会把工具信息发给模型。你也可以用bind绑定temperature这类参数,model.bind(temperature=0.7),同一段代码里同一个模型就能有不同配置,不用为每个配置新建实例。还有个常见用法是绑定停止词,model.bind(stop=["\\n\\n"]),让模型生成到指定符号就停。做结构化输出时挺管用,能挡住模型在结果后面啰嗦一大段。


完整RAG示例

把上面的东西凑一块,写个完整的RAG链。检索相关文档,和原始问题一起塞进提示词,让模型回答。

fromlangchain_openaiimportChatOpenAI,OpenAIEmbeddingsfromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.runnablesimportRunnablePassthroughfromlangchain_community.vectorstoresimportChromafromoperatorimportitemgetter embeddings=OpenAIEmbeddings()store=Chroma(embedding_function=embeddings)store.add_texts(["LCEL用管道符组合组件","RunnablePassthrough原样传递输入","RunnableParallel并行执行多个组件",])retriever=store.as_retriever(search_kwargs={"k":2})prompt=ChatPromptTemplate.from_template("根据以下背景回答问题\n背景:{context}\n问题:{question}")defformat_docs(docs):return"\n".join(doc.page_contentfordocindocs)rag_chain=({"context":retriever|format_docs,"question":itemgetter("question")}|prompt|ChatOpenAI(model="gpt-4o-mini")|StrOutputParser())print(rag_chain.invoke({"question":"LCEL怎么组合组件"}))

这段代码值得拆开看。字典里context走检索器再格式化成字符串,question用itemgetter从输入取出来,两路并行执行。然后塞进提示词,过模型,解析输出。一个小小的字典加几个管道符,就把检索、格式化、拼提示词、调模型、解析输出全串好了。

调试这种链有个习惯,把链断成几段分别invoke,确认每一段输出对得上再接起来。RAG最容易出问题的是检索这一步,context拿不到东西,模型就开始瞎编。建议先把retriever单独跑一遍看返回的文档质量,没问题再接进链里。还有一个排查手段,把prompt单独invoke一下,看填充后的提示词长什么样。有时候变量没传进去,模板里留着空占位符,模型自然答非所问,这种问题瞄一眼渲染结果就暴露了。


这篇把LCEL的几个核心组件过了一遍,管道符的底层机制、RunnablePassthrough的透传、RunnableLambda的函数包装、RunnableParallel的并行、itemgetter的取值、bind的参数绑定,最后拼了个RAG链。你会发现LCEL的套路就这些,再复杂的流程也是这些零件搭出来的,多练几次手感就有了。

下一篇聊回调与监控,看看链跑起来之后怎么追踪每一步的执行情况,token花在哪里,耗时多少,出问题怎么定位到具体那一段。

http://www.jsqmd.com/news/1345704/

相关文章:

  • windows网络适配器驱动开发-传入操作帧唤醒(下)
  • Windows 11任务栏拖放功能如何快速恢复:实用高效解决方案指南
  • 2026灞桥区附近成人吉他古筝培训优质机构盘点 - 谁都没有我好看
  • 南昌市商务车改装怎么选才不后悔?星博商务车改装资质齐全质量好,用实木拼花工艺与色彩美学,重塑第二排尊享体验 - 专业优选推荐榜
  • AI文本去塑料感:stop-slop项目如何消除大模型写作的“AI味”
  • AI应用集成安全:从OpenAI事件看第三方中间层风险与防护实践
  • 基于UVM的AHB Lite eFlash控制器验证平台设计与实现
  • 前言写作黄金法则:从痛点切入到价值承诺的完整指南
  • 多场景适配专业论文写作工具,掌桥科研AI VS Claude你选哪个?
  • 天猫店群自动化管理系统:跨平台订单统一汇总,一个系统管所有平台发货
  • NS800RT7P65D同步降压转换器在工业电机驱动中的选型、设计与调试全攻略
  • 2026 年选择神农架景区怎么做不踩坑?把景区体系、季节规划、一体化服务边界讲清楚 - 中国品牌企业推荐网
  • 2026深圳功率因数校正装置公司深圳谐波治理装置公司哪家豪?实用选购指南(更新时间:2026-08-07) - geo88
  • 银川市全钢加重型防静电地板厂家哪家好、硫酸钙防静电地板厂家推荐怎么选不踩坑?2026避坑指南 - geo88
  • 2026世界杯球星全景分析:从巅峰王者到未来新星的战术影响
  • UniApp多端开发实战:从环境搭建到打包上线的全流程指南
  • AI与MR融合:构建智能心理支持系统的架构设计与工程实践
  • 回调与监控,用Callbacks追踪Agent的每一步执行过程
  • 原神成就数据导出终极指南:YaeAchievement如何帮你永久保存游戏记忆
  • Floyd与A*算法解析:最短路径与骑士攻击实战
  • ipasim技术深度解析:Windows平台iOS模拟器的架构实现与跨平台兼容性挑战
  • Unity CJ Lib集成实战:解决五大常见问题与性能优化指南
  • 2026SCI辅导平台怎么选?主流5家机构**避坑! - 小艾学姐
  • 5分钟免费激活Windows系统:KMS_VL_ALL_AIO智能激活工具完全指南
  • 2026深圳商场嵌入式APF|深圳医院用有源滤波器源头厂家怎么选?实用选购指南推荐几家(更新时间:2026-08-07) - geo88
  • 【2026-08】跨年错账修正优秀办理公司怎么选?内部清算审核、财税疑难解决优选——明快业财税 - 多才菠萝
  • windows网络适配器驱动开发-WPA3 SoftAP(一)
  • Day 42:语义搜索来了——Elasticsearch kNN 向量检索与混合搜索
  • 中国技术大败局 | 专利023:当“算法串联”被包装成“智能续航”,我们离技术空心化还有多远?
  • 终极指南:如何用novideo_srgb实现NVIDIA显卡硬件级色彩校准