芥末
发布于 2025-11-25 / 0 阅读
0
0

用 deepagents 构建长任务 Agent:规划、文件系统、子 Agent 与 Milvus 记忆

短对话 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_todosread_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)

执行过程中会发生几件事:

  1. Agent 使用 write_todos 把研究任务拆成多个可执行步骤。
  2. 搜索结果如果很大,会被保存到工具结果文件里。
  3. Agent 通过 grepread_file 读取关键片段,而不是把所有材料塞进上下文。
  4. 可复用结论会写入 /memories/
  5. /memories/ 路径被 CompositeBackend 路由到 Milvus 对应的持久化存储。

内置工具备忘表

类别工具说明
任务管理write_todos创建或更新待办事项列表
任务管理read_todos查看当前任务进度
文件系统ls列出目录内容
文件系统read_file读取文件,适合配合分页参数处理大文件
文件系统write_file写入文件
文件系统edit_file精确替换文件内容
文件系统glob按模式查找文件
文件系统grep搜索文件内容
文件系统execute在沙箱中执行命令,需要后端支持
子 Agenttask把子任务委托给指定子 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_oncheckpointer 使用。审批选项至少包含:

["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 不只是在一次会话里完成任务,还能把可复用知识沉淀下来,在后续任务中通过语义检索重新利用。


评论