全书目录

第九编 框架层

第 43 章 LangChain:把 Agent 做成高层入口、中间件与应用接口

18 分钟 7,800 字 第 44 / 161 个阅读单元

LangChain 最容易被误解的地方,在于很多人会把它和 LangGraph 混成同一件事。

但从当前官方分层看,两者的职责已经非常清楚:

  • LangGraph 是低层 orchestration framework and runtime
  • LangChain 是建立在其上的高层 agent framework

因此,第 43 章第一条结论应该是:

text
LangChain 不是以“图运行时”为中心来组织世界,
而是以“开发者如何更快构造一个标准 Agent 应用接口”为中心。

这句话看上去细微,实际上非常重要。

因为 LangChain 的第一入口不是 StateGraph,而是 create_agent()。 这意味着它默认想帮你做的事情,不是先暴露节点、边和 reducer,而是先把常见 Agent 应用压缩成一套高层接口:

  1. 选模型
  2. 配工具
  3. 设 system prompt
  4. 加 middleware
  5. 需要时启用 structured output
  6. 需要时加 checkpointer 和 store
  7. 直接得到一个能跑的 agent graph

从这个入口出发,LangChain 真正关注的是:

text
如何把一个生产常见的 Agent 系统,
压缩成一套更稳定、更统一、更容易团队复用的高层入口。

#43.1 它真正解决的,不是“底层执行”,而是“高层接入”

理解 LangChain,最关键的一步,不是背 API,而是先接受一个事实:

它并不试图成为终极 runtime。

官方自己已经把分层说得很清楚了。 LangChain 负责“最容易开始构建 LLM 应用与 agents”的那一层; LangGraph 负责低层 orchestration runtime; LangSmith 负责 tracing、eval、deployment、observability。

也就是说,LangChain 的第一性问题不是:

  • 图如何执行
  • 检查点如何持久化
  • 中断如何恢复

这些问题它当然能碰到,但它不是从这些问题出发组织系统的。

它首先面对的是另一组问题:

  • 如何用统一接口接入不同模型
  • 如何把 tools、state、context、structured output 放进同一入口
  • 如何让常见治理需求有稳定的工程挂点
  • 如何让团队先在一个标准 agent loop 上协作起来

因此,LangChain 真正解决的是:

text
Agent 应用如何高层接入、统一组织、快速起步。

这也是为什么它最强的地方,不是“又多了一个底层控制原语”,而是:

  • create_agent
  • 标准模型接口
  • 标准消息接口
  • middleware 控制面
  • structured output 抽象
  • 与 LangGraph / LangSmith 的协作分层

如果说 LangGraph 代表的是“Agent 首先是一套执行系统”,那么 LangChain 代表的更像是:

text
Agent 首先是一套应用接口和工程入口。

#43.2 入口对象:为什么 create_agentStateGraph 更能暴露它的本质

LangChain 里,真正暴露框架本质的,不是某个复杂 class,而是 create_agent()

这背后的含义非常大。

因为当一个框架把最核心入口设计成 create_agent(model=..., tools=..., middleware=...) 这种形式时,它实际上已经在替你做三个决定:

第一,它假设多数开发者面对的是“标准 agent loop”,不是任意复杂图。

第二,它假设多数问题都可以先被组织成几类熟悉配置:

  • 模型
  • 工具
  • 消息
  • 中间件
  • 输出格式
  • 状态扩展

第三,它假设真正需要被高层化的,不是底层执行细节,而是“构建入口”的一致性。

这也是为什么 LangChain 给人的第一心智不是图,而是应用。

你不会一上来就去想:

  • 节点怎么连
  • reducer 怎么写
  • checkpoint 怎么分层

你更可能先想:

  • 用哪个模型
  • 暴露哪些工具
  • 需要什么 guardrails
  • 输出要不要结构化
  • 上下文怎么裁剪

也就是说,它把 Agent 系统入口从“图程序设计”转换成了“应用配置与控制面设计”。

这一步非常关键。 因为很多团队并不是一开始就需要复杂图,而是一开始就需要:

  • 一套统一入口
  • 一组共同约定
  • 一些标准工程挂点

从源码角度看,这一点也非常清楚。 create_agent 虽然是高层 API,但最后返回的并不是某个神秘助手对象,而是编译好的 CompiledStateGraph

这说明它并不是在抛弃底层运行时。 它做的事情更准确地说是:

text
把底层 graph runtime 包装成高层 agent application 接口。

所以,第 43 章第二条结论应该是:

text
LangChain 的高层化,不是取消图,
而是把图藏到统一入口之后。

#43.3 对象模型:AgentState、middleware、response_format 才是它的主语

如果按第 41 章的方法去找 LangChain 的一等对象,会发现它的核心对象模型并不是图原语,而是:

  • create_agent
  • AgentState
  • middleware
  • state_schema
  • context_schema
  • response_format
  • ToolStrategy
  • ProviderStrategy
  • built-in middleware

这里最值得注意的是,它把“控制面对象”提到了前台。

在很多低成熟度项目里,开发者一谈 Agent,就只谈三件事:

  • prompt
  • tool
  • memory

但在 LangChain 的对象模型里,这三件事并不足以定义一个成熟 agent。

因为在它这里,真正重要的是:

  • agent 状态如何组织
  • 模型调用前后是否可拦截
  • 工具调用前后是否可拦截
  • 最终输出是否有 schema 契约
  • 哪些上下文属于运行期注入,哪些属于可持久状态

换句话说,LangChain 的高层对象不是在描述“一个会思考的助手”,而是在描述“一个可被工程化改造的 agent 应用”。

尤其是 AgentState。 从源码看,它不是单纯的 messages 容器,而是最小运行态协议的一部分。 默认 state 里同时出现了:

  • messages
  • 内部控制字段
  • 结构化输出槽位

这意味着 LangChain 的 state 并不是纯聊天视图,而是把:

  • 对话消息
  • 内部控制
  • 输出契约

都放进了一个面向应用的高层状态接口里。

再看 response_format。 这也不是一个“方便参数”而已。 它代表的是另一层思路:输出格式不应只是自然语言约定,而应成为框架级接口。

如果 provider 支持原生 structured output,就走 ProviderStrategy。 如果不支持,就回退到 ToolStrategy。 这个设计的重点不在“技术实现得多巧妙”,而在:

text
LangChain 希望开发者面对的是统一结果契约,
而不是一堆 provider 差异。

这说明它在对象层真正高层化的,是应用接口,不是底层执行。

#43.4 生命周期:标准 agent loop 如何被做成统一接口

如果沿着运行生命周期去看,LangChain 的高层设计会更清楚。

它的典型生命周期不是“先写图,再运行图”,而是:

  1. 组装高层配置
  2. 解析模型与工具
  3. 合并 state / context 契约
  4. 安装 middleware
  5. 绑定 structured output 策略
  6. 生成标准 agent loop
  7. 编译成底层 graph runtime
  8. 通过 invoke / stream / ainvoke / astream 运行

这和直接使用 LangGraph 的区别非常明显。

LangGraph 里,你先定义“图是什么”。 在 LangChain 里,你先定义“应用需要什么”,再由框架生成合适的运行图。

这背后的本质差异是:

text
LangGraph 让你先定义执行结构;
LangChain 让你先定义应用能力。

所以它的生命周期里最重要的一步,不是 compile 本身,而是“配置如何被收敛成标准 loop”。

例如:

  • model 字符串被标准化成统一模型接口
  • tool 列表被拆成 provider-side 与 client-side 执行能力
  • structured output 被转成 provider strategy 或 tool strategy
  • middleware 被转成 hook 节点或 wrapper

最终,这一切才落到图上。

这也是为什么 LangChain 适合团队级高层开发。 因为团队协作时,最有价值的往往不是“每个人都能自己画图”,而是“每个人都围绕同一个入口和同一套生命周期协作”。

#43.5 状态契约:state_schemacontext_schemastore 分别在说什么

LangChain 的一个非常重要、但经常被低估的设计,是它把状态契约分成了几层。

最核心的一对,是:

  • state_schema
  • context_schema

这两者如果不区分清楚,系统很快就会失控。

state_schema 解决的是:

  • 本次 agent run 中,哪些信息属于可变工作状态
  • 哪些字段会进入状态更新
  • 哪些字段可能被 middleware、model、tool 写入或读取
  • 哪些字段会参与持久化、reducer 或输出裁剪

换句话说,它是工作状态的契约。

context_schema 的位置完全不同。 它更像运行时依赖注入边界,解决的是:

  • 哪些上下文来自外部系统
  • 哪些信息应该被 agent 使用,但不应被当作状态自由改写
  • 哪些对象本质上更接近环境、权限、用户身份、连接资源

因此,两者真正的区别可以压缩成一句话:

text
state 是 agent 正在处理的工作内存,
context 是外部注入的运行环境。

再往下还有 store。 这又是第三层。

它解决的不是当前 run 的状态,也不是当前 run 的注入环境,而是跨运行、跨会话的长期共享信息。

于是,LangChain 的状态分层其实是非常工程化的:

  • state:本次会话/运行中的短期状态
  • context:本次运行注入的外部环境与约束
  • store:跨运行长期存在的共享数据

这和本书前面反复强调的“输入契约、状态模型、长期记忆分层”完全同构。

也正是因为有这种分层,LangChain 才能比较自然地承载:

  • 动态工具过滤
  • 权限裁剪
  • 用户角色差异
  • 会话态 summarization
  • 长期记忆读取

如果没有这层分离,高层 agent API 很快就会沦为“prompt + messages 黑箱”。

#43.6 middleware:为什么它才是 LangChain 真正的工程控制面

如果说 create_agentLangChain 的入口中心,那么 middleware 就是它的控制面中心。

这件事必须讲透。

很多人会把 middleware 理解成“可选插件系统”,这会严重低估它。 从官方文档和博客的叙事看,middleware 的地位其实非常高。

因为 LangChain 已经明确把一个标准 agent loop 拆成若干可拦截步骤:

  • before agent
  • before model
  • wrap model call
  • wrap tool call
  • after model
  • after agent

这意味着什么?

意味着过去那些只能靠 prompt 或 ad hoc 代码实现的横切逻辑,现在有了统一结构位置。

例如:

  • 动态模型选择
  • fallback
  • retry
  • logging
  • summarization
  • PII 处理
  • tool selection
  • 权限限制
  • output 审查
  • HITL 审批

这些都不是“业务主流程”,却又决定系统是否可上线。 也正因为如此,它们不适合继续散落在 prompt 和工具描述里。

从这个角度看,middleware 的价值并不是“让你多插几个 hook”,而是:

text
LangChain 把 Agent 的治理逻辑、上下文工程和运行策略,
从 prompt 技巧提升成了统一工程层。

这也解释了为什么 LangChain 官方会把 context engineering 和 middleware 紧紧绑在一起。 因为真实世界里,很多 agent 失败的原因,并不是模型不够强,而是送给模型的上下文不对。

而“上下文如何被裁剪、追加、汇总、改写”,本质上就是 middleware 问题。

因此,本章第三条结论应该是:

text
LangChain 真正高层化的,不只是 Agent API,
而是围绕 Agent API 的工程控制面。

#43.7 structured output:它为什么不是“小功能”,而是应用契约

LangChain 里,structured output 也不能被理解成“小语法糖”。

它的真正价值在于,把“模型最终输出什么结构”提升成一等接口。

这件事听起来简单,但非常关键。

因为一旦 agent 进入真实系统,输出往往不是只给人看,而是要进入:

  • 前端渲染
  • 后端存储
  • 下游服务调用
  • 评测指标
  • 审核与审计

这时,如果输出仍然只是自然语言文本,再强的 prompt 也会出现脆弱解析问题。

所以 LangChain 的处理非常有代表性:

  • 优先使用 provider-native structured output
  • 如果 provider 不支持,就退回 tool strategy
  • 最终把结构化结果放进 structured_response

这里最值得注意的不是实现方式,而是框架态度:

text
结果格式不是调用方的临时后处理问题,
而是 agent 应用接口本身的一部分。

这也体现了 LangChain 和许多“把 agent 当自然语言黑箱”的工具的分界。

它并不是只想让你“问一个问题,得到一句回答”。 它更想让你得到:

  • 可验证
  • 可消费
  • 可传递
  • 可测试

的应用结果。

这正是一个高层应用框架应有的姿态。

#43.8 人审、guardrails 与 HITL:它如何把底层原语包装成高层模式

LangChain 里,人审与治理同样体现出它的层级定位。

它并没有重新发明中断原语。 底层暂停、恢复、持久化这些能力,仍然来自 LangGraph

但它做了另一件更偏产品和工程的事:

text
把这些底层原语包装成高层 agent 模式。

最典型的就是 HumanInTheLoopMiddleware

它并不是说:

  • 这里有个 interrupt,你自己处理

而是把问题重写成开发者更熟悉的高层控制语句:

  • 哪些工具调用需要审批
  • 人类可以 approve / edit / reject 什么
  • 审批插在哪个生命周期节点

这说明 LangChain 的 HITL 并不以“任意图节点暂停”为中心,而是以“标准 agent loop 的治理策略” 为中心。

同样,guardrails 在这里也不是一个孤立库。 它和 middleware、structured output、context engineering 一起,构成了高层控制面的一部分。

于是,LangChain 对治理的理解其实非常清晰:

  • 底层原语交给 runtime
  • 高层策略交给 middleware
  • 结果契约交给 structured output
  • 观测与评测交给 LangSmith

这套分工非常像成熟软件工程中的分层设计,而不是 demo 式 agent 封装。

#43.9 业务适配:它为什么更适合“标准 agent 应用”而不是“复杂 runtime 定制”

如果从业务适配角度看,LangChain 最适合的,不是所有 agent 问题,而是一类很具体的问题:

  • 你已经知道自己在做“一个 agent 应用”
  • 你需要快速接模型和工具
  • 你需要统一入口和统一状态接口
  • 你需要中间件层来承载治理、总结、重试、策略
  • 你希望团队在一个高层范式里协作

这类问题的共同点是:

text
你更需要“标准化接入”,而不是“完全自定义执行图”。

所以它特别适合这些场景:

  • 通用企业助手
  • 标准工具型 copilot
  • 常见客服助手
  • 需要结构化输出的应用 agent
  • 团队内部快速原型和可治理 MVP

但一旦问题变成下面这种形状,它就不再是最舒服的抽象:

  • 强确定性 + 强动态性混合
  • 复杂多阶段路由
  • 深度自定义状态机
  • 任意节点暂停恢复
  • 大量细粒度并行与分叉
  • 非标准 agent loop

这时你就会开始感到高层抽象在“帮你省事”和“挡住你控制”之间出现张力。

所以,LangChain 的业务适配边界可以很清楚地写成:

text
它更适合标准 agent application,
不适合作为复杂运行时定制的最终抽象层。

#43.10 团队价值:统一入口、统一接口、统一控制面

从团队协作角度看,LangChain 的价值往往比单人开发时更明显。

因为团队里真正难的,通常不是“写出第一个能跑的 agent”,而是:

  • 让不同成员围绕同一入口工作
  • 让模型切换不会影响整个应用层
  • 让安全、日志、上下文压缩、审批逻辑可复用
  • 让输出格式稳定,便于前后端协作

这些事情都不是底层 runtime 直接解决的。 它们更像“高层研发接口”的职责。

LangChain 恰恰站在这层。

它给团队带来的真实好处,大致有四个:

第一,统一 agent 构建入口。 大家不必各自发明 loop。

第二,统一模型与消息接口。 provider 差异不必直接扩散到业务层。

第三,统一控制面。 middleware 成为横切工程逻辑的稳定收口点。

第四,统一输出契约。 structured output 让前后端、评测、数据层更容易对齐。

如果再加上与 LangSmith 的天然协作关系,这套高层框架就更像:

text
一个面向团队研发的 Agent 应用接口层。

#43.11 它和 LangGraph、LangSmith 的真正关系

到这里,必须把 LangChain 放回完整分层里看。

最简单的说法是:

  • LangChain 负责高层 agent interface
  • LangGraph 负责低层 runtime / orchestration
  • LangSmith 负责 tracing / eval / deployment / observability

这三个层次如果不区分清楚,就会产生很多误读。

例如有人会问:

  • 为什么 LangChain 不把复杂图全部暴露出来?
  • 为什么 LangGraph 不直接提供高层应用入口?
  • 为什么观测和评测不是 LangChain 内建完成?

答案很简单:

它们本来就不是一个层。

LangChain 的强项在于:

  • 快速开始
  • 统一接口
  • 工程控制面
  • 高层应用模式

LangGraph 的强项在于:

  • 低层状态图
  • 可恢复执行
  • 中断与分叉
  • 深度编排控制

LangSmith 的强项在于:

  • 轨迹观测
  • 评测
  • 提示测试
  • 部署与运维

一旦这样理解,LangChain 的位置就会很准确:

text
它不是 Agent 控制内核,
而是 Agent 应用框架。

#43.12 最常见的误用:高层抽象最容易把人带偏到哪里

真正理解 LangChain,也要看它最容易被误用成什么。

最常见的误用,至少有六种。

第一,把 LangChain 当成底层 runtime 内核。 这样一来,当你需要精细控制状态转移和复杂拓扑时,就会误判它的抽象层级。

第二,误以为用了高层 agent API,就不再需要关心状态设计。 实际上 state、context、store 的边界依然必须自己建模。

第三,把所有复杂业务都硬塞进默认 create_agent loop。 表面上更快,实际上会越来越脆。

第四,把 middleware 当成零散插件,而不是统一控制面。 结果团队继续沿着“prompt 技巧堆叠”而不是“工程层治理”前进。

第五,把 structured output 理解成格式问题,而不是应用契约问题。 结果输出依旧停留在自然语言后解析。

第六,把多 agent 当成复杂度上升时的默认答案。 事实上,官方也反复提醒:不是每个复杂任务都需要 multi-agent,很多时候真正需要的是更好的上下文工程和更清晰的工作流边界。

因此,本章最后留下的判断应该是:

text
LangChain 的优势,不在于它比 LangGraph 更底层,
而在于它把常见 Agent 应用的入口、中间件和工程模式高层化了。

进一步压缩成一句话就是:

text
LangChain 更像 Agent 应用框架,
而不是 Agent 控制内核;
它最强的地方,是把“常见 Agent 系统”做成统一入口,而不是替代底层运行时。