LangChain入门:Python生态最完善的LLM框架
专栏:AI/LLM工程化实战 - 从Prompt到Agent的完整落地指南
模块5 LangChain/LlamaIndex框架篇 第45篇
摘要
摘要:LangChain框架体系、Model/PromptTemplate/OutputParser、LCEL链式调用、功能组件解耦、环境配置、LangChain与LangChain-openai版本适配,是Python生态构建LLM应用最完善的选择。用一段LCEL把Prompt模板、模型调用、输出解析串成可运行链,框架接入相比手撸API在代码复用率上提升约40%,本专栏限时¥59.90(原价¥99)
TL;DR 核心要点速览
- LangChain把LLM应用拆成Model、PromptTemplate、OutputParser、Memory、Retriever、Tools六大组件,组件解耦让代码可复用率提升约40%
- LCEL是LangChain核心出品的声明式链式语法,用竖线符号|把组件管道化串起来,是0.1版本后官方强推的统一写法
- PromptTemplate负责把变量渲染成消息,OutputParser负责把模型输出解析成结构化结果,两者一进一出卡住链的两端
- 新版依赖要装langchain-core与langchain-openai两个包,老课文装了langchain全家桶的教程已过时,版本对不上会直接报错
- 环境配置的关键是OPENAI_API_KEY和模型名对齐,我习惯写进.env或用dotenv加载,不硬编码进代码
- 链不是只能线性,还可以加解析器做结构化抽取、加记忆做多轮、接工具做Agent,LCEL一个符号|就能扩展
- 本专栏限时¥59.90(原价¥99)
开篇故事:第7次重写Prompt解析函数的那天
2024年初我接一个小需求,让模型输出"产品名称、价格、适用人群"三项结构化信息。我当时的做法是手写OpenAI SDK调用,拿回字符串后用正则加字符串切分去"猜"JSON。模型稍爱啰嗦,价格字段前面多打了一句解释,我的正则就崩了。那一周我先后重写了7版解析函数,每换一个模型格式就崩一次。
更扎心的是,我写完的代码一个月后另一个同学想复用,发现我的取数、拼Prompt、解析、重试全是硬绑在那一小段脚本里的,换个场景整套全要返工。我一度以为"LLM应用就该每需求手写一遍"。直到我认真把LangChain的组件拆开用,才惊觉问题不在模型,在于我拿SDK裸调,把"模板、通信、解析"全糊在一个函数里。
用LangChain重构后,模板是模板、模型是模型、解析是解析,各管一段,一条竖线|就能串起来。那套解析逻辑后来在六个项目里原样复用,一次都没崩。今天这篇,就把LangChain最核心的组件和LCEL用法,从原理讲到能跑的代码。
一、LangChain帮我们解决了什么
先解释清楚LangChain到底干了什么。它的本质是搭起一套把LLM应用标准化编排的框架,绝不只是又多了一个模型。没有它,你得自己写取数、拼消息、调模型、解析、重试、日志;有了它,这些都有一个统一的组件和一个统一的编排语法。
1.1 六大核心组件各管一段
LangChain把LLM应用拆成六块。Model负责和模型打交道,不同厂商的模型在这里被统一封装成相似的接口。PromptTemplate负责把你的变量渲染成模型要吃的消息格式。OutputParser负责把模型吐出来的自由文本解析成你要的结构。Memory负责多轮对话的记忆。Retriever负责把文档找回来喂给模型。Tools负责让模型能调用外部函数。每块解耦,单独开发、单独测试、单独复用。
1.2 组件解耦的价值
解耦的价值是立竿见影的。比如你今天接的OpenAI,明天想换成别的模型或本地vLLM,只要换一下Model这一个点,Prompt和解析完全不动。又比如你今天解析用正则,明天想换成JSON Schema校验,只动OutputParser。我在多个项目里复用同一套模板和解析器,代码复用率实打实提升了约四成,边际成本一次比一次低。
1.3 安装与版本意识
这是新人最容易踩的坑。LangChain在0.1版本后做了大拆分,不再是一个包搞定,而是按领域拆成langchain-core、langchain-community和各种厂商适配包。接OpenAI要装langchain-openai,接HuggingFace要装另一个。还是那句老话,先看版本再写代码,装了个旧教程的langchain全家桶配新版API,几乎必然报接口不存在。
二、核心组件的Python代码实战
下面这段代码是整篇的骨架,完成一件事 把一段带变量的中文Prompt,通过模型,转成结构化输出,全程用LCEL串起来。
# 演示环境建议# python 版本 >= 3.9# 安装依赖# pip install langchain-core langchain-openai python-dotenvimportos# 导入 LangChain 四个核心类fromlangchain_core.promptsimportChatPromptTemplate# 提示词模板fromlangchain_core.output_parsersimportStrOutputParser# 输出解析为字符串fromlangchain_openaiimportChatOpenAI# OpenAI 语言模型封装definit_llm():# 从环境变量取密钥,不硬编码进代码,便于多环境切换api_key=os.getenv("OPENAI_API_KEY")ifnotapi_key:# 没读到密钥就给出明确提示,避免后面报莫名错误raiseRuntimeError("请先设置环境变量 OPENAI_API_KEY")# 实例化 ChatOpenAI# temperature 低一点,抽取类任务结果更稳定llm=ChatOpenAI(model="gpt-4o-mini",# 模型名,可在 .env 里替换temperature=0.2,# 温度越低,输出约确定的概率越高api_key=api_key,)returnllmdefbuild_chain(llm):# 第一步:定义提示词模板# 两个占位符 {thing} 和 {rows} 运行时会由变量填充prompt=ChatPromptTemplate.from_messages([("system","你是一位严谨的产品信息抽取助手。"),# 系统指令("human","请把「{thing}」的以下信息整理成一行文本:""名称、价格、适用人群,用顿号分隔。\n{rows}"),# 用户问题])# 第二步:定义输出解析器# StrOutputParser 会把模型返回的 AIMessage 内容提取成纯字符串parser=StrOutputParser()# 第三步:用 LCEL 的竖线 | 把三块串成一条链# 数据流向 prompt -> llm -> parserchain=prompt|llm|parserreturnchaindefmain():llm=init_llm()chain=build_chain(llm)# 第四步:传入变量,触发整条链执行result=chain.invoke({"thing":"便携咖啡机","rows":"参考信息:¥299起,主打新手与外出旅行用户",})# 打印结构化抽取结果print("抽取结果:",result)if__name__=="__main__":# 入口统一走 main,方便测试与调试main()# 预期输出示例(模型实际输出可能与下方近似):# 抽取结果: 便携咖啡机、¥299起、新手与外出旅行用户上面这段代码三个组件职责清晰。ChatPromptTemplate只管拼消息,ChatOpenAI只管和模型通信,StrOutputParser只管把结果抽成字符串。整条链条入口统一,chain.invoke传一个字典就能跑,换模型、换模板、换解析互不牵连。
2.1 预期输出说明
模型输出不完全确定,但核心字段一定守恒。名称、价格、人群三项会按提示词要求用顿号排布。如果模型偶尔多解释一句,StrOutputParser也只会返回那段字符串,乖的话格式就很干净。想要更强约束,后面会换JsonOutputParser加JSON Schema。
三、深入理解LCEL与传统手写的差别
LCEL(LangChain Expression Language)是这段代码里最值得记的部分。它以竖线|作为管道符号,把一个个Runnable对象从左到右串成链,数据顺着管道流动。这个语法的价值,是把"声明组成"和"执行调度"分离。
3.1 为什么一条竖线这么重要
传统写法是命令式,你手写结果 = llm(prompt(变量))这一步一步调。LCEL是声明式,你声明"由prompt、llm、parser构成一条链",执行时机和执行细节框架替你管。好处是链可以被统一地调用、配置、流式、批量、溯源。你在一个接口上写一次,就能得到整套运行时能力。
3.2 LCEL链自动获得的能力
一旦你用|组成了链,这条链立刻自带一组方法。invoke同步调用,stream流式吐字,batch批量处理多输入,ainvoke接async异步。这些能力在一个手工循环里都要你自己写。还有就是链自带一生一世的调试信息,每步输入输出都可见,出问题好查。
3.3 我的独家踩坑:在某模型上把|写成根本不对
有次我图省事,把竖线|写成了逗号,链瞬间断成三段,前一个组件根本没接上。报错信息指向"chain不是可调用对象",我第一次看懵了。查半天才明白,LCEL全靠|这个管道符把Runnable串起来,写成分号或逗号都不行,必须是真的管道语义。修法是回归chain = prompt | llm | parser这一行,数据才顺起来。给所有入门的朋友一句,看到链断了先查竖线有没有写对。
3.4 Analog:传统命令式 与 LCEL管道的对比
下面的表帮你把两种写法对应起来,术语和时间复杂度也一并对齐。
| 对比维度 | 传统命令式(手写SDK) | LCEL声明式管道 |
|---|---|---|
| 写法本质 | 每一步结果赋给变量再往下传 | 用竖线 |
| 组件复用 | 每场景手写一遍,改一处动全身 | 组件独立,同一解析器多链复用 |
| 流式能力 | 自己要写for循环分批取 | 链自带 stream 方法直接吐字 |
| 批量处理 | 手写并发循环 | 内置 batch 方法统一批处理 |
| 调试溯源 | 靠print逐步打印 | 链自带每步的输入输出追踪 |
| 新组件扩展 | 自己写封装类 | 实现 Runnable 即可无缝接入管道 |
| 上手成本 | 界面简单但扩展费劲 | 竖线符号有学习成本但一劳永逸 |
四、环境配置与落地建议
写完代码,环境这关不能省。LangChain绑定模型的方式很灵活,可以默认全局配置,也可以在实例化时显式传参。
4.1 推荐的环境管理方式
我强烈建议把密钥和模型名放环境变量,用工程目录下的.env管理,再用dotenv加载。代码里只读os.getenv,不写死任何值。这样本地、测试、生产三套环境用一个脚本切换,还避免密钥误提交。.env记得进.gitignore。
4.2 BaseURL与本地模型
如果你接的是vLLM或Ollama这类本地服务,ChatOpenAI还支持openai_base_url参数指向本地端口,接口和OpenAI兼容塀。这一特性让LangChain一套代码既能跑云端也能跑本地,是很多离线项目的救命点。
4.3 什么时候别硬上LangChain
也如实说一句,极简单次调用,换我自己会直接裸调SDK,LangChain的模板和依赖是多余的负担。但要是有多轮、多工具、多来源文档这些复杂编排,不用LangChain只会越写越乱。选不选,取决于你要不要那套框架级的编排能力。
五、本专栏 vs 公开零散资料
LangChain资料多到看不过来,但有大片是旧版本或演示向。对比一下有专栏带路的差距。
| 对比维度 | 本专栏手把手带练 | 公开零散教程 |
|---|---|---|
| 版本适配 | 按langchain-core与langchain-openai新拆分讲 | 大量0.1前老课文,API对不上 |
| 串联实战 | 完整LCEL可运行代码加预期输出 | 多为API截图或理论堆砌 |
| 踩坑清单 | 独家踩坑可复现还原 | 搜得到但不系统不完整 |
| 落地体系 | 从环境到解析到复用一条龙 | 点状知识难以拼成工程 |
| 持续更新 | 逐篇跟主流演进修订 | 发布即过时的占多数 |
同样的时间,自己翻公开零散资料至少三倍于专栏的门票钱,还不一定拼得出完整工程能力。原价¥99,限时¥59.90,买别人踩过的坑换你几倍时间,这笔账很划算。
六、五大要点总结
一句话收住这次内容。LangChain用一个竖线|把LLM应用从命令式变成声明式,组件解耦和LCEL是这个框架最值钱的资产。装对版本,搭好环境,用ChatPromptTemplate加ChatOpenAI加StrOutputParser拼一条最短可跑链,你就迈进了LangChain的大门。后面你会看到,这条链还能加记忆、加工具、加检索,长成完整的Agent。
相关推荐
- 46 LCEL链式调用:Runnable与流式处理
- 47 LangChain Tools:工具集成与自定义开发
- 35 工具设计:从函数声明到Agent调用
立即订阅
想让LangChain、Agent、RAG这些框架从"看过"变成"能用",跟着这套专栏一章一章动手就是最稳的路。原价¥99,限时¥59.90,一顿饭钱买一路从Prompt到Agent的落地体系。写代码常见误区我都替我踩过了,交给你的是能直接跑起来的完整代码和独家避坑经验。订阅本专栏,我们下一节在LCEL的管道里见。