LangChain:链的核心方法、构建使用、高级用法
LangChain 中的链(Chain)是其核心抽象之一,用于将多个组件(如模型、提示词模板、输出解析器等)组合成一个可执行的流水线,支持同步/异步调用、流式输出、序列化及记忆功能,是构建LLM应用的基础架构。
现代的 LangChain 主要推荐使用 LangChain 表达式语言(LCEL) 来构建链,它更简洁、功能也更强大。
核心方法
现代 LangChain 链的核心是 Runnable 接口,它为所有可执行组件(包括 LCEL 链)定义了标准化的调用方式。
方法解析
| 同步方法 | 异步方法 | 功能描述 | 输入类型 | 输出类型 |
|---|---|---|---|---|
invoke |
ainvoke |
单输入单输出,最基本的调用方式。 | InputType |
OutputType |
batch |
abatch |
批量处理多个输入,高效处理列表。默认并发执行。 | list[InputType] |
list[OutputType] |
stream |
astream |
流式输出,逐步生成结果,适用于长文本生成场景。 | InputType |
迭代器 |
astream_log |
- | 流式输出并包含中间步骤的日志,方便调试复杂链。 | InputType |
包含日志的流 |
调用方式
| 方法 | 说明 | 示例 |
|---|---|---|
invoke(input) |
同步调用 | chain.invoke({"input": "..."}) |
ainvoke(input) |
异步调用 | await chain.ainvoke({"input": "..."}) |
stream(input) |
流式输出(逐 token) | for chunk in chain.stream(...): print(chunk) |
batch(inputs) |
批量处理 | chain.batch([{...}, {...}]) |
配置参数
RunnableConfig 是一个配置字典,在调用 invoke, ainvoke, batch 等方法时通过 config 参数传入,用于控制 Runnable 的执行、调试和观测行为。
1 | chain.invoke(input_data, config=config_dict) |
config_dict 是一个字典,支持以下主要字段:
| 字段 | 类型 | 默认值 | 作用 | 是否传递至子调用 |
|---|---|---|---|---|
tags |
list[str] |
None |
为运行添加标签, 用于在追踪和日志中分类筛选, 可用于在 LangSmith 中过滤追踪 |
✅ 是 |
metadata |
dict[str, Any] |
None |
附加元数据,例如用户 ID、环境信息, 值需可JSON序列化,用于上下文追踪。 |
✅ 是 |
callbacks |
Callbacks |
None |
注册回调处理器,监听执行生命周期事件 (如开始、结束、错误)。 |
✅ 是 |
run_name |
str |
类名 | 覆盖追踪器中显示的运行名称。 | ❌ 否 |
run_id |
UUID |
自动生成 | 为本次执行指定唯一标识符, 用于追踪和关联。 |
❌ 否 |
max_concurrency |
`int | None` | None |
批量处理(batch)时的最大并行数,默认基于 ThreadPoolExecutor。 |
recursion_limit |
int |
25 |
限制递归调用(如Agent执行)的 最大深度,防止无限循环。 |
✅ 是 |
configurable |
dict[str, Any] |
None |
为动态配置字段提供运行时值, 如 temperature、model 等。可用户自定义配置,比如 session_id,常用于记忆、适配器等 |
✅ 是 |
timeout |
float |
None |
设置请求超时时间(秒),超时后抛出错误。 | ❌ 否 |
1 | config = { |
configurable
configurable 字段是实现运行时动态配置的核心,其设计主要服务于两大场景:
1. 调整预定义的 configurable_fields
此功能允许在不改变代码结构的情况下,运行时动态调整组件的关键参数。比如,一个链中定义了一个可配置的 temperature(温度系数),可以在每次调用时赋予其不同的值。
1 | from langchain_core.runnables import ConfigurableField |
2. 传递运行时密钥 (Secrets)
这是官方推荐的安全实践。对于敏感信息(如API密钥),你可以用 双下划线 __ 作为键名前缀来传递,LangChain 会确保这些信息不会被记录到追踪系统中。
1 |
|
在 LangSmith 的追踪记录中,traceable_key 可见,而 __my_secret 会被自动过滤隐藏
配置继承与合并:
理解 RunnableConfig 在复杂管道中的传播机制,是高级调试的关键。
- 配置继承:当在链的最外层(如
chain.invoke(config={...}))传入配置时,大部分字段(tags,metadata,callbacks,configurable)会默认向下传播,让内部的所有子组件都能自动应用这些配置。 - 配置合并:如果子组件也定义了自身的配置,LangChain 会进行智能合并。例如,
tags会被合并并去重,而metadata则是浅合并,子组件的值会覆盖父级传递的同名键。
输入参数
链的输入通常是一个字典,包含所有需要的变量
可以通过
input_schema查看链的输入要求:1
print(chain.input_schema.schema()) # 查看输入数据格式要求
config使用示例
示例1:为链添加追踪信息
1 | from langchain_core.callbacks import StdOutCallbackHandler |
执行时,StdOutCallbackHandler 会打印出链执行的每一步(例如 prompt 格式化后的内容、模型调用的 token 统计等)。LangSmith 中也会按 tags 和 metadata 进行分类和过滤。
示例2:传递会话 ID
当需要为链绑定记忆(RunnableWithMessageHistory)或实现多租户隔离时,会使用 configurable 字段:
1 | from langchain.memory import ChatMessageHistory |
注意:configurable 中的键值对会被传递给 Runnable 内部使用的适配器(如 BaseChatMessageHistory),从而区分不同会话。
示例3:批量处理与并发控制
当使用 batch 处理大量输入时,可以限制并发请求数量,避免触发 API 速率限制:
1 | inputs = [{"topic": "cats"}, {"topic": "dogs"}, {"topic": "birds"}] |
示例4:限制链的递归深度
在 Agent 或某些循环链中,防止无限循环,设置 recursion_limit限制递归深度。
1 | from langchain.agents import create_openai_functions_agent, AgentExecutor |
示例5:在异步中使用config
异步方法同样支持 config:
1 | async def run(): |
config注意事项
config参数是运行时的,不会改变链本身的结构,因此同一个链可以用不同的配置重复调用。- 如果在链构建时已经通过
.with_config()设置了默认配置,运行时传入的配置会合并(运行时优先级更高)。 configurable字段除了用于记忆,还可以用于自定义Runnable内部的动态行为(例如根据configurable["model_name"]动态切换模型)。
链的构建
创建方式
- LCEL语法:最常用方式,使用
|操作符连接组件 - Runnable组合:通过
RunnableSequence、RunnableParallel等高级组合器 - Legacy Chains:旧版链类(如
LLMChain、SequentialChain)
基本使用
基础链 (Basic Chain)
一个最简单的链,将提示词模板(PromptTemplate) 和模型(Model) 串联起来
1 | from langchain_core.prompts import ChatPromptTemplate |
顺序链 (Sequential Chain)
将多个链串联起来,前一个的输出作为后一个的输入,形成多步骤任务。
1 | from langchain_core.output_parsers import StrOutputParser |
并行链 (Parallel Chain)
使用 RunnableParallel 可以并发执行多个任务,并将结果组合成一个字典。
1 | from langchain_core.runnables import RunnableParallel |
进阶技巧
自定义链
当内置功能无法满足需求时,LangChain 提供了两种自定义链的方式:
使用 LCEL实现 (推荐)
这种方式更简洁,并能自动获得 stream, batch 等全部功能。
1 | from langchain_core.runnables import RunnableLambda |
继承 Chain 基类 (旧方式)
当需要深度集成 LangChain 的回调和配置系统时使用。
1 | from langchain.chains.base import Chain |
流式输出
1 | # 同步流式输出 |
异步支持
对于 I/O 密集型操作(如 API 调用),使用异步版本 ainvoke, abatch, astream 可以显著提升应用并发性能。
1 | # 在 async 函数内部 |
异步调用
1 | from langchain_openai import ChatOpenAI |
异步并行处理
1 | from langchain_core.runnables import RunnableParallel |
异步并发执行
ainvoke是 LangChain 链的异步调用方法,比同步invoke更适合高并发场景。asyncio.gather让所有请求真正并发执行(而非顺序等待),从而显著减少总耗时。
1 | from langchain_core.prompts import ChatPromptTemplate |
调试与追踪
全局调试
设置全局调试标志,打印所有链的详细输入输出信息。
1 | from langchain.globals import set_debug, set_verbose |
- LangSmith:推荐使用 LangSmith 平台进行全面的测试、监控和评估。
插入调试步骤
1 | from langchain_core.runnables import RunnableLambda |
LangSmith 追踪
1 | import os |
链的序列化
LangChain 的 Serializable 对象(包括大多数 LCEL 链)可以很方便地进行序列化和反序列化,以便保存、加载和分享。
若需要完整保存配置与链,建议将链的配置(如 prompt 模板、模型参数)存储为字典,再重新构建。
LangChain 主要通过 langchain_core.load 模块中的函数来实现对象的保存与加载。
LangChain 官方提供的四个核心函数如下:
四个核心方法
| 函数 | 功能 | 核心参数 |
|---|---|---|
dumpd |
将对象序列化为一个 Python 字典,便于后续处理或转为JSON。 | obj: 要序列化的LangChain对象 |
dumps |
将对象直接序列化为一个 JSON 字符串,适合保存到文件或网络传输。 | obj, pretty (可选) |
load |
从一个 Python 字典中反序列化,重建LangChain对象。 | obj (字典), secrets_map (可选) |
loads |
从一个 JSON 字符串中反序列化,重建LangChain对象。 | s (字符串), secrets_map (可选) |
方法使用示例
| 函数 | 方法示例 | 用途 |
|---|---|---|
| 转换为JSON字符串 | from langchain_core.load import dumps json_str = dumps(chain, pretty=True) |
用于保存到文件或网络传输。 |
| 转换为Python字典 | from langchain_core.load import dumpd dict_repr = dumpd(chain) |
可用于调试或在JSON不直接支持的场景下使用。 |
| 从JSON字符串加载 | from langchain_core.load import loads chain = loads(json_str, secrets_map={...}) |
反序列化的对应方法。 |
| 从字典加载 | from langchain_core.load import load chain = load(dict_repr, secrets_map={...}) |
从字典重建对象。 |
完整代码示例
1 | import json |
注意:反序列化时会检查导入路径的安全性,以防止恶意代码执行。
LangChain 的安全性是其设计的核心,尤其是在反序列化环节,提供了多层保护。
- 分离秘密信息(Secrets)
这是最重要的安全特性。当序列化一个对象(如包含了API Key的ChatOpenAI模型)时,API密钥这类敏感信息不会被写入到生成的JSON字符串或字典中。
对象的.lc_secrets属性定义了哪些字段是秘密。反序列化时,你需要通过secrets_map参数显式地提供这些秘密,从而安全地将它们注入回新创建的对象中。 - 基于白名单的类加载
反序列化过程并非随意实例化任何类,而是使用一个白名单(allowlist) 机制。load和loads函数默认只允许实例化白名单中的LangChain核心类(如ChatPromptTemplate,AIMessage,Document等)。任何不在白名单中的类都会被拒绝,有效防止了恶意类的加载。 - 序列化注入防护
LangChain的序列化机制能抵御注入攻击。如果一个普通字典意外地包含了'lc'这个内部键,在序列化时,这个字典会被“转义”(escaped),包裹在一个特殊的{"__lc_escaped__": {...}}结构中。这确保了该字典在反序列化时被当作普通数据,而不是LangChain对象来实例化,从而阻断了攻击路径。
序列化/反序列化最佳实践:
- 仅处理可信数据:永远不要对不受信任的输入使用
load或loads函数,反序列化是一个Beta版功能,存在潜在风险。 - 最小化权限:使用最严格的白名单配置,尽量提供一个明确的类列表,而非使用
'core'或'all'。 - 安全地注入秘密:将
secrets_from_env保持为默认的False,并通过secrets_map精确提供所需秘密。
添加记忆
记忆组件(Memory)
- 内存:
ChatMessageHistory(测试用) - Redis:
RedisChatMessageHistory - PostgreSQL:
PostgresChatMessageHistory
RunnableWithMessageHistory(LCEL)
1 | # 导入 LangChain 的核心历史链包装器,用于给普通链添加对话记忆功能 |
关键点说明:
- **
MessagesPlaceholder**:在 prompt 中预留一个位置,运行时由RunnableWithMessageHistory自动填入历史对话消息列表。 - **
RunnableWithMessageHistory**:负责在每次调用前从get_session_history获取历史记录,合并到输入字典中;调用后将新的对话对(用户输入 + AI 回复)自动保存回历史。 - **
config中的session_id**:用于区分不同用户/会话,实现记忆隔离。同一session_id共享历史,不同session_id互不干扰。
ConversationChain(旧)
记忆(Memory)让链拥有“状态”,能够记住之前的对话内容,是实现多轮对话的关键。
1 | # 导入 LangChain 旧版记忆组件:缓冲区记忆(存储完整对话历史) |
LangChain 提供了多种记忆类型以适应不同场景:
-
ConversationBufferMemory:最常用,存储所有历史消息,适合短对话。 -
ConversationBufferWindowMemory:限制上下文窗口,只保留最近 K 轮对话,避免内存溢出。 -
ConversationSummaryMemory:总结历史,使用 LLM 将对话历史压缩为摘要,适合处理非常长的对话。 -
ConversationEntityMemory:提取实体信息,记忆对话中特定实体(如人物、地点)的信息,适合需要个性化回答的场景。
实现方式的差异
| 对比维度 | 旧方式(ConversationChain) |
新方式(RunnableWithMessageHistory + LCEL) |
|---|---|---|
| 语法风格 | 封装好的“大而全”类,内部隐藏细节 | 基于 LCEL 的显式管道,每个组件清晰可见 |
| 记忆存储 | 通过 ConversationBufferMemory 自动管理 |
用户自定义 get_session_history 函数,支持任意后端(内存/Redis/DB) |
| 提示模板 | 内置固定模板(可覆盖但不灵活) | 完全自定义模板, 支持 MessagesPlaceholder 显式控制历史位置 |
| 会话隔离 | 每个 ConversationChain 实例默认一个记忆,多会话需手动创建多个实例 |
通过 configurable 中的 session_id 轻松实现多会话隔离,无需多实例 |
| 流式输出 | 不支持(predict 仅返回完整结果) |
原生支持 .stream() 逐 token 输出 |
| 异步支持 | 支持 apredict,但并发控制较弱 |
原生支持 ainvoke、abatch,性能更好 |
| 调试观测 | 可设置 verbose=True 打印日志 |
可集成 LangSmith、插入 RunnableLambda 调试,更细粒度 |
| 序列化 | 序列化能力弱,难以保存/加载 | 支持 dumpd / load,便于分享和部署 |
两种实现方式总结
ConversationChain是 LangChain 早期版本的“一站式”记忆对话方案,使用简单但扩展性较差,目前已进入维护模式,不推荐用于新项目。RunnableWithMessageHistory是基于 LCEL 的现代替代方案,虽然代码量稍多,但提供了完全的控制权、更好的性能和与 LangChain 生态(流式、异步、LangSmith)的深度集成。- 两者在概念上等效:都是将提示 + LLM + 记忆组合起来。但
RunnableWithMessageHistory可以看作是对ConversationChain内部机制的解构和重构,使其更符合函数式/管道式编程范式。
最佳实践
**使用 LCEL 而非旧式
LLMChain**:更现代、类型安全、支持流式。启用缓存:减少重复 LLM 调用。
1
2
3from langchain.cache import InMemoryCache
from langchain.globals import set_llm_cache
set_llm_cache(InMemoryCache())控制记忆长度:避免 token 爆炸,可使用
trim_messages裁剪历史。生产环境使用异步:
ainvoke/abatch提升并发性能。集成 LangSmith:全链路追踪与调试。
LangChain:链的核心方法、构建使用、高级用法
http://blog.gxitsky.com/2026/04/15/AI-LangChain-038-Chain-Use-Method/

