一句话价值:写一个能跑的 Agent 不难,难的是让它别把上下文撑爆、别让子任务互相污染、别跑两轮就失忆 。LangChain 的
createAgent给了你一个干净的循环,剩下这些生产环境的脏活全得自己干。DeepAgents 做的事,就是把这些脏活预先焊进框架里------它不是更聪明的模型,而是更省事的脚手架。
先看一个现实的坑
假设你要做一个"能查资料、能写文件、能分步办事"的研究助手。用最朴素的 createAgent 起手:
js
const agent = createAgent({
model,
tools: [webSearch], // 只有搜索
systemPrompt: "你是一个研究助手"
});
跑起来很快就能发现问题------它只会搜索,不会记笔记。
于是你把搜索到的东西全塞进对话历史里。跑个十几轮,messages 数组越来越长,token 消耗直线飙升,最后撞上限。
再往下走,你想把这活儿拆开并行做:A 查框架甲、B 查框架乙。但两个子任务塞在同一个对话里,甲的资料会污染乙的判断。你开始手工给子任务开新会话、传结果......
到这里你会发现:你要写的东西,跟业务一点关系都没有。 全是 Agent 的"基建"------上下文怎么管、子任务怎么隔离、跨会话怎么记住。
DeepAgents 就是冲着这批基建来的。
三层生态:积木、蓝图、半成品
很多人第一次看 LangChain 全家桶会懵:LangChain、LangGraph、DeepAgents 到底啥关系?
┌──────────────────────────────────────────────────────────────┐
│ DeepAgents 半成品 Agent 框架(精装房) │
│ 预装:文件系统 / 记忆 / 技能 / 子代理 / 摘要 │
│ ↑ 复用 middleware 机制,把内置能力写成一个个中间件 │
├──────────────────────────────────────────────────────────────┤
│ LangGraph 底层运行时蓝图 │
│ 状态管理 / 循环路由 / 持久化执行 / 流式 │
│ ↑ createAgent 把 middleware 编排成一张图 │
├──────────────────────────────────────────────────────────────┤
│ LangChain 积木层 │
│ chat model / tool / 消息类型 / createMiddleware │
│ 定义了 middleware + hooks 机制(关键) │
└──────────────────────────────────────────────────────────────┘
换成一句更好记的话:
| 层 | 比喻 | 回答的问题 |
|---|---|---|
| LangChain | 一堆 AI 开发积木 | 有哪些零件? |
| LangGraph | 搭建复杂工作流的底层蓝图 | 零件怎么连成图? |
| DeepAgents | 半成品 Agent 框架 | 复杂 Agent 怎么少写重复代码? |
关键认知:DeepAgents 不是另一个从零写的运行时。它架在 LangChain + LangGraph 之上------LangChain 出积木、LangGraph 出运行时,它则在这之上加了一层"开箱即用"。
它同时有 Python 版(PyPI 的 deepagents)和 JS/TS 版(npm 的 deepagents),API 几乎一一对应。
⚠️ 一个小提醒:搜 "deepagents" 会同时出现两个东西------Deep Agents (这个库)和 Deep Agents Code(一个 CLI 工具)。别混淆。
createAgent 到底给了你什么
在聊 DeepAgents 之前,得先看清它替代的那个东西。
先说没有 agent 的日子:你直接调 LLM(ChatOpenAI),它是一问一答------你说话,它回话,仅此而已,它不能做事。
Agent 的本质,是给 LLM 装上"手脚":
scss
agent = 模型(大脑) + 工具(手脚) + 一个循环(自主决策)
那个循环是这样跑的:
arduino
模型说"我要调用工具 A"
↓
框架执行工具 A
↓
把结果塞回消息
↓
模型看结果,决定下一步
↓
......直到模型说"任务完成"
这个循环,就是 agent 区别于聊天机器人的全部秘密------它让模型能"自主地、分多步地"把一件真事办成。
createAgent 干的活,就是帮你把这个循环搭好。你不再需要手写:怎么把工具定义喂给模型、怎么解析模型返回的工具调用、怎么执行工具、怎么判断结束。你只传三样:
js
const agent = createAgent({ model, tools, systemPrompt });
就得到一个能自主干活的 agent。它背后是 LangGraph 编译出来的一张图。
但它给的是光溜溜的循环------除了"模型 ↔ 工具"这个骨架,什么都没有。
DeepAgents 要解决的三个痛点
一旦任务变长变复杂,光溜溜的循环立刻暴露三个问题:
| 痛点 | 具体表现 | DeepAgents 的答案 |
|---|---|---|
| 上下文越跑越长 | 撞 token 上限、成本失控 | 虚拟文件系统 + 摘要/上下文卸载:中间结果写进文件,而不是塞进 prompt |
| 大任务没法并行 | 子任务互相污染上下文 | subagent 派发,每个子代理跑在隔离的上下文窗口里 |
| 跑几轮就"失忆" | 换会话重来 | 长期记忆 + skills,按需加载 |
三个痛点指向同一个核心思路:
用文件系统当上下文管理的载体------"文件系统即记忆"。
这是个很值得品的设计判断。大多数人的第一反应是"上下文不够就上向量库",但 DeepAgents 选了更朴素的路:把中间结果写成文件,需要时再读回来。原因很简单------文件是结构化、可寻址、可增量读写的,而塞进 prompt 的每个 token 都要反复付费,还挤占推理空间。
它的能力被官方分成四大支柱:
markdown
1. Execution environment 执行环境
├─ 自定义 tool / LangChain tool / MCP 工具
├─ 虚拟文件系统(可插拔 backend + 声明式权限)
└─ 沙盒执行、进程内 JS 解释器、事件流式
2. Context management 上下文管理(四层)
├─ Skills 按需加载的能力片段
├─ Memory 跨会话长期记忆
├─ Summarization 摘要 + 大结果卸载到文件
└─ Prompt caching 省 token
3. Delegation 委派
├─ 内置 task 工具,派发隔离上下文的子代理
└─ TodoListMiddleware(write_todos)结构化任务清单
4. Steering 引导
└─ human-in-the-loop:关键决策点暂停、等人类审批
一个函数就搞定:createDeepAgent
回到开头那个"只会搜索"的助手。同样的需求,DeepAgents 的写法是:
js
import { createDeepAgent, FilesystemBackend } from "deepagents";
const agent = createDeepAgent({
model,
systemPrompt: "你是一个研究助手,用文件系统记录中间结果",
backend: new FilesystemBackend({
rootDir: workspaceDir,
virtualMode: true,
}),
});
注意------没有 middleware 数组 。就传了个 backend。
对比一下要达成同样效果,createAgent 得写什么:
js
// createAgent 版:手动组装
const agent = createAgent({
model,
tools: [],
systemPrompt: "...",
middleware: [
createFilesystemMiddleware({
backend: new FilesystemBackend({
rootDir: workspaceDir,
virtualMode: true,
})
})
]
});
两个版本我都跑过(代码在 demo/src/deepagents/ 下),实际效果对照:
| 中间件 | 状态 | 提供的能力 |
|---|---|---|
FilesystemMiddleware |
自动(必需) | ls / read_file / write_file / edit_file / glob / grep |
SubAgentMiddleware |
自动(必需) | task 工具,派发子代理 |
SummarizationMiddleware |
自动 | 长对话自动摘要 |
TodoListMiddleware |
⚠️ 需 opt-in | write_todos 任务规划 |
前三个是"开箱即用"的核心。这就是 createDeepAgent 相对 createAgent 的本质差别:一个给你空循环,一个给你预装好生产插件的循环。
跑完之后最直观的验证不是看控制台,而是去看磁盘------工作区目录里会真实出现 agent 用 write_file 写的文件。这时候你就知道"文件系统即记忆"不是说说而已。
⚠️ 这里有个后面会反复踩的坑:
TodoListMiddleware那一行我标了"需 opt-in"。它不是自动挂的 ,而且不挂的时候不报错------直到你写提示词让模型用write_todos,才会发现它压根没这个工具。第三篇会细讲。
一个必要的概念澄清:谁是什么
理解这套体系时,有个很容易搞混的地方:这些组件哪个才是 Runnable?
LangChain 里 Runnable 是一套接口协议,承诺三件事:
scss
任何 Runnable 都:
.invoke() 能调用
.stream() 能流式
.pipe() 能组合(LCEL 的核心)
于是很自然的一个推断是:agent、middleware、backend 是不是层层继承 Runnable?
不是。 只有 agent 是。
bash
Runnable 协议(invoke/stream/pipe)
↑ ✅ 是 Runnable
Agent ← createAgent 返回,背后是 LangGraph 编译好的图
↑ ❌ 不是 Runnable,是"插件/钩子集合"
Middleware ← createMiddleware 返回,只是配置对象
↑ ❌ 不是 Runnable,是"实现细节"
Backend ← 文件系统的落地实现
判断标准特别简单:能 .invoke() 的才是 Runnable 。middleware 和 backend 你能 .invoke() 它吗?不能。
那 middleware 到底是什么?准确说:middleware 的 hooks 会在 createAgent 编译 agent 图的时候,被"织进"这个 Runnable 里------变成图里的节点和边。所以是 middleware「参与构成」Runnable,而不是「继承」Runnable。
顺带纠正另一个说法:解耦靠的是**「协议 + 组合 + 依赖注入」**,不是「继承基类」。而且这里其实混了两种不同类型的解耦:
bash
① Runnable 的组合式解耦(横向串能力)
LLM | prompt | tool | agent ← 用 .pipe() 串成一条链
② middleware/backend 的插件式解耦(纵向抽横切)
agent 核心循环
├─ loggingMiddleware ← 可观测性,插拔不影响核心
├─ filesystemMiddleware ← 文件能力
│ └─ FilesystemBackend ← 落地实现,可换成内存/云
└─ ...
小结
- DeepAgents 是 LangChain 官方的 agent harness:架在 LangChain + LangGraph 之上,不重造轮子。
- 三层定位:LangChain 积木 → LangGraph 蓝图 → DeepAgents 半成品框架。
- 它解决三个生产痛点:上下文爆炸、子任务污染、跨会话失忆。核心思路是"文件系统即记忆"。
createDeepAgentvscreateAgent:一个预装插件,一个空循环。- 只有 agent 是
Runnable,middleware 是被织进图里的插件。
但这里还留了个没说透的问题:DeepAgents 那些"开箱即用"的能力,到底是怎么挂上去的?
答案就藏在一个词里------middleware。下一篇我们从零手写一个中间件,把它的机制彻底拆开。
系列导航
| 序号 | 标题 | 主题 |
|---|---|---|
| 01 | 当 Agent 框架开始「交钥匙」 | 定位、痛点、createAgent vs createDeepAgent |
| 02 | 六种 Hook 与两种范式 | middleware 机制:节点式 vs 包裹式 |
| 03 | 一个中间件就是一项能力 | 内置中间件拆解:文件系统、记忆、技能、子代理 |
| 04 | 参数拼错不报错 | 生产避坑:静默失效、流式观测与调试 |