Zata 扎根技术 · 记录思考 · 见更大的世界

Better CodeA Brighter Tomorrow

保持好奇持续成长

Featured image of post DeepAgents完全指南

DeepAgents完全指南

从协议、架构到生产实践:全面解析 LangChain Deep Agents 的设计理念、API、文件系统、子智能体、上下文管理与部署策略

DeepAgents 完全指南

本文基于 deepagents==0.7.15(Python,2026-09-16 发布)撰写,涵盖从入门概念到生产部署的全部核心内容。0.x 版本 API 变动较快,如果你的版本低于 0.6,请先升级再对照本文——0.6.0 改了消息历史的持久化格式,升级路径见 3.5 节。


第一章:DeepAgents 是什么

1.1 一句话定义

DeepAgents(包名 deepagents)是 LangChain 团队推出的一个 agent harness(智能体运行框架)—— 一个开箱即用、高度可定制、面向长周期多步任务的智能体运行时。

它不是又一个 LangGraph 图编排工具,也不只是 LangChain 的薄封装,而是一个带有以下"观点"(opinionated)的完整框架:

  • 默认就适合长任务:内置文件系统、上下文压缩、子智能体委派,不用你自己搭。
  • 每个组件都可以替换:模型、文件后端、中间件、子智能体、技能,全部可插拔。
  • 模型无关:只要模型支持 tool calling 就能用——GPT、Claude、Gemini、Qwen、本地 Ollama 都行。
  • 生产就绪:底层是 LangGraph,天然支持流式输出、持久化、checkpoint、断点恢复。

灵感来源是 Claude Code:团队想搞清楚 Claude Code 为什么通用,然后把那些设计提炼成一个可编程的框架。

1.2 在 LangChain 生态中的位置

理解 DeepAgents 的关键,是理解 LangChain 生态的三层结构:

┌─────────────────────────────────┐
│         LangSmith               │  ← 可观测性、评测、监控
├─────────────────────────────────┤
│         Deep Agents             │  ← 完整 harness(本文主角)
├─────────────────────────────────┤
│    LangChain create_agent       │  ← 轻量 agent(无内置文件系统/子agent)
├─────────────────────────────────┤
│         LangGraph               │  ← 图运行时(状态、节点、边)
└─────────────────────────────────┘
  • LangGraph 是底层的图运行时,管状态、节点、边、checkpoint。
  • LangChain create_agent 是最小的 agent 循环:模型 + 工具 + 消息 → 循环 → 回复。
  • DeepAgents 在 create_agent 之上叠加了一整套 middleware(文件系统、子智能体、上下文压缩、技能、权限),把"裸 agent"变成一个完整的 agent harness。
  • 任何 LangGraph 编译后的图都可以作为 DeepAgents 的子智能体传入,实现自定义编排与 DeepAgents 默认行为的混合。

什么时候用什么层:

场景用什么
需要 planning、context 管理、子智能体委派DeepAgents
只需要一个简单 tool-calling 循环LangChain create_agent
agent 循环不是你想要的形状,需要自定义图LangGraph
需要监控、评估、traceLangSmith(可与上面任何层搭配)

1.3 核心能力一览

能力说明
文件系统内置 ls / read_file / write_file / edit_file / delete / glob / grep,支持多种可插拔后端
子智能体主 agent 通过 task 工具委派任务给拥有独立上下文的子 agent
上下文管理自动摘要长对话,超大工具输出自动转存到文件系统
Shell 执行通过 Sandbox 后端提供 execute 工具
代码执行CodeInterpreterMiddleware(实验性)在受限 QuickJS 沙箱里跑代码与编程式工具调用
持久记忆可插拔的状态和存储后端,支持跨会话记忆
Human-in-the-Loop工具调用或文件操作前暂停,等待人工审批/编辑/拒绝
Skills可复用的行为模板,按需加载,渐进式披露
结构化输出主 agent 和子 agent 均支持 Pydantic 模型作为输出 schema
MCP 工具可接入任意 MCP server 作为工具

1.4 安装

# 推荐
uv add deepagents

# 或者
pip install deepagents

要求 Python >= 3.11, < 4.0。


第二章:架构与中间件协议

DeepAgents 的一切能力都由**中间件栈(middleware stack)**提供。理解这个栈,就理解了 DeepAgents 的全部。

2.1 中间件栈的完整顺序

当你调用 create_deep_agent() 时,框架会按以下顺序组装中间件:

┌─ 基础栈(Base stack)────────────────────────────────┐
│ 1. SkillsMiddleware          (仅当传入 skills)      │
│ 2. FilesystemMiddleware      (始终存在)             │
│ 3. SubAgentMiddleware        (有子智能体时存在)      │
│ 4. SummarizationMiddleware   (始终存在)             │
│ 5. PatchToolCallsMiddleware  (始终存在)             │
│ 6. AsyncSubAgentMiddleware   (有异步子智能体时存在)   │
│ 7. 你的自定义 middleware       (插入到这里)           │
├─ 尾部栈(Tail stack)────────────────────────────────┤
│ 8. Harness profile 额外中间件                          │
│ 9. 工具排除过滤(excluded_tools)                      │
│ 10. AnthropicPromptCachingMiddleware(非 Anthropic noop)│
│ 11. BedrockPromptCachingMiddleware(未装则跳过)        │
│ 12. FireworksPromptCachingMiddleware(未装则跳过)      │
│ 13. MemoryMiddleware         (仅当传入 memory)       │
│ 14. HumanInTheLoopMiddleware (仅当传入 interrupt_on)  │
└──────────────────────────────────────────────────────┘

关键点:

  • 你的 middleware 插在位置 7,在核心中间件之后、prompt caching 和 memory 之前。
  • 如果你的 middleware 实例的 .name 与某个内置中间件相同,它会原地替换该内置实例,而不是追加。
  • 某些核心中间件(FilesystemMiddleware、SubAgentMiddleware、PatchToolCallsMiddleware)受框架保护,不能通过 excluded_middleware 移除。
  • 上图是默认组装结果。实验性中间件(如 CodeInterpreterMiddleware)不在其中,只有你显式加进 middleware 才会出现。

2.2 核心中间件详解

FilesystemMiddleware

始终存在。提供以下工具:

工具功能
ls(path)列出目录内容
read_file(file_path, offset, limit)读取文件,支持分页和多模态(图片/音频/视频)
write_file(file_path, content)写入文件,已存在的文件直接覆盖(0.7.0 起的行为,此前是报错)
edit_file(file_path, old_string, new_string, replace_all)精确字符串替换,old_string 为空会被拒绝(0.7.14 起)
delete(file_path)删除文件或递归删除目录(0.7.0 新增)
glob(pattern, path)模式匹配查找文件
grep(pattern, path, glob)文本搜索(优先使用 ripgrep)
execute(command, timeout)执行 shell 命令(仅当后端实现 SandboxBackendProtocol)

0.7.0 开始,这套读写工具明显向「弱模型也能稳定用」的方向调过一遍,几个行为值得记住:

  • write_file 覆盖而不报错。以前写同名文件会失败,agent 得先 delete 再写;现在直接覆盖。
  • read_file 分页可续读。返回结果里会报总行数、剩余行数以及下一次该用的 offset,模型不用自己猜还剩多少没读。
  • grep / glob 返回部分结果而不是卡死。在大目录树上超过上限时,结果里带一个 truncated 标记,grep 另有 1000 条匹配上限,并支持流式输出和可选的上下文行。
  • edit_file 拒绝空的 old_string。以前传空串会导致不可预期的替换(0.7.14 修的)。

如果你只想暴露其中一部分工具(比如给 agent 一个只读的文件视图),FilesystemMiddleware 接受工具白名单:

from deepagents.middleware.filesystem import FilesystemMiddleware

fs_mw = FilesystemMiddleware(tools=["ls", "read_file", "glob", "grep"])

agent = create_deep_agent(model=model, middleware=[fs_mw])

它还负责大工具结果的自动转存:

  • 默认 20000 token 以上的工具输出会被转存到文件系统(路径如 /large_tool_results/),上下文中只保留摘要和文件路径。
  • 用户消息超过 50000 token 也会被转存。
  • 转存文件按 tool_call_id 命名;对于没有 ID 的调用,0.7.15 起会生成唯一路径,避免多次转存撞到同一个文件名。

SubAgentMiddleware

有子智能体时存在。提供 task 工具,主 agent 用它委派任务给子 agent。子 agent 拥有完全隔离的上下文窗口——主 agent 只看到子 agent 的最终输出。

SummarizationMiddleware

始终存在。当对话 token 数超过阈值时,自动将旧消息压缩为摘要,完整历史转存到文件系统(路径如 /conversation_history/)。阈值的默认值会根据模型的最大输入 token 自动计算,回退方案是固定保留最近 20 条消息。

PatchToolCallsMiddleware

始终存在。修复消息历史中悬空的工具调用(例如 agent 在中断后恢复、或收到格式错误的 tool-call 参数时),确保消息流的一致性。

SkillsMiddleware

仅当传入 skills 参数时存在。将技能目录中的 SKILL.md 元数据(name + description)注入系统提示词,让 agent 知道有哪些技能可用。Agent 判断当前任务匹配某个技能时,会主动读取该技能的完整指令。

MemoryMiddleware

仅当传入 memory 参数时存在。在 agent 启动时加载指定的 AGENTS.md 文件内容,追加到系统提示词中。这是 DeepAgents 的"启动记忆"机制。

CodeInterpreterMiddleware(Interpreter,beta)

0.6.0 引入,不在默认栈里,需要你显式加进 middleware。官方文档里这块叫 Interpreters——给 agent 一个进程内的可编程工作区:agent 写一段 JavaScript,运行时执行后只把结果返回,中间变量不进入模型上下文。

它解决的是一个很具体的问题:正常的工具调用循环里,一次模型 turn 发出的那批 tool call 是固定的——不能循环、不能根据结果分支、不能重试、也不能把上一个调用的输出喂给下一个,除非再来一轮模型推理,而且每个结果都会回到上下文里。让模型去分派几百个条目本身也不可靠,它往往只抽样一部分。Interpreter 把这类编排从"模型逐步决策"挪到"代码里跑",模型只需要想清楚要做什么。

pip install -U "deepagents[quickjs]"
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware

agent = create_deep_agent(
    model=model,
    middleware=[CodeInterpreterMiddleware()],
)

它给 agent 加了一个 eval 工具,agent 写 JS 调它,你不用手动调。几个要点:

  • 默认零外部能力。QuickJS 环境下拿不到宿主机文件系统、网络、shell、包管理器,连时钟都没有,只能计算、保状态、往 console.log/warn/error 写。要放开只有两条明确的口子:PTC(programmatic tool calling,用 ptc=["web_search"] 给一个工具白名单,代码里通过 tools.webSearch(...) 调)和动态子智能体(有子智能体时自动暴露 task() 全局,可以 Promise.all 并行分派)。
  • 状态持久化由 mode 控制:"thread"(默认,跨 turn 持久,每轮结束写快照)、"turn"(仅一轮内保持)、"call"(每次 eval 全新 REPL)。快照只保留可序列化的数据——函数和类恢复后会变成不可访问的残留物。
  • 隔离是"能力受限"而不是"内存隔离"。QuickJS 跑在同进程里,官方明确说这是 scoped interpreter runtime,不是生产级 sandbox。不可信代码要另外放到独立进程或容器里跑。
  • PTC 不经过常规工具调用路径,所以 interrupt_on 的审批对 PTC 里调用的工具不生效。PTC 白名单本身就是权限边界,别把能碰敏感系统、花钱或改数据的工具放进去。

和 SandboxBackend 的 execute 分工很清楚:sandbox 是"对着环境写代码"(shell、装依赖、跑测试、改文件),interpreter 是"在 agent 循环里写代码"(组合工具、保状态、决定什么回给模型)。

HumanInTheLoopMiddleware

仅当传入 interrupt_on 或 permissions 中有 mode="interrupt" 的规则时存在。在指定的工具调用前暂停执行,返回中断信息,等待人工决策(approve / edit / reject / respond)。

2.3 系统提示词的组装

最终发给模型的 system prompt 不是一个字符串拼两个变量那么简单。完整顺序是:

1. 你传入的 system_prompt            (可选的用户层)
2. 基础 agent 提示词                  (BASE)
3. Memory 提示词                      (仅当传了 memory:AGENTS.md + 使用说明)
4. Skills 提示词                      (仅当传了 skills:技能位置 + 各技能 frontmatter + 用法)
5. 虚拟文件系统提示词                  (含 execute 工具说明,如果用 sandbox 后端)
6. 子智能体提示词                      (task 工具的用法)
7. 你自定义中间件附带的提示词            (如果自定义中间件有)
8. Human-in-the-loop 提示词            (仅当设置了 interrupt_on)

HarnessProfile 则控制其中的 BASE 和 SUFFIX 两个变量:

  • base_system_prompt:整体替换基础提示词。
  • system_prompt_suffix:在末尾追加指令。

大部分时候你只需要关心 system_prompt 参数。BASE 和 SUFFIX 是框架层面为不同模型提供的默认调优,一般不需要动。

注意 system_prompt 是静态的——每次调用都一样。如果你需要动态提示词(比如按用户权限切换「你有管理员权限」和「你只读」,或者从长期记忆里注入用户偏好),要用 @dynamic_prompt 写中间件,在里读 request.runtime.context 和 request.runtime.store。只在工具内部用到 context 时不必写中间件——工具本身就能拿到 ToolRuntime。

值得一提的是 0.7.0 对这块做过一次大幅瘦身,动机是"别把工具的用法重复写两遍"——工具 schema 本身已经说明了怎么用,再往提示词里塞一遍纯属浪费 token。具体做法是:内置的 BASE 提示词默认改为空,同时删掉与工具 schema 重复的说明性文字。效果是默认 agent 的工具描述 token 从 4005 降到 2302(−43%);再叠上空 BASE 和按需开启的 todos,单轮输入的 token 从 5395 降到 1895(−65%)。工具的实际行为没有变化。

如果你在自己的 system_prompt 里也习惯把工具用法写得很细,这一点可以直接借鉴:先看 schema 已经说了什么,再决定提示词里还欠什么。

2.4 HarnessProfile

HarnessProfile 是"每个模型的默认调优配置包"。当你传入一个模型时,框架会自动解析对应的 profile,决定:

  • 额外的中间件(如 Anthropic prompt caching、Bedrock prompt caching)
  • 是否禁用某些内置工具(excluded_tools)
  • 是否排除某些中间件(excluded_middleware)
  • 额外的系统提示词内容

大部分使用场景不需要手动创建 profile。框架会根据模型类型自动选择。

内置 profile 的覆盖范围一直在扩。0.7.0 加入了 NVIDIA Nemotron 3 Ultra 的 profile,同时带上了 NIM 的 app-origin 归因;prompt caching 这一侧也补齐了 Bedrock(走 deepagents[aws] extra)和 Fireworks(自动做缓存会话亲和)。0.7.15 顺手修了两个 profile 相关的小问题:model profile 的 key 现在允许出现冒号,Nemotron 的内置 profile 也要求真正的 task transition 才生效。


第三章:create_deep_agent 完整签名

这是 DeepAgents 最核心的函数,理解了每个参数,就理解了整个框架的入口。

from deepagents import create_deep_agent

agent = create_deep_agent(
    model=...,           # 模型
    tools=...,           # 自定义工具
    system_prompt=...,   # 系统提示词
    middleware=...,      # 额外中间件
    subagents=...,       # 子智能体配置
    skills=...,          # 技能目录路径
    memory=...,          # AGENTS.md 记忆文件路径
    permissions=...,     # 文件系统权限规则
    backend=...,         # 文件系统后端
    interrupt_on=...,    # 人工审批的工具名映射
    response_format=..., # 结构化输出 schema
    state_schema=...,    # 自定义图状态 schema
    context_schema=...,  # 运行时上下文 schema
    checkpointer=...,    # LangGraph checkpointer(持久化)
    store=...,           # LangGraph store(跨会话存储)
    debug=...,           # 调试模式
    name=...,            # 图的名称
    cache=...,           # 缓存
)

3.1 model 参数

接受三种形式:

# 1. 字符串(推荐——使用 provider:model 格式)
agent = create_deep_agent(model="openai:gpt-5.5")

# 2. 预初始化的 LangChain ChatModel 实例
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-5.5", temperature=0)
agent = create_deep_agent(model=model)

# 3. 传入 None(已弃用,默认是 claude-sonnet-4-6)
# 从 0.5.3 开始弃用,将在 1.0.0 中移除

provider:model 字符串由 LangChain 的 init_chat_model 函数解析。支持的 provider 包括:openai, anthropic, google_genai, aws, azure_openai, ollama, fireworks, together, mistral, groq, xai 等。

重要:OpenAI 模型的 Responses API 默认行为

从 DeepAgents 0.6.x 开始,如果你使用 OpenAI 模型,默认走 Responses API(不是 Chat Completions)。这是因为 Responses API 支持 OpenAI 的内置工具(web search、code interpreter 等)。

如果你想强制走 Chat Completions:

from langchain.chat_models import init_chat_model

model = init_chat_model("openai:gpt-5.5", use_responses_api=False)
agent = create_deep_agent(model=model)

如果你想用 Responses API 但禁用服务端数据保留:

model = init_chat_model(
    "openai:gpt-5.5",
    use_responses_api=True,
    store=False,
    include=["reasoning.encrypted_content"],
)
agent = create_deep_agent(model=model)

接入阿里云百炼(DashScope)

百炼提供 OpenAI 兼容端点,走 Chat Completions 协议:

from langchain_openai import ChatOpenAI
from deepagents import create_deep_agent

model = ChatOpenAI(
    model="qwen-plus",  # 或 qwen3-max、qwen-max 等
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

agent = create_deep_agent(model=model, tools=[my_tools])

如果需要百炼的 web_search 等服务端内置工具,有两种策略:

  1. 在 tools 中包装:把百炼 Responses API 的 web_search 调用封装为一个普通 function tool,DeepAgents 通过 tool calling 调用它。
  2. 直接用 Responses API:绕过 DeepAgents,直接调用百炼的 Responses API(失去 DeepAgents 的 harness 能力)。

3.2 tools 参数

传入模型可以调用的自定义工具。这些工具与内置工具合并(additive),不会替换内置工具:

from langchain.tools import tool

@tool
def search_database(query: str) -> str:
    """搜索产品数据库,返回匹配结果"""
    return db.search(query)

@tool
def generate_report(data: dict) -> str:
    """根据数据生成分析报告"""
    return report_gen.run(data)

agent = create_deep_agent(model=model, tools=[search_database, generate_report])

要移除内置工具,需要使用 HarnessProfile 的 excluded_tools 机制,而不是简单地不传。

3.3 system_prompt 参数

你自定义的系统提示词。可以是字符串或 SystemMessage:

agent = create_deep_agent(
    model=model,
    system_prompt="你是一个数据分析助手。在给出结论之前,先用 Python 工具验证数据。",
)

如果传入 SystemMessage,会保留其上的 cache_control 标记(用于 Anthropic prompt caching 的显式断点)。

3.4 response_format 参数

让 agent 的最终输出符合指定的结构化 schema:

from pydantic import BaseModel, Field

class ResearchReport(BaseModel):
    """研究报告的结构化输出"""
    title: str = Field(description="报告标题")
    summary: str = Field(description="核心发现摘要")
    key_findings: list[str] = Field(description="关键发现列表")
    confidence: float = Field(description="置信度 0-1")
    sources: list[str] = Field(description="参考来源")

agent = create_deep_agent(
    model=model,
    tools=[web_search],
    response_format=ResearchReport,
)

result = agent.invoke({"messages": [{"role": "user", "content": "调研 LangGraph 的最新特性"}]})

# 访问结构化输出
report = result["structured_response"]
print(report.title)
print(report.confidence)

response_format 接受:

  • Pydantic BaseModel 子类
  • Python dataclass
  • TypedDict
  • JSON Schema 字典
  • ToolStrategy(schema):通过 tool calling 提取
  • ProviderStrategy(schema):使用 provider 原生结构化输出
  • AutoStrategy(schema):自动选择最佳策略

3.5 checkpointer 与持久化

DeepAgents 底层是 LangGraph,所以天然支持持久化:

from langgraph.checkpoint.memory import MemorySaver

agent = create_deep_agent(
    model=model,
    tools=tools,
    checkpointer=MemorySaver(),  # 内存持久化
)

# 使用 thread_id 区分不同会话
config = {"configurable": {"thread_id": "user-123-session-456"}}
result = agent.invoke({"messages": [...]}, config=config)

生产环境使用 PostgreSQL 或 SQLite:

from langgraph.checkpoint.postgres import PostgresSaver

checkpointer = PostgresSaver.from_conn_string("postgresql://...")
agent = create_deep_agent(model=model, checkpointer=checkpointer)

文件系统(StateBackend)的内容也会随 checkpoint 持久化——同一 thread 内跨多轮对话,文件不会丢失。

DeltaChannel:0.6.0 起的持久化格式变更

从 0.6.0 开始,消息历史和 agent 文件改用 DeltaChannel 存储。原来的做法是每一步都把累积后的全量值重新序列化进 checkpoint;DeltaChannel 只存这一步写入的增量。对长线程来说这是数量级的差别——checkpoint 的大小不再随对话变长而线性膨胀。相应地,可以配 snapshot_frequency=K 每 K 步写一次全量快照,用来给读取延迟封顶。

这里有一条必须知道的兼容性红线:一旦 thread 用 0.6.0+ 持久化过,就不支持回滚。

DeltaChannel 写出的 checkpoint 是新格式,0.6.0 之前的版本读不了。降级时这些 channel 会切回非 delta 实现,而已有的 delta checkpoint 就变成不可读了——症状是状态重建不完整或直接出错,而且不一定当场暴露。

真的需要降级,只有两条路:

  1. 先用 delta-channel-dump 恢复脚本把受影响的 thread 迁移掉;
  2. 或者直接丢弃这些 thread。

推广来说:不要让同一个 channel 在 delta 和非 delta 两种表示之间来回切换。升级前先想清楚要不要留退路,留退路就在升级前把 thread 导出。


第四章:文件系统与后端

DeepAgents 内置的文件系统是一个虚拟文件系统(VFS)。它不是直接操作你的操作系统文件,而是通过**后端协议(backend protocol)**抽象,让文件操作落到不同的存储介质上。

4.1 内置工具

工具名签名说明
lsls(path: str) -> list[str]列出目录内容
read_fileread_file(file_path: str, offset: int = 0, limit: int = 500) -> str读取文件内容,支持偏移和限制
write_filewrite_file(file_path: str, content: str) -> str写入文件,已存在则直接覆盖
edit_fileedit_file(file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> str精确字符串替换
deletedelete(file_path: str) -> str删除文件(需要后端支持删除操作)
globglob(pattern: str, path: str = "/") -> list[str]模式匹配查找
grepgrep(pattern: str, path: str = "/", glob: str = None) -> list[str]内容搜索(优先使用 ripgrep)
executeexecute(command: str, timeout: int = 300) -> str执行 shell 命令(仅当后端实现 SandboxBackendProtocol)

读类工具在 0.7.0 之后都改成了「宁可返回部分结果,也不要卡住」的语义,这是专门为开源模型调过的——弱模型容易在大目录树上触发一次巨量 grep,然后整轮就废了:

  • read_file 会告诉你总共多少行、还剩多少行,以及下一次该传的 offset,模型可以稳定地一页页往下读。
  • grep / glob 命中过多时返回带 truncated 标记的部分结果;grep 另有 1000 条匹配上限,支持流式输出和可选上下文行。
  • glob 在路径不合理时会主动提示该往哪个 path 下找(0.7.14 起)。
  • 顺带一提,edit_file 从上到下按 old_string 精确匹配,传空串会被直接拒绝。

4.2 后端协议

DeepAgents 的文件后端遵循 BackendProtocol。核心方法是:

class BackendProtocol(Protocol):
    def ls(self, path: str) -> list[str]: ...
    def read(self, file_path: str, offset: int = None, limit: int = None) -> str: ...
    def write(self, file_path: str, content: str) -> None: ...
    def edit(self, file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> None: ...
    def delete(self, file_path: str) -> None: ...
    def glob(self, pattern: str, path: str = "/") -> list[str]: ...
    def grep(self, pattern: str, path: str = "/", glob: str = None) -> list[str]: ...

如果后端实现了 SandboxBackendProtocol(继承自 BackendProtocol,额外要求 execute 方法),就会额外暴露 execute 工具。

4.3 StateBackend(默认)

StateBackend 是默认后端。文件内容直接存储在 LangGraph 的 state 中:

# 不传 backend 参数,默认使用 StateBackend
agent = create_deep_agent(model=model, tools=tools)

# 文件存在 state["files"] 字典中,key 是路径,value 是内容字符串
result = agent.invoke({"messages": [{"role": "user", "content": "写一个 hello.py"}]})
print(result["files"])  # {"/hello.py": "print('hello world')"}

特点:

  • 文件随 checkpoint 持久化,同一 thread 内跨会话不丢失。
  • 跨 thread 不共享(每个 thread 有独立的 state)。
  • 无需外部依赖。
  • 适合快速原型和不需要真正落盘的场景。

4.4 CompositeBackend

CompositeBackend 允许你把不同路径前缀路由到不同后端,实现"混合存储":

from deepagents.backends import CompositeBackend, StateBackend, StoreBackend, SandboxBackend

backend = CompositeBackend(
    default=StateBackend(),
    routes={
        "/workspace": StateBackend(),
        "/artifacts": StoreBackend(store=my_store, namespace=("artifacts",)),
        "/memory": StoreBackend(store=my_store, namespace=("agent_memory",)),
        "/sandbox": SandboxBackend(root_dir="/tmp/agent_sandbox", timeout=60),
        "/data": CompositeBackend.FilesystemBackend(root_dir="/path/to/shared_data"),
    },
)

agent = create_deep_agent(model=model, backend=backend)

4.5 SandboxBackend

SandboxBackend 允许 agent 在一个受控的目录中执行 shell 命令:

from deepagents import create_deep_agent
from deepagents.backends import SandboxBackend

backend = SandboxBackend(
    root_dir="/tmp/agent_workspace",
    timeout=120,          # 命令超时(秒)
    max_output_size=50000,
)

agent = create_deep_agent(model=model, backend=backend)

# agent 可以调用 execute("pip install requests") 或 execute("python analysis.py")

实现 SandboxBackendProtocol 后,execute 工具会自动暴露给 agent。

4.6 FilesystemBackend(本地真实文件系统)

如果你想让 agent 直接操作本地磁盘(例如代码编辑、数据处理),可以使用 FilesystemBackend:

from deepagents.backends.filesystem import FilesystemBackend

backend = FilesystemBackend(root_dir="/Users/zata/code/my-project")

agent = create_deep_agent(model=model, backend=backend)

# agent 可以 read_file / write_file / edit_file / glob / grep 操作真实文件

4.7 StoreBackend(跨会话存储)

StoreBackend 利用 LangGraph 的 Store 接口,将文件内容存储在一个跨 thread 共享的键值存储中:

from langgraph.store.memory import InMemoryStore
from deepagents.backends import StoreBackend

store = InMemoryStore()
backend = StoreBackend(store=store, namespace=("shared_files",))

agent = create_deep_agent(model=model, backend=backend)

# 文件在不同 thread 之间共享(只要使用同一个 store)

生产环境搭配 PostgreSQL Store:

from langgraph.store.postgres import PostgresStore

store = PostgresStore.from_conn_string("postgresql://...")
backend = StoreBackend(store=store, namespace=("knowledge_base",))
agent = create_deep_agent(model=model, backend=backend)

ContextHubBackend:用 LangSmith Hub 当存储(0.6.0 新增)

如果你已经在用 LangSmith,ContextHubBackend 省掉了单独部署一个 LangGraph store 的麻烦——它把 agent 的文件(技能、记忆、其他需要持久化的上下文)直接存成 LangSmith Hub 上的 commit。

from deepagents.backends import ContextHubBackend

backend = ContextHubBackend()  # 走 LangSmith 凭据
agent = create_deep_agent(model=model, backend=backend)

它真正的价值在每次写入都带版本历史:skill 和 memory 这类文件最需要的就是「改错了能回退、能看出是谁在什么时候改的」,而这些能力在 Hub 上是现成的。代价是绑定了 LangSmith,纯本地或自托管场景还是用 StoreBackend。

4.8 权限控制

通过 permissions 参数精细控制文件系统访问:

agent = create_deep_agent(
    model=model,
    backend=backend,
    permissions=[
        {"path": "/workspace/*", "operations": ["read", "write", "edit"], "mode": "allow"},
        {"path": "/memory/*", "operations": ["read", "write"], "mode": "allow"},
        {"path": "/system/*", "mode": "deny"},
        {"path": "/*", "operations": ["read"], "mode": "allow"},
        {"path": "/*", "operations": ["write", "edit", "delete"], "mode": "interrupt"},
    ],
)

权限模式:

模式行为
allow直接放行
deny直接拒绝,返回错误信息给 agent
interrupt暂停执行,等待人工审批

权限规则按顺序匹配,第一条匹配的规则生效。


第五章:子智能体

子智能体是 DeepAgents 最有特色的能力之一。核心思想:主 agent 通过 task 工具把一个任务委派给一个拥有独立上下文窗口的子 agent。主 agent 只看到子 agent 的最终输出,不会看到子 agent 的中间过程。

这解决了长任务中的关键问题:上下文污染。当主 agent 执行搜索、阅读、分析等中间步骤时,会产生大量 token。如果这些 token 都堆在主 agent 的上下文里,很快就会超出限制或降低推理质量。子智能体隔离了这些噪音。

5.1 预配置子智能体

from deepagents import create_deep_agent
from deepagents.subagents import SubAgent

researcher = SubAgent(
    name="research-agent",
    description="负责搜索和整理信息,输出结构化研究报告",
    system_prompt="你是一个信息研究员。用 web_search 和 read_file 工具完成调研任务。",
    model="openai:gpt-5.5",  # 子智能体可以用不同模型
    tools=[web_search],
)

analyst = SubAgent(
    name="data-analyst",
    description="负责数据分析和可视化",
    system_prompt="你是一个数据分析师。用 Python 分析数据,生成报告。",
    model="anthropic:claude-sonnet-4-6",
    tools=[execute_python, read_file],
)

agent = create_deep_agent(
    model="openai:gpt-5.5",
    subagents=[researcher, analyst],
    system_prompt="你是一个团队负责人。用 task 工具委派任务给子智能体。",
)

当主 agent 判断需要调研时,会调用:

{
  "tool": "task",
  "args": {
    "description": "调研 LangGraph 最新特性并输出报告",
    "subagent_type": "research-agent"
  }
}

5.2 自定义子智能体(传入编译后的 LangGraph)

任何 LangGraph CompiledStateGraph 都可以作为子智能体传入:

from langgraph.graph import StateGraph, MessagesState, END

# 构建一个自定义图
workflow = StateGraph(MessagesState)
workflow.add_node("analyze", analyze_node)
workflow.add_node("report", report_node)
workflow.set_entry_point("analyze")
workflow.add_edge("analyze", "report")
workflow.add_edge("report", END)
custom_graph = workflow.compile()

agent = create_deep_agent(
    model=model,
    subagents=[
        {
            "name": "custom-analyst",
            "description": "自定义分析管道",
            "graph": custom_graph,
        }
    ],
)

5.3 主/子上下文隔离原理

主 Agent 上下文                          子 Agent 上下文
┌─────────────────────┐                ┌─────────────────────────────┐
│ system prompt       │                │ 子智能体的 system prompt     │
│ user message        │                │ task description (作为输入) │
│ ... 历史消息 ...     │                │ 子智能体的工具和文件系统      │
│ tool: task(...)     │──委派──────────→│                             │
│                     │                │  (子 agent 内部循环)         │
│                     │←───────────────│ 最终输出(文本摘要)         │
│ tool result         │                └─────────────────────────────┘
│ ... 继续 ...        │
└─────────────────────┘
  • 子 agent 看不到主 agent 的对话历史,只看到任务描述。
  • 主 agent 看不到子 agent 的中间 tool calls、搜索结果、文件内容。
  • 子 agent 的文件系统与主 agent 共享(默认使用同一个 backend)。
  • 如果子 agent 需要向主 agent 传递大文件,通过文件系统写入,主 agent 用 read_file 读取。

5.4 什么时候用子智能体

场景是否需要子智能体
简单搜索 + 回答不需要,一个 agent + web_search 就够
多步调研:搜索多个主题、阅读多个来源、综合报告需要——用 researcher 子智能体隔离搜索噪音
多文件代码修改需要——用 coder 子智能体隔离文件操作
多源数据采集和分析需要——不同子智能体处理不同数据源
单一任务,不需要委派不需要,直接用主 agent

5.5 异步子智能体:并行委派

DeepAgents 同时提供同步和异步两套接口。当需要并行处理多个独立任务时,可以在异步运行环境中用 asyncio.gather 同时发起多个调用,每个子智能体在自己的隔离上下文中工作,主流程只汇总最终结果。

import asyncio

# 同一 agent 的多个独立任务并行执行
results = await asyncio.gather(
    agent.ainvoke({"messages": [("user", "研究主题 A")]}, config),
    agent.ainvoke({"messages": [("user", "研究主题 B")]}, config),
)

如果子智能体内部执行的是 I/O 密集操作(网络搜索、文件读写),并行能显著缩短总耗时。注意每个并行调用会消耗独立的模型调用和 token 配额;asyncio.gather 默认在任一任务异常时取消全部,生产代码建议加 return_exceptions=True 后逐个检查。

5.6 多级子智能体

子智能体的 subagents 字段可以继续传入子智能体配置,形成"主 agent → 一级子 agent → 二级子 agent"的层级结构。每一层都有独立的上下文窗口,适合大型项目中"项目经理 → 领域专家 → 执行者"的分工模式。

deep_researcher = SubAgent(
    name="deep-researcher",
    model="openai:gpt-5.5",
    system_prompt="深入研究并写报告",
    subagents={
        "subagents": [
            SubAgent(
                name="fact-checker",
                model="openai:gpt-5.5",
                system_prompt="验证研究结论中的关键事实",
            ),
        ],
    },
)

层级不宜过深(建议不超过 2-3 层):每加一层,主 agent 看到的信息就更间接,且 token 消耗按乘数增长。


第六章:上下文管理

DeepAgents 内置了两层上下文管理机制:摘要压缩和大工具输出转存。

6.1 SummarizationMiddleware:对话摘要压缩

当对话的 token 数超过阈值时,SummarizationMiddleware 会:

  1. 将旧消息替换为一条摘要消息
  2. 将完整的旧消息转存到文件系统(路径如 /conversation_history/{message_id}.json)
  3. 保留最近的消息在上下文中
压缩前:
[sys] [msg1] [msg2] [msg3] [msg4] [msg5] ... [msgN]
       ↑ 全部保留在上下文中

压缩后:
[sys] [摘要消息(包含历史概要+文件路径)] [msgN-2] [msgN-1] [msgN]
                              ↑ 完整历史在 /conversation_history/

阈值配置:

from deepagents import create_deep_agent
from deepagents.middleware.summarization import SummarizationMiddleware

agent = create_deep_agent(
    model=model,
    tools=tools,
    middleware=[
        SummarizationMiddleware(
            token_threshold=100000,   # 超过 100k token 触发压缩
            keep_messages=10,          # 保留最近 10 条消息
        )
    ],
)

阈值默认值根据模型的最大输入 token 自动计算(官方口径是达到模型上下文窗口的约 85%)。例如:

  • GPT-5(128k context)→ 阈值约 100k token
  • Claude(200k context)→ 阈值约 160k token
  • 回退方案:保留最近 20 条消息

按需压缩:让 agent 自己决定什么时候压

自动摘要只在逼近阈值时才动手,但有些场景你想让它主动在任务边界处压缩——比如一个子任务刚交付完,历史已经没用但离阈值还远。

加一个 compact_conversation 工具就能做到:

from deepagents import create_deep_agent
from deepagents.backends import StateBackend
from deepagents.middleware.summarization import create_summarization_tool_middleware

backend = StateBackend  # 用默认后端时这样写

agent = create_deep_agent(
    model=model,
    middleware=[create_summarization_tool_middleware(model, backend)],
)

注意这不是替代品:加上它并不会关掉 85% 的自动压缩,两者共用同一个摘要引擎和状态。自定义中间件会被插到 PatchToolCallsMiddleware 之后(也就是 2.1 节栈里的位置 7)。

6.2 大工具输出转存

FilesystemMiddleware 监控所有工具的输出。当输出超过阈值(默认 20000 token)时:

  1. 将完整输出写入文件系统(路径如 /large_tool_results/{tool_call_id}.txt)
  2. 在上下文中只保留摘要消息:[输出过大,已转存到 /large_tool_results/xxx.txt,请用 read_file 读取]
# 你可以自定义阈值
from deepagents.middleware.filesystem import FilesystemMiddleware

fs_mw = FilesystemMiddleware(
    large_output_threshold=10000,  # 10000 token 超限
    large_output_dir="/my_tool_results",
)

agent = create_deep_agent(model=model, middleware=[fs_mw])

6.3 记忆与跨会话

通过 memory 参数加载 AGENTS.md:

agent = create_deep_agent(
    model=model,
    tools=tools,
    memory="/path/to/AGENTS.md",  # 启动时注入到系统提示词
)

对于需要动态更新的长期记忆,使用 StoreBackend:

from deepagents.backends import StoreBackend
from langgraph.store.memory import InMemoryStore

store = InMemoryStore()
memory_backend = StoreBackend(store=store, namespace=("agent_memory",))

agent = create_deep_agent(
    model=model,
    backend=memory_backend,
    system_prompt="你是一个有记忆的助手。在 /memory 目录中维护长期记忆。",
)

Agent 可以通过文件工具读写 /memory/ 目录,实现跨会话的记忆更新。


第七章:Skills(技能系统)

Skills 是 DeepAgents 的渐进式披露机制——将复杂的指令和工具组合打包成可复用的模块,按需加载。

7.1 基本用法

每个技能是一个目录,包含一个 SKILL.md 文件:

my_skills/
├── code-review/
│   └── SKILL.md
├── data-viz/
│   ├── SKILL.md
│   └── templates/
│       └── chart.py
└── web-research/
    └── SKILL.md

SKILL.md 的格式:

---
name: code-review
description: 对代码库进行系统性审查,检查安全性、性能、可维护性
---

## 步骤

1. 读取目标文件列表(用 glob 工具)
2. 逐一读取关键文件
3. 检查以下方面:
   - 安全性:SQL注入、XSS、不安全的反序列化
   - 性能:N+1 查询、内存泄漏、不必要的全表扫描
   - 可维护性:函数长度、重复代码、缺失的类型注解
4. 用 write_file 工具输出审查报告到 /reports/code-review-{date}.md

## 工具

优先使用 grep 搜索特定模式,用 read_file 读取上下文。
agent = create_deep_agent(
    model=model,
    tools=tools,
    skills="/path/to/my_skills",
)

SkillsMiddleware 会将每个技能的 name 和 description 注入系统提示词。Agent 判断当前任务匹配某个技能时,会主动 read_file 读取该技能的完整指令。

7.2 为什么是"渐进式披露"

如果所有技能的完整内容都塞在系统提示词里,token 浪费严重。渐进式披露的设计是:

  • 系统提示词只包含元数据(name + description),几十个 token 每技能。
  • 完整指令在文件系统中,agent 按需读取。
  • 10 个技能的元数据 ≈ 300 token,比全量内容(可能几千 token)节省 90% 以上。

7.3 带资源的技能

技能目录中可以包含额外文件(模板、脚本、配置等),agent 可以通过文件系统工具访问:

my_skills/
└── report-generator/
    ├── SKILL.md
    ├── templates/
    │   ├── report_template.md
    │   └── chart_template.py
    └── config.yaml

Agent 读取 SKILL.md 后,可以根据指令进一步读取 templates/ 中的文件。

7.4 社区技能示例:研究评估评分标准

官方仓库提供了多个可复用的技能示例。下面是一个"研究评估"技能(rubrics 模式)的典型结构——把评审专家的评分维度固化成可复用的指令文件:

---
name: evaluate-research
description: 用评分标准评估研究报告质量
---

## 评分标准

按以下维度评估研究报告(每项 1-5 分):

1. **事实准确性**:关键事实是否有可靠来源支撑
2. **逻辑连贯性**:结论是否从证据自然推出
3. **完整性**:是否遗漏了重要角度
4. **可操作性**:读者能否据此做出决策

## 流程

1. 阅读研究报告全文
2. 逐项打分并给出具体依据
3. 汇总总分和改进建议

将此文件放入技能目录后,agent 在收到"评估报告"类任务时会自动加载该技能。你可以基于自己的领域编写类似的评分模板——技能系统的本质就是把"怎么做"的专家知识封装成可复用的指令文件。


第八章:Human-in-the-Loop(人机协作)

DeepAgents 允许在工具调用前暂停,等待人工决策。这在生产环境中至关重要——你不希望 agent 在没有人工审核的情况下执行危险操作(删除文件、发送邮件、执行数据库变更等)。

8.1 interrupt_on 参数

agent = create_deep_agent(
    model=model,
    tools=[send_email, delete_record, web_search, write_file],
    interrupt_on={
        "send_email": {"allowed_decisions": ["approve", "edit", "reject"]},
        "delete_record": {"allowed_decisions": ["approve", "reject"]},
    },
)

# 第一次 invoke 会触发中断
result = agent.invoke({"messages": [...]})

# 检查是否中断
if result["__interrupt__"]:
    # 获取中断信息
    interrupt_info = result["__interrupt__"][0]
    print(f"工具: {interrupt_info.value['action_request']['action']}")
    print(f"参数: {interrupt_info.value['action_request']['args']}")

    # 人工决策
    human_decision = interrupt_info.value["resume"]
    # 或者用 resume 重新启动
    resumed = agent.invoke(
        {"__interrupt__": [{"resume": {"type": "accept"}}]},
        config=config,
    )

8.2 通过 permissions 实现文件操作审批

agent = create_deep_agent(
    model=model,
    backend=backend,
    permissions=[
        {"path": "/workspace/*", "mode": "allow"},
        {"path": "/*", "operations": ["write", "edit", "delete"], "mode": "interrupt"},
    ],
)

所有 workspace 外的写操作都会触发人工审批。

8.3 中断恢复协议

DeepAgents 的 HITL 基于 LangGraph 的 interrupt 机制。中断后:

  1. 图的状态(包括 messages、files)已通过 checkpointer 保存。
  2. agent.invoke() 返回 __interrupt__ 信息。
  3. 人工决策通过 agent.invoke() resume:
config = {"configurable": {"thread_id": "session-1"}}

# 第一次调用,触发中断
result1 = agent.invoke({"messages": [...]}, config=config)

# 检查中断并恢复
if "__interrupt__" in result1:
    decision = input("批准/编辑/拒绝?(approve/edit/reject): ")
    result2 = agent.invoke(
        Command(resume={"type": decision}),
        config=config,
    )

支持的决策类型:

决策行为
{"type": "accept"}批准,继续执行工具调用
{"type": "edit", "args": {...}}编辑参数后继续执行
{"type": "reject", "message": "..."}拒绝,将拒绝理由传回给 agent
{"type": "respond", "message": "..."}不执行工具,向 agent 提供额外信息

第九章:流式输出与中间过程

DeepAgents 基于 LangGraph,支持多种流式模式:

9.1 流式 token

from langchain_core.messages import AIMessageChunk

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

for event in agent.stream(
    {"messages": [{"role": "user", "content": "分析这份数据"}]},
    config=config,
    stream_mode="messages",
):
    # event 是 (message_chunk, metadata) 元组
    msg_chunk, metadata = event
    if hasattr(msg_chunk, "content") and isinstance(msg_chunk.content, str):
        print(msg_chunk.content, end="", flush=True)

9.2 流式工具调用

for event in agent.stream(
    input_data,
    config=config,
    stream_mode="updates",
):
    # 每个节点的更新
    for node_name, node_update in event.items():
        print(f"节点: {node_name}")
        if "messages" in node_update:
            for msg in node_update["messages"]:
                print(f"  {msg.type}: {msg.content[:100] if isinstance(msg.content, str) else msg.content}")

9.3 流式文件操作

通过 stream_mode="updates",可以看到文件系统的变化:

for event in agent.stream(
    input_data,
    config=config,
    stream_mode="updates",
):
    for node_name, node_update in event.items():
        if "files" in node_update:
            print(f"文件变化: {node_update['files'].keys()}")

9.4 事件流(v3):stream_events 与类型化投影

上面几种 stream_mode 是 LangGraph 的原始流式接口——你拿到的是 dict,要自己判断这一块是什么。LangGraph 1.2.0 / DeepAgents 0.6.0 起多了一套更适合应用层写代码的东西:事件流(event streaming),传 version="v3" 开启。

核心变化是:一次 run 产出的事件流被归一化后,经过一层 transformer 管线,暴露成几个带类型的投影,可以同时被多个消费者读——读 stream.messages 不会消耗 stream.values 需要的事件。

stream = agent.stream_events(
    {"messages": [{"role": "user", "content": "分析这份数据"}]},
    version="v3",
)

# 只看模型输出:token 级
for message in stream.messages:
    print(str(message.text), end="", flush=True)

# 最终状态
final_state = stream.output

可用的投影:

投影用途
stream遍历所有协议事件(原始层)
stream.messages模型消息与 token 增量,另有 .reasoning 和 .tool_calls
stream.values每一步之后的状态快照,stream.output 拿最终值
stream.subgraphs发现并观察嵌套图/子智能体执行
stream.interrupts / stream.interrupted人机协作中断的载荷与判定
stream.extensions自定义 transformer 的投影

对 DeepAgents 来说,stream.subgraphs 特别有用——子智能体的执行会作为嵌套作用域出现在这里,直接看 subgraph.graph_name 和 subgraph.path 就行,不用去解析 namespace 字符串:

stream = agent.stream_events(input_data, config=config, version="v3")

for subgraph in stream.subgraphs:
    print(subgraph.graph_name, subgraph.path)
    for message in subgraph.messages:
        print(message.text)

异步场景下可以并发消费多个投影:

stream = await agent.astream_events(input_data, version="v3")

async def consume_messages():
    async for message in stream.messages:
        print(f"[llm] node={message.node}")

async def consume_subgraphs():
    async for subgraph in stream.subgraphs:
        print(f"[subgraph] path={subgraph.path}")

await asyncio.gather(consume_messages(), consume_subgraphs())

同步代码想在严格的到达顺序里混着读多个投影,用 stream.interleave(...):

for name, item in stream.interleave("values", "messages", "subgraphs"):
    if name == "values":
        print(f"[state] keys={list(item)}")
    elif name == "messages":
        print(f"[llm] node={item.node}")
    elif name == "subgraphs":
        print(f"[subgraph] path={item.path}")

底层通道(event["method"])有 values、updates、messages、tools、lifecycle、checkpoints、input、tasks、custom。其中 messages 是按 content block 建模的,事件序列固定为 message-start → content-block-start → content-block-delta → content-block-finish → message-finish,文本、推理、工具调用、多模态内容都有显式边界,不再依赖各家 provider 的格式。

v1 和 v2 的写法保持兼容,没有破坏性变更——现有代码不用动,新代码建议直接从 v3 起步。


第十章:生产部署

10.1 部署选项

方案适用场景
LangGraph Platform托管部署,LangSmith 集成,自动扩缩
Docker + 自建需要完全控制的私有化部署
Serverless(Vercel/Modal/Cloud Run)事件驱动、低流量场景
LangSmith Deployment与 LangSmith 评估和监控深度集成

10.2 Docker 部署示例

FROM python:3.12-slim
WORKDIR /app

# 安装依赖
COPY pyproject.toml uv.lock ./
RUN pip install uv && uv sync --frozen

# 复制代码
COPY . .

# 暴露端口
EXPOSE 8000

# 启动(LangGraph serve)
CMD ["uv", "run", "langgraph", "serve", "--host", "0.0.0.0", "--port", "8000"]

10.3 环境变量

# 模型 API Key
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-...

# LangSmith(可选,但强烈推荐)
LANGSMITH_TRACING=true
LANGSMITH_API_KEY=lsv2_...
LANGSMITH_PROJECT=my-agent

# 数据库(生产 checkpointer)
DATABASE_URL=postgresql://user:pass@host:5432/db

10.4 安全模型

DeepAgents 遵循"信任 LLM"模型。Agent 可以做任何工具允许的事情。安全边界必须在你这一侧强制执行:

  • 后端层面:使用 SandboxBackend 或 FilesystemBackend 的 root_dir 限制 agent 能访问的目录。
  • 权限层面:使用 permissions 参数控制读写和删除操作。
  • 工具层面:不要暴露你不想让 agent 执行的工具。
  • HITL 层面:对危险操作(删除、发送、支付)设置 interrupt_on。
  • 网络层面:如果 agent 有 web 访问能力,确保它不能访问内网端点。

10.5 可观测性

LangSmith 集成:

export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=your_key
export LANGSMITH_PROJECT=production-agent

设置环境变量后,所有 agent 调用自动产生 trace。无需改代码。

10.6 评测

from langsmith import Client

client = Client()

# 创建评测数据集
dataset = client.create_dataset("deep-agents-eval")
client.create_examples(
    dataset_id=dataset.id,
    inputs=[
        {"messages": [{"role": "user", "content": "调研 X 并写报告"}]},
        {"messages": [{"role": "user", "content": "修复 bug #123"}]},
    ],
)

# 运行评测
client.evaluate(
    agent,
    data=dataset.id,
    evaluators=[
        # 自定义评估器
        lambda outputs, reference: {"accuracy": compute_match(outputs, reference)},
    ],
)

第十一章:实战模式

11.1 研究智能体

from deepagents import create_deep_agent
from deepagents.subagents import SubAgent
from langchain.tools import tool

@tool
def web_search(query: str) -> str:
    """搜索互联网获取信息"""
    # 接入 Tavily / Bocha / 百炼 WebSearch MCP
    ...

@tool
def web_extract(url: str) -> str:
    """抽取网页正文内容"""
    ...

research_subagent = SubAgent(
    name="researcher",
    description="搜索和整理信息",
    system_prompt="你是一个信息研究员。用 web_search 和 web_extract 完成调研。",
    model="openai:gpt-5.5",
    tools=[web_search, web_extract],
)

agent = create_deep_agent(
    model="openai:gpt-5.5",
    subagents=[research_subagent],
    system_prompt="""你是一个研究助手。
    收到调研任务后,用 task 工具委派给 researcher 子智能体。
    基于子智能体的输出,综合并生成最终回答。""",
)

11.2 代码智能体

from deepagents.backends.filesystem import FilesystemBackend

backend = FilesystemBackend(root_dir="/path/to/repo")

agent = create_deep_agent(
    model="anthropic:claude-sonnet-4-6",
    backend=backend,
    system_prompt="""你是一个代码编辑助手。
    先用 glob 和 grep 了解代码库结构。
    修改代码前先 read_file 阅读上下文。
    修改后用 execute 工具运行测试验证。
    修改必须最小化,不要过度工程化。""",
)

11.3 数据分析智能体

from deepagents.backends import SandboxBackend

backend = SandboxBackend(
    root_dir="/tmp/data_sandbox",
    timeout=300,
)

agent = create_deep_agent(
    model="openai:gpt-5.5",
    backend=backend,
    system_prompt="""你是一个数据分析师。
    先用 execute("ls /") 查看数据文件。
    用 execute("python -c \"...\\"") 或 execute("head -n 100 data.csv") 探索数据。
    用 execute("python analysis.py") 执行分析脚本。
    用 write_file 生成分析报告。""",
)

11.4 混合模式:DeepAgents + 百炼 Responses API

当你需要 DeepAgents 的 harness 能力 + 百炼的 web_search:

import os
from openai import OpenAI
from deepagents import create_deep_agent
from langchain_openai import ChatOpenAI

# 百炼 Responses API 客户端(用于 web_search)
bailian = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

@tool
def bailian_web_search(query: str) -> str:
    """搜索互联网获取最新信息(使用百炼 web_search)"""
    response = bailian.responses.create(
        model="qwen3-max",
        input=query,
        tools=[{"type": "web_search"}],
    )
    # 提取搜索结果
    results = []
    for item in response.output:
        if getattr(item, "type", None) == "web_search_call":
            results.append(str(item))
        elif getattr(item, "type", None) == "message":
            for content in getattr(item, "content", []):
                if hasattr(content, "text"):
                    results.append(content.text)
    return "\n".join(results)

# DeepAgents 走 Chat Completions
model = ChatOpenAI(
    model="qwen-plus",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

agent = create_deep_agent(
    model=model,
    tools=[bailian_web_search],
    system_prompt="你是一个研究助手。用 bailian_web_search 查询最新信息。",
)

第十二章:协议对比总结

12.0 Deep Agents Code(dcode)

除了可编程的 Python/JS 库,LangChain 还提供了 Deep Agents Code(dcode)——一个预构建的终端编码智能体,定位类似 Claude Code 或 Cursor CLI,底层就是 DeepAgents harness。它让你不用写代码就能体验文件系统、子智能体、上下文管理等全部能力。

# 一键安装
curl -LsSf https://langch.in/dcode | bash

# 在项目目录中启动
cd your-project
dcode

dcode 使用任何支持 tool calling 的 LLM 作为后端。对于想先"用"再"编"的用户,建议先试 dcode 感受 harness 行为,再用 create_deep_agent 定制自己的流程。

dcode 迭代很快(几乎每周发版),近期(0.1.67 / 0.1.68)值得关注的更新:

  • 工作区隔离增强:项目策略按 workspace 解析、dotenv 按 workspace 作用域隔离、LangSmith 认证与 tracing 隔离;跨工作区切换时复用 server 并在重启前请求确认。
  • 模型支持:新增 Fireworks GLM-5.3 / GLM-5.3 Flash 的用量计价,推荐模型列表纳入 GLM 5.3 系列。
  • subagent 行为:general-purpose 子智能体默认以 fork 模式运行(继承完整上下文),并支持 fresh subagent 与按任务选择工具。

官方文档:https://docs.langchain.com/deepagents-code

12.1 DeepAgents 的模型协议

DeepAgents 对模型的唯一要求是 tool calling。它不关心底层是 Chat Completions 还是 Responses API,只要 LangChain ChatModel 能完成以下循环:

发送 [system, user, assistant(tool_calls), tool(results)] → 模型返回 tool_calls 或 text → 循环
  • Chat Completions 兼容端点(OpenAI、DashScope、Ollama、vLLM):直接用 ChatOpenAI。
  • Responses API(OpenAI / 百炼):通过 init_chat_model("openai:...", use_responses_api=True)。
  • Anthropic:直接用 ChatAnthropic 或 "anthropic:claude-sonnet-4-6"。
  • 本地模型:ChatOllama 或 ChatVLLM。

12.2 与百炼 Responses API 的关系

维度DeepAgents + Chat Completions百炼 Responses API
协议Chat Completions(messages)Responses(input)
工具调用客户端执行(LangChain tool calling)服务端内置(web_search 等)
Agent 循环DeepAgents 框架管你自己管
搜索需要自己包装工具服务端内置
文件系统内置无
子智能体内置无
上下文管理内置无
HITL内置无

12.3 选型决策树

需要 DeepAgents 的 harness 能力?
├─ 是 → 需要 web_search?
│   ├─ 是 → 自己包装搜索为 function tool(推荐)
│   │       或混合模式(DeepAgents + 百炼 Responses API)
│   └─ 否 → 直接用 DeepAgents + Chat Completions
└─ 否 → 需要服务端内置工具(web_search 等)?
    ├─ 是 → 直接用百炼 Responses API
└─ 否 → LangChain create_agent / LangGraph

12.4 RubricMiddleware:自评迭代循环

DeepAgents 内置了 RubricMiddleware,让你用"评分标准"定义什么叫做完成。当 agent 准备结束回复时,中间件会调用一个独立的"评分器"子智能体,根据 rubric 逐条检查 agent 的输出。如果评分器返回 needs_revision,其反馈会作为 HumanMessage 注入上下文,agent 循环继续——直到评分器返回 satisfied、failed 或达到最大迭代次数。

from deepagents import create_deep_agent
from deepagents.middleware import RubricMiddleware

rubric_mw = RubricMiddleware(
    rubric=[
        {"criterion": "所有关键论断都有来源引用", "weight": 3},
        {"criterion": "结论与证据逻辑一致", "weight": 2},
        {"criterion": "无事实错误", "weight": 3},
    ],
    grader_model="openai:gpt-5.5",
    max_iterations=3,
)

agent = create_deep_agent(
    model="openai:gpt-5.5",
    middleware=[rubric_mw],
    system_prompt="你是一个研究助手,写完报告后自我检查再提交。",
)

这个模式非常适合对输出质量有硬性要求的场景(合规文档、技术报告、代码审查),把"人工再审一遍"变成可编程的自动质量门。

12.5 官方示例集导读

GitHub 仓库的 examples/ 目录包含十多个端到端案例,按学习优先级推荐如下:

示例亮点
content-builder-agent用 Memory + Skills + Subagents 三原语构建内容写作 agent,最适合入门
deep_research完整的深度研究 agent(多轮搜索 + 报告生成),展示实际 token 管理
text-to-sql-agent自然语言 → SQL,展示 planning + filesystem + subagent 组合
rubric_middlewareRubricMiddleware + LangSmith trace,展示自评迭代全流程
async-subagent-server异步子智能体 + 服务端部署,生产级 FastAPI 集成
ralph_modeRalph 自主循环模式(Geoff Huntley),while :; do agent; done 的 DeepAgents 实现
better-harness用一个 DeepAgent 优化另一个 agent harness 的元循环(harness engineering)

建议先跑 content-builder-agent 理解三原语(Memory / Skills / Subagents),再按需深入其他案例。


附录

A. 常用环境变量

变量说明
OPENAI_API_KEYOpenAI API key
ANTHROPIC_API_KEYAnthropic API key
DASHSCOPE_API_KEY阿里云百炼 API key
LANGSMITH_TRACING启用 LangSmith trace
LANGSMITH_API_KEYLangSmith API key
LANGSMITH_PROJECTLangSmith 项目名
LANGCHAIN_TRACING_V2启用 LangSmith trace(旧变量名)
LANGCHAIN_API_KEYLangSmith API key(旧变量名)
LANGCHAIN_PROJECTLangSmith 项目名(旧变量名)

B. 版本注意事项

  • deepagents 0.7.x 是当前最新版本系列(最新 0.7.15,2026-09-16),尚未发布 0.8 或 1.0。
  • 0.5.x → 0.6.x 有重大变化:SubAgent 类型变化、response_format 新增,并且消息历史与 agent 文件切到 DeltaChannel 持久化,此变更不可回滚(见 3.5 节)。
  • 0.6.x → 0.7.x 新增了 skills、memory、permissions、delete 工具、FilesystemMiddleware 工具白名单,同时做了一次提示词瘦身(见 2.3 节)。
  • 0.7.13 → 0.7.14 → 0.7.15 都是修补性质:read_file 输出格式调整、edit_file 拒绝空 old_string、ls/glob 字符计数修正、无 ID 的工具结果转存改用唯一路径、subagent state key 传递修复。
  • 如果从 0.3.x 升级,SubAgent 已从 dict 改为类型化对象,create_deep_agent 的参数名有变化。
  • 0.x 版本 API 变动快,生产环境建议锁定版本(uv add deepagents==0.7.15)。

本文覆盖的版本快照(截至 2026-09-21)

包本文基准版本发布日期备注
deepagents(Python)0.7.152026-09-16核心库,本文主体内容
deepagents-code(dcode)0.1.722026-09-21终端编码智能体,见 12.0 节
deepagents-talon0.0.82026-09-11独立发布,本文未覆盖
langchain-quickjs0.3.72026-09-06JS 运行时,CodeInterpreterMiddleware 依赖它,本文未展开
langchain1.4.02026-09-01MCP 支持已内建进 langchain.mcp
langchain-mcp-adapters——已被 langchain.mcp 取代,不再维护
deepagents.js(JS/TS)——独立仓库,见附录 C

下次更新检查清单

  1. 查看 GitHub Releases 确认 deepagents 最新 minor 版本(当前 0.7.x;如果发布 0.8 或 1.0,优先检查 breaking changes)。
  2. 对比 CHANGELOG(或 release notes),重点看:
    • 新增/移除的中间件(libs/deepagents/deepagents/middleware/ 目录)
    • create_deep_agent 签名变化
    • 新增的 examples 目录
  3. 检查 deepagents-code 是否有新 major 功能(dcode 迭代很快,几乎每周发版)。
  4. 确认本文引用的文档链接是否仍然有效(LangChain 文档路径经常重组)。
  5. 如果有新的 middleware 或新的 examples,优先补充到第 2 章(中间件详解)和 12.5 节(示例导读)。

C. 生态与跨语言

  • JavaScript/TypeScript:DeepAgents 提供了 deepagents.js 库(GitHub),适合在 Node.js 环境中构建 agent。
  • Deep Agents Code:终端编码智能体,安装命令见 12.0 节。

D. 相关资源


本文基于 DeepAgents 0.7.15 版本编写,2026 年 9 月。API 在 1.0 之前可能变化,请以官方文档为准。

使用 Hugo 构建
主题 Stack 由 Jimmy 设计