短对话 Agent 做演示并不难:用户问一句,模型调用一次工具,再把结果返回即可。真正麻烦的是长任务,比如调研一个技术选型、维护一个代码仓库、生成一份完整分析报告,或者连续执行几十次工具调用的业务流程。
这类任务会遇到几个典型问题:
- 工具调用结果越来越多,全部塞进上下文会迅速消耗 token。
- 模型需要在大量历史消息里找关键信息,越往后越容易跑偏。
- 子任务之间相互污染,例如调研任务的材料影响了代码修改任务的判断。
- 任务执行到一半被人工打断后,恢复执行时容易丢状态。
- 临时文件、长期记忆、工具结果混在一起,难以管理。
deepagents 是 LangChain 推出的开源 Agent 框架,它把长任务拆成几个可控的基础设施能力:任务规划、文件系统访问、子 Agent 委托、可替换后端、人机审批和 LangGraph 状态图能力。它的目标不是让模型“更聪明”,而是给模型一个更适合完成复杂任务的工作环境。
deepagents 要解决的问题:上下文不是无限工作台
传统 Agent 常见的信息流是这样的:
flowchart LR
U[用户任务] --> LLM[大模型]
LLM --> T1[工具调用 1]
T1 --> R1[结果写回上下文]
R1 --> LLM
LLM --> T2[工具调用 2]
T2 --> R2[结果继续写回上下文]
R2 --> LLM
LLM --> T3[工具调用 N]
T3 --> R3[上下文越来越长]
这个模式适合短流程,但长任务会出现“上下文堆积”。一次网页搜索、一次代码检索、一次数据库查询都可能返回大量内容,如果每次都原样追加到消息历史里,成本会越来越高,模型也会被噪声干扰。
deepagents 的思路是把上下文从“聊天记录仓库”改成“任务控制台”。模型上下文里只保留当前需要处理的信息、文件路径、任务状态和必要摘要,大块内容放到文件系统或持久化存储里。
flowchart TB
U[用户输入长任务] --> A[deepagents 主 Agent]
A --> P[任务规划<br/>write_todos / read_todos]
A --> F[文件系统<br/>读写文件、搜索内容、保存大结果]
A --> S[子 Agent 委托<br/>隔离上下文执行子任务]
F --> B{Backend}
B --> SB[StateBackend<br/>单会话临时状态]
B --> FB[FilesystemBackend<br/>本地文件]
B --> ST[StoreBackend<br/>持久化存储]
ST --> M[Milvus<br/>语义记忆检索]
A --> G[LangGraph StateGraph<br/>流式输出、检查点、人机交互]
deepagents 创建出来的 Agent 本质上是编译后的 LangGraph StateGraph,所以可以直接使用 LangGraph 的流式输出、检查点、人机交互中断等能力。
三个核心机制
任务规划:先把复杂指令变成待办事项
长任务失败的一个常见原因是模型边想边做,执行路径越来越随机。deepagents 通过 TodoListMiddleware 给 Agent 注入两个工具:
| 工具 | 作用 |
|---|---|
write_todos | 创建结构化待办事项,记录任务、优先级、依赖关系和状态 |
read_todos | 读取当前待办事项,确认哪些完成、哪些待处理 |
一个研究任务可以被拆成类似这样的结构:
[
{
"task": "收集 Milvus 的核心架构资料",
"status": "pending",
"priority": "high"
},
{
"task": "整理 Milvus 与 pgvector、Qdrant 的差异",
"status": "pending",
"priority": "high"
},
{
"task": "生成适合生产环境选型的结论",
"status": "pending",
"priority": "medium"
}
]
这样做的好处很具体:Agent 不需要在脑海里记住所有步骤,而是通过待办事项维护任务进度。每完成一个子任务,就能更新状态;任务中断后,也能根据待办列表恢复执行。
文件系统访问:把大结果放到工作区,而不是塞进上下文
deepagents 内置了文件系统工具,让 Agent 像使用一个项目工作区一样处理材料。
| 工具 | 用途 |
|---|---|
ls | 列出目录文件,路径需要以 / 开头 |
read_file | 读取文件内容,支持 offset / limit 这类分页参数 |
write_file | 创建文件或覆盖文件 |
edit_file | 对文件执行精确字符串替换 |
glob | 按模式匹配文件,例如查找 /**/*.py |
grep | 在文件中搜索文本模式 |
execute | 在沙箱环境中执行 shell 命令,需要后端支持 SandboxBackendProtocol |
文件系统工具不只是“让 Agent 会读写文件”。更关键的是,大型工具调用结果可以自动落盘。
例如一次搜索工具返回了 100KB 内容,传统做法会把这 100KB 全部放进上下文。deepagents 可以把结果保存为:
/tool_results/internet_search_1.txt
Agent 上下文里只保留文件路径。需要查看细节时,再用 grep 定位关键词,用 read_file 分页读取相关片段。
sequenceDiagram
participant A as Agent
participant T as 搜索工具
participant FS as 文件系统
participant C as 上下文
A->>T: 查询资料
T-->>A: 返回大体积结果
A->>FS: 写入 /tool_results/search_1.txt
A->>C: 只记录文件路径
A->>FS: grep / read_file 读取关键片段
FS-->>A: 返回小片段
这个模式能减少 token 消耗,也能让模型的注意力集中在当前步骤需要的信息上。
子 Agent 委托:让不同子任务隔离执行
长任务经常包含多个性质不同的子任务。比如“调研资料”“分析数据”“修改代码”“写报告”都可以由不同子 Agent 处理。deepagents 通过 SubAgentMiddleware 注入 task 工具,主 Agent 可以把子任务委托给专门的子 Agent。
子 Agent 有独立的上下文窗口、工具集和系统提示。它完成任务后,把结果返回给主 Agent,而不是把整个执行过程都污染到主 Agent 的上下文里。
flowchart LR
M[主 Agent] -->|task: 调研 Milvus| R[research-agent]
M -->|task: 分析数据| D[data-analyzer]
M -->|task: 修改代码| C[code-agent]
R -->|返回调研摘要| M
D -->|返回分析结论| M
C -->|返回代码修改说明| M
适合委托给子 Agent 的任务通常有两个特征:
| 子任务类型 | 为什么适合委托 |
|---|---|
| 深度搜索 | 会产生大量中间材料,不适合全部进入主上下文 |
| 代码分析 | 需要频繁 grep、read_file、edit_file,过程较长 |
| 数据分析 | 可能需要执行脚本、读取数据文件、生成中间结果 |
| 报告生成 | 需要独立组织结构,避免被工具细节干扰 |
deepagents 的定制能力
deepagents 不是固定流程框架,而是由一组可组合的中间件和后端组成。业务系统可以按需要替换工具、提示词、子 Agent 和存储后端。
System Prompt:定义领域工作流和边界
自定义 system prompt 会追加到 deepagents 默认注入的指令之后,适合放业务约束、领域流程和停止标准。
| 应该写进 system prompt 的内容 | 示例 |
|---|---|
| 领域工作流 | 数据分析任务先做探索性分析,再建立模型,最后解释指标 |
| 工具协同规则 | 使用 grep 定位代码位置,再用 read_file 查看上下文 |
| 合并策略 | 相似的文献检索任务合并成一个待办事项 |
| 停止标准 | 工具调用超过 100 次仍无结论时停止并说明原因 |
| 记忆写入规则 | 可复用结论写入 /memories/ 目录 |
不适合写进 system prompt 的内容:
| 不建议内容 | 原因 |
|---|---|
| 重复内置工具说明 | 默认中间件已经注入,重复会浪费上下文 |
| 禁止使用待办事项 | 会和 TodoListMiddleware 的目标冲突 |
| 模糊口号 | 例如“尽量做好”“认真分析”,不能约束行为 |
自定义 Tools:把业务能力暴露给 Agent
deepagents 支持普通 Python 函数作为工具。函数签名定义参数,docstring 会作为工具描述提供给模型。
from deepagents import create_deep_agent
from tavily import TavilyClient
tavily_client = TavilyClient(api_key="YOUR_TAVILY_API_KEY")
def internet_search(query: str, max_results: int = 5) -> str:
"""Run a web search and return concise search results."""
results = tavily_client.search(query=query, max_results=max_results)
return "\n".join(
f"{item['title']}: {item['content']}"
for item in results["results"]
)
agent = create_deep_agent(
tools=[internet_search]
)
如果已有 MCP(Model Context Protocol,模型上下文协议)工具,也可以通过 langchain-mcp-adapters 接入。
from langchain_mcp_adapters.client import MultiServerMCPClient
from deepagents import create_deep_agent
async def main():
mcp_client = MultiServerMCPClient(
{
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
"transport": "stdio",
}
}
)
mcp_tools = await mcp_client.get_tools()
agent = create_deep_agent(
tools=mcp_tools
)
async for chunk in agent.astream(
{
"messages": [
{
"role": "user",
"content": "检查 /tmp 目录下的项目结构并给出说明"
}
]
}
):
chunk["messages"][-1].pretty_print()
Middleware:把工具、提示和生命周期逻辑封装起来
Middleware 适合处理横切能力,例如统一注入工具、修改提示词、记录审计日志、限制危险操作等。
from langchain_core.tools import tool
from deepagents import create_deep_agent
from deepagents.middleware import AgentMiddleware
@tool
def get_weather(city: str) -> str:
"""Get the weather in a city."""
return f"The weather in {city} is sunny."
class WeatherMiddleware(AgentMiddleware):
tools = [get_weather]
agent = create_deep_agent(
middleware=[WeatherMiddleware()]
)
常见内置中间件可以按职责理解:
| Middleware | 核心职责 |
|---|---|
| TodoListMiddleware | 注入 write_todos、read_todos,要求 Agent 维护任务计划 |
| FilesystemMiddleware | 注入文件系统工具,管理工作区和大结果落盘 |
| SubAgentMiddleware | 注入 task 工具,支持子 Agent 委托 |
| PatchToolCallsMiddleware | 处理人机交互中断后的工具调用恢复 |
| 其他自定义 Middleware | 注入业务工具、改写提示、增加审计或权限控制 |
Subagents:给专门任务配置专门模型和工具
普通子 Agent 可以用字典配置:
from deepagents import create_deep_agent
def internet_search(query: str) -> str:
"""Run a web search."""
return "search results"
research_subagent = {
"name": "research-agent",
"description": "Used to research in-depth questions",
"prompt": "You are an expert researcher. Use search tools and return concise findings.",
"tools": [internet_search],
"model": "openai:gpt-4o", # 不填时默认使用主 Agent 模型
}
agent = create_deep_agent(
subagents=[research_subagent]
)
如果子任务已经有一套复杂 LangGraph 流程,也可以把预先编译好的图作为子 Agent。
from deepagents import CompiledSubAgent, create_deep_agent
from langgraph.prebuilt import create_react_agent
custom_graph = create_react_agent(
model="openai:gpt-4o",
tools=[internet_search],
prompt="You are a data analysis specialist."
)
agent = create_deep_agent(
subagents=[
CompiledSubAgent(
name="data-analyzer",
description="Specialized agent for data analysis",
runnable=custom_graph
)
]
)
interrupt_on:给危险工具加人工审批
删除文件、发邮件、提交订单、执行生产命令这类操作不应该完全交给模型自动决定。deepagents 可以通过 interrupt_on 指定哪些工具需要人工审批。
from langchain_core.tools import tool
from deepagents import create_deep_agent
from langgraph.checkpoint.memory import MemorySaver
@tool
def delete_file(path: str) -> str:
"""Delete a file from the filesystem."""
return f"Deleted {path}"
agent = create_deep_agent(
tools=[delete_file],
interrupt_on={
"delete_file": {
"allowed_decisions": ["approve", "edit", "reject"]
}
},
checkpointer=MemorySaver()
)
这里的 checkpointer 很关键。人工审批会让执行暂停,如果没有检查点,恢复执行时容易丢失状态。
Backends:把临时工作区和持久化数据分开
deepagents 的文件系统能力依赖 backend。不同 backend 适合不同存储场景。
| Backend | 适合场景 | 数据生命周期 |
|---|---|---|
| StateBackend | 单次会话临时文件、工具结果缓存 | 会话级 |
| FilesystemBackend | 本地项目目录、代码仓库操作 | 取决于本地磁盘 |
| StoreBackend | 需要跨会话保存的数据 | 持久化 |
| CompositeBackend | 不同路径路由到不同 backend | 混合 |
本地项目目录可以直接使用 FilesystemBackend:
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
agent = create_deep_agent(
backend=FilesystemBackend(root_dir="/path/to/project")
)
复杂系统更适合使用 CompositeBackend。例如:
| 路径 | Backend | 用途 |
|---|---|---|
/workspace/ | StateBackend | 当前任务工作区 |
/temp/ | StateBackend | 临时工具结果 |
/memories/ | StoreBackend + Milvus | 跨会话长期记忆 |
/knowledge/ | StoreBackend + Milvus | 可复用知识沉淀 |
为什么要搭配 Milvus 做长期记忆
StateBackend 适合一次任务内的临时状态,但不能解决跨会话记忆问题。Agent 如果要长期保存用户偏好、历史研究结论、业务规则、反馈记录,就需要持久化存储。
关系型数据库能保存精确记录,但 Agent 记忆经常需要“语义检索”。例如用户新任务是“比较向量数据库索引策略”,系统需要找回过去写过的“Milvus HNSW 与 IVF_FLAT 对比”“Qdrant filter 查询性能记录”等相关材料。这类召回更适合向量数据库。
Milvus 在这个架构里承担语义记忆层:
flowchart LR
A[Agent 产生可复用结论] --> W[写入 /memories/xxx.md]
W --> E[生成 embedding 向量表示]
E --> M[(Milvus Collection)]
U[新任务] --> Q[任务语义向量]
Q --> M
M --> R[召回相关历史记忆]
R --> A
需要注意,Milvus 适合保存和检索语义记忆,不等于替代所有状态存储。如果要保存严格事务状态,例如订单状态、审批流状态、用户权限,仍然应该使用数据库或业务系统自己的状态机。
快速开始:构建带 Milvus 记忆的 deepagents
步骤一:安装依赖
pip install -U deepagents tavily-python langchain-milvus langchain-openai langgraph
如果使用 Tavily 搜索和 OpenAI embedding,可以配置环境变量:
export TAVILY_API_KEY="your-tavily-api-key"
export OPENAI_API_KEY="your-openai-api-key"
export MILVUS_URI="http://localhost:19530"
步骤二:配置混合后端
CompositeBackend 负责路径路由。临时文件走 StateBackend,长期记忆走 StoreBackend,底层存储接到 Milvus。
import os
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
from langchain_openai import OpenAIEmbeddings
from langchain_milvus.storage import MilvusStore
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small"
)
milvus_store = MilvusStore(
collection_name="agent_memories",
embedding_service=embeddings,
connection_args={
"uri": os.environ["MILVUS_URI"]
}
)
backend = CompositeBackend(
default=StateBackend(),
routes={
"/memories/": StoreBackend(store=milvus_store),
"/knowledge/": StoreBackend(store=milvus_store),
}
)
如果只是本地验证,可以先用 LangGraph 的内存 Store 替代 Milvus:
from langgraph.store.memory import InMemoryStore
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
backend = CompositeBackend(
default=StateBackend(),
routes={
"/memories/": StoreBackend(store=InMemoryStore())
}
)
内存 Store 只能用于测试,进程退出后数据会消失。
步骤三:创建搜索工具
import os
from tavily import TavilyClient
tavily_client = TavilyClient(
api_key=os.environ["TAVILY_API_KEY"]
)
def internet_search(query: str, max_results: int = 5) -> str:
"""Search the internet and return compact results."""
results = tavily_client.search(
query=query,
max_results=max_results
)
return "\n\n".join(
f"Title: {item['title']}\nContent: {item['content']}"
for item in results["results"]
)
步骤四:创建 Agent
系统提示里要明确告诉 Agent:哪些信息需要写入 /memories/。否则模型可能只完成当前任务,不会主动沉淀长期记忆。
from deepagents import create_deep_agent
agent = create_deep_agent(
tools=[internet_search],
system_prompt=(
"你是技术研究专家。执行复杂研究任务时,需要先使用待办事项规划步骤。"
"搜索得到的大块材料应保存到文件中,再用 grep 和 read_file 读取关键片段。"
"具有跨任务复用价值的结论、用户偏好、技术对比和决策依据,"
"必须写入 /memories/ 目录,方便后续会话复用。"
),
backend=backend
)
步骤五:运行任务
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "研究 Milvus 向量数据库的技术特点,并整理适合生产选型的结论"
}
]
}
)
print(result["messages"][-1].content)
执行过程中会发生几件事:
- Agent 使用
write_todos把研究任务拆成多个可执行步骤。 - 搜索结果如果很大,会被保存到工具结果文件里。
- Agent 通过
grep和read_file读取关键片段,而不是把所有材料塞进上下文。 - 可复用结论会写入
/memories/。 /memories/路径被CompositeBackend路由到 Milvus 对应的持久化存储。
内置工具备忘表
| 类别 | 工具 | 说明 |
|---|---|---|
| 任务管理 | write_todos | 创建或更新待办事项列表 |
| 任务管理 | read_todos | 查看当前任务进度 |
| 文件系统 | ls | 列出目录内容 |
| 文件系统 | read_file | 读取文件,适合配合分页参数处理大文件 |
| 文件系统 | write_file | 写入文件 |
| 文件系统 | edit_file | 精确替换文件内容 |
| 文件系统 | glob | 按模式查找文件 |
| 文件系统 | grep | 搜索文件内容 |
| 文件系统 | execute | 在沙箱中执行命令,需要后端支持 |
| 子 Agent | task | 把子任务委托给指定子 Agent |
适合使用 deepagents 的场景
| 场景 | 是否适合 | 原因 |
|---|---|---|
| 多轮技术调研 | 适合 | 搜索结果多,适合用文件系统沉淀材料 |
| 代码仓库分析和修改 | 适合 | 需要 grep、read_file、edit_file 等文件操作 |
| 数据分析报告生成 | 适合 | 中间结果多,适合拆任务和保存文件 |
| 需要人工审批的自动化流程 | 适合 | interrupt_on 可以拦截危险工具 |
| 跨会话知识积累 | 适合 | 可以结合 StoreBackend 和 Milvus |
| 单轮问答 | 不太适合 | 直接调用模型更简单 |
| 极低延迟接口 | 不太适合 | 规划、文件操作和子 Agent 会增加调用链路 |
| 强确定性业务流程 | 视情况而定 | 如果流程完全固定,普通工作流引擎可能更合适 |
常见坑
不要把所有信息都写进 system prompt
system prompt 应该写规则和边界,不应该写大量业务资料。业务资料更适合放入 /knowledge/ 或向量数据库,通过检索按需取回。
长期记忆需要明确写入规则
Agent 不一定会主动保存记忆。需要在系统提示里说明:
具有跨任务复用价值的结论写入 /memories/
临时草稿写入 /workspace/
搜索原始结果写入 /tool_results/
路径规则越明确,后端路由越稳定。
文件路径要统一使用绝对路径
deepagents 文件系统工具通常要求路径以 / 开头。推荐约定几个固定目录:
/workspace/ 当前任务工作区
/temp/ 临时文件
/tool_results/ 工具调用大结果
/memories/ 长期记忆
/knowledge/ 领域知识
子 Agent 不是越多越好
子 Agent 能隔离上下文,但也会增加模型调用次数。适合拆给子 Agent 的任务应该足够独立,并且有明确输入输出。简单步骤直接由主 Agent 执行即可。
危险工具必须加审批
删除文件、写数据库、发送消息、执行 shell 命令等工具,都应该配合 interrupt_on 和 checkpointer 使用。审批选项至少包含:
["approve", "edit", "reject"]
这样人工可以批准、修改参数或拒绝执行。
Milvus 管语义记忆,不管所有状态
Milvus 擅长相似度检索,适合保存文本记忆、研究结论、知识片段。严格业务状态仍应放在业务数据库里。一个稳妥的生产架构通常是:
flowchart LR
A[Agent] --> M[Milvus<br/>语义记忆]
A --> DB[(业务数据库<br/>事务状态)]
A --> FS[对象存储或文件系统<br/>原始文件]
A --> CP[Checkpointer<br/>执行恢复]
deepagents 的价值在于把长任务 Agent 所需的基础能力拆清楚:计划用待办事项维护,材料用文件系统承载,复杂子任务用独立 Agent 隔离,长期记忆用持久化后端保存。配合 Milvus 后,Agent 不只是在一次会话里完成任务,还能把可复用知识沉淀下来,在后续任务中通过语义检索重新利用。