LangChain Agents 核心机制与架构解析

Loading...

Agents - Docs by LangChain

一句话总结

LangChain 体系中,智能体(Agent)是由大语言模型(Model)与运行容器(Harness)组合而成的可配置执行循环;它在状态机中持续决策并调用工具,结合中间件实现容错、脱敏、上下文压缩与人工审批,是构建具备自主行动、复杂推理和长任务处理能力的 AI 应用核心基石。


核心概念与常用 API 解析

Agent 的核心不再是单次问答,而是一个受控的执行循环。模型读取当前 State,决定是否调用工具;运行时执行工具并把结果写回 messages;模型继续基于新上下文推理,直到不再请求工具并生成最终回答。

补充理解:State 是什么? State 可以理解为 Agent 在 LangGraph 中流转的“运行状态快照”,至少包含 messages。用户输入、模型的工具调用决策、工具返回结果、最终回答都会作为状态更新写回 State,从而支撑多步推理、工具调用、流式输出和 checkpoint 恢复。

Agent = Model + Harness

官方文档用 Agent = Model + Harness 描述架构边界:

  • Model:推理核心,负责工具选择、工具结果解释、任务终止判断和最终响应生成。
  • Harness:围绕模型循环的一整套运行容器,包括 system_prompttoolsmiddlewarecheckpointercontext_schemastore 等。其中 checkpointer 负责保存和恢复会话线程中的 Statecontext_schema 定义单次运行时 context 的结构,store 负责跨会话的长期记忆存储。

这意味着生产级 Agent 的质量不只取决于模型本身,还取决于上下文工程、工具 schema(工具入参结构定义)、状态恢复、异常处理、安全护栏和观测能力。

create_agent():Agent 的统一构建入口

create_agent() 是 LangChain 构建 Agent 的核心工厂方法。它将 modeltoolssystem_promptresponse_formatmiddleware 等组件组装成一个可执行的图结构,并支持 invoke()stream_events()checkpointinterrupt 和多 Agent 嵌套。

其中 stream_events() 用于实时观察 Agent 的消息生成、工具调用和状态变化;interrupt 用于在高风险动作前暂停 Agent,保存当前 State,等待人工审批或外部决策后再恢复执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from langchain.agents import create_agent
from langchain.tools import tool

@tool
def search(query: str) -> str:
    """根据查询词搜索信息。"""
    return f"查询结果:{query}"

agent = create_agent(
    model="openai:gpt-5.5",
    tools=[search],
    system_prompt="你是一个简洁、准确的 AI 助手。",
)

model=:模型选择与 provider 绑定

model= 可以传入 "provider:model" 形式的字符串,也可以传入已经初始化好的模型实例。文档中出现的模型名称包括:

  • google_genai:gemini-3.5-flash
  • openai:gpt-5.5
  • anthropic:claude-sonnet-4-6
  • openrouter:z-ai/glm-5.2
  • fireworks:accounts/fireworks/models/kimi-k2p7-code
  • baseten:zai-org/GLM-5.2
  • ollama:north-mini-code-1.0

模型能力会影响 tool callingstructured output、上下文窗口、多模态输入、推理稳定性和延迟成本。生产选型时不能只看生成质量,还要验证目标模型是否稳定支持工具调用、结构化输出和长上下文推理

tools=@tool:Agent 的行动接口

tools 是模型可调用的动作集合,可以是普通 Python callable(可调用的 Python 对象)、LangChain tooltool dict(工具定义字典)。工具的函数名、参数类型和 docstring 会被转换成模型可理解的 schema(结构定义),因此工具描述会直接影响模型是否能正确选择工具并生成参数。

1
2
3
4
5
6
from langchain.tools import tool

@tool
def get_weather(city: str) -> str:
    """获取指定城市的天气信息。"""
    return f"{city} 当前天气晴朗。"

工程上应让工具名语义明确、参数强类型、docstring 描述清楚输入输出边界。不要把鉴权信息、租户信息等敏感业务上下文暴露为模型可自由生成的工具参数,应通过 context 或服务端逻辑注入。

system_prompt=:行为策略与任务边界

system_prompt= 用于定义 Agent 的行为基调、回答风格、任务边界和安全规则。它可以传入字符串,也可以传入 SystemMessage。如果提示词依赖用户身份、租户权限、功能开关或运行时状态,更适合通过 middlewareruntime.context 动态注入,而不是拼接大量脆弱的字符串。

SystemMessage(系统消息对象):LangChain 中表示系统级指令的消息对象,用于承载 Agent 的行为规则、回答风格、任务边界和安全约束。

response_format=:结构化输出

response_format= 用于让 Agent 返回经过 schema 校验的结构化结果。常见写法是传入 Pydantic BaseModel

Pydantic BaseModel:Pydantic 提供的数据模型基类,用于声明结构化数据的字段和类型,并在运行时进行校验。在 response_format= 中传入 BaseModel 子类,可以让 Agent 返回符合该结构定义的 structured_response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from pydantic import BaseModel
from langchain.agents import create_agent

class Answer(BaseModel):
    summary: str
    confidence: float

agent = create_agent(
    model="openai:gpt-5.5",
    tools=[],
    response_format=Answer,
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "请总结 AI Agent 的发展趋势。"}]}
)

structured = result["structured_response"]

当直接传入 schema 类型时,LangChain 会根据模型能力自动选择策略:

  • ProviderStrategy(原生结构化输出策略):使用模型厂商原生结构化输出,通常更稳定。
  • ToolStrategy(工具调用结构化输出策略):通过工具调用协议模拟结构化输出,兼容面更广。

最终结构化结果从 result["structured_response"] 中读取。上线前必须验证目标模型是否能同时稳定处理业务工具调用和最终结构化输出

invoke()thread_idcheckpointer

invoke() 是一次完整 Agent run 的入口。输入通常是:

1
{"messages": [{"role": "user", "content": "请总结 AI Agent 工程化能力。"}]}

如果需要多轮会话,需要在 config 中传入 thread_id

1
config = {"configurable": {"thread_id": "session-001"}}

thread_id 只是会话作用域标识,真正保存状态的是 checkpointer。本地示例可以使用 InMemorySaver()生产环境应替换为持久化 checkpoint

1
2
3
4
5
6
7
from langgraph.checkpoint.memory import InMemorySaver

agent = create_agent(
    model="openai:gpt-5.5",
    tools=[],
    checkpointer=InMemorySaver(),
)

context_schemacontextruntime.context

context 用于传递单次运行的业务上下文,例如 user_id、租户、权限、功能开关、请求来源等。通过 context_schema 定义结构,再在 invoke() 时传入。

context(单次运行时上下文):只在当前这次 invoke() / run 中生效,用于传递本次运行的业务数据,不负责保存对话历史,也不等同于长期记忆。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from dataclasses import dataclass

@dataclass
class RequestContext:
    user_id: str
    tenant_id: str

result = agent.invoke(
    {"messages": [{"role": "user", "content": "请查询我的任务。"}]},
    config={"configurable": {"thread_id": "session-001"}},
    context=RequestContext(user_id="user-123", tenant_id="tenant-ai"),
)

工具和中间件可以通过 runtime.context 读取调用 invoke() 时传入的 context 数据,例如 user_idtenant_id、权限和功能开关。

需要严格区分以下概念:

  • thread_id:会话线程标识,用于配合 checkpointer 恢复短期状态。
  • context:单次运行时上下文,用于本次 run 的业务依赖注入。
  • store:跨线程、跨会话的长期记忆存储。

stream_events():中间过程观测

invoke() 只返回最终结果。复杂 Agent 往往会执行多次模型调用和工具调用,因此需要 stream_events()stream() 暴露中间状态、工具调用和进度更新。生产 UI、调试工具路由、观察长任务执行轨迹时,流式事件比最终结果更重要。

stream()stream_events() 的区别: stream() 用于消费 Agent 的状态更新流,适合业务侧展示中间结果;stream_events() 用于消费更细粒度的运行事件流,适合调试工具调用、观测执行链路和构建 tracing。


周边与扩展 API 梳理

middleware 是工程化 Agent 的核心扩展点。它不是独立运行时,而是挂载在 create_agent() 返回的 Agent 图上,在模型调用前后工具调用前后状态更新或中断恢复等关键位置插入确定性逻辑。

容错、降级与调用限制

  • ModelRetryMiddleware:处理模型调用的瞬时失败,例如限流、超时、网络抖动。
  • ToolRetryMiddleware:处理工具执行失败,避免每个工具都手写重复 try/except
  • ModelCallLimitMiddleware:限制模型调用次数,防止无限循环和成本失控。
  • ToolCallLimitMiddleware:限制工具调用次数,可按全局或特定工具维度控制。
  • ModelFallbackMiddleware:主模型失败时切换备用模型,用于 provider redundancy、成本降级和高可用兜底。

这类中间件的核心价值是把基础设施失败、成本边界和执行上限从 prompt 软约束变成运行时硬约束。

安全护栏与人工介入

  • PIIMiddleware:检测并处理个人身份信息,例如邮箱、电话等。它把隐私保护从提示词提醒升级为确定性拦截。
  • HumanInTheLoopMiddleware:在高影响工具调用前触发人工审批。典型场景包括写文件、发邮件、数据库更新、支付、外部系统写操作。

HumanInTheLoopMiddleware 依赖 checkpoint 保存中断状态。人工决策通常包括 approveeditrejectrespond。它本质上是在工具执行前设置运行时门禁,而不是事后审计。

上下文管理与长期记忆

  • SummarizationMiddleware(摘要压缩中间件):当上下文接近窗口上限时自动压缩旧消息,同时保留近期消息。
  • MemoryMiddleware(长期记忆中间件):加载长期指令或记忆,例如 sources=["./AGENTS.md"]
  • SkillsMiddleware(技能加载中间件):按需加载领域技能,避免一次性把所有知识塞入上下文。
  • storeruntime.storestore 是长期记忆存储,runtime.store 是工具或中间件在运行时访问这份长期存储的入口,适合读写用户偏好、组织规则、业务实体和经验信息。

版本注意:SummarizationMiddleware 的旧参数已不推荐使用。 summary_prefixmax_tokens_before_summarymessages_to_keep 属于 deprecated 参数,新代码建议分别改用 summary_prompttrigger=("tokens", value)keep=("messages", value)。其中 trigger 用于定义“什么时候触发摘要”,keep 用于定义“摘要后保留多少近期上下文”。

执行环境与长任务工作区

  • FilesystemMiddleware(文件工作区中间件):给 Agent 提供可读写工作区,适合保存中间产物、草稿、文件分析结果和跨步骤状态。
  • StateBackend:为 deepagents 中间件提供状态后端。
  • Sandboxes:用于隔离执行环境,降低代码执行和文件操作风险。
  • Interpreters:用于代码解释执行,适合数据分析、日志处理、代码研究等任务。

长任务不应把全部材料塞进模型上下文,而应通过文件系统、工具检索、摘要和子 Agent 分层处理。

规划与多智能体委派

  • TodoListMiddleware:为 Agent 提供显式任务列表能力,适合多步骤规划和长任务跟踪。
  • SubAgentMiddleware:让主 Agent 把子任务委派给上下文隔离的子 Agent。
  • create_deep_agent:预组装文件系统、摘要、子 Agent、prompt caching 等能力,适合长时间运行的 coding 和 research 任务。
  • name=:为 Agent 设置标识符,便于嵌入 multi-agent 系统或作为子图使用。

SubAgentMiddleware 的典型架构是 supervisor 模式:主 Agent 负责路由、规划和汇总,子 Agent 负责专业任务执行。它能减少主上下文污染,并降低工具过多导致的注意力稀释。

supervisor 模式:由一个主 Agent 作为“总控”,负责拆解任务、调度子 Agent 和汇总结果;各子 Agent 拥有独立上下文,专注完成具体子任务。

观测、评估与 LangSmith

LangSmith 用于 trace Agent 的执行轨迹、调试工具调用、评估输出质量、监控异常和分析 token 成本。对于 Agent 系统,观测对象不是最终文本,而是完整决策链路:模型调用、工具调用、状态更新、中断恢复、重试、成本和延迟。

LangSmith 不是简单的日志工具,而是 Agent 的可观测性与评估平台,用于追踪执行链路、分析工具调用、监控成本延迟,并系统性评估输出质量。


工程化代码落地示例

本节通过两个可独立运行的脚本,展示 LangChain Agents 在工程中的典型落地方式:示例 1 聚焦生产级 Agent 调用链路,覆盖工具调用、结构化输出、运行时上下文、短期记忆和容错控制;示例 2 聚焦长任务 Agent Harness,展示如何通过上下文压缩、技能加载、本地资源管理和子 Agent 委派支撑复杂任务执行。

示例 1:生产级 Agent 调用链路:工具调用、结构化输出与短期记忆

enterprise_agent_pipeline.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Author : nanzet
# Description : 演示具备工具调用、结构化输出、短期记忆、上下文注入和容错控制的生产级 Agent
# requirements : pip install -U langchain langchain-google-genai langgraph typing_extensions

import json
import os
import sys
from dataclasses import dataclass
from typing import Any

from langchain.agents import create_agent
from langchain.agents.middleware import (
    ModelCallLimitMiddleware,
    ModelRetryMiddleware,
    ToolCallLimitMiddleware,
    ToolRetryMiddleware,
)
from langchain.tools import ToolRuntime, tool
from langchain_core.utils.uuid import uuid7
from langgraph.checkpoint.memory import InMemorySaver
from typing_extensions import TypedDict

MODEL_NAME = "google_genai:gemini-3.5-flash"


@dataclass
class RequestContext:
    user_id: str
    tenant_id: str


class AgentAnswer(TypedDict):
    summary: str
    confidence: float
    next_actions: list[str]


def require_env(name: str) -> str:
    value = os.getenv(name)
    if not value:
        raise RuntimeError(f"缺少环境变量:{name}。请设置后重新运行脚本。")
    return value


def build_agent() -> Any:

    @tool
    def search(query: str, runtime: ToolRuntime[RequestContext]) -> str:
        """根据查询词检索信息,并返回中文摘要。"""
        return (
            f"用户 {runtime.context.user_id} 在租户 {runtime.context.tenant_id} 中查询:{query}。"
            "模拟检索结果:Agent 的核心是模型根据上下文决定是否调用工具,"
            "工具结果会写回消息状态,模型继续推理直到生成最终答案。"
        )

    return create_agent(
        model=MODEL_NAME,
        tools=[search],
        system_prompt=(
            "你是一个严谨的 AI Agent 架构助手。"
            "请使用中文回答,优先给出工程可落地的结论,避免空泛表达。"
        ),
        response_format=AgentAnswer,
        context_schema=RequestContext,
        checkpointer=InMemorySaver(),
        middleware=[
            ModelRetryMiddleware(max_retries=3),  # 模型调用失败时最多重试 3 次
            ToolRetryMiddleware(max_retries=2),  # 工具调用失败时最多重试 2 次
            ModelCallLimitMiddleware(
                run_limit=8, exit_behavior="end"
            ),  # 当前这次 run 中,最多允许 8 次模型调用。超过后优雅结束,防止模型无限循环。
            ToolCallLimitMiddleware(
                run_limit=5, exit_behavior="continue"
            ),  # 当前这次 run 中,最多允许 5 次工具调用。超过后拦截工具调用,但让模型继续根据已有上下文收尾回答。
        ],
        name="research_assistant",
    )


def main() -> int:
    try:
        require_env("GOOGLE_API_KEY")

        agent = build_agent()
        config = {"configurable": {"thread_id": str(uuid7())}}
        context = RequestContext(user_id="user-123", tenant_id="tenant-ai-agent")

        first_result = agent.invoke(
            {
                "messages": [
                    {
                        "role": "user",
                        "content": "请检索并总结 AI Agent 工程化落地的三个关键能力。",
                    }
                ]
            },
            config=config,
            context=context,
        )

        structured_response = first_result["structured_response"]
        print("结构化摘要:")
        print(json.dumps(structured_response, ensure_ascii=False, indent=2))

        second_result = agent.invoke(
            {
                "messages": [
                    {
                        "role": "user",
                        "content": "继续说明这些能力在高级 AI Agent 面试中应该如何表达。",
                    }
                ]
            },
            config=config,
            context=context,
        )

        second_structured = second_result["structured_response"]
        print("第二轮结构化回答:")
        print(json.dumps(second_structured, ensure_ascii=False, indent=2))
        return 0

    except ImportError as exc:
        print(f"依赖导入失败:{exc}")
        print(
            "请执行:pip install -U langchain langchain-google-genai langgraph typing_extensions"
        )
        return 1
    except Exception as exc:
        print(f"脚本执行失败:{exc}")
        return 1


if __name__ == "__main__":
    sys.exit(main())

输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
结构化摘要
{
  "summary": "AI Agent 工程化落地的三个关键能力如下:\n\n1. **冷热解耦的结构化记忆管理能力**:解决 LLM 上下文(Context)窗口限制与记忆丢失问题。工程落地需构建**短期记忆**(基于 Redis 的会话滑动窗口与动态摘要)与**长期记忆**(基于向量数据库的 RAG 语义检索与 Key-Value 用户画像存储)的冷热分离架构,并建立记忆的自动写入、剪枝与关联检索机制。\n\n2. **确定性执行与安全工具编排能力**:解决大模型输出“幻觉”与企业级业务高确定性要求的冲突。工程落地需通过**强类型 Schema 校验**(如 JSON Schema 校验参数)、**工具调用的异常容错与重试机制**、**代码解释器的沙箱安全隔离**,并结合**状态机/有状态工作流引擎(如 LangGraph、Temporal)**,在 Agent 推理与确定性执行流之间建立安全边界。\n\n3. **DAG 状态链路可观测性与闭环评估能力**:解决 Agent 内部推理“黑盒”、故障排查困难以及迭代效果无法量化的问题。工程落地需引入**分布式 Trace 链路追踪**(如 OpenTelemetry、LangSmith),将 Planning-Reasoning-Action 拆解为有向无环图(DAG)并可视化;同时建立**回归评估流水线**,使用金标准数据集(Golden Dataset)与 LLM-as-a-judge 机制,实现 Agent 提示词和逻辑迭代的自动化量化评估。",
  "confidence": 0.95,
  "next_actions": [
    "设计并实现基于 Redis + 向量数据库的 Agent 双层记忆存储方案,优化多轮对话上下文消耗。",
    "引入 LangGraph 框架,定义包含人工确认(Human-in-the-loop)的安全工具编排工作流。",
    "在测试环境中部署 LangSmith 或 Phoenix 观测服务,接入 Trace 埋点以实现 Agent 执行链路的全面可视化监控。"
  ]
}
第二轮结构化回答
{
  "summary": "在高级 AI Agent 面试中,候选人应避免“套用 LangChain 默认模板”的初级描述,转而采用“痛点痛击 -> 架构权衡 -> 指标量化”的高级叙事结构。以下是针对三大关键能力的高分面试表达策略:\n\n1. **记忆管理能力面试表达:从“接入向量库”升级为“高并发冷热解耦的记忆引擎设计”**\n   * **痛点引入**:若将全部历史多轮会话直接塞入 Prompt,不仅会导致 Token 成本呈指数级上升,还会因大模型“Lost in the Middle(中间信息迷失)”导致推理准确率下降。\n   * **高分话术**:在架构设计中,我采用了**冷热解耦的记忆架构**。**短期记忆**基于 Redis 实现,采用“滑动窗口 + 异步 LLM 自动摘要压缩”机制,确保每次携带的上下文不超过 4K Token;**长期记忆**采用基于 Pydantic-Schema 结构化的 KV 存储(存储用户画像与偏好)和基于语义分片(Semantic Chunking)的向量库检索(RAG)。同时,设计了**记忆衰减算法(如结合时间权重的 Ebbinghaus 算法)**,动态计算记忆召回的 Relevance 评分,从而在保障 Agent 长期拟人化认知的同时,将单次 API Token 成本降低了 40%。\n\n2. **工具编排能力面试表达:从“调用 Function Calling”升级为“状态机强约束与防刷防爆控制”**\n   * **痛点引入**:完全自由的 ReAct(Reasoning and Acting)架构极易陷入“工具死循环”或在异常输入下产生不确定性的系统越权,造成账单爆炸和安全合规风险。\n   * **高分话术**:为了在企业级落地中兼顾 Agent 的灵活性与业务的硬确定性,我采用了 **LangGraph / 状态机(State Graph)** 对 Agent 推理路径进行局部强约束。对于高危工具调用(如资金交易、写库操作),在状态机的边(Edge)上配置了 **Human-In-The-Loop(人工介入)中断挂起机制**;在工具层自研了 **Pydantic 动态 Schema 校验拦截器**,对 LLM 吐出的 JSON 进行静态类型强校。针对黑客攻击或模型死循环,设计了**熔断器模式(Circuit Breaker)**,单次 Session 的工具调用次数(Max Iterations)严格限制在 5 次以内,单次会话超时限制在 15s 以内,保障了系统高可用与安全防御。\n\n3. **可观测性能力面试表达:从“看后台 Log”升级为“基于 OpenTelemetry 的 DAG 树状 Tracing 与闭环评估体系”**\n   * **痛点引入**:Agent 的推理过程是多步且非线性的,传统的单点 APM 日志无法还原“LLM 为什么在第三步选错了工具”这一黑盒现场。\n   * **高分话术**:我为生产环境引入了 **OpenTelemetry 规范与 Phoenix/LangSmith 深度集成的 Trace 监控架构**。每一次 Agent 的复杂调度都会生成一个 Trace ID,将 Planning -> Tool Call -> Observation -> Re-planning 的闭环拆解为树状 Spans,能够秒级定位是 Prompt 语义偏移、API 延迟还是 LLM 幻觉导致的任务失败。此外,我们搭建了**自动化回归评估管线(CI/CD Evaluation Pipeline)**,积累了 500+ 条真实业务场景的 Golden Dataset,采用 `Ragas` 框架评估检索召回率与内容忠实度,并配合 **LLM-as-a-Judge** 双模型交叉评分。每一次 Agent 拓扑逻辑或 Prompt 的修改,必须通过自动化 Regression Test 且评分提升后才能灰度发布,使 Agent 的策略调优走向数据驱动。",
  "confidence": 0.98,
  "next_actions": [
    "结合你目前的实际项目,将上述“冷热记忆”、“状态机熔断”、“DAG Trace”话术整理进个人简历的“项目深挖(Deep Dive)”模块中。",
    "模拟面试官可能提出的追问(如:“如何解决向量检索的噪声污染?”“多并发下 Redis 锁如何设计?”),准备对应的技术底座支持方案。"
  ]
}

代码说明:

  • 该脚本演示一个偏生产化的 Agent 调用链路,覆盖工具调用、结构化输出、短期记忆、运行时上下文注入和调用限制。
  • RequestContext 使用 dataclass 定义单次运行的业务上下文,包括 user_idtenant_id,并通过 context_schema=RequestContext 注册给 Agent
  • search() 使用 @tool 声明为工具函数,并通过 runtime.context 读取 invoke() 时传入的 context 数据,实现工具侧的运行时上下文访问。
  • AgentAnswer 使用 TypedDict 定义结构化输出 schema,避免自定义 Pydantic 类型被 checkpointer 序列化时产生未注册类型 warning。
  • create_agent() 中的 response_format=AgentAnswer 用于约束最终结构化结果,输出会写入 result["structured_response"]
  • checkpointer=InMemorySaver() 启用短期记忆,配合 thread_id 保存同一会话线程下的消息状态,使第二轮调用可以接续第一轮上下文。
  • config={"configurable": {"thread_id": str(uuid7())}} 为当前会话生成唯一线程标识,用于隔离不同会话的短期状态。
  • ModelRetryMiddlewareToolRetryMiddleware 分别处理模型调用与工具调用的失败重试,提升瞬时错误下的稳定性。
  • ModelCallLimitMiddlewareToolCallLimitMiddleware 分别限制单次 run 中的模型调用次数和工具调用次数,防止 Agent 无限循环或工具滥用。
  • exit_behavior="end" 表示模型调用超限后直接结束当前运行;exit_behavior="continue" 表示工具调用超限后跳过后续工具执行,让模型基于已有上下文继续收尾回答。
  • 脚本连续调用两次 agent.invoke(),并复用同一个 configthread_id,用于演示短期记忆如何让第二轮问题接续第一轮对话状态。

示例 2:长任务 Agent Harness:上下文压缩、技能加载与子 Agent 委派

deep_agent_harness_demo.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Author : nanzet
# Description : 演示 Deep Agents 风格的文件系统、摘要、技能、任务列表和子 Agent 委派配置
# requirements : pip install -U langchain langchain-google-genai langchain-deepseek langgraph deepagents


import os
import sys
from pathlib import Path
from typing import Any

from deepagents.backends import StateBackend
from deepagents.middleware import (
    FilesystemMiddleware,
    MemoryMiddleware,
    SkillsMiddleware,
    SummarizationMiddleware,
)
from deepagents.middleware.subagents import SubAgentMiddleware
from langchain.agents import create_agent
from langchain.agents.middleware import TodoListMiddleware
from langchain.tools import tool

MAIN_MODEL_NAME = "google_genai:gemini-3.5-flash"
SUMMARY_MODEL_NAME = MAIN_MODEL_NAME
SUB_AGENT_MODEL_NAME = "deepseek:deepseek-v4-flash"

SCRIPT_DIR = Path(__file__).resolve().parent
AGENTS_FILE = SCRIPT_DIR / "AGENTS.md"
SKILLS_DIR = SCRIPT_DIR / "skills"


def require_env(name: str) -> str:
    value = os.getenv(name)
    if not value:
        raise RuntimeError(f"缺少环境变量:{name}。请设置后重新运行脚本。")
    return value


def prepare_local_sources() -> None:
    AGENTS_FILE.write_text(
        "请始终使用中文输出,并优先给出工程化结论。\n",
        encoding="utf-8",
    )
    SKILLS_DIR.mkdir(exist_ok=True)
    (SKILLS_DIR / "research.md").write_text(
        "研究任务需要先拆解问题,再检索证据,最后输出结构化结论。\n",
        encoding="utf-8",
    )


def extract_text_content(content: Any) -> str:
    if isinstance(content, str):
        return content

    if isinstance(content, list):
        text_parts: list[str] = []
        for part in content:
            if isinstance(part, dict) and part.get("type") == "text":
                text = part.get("text", "")
                if text:
                    text_parts.append(text)
        if text_parts:
            return "\n".join(text_parts)

    return str(content)


def build_agent() -> Any:

    @tool
    def search(query: str) -> str:
        """根据查询词搜索资料,并返回中文摘要。"""
        return (
            f"查询:{query}。模拟结果:长任务 Agent 通常需要上下文压缩、"
            "文件系统工作区、显式任务列表和子 Agent 隔离上下文。"
        )

    backend = StateBackend()

    return create_agent(
        model=MAIN_MODEL_NAME,
        tools=[search],
        system_prompt=(
            "你是主控 Agent,负责规划任务、委派研究子 Agent,"
            "并用中文输出结构清晰的最终结论。"
        ),
        middleware=[
            FilesystemMiddleware(backend=backend),
            SummarizationMiddleware(
                model=SUMMARY_MODEL_NAME,
                backend=backend,
                trigger=("tokens", 4000),
                keep=("messages", 20),
            ),
            MemoryMiddleware(backend=backend, sources=[str(AGENTS_FILE)]),
            SkillsMiddleware(backend=backend, sources=[str(SKILLS_DIR)]),
            TodoListMiddleware(),
            SubAgentMiddleware(
                backend=backend,
                subagents=[
                    {
                        "name": "researcher",
                        "description": "搜索资料并返回结构化中文摘要。",
                        "system_prompt": "请使用搜索工具研究问题,并用中文总结关键点。",
                        "tools": [search],
                        "model": SUB_AGENT_MODEL_NAME,
                        "middleware": [],
                    }
                ],
            ),
        ],
        name="research_supervisor",
    )


def main() -> int:
    try:
        require_env("GOOGLE_API_KEY")
        require_env("DEEPSEEK_API_KEY")
        prepare_local_sources()

        agent = build_agent()

        result = agent.invoke(
            {
                "messages": [
                    {
                        "role": "user",
                        "content": "请研究 LangChain Agent 在长任务中的上下文管理和任务委派方案。",
                    }
                ]
            },
            config={"configurable": {"thread_id": "deep-agent-demo-thread"}},
        )

        final_content = result["messages"][-1].content
        print("最终回答:")
        print(extract_text_content(final_content))
        return 0

    except ImportError as exc:
        print(f"依赖导入失败:{exc}")
        print(
            "请执行:pip install -U langchain langchain-google-genai langchain-deepseek langgraph deepagents"
        )
        return 1
    except Exception as exc:
        print(f"脚本执行失败:{exc}")
        return 1


if __name__ == "__main__":
    sys.exit(main())

输出结果:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
最终回答
# LangChain Agent 在长任务中的上下文管理与任务委派方案深度研究报告

在构建生产级 AI Agent 系统的过程中**长任务Long-running Tasks**例如软件自主开发多源异构数据分析多阶段业务审批工作流是最具挑战性的场景单一 Agent 在面对此类任务时通常会遭遇**上下文窗口溢出Token 膨胀****注意力失焦Lost in the Middle****工具调用过载Tool Overload** 以及 **规划失效Planning Failure** 等多重瓶颈

本报告对基于 LangChain / LangGraph 生态系统的**长任务上下文管理**与**任务委派多 Agent 协同**方案进行系统性梳理对比并深入剖析二者的**协同效应**最终提供生产级的最佳实践架构设计模式

---

## 第一部分:长任务中的上下文管理方案

传统的 Prompt 工程记忆模式在长交互复杂推理的长任务中捉襟见肘LangGraph 引入的基于状态持久化的管理机制彻底改变了这一现状

### 1. 传统 LangChain 记忆机制的演进与局限

| 方案名称 | 实现原理 | 优点 | 缺点长任务瓶颈 | 典型适用场景 |
| :--- | :--- | :--- | :--- | :--- |
| **滑动窗口**<br>`ConversationBufferWindowMemory` | 仅保留最近 $k$ 轮对话直接丢弃更早的交互记录 | 实现极其简单Token 消耗有硬性上限低延迟 | 远期关键信息如任务初始目标全局约束用户设定会无情丢失 | 简单客服短轮次单向 QA |
| **摘要记忆**<br>`ConversationSummaryMemory` | 触发 LLM 对历史消息进行再摘要用摘要文本替代旧历史 | 能够极大地压缩 Token理论上可保留全局核心语义 | **摘要漂移Summarization Drift**反复摘要导致信息失真且容易滋生 LLM 幻觉增加了额外的 LLM 调用开销 | 中等长度20~100轮对话 |
| **语义检索记忆**<br>`VectorStoreRetrieverMemory` | 向量化对话检索时提取与当前 Query 语义最相似的历史片段 | 可存储几乎无限的历史 | **碎片化上下文**检索结果丧失时间轴连贯性若 Embedding 匹配不佳关键决策依据会漏召 | 知识库型客服跨 Session 记忆回溯 |
| **消息裁剪**<br>`trim_messages` | 通过 Tiktoken 或 LLM Tokenizer 按 Token 预算对消息列表进行切片 | 精准控制 Token 边界避免窗口溢出Out-of-Memory)。 | 属于生硬的物理截断缺乏智能语义筛选 | 作为系统兜底的安全网Guardrail)。 |

---

### 2. LangGraph 现代状态管理哲学

LangGraph 改变了记忆即 Prompt 拼接的传统认知将上下文管理升华为**有结构可持久化可审计可回溯的状态机管理**

```
                   ┌─────────────────────────────┐
                           State (类型定义)      
                     - messages: Annotated[...] 
                     - global_context: dict     
                     - summary: str             
                   └──────────────┬──────────────┘
                                  
               ┌──────────────────┼──────────────────┐
                                                   
     ┌──────────────────┐┌──────────────────┐┌──────────────────┐
       SQLite / Postgre││   Thread ID      ││  Time Travel     
        Checkpointer   ││ (多用户/会话隔离)  ││ (历史版本链/回滚)
     └──────────────────┘└──────────────────┘└──────────────────┘
```

#### A. 结构化 State 与 Checkpointer (持久化层)

* **状态结构化**在 LangGraph 中State 通常通过 TypedDict 或 Pydantic 模型定义不仅能存原始 Message 数组还可以存强类型的结构化字段如 `user_profile: dict`, `task_steps: list`)。
* **增量持久化Checkpointing**Checkpointer如 `AsyncPostgresSaver` 或 `SqliteSaver`在**每个节点Node执行完毕后自动保存 State 差异Diff的快照**这保证了即使应用突然中断重启Agent 也能从最后一个正确的 Checkpoint 完美恢复

#### B. 线程(Threads)与多会话管理

* 通过配置 `thread_id` 实现物理隔离在同一个 `thread_id` 内LangGraph 自动关联其对应的 Checkpoint 版本链多用户并发交互时上下文完全隔离无需开发者手动管理会话状态存取

#### C. `add_messages` 增量合并与高级修剪

* **自动去重合并**使用 `Annotated[list, add_messages]` 定义的消息列表当传入具有相同 `id` 的消息时会自动覆盖或删除如传入一个带有 `id` 且 `content=""` 的消息进行逻辑删除),避免了消息列表的无序膨胀
* **边界懒压缩策略Lazy Compression**在 Node 内部执行 LLM 调用前仅在 Token 逼近限制例如大于 8000 tokens才触发更新 Summary 字段 + 裁剪 recent messages的组合策略这既保证了近期上下文的连贯又通过 Summary 字段保留了远期关键决策

#### D. 历史回溯(Time Travel)与人工介入(Human-in-the-loop)

* **Time Travel**允许开发者或用户获取某一 `thread_id` 的所有 checkpoint 历史选择特定的 `checkpoint_id` 跳转历史分支”,在当时的状态下注入新的 Prompt 或变量重新执行这在长任务由于某一步工具调用失败需要倒带重来提供了系统级容错
* **Human-in-the-loop (HIL)**通过 `interrupt_before`或 `interrupt_after`显式中断图的执行将状态冻结等待人工审批Approve)、修改Modify状态人工修改后的数据通过 `update_state`能够无缝合并回当前状态极大地降低了长任务在核心决策点的偏离概率

---

## 第二部分:长任务中的任务委派(Task Delegation)方案

当长任务的复杂性超越了单个 Agent 的推理上限必须引入**任务委派与多 Agent 协作**

### 1. 为什么长任务需要任务委派?

1. **突破工具过载Tool Overload)”**LLM 能够完美遵循的工具指令数量通常在 5 个以内超过 10 个工具时Agent 频繁出现误用漏用工具委派可以将工具按职责分散给不同 Worker
2. **避免级联错误累积**长任务中第一步的微小偏差会导致后续规划全盘崩溃通过委派每个子任务执行完毕后都会回到主控Orchestrator处进行校验及时斩断错误链路
3. **状态隔离与注意力聚焦**子 Agent 运行时拥有纯净的仅与子任务相关的上下文空间不受全局复杂历史的干扰

---

### 2. 多 Agent 任务委派三大核心设计模式

#### Pattern 1: 树状/分层级协作模式(Hierarchical Agent Teams / Supervisor-Worker)

* **结构**一个高能力的 **Supervisor Agent主控** 充当项目经理拥有任务拆解动态路由结果校验的能力下挂多个 **Worker Agents执行员**各自配备专用工具
* **通信与状态流转**
    1. Supervisor 接收全局 Task将其拆解为 Sub-tasks
    2. Supervisor 产生决策利用条件边Conditional Edges将 Payload 分发至对应的 Worker
    3. Worker 运行在其局部 State 空间中执行完毕后向 Supervisor 提交结果Response)。
    4. Supervisor 进行 QA 校验如果合格则更新全局 State继续分发下一步若不合格则要求原 Worker 重试或重规划
* **优缺点**
  * *优点*流程高度可控可审计单点纠错能力强
  * *缺点*对 Supervisor 的推理能力要求极高存在单点瓶颈相比对等协作延迟稍高

#### Pattern 2: 网络/对等协作模式(Network / Collaborative Pattern)

* **结构**去中心化Agent A 执行完自己的动作后直接决定将任务交棒给 Agent B 或 Agent C
* **通信与状态流转**
  * 通过共享的消息图MessageGraph或共享 State每个 Agent 根据当前 State 内的最近一条消息或特定状态标记),自主决定自己是否应当抢占任务并处理处理完后再追加消息触发下一个 Agent 的激活
* **优缺点**
  * *优点*高度动态灵活性强适合发散型探索型任务如多人脑暴跨角色代码重构)。
  * *缺点*容易产生死锁死循环”,状态流转难以追踪不适合严肃商业流

#### Pattern 3: 编排式(Orchestration) vs 编舞式(Choreography)

* **编排式Orchestration**由一个明确的中枢逻辑可以是 Supervisor LLM也可以是固定的 Python 状态机图逻辑)”显式规划所有的 Step
* **编舞式Choreography**没有集中式中枢每个 Agent 订阅事件流自主做出响应

> *在长任务最佳实践中*通常在**顶层使用编排式**确保大方向不偏偏满足业务合规与 SLA),在**底层子任务执行时采用编舞式**或微型对等网络以提升局部吞吐量

---

### 3. LangGraph 中子图(Subgraphs)作为独立委派单元的设计

在 LangGraph 中**子图Subgraph** 是任务委派的最佳技术载体

```python
# 1. 定义子任务执行团队(Worker Subgraph)的独立 State
class ResearchTeamState(TypedDict):
    query: str
    raw_docs: List[str]
    synthesized_report: str

research_graph = StateGraph(ResearchTeamState)
research_graph.add_node("retrieve", retrieve_node)
research_graph.add_node("synthesize", synthesize_node)
research_graph.set_entry_point("retrieve")
research_graph.set_finish_point("synthesize")
compiled_research_team = research_graph.compile()

# 2. 在主控(Supervisor)图中将其作为一个普通 Node 嵌入
class MainState(TypedDict):
    main_task: str
    sub_task_payload: dict  # 传递给子图的 Payload
    final_result: str

main_builder = StateGraph(MainState)
main_builder.add_node("supervisor", supervisor_node)
main_builder.add_node("research_node", compiled_research_team)  # 嵌入子图
```

**子图通信协议与状态同步机制**

* **单向注入与抽取State Mapping**当主图跳转到子图节点时主图的 `sub_task_payload` 会被提取并映射到子图的 `query`
* **状态隔离**子图在运行 `retrieve` 和 `synthesize` 时的所有消息和中间变量仅保留在子图的独立 Checkpoint 中主图对其完全无感”,从而彻底避免了主图的上下文遭受海量原始网页数据的污染
* **结果合并Reduction**当子图执行到 Finish Point 时它只将最终的 `synthesized_report` 传回主图节点主图将其 merge 到全局 State 中

---

## 第三部分:上下文管理与任务委派的协同效应

上下文管理与任务委派并不是割裂的两个技术点在长任务中它们呈现出强烈的**相辅相成Synergistic**关系

```
┌─────────────────────────────────────────────────────────────┐
                       任务委派 (Task)                        
 ┌────────────────────────┐       ┌────────────────────────┐ 
     主控 (Supervisor)    ├──────►│     子图 (Subgraph)     
 └───────────▲────────────┘       └───────────┬────────────┘ 
└─────────────┼────────────────────────────────┼──────────────┘
               过滤裁剪变量映射             仅返回高置信度结果
┌─────────────┴────────────────────────────────▼──────────────┐
                    上下文管理 (Context)                     
 ┌────────────────────────┐       ┌────────────────────────┐ 
   全局轻量 State / LTM             局部临时 State / STM  
 └────────────────────────┘       └────────────────────────┘ 
└─────────────────────────────────────────────────────────────┘
```

1. **任务委派是最好的上下文物理隔离墙**
    传统的 Context Management 费尽心思想用各种算法压缩 Prompt而 **Task Delegation 机制直接从物理上将全局长任务划分为一个个微型状态图Subgraphs)”**每个子图拥有专属短暂生命周期的短期记忆STM),执行完毕后随手丢弃只将干燥度极高信息密度极高的结论汇入全局长期记忆LTM)。这从根源上消除了 `Lost in the Middle` 效应
2. **上下文管理为主控决策提供精准沙盒**
    主控Supervisor在做复杂的任务拆解和下一步分配时最忌讳垃圾信息干扰通过 LangGraph 的 `Message Trimming` 和状态过滤主控节点在调用时永远只暴露**任务定义当前阶段进度人工干预输入以及上一个 Worker 的精炼 Response**高置信度低噪的上下文使主控的规划Planning成功率呈指数级提升
3. **Checkpointer 守护分布式委派的全局一致性**
    当一个复杂的 Supervisor-Worker 体系中有多层嵌套子图时一旦最深层的 Worker 发生 API 报错网络断开或逻辑异常Checkpointer 能够提供**全链路分布式事务般的状态恢复**我们可以精确回退到导致异常的那个子 checkpoint重新委派避免了整个分布式 Agent Team 从头跑一次带来的巨大 Token 消耗和时间开销

---

## 第四部分:长任务 Agent 最佳实践架构设计模式

结合上述研究以下推荐三种可在生产环境中直接落地的 Agent 架构模式

### Pattern A: 边界裁剪编排模式(Border-Trimming Orchestrator)

* **核心思路**主控全局控盘在每个 Worker Node 的**入口边Entry Edge** 和 **出口边Exit Edge** 强制加入 Context Filter
* **实现要点**
  * 在进入 Worker A 之前通过 Python 逻辑自动剥离全局 State 中除 `A_task_instruction``A_necessary_schema` 以外的所有无用字段
  * Worker A 结束后在返回主图前调用 `trim_messages(strategy="last", max_tokens=1000)`仅保留最近一轮交互并将核心指标如 `extracted_data: dict`存入全局结构化变量绝不让 Worker 内部的嘴碎吐回给全局

### Pattern B: 带有人工审核的层级子图容错模式(Checkpointed Hierarchical HIL)

* **核心思路**适合高合规高精度的长任务将任务委派给多个 Subgraph但在 Subgraph 之间和关键执行节点加入 `interrupt_before`
* **设计示例**
    1. **Orchestrator** 分解任务得到 `Plan_v1`
    2. 触发 `interrupt_before["execute_plan"]` 暂停系统通知人工校验 `Plan_v1`
    3. 人工在前端界面若发现不合理通过 `graph.update_state(config, {"plan": "修正后的Plan_v1"})` 动态注入
    4. 调用 `graph.invoke(None, config)` 唤醒 Agent进入各 Worker 节点执行
    5. 若 Worker 执行失败系统通过 Time Travel 回滚到步骤 2 的 Checkpoint人工再次纠偏重新运行

### Pattern C: 双轨记忆共享网络模式(Dual-Track Memory Shared Network)

* **核心思路**既保留全局上下文的大局观长期记忆 LTM)”,又保障子任务执行的高频响应短期记忆 STM)。
* **实现机制**
  * **Track 1全局长期记忆轨**在顶层 State 中维护一个只读的 `GlobalContext`包含用户长效偏好系统核心目标关键参数约束),该字段在整个长任务运行期间**只读不写**在每次委派给 Worker 时强制作为 System Prompt 注入
  * **Track 2局部短期工作轨**Worker 在执行复杂的多步交互代码尝试时产生海量的中间消息全部存留在 `MessagesState` 中当 Worker 退出时通过一个特定的 `summarize_node` 将消息压缩成 200 字以内的 `progress_update`追加至主图同时 `MessagesState` 被整体归档/截断清空

---

## 总结

在长任务场景下LangChain / LangGraph 提供了目前业界最完备的理论框架和技术实现

* **上下文管理**不再局限于单纯的 Prompt 拼接或全量向量检索而是基于 **Checkpointer 机制**通过**结构化状态多线程隔离懒摘要压缩Time TravelHIL人工介入**构建了兼顾弹性可追溯性和低 token 消耗的鲁棒上下文层
* **任务委派**通过 **Supervisor-Worker 分层架构** 和 **独立子图Subgraphs** 实现了职责专业化状态高度隔离
* **两者的完美结合**通过**物理划分上下文域State Isolation**与**边界数据精准映射**极大地稀释了复杂长任务的熵增速度是将 Agent 从玩具原型推向生产级系统的必由之路

代码说明:

  • 该脚本演示一个 Deep Agents 风格的长任务 Agent Harness,重点展示文件系统工作区、上下文压缩、技能加载、任务列表和子 Agent 委派。
  • MAIN_MODEL_NAME="google_genai:gemini-3.5-flash" 作为主控 Agent 模型,负责规划、路由和最终汇总;SUB_AGENT_MODEL_NAME="deepseek:deepseek-v4-flash" 作为研究子 Agent 模型,负责具体资料研究任务。
  • SCRIPT_DIRAGENTS_FILESKILLS_DIR 用于固定本地资源生成路径,确保 AGENTS.mdskills/ 始终创建在脚本同级目录,而不是当前终端运行目录。
  • prepare_local_sources() 会生成本地长期指令文件 AGENTS.md 和技能说明目录 skills/,供 MemoryMiddlewareSkillsMiddleware 加载。
  • FilesystemMiddleware 提供文件系统工作区能力,让长任务可以围绕文件进行读写和中间结果管理。
  • SummarizationMiddleware 通过 trigger=("tokens", 4000)keep=("messages", 20) 在上下文过长时自动摘要旧消息,降低上下文窗口压力。
  • MemoryMiddlewareAGENTS.md 加载长期指令,SkillsMiddlewareskills/ 加载任务技能说明,用于增强 Agent 的长期行为约束和领域能力。
  • TodoListMiddleware 为复杂任务提供显式任务列表管理,帮助 Agent 拆解、跟踪和推进长任务。
  • SubAgentMiddleware 注册名为 researcher 的子 Agent,由主控 Agent 负责委派研究任务,子 Agent 使用独立模型和工具完成专业子任务。
  • extract_text_content() 用于从模型返回的 content parts 中提取纯文本,避免直接打印 typetextextras.signature 等底层响应结构。
  • thread_id="deep-agent-demo-thread" 用于标识当前会话线程,方便 Agent 在同一线程中维持任务状态。

常见踩坑与高频面试点

常见踩坑

踩坑 1:只传 thread_id,但没有配置 checkpointer

  • 原因:thread_id 只是会话作用域标识,不负责保存状态。
  • 修复方式:本地调试使用 checkpointer=InMemorySaver(),生产环境使用持久化 checkpointer(例如 PostgresSaverAsyncPostgresSaverSqliteSaverMongoDBSaver 或 Redis 类后端。其中 PostgresSaver / AsyncPostgresSaver 更适合多实例生产部署,因为它能在进程重启、任务中断、人工审批和长任务恢复场景中可靠保存并恢复图状态);涉及多轮对话、HITL 中断恢复和长任务续跑时必须配置。

踩坑 2:混淆 contextthread_idstore

  • 原因:context 是单次运行时依赖,thread_id 是短期会话线程,store 是长期记忆。
  • 修复方式:把 user_id、租户、权限放入 context;把会话连续性放入 thread_idcheckpointer;把跨会话偏好、组织规则和业务实体放入 store

踩坑 3:工具 schema 设计过于随意

  • 原因:模型依赖工具名、参数类型和 docstring 进行工具选择与参数生成。
  • 修复方式:使用 @tool,保持参数强类型,docstring 明确工具能力、输入含义和输出边界,避免让模型生成鉴权类参数。

踩坑 4:用 system_prompt 替代确定性安全策略

  • 原因:prompt 是软约束,无法稳定处理隐私、审批、调用次数和高风险动作。
  • 修复方式:使用 PIIMiddlewareHumanInTheLoopMiddlewareModelCallLimitMiddlewareToolCallLimitMiddleware 等中间件实现硬边界

踩坑 5:长任务不做上下文管理

  • 原因:Agent 循环会不断累积 messages、工具结果和中间步骤,最终导致上下文溢出或注意力污染。
  • 修复方式:使用 SummarizationMiddlewareFilesystemMiddlewareMemoryMiddlewareSkillsMiddleware 和子 Agent 隔离上下文。

踩坑 6:结构化输出与工具调用没有联合验证

  • 原因:response_format 可能走 ProviderStrategyToolStrategy,不同模型对结构化输出和工具调用并用的支持程度不同。
  • 修复方式:上线前针对目标模型压测 structured_response 成功率、schema 兼容性和工具调用冲突情况。

踩坑 7:缺少流式观测和 LangSmith trace

  • 原因:只看最终回答无法定位工具选择错误、循环过长、token 成本异常、上下文污染和重试风暴。
  • 修复方式:开发阶段使用 stream_events() 观察执行过程,生产阶段接入 LangSmith 做 tracing(调用链追踪)、eval(效果评估) 和 monitoring(线上监控)。

高频面试点

Q1:LangChain Agent 的核心执行模型是什么?

答: 核心是 model -> tool_calls -> tool_results -> model 的状态循环。模型读取当前 State 中的 messages 和上下文,决定是否调用工具;运行时执行工具并把结果写回状态;模型继续推理,直到不再请求工具并输出最终答案。相比普通 LLM.invoke(),Agent 多了行动能力、状态演进和多步决策;相比固定工作流,Agent 的步骤数量和工具选择由模型动态决定。

Q2:为什么官方说 Agent = Model + Harness?

答: Model 是推理引擎,负责工具选择、工具结果解释和任务终止判断;Harness 是运行容器,包含 toolssystem_promptmiddlewarecheckpointercontext_schemastore 和流式机制。单纯模型调用只能生成文本,而 Harness 让模型具备可控执行、状态恢复、安全拦截和工程扩展能力。

Q3:create_agent() 的架构定位是什么?

答: create_agent() 是构建 Agent harness 的统一入口,它把 model=tools=system_prompt=response_format=middleware=checkpointer=context_schema= 等组装成可执行图。底层依赖 LangGraph 的状态图能力,因此支持 invoke()stream_events()checkpointinterrupt 和子图组合。相比手写循环,它提供标准化、可组合、可观测的 Agent 运行框架。

Q4:thread_id、context、store 如何区分?

答: 三者分别解决会话连续性、运行时依赖注入和长期知识沉淀。

  • thread_id 是会话线程的唯一标识符,配合 checkpointer 保存、关联和恢复该线程下的短期消息历史与图状态;
  • context 是单次运行时输入,通过 runtime.context 给工具和中间件读取;
  • store 不绑定某一次会话线程,而是保存可长期复用的信息,例如用户偏好、业务规则和组织知识;在同一应用内,只要多条会话线程或多个 Agent 运行共享同一个 store 后端,就可以通过 runtime.store 读取这些长期记忆。

Q5:ProviderStrategy 和 ToolStrategy 的区别是什么?

答: ProviderStrategy 使用模型厂商原生结构化输出能力,约束更强、解析更稳定;ToolStrategy 通过工具调用协议让模型输出符合 schema 的参数,兼容更多模型但依赖 tool calling 质量。直接传 response_format=Schema 时,LangChain 会根据模型能力自动选择。工程上必须验证目标模型是否能同时稳定处理业务工具和最终结构化响应。

Q6:为什么生产 Agent 必须重视 middleware?

答: 很多生产控制不能交给模型自觉完成。middleware 可以在模型调用、工具调用、状态更新等生命周期中插入确定性逻辑,例如重试、fallback、调用限制、PII 处理、人工审批和上下文压缩。相比 promptmiddleware 更可测试、更可组合、更可审计。

这里的 fallback 指降级或备用路径切换,例如主模型失败时切换到备用模型,主工具不可用时切换到备用工具,或结构化输出失败时切换到更保守的解析策略。

Q7:什么时候应该引入 SubAgentMiddleware?

答:任务跨领域工具集合很大上下文容易污染、需要并行研究或隔离风险时,应引入 SubAgentMiddleware。主 Agent 作为 supervisor 负责任务拆解、路由和汇总;子 Agent 使用独立 system_prompttoolsmodel 和上下文完成专门任务。它比单 Agent 更能降低注意力稀释比固定流程更保留动态任务拆解能力

Q8:HumanInTheLoopMiddleware 的底层机制是什么?

答: HumanInTheLoopMiddleware模型生成工具调用之后、工具执行之前检查 interrupt_on 策略;命中后触发 LangGraph interrupt,并依赖 checkpoint 保存当前图状态。人工返回 approveeditrejectrespond 后,Agent 从中断点恢复。它是执行前控制,而不是事后审计

官方 HumanInTheLoopMiddleware 文档里,interrupt_on 的值主要有三种写法:

1
2
3
4
5
6
7
HumanInTheLoopMiddleware(
    interrupt_on={
        "write_file": True,
        "execute_sql": {"allowed_decisions": ["approve", "reject"]},
        "read_data": False,
    }
)

含义是:

  • 当模型准备调用 write_file 时,触发人工审批。
  • 当模型准备调用 execute_sql 时,触发人工审批,但人工只能 approvereject
  • 当模型准备调用 read_data 时,不触发人工审批,直接执行。

interrupt_on 用于配置哪些工具调用需要人工审批,以及允许人工做哪些决策。它可以对某个工具设置 TrueFalse,或设置 allowed_decisionswhen 条件函数。人工决策中,approve 表示按原参数执行,edit 表示修改参数后执行,reject 表示拒绝执行并把反馈交还给模型,respond 表示人工直接作为工具结果返回,通常用于 ask_user 这类交互式工具。

Q9:如何防止 Agent 无限循环和成本失控?

答: 需要多层控制:核心原则是把开放式推理循环约束在次数、时间、成本和权限边界内。

  • Model:设置 timeoutmax_retries
  • Agent:使用 ModelCallLimitMiddlewareToolCallLimitMiddleware
  • Tool:保证幂等、限流和超时。
    • 幂等:用于防止同一工具调用被重复执行后产生重复副作用;
    • 限流:用于限制工具调用频率和次数,避免 Agent 循环调用导致成本、额度或外部系统压力失控;
    • 超时:用于防止单次工具调用长时间阻塞整个 Agent 执行循环。
  • 观测层:使用 LangSmith 追踪调用链、token、延迟和错误。

Q10:如何判断一个 Agent 是否生产可用?

答: 不能只看最终回答质量,而要评估完整决策轨迹,包括工具调用准确率结构化输出成功率平均模型调用次数工具失败恢复上下文压缩质量HITL 命中率PII 拦截率延迟token 成本回归测试表现。Agent 是“推理 + 执行”的闭环系统,因此评估对象必须覆盖执行轨迹和外部副作用。

Licensed under CC BY-NC-SA 4.0
最后更新于 Jul 08, 2026 22:32 +0800
使用 Hugo 构建
主题 StackJimmy 设计