LangChain 资深开发者完全指南

3 阅读29分钟

版本参考:LangChain v1.x(2026年4月,langchain-core 1.3.0 / langchain 1.1+) 适用读者:有 Python 经验、希望构建生产级 LLM 应用、理解 DAG 与协议型组合的资深工程师 当前 github 地址:github.com/langchain-a…


目录

  1. 框架定位与 v1.0 的范式转变
  2. Monorepo 结构与包拆分
  3. LCEL 与 Runnable 协议
  4. 组合原语:Sequence / Parallel / Branch / Lambda / Passthrough
  5. RunnableConfig、回调传播与 contextvars
  6. 语言模型层次:BaseChatModel / BaseLLM / init_chat_model
  7. 消息类型与标准化 Content Blocks
  8. Prompt 模板体系
  9. 工具定义:@tool、StructuredTool、BaseTool
  10. 工具调用与结构化输出:bind_tools / with_structured_output
  11. create_agent:v1.0 唯一的 Agent 抽象
  12. Middleware 子系统(v1.0 旗舰特性)
  13. 结构化输出策略:Auto / Provider / Tool
  14. 检索与 RAG 体系
  15. Memory:从旧内存类到 LangGraph Checkpointer
  16. Callbacks 与 LangSmith 追踪
  17. 输出解析器
  18. Partner 包生态
  19. v0.x → v1.0 迁移要点
  20. LangChain 与 LangGraph 的关系与选型
  21. 生产最佳实践
  22. API 快速参考

1. 框架定位与 v1.0 的范式转变

1.1 LangChain 是什么

LangChain 是一套面向 LLM 应用的可组合抽象层,其设计目标是:

提供统一的 模型 / 提示 / 工具 / 检索 / 消息 / 回调 协议,让开发者以声明式方式拼装 LLM 管道,而无需被具体提供商 SDK 锁定。

在 v1.0(2025年10月22日 GA)之后,它的定位更加明确:

  • langchain-core:所有抽象协议的定义(Runnable、BaseChatModel、BaseTool、BaseMessage、Callbacks)
  • langchain:面向用户的主包,核心是 create_agentinit_chat_model、标准化 content blocks
  • langchain-classic:旧版链、旧版 Agent、旧版 memory、旧版检索器的归档
  • partner packages:OpenAI / Anthropic / Google 等供应商实现
  • LangGraph:真正的执行运行时(v1.0 的 create_agent 底层就是 LangGraph)

1.2 v1.0 的核心范式转变

维度v0.xv1.0
Agent 抽象AgentExecutor + 多种 create_*_agent 工厂统一 create_agent,底层编译为 LangGraph
自定义状态支持 dataclass / Pydantic / TypedDict仅允许 TypedDict 继承 AgentState
Hooks 系统散落的 pre_model_hookpost_model_hook统一的 Middleware 子系统
消息内容各家 provider 原生格式不一致标准化 content_blocks 属性
结构化输出Prompted JSON + 各种 response_formatAutoStrategy / ProviderStrategy / ToolStrategy
Memory18 个 *Memory委托给 LangGraph Checkpointer + Store
langchain-community包含 200+ 集成大多迁出到独立 repo,留骨架

1.3 LangChain、LangGraph、LangSmith 三件套

LangSmith      — 观测与评估平台(SaaS,独立)
  ↑ traces
LangChain      — 高层组合层、Agent、Middleware、检索
  ↓ compiles to
LangGraph      — 低层有状态执行运行时(StateGraph)
  ↓ built on
langchain-core — Runnable / BaseMessage / BaseTool / Callbacks 协议

v1.0 之后,这三层边界前所未有地清晰:核心协议 → 执行引擎 → 应用封装


2. Monorepo 结构与包拆分

2.1 目录布局

截至 2026 年 4 月,libs/ 目录结构:

langchain-ai/langchain/
├── libs/
│   ├── core/                    # langchain-core (v1.3.0),所有协议
│   ├── langchain_v1/            # langchain (v1.x),主用户包
│   ├── langchain/               # langchain-classic,旧链/旧 Agent/旧 memory
│   ├── partners/                # OpenAI / Anthropic / Ollama 等
│   ├── text-splitters/          # langchain-text-splitters
│   ├── standard-tests/          # 接口一致性测试套件
│   ├── model-profiles/          # 模型能力元数据
│   └── cli/                     # langchain CLI
├── docs/                        # 文档源码
└── cookbook/                    # 示例 notebook

2.2 包职责与依赖

langchain
    ├── depends on → langchain-core
    └── depends on → langgraph (create_agent 运行时)

langchain-classic
    └── depends on → langchain-core
                   → langchain (部分旧 API 从新包重导出)

langchain-openai / langchain-anthropic / ...
    └── peer dependency → langchain-core

langchain-text-splitters
    └── depends on → langchain-core (仅 Document 类型)

2.3 版本策略

  • 独立版本号langchain-corelangchain 独立发布,4 月中旬时 core 在 1.2.30 → 1.3.0 这条小步快跑的线上。
  • "2.0 前无破坏"承诺:v1.0 GA 之后公开承诺在 2.0 前不再做破坏性变更,只做增量扩展。
  • Partner 包独立节奏:每个 partner 包有自己的 test matrix 与 release 节奏,新模型 ID 直接透传到 provider SDK,无需等 LangChain 发版。
  • standard-tests:强制所有 partner 实现共享的一致性测试(BaseChatModel / Embeddings / VectorStore 都跑同一套用例),是"协议型"架构的质量基石。

2.4 langchain-community 的退场

v0.x 时代 langchain-community 是一个巨大的胶水包,装了 200+ 第三方集成。在 v1.0 时代:

  • 主流集成(Google、AWS、Pinecone、MongoDB 等)各自拆出独立 repo、独立 PyPI 包;
  • langchain-community 保留了小众/低频的集成,以及一些过渡期尚未拆出的模块;
  • 新集成不再向 langchain-community 提交,而是按 partner package 模板新建 repo。

3. LCEL 与 Runnable 协议

3.1 为什么要有 LCEL

LCEL(LangChain Expression Language)是一套管道式组合 DSL。核心动机:

  • 同一套代码覆盖 sync/async/streaming/batchinvoke / ainvoke / stream / astream / batch / abatch 由协议保证。
  • 天然并行RunnableParallelbatch 自动利用线程池 / asyncio.gather。
  • 天然可观测:每一层 Runnable 都通过 CallbackManager 上报 run,LangSmith 自动串联。
  • 运行时可配置with_configwith_retrywith_fallbackswith_listenersconfigurable_fields 全部免费。

3.2 Runnable 协议

所有 LCEL 组件都实现 langchain_core.runnables.base.Runnable[Input, Output]

class Runnable(Generic[Input, Output], ABC):
    # 同步 API
    def invoke(self, input: Input, config: RunnableConfig | None = None, **kwargs) -> Output: ...
    def stream(self, input: Input, config=None, **kwargs) -> Iterator[Output]: ...
    def batch(self, inputs: list[Input], config=None, *, return_exceptions=False,
              max_concurrency: int | None = None) -> list[Output]: ...
    def batch_as_completed(self, inputs, ...) -> Iterator[tuple[int, Output | Exception]]: ...

    # 异步 API
    async def ainvoke(self, input, config=None, **kwargs) -> Output: ...
    async def astream(self, input, config=None, **kwargs) -> AsyncIterator[Output]: ...
    async def abatch(self, inputs, config=None, *, return_exceptions=False, **kwargs) -> list[Output]: ...
    async def abatch_as_completed(self, inputs, ...) -> AsyncIterator[tuple[int, Output | Exception]]: ...

    # 高保真事件流
    async def astream_events(self, input, config=None, *,
                             version: Literal["v1","v2"]="v2", ...) -> AsyncIterator[StreamEvent]: ...

    # 组合辅助
    def __or__(self, other) -> RunnableSequence: ...
    def __ror__(self, other) -> RunnableSequence: ...
    def with_config(self, config: RunnableConfig) -> Runnable: ...
    def with_retry(self, *, stop_after_attempt=3, wait_exponential_jitter=True, ...) -> Runnable: ...
    def with_fallbacks(self, fallbacks: list[Runnable], *, exceptions_to_handle=(Exception,)) -> Runnable: ...
    def with_types(self, *, input_type=None, output_type=None) -> Runnable: ...
    def with_listeners(self, *, on_start=None, on_end=None, on_error=None) -> Runnable: ...
    def bind(self, **kwargs) -> RunnableBinding: ...
    def assign(self, **mappers: Runnable | Callable) -> Runnable: ...

    # 自省
    def get_graph(self, config=None) -> Graph: ...
    def get_input_schema(self, config=None) -> type[BaseModel]: ...
    def get_output_schema(self, config=None) -> type[BaseModel]: ...
    def config_schema(self, *, include=None) -> type[BaseModel]: ...

3.3 管道操作符 | 的秘密

chain = prompt | model | parser

|Runnable.__or__ / __ror__ 重载。等价于:

chain = RunnableSequence(first=prompt, middle=[model], last=parser)

非 Runnable 的 Python 值(如 dict、callable)会被自动包装:

  • dictRunnableParallel
  • callableRunnableLambda

所以 {"ctx": retriever, "q": RunnablePassthrough()} | prompt | model 是合法的。

3.4 batch 与 abatch 的实现细节

  • batch 默认在线程池里跑 invokemax_concurrency 控制并发。
  • abatchasyncio.gather 展开。
  • BaseLLM.batch 特化过:max_concurrency=None 时走 provider 原生批量接口(generate_prompt(prompts=[...]))。因此推理 API 费率敏感时应避免显式传 max_concurrency

3.5 astream_events v2 事件分类

每个事件结构为:

StreamEvent = TypedDict("StreamEvent", {
    "event": str,                         # "on_chat_model_start" 等
    "name": str,                          # Runnable 名称
    "run_id": str,
    "parent_ids": list[str],              # v2 才有:从 root 到 immediate parent
    "tags": list[str],
    "metadata": dict,
    "data": dict,                         # 具体 payload
})

事件名形如 on_<type>_<start|stream|end><type> 取值:chat_model | llm | chain | tool | retriever | prompt | parser。外加 on_custom_event(v2 专有)用于用户自定义事件。

3.6 LCEL 的经典套路

from langchain_core.runnables import RunnablePassthrough, RunnableParallel
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate.from_messages([
    ("system", "Answer using the context only."),
    ("human", "Context:\n{ctx}\n\nQuestion: {question}"),
])

rag_chain = (
    RunnableParallel(
        ctx=retriever,
        question=RunnablePassthrough(),
    )
    | prompt
    | model
    | StrOutputParser()
)

answer = rag_chain.invoke("What is LCEL?")

关键理解:每一根 | 都是 Runnable,每一层都可以 stream / batch / trace / retry


4. 组合原语

4.1 核心组合类

用途典型写法
RunnableSequence顺序管道a | b | c 自动构造
RunnableParallel并发扇出到 N 个 runnable{"a": a, "b": b}RunnableParallel(a=a, b=b)
RunnableLambda把普通函数变 RunnableRunnableLambda(lambda x: x.upper())
RunnablePassthrough原样透传RunnablePassthrough()
RunnablePassthrough.assign在 dict 上追加计算字段RunnablePassthrough.assign(length=lambda d: len(d["text"]))
RunnableBranchif/elif/else 分支RunnableBranch((cond1, branch1), (cond2, branch2), default)
RunnableEach对列表元素逐个应用sub_chain.map()
RunnableConfigurableAlternatives运行时切换实现model.configurable_alternatives(...)
RunnableConfigurableFields运行时改参数model.configurable_fields(temperature=ConfigurableField(id="temp"))

4.2 RunnablePassthrough.assign 的价值

RAG 场景下特别好用:

chain = (
    RunnablePassthrough.assign(
        ctx=lambda x: retriever.invoke(x["question"])
    )
    | prompt
    | model
)
chain.invoke({"question": "..."})
# ctx 自动算出来,并与 question 合并进下游

它等价于 RunnableAssign(mapper=RunnableParallel(ctx=...)),但代码更短。

4.3 RunnableBranch 的条件分发

from langchain_core.runnables import RunnableBranch

router = RunnableBranch(
    (lambda x: "code" in x["intent"], code_chain),
    (lambda x: "sql"  in x["intent"], sql_chain),
    fallback_chain,  # 末尾是默认分支
)

每个 predicate 必须同步返回 bool,不能是 Runnable。需要用 LLM 做路由时,先在上游跑一条 chain 产出 intent

4.4 .bind():冻结参数

strict_json_model = model.bind(response_format={"type": "json_object"})
web_model         = model.bind(tools=[web_search_tool], tool_choice="any")

返回 RunnableBinding,是一个轻量代理:所有调用自动附加绑定的 kwargs。


5. RunnableConfig、回调传播与 contextvars

5.1 RunnableConfig 结构

class RunnableConfig(TypedDict, total=False):
    tags: list[str]                      # LangSmith 过滤器列列
    metadata: dict[str, Any]             # LangSmith 自定义列
    callbacks: Callbacks                 # 回调处理器列表
    run_name: str                        # 在 trace 里显示的名字
    run_id: UUID                         # 强制指定 run_id
    max_concurrency: int | None          # batch 并发
    recursion_limit: int                 # LangGraph 递归深度
    configurable: dict[str, Any]         # configurable_* 运行时覆盖

任何 Runnable 方法都可以接收 config 作为第二个位置参数:

chain.invoke(
    {"question": "..."},
    config={"tags": ["prod"], "metadata": {"user_id": "u-42"}, "callbacks": [MyHandler()]},
)

5.2 回调在子 Runnable 中的传播

_call_with_configRunnable 内部的 wrapper,它会:

  1. 基于当前 config 构造一个子 CallbackManager
  2. 触发 on_chain_start
  3. parent_run_id 写入子 Runnable 的 config;
  4. 执行实际逻辑;
  5. 触发 on_chain_end / on_chain_error

于是 prompt | model | parser 整条链上,每一步的 trace 都自动挂到前一步下面。

5.3 Python 3.9/3.10 的 async 陷阱

Python 3.11 开始,contextvarsasyncio.Task 间自动拷贝,RunnableConfig 会自动传播。3.9/3.10 必须手动把 config 传进每层 await

# 3.9 / 3.10:必须手动 thread config
async def my_node(state, config):
    msg = await model.ainvoke(state["messages"], config)   # ← 一定要显式传
    return {"messages": [msg]}

astream_events 在老版本 Python 上经常因为丢 config 导致 "miss events"。3.11+ 是安全的。

5.4 with_config:永久附加配置

prod_chain = chain.with_config({"tags": ["prod"], "metadata": {"env": "prod"}})

返回新 Runnable,之后每次 invoke 都带这套 config,不影响原链。


6. 语言模型层次

6.1 继承关系

Runnable
  └── RunnableSerializable
       └── BaseLanguageModel[Output]
            ├── BaseChatModel           (Output = AIMessage)
            └── BaseLLM                 (Output = str)
                 └── LLM                (单 prompt 的 BaseLLM 简化)

6.2 BaseChatModel 核心钩子

子类必须实现 _generate,可选 _stream / _agenerate / _astream

class BaseChatModel(BaseLanguageModel[AIMessage]):
    def _generate(self, messages: list[BaseMessage], stop=None,
                  run_manager: CallbackManagerForLLMRun | None = None, **kwargs) -> ChatResult: ...
    def _stream(self, messages, stop=None, run_manager=None, **kwargs) -> Iterator[ChatGenerationChunk]: ...
    async def _agenerate(...) -> ChatResult: ...
    async def _astream(...) -> AsyncIterator[ChatGenerationChunk]: ...

    # 元数据
    @property
    def _llm_type(self) -> str: ...           # 用于 trace
    @property
    def _identifying_params(self) -> dict: ...

    # 流控
    disable_streaming: Union[bool, Literal["tool_calling"]] = False
    output_version: Optional[str] = None       # v0 / v1 / provider-specific

    # LangSmith
    def _get_ls_params(self, stop=None, **kwargs) -> LangSmithParams: ...

output_version 控制 AIMessage.content 的 shape:v1 会写标准化 content blocks,v0 保留 provider 原生格式。

6.3 init_chat_model:统一工厂

核心 API,位于 langchain.chat_models.init_chat_model

from langchain.chat_models import init_chat_model

# 简写:自动推断 provider
model = init_chat_model("gpt-5.2")
model = init_chat_model("claude-sonnet-4-6")

# 显式前缀
model = init_chat_model("openai:gpt-4")
model = init_chat_model("anthropic:claude-haiku-4-5-20251001")

# 运行时可切模型
model = init_chat_model(
    model="gpt-5.2",
    configurable_fields=("model", "temperature"),
    config_prefix="llm",
)

# 任意字段可改
model = init_chat_model(configurable_fields="any")

# 用法
model.invoke([HumanMessage("Hi")],
             config={"configurable": {"llm_model": "claude-sonnet-4-6",
                                      "llm_temperature": 0.2}})

configurable_fields="any" 特别强大,也特别危险——永远不要把未经校验的用户输入直接塞进 configurable dict,否则用户可以切换到贵模型或覆盖 system prompt。

6.4 返回类型:_ConfigurableModel

configurable_fields 非空时,init_chat_model 返回一个 _ConfigurableModel,它把实际模型构造推迟到第一次调用时,并按 config["configurable"] 动态 resolve。


7. 消息类型与标准化 Content Blocks

7.1 基础消息类

langchain_core.messages

角色常见字段
SystemMessagesystemcontent
HumanMessageusercontent(可为 str 或多模态 list)
AIMessageassistantcontent, tool_calls, additional_kwargs, usage_metadata, response_metadata
ToolMessagetoolcontent, tool_call_id, artifact
FunctionMessagefunction(legacy)同上
ChatMessage自定义 rolecontent, role

每种消息都有对应的 Chunk:AIMessageChunkHumanMessageChunk 等。Chunk 可以用 + 累加

acc = AIMessageChunk(content="")
async for chunk in model.astream(messages):
    acc = acc + chunk
# acc 就是完整的 AIMessage,tool_call_chunks 也会被正确合并

7.2 AIMessage.tool_calls

v1 时代 tool_calls 字段是一等公民:

class ToolCall(TypedDict):
    name: str
    args: dict[str, Any]
    id: str
    type: Literal["tool_call"]

流式场景下用 tool_call_chunks

class ToolCallChunk(TypedDict):
    name: str | None
    args: str | None        # partial JSON string
    id: str | None
    index: int | None

累加时 LangChain 会自动将 partial JSON 拼接并在可解析时 promote 到 tool_calls

7.3 标准化 Content Blocks(v1 旗舰特性)

为了解决各 provider 返回结构迥异的问题(Anthropic 返回 text + thinking + tool_use 的 block list,OpenAI 返回 string + 外挂 tool_calls,Gemini 又不一样),v1 新增懒属性 content_blocks

from langchain_core.messages import AIMessage

msg: AIMessage = model.invoke(...)

for block in msg.content_blocks:
    match block["type"]:
        case "text":        print(block["text"])
        case "reasoning":   print("[thinking]", block["reasoning"])
        case "citation":    print("[cite]", block["url"])
        case "tool_call":   print("→", block["name"], block["args"])
        case "image":       handle_image(block["source"])
        case "audio":       ...

所有受支持的 provider 适配器都实现了 native → 标准 block 的转换器。msg.content 仍然保留原生结构,你可以按需选择哪一层。

7.4 v1 消息 API 收紧

  • AIMessage.example 字段已移除,改用 additional_kwargs["example"]
  • .text() 由方法改为属性msg.text 能拿到纯文本拼接。方法形式在 v1 还能用但会 warn,v2 将移除。
  • 老代码 if m.text(): 要改成 if m.text:

8. Prompt 模板体系

8.1 类层次

BasePromptTemplate
├── StringPromptTemplate
│   ├── PromptTemplate              # 传统字符串模板
│   └── FewShotPromptTemplate
└── BaseChatPromptTemplate
    ├── ChatPromptTemplate          # 主要使用的
    └── FewShotChatMessagePromptTemplate

8.2 ChatPromptTemplate

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are {persona}."),
    MessagesPlaceholder("history", optional=True),
    ("human", "{question}"),
])

messages = prompt.invoke({
    "persona": "a senior Python engineer",
    "history": [HumanMessage("hi"), AIMessage("hello")],
    "question": "Explain asyncio.",
}).to_messages()
  • 元组简写:("system", ...), ("human", ...), ("ai", ...), ("placeholder", "{var}")
  • 占位符支持 optional=True,允许变量缺失不报错。
  • template_format 可选 "f-string"(默认)或 "jinja2"

8.3 Few-shot 模板

from langchain_core.prompts import FewShotChatMessagePromptTemplate

example_prompt = ChatPromptTemplate.from_messages([
    ("human", "{input}"), ("ai", "{output}"),
])

few_shot = FewShotChatMessagePromptTemplate(
    example_prompt=example_prompt,
    examples=[
        {"input": "2+2", "output": "4"},
        {"input": "3+3", "output": "6"},
    ],
)

final = ChatPromptTemplate.from_messages([
    ("system", "Do math."),
    few_shot,
    ("human", "{question}"),
])

配合 ExampleSelector(embedding 近邻、长度加权等)可以做动态样例选取。


9. 工具定义

9.1 @tool 装饰器(90% 场景的首选)

from langchain_core.tools import tool

@tool
def add(a: int, b: int) -> int:
    """Add two integers."""
    return a + b

# 高级形式
@tool("weather", args_schema=WeatherSchema, return_direct=True)
def get_weather(city: str) -> dict: ...

# 带 artifact 的工具
@tool(response_format="content_and_artifact")
def search(q: str) -> tuple[str, list[dict]]:
    hits = do_search(q)
    summary = format_hits(hits)
    return summary, hits      # (content, artifact)
  • 自动从类型注解与 docstring 生成 args_schemadescriptionname
  • response_format="content_and_artifact" 时,content 喂回 LLM,artifact 留在 ToolMessage.artifact 供后续取用。

9.2 StructuredTool

适合从已有函数快速生成工具:

from langchain_core.tools import StructuredTool

def book(flight_no: str, seat: str) -> str: ...

book_tool = StructuredTool.from_function(
    func=book,
    name="book_flight",
    description="Book a flight seat.",
    args_schema=BookSchema,
)

9.3 BaseTool 手写类

需要细粒度控制(复杂 async、权限校验)时:

from langchain_core.tools import BaseTool
from pydantic import BaseModel

class QuerySchema(BaseModel):
    sql: str

class SqlTool(BaseTool):
    name: str = "sql_query"
    description: str = "Run a read-only SQL query."
    args_schema: type[BaseModel] = QuerySchema

    def _run(self, sql: str) -> str:
        _assert_readonly(sql)
        return self._db.run(sql)

    async def _arun(self, sql: str) -> str:
        _assert_readonly(sql)
        return await self._db.arun(sql)

9.4 Injected 注解:对 LLM 不可见的参数

from typing import Annotated
from langchain_core.tools import tool, InjectedToolCallId
from langgraph.prebuilt import InjectedState

@tool
def remember(
    fact: str,
    *,
    state: Annotated[AgentState, InjectedState],
    tool_call_id: Annotated[str, InjectedToolCallId],
) -> str:
    """Save a fact to the user's memory."""
    state["memory"].append(fact)
    return "OK"

InjectedState / InjectedToolCallId / InjectedToolArg 让 LangGraph 在运行时注入值,但生成的 JSON Schema 会自动剔除这些参数——LLM 根本看不到它们。

9.5 把 retriever 变成 tool

from langchain.tools.retriever import create_retriever_tool

kb_tool = create_retriever_tool(
    retriever,
    name="search_kb",
    description="Search the internal knowledge base.",
)

10. 工具调用与结构化输出

10.1 bind_tools

model_with_tools = model.bind_tools(
    tools=[add, weather, sql_tool, {"type": "web_search_20250604"}],  # BaseTool / @tool / dict
    tool_choice="auto",                 # "auto" | "any" | "none" | "required" | {"type":"function","function":{"name":"..."}}
    strict=True,                        # OpenAI/xAI:严格 schema 约束
    parallel_tool_calls=True,           # OpenAI
)

msg: AIMessage = model_with_tools.invoke([HumanMessage("用工具加 2 和 3")])
for tc in msg.tool_calls:
    print(tc["name"], tc["args"])
  • dict 类型参数代表 provider 的内建工具(如 OpenAI Responses API 的 web_searchfile_searchcomputer_use),LangChain 原样透传。
  • tool_choice="any" 在 Anthropic 表示"必须调用某个工具",OpenAI 对应 "required"

10.2 with_structured_output

from pydantic import BaseModel, Field

class Answer(BaseModel):
    city: str
    temperature_c: float = Field(description="Celsius")

structured = model.with_structured_output(
    schema=Answer,
    method="json_schema",   # 可选 "function_calling" | "json_mode" | "json_schema"
    include_raw=False,      # True 时返回 {"raw":..., "parsed":..., "parsing_error":...}
    strict=True,
)

ans: Answer = structured.invoke("What's the temperature in Tokyo?")

10.3 两个常见坑

坑 1:with_structured_output(S).bind_tools([t]) 会挂 with_structured_output 返回的是 RunnableSequence(内部已经绑好了一个隐藏抽取工具),没有 bind_tools 方法。解决方案:在 bind_toolstool_choice 指定 schema 对应的工具名,不要再走 with_structured_output

坑 2:流式状态下 tool args 合并不稳 部分 provider 在 .stream(...) 下会把 tool call 的 args 单次吐出(无 partial chunk),导致你等不到增量 JSON。Issue #29129 描述了这一点。Workaround:对 tool call 走 .invoke(非 stream),或只用 .astream_events(version="v2") 来拿 on_chat_model_stream 事件。

10.4 回调注入

msg = model.invoke(
    messages,
    config={
        "callbacks": [UsageMetadataCallbackHandler()],
        "tags": ["extraction"],
        "metadata": {"task_id": "T-101"},
    },
)

不要通过 model = ChatOpenAI(callbacks=[...]) 旧式绑定——v1.0 已完全改用 config 注入模型。


11. create_agent:v1.0 唯一的 Agent 抽象

11.1 签名

from langchain.agents import create_agent, AgentState
from langchain.agents.structured_output import ToolStrategy, ProviderStrategy

agent = create_agent(
    model: str | BaseChatModel,
    tools: Sequence[BaseTool | Callable | dict],
    *,
    prompt: str | ChatPromptTemplate | Callable | None = None,
    response_format: ToolStrategy | ProviderStrategy | type | None = None,
    state_schema: type[AgentState] | None = None,
    middleware: Sequence[AgentMiddleware] = (),
    checkpointer: BaseCheckpointSaver | None = None,   # LangGraph
    store: BaseStore | None = None,                    # LangGraph
    context_schema: type | None = None,
    name: str | None = None,
    version: Literal["v1", "v2"] = "v2",
    debug: bool = False,
)

返回值是一个 已编译的 LangGraph CompiledStateGraph:你可以直接 .invoke / .stream / .astream_events / .get_state / .update_state / .get_graph().draw_mermaid()

11.2 最小示例

from langchain.agents import create_agent
from langchain_core.tools import tool

@tool
def add(a: int, b: int) -> int:
    """Add."""
    return a + b

agent = create_agent(
    model="openai:gpt-5.2",
    tools=[add],
    prompt="You are a math assistant.",
)

result = agent.invoke({"messages": [HumanMessage("1+2+3?")]})
print(result["messages"][-1].content)

11.3 AgentState

from typing_extensions import TypedDict, Annotated, NotRequired
from langgraph.graph.message import add_messages
from langgraph.managed import RemainingSteps

class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]
    remaining_steps: NotRequired[RemainingSteps]
    structured_response: NotRequired[Any]     # 仅当 response_format 被设置

v1.0 强制要求:自定义 state 必须继承 AgentState 并且是 TypedDict。Pydantic / dataclass 不再被接受,这是 v1.0 迁移中最常见的 breaking change。

class MyState(AgentState):                     # OK
    user_id: str
    notes: list[str]

# 以下都会被拒绝(v1.0):
# class MyState(BaseModel): ...
# @dataclass class MyState: ...

11.4 remaining_steps

remaining_steps = recursion_limit − steps_taken。当它低于 2 且还有 pending tool call 时,agent 会优雅返回一条 "Sorry, need more steps to process this request."而不是GraphRecursionError——这是一个比 v0.x 更友好的兜底。

11.5 Agent 与 prompt 的几种写法

# 1) 静态字符串 → 变成 SystemMessage
agent = create_agent(model, tools, prompt="You are helpful.")

# 2) ChatPromptTemplate
agent = create_agent(model, tools, prompt=ChatPromptTemplate.from_messages([
    ("system", "You help users with {domain} questions."),
    ("placeholder", "{messages}"),
]))

# 3) Callable(state, runtime) → list[BaseMessage]
def dynamic_prompt(state, runtime):
    persona = runtime.context.persona
    return [SystemMessage(f"Act as {persona}.")] + state["messages"]

agent = create_agent(model, tools, prompt=dynamic_prompt)

11.6 从 AgentExecutor 迁过来

v0.xv1.0
create_tool_calling_agent(llm, tools, prompt) + AgentExecutor(agent, tools)create_agent(model, tools, prompt=prompt)
create_react_agent(llm, tools, prompt)(字符串解析版)create_agent(model, tools, prompt=...)
AgentExecutor(..., return_intermediate_steps=True)遍历 result["messages"] 自己筛 ToolMessage
AgentExecutor(..., max_iterations=10)agent.invoke(..., config={"recursion_limit": 10*2+1})
pre_model_hook / post_model_hookMiddleware 的 before_model / after_model

12. Middleware 子系统

12.1 三种 Hook 风格

风格钩子语义
Node-style(观察/修饰)before_agent, before_model, after_model, after_agent在节点前后运行,可返回 state 增量
Wrap-style(拦截)wrap_model_call, wrap_tool_call包住 LLM 调用/工具调用,决定是否真正执行
Conveniencemodify_model_request一次性改 tools / messages / tool_choice / 输出格式

执行顺序:

before_agent
  → before_model[各中间件顺序]
    → wrap_model_call[嵌套洋葱]
      → 实际模型调用
    → after_model[各中间件倒序]
  → tool loop / wrap_tool_call
after_agent

12.2 装饰器形式

from langchain.agents.middleware import before_model, wrap_model_call

@before_model
def inject_user_profile(state, runtime):
    return {"messages": [SystemMessage(f"User: {runtime.context.user_name}")]}

@wrap_model_call
def retry_on_rate_limit(request, handler):
    for i in range(3):
        try:
            return handler(request)
        except RateLimitError:
            time.sleep(2 ** i)
    raise

12.3 类形式(推荐用于复杂场景)

from langchain.agents.middleware import AgentMiddleware

class AuditMiddleware(AgentMiddleware):
    state_schema = AuditState              # 扩 state 的首选方式

    tools = [audit_tool]                   # 本 middleware scoped 的工具

    def before_model(self, state, runtime):
        log.info("model call", user=runtime.context.user_id)
        return None

    def wrap_tool_call(self, request, handler):
        if is_dangerous(request.tool_call):
            raise PermissionError("blocked")
        return handler(request)

state_schema 属性是 v1.0 扩展 state 的官方方式——比在 create_agent(state_schema=...) 里手动合并更安全。

12.4 v1 自带 Middleware 清单

Middleware功能
SummarizationMiddleware历史超阈值时在 before_model 里做增量总结
ModelRetryMiddleware(v1.1)wrap_model_call 里指数退避重试
PIIMiddleware输入/输出/工具结果的 PII 掩码/哈希/硬阻断,失败抛 PIIDetectionError
LLMToolSelectorMiddleware用廉价模型从大 toolset 预选子集
ShellToolMiddleware注入 shell 工具 + before_agent/after_agent 生命周期管理
AnthropicPromptCachingMiddleware(ttl="5m")在 langchain-anthropic:自动标记 cache_control
Anthropic text_editor / memory_tool / file_search / bash_tool middleware把 Anthropic 服务器端工具能力接入 agent

12.5 Middleware vs 旧 hook 的不兼容警告

langgraph.prebuilt.create_react_agentpre_model_hook / post_model_hook 不能与 middleware 共存——混用会触发 runtime error。迁 middleware 时要一并把 hook 删掉。


13. 结构化输出策略

13.1 三种策略

langchain.agents.structured_output

Strategy适用原理
AutoStrategy[T]默认,推 schema 就会选它优先选 ProviderStrategy,不支持时回落到 ToolStrategy
ProviderStrategy(schema, strict=None)OpenAI JSON schema、Anthropic structured outputs、Gemini JSON mode使用 provider 原生结构化 endpoint
ToolStrategy(schema, tool_message_content=None, handle_errors=...)任何支持 tool calling 的模型合成一个 schema 工具,末回合 tool_choice="any"

13.2 用法

from pydantic import BaseModel

class TicketTriage(BaseModel):
    severity: Literal["P0","P1","P2","P3"]
    category: str
    summary: str

# 方式 1:直接传 schema(AutoStrategy)
agent = create_agent(model, tools, response_format=TicketTriage)

# 方式 2:手动选 ToolStrategy(跨 provider 最稳)
agent = create_agent(
    model, tools,
    response_format=ToolStrategy(
        schema=TicketTriage,
        handle_errors=lambda e: f"Please fix: {e}",
    ),
)

# 结果在 state["structured_response"]
result = agent.invoke({"messages": [HumanMessage("...")]} )
triage: TicketTriage = result["structured_response"]

13.3 常见问题

  1. 流式受限:两种策略下最后一轮都是"强制 tool call",几乎没有前置 text,astream 会很短。
  2. Schema 必须静态已知:目前不支持根据运行时状态动态换 schema(路线图上)。
  3. Gemini 3 误路由:若 Gemini 3 被 AutoStrategy 误选到 ToolStrategy,显式包 ProviderStrategy(TicketTriage) 即可。
  4. 异常StructuredOutputErrorMultipleStructuredOutputsErrorStructuredOutputValidationError

14. 检索与 RAG 体系

14.1 Document 与 BaseLoader

from langchain_core.documents import Document

doc = Document(
    page_content="LangChain is ...",
    metadata={"source": "README.md", "page": 1},
    id="doc-1",
)

BaseLoader 协议:

class BaseLoader(ABC):
    def load(self) -> list[Document]: ...
    def lazy_load(self) -> Iterator[Document]: ...
    async def aload(self) -> list[Document]: ...
    async def alazy_load(self) -> AsyncIterator[Document]: ...

常用 loader:TextLoader / CSVLoader / JSONLoader / UnstructuredFileLoader / DirectoryLoader / PyPDFLoader / PyMuPDFLoader / UnstructuredPDFLoader / WebBaseLoader / RecursiveUrlLoader / PlaywrightURLLoader / SitemapLoader,云存储 loader 分散在各自的 partner 包。

14.2 Text Splitters

from langchain_text_splitters import (
    RecursiveCharacterTextSplitter,
    TokenTextSplitter,
    MarkdownHeaderTextSplitter,
    Language,
)

splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    separators=["\n\n", "\n", " ", ""],
    length_function=len,
    keep_separator=True,
)

py_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.PYTHON, chunk_size=1500, chunk_overlap=200,
)

chunks: list[Document] = splitter.split_documents(loader.load())

高级:SemanticChunker(基于 embedding 断句)、MarkdownHeaderTextSplitterHTMLSectionSplitter

14.3 Embeddings 与 VectorStore

from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS

embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
store = FAISS.from_documents(chunks, embeddings)
retriever = store.as_retriever(
    search_type="mmr",
    search_kwargs={"k": 5, "fetch_k": 20, "lambda_mult": 0.5},
)

搜索类型:

  • "similarity":最近邻
  • "mmr":Maximal Marginal Relevance,平衡相关性与多样性
  • "similarity_score_threshold":带阈值过滤

search_kwargs 支持 filter={...} 做 metadata 过滤(各 vectorstore 表达式略有差异)。

14.4 高级检索器(都在 langchain-classic 下)

检索器用途
MultiQueryRetrieverLLM 把查询改写成 N 个不同措辞的 query,取并集
ContextualCompressionRetrieverLLMChainExtractor / LLMChainFilter / EmbeddingsFilter 压缩命中
ParentDocumentRetriever存子块(高召回),返父块(全上下文)
MultiVectorRetriever索引 summary / HyDE 问题,返回原文
EnsembleRetriever稀疏(BM25)+ 稠密融合,RRF 加权
SelfQueryRetrieverLLM 把自然语言 query 编译成结构化 metadata 过滤
TimeWeightedVectorStoreRetriever时间衰减加权

14.5 Indexing API(增量同步)

from langchain.indexes import SQLRecordManager, index

rm = SQLRecordManager(namespace="my_ns", db_url="sqlite:///rm.db")
rm.create_schema()

index(
    docs,
    record_manager=rm,
    vector_store=store,
    cleanup="incremental",         # incremental / full / None
    source_id_key="source",
)

基于内容哈希 + source id 做可重跑的去重同步,生产数据管道必备。

14.6 典型 RAG 链

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

template = """仅基于上下文回答问题。不知道就说不知道。

上下文:
{context}

问题:{question}
"""

prompt = ChatPromptTemplate.from_template(template)

rag_chain = (
    {"context": retriever | (lambda docs: "\n\n".join(d.page_content for d in docs)),
     "question": RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)

15. Memory:从旧内存类到 LangGraph Checkpointer

15.1 旧 memory 的终结

下列 v0.x 的类全部在 v1.0 移到 langchain-classic,并且不再推荐使用

  • ConversationBufferMemory
  • ConversationBufferWindowMemory
  • ConversationSummaryMemory
  • ConversationSummaryBufferMemory
  • ConversationKGMemory
  • ConversationEntityMemory
  • VectorStoreRetrieverMemory
  • CombinedMemory
  • ReadOnlySharedMemory

原因:它们早于 tool calling 时代设计,缺乏 thread/user 维度,且无法跨进程持久化。

15.2 新架构:Checkpointer + Store

Checkpointer(会话内持久化,按 thread_id)
  └── InMemorySaver / MemorySaver        (开发)
  └── SqliteSaver / AsyncSqliteSaver     (本地文件)
  └── PostgresSaver / AsyncPostgresSaver (生产)

Store(跨会话长期记忆,按 namespace)
  └── InMemoryStore
  └── PostgresStore
  └── 自带向量索引(embedding config)

thread_iduser_id 通过 config["configurable"] 在运行时传入:

agent = create_agent(
    model, tools,
    checkpointer=PostgresSaver.from_conn_string("postgresql://..."),
    store=PostgresStore.from_conn_string("postgresql://..."),
)

agent.invoke(
    {"messages": [HumanMessage("记住我喜欢 Python")]},
    config={"configurable": {"thread_id": "chat-42", "user_id": "u-100"}},
)

15.3 trim_messages 辅助函数

超长历史控制的银弹:

from langchain_core.messages import trim_messages

trimmed = trim_messages(
    messages,
    max_tokens=4000,
    token_counter=model,          # BaseLanguageModel 或 Callable
    strategy="last",              # "first" | "last"
    start_on="human",             # 确保从 human 开始(保留对话完整性)
    end_on=["human", "tool"],
    include_system=True,
    allow_partial=False,
)

最佳实践:把它写进一个 SummarizationMiddleware 或自定义的 before_model 里,每次模型调用前自动裁剪。

15.4 LangMem

LangMem SDK 构建在 BaseStore 之上,提供语义记忆(用户偏好、事实)与程序记忆(how-to 经验)两种高层 API。适合已经用 Store 做了基础的项目继续深化。


16. Callbacks 与 LangSmith 追踪

16.1 BaseCallbackHandler

位于 langchain_core.callbacks.base,由六个 Mixin 拼成:

BaseCallbackHandler
├── LLMManagerMixin       → on_llm_start / new_token / end / error
├── ChainManagerMixin     → on_chain_start / end / error
├── ToolManagerMixin      → on_tool_start / end / error
├── RetrieverManagerMixin → on_retriever_start / end / error
├── CallbackManagerMixin  → on_chat_model_start / on_text
└── RunManagerMixin       → on_agent_action / on_agent_finish / on_custom_event

AsyncCallbackHandler 提供异步镜像。每个钩子都接收 run_idparent_run_idtagsmetadataserialized

16.2 最小 Handler

from langchain_core.callbacks.base import BaseCallbackHandler

class TokenPrinter(BaseCallbackHandler):
    def on_llm_new_token(self, token: str, **kwargs):
        print(token, end="", flush=True)

chain.invoke({"q": "..."}, config={"callbacks": [TokenPrinter()]})

16.3 CallbackManager 内部机制

handle_event 是核心 dispatch:

  • 同步上下文 + 同步 handler:直接调用。
  • 同步上下文 + 异步 handler:汇集为 coroutine,用 asyncio.Runner(3.11+)或 asyncio.run 跑完。
  • 异步上下文:走 ahandle_event,用 asyncio.gather 并发所有 async handler。
  • run_inline=True 的 handler 顺序执行,确保关键副作用不并发。
  • Handler 没实现 on_chat_model_start 时,系统自动降级为 on_llm_start + 字符串化消息。

16.4 LangSmith 零配置接入

export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=lsv2_...
export LANGSMITH_PROJECT=my-agent

langchain_core.tracers.context 会自动注册 LangChainTracer,之后任何 Runnable 的执行都进入 LangSmith:

  • 嵌套 run 依靠 parent_run_id 自动成树;
  • metadata={...}tags=[...] 成为 UI 可筛选列;
  • LLM 调用的 token usage 从 AIMessage.usage_metadata 上报;
  • Agent 的多步执行(model → tool → model → ...)在一个 trace 里完整展开。

16.5 不要再用 model = ChatOpenAI(callbacks=[...])

v1.0 已全面改为 config 注入。模型直接带 handler 会在组合进 LCEL 时丢失继承,导致下游 runnable 看不到这些 handler。


17. 输出解析器

17.1 类层次

BaseOutputParser[T]                    → invoke 入口
└── BaseTransformOutputParser          → 支持流式
    └── BaseCumulativeTransformOutputParser  → 累加后 emit diff

17.2 关键实现

Parser输出特点
StrOutputParserstr直接取 AIMessage.content 的文本
JsonOutputParser(pydantic_object=None)dict宽容 JSON,流式下能 emit 部分 dict
PydanticOutputParser(pydantic_object, diff=False)Pydantic 实例diff=True 流式下吐 JSON-Patch
StructuredOutputParser.from_response_schemas([...])dict基于 ResponseSchema 描述,轻量但校验弱
XMLOutputParser(tags=[...])dictXML 结构化
PydanticToolsParser(tools=[M1, M2])list[Union[M1,M2]]AIMessage.tool_calls 抽取并 dispatch 到对应 Pydantic
JsonOutputToolsParser(first_tool_only=False)list[dict]同上但 dict 形式
OutputFixingParser.from_llm(parser, llm)T解析失败时让 LLM 自我修复
RetryOutputParser.from_llm(parser, llm)T类似但带原始 prompt 上下文

17.3 get_format_instructions 的地位

所有 parser 都提供 get_format_instructions() -> str,用于塞进 prompt:

parser = PydanticOutputParser(pydantic_object=MySchema)
prompt = ChatPromptTemplate.from_messages([
    ("system", "输出必须严格遵守格式。\n{format_instructions}"),
    ("human", "{query}"),
]).partial(format_instructions=parser.get_format_instructions())

chain = prompt | model | parser

但在 v1.0 时代,结构化输出优先走 model.with_structured_output(Schema),比 parser + 模板更稳定。只有 provider 不支持原生结构化时才回落到 parser。


18. Partner 包生态

18.1 langchain-openai

from langchain_openai import ChatOpenAI, AzureChatOpenAI, OpenAIEmbeddings

model = ChatOpenAI(
    model="gpt-5.2",
    temperature=0,
    streaming=True,
    use_responses_api=True,             # o-series / server-side tool 必开
    reasoning_effort="medium",
    service_tier="scale",
    logprobs=True,
    top_logprobs=5,
    include=["reasoning.summary"],      # 请求推理摘要回显
    strict=True,                        # structured output 严格 schema
)

emb = OpenAIEmbeddings(model="text-embedding-3-large", dimensions=1024)

18.2 langchain-anthropic

from langchain_anthropic import ChatAnthropic
from langchain_anthropic.middleware import AnthropicPromptCachingMiddleware

model = ChatAnthropic(
    model="claude-sonnet-4-6",
    max_tokens=8192,
    thinking={"type": "enabled", "budget_tokens": 10_000},   # extended thinking
)

agent = create_agent(
    model,
    tools=[...],
    middleware=[AnthropicPromptCachingMiddleware(ttl="5m")],
)

Anthropic 特殊工具(text_editor、memory_tool、file_search、bash_tool)以 middleware 形式接入,而不是手写 tool。

18.3 langchain-google-genai v4.0.0+

已迁移到合并后的 google-genai SDK,同时覆盖 Gemini Developer API 与 Vertex AI 的 Gemini,取代了部分 langchain-google-vertexai 功能:

from langchain_google_genai import ChatGoogleGenerativeAI, GoogleGenerativeAIEmbeddings

model = ChatGoogleGenerativeAI(model="gemini-2.5-pro", temperature=0)

18.4 langchain-aws(独立 repo)

from langchain_aws import ChatBedrockConverse   # 推荐走 Converse API
# from langchain_aws import ChatBedrock         # 旧接口
# from langchain_aws import BedrockLLM, BedrockEmbeddings

model = ChatBedrockConverse(
    model="anthropic.claude-sonnet-4-6",
    region_name="us-east-1",
)

另含 AmazonKendraRetriever、Neptune graph、DynamoDB 对话历史等。

18.5 Partner 包的公约

  • peer depend on langchain-core:升 core 不强制升 partner。
  • standard-tests:模型必须通过同一套 BaseChatModel 测试;否则 PR 无法合并。
  • 模型 ID 透传:新模型一出,provider SDK 能调就能用,不需要 LangChain 升级。

19. v0.x → v1.0 迁移要点

19.1 一眼看懂的变化清单

  1. langchain namespace 大瘦身:老 chain / 老 agent / 老 memory / MultiQueryRetriever / EnsembleRetriever 等都搬到 langchain-classic
  2. Agent 只剩 create_agentAgentExecutorcreate_tool_calling_agentcreate_openai_tools_agentcreate_openai_functions_agentcreate_react_agent(字符串解析)、create_json_agentcreate_structured_chat_agentinitialize_agent 全部 deprecate。
  3. langgraph.prebuilt.create_react_agent 也 deprecate,别名转发到 langchain.agents.create_agent
  4. 自定义 state 必须是 TypedDict:Pydantic / dataclass 被明确拒绝。
  5. Middleware 取代所有 hookpre_model_hook / post_model_hook / runtime_config 归一到 middleware。
  6. 标准化 content blocksAIMessage.content_blocks 属性统一多模态 / 推理 / 引用。
  7. 消息 API 收紧.text() → 属性;AIMessage.example 移除。
  8. 结构化输出:prompted JSON response_format 被移除;必须用 Auto/Provider/Tool Strategy 或 with_structured_output
  9. create_agent(tools=...) 拒绝 ToolNode:只接受 BaseTool | Callable | dict
  10. Memory 全部迁移到 LangGraph:Checkpointer + Store。
  11. 文档迁到 docs.langchain.com/oss/python/...

19.2 典型迁移 diff

# v0.x
from langchain.agents import AgentExecutor, create_tool_calling_agent
prompt = ChatPromptTemplate.from_messages([
    ("system","You are helpful."),
    ("placeholder","{chat_history}"),
    ("human","{input}"),
    ("placeholder","{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True, max_iterations=10)
executor.invoke({"input": "...", "chat_history": history})

# v1.0
from langchain.agents import create_agent
agent = create_agent(model, tools, prompt="You are helpful.")
agent.invoke(
    {"messages": history + [HumanMessage("...")]},
    config={"recursion_limit": 21},   # 大致对应 max_iterations=10
)

19.3 v1.1 要点

  • 新增 ModelRetryMiddleware
  • 修复 v1.1.0 的一个 regression:langchain.agents.__init__ 一度漏出 create_agent(patch 版已修)。
  • 稳定性与可观测性增强,无 breaking。

19.4 迁移心法

  • 先跑 classic 兼容pip install langchain-classic,加 from langchain_classic.memory import ConversationBufferMemory 这类 alias 让旧代码先活着。
  • 再拆 Agent:把 AgentExecutor 替换成 create_agent,同步把 hook 改写成 middleware。
  • 最后拆 state:如果用了 Pydantic / dataclass state,必须转成 TypedDict

20. LangChain 与 LangGraph 的关系与选型

20.1 层级关系

LangSmith                              ← 可观测(独立平台)
   ↑ trace
LangChain (create_agent / Middleware / Chains / Retrievers)
   ↓ compile
LangGraph (StateGraph / Pregel / Checkpointer / Interrupt)
   ↓ depends on
langchain-core (Runnable / BaseMessage / BaseTool / Callbacks)

20.2 选型决策树

优先用 LangChain create_agent

  • 标准 tool-calling agent
  • 单线性或轻度分支工作流
  • RAG 管道、chatbot、原型
  • 产品首月想快速上线

下沉到 LangGraph 原生

  • 复杂分支 / 循环 / 子图
  • 多 Agent 协作(supervisor / hierarchical / swarm)
  • 需要 time-travel、replay、interrupt
  • 长耗时持久化任务(数小时/数天)
  • 节点级流式、背压控制

混合使用(最常见):

  • 顶层用 LangGraph StateGraph
  • 每个节点内部是 create_agent 或 LCEL chain;
  • Checkpointer、Store、tools、messages 全局共享。

20.3 互操作要点

  • create_agent 的返回值 就是 一个 LangGraph CompiledStateGraph,可以 .invoke / .stream / .astream_events / .get_state / .update_state
  • Middleware 只对 LangChain 有效;在原生 LangGraph 里要靠节点 + 边实现等效行为。
  • 工具、消息、checkpointer、store、模型——两边共享同一套抽象,不存在"LangChain 版 tool"和"LangGraph 版 tool"。

20.4 一条经验法则

如果你不确定,从 LangChain 起步,等到遇到 create_agent 的 API 没法表达你的需求时,再切到 LangGraph 原生图。不要一开始就跳到 StateGraph


21. 生产最佳实践

21.1 可观测性

  • 永远开 LangSmithLANGSMITH_TRACING=true 是零成本的。
  • 关键 Runnable 加 with_config({"run_name": "xxx"}),trace 里可读性高一个数量级。
  • metadata={"user_id": ..., "session_id": ..., "env": ...}:这些字段在 LangSmith 里直接可筛可 group。

21.2 可靠性

  • 重试:关键 LLM 调用走 .with_retry(stop_after_attempt=3, wait_exponential_jitter=True),或用 ModelRetryMiddleware
  • 回退primary.with_fallbacks([fallback_model]) 处理 provider 断线。
  • 超时:在 provider client 层设置 timeout(OpenAI 的 timeout=...),LangChain 不做全局超时。
  • 幂等:对外部副作用工具用 tool_call.id 做去重;checkpointer 恢复时同一个 tool call 不会重跑(只要你的工具自身幂等)。

21.3 成本控制

  • UsageMetadataCallbackHandler 聚合 token usage。
  • 大 toolset 走 LLMToolSelectorMiddleware,用廉价模型预筛。
  • 缓存:OpenAI 的 prompt caching 对稳定前缀自动生效;Anthropic 用 AnthropicPromptCachingMiddleware
  • 结构化输出优先走 ProviderStrategy(native JSON mode),比强制 tool call 便宜

21.4 安全

  • PIIPIIMiddleware 做输入/输出/工具结果的掩码或硬阻断。
  • 工具访问控制:在 wrap_tool_call 中做权限校验;InjectedState 注入用户身份。
  • configurable="any"永不直接暴露给终端用户。
  • prompt injection:retriever 召回的文本应该过滤(或限制到工具使用场景下不执行指令)。

21.5 并发与吞吐

  • .abatch(inputs, max_concurrency=N) 是最直接的并发工具。
  • I/O 密集链路全走 async(ainvoke / astream / abatch)。
  • LangGraph 的多 Agent 并行默认走 Send,不是 asyncio——但底层仍然是 async。

21.6 版本管理

  • langchainlangchain-core、partner 包、langgraph 都 pin 到 minor(例如 langchain~=1.1langchain-core~=1.3)。
  • 关注 langchain/changelog 的 patch 公告;v1 承诺 "no breaking until 2.0",但 bug fix 随时可能改行为细节。

22. API 快速参考

22.1 Runnable 方法速查

方法返回用途
invoke(input, config)Output同步单次
ainvoke(input, config)Output异步单次
stream(input, config)Iterator[Output]同步流
astream(input, config)AsyncIterator[Output]异步流
batch(inputs, config)list[Output]同步批量
abatch(inputs, config)list[Output]异步批量
astream_events(input, config, version="v2")AsyncIterator[StreamEvent]高保真事件流
with_config(config)Runnable附加配置
with_retry(...)Runnable重试
with_fallbacks([...])Runnable回退
with_listeners(on_start, on_end, on_error)Runnable侦听
bind(**kwargs)RunnableBinding冻结参数
assign(**mappers)RunnableAssigndict 追加字段
get_graph(config)Graph画图

22.2 创建 Agent 速查

from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import (
    AgentMiddleware, before_model, after_model,
    wrap_model_call, wrap_tool_call,
    SummarizationMiddleware, ModelRetryMiddleware, PIIMiddleware,
    LLMToolSelectorMiddleware,
)
from langchain.agents.structured_output import (
    AutoStrategy, ProviderStrategy, ToolStrategy,
    StructuredOutputError, MultipleStructuredOutputsError,
    StructuredOutputValidationError,
)
from langchain.chat_models import init_chat_model

22.3 工具速查

from langchain_core.tools import (
    tool, BaseTool, StructuredTool, Tool,
    InjectedToolArg, InjectedToolCallId,
)
from langgraph.prebuilt import InjectedState, InjectedStore
from langchain.tools.retriever import create_retriever_tool

22.4 消息速查

from langchain_core.messages import (
    BaseMessage, HumanMessage, AIMessage, SystemMessage,
    ToolMessage, FunctionMessage, ChatMessage,
    AIMessageChunk, HumanMessageChunk,
    trim_messages,
)
# 标准 content blocks(v1)
# AIMessage.content_blocks: list[TextContentBlock | ReasoningContentBlock |
#   Citation | ToolCall | ServerToolCall | ImageContentBlock |
#   AudioContentBlock | VideoContentBlock | FileContentBlock |
#   NonStandardContentBlock]

22.5 关键源码路径

文件内容
libs/core/langchain_core/runnables/base.pyRunnable, RunnableSequence, RunnableParallel, RunnableBinding
libs/core/langchain_core/runnables/passthrough.pyRunnablePassthrough, RunnableAssign
libs/core/langchain_core/runnables/branch.pyRunnableBranch
libs/core/langchain_core/runnables/config.pyRunnableConfig、回调传播、contextvars
libs/core/langchain_core/language_models/base.pyBaseLanguageModel, LangSmithParams
libs/core/langchain_core/language_models/chat_models.pyBaseChatModel
libs/core/langchain_core/language_models/llms.pyBaseLLM, LLM
libs/core/langchain_core/messages/所有消息类
libs/core/langchain_core/messages/content_blocks.py标准 content blocks
libs/core/langchain_core/prompts/chat.pyChatPromptTemplate, MessagesPlaceholder
libs/core/langchain_core/output_parsers/pydantic.pyPydanticOutputParser
libs/core/langchain_core/tools/BaseTool, StructuredTool, @tool
libs/core/langchain_core/callbacks/base.pyBaseCallbackHandler
libs/core/langchain_core/callbacks/manager.pyCallbackManager, handle_event
libs/core/langchain_core/tracers/context.pyLangSmith tracer 注入
libs/langchain_v1/langchain/agents/create_agent, AgentState
libs/langchain_v1/langchain/agents/middleware/所有 Middleware
libs/langchain_v1/langchain/agents/structured_output/Auto/Provider/Tool Strategy
libs/langchain_v1/langchain/chat_models/base.pyinit_chat_model
libs/text-splitters/langchain_text_splitters/所有 splitter
libs/langchain/langchain-classic(legacy)
libs/partners/openai/, anthropic/, ...partner 实现

结语

LangChain v1.0 的核心收获:

  1. Runnable 协议是根——所有东西都是 Runnable,| 是协议的糖。
  2. create_agent 是面——别再手写 AgentExecutor,middleware 是新的扩展点。
  3. langchain-core 是骨——只要你在 core 的协议里编程,模型/provider/工具都可替换。
  4. LangGraph 是肌肉——真正的执行运行时在底层,需要时随时下沉。
  5. LangSmith 是神经——零成本接入,线上问题 90% 的答案都在 trace 里。

一句话总结:create_agent 解决 80% 的日常问题,用 LangGraph 解决剩下 20% 的复杂编排,用 LangChain Core 保证你永远不被 provider 绑死。