
这两年我见过最多的一个画面是模型回答得头头是道代码却怎么都接不住。你让大模型提取一段合同里的日期它反手给你一个包在Markdown代码块里的JSON你让它判断用户购买意向它十个回复里总有几个带好的以下是分析结果这种废话。这个模型说得对但产品用不上的落差正是LangChain这批编排框架出现的真正原因也是很多项目从单点调API转向框架的起点。这篇文章不聊Prompt技巧也不打算把LangChain的API背一遍。我想讲清楚一个核心问题LLM为什么不是万能药为什么我们需要一个编排框架来承接它。适合已经能独立调通模型接口、正准备往复杂任务走的开发者看。如果你还在犹豫要不要学LangChain或者觉得直接调API不就行了吗那这篇正好回答你的疑虑。1. 从客服话术生成场景看模型单点调用为何会卡壳先说我反复遇到的一类需求。某项目要做客服话术生成流程很清晰先根据用户留言判断购买意向再查询商品库存最后生成一段带促销信息的话术。听起来不复杂可当我真的只用一次模型调用去实现时问题接二连三地冒出来。模型在自由聊天时表现很好一旦进了业务系统它就成了一个不可控的接口。你会遇到三类非常典型的错位每类错位都直接把项目卡死在集成阶段。1.1 输出污染干净JSON被Markdown外壳包住第一次让我意识到问题严重的是让模型返回结构化数据。我要求它输出一个JSON对象包含购买意向和置信度两个字段它返回的内容长这样好的以下是您需要的JSON格式数据 json { intent: high, confidence: 0.92 }这不是个案。有时候是结尾多了句如果需要其他格式请告诉我有时候是整个JSON被markdown代码块包住有时候干脆在最后一个字段后面多一个逗号。为了保证程序能跑你的代码里必须写一个很脆弱的解析函数先剥掉代码块标记再找第一个左花括号和最后一个右花括号还要容忍尾逗号。 这段容错代码一次两次写没问题但每个用到结构化输出的场景你都要写一遍而且永远不知道模型下次会冒出什么新格式。你慢慢会发现你实际在做的事情是替模型补一个输出协议——这本来应该是框架层统一解决的东西。 ### 1.2 事实边界模型不该编库存而该查库存 比格式问题更严重的是模型对事实边界的混乱。我在调试某客服项目时问它这款商品还有库存吗模型会根据训练数据里的相似问题礼貌地编一个目前库存可能还有的答案出来。它不知道真实的库存表在哪更不知道应该先调用库存查询接口再回答。 正确的做法是模型负责判断这个问题需要查询库存这个意图真正查数据库的动作由一段确定性代码完成查询结果再交回模型组织语言。但如果你只给模型一个文本接口它根本没有调用工具的概念只会凭常识继续往下编。 要解决这个问题你的代码必须承担起决策与执行分离的职责先让模型输出一个意图标记再根据标记决定要不要调用库存接口。听起来不难但一旦有多工具选择、多轮确认、结果异常重试这些代码立刻会膨胀成一套不可维护的分支逻辑。 ### 1.3 上下文漂移第五轮对话忘了第一轮的约束 第三个卡壳点是多轮对话的状态管理。用户第一轮说给我查一款降噪耳机预算一千以内系统回答好的。第二轮用户改口预算加到两千系统也确认了。到了第五轮用户问那我刚才说的那款有没有白色模型可能已经分不清那款指的是哪款甚至忘了预算已经改过。 模型本身并不具备记忆能力它的上下文完全依赖调用方把历史消息重新喂给它。三轮以内的对话你可以在请求里粗暴地拼上历史文本到了第五轮、第八轮拼接策略就开始出问题——历史太长会撑爆Token限制太短又会丢关键信息。你需要一套记忆管理机制什么时候保留完整历史什么时候换成摘要关键约束怎么提取出来长期记住。这已经不是模型调用的问题而是应用层的状态管理问题。 ### 1.4 三个卡壳点指向同一个答案 这三个问题——输出污染、工具调用缺失、上下文漂移——单独看都是小毛病凑在一起就足以让项目从demo能跑变成上线半个月还在修边角。它们的共同点是模型本身很强大但应用层缺少一套标准化的机制来约束输出、连接工具、管理状态。 这就是编排框架要解决的事。LangChain这类工具不是让模型变强它解决的是模型周围那圈脏活。理解到这一层你才算真正知道为什么需要它。 ## 2. 裸写多步LLM流程时间到底耗在哪了 如果把任务从一次对话扩展成多步骤业务链路裸写代码的问题会进一步放大。我也曾经坚持手写封装层觉得框架是过度设计。后来把一个三步流程的项目从零手抄一遍才意识到时间都耗在了哪里。 ### 2.1 裸写链路时那些多余的代码在干嘛 就拿模型写文案→调用某个检查接口→模型根据反馈改稿这个最简单的Agent雏形来说手写时你要处理的不只是三次模型调用 - 不同的模型供应商请求格式不一样你要为每个来源单独封装HTTP请求、鉴权、超时、限流。 - Prompt模板散落在代码里改一个提示词可能要在多个文件里同步修改。 - 每次调用的输出都不一定是标准文本你要做二次清洗、格式校验、失败重试。 - 中间步骤里如果嵌入了外部API你需要手动把返回结果拼进下一轮的上下文中。 - 整条链路上没有日志出了错只能靠print调式。 这些工作跟模型能力毫无关系它们是纯粹的工程问题。问题在于它们出现的频率极高而你每次都要重新写一遍。 ### 2.2 简单对比裸代码方案与编排框架方案 我用一个表格直观展示同一批工程需求裸写和用框架的差距。 | 你要做的事 | 裸代码时的实现 | 用了编排框架后的写法 | | --- | --- | --- | | 切换模型供应商 | 改所有请求代码、参数映射 | 替换一个模型对象配置 | | 让模型输出结构化内容 | 自己写正则、剥Markdown、容错 | 配置解析器框架统一抽取 | | 在链路中增加一个步骤 | 改调用顺序、手动拼接上下文 | 增删一个节点或组件 | | 记录每次调用的输入输出 | 手动打日志还要自己加字段 | 链路组件自带调用上下文记录 | 表格里每一条都不是少写了五行代码的差别而是重构一次要花一小时和改一行配置的差别。后者意味着你可以更频繁地调整业务流程而不必把时间花在重复劳动上。 ### 2.3 任务复杂度拐点在哪出现 我自己的直观经验是一次模型调用可以裸写两次线性调用再写个普通函数也能接受但一旦出现模型输出决定要不要调用工具、工具结果又回喂给模型这种交叉结构手写复杂度会陡增。 这个拐点大概出现在需要三四个功能点协作的时候。比如一个完整的智能客服先分类→然后决策是否查库→再生成回复→最后要做敏感词过滤。到这种规模你靠手工维护的状态变量和分支判断已经很难看懂整个流程了。你会自然而然希望有一种方式能把步骤、分支、调用关系写成看得懂的结构。 这不是框架在强行刷存在感而是任务本身的复杂度到了需要编排层的时候。框架在这里扮演的角色类似你写工程时用的控制器你当然可以手写所有路由但一个成熟的路由器会帮你省掉大量边界情况处理。 ## 3. LangChain替我们扛住的三类脏活值得了解 说完为什么需要再来看看LangChain具体做了哪几件事。我的观点是框架的存在不是为了让代码看起来更酷而是它把LLM应用中最常重复的三类脏活封装成了标准组件。理解这三类脏活你就理解了编排框架的底层逻辑。 ### 3.1 脏活之一Prompt模板与输出解析的规范化 第一类脏活是把模型输入输出的非标准化问题变成标准化接口。以结构化输出为例裸写时你的解析函数差不多长这样 python import json def extract_json_from_model_output(raw): text raw.strip() if text.startswith(json): text text[7:] if text.endswith(): text text[:-3] start text.find({) end text.rfind(}) if start -1 or end -1: raise ValueError(no json found) return json.loads(text[start:end 1])这段函数看着不复杂但它是脆的。碰到模型在JSON后面追加说明文字、字段名变化、嵌套结构异常等问题你都得一层层补逻辑。而在LangChain里你可以用输出解析器直接声明需要的结构然后让框架负责从模型回复里抽取。它内部做的不只是正则还包括格式纠正、内容校验、字段映射这些是很多项目积累下来的通用解法。Prompt模板也一样。没有模板时你会在代码里拼接字符串改提示词需要找到对应的拼接处。用模板之后提示词和代码分离变量自动注入还能针对不同模型选择不同的模板版本。这本身并不神奇但它把模型沟通协议变成了可维护的配置项。3.2 脏活之二对话记忆从拼字符串变成管状态第二类脏活是把对话上下文从字符串拼接升级成受管状态。前面说过模型没有记忆多轮对话的上下文必须由应用层维护。没有框架辅助时你要自己做三件事什么时候截断历史、什么时候压缩成摘要、哪些关键信息必须单独提取长期保留。LangChain的记忆组件提供了一些默认策略滑动窗口只保留最近几轮摘要记忆在历史太长时自动生成摘要替代向量记忆把历史内容索引化相关片段可以被随时检索。我用一句话给非技术朋友解释过以前是把整本聊天记录复印一份全塞给模型现在是给模型带一份浓缩摘要它需要细节时再翻档案室。这里的价值不在于组件本身写得有多巧妙而在于你不必在第一个项目就把这套记忆机制从头设计一遍。你只需要理解策略的适用场景然后选用合适的那一个。如果连这一步都省掉项目还没上线上下文管理就会先拖垮你。3.3 脏活之三模型决策到工具调用的翻译循环第三类脏活也是LangChain最核心的价值点把模型的我想做什么翻译成程序实际执行什么。我前面提到模型会编造库存答案就是因为缺少工具调用机制。LangChain里的Agents解决的就是这个模型先输出一个特殊格式的行动计划比如调用查询库存函数参数是product_id123。框架识别到这个意图后会真正执行对应的Python函数然后把执行结果作为新的上下文回传给模型模型再决定下一步是继续调用还是生成最终答案。这个循环在论文里被叫作ReAct推理加行动模式本质上就是一个外部循环推理→行动→观察结果→再次推理。框架负责把循环体标准化包括停止条件、错误处理、工具注册、结果的格式整理。如果没有框架你要手工维护这个循环还要处理超时、死循环、工具报错后如何告诉模型等问题。这些边缘情况加起来很容易让新手手写实现崩溃。你可以把LangChain理解成一个连接器集线器一边连接各种模型供应商另一边连接业务工具和数据结构。模型的输出不再是终点而是一个中间态。4. 别把框架当银弹这些情况我反而会放下LangChain标题说LLM不是万能药我想同等地补一句编排框架也不是。我在项目里见过两种极端一种是完全不用框架导致代码失控另一种是任务只有两步却非要把Chain、Agent、记忆全部塞进来结果调试一个下午都定位不到问题。4.1 抽象层背后的排查黑洞框架把很多步骤封装成了组件这是好事但也带来了新的排查成本。裸代码出错时堆栈信息很直观这一行是我写的那一个函数是我写的。框架则有层层封装报错可能藏在某个底层组件的内部你定位问题要先穿过好几层抽象。我在实际项目里遇到过一个Agent链路突然开始重复调工具最后发现是某次回传结果里多了一个空白字符导致模型一直误判还没拿到结果。这种问题如果用裸代码写几十行里一眼就能看到拼接逻辑在框架的循环里你反而要找半天。框架管住了复杂度的下限但当你需要精细控制某一步的时候它会把你和细节隔开。所以我不建议在任何项目里无脑套LangChain。合适的做法是先判断这一步需要多少模型参与再决定要不要请出框架。框架服务的是模式化的需求而不是所有的需求。4.2 两步以内的固定流程裸代码反而更顺手有一种场景我基本不会用编排框架流程完全固定、步骤只有两三个、中途没有模型决策点。比如翻译一段文字→让另一个模型润色这个线性流程两个API调用一个串行函数就解决了。把这种链路放进LangChain里你反而要学习它的组件抽象、配置加载方式、调用方式。收益几乎为零学习成本却是实打实的。这就像去餐厅吃饭点一道现成的菜很合适但如果我只是要一杯白开水直接倒就行没必要走整套后厨流程。判断的标准很简单步骤数量和决策点。步骤少、决策点少裸代码更可控步骤多、出现模型决定走哪条路的情况框架才真正发挥价值。4.3 我判断要不要上框架的几个问题我把自己的判断标准整理成下面几个问题你在设计项目时也可以对照着过一遍流程里有多少个需要模型参与的点超过两个我倾向考虑框架。业务步骤会不会频繁调整如果会框架的组件化结构能让我更快改。模型输出是否需要对接外部工具或数据库有工具调用框架的Agent机制会省很多事。是否要求在多个模型供应商之间切换有这个需求框架的抽象层才划算。团队里其他人对这套工具的熟悉程度如何如果没人用过要把学习时间算进成本。这些问题如果大部分回答否我会选择手写一个轻量的调用层。如果大部分是是再引入编排框架并且只引入解决核心问题的部分不为了完整而完整。5. 我现在划分模型调用与编排框架边界的实操思路最后分享一套我在项目中实际使用的划分思路。它不保证适合所有团队但至少让我在踩过几次坑之后很少再为该不该用框架纠结。5.1 按复杂度分层的选型结构我的做法是给项目分四层裸API调用、轻量函数封装、编排框架、图式工作流引擎。第一层是单次模型调用直接写请求函数不引额外依赖。第二层是两三个步骤的固定流程封装成普通Python函数内部手动串调用。第三层才是LangChain用于多步骤、含工具调用、需要记忆管理的场景。第四层是更重的工作流引擎适合需要明确状态流转、分支条件、并行执行的复杂业务系统。这四层的边界不是死板的。某个功能今天只有两个步骤明天因为加了校验逻辑变成了四步我就会考虑从第二层升级到第三层。关键是别反过来项目还很轻先上了重框架后面每个小改动都要理解框架怎么处理。5.2 上框架之后观测与回滚仍然要自己做我见过一个常见误区用上框架就以为可以不用关心调用过程结果出了线上问题连哪一步Prompt导致行为突变都定位不到。框架给你的是编排能力不是观测能力。我自己在项目里不管用不用LangChain都会保证三点每次模型调用前记录输入摘要调用后记录输出和耗时每轮工具调用都留下独立日志关键参数和Prompt版本能够随时回退。框架提供的链路追踪可以辅助但它不能完全替代自己的业务日志。模型的行为是会漂移的模板调整一次输出可能全部变样。如果没有回滚机制你只能对着线上突发问题干瞪眼。5.3 给正在进阶的读者三条可执行建议如果你正准备从会调API走向做过完整AI应用我建议按这个顺序来先用裸代码手写一个最简单的Agent循环哪怕只支持一个工具也要亲手经历模型输出行动计划→代码执行→结果回传的完整过程。做完这一步你对LangChain里Agent组件的理解会扎实很多而不是只会调包。第二条建议是不要什么都做成Agent。能用确定性代码完成的步骤比如格式化、字段校验、固定分支判断全用普通代码写。模型负责的应该是理解和生成其余能写死就写死。这样你的项目不会出现模型随机决定今天要不要调接口这种悬念。第三条建议是学框架的时候先读设计动机再背API。LangChain的文档里会解释为什么要有Chain、为什么要有Agent、为什么记忆是这样设计的。先把这些问题想明白API怎么用只是查手册的事。很多同学学完框架只会照着教程抄换一个场景就不知道怎么写原因就是他跳过了为什么这一层。我在实际项目里最深的体会是模型负责不确定性框架负责确定性而你的职责是分清两者。每次我把任务拆成这里需要模型理解那里只是流程流转两层项目结构就立刻清晰了。框架不是为了让我们写更多代码而是把那些不得不写的代码放进可维护的容器里。这大概就是编排框架对于LLM项目的全部价值也是我写下这一篇的真正原因。