LangChain 核心架构深度解析:从设计哲学到工程实践

角色 :你是一名资深 AI 前端架构师,深度使用过 LangChain 构建生产级 LLM 应用。

任务 :基于 LangChain 官方源码(langchain-ai/langchain),撰写一篇面向中高级前端/全栈工程师的技术博客。

要求

  • 逻辑结构:总-分-总,先讲设计哲学,再逐层拆解核心模块,最后给出工程实践建议
  • 每个核心概念必须配有:是什么 → 为什么这样设计 → 怎么用(代码示例)
  • 重点覆盖:分层架构、Runnable/LCEL、提示模板、模型抽象、Agent 系统、RAG 存储链路、可观测性
  • 语言:中文,技术术语保留英文原词
  • 风格:严谨但不枯燥,有工程师视角的批判性思考

0. 设计哲学:一切皆 Runnable

LangChain 的核心设计理念是通过统一接口实现组合性(Composability) 。无论是 LLM、提示模板、工具还是检索器,所有组件都实现同一个抽象接口 Runnable,从而可以像乐高积木一样自由拼接。

这个设计解决了 LLM 应用开发中最痛的问题:不同模型、不同组件之间的胶水代码爆炸


1. 分层架构:四层清晰分离

复制代码
┌─────────────────────────────────────────────┐
│  Integration Layer  (partners/)             │  ← langchain-openai, anthropic, ollama...
├─────────────────────────────────────────────┤
│  Implementation Layer  (langchain_v1/)      │  ← create_agent, init_chat_model...
├─────────────────────────────────────────────┤
│  Foundation Layer  (langchain-core/)        │  ← Runnable, BaseChatModel, BaseTool...
├─────────────────────────────────────────────┤
│  Tooling Layer  (text-splitters, standard-tests) │
└─────────────────────────────────────────────┘
层级 包名 职责
基础抽象层 langchain-core 定义所有接口,零第三方依赖
实现层 langchainlangchain_v1 高层工具函数,集成 LangGraph
集成层 partners/ 各厂商具体实现
测试层 standard-tests 标准化集成测试套件

关键设计决策langchain-core 故意保持极轻量依赖(只有 pydanticlangsmithtenacity),任何厂商都可以基于它实现自己的集成包,而不引入不必要的依赖。 1 2


2. Runnable 接口与 LCEL:组合的基石

2.1 Runnable 抽象

Runnable[Input, Output] 是整个框架的核心抽象,定义在 libs/core/langchain_core/runnables/base.py

唯一必须实现的方法只有 invoke,其余方法均有默认实现:

方法 说明
invoke(input, config?) 同步调用,一进一出
ainvoke(input, config?) 异步版本(默认在线程池中运行 invoke
batch(inputs, config?) 批量调用,默认并发执行
stream(input, config?) 流式输出,逐 chunk 返回
astream_events(input, version?) 异步事件流,用于细粒度观测

2.2 LCEL:用 | 运算符构建 Pipeline

LangChain Expression Language(LCEL)是 LangChain 的声明式链式语法,核心就是重载了 | 运算符:

python 复制代码
# 三种等价写法
chain = prompt | model | parser          # LCEL 管道语法
chain = prompt.pipe(model, parser)       # .pipe() 方法
chain = RunnableSequence(prompt, model, parser)  # 显式构造

| 运算符背后创建的是 RunnableSequence,它是最重要的组合原语 ,自动支持 sync/async/batch/stream。 4

2.3 核心组合原语

原语 作用 典型用法
RunnableSequence 串行管道,前一步输出是后一步输入 `prompt
RunnableParallel 并行扇出,同一输入分发给多个 Runnable {"summary": chain1, "keywords": chain2}
RunnableLambda 将任意 Python 函数包装为 Runnable RunnableLambda(my_func)
RunnablePassthrough 透传输入,常用于在并行中保留原始数据 RunnablePassthrough.assign(context=retriever)
RunnableBranch 条件路由,根据谓词选择不同分支 多意图路由
RunnableWithFallbacks 主链失败时自动切换备用链 模型降级
RunnableWithMessageHistory 自动注入/保存对话历史 多轮对话

RAG 链的典型写法

python 复制代码
from langchain_core.runnables import RunnableParallel, RunnablePassthrough

rag_chain = (
    RunnableParallel(
        context=retriever,          # 并行:检索文档
        question=RunnablePassthrough()  # 并行:透传问题
    )
    | prompt      # 将 context + question 填入模板
    | model       # 调用 LLM
    | parser      # 解析输出
)

2.4 RunnableConfig:运行时控制

RunnableConfig 是贯穿整个调用链的配置对象,通过 ContextVar 自动向下传播,无需手动透传:

python 复制代码
chain.invoke(
    input,
    config={
        "tags": ["prod"],           # 用于 LangSmith 过滤
        "metadata": {"user": "u1"}, # 附加到 trace
        "callbacks": [my_handler],  # 自定义回调
        "max_concurrency": 5,       # 控制 batch 并发数
        "configurable": {"llm": "fast"},  # 运行时切换组件
    }
)

3. 提示模板:结构化 LLM 输入

3.1 ChatPromptTemplate

ChatPromptTemplate 是最常用的提示模板,支持多种消息格式:

python 复制代码
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个专业的 {role}。"),
    MessagesPlaceholder("history"),   # 动态注入历史消息列表
    ("human", "{question}"),
])

MessagesPlaceholder 是多轮对话的关键,它允许在模板中预留一个位置,运行时注入完整的消息列表。

3.2 Few-Shot 提示

FewShotChatMessagePromptTemplate 支持语义相似度动态选择示例,结合向量存储实现智能 few-shot:

python 复制代码
few_shot_prompt = FewShotChatMessagePromptTemplate(
    input_variables=["input"],
    example_selector=SemanticSimilarityExampleSelector(vectorstore=vs),
    example_prompt=human_template + ai_template,
)

4. 模型抽象:厂商无关的统一接口

4.1 两类模型基类

基类 输入/输出 适用场景
BaseChatModel List[Message]AIMessage 现代对话模型(GPT-4, Claude, Gemini)
BaseLLM strstr 传统补全模型

所有厂商集成(ChatOpenAIChatAnthropicChatOllama)都继承自 BaseChatModel,实现同一接口,切换模型只需换一行代码

4.2 init_chat_model:运行时动态选择模型

python 复制代码
from langchain.chat_models import init_chat_model

# 通过字符串指定 provider:model
model = init_chat_model("openai:gpt-4o")
model = init_chat_model("anthropic:claude-3-5-sonnet-20241022")

# 支持运行时通过 configurable 切换
model = init_chat_model(configurable_fields="any")
result = model.with_config(configurable={"model": "openai:gpt-4o"}).invoke("Hello")

5. Agent 系统:自主决策的执行引擎

5.1 现代 Agent 架构

现代 LangChain Agent 基于 LangGraph StateGraph 构建,通过 create_agent 工厂函数创建:

python 复制代码
from langchain.agents import create_agent
from langchain_core.tools import tool

@tool
def search_web(query: str) -> str:
    """搜索互联网获取最新信息。"""
    return f"搜索结果: {query}"

agent = create_agent(
    model="openai:gpt-4o",
    tools=[search_web],
    system_prompt="你是一个有用的助手。"
)

5.2 Agent 执行循环


#mermaid-svg-J6cLwiWrsKp1AJol{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-J6cLwiWrsKp1AJol .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-J6cLwiWrsKp1AJol .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-J6cLwiWrsKp1AJol .error-icon{fill:#552222;}#mermaid-svg-J6cLwiWrsKp1AJol .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-J6cLwiWrsKp1AJol .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-J6cLwiWrsKp1AJol .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-J6cLwiWrsKp1AJol .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-J6cLwiWrsKp1AJol .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-J6cLwiWrsKp1AJol .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-J6cLwiWrsKp1AJol .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-J6cLwiWrsKp1AJol .marker{fill:#333333;stroke:#333333;}#mermaid-svg-J6cLwiWrsKp1AJol .marker.cross{stroke:#333333;}#mermaid-svg-J6cLwiWrsKp1AJol svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-J6cLwiWrsKp1AJol p{margin:0;}#mermaid-svg-J6cLwiWrsKp1AJol .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-J6cLwiWrsKp1AJol .cluster-label text{fill:#333;}#mermaid-svg-J6cLwiWrsKp1AJol .cluster-label span{color:#333;}#mermaid-svg-J6cLwiWrsKp1AJol .cluster-label span p{background-color:transparent;}#mermaid-svg-J6cLwiWrsKp1AJol .label text,#mermaid-svg-J6cLwiWrsKp1AJol span{fill:#333;color:#333;}#mermaid-svg-J6cLwiWrsKp1AJol .node rect,#mermaid-svg-J6cLwiWrsKp1AJol .node circle,#mermaid-svg-J6cLwiWrsKp1AJol .node ellipse,#mermaid-svg-J6cLwiWrsKp1AJol .node polygon,#mermaid-svg-J6cLwiWrsKp1AJol .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-J6cLwiWrsKp1AJol .rough-node .label text,#mermaid-svg-J6cLwiWrsKp1AJol .node .label text,#mermaid-svg-J6cLwiWrsKp1AJol .image-shape .label,#mermaid-svg-J6cLwiWrsKp1AJol .icon-shape .label{text-anchor:middle;}#mermaid-svg-J6cLwiWrsKp1AJol .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-J6cLwiWrsKp1AJol .rough-node .label,#mermaid-svg-J6cLwiWrsKp1AJol .node .label,#mermaid-svg-J6cLwiWrsKp1AJol .image-shape .label,#mermaid-svg-J6cLwiWrsKp1AJol .icon-shape .label{text-align:center;}#mermaid-svg-J6cLwiWrsKp1AJol .node.clickable{cursor:pointer;}#mermaid-svg-J6cLwiWrsKp1AJol .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-J6cLwiWrsKp1AJol .arrowheadPath{fill:#333333;}#mermaid-svg-J6cLwiWrsKp1AJol .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-J6cLwiWrsKp1AJol .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-J6cLwiWrsKp1AJol .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J6cLwiWrsKp1AJol .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-J6cLwiWrsKp1AJol .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J6cLwiWrsKp1AJol .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-J6cLwiWrsKp1AJol .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-J6cLwiWrsKp1AJol .cluster text{fill:#333;}#mermaid-svg-J6cLwiWrsKp1AJol .cluster span{color:#333;}#mermaid-svg-J6cLwiWrsKp1AJol div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-J6cLwiWrsKp1AJol .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-J6cLwiWrsKp1AJol rect.text{fill:none;stroke-width:0;}#mermaid-svg-J6cLwiWrsKp1AJol .icon-shape,#mermaid-svg-J6cLwiWrsKp1AJol .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-J6cLwiWrsKp1AJol .icon-shape p,#mermaid-svg-J6cLwiWrsKp1AJol .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-J6cLwiWrsKp1AJol .icon-shape .label rect,#mermaid-svg-J6cLwiWrsKp1AJol .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-J6cLwiWrsKp1AJol .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-J6cLwiWrsKp1AJol .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-J6cLwiWrsKp1AJol :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

用户输入
before_agent 中间件
before_model 中间件
LLM 推理
after_model 中间件
有 tool_calls?
执行工具
返回最终答案

5.3 中间件架构(Middleware)

Agent 系统采用中间件模式,提供多个扩展钩子:

中间件 钩子 功能
SummarizationMiddleware before_model Token 超限时自动压缩历史
HumanInTheLoopMiddleware wrap_tool_call 工具调用前请求人工审批
ModelFallbackMiddleware wrap_model_call 主模型失败时自动降级
LLMToolSelectorMiddleware before_model 根据查询动态筛选工具集
ContextEditingMiddleware before_model 裁剪工具结果控制上下文窗口

6. RAG 存储链路:从文档到检索

6.1 核心数据流

复制代码
原始文档
  ↓ BaseLoader.lazy_load()
Document(page_content, metadata)
  ↓ TextSplitter.split_documents()
List[Document](分块)
  ↓ Embeddings.embed_documents()
向量
  ↓ VectorStore.add_documents()
持久化存储
  ↓ VectorStore.similarity_search(query)
相关文档 → 注入 Prompt → LLM

6.2 VectorStore 接口

VectorStore 基类提供三种核心检索方式:

方法 说明
similarity_search(query, k) 纯相似度检索,返回 top-k
max_marginal_relevance_search MMR 算法,在相关性和多样性间取平衡
as_retriever() 转换为 Runnable 检索器,可直接接入 LCEL 管道

6.3 Indexing API:防重复写入

index() 函数通过 RecordManager 追踪文档哈希,实现增量同步

python 复制代码
from langchain_core.indexing import index

result = index(
    docs_source,          # 文档来源(Loader 或 List[Document])
    record_manager,       # 记录管理器(追踪已索引文档)
    vector_store,         # 目标向量存储
    cleanup="incremental" # None | "incremental" | "full"
)

三种清理模式:None(不删除)、incremental(删除内容变更的旧版本)、full(删除本次未出现的所有文档)。


7. 可观测性:LangSmith 深度集成

LangSmith 在 langchain-core 层就已集成,所有 Runnable 的执行都会自动产生 trace。

astream_events API 是最细粒度的观测接口,每个组件的生命周期都会产生事件:

python 复制代码
async for event in chain.astream_events(input, version="v2"):
    # event["event"] 如: "on_chat_model_stream", "on_tool_end"
    # event["name"]  组件名称
    # event["data"]  包含 input/output/chunk
    if event["event"] == "on_chat_model_stream":
        print(event["data"]["chunk"].content, end="")
组件类型 产生的事件
BaseChatModel on_chat_model_start/stream/end
BaseTool on_tool_start/end/error
BaseRetriever on_retriever_start/end/error
RunnableSequence on_chain_start/stream/end

8. 工程实践建议

1. 优先使用 LCEL 而非手写循环

LCEL 管道自动获得 async、batch、streaming、tracing 支持,手写循环需要自己实现这些。

2. 用 configurable_alternatives 实现模型热切换

在 A/B 测试或多环境部署时,通过 RunnableConfig.configurable 在运行时切换模型,无需重新构建链。

3. Agent 优先选择 LangGraph

create_agent 底层已经是 LangGraph,对于复杂的多步骤、有状态、需要人工介入的场景,直接使用 LangGraph 的 StateGraph 获得更细粒度的控制。

4. RAG 必用 Indexing API

直接调用 vector_store.add_documents() 会导致重复写入,生产环境务必通过 index() 函数管理文档同步。

5. 用 with_fallbacks 提升生产稳定性

python 复制代码
primary_model.with_fallbacks(
    [backup_model],
    exceptions_to_handle=(RateLimitError,)
)

总结

LangChain 的核心价值在于三点:

  1. 统一抽象Runnable 接口让所有组件可互换、可组合
  2. 声明式组合 :LCEL 的 | 语法让复杂 Pipeline 的构建和维护成本极低
  3. 生产就绪:内置 streaming、async、retry、fallback、tracing,不需要从零实现

理解了 RunnableLCELAgent(LangGraph) 这条主线,就掌握了 LangChain 90% 的精髓。 18 19

Citations

File: libs/core/langchain_core/init.py (L1-9)

python 复制代码
"""`langchain-core` defines the base abstractions for the LangChain ecosystem.

The interfaces for core components like chat models, LLMs, vector stores, retrievers,
and more are defined here. The universal invocation protocol (Runnables) along with
a syntax for combining components are also defined here.

**No third-party integrations are defined here.** The dependencies are kept purposefully
very lightweight.
"""

File: AGENTS.md (L30-33)

markdown 复制代码
- **Core layer** (`langchain-core`): Base abstractions, interfaces, and protocols. Users should not need to know about this layer directly.
- **Implementation layer** (`langchain`): Concrete implementations and high-level public utilities
- **Integration layer** (`partners/`): Third-party service integrations. Note that this monorepo is not exhaustive of all LangChain integrations; some are maintained in separate repos, such as `langchain-ai/langchain-google` and `langchain-ai/langchain-aws`. Usually these repos are cloned at the same level as this monorepo, so if needed, you can refer to their code directly by navigating to `../langchain-google/` from this monorepo.
- **Testing layer** (`standard-tests/`): Standardized integration tests for partner integrations

File: libs/core/langchain_core/runnables/base.py (L618-707)

python 复制代码
    def __or__(
        self,
        other: Runnable[Any, Other]
        | Callable[[Iterator[Any]], Iterator[Other]]
        | Callable[[AsyncIterator[Any]], AsyncIterator[Other]]
        | Callable[[Any], Other]
        | Mapping[str, Runnable[Any, Other] | Callable[[Any], Other] | Any],
    ) -> RunnableSerializable[Input, Other]:
        """Runnable "or" operator.

        Compose this `Runnable` with another object to create a
        `RunnableSequence`.

        Args:
            other: Another `Runnable` or a `Runnable`-like object.

        Returns:
            A new `Runnable`.
        """
        return RunnableSequence(self, coerce_to_runnable(other))

    def __ror__(
        self,
        other: Runnable[Other, Any]
        | Callable[[Iterator[Other]], Iterator[Any]]
        | Callable[[AsyncIterator[Other]], AsyncIterator[Any]]
        | Callable[[Other], Any]
        | Mapping[str, Runnable[Other, Any] | Callable[[Other], Any] | Any],
    ) -> RunnableSerializable[Other, Output]:
        """Runnable "reverse-or" operator.

        Compose this `Runnable` with another object to create a
        `RunnableSequence`.

        Args:
            other: Another `Runnable` or a `Runnable`-like object.

        Returns:
            A new `Runnable`.
        """
        return RunnableSequence(coerce_to_runnable(other), self)

    def pipe(
        self,
        *others: Runnable[Any, Other] | Callable[[Any], Other],
        name: str | None = None,
    ) -> RunnableSerializable[Input, Other]:
        """Pipe `Runnable` objects.

        Compose this `Runnable` with `Runnable`-like objects to make a
        `RunnableSequence`.

        Equivalent to `RunnableSequence(self, *others)` or `self | others[0] | ...`

        Example:
            ```python
            from langchain_core.runnables import RunnableLambda


            def add_one(x: int) -> int:
                return x + 1


            def mul_two(x: int) -> int:
                return x * 2


            runnable_1 = RunnableLambda(add_one)
            runnable_2 = RunnableLambda(mul_two)
            sequence = runnable_1.pipe(runnable_2)
            # Or equivalently:
            # sequence = runnable_1 | runnable_2
            # sequence = RunnableSequence(first=runnable_1, last=runnable_2)
            sequence.invoke(1)
            await sequence.ainvoke(1)
            # -> 4

            sequence.batch([1, 2, 3])
            await sequence.abatch([1, 2, 3])
            # -> [4, 6, 8]
            ```

        Args:
            *others: Other `Runnable` or `Runnable`-like objects to compose
            name: An optional name for the resulting `RunnableSequence`.

        Returns:
            A new `Runnable`.
        """

File: libs/core/langchain_core/runnables/base.py (L2995-3017)

python 复制代码
class RunnableSequence(RunnableSerializable[Input, Output]):
    """Sequence of `Runnable` objects, where the output of one is the input of the next.

    **`RunnableSequence`** is the most important composition operator in LangChain
    as it is used in virtually every chain.

    A `RunnableSequence` can be instantiated directly or more commonly by using the
    `|` operator where either the left or right operands (or both) must be a
    `Runnable`.

    Any `RunnableSequence` automatically supports sync, async, batch.

    The default implementations of `batch` and `abatch` utilize threadpools and
    asyncio gather and will be faster than naive invocation of `invoke` or `ainvoke`
    for IO bound `Runnable`s.

    Batching is implemented by invoking the batch method on each component of the
    `RunnableSequence` in order.

    A `RunnableSequence` preserves the streaming properties of its components, so if
    all components of the sequence implement a `transform` method -- which
    is the method that implements the logic to map a streaming input to a streaming
    output -- then the sequence will be able to stream input to output!

File: libs/core/langchain_core/prompts/chat.py (L53-105)

python 复制代码
class MessagesPlaceholder(BaseMessagePromptTemplate):
    """Prompt template that assumes variable is already list of messages.

    A placeholder which can be used to pass in a list of messages.

    !!! example "Direct usage"

        ```python
        from langchain_core.prompts import MessagesPlaceholder

        prompt = MessagesPlaceholder("history")
        prompt.format_messages()  # raises KeyError

        prompt = MessagesPlaceholder("history", optional=True)
        prompt.format_messages()  # returns empty list []

        prompt.format_messages(
            history=[
                ("system", "You are an AI assistant."),
                ("human", "Hello!"),
            ]
        )
        # -> [
        #     SystemMessage(content="You are an AI assistant."),
        #     HumanMessage(content="Hello!"),
        # ]
        ```

    !!! example "Building a prompt with chat history"

        ```python
        from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

        prompt = ChatPromptTemplate.from_messages(
            [
                ("system", "You are a helpful assistant."),
                MessagesPlaceholder("history"),
                ("human", "{question}"),
            ]
        )
        prompt.invoke(
            {
                "history": [("human", "what's 5 + 2"), ("ai", "5 + 2 is 7")],
                "question": "now multiply that by 4",
            }
        )
        # -> ChatPromptValue(messages=[
        #     SystemMessage(content="You are a helpful assistant."),
        #     HumanMessage(content="what's 5 + 2"),
        #     AIMessage(content="5 + 2 is 7"),
        #     HumanMessage(content="now multiply that by 4"),
        # ])
        ```

File: libs/core/langchain_core/prompts/chat.py (L790-824)

python 复制代码
class ChatPromptTemplate(BaseChatPromptTemplate):
    """Prompt template for chat models.

    Use to create flexible templated prompts for chat models.

    !!! example

        ```python
        from langchain_core.prompts import ChatPromptTemplate

        template = ChatPromptTemplate(
            [
                ("system", "You are a helpful AI bot. Your name is {name}."),
                ("human", "Hello, how are you doing?"),
                ("ai", "I'm doing well, thanks!"),
                ("human", "{user_input}"),
            ]
        )

        prompt_value = template.invoke(
            {
                "name": "Bob",
                "user_input": "What is your name?",
            }
        )
        # Output:
        # ChatPromptValue(
        #    messages=[
        #        SystemMessage(content='You are a helpful AI bot. Your name is Bob.'),
        #        HumanMessage(content='Hello, how are you doing?'),
        #        AIMessage(content="I'm doing well, thanks!"),
        #        HumanMessage(content='What is your name?')
        #    ]
        # )
        ```

File: libs/core/langchain_core/prompts/few_shot.py (L325-376)

python 复制代码
        ```

        Prompt template with dynamically selected examples:

        ```python
        from langchain_core.prompts import SemanticSimilarityExampleSelector
        from langchain_core.embeddings import OpenAIEmbeddings
        from langchain_core.vectorstores import Chroma

        examples = [
            {"input": "2+2", "output": "4"},
            {"input": "2+3", "output": "5"},
            {"input": "2+4", "output": "6"},
            # ...
        ]

        to_vectorize = [" ".join(example.values()) for example in examples]
        embeddings = OpenAIEmbeddings()
        vectorstore = Chroma.from_texts(to_vectorize, embeddings, metadatas=examples)
        example_selector = SemanticSimilarityExampleSelector(vectorstore=vectorstore)

        from langchain_core import SystemMessage
        from langchain_core.prompts import HumanMessagePromptTemplate
        from langchain_core.prompts.few_shot import FewShotChatMessagePromptTemplate

        few_shot_prompt = FewShotChatMessagePromptTemplate(
            # Which variable(s) will be passed to the example selector.
            input_variables=["input"],
            example_selector=example_selector,
            # Define how each example will be formatted.
            # In this case, each example will become 2 messages:
            # 1 human, and 1 AI
            example_prompt=(
                HumanMessagePromptTemplate.from_template("{input}")
                + AIMessagePromptTemplate.from_template("{output}")
            ),
        )
        # Define the overall prompt.
        final_prompt = (
            SystemMessagePromptTemplate.from_template("You are a helpful AI Assistant")
            + few_shot_prompt
            + HumanMessagePromptTemplate.from_template("{input}")
        )
        # Show the prompt
        print(final_prompt.format_messages(input="What's 3+3?"))  # noqa: T201

        # Use within an LLM
        from langchain_core.chat_models import ChatAnthropic

        chain = final_prompt | ChatAnthropic(model="claude-3-haiku-20240307")
        chain.invoke({"input": "What's 3+3?"})
        ```

File: libs/langchain_v1/tests/integration_tests/chat_models/test_base.py (L22-35)

python 复制代码
async def test_init_chat_model_chain() -> None:
    model = init_chat_model("gpt-4o", configurable_fields="any", config_prefix="bar")
    model_with_tools = model.bind_tools([Multiply])

    model_with_config = model_with_tools.with_config(
        RunnableConfig(tags=["foo"]),
        configurable={"bar_model": "claude-sonnet-4-5-20250929"},
    )
    prompt = ChatPromptTemplate.from_messages([("system", "foo"), ("human", "{input}")])
    chain = prompt | model_with_config
    output = chain.invoke({"input": "bar"})
    assert isinstance(output, AIMessage)
    events = [event async for event in chain.astream_events({"input": "bar"}, version="v2")]
    assert events

File: libs/langchain_v1/langchain/agents/init.py (L1-9)

python 复制代码
"""Entrypoint to building [Agents](https://docs.langchain.com/oss/python/langchain/agents) with LangChain."""  # noqa: E501

from langchain.agents.factory import create_agent
from langchain.agents.middleware.types import AgentState

__all__ = [
    "AgentState",
    "create_agent",
]

File: libs/langchain_v1/langchain/agents/factory.py (L543-688)

python 复制代码
        ):
            return True

    return (
        any(part in model_name.lower() for part in FALLBACK_MODELS_WITH_STRUCTURED_OUTPUT)
        if model_name
        else False
    )


def _handle_structured_output_error(
    exception: Exception,
    response_format: ResponseFormat[Any],
) -> tuple[bool, str]:
    """Handle structured output error.

    Returns `(should_retry, retry_tool_message)`.
    """
    if not isinstance(response_format, ToolStrategy):
        return False, ""

    handle_errors = response_format.handle_errors

    if handle_errors is False:
        return False, ""
    if handle_errors is True:
        return True, STRUCTURED_OUTPUT_ERROR_TEMPLATE.format(error=str(exception))
    if isinstance(handle_errors, str):
        return True, handle_errors
    if isinstance(handle_errors, type):
        if issubclass(handle_errors, Exception) and isinstance(exception, handle_errors):
            return True, STRUCTURED_OUTPUT_ERROR_TEMPLATE.format(error=str(exception))
        return False, ""
    if isinstance(handle_errors, tuple):
        if any(isinstance(exception, exc_type) for exc_type in handle_errors):
            return True, STRUCTURED_OUTPUT_ERROR_TEMPLATE.format(error=str(exception))
        return False, ""
    return True, handle_errors(exception)


def _chain_tool_call_wrappers(
    wrappers: Sequence[ToolCallWrapper],
) -> ToolCallWrapper | None:
    """Compose wrappers into middleware stack (first = outermost).

    Args:
        wrappers: Wrappers in middleware order.

    Returns:
        Composed wrapper, or `None` if empty.

    Example:
        ```python
        wrapper = _chain_tool_call_wrappers([auth, cache, retry])
        # Request flows: auth -> cache -> retry -> tool
        # Response flows: tool -> retry -> cache -> auth
        ```
    """
    if not wrappers:
        return None

    if len(wrappers) == 1:
        return wrappers[0]

    def compose_two(outer: ToolCallWrapper, inner: ToolCallWrapper) -> ToolCallWrapper:
        """Compose two wrappers where outer wraps inner."""

        def composed(
            request: ToolCallRequest,
            execute: Callable[[ToolCallRequest], ToolMessage | Command[Any]],
        ) -> ToolMessage | Command[Any]:
            # Create a callable that invokes inner with the original execute
            def call_inner(req: ToolCallRequest) -> ToolMessage | Command[Any]:
                return inner(req, execute)

            # Outer can call call_inner multiple times
            return outer(request, call_inner)

        return composed

    # Chain all wrappers: first -> second -> ... -> last
    result = wrappers[-1]
    for wrapper in reversed(wrappers[:-1]):
        result = compose_two(wrapper, result)

    return result


def _chain_async_tool_call_wrappers(
    wrappers: Sequence[
        Callable[
            [ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
            Awaitable[ToolMessage | Command[Any]],
        ]
    ],
) -> (
    Callable[
        [ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
        Awaitable[ToolMessage | Command[Any]],
    ]
    | None
):
    """Compose async wrappers into middleware stack (first = outermost).

    Args:
        wrappers: Async wrappers in middleware order.

    Returns:
        Composed async wrapper, or `None` if empty.
    """
    if not wrappers:
        return None

    if len(wrappers) == 1:
        return wrappers[0]

    def compose_two(
        outer: Callable[
            [ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
            Awaitable[ToolMessage | Command[Any]],
        ],
        inner: Callable[
            [ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
            Awaitable[ToolMessage | Command[Any]],
        ],
    ) -> Callable[
        [ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
        Awaitable[ToolMessage | Command[Any]],
    ]:
        """Compose two async wrappers where outer wraps inner."""

        async def composed(
            request: ToolCallRequest,
            execute: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]],
        ) -> ToolMessage | Command[Any]:
            # Create an async callable that invokes inner with the original execute
            async def call_inner(req: ToolCallRequest) -> ToolMessage | Command[Any]:
                return await inner(req, execute)

            # Outer can call call_inner multiple times
            return await outer(request, call_inner)

        return composed

    # Chain all wrappers: first -> second -> ... -> last
    result = wrappers[-1]

File: libs/core/langchain_core/vectorstores/base.py (L43-45)

python 复制代码
class VectorStore(ABC):
    """Interface for vector store."""

File: libs/core/langchain_core/indexing/api.py (L175-205)

python 复制代码
def _get_document_with_hash(
    document: Document,
    *,
    key_encoder: Callable[[Document], str]
    | Literal["sha1", "sha256", "sha512", "blake2b"],
) -> Document:
    """Calculate a hash of the document, and assign it to the uid.

    When using one of the predefined hashing algorithms, the hash is calculated
    by hashing the content and the metadata of the document.

    Args:
        document: Document to hash.
        key_encoder: Hashing algorithm to use for hashing the document.
            If not provided, a default encoder using SHA-1 will be used.
            SHA-1 is not collision-resistant, and a motivated attacker
            could craft two different texts that hash to the
            same cache key.

            New applications should use one of the alternative encoders
            or provide a custom and strong key encoder function to avoid this risk.

            When changing the key encoder, you must change the
            index as well to avoid duplicated documents in the cache.

    Raises:
        ValueError: If the metadata cannot be serialized using json.

    Returns:
        Document with a unique identifier based on the hash of the content and metadata.
    """

File: libs/core/langchain_core/indexing/api.py (L400-450)

python 复制代码
            "delete" and "add_documents" required methods.
        ValueError: If source_id_key is not None, but is not a string or callable.
        TypeError: If `vectorstore` is not a `VectorStore` or a DocumentIndex.
        AssertionError: If `source_id` is None when cleanup mode is incremental.
            (should be unreachable code).
    """
    # Behavior is deprecated, but we keep it for backwards compatibility.
    # # Warn only once per process.
    if key_encoder == "sha1":
        _warn_about_sha1()

    if cleanup not in {"incremental", "full", "scoped_full", None}:
        msg = (
            f"cleanup should be one of 'incremental', 'full', 'scoped_full' or None. "
            f"Got {cleanup}."
        )
        raise ValueError(msg)

    if (cleanup in {"incremental", "scoped_full"}) and source_id_key is None:
        msg = (
            "Source id key is required when cleanup mode is incremental or scoped_full."
        )
        raise ValueError(msg)

    destination = vector_store  # Renaming internally for clarity

    # If it's a vectorstore, let's check if it has the required methods.
    if isinstance(destination, VectorStore):
        # Check that the Vectorstore has required methods implemented
        methods = ["delete", "add_documents"]

        for method in methods:
            if not hasattr(destination, method):
                msg = (
                    f"Vectorstore {destination} does not have required method {method}"
                )
                raise ValueError(msg)

        if type(destination).delete == VectorStore.delete:
            # Checking if the VectorStore has overridden the default delete method
            # implementation which just raises a NotImplementedError
            msg = "Vectorstore has not implemented the delete method"
            raise ValueError(msg)
    elif isinstance(destination, DocumentIndex):
        pass
    else:
        msg = (
            f"Vectorstore should be either a VectorStore or a DocumentIndex. "
            f"Got {type(destination)}."
        )
        raise TypeError(msg)

File: libs/core/langchain_core/tracers/event_stream.py (L101-200)

python 复制代码
class _AstreamEventsCallbackHandler(AsyncCallbackHandler, _StreamingCallbackHandler):
    """An implementation of an async callback handler for astream events."""

    def __init__(
        self,
        *args: Any,
        include_names: Sequence[str] | None = None,
        include_types: Sequence[str] | None = None,
        include_tags: Sequence[str] | None = None,
        exclude_names: Sequence[str] | None = None,
        exclude_types: Sequence[str] | None = None,
        exclude_tags: Sequence[str] | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize the tracer."""
        super().__init__(*args, **kwargs)
        # Map of run ID to run info.
        # the entry corresponding to a given run id is cleaned
        # up when each corresponding run ends.
        self.run_map: dict[UUID, RunInfo] = {}
        # The callback event that corresponds to the end of a parent run
        # may be invoked BEFORE the callback event that corresponds to the end
        # of a child run, which results in clean up of run_map.
        # So we keep track of the mapping between children and parent run IDs
        # in a separate container. This container is GCed when the tracer is GCed.
        self.parent_map: dict[UUID, UUID | None] = {}

        self.is_tapped: dict[UUID, Any] = {}

        # Filter which events will be sent over the queue.
        self.root_event_filter = _RootEventFilter(
            include_names=include_names,
            include_types=include_types,
            include_tags=include_tags,
            exclude_names=exclude_names,
            exclude_types=exclude_types,
            exclude_tags=exclude_tags,
        )

        try:
            loop = asyncio.get_event_loop()
        except RuntimeError:
            loop = asyncio.new_event_loop()
        memory_stream = _MemoryStream[StreamEvent](loop)
        self.send_stream = memory_stream.get_send_stream()
        self.receive_stream = memory_stream.get_receive_stream()

    def _get_parent_ids(self, run_id: UUID) -> list[str]:
        """Get the parent IDs of a run (non-recursively) cast to strings."""
        parent_ids = []

        while parent_id := self.parent_map.get(run_id):
            str_parent_id = str(parent_id)
            if str_parent_id in parent_ids:
                msg = (
                    f"Parent ID {parent_id} is already in the parent_ids list. "
                    f"This should never happen."
                )
                raise AssertionError(msg)
            parent_ids.append(str_parent_id)
            run_id = parent_id

        # Return the parent IDs in reverse order, so that the first
        # parent ID is the root and the last ID is the immediate parent.
        return parent_ids[::-1]

    def _send(self, event: StreamEvent, event_type: str) -> None:
        """Send an event to the stream."""
        if self.root_event_filter.include_event(event, event_type):
            self.send_stream.send_nowait(event)

    def __aiter__(self) -> AsyncIterator[Any]:
        """Iterate over the receive stream.

        Returns:
            An async iterator over the receive stream.
        """
        return self.receive_stream.__aiter__()

    async def tap_output_aiter(
        self, run_id: UUID, output: AsyncIterator[T]
    ) -> AsyncIterator[T]:
        """Tap the output aiter.

        This method is used to tap the output of a `Runnable` that produces an async
        iterator. It is used to generate stream events for the output of the `Runnable`.

        Args:
            run_id: The ID of the run.
            output: The output of the `Runnable`.

        Yields:
            The output of the `Runnable`.
        """
        sentinel = object()
        # atomic check and set
        tap = self.is_tapped.setdefault(run_id, sentinel)
        # wait for first chunk
        first = await anext(output, sentinel)
        if first is sentinel:

File: libs/core/langchain_core/runnables/fallbacks.py (L36-108)

python 复制代码
class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
    """`Runnable` that can fallback to other `Runnable` objects if it fails.

    External APIs (e.g., APIs for a language model) may at times experience
    degraded performance or even downtime.

    In these cases, it can be useful to have a fallback `Runnable` that can be
    used in place of the original `Runnable` (e.g., fallback to another LLM provider).

    Fallbacks can be defined at the level of a single `Runnable`, or at the level
    of a chain of `Runnable`s. Fallbacks are tried in order until one succeeds or
    all fail.

    While you can instantiate a `RunnableWithFallbacks` directly, it is usually
    more convenient to use the `with_fallbacks` method on a `Runnable`.

    Example:
        ```python
        from langchain_core.chat_models.openai import ChatOpenAI
        from langchain_core.chat_models.anthropic import ChatAnthropic

        model = ChatAnthropic(model="claude-sonnet-4-6").with_fallbacks(
            [ChatOpenAI(model="gpt-5.4-mini")]
        )
        # Will usually use ChatAnthropic, but fallback to ChatOpenAI
        # if ChatAnthropic fails.
        model.invoke("hello")

        # And you can also use fallbacks at the level of a chain.
        # Here if both LLM providers fail, we'll fallback to a good hardcoded
        # response.

        from langchain_core.prompts import PromptTemplate
        from langchain_core.output_parser import StrOutputParser
        from langchain_core.runnables import RunnableLambda


        def when_all_is_lost(inputs):
            return (
                "Looks like our LLM providers are down. "
                "Here's a nice 🦜️ emoji for you instead."
            )


        chain_with_fallback = (
            PromptTemplate.from_template("Tell me a joke about {topic}")
            | model
            | StrOutputParser()
        ).with_fallbacks([RunnableLambda(when_all_is_lost)])
        ```
    """

    runnable: Runnable[Input, Output]
    """The `Runnable` to run first."""
    fallbacks: Sequence[Runnable[Input, Output]]
    """A sequence of fallbacks to try."""
    exceptions_to_handle: tuple[type[BaseException], ...] = (Exception,)
    """The exceptions on which fallbacks should be tried.

    Any exception that is not a subclass of these exceptions will be raised immediately.
    """
    exception_key: str | None = None
    """If `string` is specified then handled exceptions will be passed to fallbacks as
    part of the input under the specified key.

    If `None`, exceptions will not be passed to fallbacks.

    If used, the base `Runnable` and its fallbacks must accept a dictionary as input.
    """

    model_config = ConfigDict(
        arbitrary_types_allowed=True,
    )

File: libs/core/README.md (L29-34)

markdown 复制代码
The LangChain ecosystem is built on top of `langchain-core`. Some of the benefits:

- **Modularity**: We've designed Core around abstractions that are independent of each other, and not tied to any specific model provider.
- **Stability**: We are committed to a stable versioning scheme, and will communicate any breaking changes with advance notice and version bumps.
- **Battle-tested**: Core components have the largest install base in the LLM ecosystem, and are used in production by many companies.

File: README.md (L63-71)

markdown 复制代码
LangChain helps developers build applications powered by LLMs through a standard interface for models, embeddings, vector stores, and more.

- **Real-time data augmentation** --- Easily connect LLMs to diverse data sources and external/internal systems, drawing from LangChain's vast library of integrations with model providers, tools, vector stores, retrievers, and more
- **Model interoperability** --- Swap models in and out as your engineering team experiments to find the best choice for your application's needs. As the industry frontier evolves, adapt quickly --- LangChain's abstractions keep you moving without losing momentum
- **Rapid prototyping** --- Quickly build and iterate on LLM applications with LangChain's modular, component-based architecture. Test different approaches and workflows without rebuilding from scratch, accelerating your development cycle
- **Production-ready features** --- Deploy reliable applications with built-in support for monitoring, evaluation, and debugging through integrations like LangSmith. Scale with confidence using battle-tested patterns and best practices
- **Vibrant community and ecosystem** --- Leverage a rich ecosystem of integrations, templates, and community-contributed components. Benefit from continuous improvements and stay up-to-date with the latest AI developments through an active open-source community
- **Flexible abstraction layers** --- Work at the level of abstraction that suits your needs --- from high-level chains for quick starts to low-level components for fine-grained control. LangChain grows with your application's complexity
相关推荐
满怀冰雪2 小时前
01_LangChain是什么_带你理解LLM应用框架
python·langchain
国科安芯2 小时前
基于AS32S601ZIT2型抗辐照MCU的商业航天卫星姿态确定与控制系统研究
单片机·嵌入式硬件·安全·fpga开发·架构·risc-v
wanghowie2 小时前
35. 从AI客服到AI运营助手:Workflow、Single Agent、Multi-Agent、Agent Native 的架构选型实践
大数据·人工智能·架构
doiito2 小时前
【Agent Harness实战】认清现实吧,LLM就是个“超级赌场”,而我们需要的是一套“紧箍咒”
架构·rust
沪漂阿龙2 小时前
RAG 是什么?为什么大模型需要外挂知识库?
人工智能·架构·langchain
Benszen2 小时前
eNSP搭建企业网毕设(详细配置命令)
架构
Tina学编程2 小时前
LangChain P2 | 给LangChain装上手脚[MacOS]
langchain
vortex52 小时前
Alpine Linux 运行架构解析:从内核到容器的精简之道
linux·运维·架构