Max
搜索
返回故事会

为什么需要 LangChain:从模型调用到工程落地

71 分钟阅读1Max ZhangLLMOps
LangChainPrompt EngineeringLangSmith

去年夏天,我一个做后端的兄弟遇到了件挺卧槽的事。

他们公司是一家 SaaS 服务商,老板在周会上突然拍板:"客服系统给我接入 AI,下个月上线。"我兄弟当时心想,这还用加班?OpenAI 的 API 他早玩得飞起。周二晚上开了一瓶红牛,周三早上 Demo 就跑通了。用户在聊天框里打字,模型回一大段话,看着像模像样。

周一演示的时候,老板当场拍了桌子——不是生气,是激动:"卧槽这个厉害,赶紧推上线!"

产品经理在旁边疯狂点头。

那晚他心情特别好。发朋友圈说"AI 开发不过如此"。

然后第三周开始,需求像开闸泄洪一样涌了进来。

产品经理:"能不能支持多轮对话?用户每次都重新描述一遍自己是谁,体验太差了。"

他想了想,"行,存历史记录嘛。每次调用把前面的话拼回去。"

又过两天:"能不能加流式输出?像 ChatGPT 那样一个字一个字弹出来。"

"行,加个 streaming 参数。"

又过两天:"能不能上传公司内部的报销制度和入职手册?让 AI 基于这些文档回答员工问题。"

他愣了一下。"行。文档切片,做向量检索引,提问前先检索。以前搞过类似的。"

又过两天:"把输出结构化吧。用户说'我买的东西三天了没到',你直接给我返回一个 JSON,包含分类、紧急度、情绪标签。我后端自动化流转用。"

"行。让模型输出 JSON。"

然后某天下午,财务在群里撂了一句:"张工,上个月 AI 功能花了多少钱?能拉个单子吗?"

他懵了。没算过。

第二天老板私信他:"上周回答还挺靠谱的,这周怎么有几条明显在胡说?你帮我看看怎么回事。"

他翻遍了所有日志。请求成功,状态码 200。几百条交互记录。没有一个报错。但他就是不知道哪一步出了问题。

又过了两天,运维找过来:"OpenAI 太贵了,能切到 DeepSeek 试试吗?预算有点紧。"

他打开了三个月前写的代码,发现已经找不到北了。

原来那几十行核心逻辑,在不知不觉间已经变成了一千两百多行。Prompt 字符串散落在十三个文件里。对话历史是手动拼的,用 history.map(h => h.role + ':' + h.content).join('\n') 这种小学生写法。检索逻辑、模型调用、输出解析、重试兜底、容错补偿全搅在一个叫 handleUserMessage 的函数里。他根本不知道从哪里开始排查——不是看不出问题,是任何一个改动都可能炸掉另一个不知名的角落。

最让他崩溃的是:他不敢改。

一个在团队里公认的"最敢写代码"的人,现在连挪一行 Prompt 都不敢。

这就是我想跟你聊的东西。

Demo 简单得一塌糊涂。打个 API Key,十几行代码,效果看着像魔法。但 Demo 到正式产品之间的距离,比大部分聪明人对自己的判断要远得多。

不是 LLM 不够强。是 LLM 只给了你一台引擎。没给你底盘、没给你方向盘、没给你刹车、也没给你仪表盘。

LangChain,说白了,就是给你一整套底盘的。


先停一下。问自己五个问题。

在往下聊之前,我想让你把这五个问题摆在桌面上。它们是你看完这篇文章后做决策的锚。

第一,你的 Prompt 是不是又多又乱了?

如果你只有一两个 Prompt,直接写字符串里完全没毛病。但如果你已经有十几个,而且每周都在调——那你的问题已经不是"怎么写得更好",而是"怎么管起来"。

第二,你是不是可能会换模型?

如果你非常确定永远只用一家,深度绑定那家的 SDK 不是罪。但如果你脑子里已经闪过"要不要试试 Claude"或"GPT-5 出来之后怎么切"——那你需要一个统一的接口层。

第三,你是不是需要模型碰你的私有数据?

模型不知道你公司的制度文档。不知道你本地硬盘里的 PDF。不知道用户刚刚上传的会议纪要。这些东西要接进来,不是多调一次 API 能解决的。

第四,你的输出是不是要给程序消费,不是只给人看?

如果模型输出只停留在聊天框里,格式抖一抖无所谓。但如果你要把它塞数据库、进自动化流转、写 BI 报表——那自由文本是不够的。

第五,出问题的时候你是在"找"还是在"猜"?

如果排查一条异常回答需要翻半小时日志、看六个文件、最后只能凭借经验猜测"大概是 Prompt 改坏了"——那你需要 tracing 和评测能力。

这五个问题,如果你全答"没有",那就别往下看。直接用官方 SDK,三行代码,生活轻松又快乐。

如果你有两三个"是",继续读。LangChain 大概率会让你少掉很多头发。


一个建房子的比喻,能省你一整天

你如果想搭一个狗窝,一把锤子和几盒钉子足矣。

但你要盖一栋住人的楼,地基、承重墙、水电走线、消防、通风、抗震,没有一个能省。

而且你不会从炼钢开始。你会买标准化的钢筋,买标准化的水泥砖,用标准化的管道接头。

LangChain 做的是同一件事。

它不是帮你"调模型"的。官方的 OpenAI SDK 调模型本来就很简单——三行代码,比 LangChain 还少两行。

它帮你做的是:当你的 AI 项目从"一个临时搭的狗窝"变成"一栋要长年住人的楼"的时候,你不是在满地找木板自己锯——你有一整套标准的预制件可以用。

换句话说。LangChain 不帮你钉钉子。它给你脚手架、给你走线槽、给你标准化的模块墙板。

你再品一下这句话。


那个所有团队都会经历的魔鬼曲线

我见过至少六个团队做 AI 应用。他们的经历像复制粘贴的一样。

第一阶段:兴奋。

拿到 API Key。写最小接口。模型回了一段像模像样的话。所有人激动得不行。PM 开始写上线计划,老板开始在董事会上画饼。

这时候最容易产出一个致命误判:"最难的部分已经做完了。"

第二阶段:膨胀。

产品开始往上摞功能。要支持多轮对话、要流式、要上传文件、要不同角色用不同语气、要输出能进后端。

业务也开始要东西。想看花了多少钱、想排查哪个回答不对劲、想基于内部知识回答、想试试不同模型效果。

团队还在硬扛。每来一个需求就加一段代码。代码长得很快。但还能跑。

第三阶段:失控。

Prompt 散落各处,改了这忘了那。对话历史自己手搓,逻辑满天飞。检索、调用、解析全混在一个大函数里。

代码从几十行变成几百行。从几百行变成上千行。不是架构,是面条。

没人敢大改。改一处,提心吊胆三天。测试不敢跑全量,只能"手动试试几个 case 感觉差不多就行"。

这时候你发现了一个残酷的事实。

最难的从来不是"调一次模型"。调一次模型容易到你最开始会怀疑 AI 行业是不是在搞笑。

最难的是把这种生成能力嵌入一个能活三年的系统。

Demo 爽。上线痛。LangChain 的价值,就是在爽和痛之间的那个拐点上出现的。


LangChain 是什么?用最俗的比喻告诉你

乐高。

就是一套围绕 LLM 应用开发的乐高积木。

你把模型、提示词、对话消息、记忆、检索、输出解析、流程编排这些东西一个个拆开,变成标准化的零件。然后按你自己的需求拼起来。

它不承诺帮你"自动做好所有事情"。世界上没有这种好事。

它做两件事。第一,把高频的工程问题变成统一抽象——你不用每次都从零琢磨"对话历史怎么存"。第二,让不同零件的接口一致——它们之间可以自由拼接。

所以当人说 LangChain 像乐高,不是说这玩意简单。是说它把复杂的建筑拆成了你能看懂、能换、能拼的块。

这些块包括:Prompt、Model、Message、Structured Output、Memory、Runnable、Chain、Agent。

理解每块解决什么问题,比背 API 重要十倍。


停下来,先把事情拆成四层

很多人一上来就被 LangChain 的一百个概念词砸晕了。这个 Template 那个 Retriever,这个 Chain 那个 Agent。

你别从 API 入手。你先把一个 AI 应用要干的活拆成四层来看。

第一层:模型调用层。

这一层只关心一件事——哪个模型、什么参数、拿回来什么。这是你最熟悉的一层,也是你 Demo 里唯一的一层。

第二层:交互组织层。

Prompt 怎么写?系统角色和用户输入怎么拆开?多轮消息怎么接?输出怎么解?——这些问题你在 Demo 里没觉得是问题,但项目一长大,它们会吃掉你一半的开发时间。

第三层:能力增强层。

模型本身不知道你的世界。你的文档、你的用户偏好、你的任务状态,它一概不知。你需要检索把外部的知识接进来,需要记忆把用户的状态留下来,需要工具让模型能操作现实系统。

第四层:工程治理层。

Token 花了多少?哪次回答变差了?Prompt 改了之后效果是升了还是降了?你到底有没有引入更多幻觉?这些不是看日志能看出来的。

LangChain 主要覆盖第二层和第三层。

第四层交给它兄弟 LangSmith。

你把坐标系画清楚了,以后看任何 LLM 工具都能秒速定位——它在哪一层?它补的是哪一块?


Prompt:你写的是几行文案,还是一套业务规则?

我见过一个项目的"核心 Prompt"长这样:

const prompt = `你是一个专业的客服助手。请根据以下要求回答用户。第一用中文,第二不要太长,第三不会就说不会不要瞎猜,第四先给结论再解释依据,第五输出要包含置信度,第六语气友好但不能太随意,第七...(省略两百字)...用户问题是:${input}`

说实话,这不是 Prompt。这是发给模型的一条微信消息。

你在这坨东西里压了多少东西?角色是谁、什么该说什么不该说、怎么判断该不该说、输出是什么格式、遇到不知道的事情该怎么办、语气应该是什么样。

然后产品经理说:"输出格式能换成表格吗?用户觉得大段文字不好读。"

你就得在这两百多字的字符串里,像考古学家一样,找准那句跟"输出格式"有关的自然语言描述,然后改它。改完之后还得祈祷没改坏——因为那句话前面牵着一个"第七",后面绑着个"再加上"。

LangChain 对 Prompt 做的事,就是把它从"一条微信"升级成"一份有结构的文档"。

System 模板管规则:你是谁,你能干什么你不能干什么,你该用什么语气。Human 模板管输入:用户说了啥,附带了啥材料,要求什么输出格式。Chat 模板管多轮:怎么把 system、human、之前 AI 回的、还有工具返回的一层一层拼好。

变量用标准占位符。不再搞那种 '根据以下资料 ' + docs + ' 回答问题 ' + question 的活。

你说:"这不就是模板引擎吗?我自己也能写。"

你当然能写。模板引擎这种东西,一个高级前端一下午就能写出来。

但你能写自动识别并拼接不同消息角色的模板吗?能写根据 token 阈值自动裁剪和压缩历史消息的模板吗?能写把 few-shot 示例正确插入对话流对应位置的模板吗?能写改了一行 Prompt 之后所有调用点自动同步的管理机制吗?

等你在自己的项目里把这些全写完的那天,你猛地发现——你他妈的已经写了一个丐版 LangChain。而且性能还没人家的好,边界条件还比人家的多。

Prompt 的本质,是轻量级业务规则。

客服助手的 Prompt 是一套规则。法律咨询的 Prompt 是另一套规则。报销查询的 Prompt 又是另一套规则。它们之间的差异不在"文字好不好听",而在"规则怎么定义"。

你把业务规则当零散字符串管理,就跟把税务报表写在便利贴上、贴满整面墙一样——前期感觉自己灵活又潇洒,后期随便来一个人碰一张便利贴你就要疯。

三个习惯你最好从一开始就培养。

第一,角色、任务、输出分成三层写。别大杂烩。角色是谁,要干什么,输出长什么样——分清楚。

第二,Prompt 按业务用途命名。永远不要再看到 prompt2promptNewpromptFinalpromptFinalFinalReally

第三,Prompt 版本要能比对。别把写 Prompt 当写诗。用同一套测试用例,跑两个版本比较分数——这本质上就是 Prompt 自动化测试。


Prompt 里最容易被忽略的战斗级决策

很多人调 Prompt 调一整天,调来调去纠结"应该说'请回答'还是'请帮我回答'"。说老实话,这差别没你想的大。

真正影响结果的是什么?

是你有没有把"不确定性"关进笼子里。

比如一句话:"如果信息不足,明确说无法确认,不要猜测。"

这句话看着不起眼。但在客服、医疗、法律、金融这些场景,它就是防火门。它能挡住大量胡说八道。不是让模型不犯错,是让它在知道自己可能犯错的时候主动停下来。

是你在 System Prompt 里定义的"行为边界"到底清不清晰。你是说"你是一位专业助手"——这基本等于没说。还是说"你是一位医药咨询助手,不做诊断,只提供基于公开文献的信息参考"——这两句的差距,上线之后就是事故率的差距。

是你给输出的约束够不够具体。"输出要简洁"对模型来说没有意义。什么叫简洁?"回答在 150 字以内"——这叫具体。

Prompt 是你和模型之间的一份轻量 API 合约。合约写得模糊,执行就不可控。


Model:把"换模型"这件事的伤害半径缩到最小

我第一次从 OpenAI 切 Claude 的时候,改了整整两天。

不是两个 API 有多难调。是我的代码里,模型调用跟业务逻辑长在了一起。

几十个文件里散落着 openai.chat.completions.create 的调用。参数名字全不一样——OpenAI 叫 messages,Claude 叫 messages 倒是也一样,但这个 system 传参方式不同,那个 stream 字段类型不同,那个 temperature 的范围定义不同。错误格式嘛——一个给你 JSON,一个给你字符串。token 统计字段也不一样。

然后我想起来产品经理说过"以后可能还会试试 DeepSeek"。

卧槽。

LangChain 的 Model 组件,就是在你的业务逻辑和各家模型之间放一个转换插头。

你把所有模型抽象成两个基本形态:LLM——传统"输入文字,输出文字"。Chat Model——"输入消息列表,输出消息列表"——这是现在所有主流模型的标准接口。

然后在这个基础上,再把批处理、流式输出、重试策略、超时这些全做成统一的。

它不是魔法。它的目标从来不是"让你觉得所有模型一样"。

因为不一样就是不一样。system prompt 生效方式不一样。tool calling 能力不一样。上下文窗口不一样。价格可能差一整个数量级。

Model 组件的真正价值是:把差异关在笼子里。不让它蹿出来咬你的业务逻辑。

你换模型的时候,改的是笼子里的事。不是你全屋装修。

说白了吧。你写一个 if (model === 'openai') { ... } else if (model === 'claude') { ... } 一次,能跑。你写十次,分布在十个不同文件里,以后换 DeepSeek 的时候漏改一个,线上炸一个,你还找不着在哪炸的。

统一抽象的意义不是让你多省事。是让你别因为忘了一个角落而半夜爬起来改 Bug。


批处理和流式输出,省的不是几行代码那么简单

你可能会说:"批处理不就是 Promise.all 嘛,流式不就是一个参数嘛。"

等你被两万条用户反馈淹过一次,你就不会这么想了。

批处理指的是:同时处理两万条数据做批量分类。你得管并发——同时调五百个请求会被限流,你得找到那个甜蜜点。你得管失败重试——哪条失败重试哪条,不能全盘重跑。你得管结果聚合——两万条结果怎么合并、怎么去重、怎么写入数据仓库。

等你把这些全写完之后,你发现这部分代码比你核心业务还长。

流式输出也是。

用户聊天的时候,你让他等五秒看一个空白屏幕,他在心理上已经判了你的系统死刑。流式把 token 一个字一个字推出来,体验完全不一样。但不同模型的流式协议不一样——有的走 SSE,有的走 WebSocket,有的走 chunked transfer。如果你自己对接这三家,适配层就够你写一周的。

Model 组件把这些内置到统一接口里。省的不是"少几行代码"。省的是"你以后再切模型时不必再写一遍这摊东西"。

想想看:你现在同一套批处理逻辑,在 OpenAI 写法是这样,在 Claude 写法是那样,在 DeepSeek 又是另一个样。每换一次模型,这套胶水重写一遍。你一年换三次,就他妈的重写了三次。Model 组件把这套逻辑写一次,钉死在抽象层里。各模型各自适配——但你的业务代码不动。

这个区分值得用大字写出来:业务逻辑不要跟模型 SDK 做朋友。它们应该通过一个中间人交流。


Message:你手搓的所有字符串拼接都是积攒的债

问你一个最简单的问题。你的用户跟 AI 聊了三轮了。你要把前三轮历史传给模型。你怎么写的?

我赌你写了这个:

const history = messages.map((m) => `${m.role}: ${m.content}`).join('\n')
const prompt = systemPrompt + '\n\n' + history + '\n\nUser: ' + input

看起来很干净。我也这么写过。

然后需求来了。历史对话超过八千 token 时,自动切掉最早的部分。

行。开始写 while 循环。每次加一行就算一次 token。加到八千就停。

又来了。切的时候保留最近三轮完整对话。三轮之前的自动压缩为摘要。

行。在模板前面拼一段 summary。然后拼最近三轮的原始对话。然后把它们一起塞给模型。

又来了。上一轮的工具调用结果也要拼进去。但工具结果是一个 JSON。你不能直接把它当普通文字拼——JSON 里的 {} 会影响模型对整体结构的理解。

卧槽。

你那个花了五分钟写的"干净的字符串拼接",现在变成了一个快四百行的怪兽。而且最关键的信息全丢了——这段内容是 system 规则?是用户输入?是历史回复?是工具调用结果?还是被摘要压缩过的二手信息?

在你拼接完的那一刻,它们全变成了同一件东西:字符串。

这时候你再想做"把第三轮对话中用户提到的偏好单独提取出来存 Memory",已经晚了。信息早给拼死了。

LangChain 的 Message 组件做的事,就是把"谁说的、什么时候说的、在什么语境下说的"这些东西留在对象层级,而不是靠字符串约定。

System Message、Human Message、AI Message、Tool Message 是不同的类型。有不同语义。有不同处理策略。

你说:"这他妈的不就是多包了一层数据结构吗。"

是的。但这一层包装让你在三个月后还能分清"这句是用户说的"和"这段是摘要生成的"。

Message 和 Prompt 是一个最常见的混淆。

Prompt 是信的正文。Message 是信封和寄信人。Prompt 管说什么。Message 管谁说、用什么身份说、什么时候说。

一个装酱油倒碗里,一个装酱油倒量杯里。倒出来的东西一样。但你接下来要对它做的事不同。


Structured Output:别让模型写作文,让它填表格

有一类需求你会接二连三地撞上:让模型输出可以被代码消费的结构化数据。

把用户消息分类。提取关键实体。输出固定格式的 JSON 给后端。

每个人刚开始都会写同一句话:"请输出 JSON。"

然后在本地打了二十个 case,感觉还行。推上线。回家睡觉。

凌晨三点,告警响了。

一看日志:模型回了一段解释文字"好的,根据您的需求我做了以下分类:",然后是 JSON。你的 JSON.parse 直接报语法错误。

你赶紧补了个正则:找 { 开头,找 } 结尾。

第二晚又响了:模型把 JSON 包在了 markdown code fence 里。json { "category": "refund" } 。你的正则又歇菜了。

第三晚没响,但你早上发现后台报表里出现了一条 priority: "urgent" 的数据。你系统里只定义了 lowmediumhigh 三个枚举值。你查这字段到底怎么来的,翻到原始日志——模型自己说:"这个工单看起来特别紧急,所以我把 priority 设成了 urgent。"

你他妈的能怎么着?你跟模型说"只能输出指定的枚举值"——但这句话也是以 Prompt 的形式发给它的。而 Prompt 本身就是自然语言。你想用自然语言去刚性约束另一段自然语言,这从根上就有问题。

LangChain 的 Structured Output 做的事,就是把约束从"自然语言层"拽到"结构层"。

你用 TypeScript 定义一个 interface。LangChain 把这个 interface 转成模型能读懂的结构化约束。模型不再自由发挥。它在填表。

填表意味着字段是可预见的。可预见意味着你的后端代码不需要写八百行 try-catch。可预见意味着输出可以直接进数据库、可以进自动化工作流、可以入 BI 看板。

而且它不再是玄学的"这个回答感觉还行"。它有字段。你觉得 category 错了,你就只测 category。你可以按字段做准确率、召回率。你的评估不再是"这坨东西怎么样",而是"这个字段对不对"。

这比大多数人想象的更重要。

因为它还带来了另一个关键收。以前你的评测是:"你看这段回答还行吧?""嗯,感觉差不多。"现在你有字段级别的准确率——category 字段的 macro F1 是 0.91,sentiment 字段是 0.78。数据冷冰冰,但你终于知道该优化哪一块了。

这和靠直觉判断质量的世界,是两码事。


Memory:模型从来没有记忆,是你在表演"它记得"

我见过不止一个人问:"为什么 AI 记不住我刚才跟它说的话?"

因为它本来就他妈的不记。

每一次模型调用都是独立的、无状态的。你觉得它"记得",是因为你的代码在上一次调用之后,悄悄地把对话内容存了起来,下一次调用时又悄悄喂回去了。

记住了:记忆不是模型的财产。是你给它构建的外部设施。

既然要你亲手造,那问题就摆在这了:怎么存?存多少?什么时候扔?

LangChain 把这拆成了好几个可选的策略。

缓冲记忆。 最直白——把所有历史全留着,每次一股脑塞给模型。好处是不丢信息,坏处是上下文越来越长,token 越来越贵,延迟越来越大。出门把所有行李背上,走两步潇洒,走两公里废了。

K 值缓冲记忆。 只保留最近 K 轮,太老的自动截掉。大部分客服和售前咨询都适合这个——用户来问,几分钟就走了,不必记得三周前的事。

令牌缓冲记忆。 按 token 数而不是轮数裁剪。这更贴近生产环境的真实约束。有的轮一句话十个 token,有的轮长文一千个 token。按 token 控制才合理。

摘要记忆。 对话太长的时候让模型帮你把早期部分总结成一段摘要。省空间。但摘要是模型吐出来的——它会丢失细节,也可能引入偏差。就像让最小靠谱的同事帮你写会议纪要。关键信息可能在,但老板当时的微表情没了。

向量存储记忆。 把历史内容做 embedding,存向量库,需要的时候按语义相似度去捡回来。这是最接近人类"长期记忆"的方案——不是把一切都记住,是把关键的存下来,该用时再调。

你可能会本能地想:"那就全上向量存储呗,听起来最先进。"

不对。

Memory 不是一个"挑一个方案了事"的问题。它是"不同类信息该用不同策略去管"的问题。

当前聊了五分钟的话题——缓冲。

三个月前提过"喜欢简短回答"的偏好——向量存储。

正在审批中、卡在第三关的流程状态——某种状态管理。

三种迥然不同的东西。你拿一种策略去管它们全部,就像用同一个温度区去存鲜肉和冻肉——最后冻肉化了,鲜肉臭了。

再记一条:Memory 和 RAG 不是一回事。两个可能都碰向量数据库,但目标根本不同。

"用户喜欢简短的回答"是 Memory。

"公司报销制度第八条"是 RAG 要检索的外部知识。

Memory 关的是人。RAG 关的是文档。把它们混在一起设计,系统迟早失焦。

很多人到这一步会犯一个典型错误:把记忆系统当储物间,什么东西都往里塞。用户权限、用户偏好、会话上下文、任务状态、外部检索来的文档片段——全堆进一个 memoryStore。这种设计前三个月看起来干净("所有东西都存这嘛"),但到了需要根据不同类型做不同策略的时候——比如权限必须实时校验不能缓存、偏好可以按月清理、对话上下文只在会话内有效——你就会发现你把一个应该分层的东西做成了一个球。


Runnable 和 LCEL:从面条到管道

好,现在你有了 Prompt、Model、Message、Parser、Memory、Retriever。手上是一堆零件。

怎么拼?

你大概率干过这个:写一个 processUserMessage 函数。第一步调 retriever,第二步拼 prompt,第三步调 model,第四步解析结果。try-catch 包上去。重试逻辑写里面。日志穿插着打。一共三百行。

这叫什么?叫面条代码。能跑。但以后你想要把"检索器"换成另一个供应商,你要在三百行的面条里把跟旧检索有关的所有逻辑一条条挑出来。漏一条,线上炸一个。

LangChain 的 Runnable 和 LCEL 就是专门反转这个局面的。

你把每一步定义成一个 Runnable。输入输出是标准的。然后你像接水管一样把它们接起来。

用户问题 -> 检索文档 -> 拼 Prompt -> 调模型 -> 解析输出 -> 返回

这跟写一个大函数有什么本质区别?

局部替换。

如果要把 Pinecone 换成 Qdrant——换 retriever 节点。其他不动。

要在模型调用前加一层语义缓存——加一个 .pipe(cacheLayer)。其他不动。

要给输出解析加 fallback 逻辑——改 parser 节点。其他不动。

面条代码里,每一次这样的变化,你都得像拆弹一样在整个函数里割胶水。

Runnable 管道的设计,本质上在帮你做一件事——把未来变化的影响圈死在一个最小的范围里

你前期多花十五分钟把管道设计清楚。后期改需求的时候少加班到天黑。

这个交易,值不值你自己掂量。


LCEL 最实际的用法:先想好每步的输入输出是什么

很多人一上来就被 LCEL 那个竖线运算符 | 劝退了。看起来像写 SQL 一样,什么鬼。

其实里面思想特别朴素——你只要在每一关想清楚一样东西:这关吃进来什么,吐出去什么。

吃进来一串 Message,吐出去一串 Message。

吃进来一串 Message,吐出去一个 string。

吃进来一个 string,吐出去一个 typed object。

你把这些"接口"想清楚了,管道自然就接起来了。

最经典的 RAG 链路就是:用户输入一个 string → 检索出一组 document → 拼成一段 context → 把 context 和问题变成一组 message → 模型吐出一个 string → parser 转成一个 typed object。

你把这个链路写在纸上,每一步标清楚输入类型和输出类型。然后你用 LCEL 把它一行行实现出来。不出两个月,你会回来感谢这一刻的自己。


LangSmith:从猜测到看见

LLM 系统最让人崩溃的故障,不是代码报错。是没报错。

请求返回 200。一切正常。可某几条回答就是明显变差了。

你怀疑 Prompt 是不是上周改了。你怀疑检索是不是把垃圾文档带进来了。你怀疑模型那边是不是做了什么静默升级。但你翻遍了机器日志,什么都没看出来。

这在传统系统里是不可思议的。传统系统你只要看请求量、响应时间、错误率、状态码——大部分问题能定位。

但 LLM 的输出质量,这些指标通通反映不出来。Token 数正常,延迟正常,没报错。

可用户说"你们的 AI 最近变笨了"。

在没有 tracing 的时候,你唯一能做的事情就是打开五六个文件,从 Prompt 看到 Retriever 配置,从配置看到模型参数,猜一圈,然后改一下"感觉上可能是问题"的东西,重新部署,再观察一天。这就是传说中的"AI 祈祷式调试"。

LangSmith 做的事,就是把这种人肉考古的过程消灭掉。

它告诉你:这条回答走的是哪个 Prompt 版本。这次检索命中了哪几篇文档。链路上每一步的输入输出是什么。哪一步吃掉最多 token。哪一个环节出了偏差。

它还能跑评测。Prompt 改之前和改之后,你用同一套标注数据集跑两条链路。结果是数据说的——不是"我觉得这版更好",是"这版 exact match 从 76% 升到了 83%,但有个 regression case 之前对现在错了"。

评测是你和你的 AI 系统之间唯一的客观对话通道。

很多团队说"先做功能,评测以后再说"。等他们真正想"再说"的那天,需求已经滚成了雪球,Prompt 改了八十个版本,没人知道哪个版本更好,改起来全是盲调。

评测就是那个"以后"。你现在不建,以后在火场上建,更痛苦。


Chain 和 Agent:不是一回事,别混

很多人在刚开始看文档的时候,会同时看到 Chain 和 Agent 两个词,自然觉得"这大概是一个东西的两种叫法"。

一字之差,天壤之别。

Chain 是流水线。你事先把每一步都定好了。第一步洗菜,第二步切菜,第三步下锅,第四步装盘。不会有意外。

Agent 是厨子。你告诉他"做一道川菜",他自己决定先放油还是先爆蒜,先炒肉还是先炒菜。你不知道他会多放两勺辣椒还是少闷了半分钟。

绝大多数你要做的 AI 功能,其实都是 Chain——

"用户问报销流程" → 检索报销相关文档 → 把检索结果拼进 Prompt → 让模型生成答案 → 解析答案返回

"用户问项目规范" → 检索该项目的规范文档 → 拼 Prompt → 生成 → 返回

流程稳定。每一步可预期。好测。好上线。

Agent 在你真的需要一个动态决策链的时候才有意义。

"用户说我报销一直没批" → Agent 判断要不要先查数据库 → 查到工单状态异常 → 再判断要不要查 OA 审批流 → 发现卡在财务 → 决定追问用户更多信息 → ...

Agent 很强。但它的不确定性也很大。你不知道它会多调几次工具、多走几个分支、多烧多少钱。你甚至不知道它会掉进哪个死循环或者哪条歧路。

我的经验主义法则:能 Chain 就 Chain。Chain 不够用了再上 Agent。别为了"技术看起来更酷"用复杂的方案。


检索:模型不认识你的世界,你得给它开门

不管你用的是哪家模型,它都不认识你。

它不认识你公司内部的报销制度。不认识你本地硬盘里的项目文档。不认识用户三分钟前刚上传的那份会议纪要。

模型的世界是训练数据构成的世界。你的世界是你的世界。两个世界的交集比零大不了几个字。

所以你要检索。

很多人把检索理解成三步:读文件、切成块、丢向量库。三步都没错,但这三步加在一起不等于一个能扛生产的检索系统。

你现实里至少还要面对这些问题:文件格式参差不齐——PDF 和 Markdown 解析方式根本不一样。有些 PDF 是扫描版,你得先 OCR。切分策略怎么定?按段落还是按标题?要不要保留元数据——文档来源、更新时间、作者?如果原文更新了,索引怎么同步——推倒重做还是增量更新?检索出的结果怎么排序?怎么去重?怎么衡量每个结果跟问题的相关性?

LangChain 的价值不在"发明了检索"——向量检索是几十年的老技术了。

它的价值在把这些拆解成可接可换的零件:文档加载器、文本切分器、embedding 接口、retriever 抽象。你不是在用"LangChain 的检索",你是在用它给你的标准化零件,组装你自己的检索引擎。

还有一点。总有人问:"现在模型上下文越来越长,直接把整本手册喂进去不行吗?"

长上下文不等于便宜。一整本书塞进去,token 成本上天。

长上下文不等于精准。信息越多,模型越可能抓不住重点。

长上下文不等于好维护。你永远不知道模型到底读了第几页、第几行、漏了哪段。

检索的意义不是把东西塞给模型就完了。

检索的意义是把最对的、最相关的、最干净的片段塞给模型。让模型在有限的注意力窗口里做精确的推理。少喂信息,但喂对信息。成本更低,质量往往也更高。


五个误解,一个都漏不掉

误解一:"LangChain 就是帮我调模型的。"

不是。调模型官方 SDK 最简洁。你写三行就能拿到结果。

LangChain 做的是组织 Prompt、管理对话、接入检索、解析输出、编排流程、追踪链路。

你不是在"调模型"。你是在"搭系统"。

误解二:"用了 LangChain 就不用理解底层了。"

放屁。

你不知道 token 和钱是什么关系、不知道 Prompt 为什么影响幻觉、不知道模型的能力边界在哪里,你就算用了世界上最好的框架也调不出靠谱的结果。

框架帮你把胶水代码省了。它不帮你理解问题。你自己得想清楚模型在你的产品里扮演什么角色、你该让它做什么不该做什么。

误解三:"项目第一天就该全套上 LangChain。"

没必要。

项目复杂度是在迭代中长出来的。你的代码抽象也应该跟着一起长,而不是一开局就堆满。

第一周,SDK 验证核心需求能不能跑通。第一个月,Prompt 开始乱了加 Template。第二个月,需要私有数据了加检索和 Memory。第三个月,流程多了上 Runnable。第五个月,准备上线了补 LangSmith。

让框架跟着需求走。别让需求停下脚步等你的框架就位。

误解四:"LangChain 能自动解决幻觉。"

不能。醒醒。

幻觉不是 bug,是 feature——是模型在语言生成任务中为了产生连贯文本而必然带有的副产品。你把检索做到完美、约束做到极致,它还是会偶尔自信地编出不存在的东西。

LangChain 能帮你的,是让检索更准——信息来源靠谱,胡说就少一点。是让输出更受约束——边界清晰,自由发挥空间就小一点。是让链路更可见——你一眼看到是检索阶段没捞出相关文档导致模型闭眼编的。

剩下的——Prompt 设计、检索质量、人工审核、评测机制——这些都得你自己一手抓。框架不替你兜底。

LangChain 能帮你让检索更准——信息来源靠谱,胡说就少一点。能让输出更受约束——边界清晰,自由发挥空间就小一点。能让链路更可见——你看得到哪里出了问题,就不会对着整条管道猜。

剩下的——Prompt 设计、检索质量、人工审核、评测机制——这些都得你自己一把把抓。

误解五:"LangChain 太重了。"

这句话要先看谁说。

如果你只做一个单轮问答对接口——用户敲字,模型回字——LangChain 对你真的重。不骗你。三行代码不需要引入一个框架。

但如果你面对的是:二十个 Prompt 模板要管、三家模型可能要切来切去、需要做 RAG 检索、要管多轮记忆、要流式又要结构化、还要能追踪 token 消耗——你问问自己,你现在手搓的那套"轻量方案",真的轻吗?

你觉得 LangChain 重,很有可能是因为你还没在你的"轻量方案"维护到第三个月、凌晨三点被告警电话叫醒过。

等到那天,你就会明白。你所谓的"轻量方案",只是把所有 LangChain 已经封装好的问题,用更乱的方式又做了一遍。


一个企业知识助手的真实演化

假设你现在要做一个内部知识助手。员工问"报销怎么走""入职流程是啥""项目规范在哪"。

第一版。

一个聊天框,一个 API,一段 Prompt。两天做好。演示时所有人疯狂点头。

问题马上来了。没引用依据,模型可能在胡说八道。

第二版。

接文档。上传 PDF、Markdown、FAQ。建索引。每次提问前先去检索,然后把检索到的段落和问题一起给模型。回答靠谱多了。

新问题:文档怎么切分才能让检索最精准?检索来的内容太长,怎么选最相关的片段?怎么在 UI 上展示引用来源,让用户知道答案来自哪个文档第几条?

第三版。

输出结构化了。除了回答文字,还要返回字段:引用了哪些来源文档、每条来源的相关度打分、整体置信度。

新问题:字段格式怎么约束?模型有时候自己发挥,返回一个叫 trust_score 的字段而不是预定义的 confidence。解析炸了怎么兜底?

第四版。

不同部门的人看到不同内容。财务搜报销,研发搜规范。每个人的检索池不同。每个人的偏好可能也不同。

新问题:哪些是权限逻辑——该由权限系统控制?哪些是 Memory——用户偏好、历史记录?哪些是会话上下文——这一轮才需要的信息?三条线分不清,代码就乱成麻。

第五版。

上线了。需要看 token 消耗了多少。需要看哪些答案被用户点了"不满意"。需要跑两个 Prompt 版本的对比实验。需要知道某条差评回答到底哪里出了问题。

到了这一步,你手里已经不是"一个聊天页面"了。这是一个完整的 AI 应用——有检索、有记忆、有结构化输出、有版本管理、有评测监控。

LangChain 和 LangSmith,就他妈的是为这种形态长出来的工具。


如果你决定不用 LangChain

这不是死路。很多团队确实走了另一条路。

他们有足够强的平台能力。需求跟通用场景偏差很大。不想引入第三方依赖。或者对某些组件有极致的性能需求——这些理由拿出来一个都成立。

但你得清楚你自己接下来要写什么。

Prompt 模板引擎。Message 对象和拼接逻辑。多模型适配层。输出解析与校验框架。文档加载器。文本切分策略。Retriever 的抽象层。流程编排引擎。Tracing 和日志系统。

这里面每一样单独说都不难。但当你要同时维护八样,而且你不靠它们吃饭、你靠你的 AI 产品吃饭的时候,事情就变了。

你可以花两天写一个 Prompt 模板引擎。再花两天写消息拼接。再花三天写个模型适配层。然后发现这个自研框架已经六百行还没覆盖完需求。再过两个月,已经有同事开始绕过你的框架直接调模型 SDK 了——因为他觉得你的框架"用着别扭"。

这不是怪谁。是自研框架的成本从来就不只是"写出来的时间",还有"教别人用的时间"、"排查兼容问题的时间"、"让它跟业界发展同步的时间"。

你的核心竞争力是"造轮子",还是"用轮子开车"?

大部分团队的答案是后者。

LangChain 的意义不是在那说"只有我能行"。它的意义是给你一个相对成熟的默认起点。你站在这个起点上,走得更快。如果你发现某个局部不够好——换它。组件化架构的核心好处就是支持局部替换。


为什么很多人觉得它"重"

这个问题也值得正面回答。

四种原因最常见。

第一,你的项目确实还不复杂。如果你现在就是单轮问答,任何框架对你都是重的。这不是框架的问题,是阶段没到。

第二,你第一次见它的时候被一堆概念同时砸脸。Prompt Template、Retriever、Runnable、Chain、Agent、Parser——一次性全涌进来,确实容易形成认知淤堵。

第三,你还没有在脑子里建起那套 LLM 工程分层。你不知道每个组件到底在解决什么问题,它们看起来就只是一堆名词。

第四,你做的项目确实不需要它。这关得承认。不是所有 AI 项目都值得引入一个应用框架。

所以问题不该是"LangChain 重不重"。

问题应该是:你的项目复杂度,是不是已经到了"不用框架的维护成本大于学框架的学习成本"那个拐点?

走过了,它就是工具。没走到,它就是负担。


全景关系图:一张图看它们怎么咬合

前面我把每个模块单独拆开讲了。但真实项目里它们不是七块积木各躺各的——它们是一组齿轮,必须精准咬合。你理解了单个模块"干什么"还不够,你得看懂它们之间"谁喂谁、谁管谁、谁看着谁"。

下面这张图,把 Prompt、Model、Message、Memory、Retriever、Runnable、LangSmith 的依赖和协作关系一次摊开:

Loading diagram...

解释这张图,三件事就够了:

第一,数据从左往右流。 用户输入 → Runnable 调度 → Message 封装 + Retriever 检索 + Memory 注入 → Prompt 拼装 → Model 生成 → Parser 解析 → 业务系统。这是一条单向流水线。每步只能改自己管辖的东西,不能伸手到别人的地盘。这就是 LCEL 管道思想的物理版。

第二,竖线是分层。 从下往上看。交互组织层——Message 到 Prompt,Prompt 到 Model,Model 到 Parser——是核心链路,断一环你就得手搓一整段替代代码。能力增强层——Memory 和 Retriever——是两根"注入管"。Memory 往 Message 里灌历史和偏好,Retriever 往 Prompt 里灌知识。它俩方向不同、治理策略不同,混成一个东西管,系统迟早失焦。工程治理层——LangSmith——不在主链路上,但虚线连到了每一个节点。它不参与业务,但出问题时它是你不瞎猜的唯一凭据。

第三,Runnable 是包在最外面的壳。 它不产出内容。它只干一件事:把"谁接在谁后面"定清楚。你把 Message、Prompt、Model、Retriever 这些零件全丢给它,告诉它"先跑这个,再跑那个,然后把这个的输出喂给下一个"。对它来说,每个零件都是黑盒——只要输入输出类型对得上,里面是 OpenAI 还是 Claude、是 Pinecone 还是 Qdrant,它一概不关心。这就是局部替换能实现的前提:换一个节点,其他节点纹丝不动。

看懂这张图,你就不会再把 LangChain 当成"一百个 API 的集合"。它是一个有骨架、有流向、有分层的东西。你写代码时心里装着这张图,不会再写出那种"检索结果、Prompt 拼接、模型调用全糊在一个大函数里"的面条。


FAQ:读者困惑预判

写了快八千字,有些问题你一定已经在脑子里转了好几圈了。我先把它们摁住。

Q: "LangChain 太重了,我直接用 API 不行吗?"

这个问题值得拆成两层看。

第一层:你现在到底在干嘛。如果就是调一次模型、回一段字、前端渲染——那别说 LangChain,你连自己封装个函数都是多余的。三行 openai.chat.completions.create,干净利索,谁劝你上框架谁是傻逼。

第二层:你三个月后在干嘛。一旦你开始管十几个 Prompt 模板、要支持三家模型随意切、要接 RAG、要管多轮记忆、要结构化输出还要流式、出了事还得知道哪一步崩的——你手搓的那套"轻量方案"已经不是方案了,是屎山。而且你他妈的会发现,你搓出来的每一块,LangChain 都已经搓过一遍了,还搓得比你好。

所以答案不是"用不用 LangChain"。答案是:你的复杂度到了没。 到了,它就是工具。没到,它就是负担。别纠结"重不重"这个伪命题——去纠结你的需求复杂度是不是已经跨过了那个拐点。

Q: "LangChain 和 Dify/Coze 这些低代码平台怎么选?"

一句话:LangChain 是给程序员用的框架,Dify 和 Coze 是给非程序员用的平台。

Dify 和 Coze 做的事是让你在网页上拖组件、配 Prompt、接知识库,点几下鼠标就能搭出一个 AI 应用。核心卖点是"不用写代码"。你如果是产品经理、运营、或想在一小时内验证一个想法——逆天好用。

但问题也在这。拖拽搭出来的东西,灵活性有天花板。你想自定义检索策略?想写一套复杂的输出解析与兜底逻辑?想把模型调用嵌入你自己的后端工作流里做深度定制——平台就开始卡你脖子了。它的确定性换的是你的自由度。

LangChain 走的是另一个极端。代码级框架,给你最大自由度,但要你自己一行行撸。你不是在"配一个应用",是在"搭一个系统"。如果你是一个工程团队,要把 AI 嵌入已有产品体系、要做持续迭代、要做自动化评测——LangChain 的自由度就是你活下去的前提。

选哪个不是"谁更好"的问题,是"你更在乎快速验证,还是更在乎长期可控"的问题。别拿捏不准就去发帖问——先把自己摆在坐标系里量一下。

Q: "LangGraph 和 LangChain 的关系是什么?"

问得好。很多人刚开始看 LangChain 生态时被这两个名字绕晕,什么鬼。

关系就一句话:LangGraph 是 LangChain 的补充,不是替代。 LangChain 管"线性流程"——你事先把每一步定死,A→B→C→D,顺着往下走。LangGraph 管"有状态的循环流程"——A 跑完之后下一步可能是 B 也可能是 C,取决于 A 的输出。甚至跑完 C 之后还能回到 A 再来一圈。

翻译成人话:Chain 是流水线,Agent 是厨子。LangChain 主要伺候流水线——你把每一步拍死在管道里就完了。但 Agent 那种"判断→调工具→看结果→再判断→再调工具"的循环模式,一根管子不够用——你需要一个图:节点是步骤,边是决策,图上可以有环,可以往回走。

所以 LangGraph 就是给 Agent 场景准备的。你在 LangChain 里定义 Prompt、Model、Retriever 这些零件,在 LangGraph 里把它们放进一个可以循环、分支、条件跳转的有向图。LangGraph 依赖 LangChain 的抽象层,但提供了一套完全不同的执行模型。

再直白点:你的流程能画成一根线——LangChain 的 LCEL 够了。你的流程画出来是个带环的图——上 LangGraph。别一上来就问"我该不该学 LangGraph",先问"我的流程到底长什么样"。流程没环,硬上 Graph,那属于自己给自己找不痛快。


最后回到那个问题

LangChain 到底给了你什么?

不是"更简单调模型"。调模型本来就不难。

它给你的是:当你的 AI 项目从"一个聪明的聊天框"长成"一个真正的产品"的时候,它不会沦为一座只有你一个人看得懂的屎山。

它帮你把 Prompt 从散落各处的字符串变成可管理的资产。

它帮你在模型之间自由切换的时候,不烧掉你一整层业务代码。

它帮你把"当前对话""用户偏好""任务状态""外部知识"这四种截然不同的信息分门别类,用不同策略管理。

它帮你的流程从一碗不可维护的面条,变成一段可以看、可以换、可以测的水管。

它让你排查问题的时候,不是在黑暗中猜,而是在数据里看。

Demo 从来不难。难的是,一年之后你还敢在周五下午打开那段代码,自信地改一行,然后准时下班。

工具从来不会替你思考。但它能帮你的思考有地方放、有结构存、有秩序长。

到头来,LangChain 教给我的,不是怎么用 API,是让我在一条蛇形曲线上少走了很多弯路。它把这个行业里大家已经摔过无数次的坑,提前填平了。

你不用 LangChain 也不会有任何问题——前提是你愿意自己填那些坑。或者你的项目短小到根本没那些坑。

但如果你确实要在这条路上走很远,找一副好底盘,不是偷懒,是清醒。

写这篇文章的时候,我又想起那个夏天半夜救火的兄弟。后来他还是学了 LangChain。不是因为什么技术情怀,就是因为太累了。他不想再做那个唯一能看懂代码的人,也不想每次产品提一个新需求都要深吸一口气再做心理建设。

他后来跟我说了一句话,我一直记着。他说:

"最好的代码不是你能写的代码。是你能在一年后不需要任何心理建设就敢改的代码。"

LangChain 帮他做到了一部分。剩下的,是他自己被教育出来的工程意识。

离开之前,我再把那五个问题扔回来。别想"该不该用 LangChain"这种大问题——先想"我到底站在哪"这种小问题——

离开之前,我再把那五个问题扔回来。别想"该不该用 LangChain",先想"我到底站在哪"——

你的 Prompt 乱了吗?你可能要换模型吗?你需要模型碰你的私有数据吗?你的输出要进系统而不只是聊天框吗?你排查问题是靠看数据还是在黑暗中猜?

这些问题跟 LangChain 没什么关系,只跟你的项目成熟度有关系。LangChain 只是你身边能拿到的其中一个答案。它不完美,但足够清晰。

你不必今天就做决定。但等哪天你盯着你那坨一千多行的面条代码的时候——突然发现自己连改一行都要做十次深呼吸——你回来再看一眼这篇文章。

你心里会有一个声音跟自己说:"卧槽,原来那家伙说的是真的。" 会懂的。

读者来信

0/1000

暂无来信,期待你的分享。