跳到正文
Elaine Blog
返回

读懂 Deep Agents:规划、文件与子 Agent 怎样组成工作系统

更新于:
Agent Harness

上一篇的 Pi 从基础循环理解 Harness。本篇换一个入口:create_deep_agent 返回的对象为什么能使用文件能力、加载 Skill 并委派子任务?答案藏在创建阶段装配的中间件与 Backend 中。

本文固定提交 54696577caf3dfcefb662db08b4a8034ec6a35cd,仓库中 deepagents 包版本为 0.7.13。这里只阅读源码,没有安装依赖、运行 Agent 或测量效果。固定提交的意义是避免把旧教程默认值与当前实现混写。

create_deep_agent 并不是一段万能提示词

graph.py的创建函数开始。它先解析模型,取得与模型相关的 HarnessProfile,校验配置,再准备工具、Backend 和子 Agent,最后组织中间件并调用底层 create_agent

Profile 会影响提示、工具描述、额外中间件和排除规则。也就是说,传入不同模型配置,最终能力装配可能不同。本文不把“某个模型配置默认有某工具”推广为所有 Deep Agents 实例的固定事实。

创建函数末尾将 checkpointer、store、state schema 等传给底层。它们不是装饰参数:是否保存执行状态、文件是否跨任务共享,依赖具体选择。创建对象成功本身不能证明持久恢复已经配置完成。

中间件怎样让能力进入循环

此提交的主线包括可选 SkillsMiddleware、FilesystemMiddleware、条件性的 SubAgentMiddleware、摘要处理和 PatchToolCallsMiddleware,之后还有 Profile 扩展、缓存、记忆和人工介入相关装配。自定义中间件也参与顺序安排与名称覆盖检查。

中间件可以影响模型请求、工具执行或状态更新。它不仅向提示里加一句“你可以读文件”,还提供工具定义和执行路径。理解某个中间件时,应同时查它注册的工具与调用后返回的状态,不只读类名。

当前实现最终再处理工具排除,避免前面自定义请求包装重新暴露应排除的名字。这个顺序体现了一个普遍原则:如果能力来自多处装配,最终可见集合需要在统一位置确定。

用两段短源码定位装配的出口

以下是固定提交中 graph.py 的短摘录,中文注释用于说明职责。第一段是一条原始语句,第二段省略了其他参数,不是完整独立程序。

# 没有传入后端时使用状态后端;它不等于当前磁盘目录。
backend = backend if backend is not None else StateBackend()
# 创建函数把已经装配好的工具和中间件交给底层 Agent。
# checkpointer 负责执行状态的保存方式,store 服务于其他持久存储需求。
return create_agent(
    model,
    tools=_tools,
    middleware=deepagent_middleware,
    checkpointer=checkpointer,
    store=store,
    # 原函数还有 system_prompt、schema、cache 等参数,此处省略。
)

因此排查“文件不在磁盘”时先查 Backend;排查“下次调用没有状态”时查 Checkpointer 与调用配置;排查“工具怎么出现的”时查中间件装配。它们不是同一个开关。

规划能力要看实际配置

不少入门资料把计划工具描述为永远内置,但这个提交的基础装配列表没有无条件加入 TodoListMiddleware。某些 Profile 会加入它,自定义应用也可以显式传入。

配套deepagents_report.py下载显式添加 TodoListMiddleware,避免让读者依赖隐含默认。工具保存任务项,模型据此选择下一步;把条目标成 completed 并不会自动验证对应文件。完成门槛仍需应用中的 Verifier。

例如计划包含“核验订单状态”,模型把它标为完成,但报告仍写 paid。任务清单能帮助组织工作,却不能代替 status == source.status 这条事实检查。

Backend 决定文件究竟在哪里

默认 Backend 在此提交中是 StateBackend。它把文件能力映射到状态,不等于宿主机目录。FilesystemBackend 则访问实际文件系统;其他 Backend 可以连接不同存储或执行设施。工具接口看起来相似,持久化与隔离含义却不同。

FilesystemBackend中的 virtual_mode 提供虚拟路径语义,源码明确说明它不提供进程隔离或沙箱。root_dir 和路径解析有助于组织文件工具,但不能限制另一个拥有宿主权限的任意进程。

一个常见错误是:在 StateBackend 下调用写文件,然后去宿主目录寻找结果。文件可能保存在返回状态或 Checkpoint 中,并没有对应磁盘路径。另一个错误是:用了持久 Store,就以为所有用户自然隔离。Store 的命名空间、调用身份和业务授权仍需设计。

子 Agent 会继承哪些状态

SubAgentMiddleware把声明式或已编译子 Agent 暴露为 task 工具。普通子任务使用任务描述构造新的消息输入,同时有选择地传递状态;返回时通过工具消息回到父任务,并排除部分专属状态键。

这个提交还包含实验性的 fork 方式,它延续父会话并重建系统提示。因此“所有子 Agent 都从完全空白上下文开始”也是错误概括。应先确认使用的是普通委派、fork 还是异步远端子任务。

共享 Backend 可能使父子看到相同文件。上下文隔离不代表写入隔离;两个子任务同时写 /report.json 仍可能冲突。更稳妥的报告检查让子任务只读候选、返回带版本的结果,由父任务统一修改与验证。

默认通用子 Agent 的添加也受 Profile 配置影响。应用应该检查最终工具与子 Agent 集合,不能仅从一个函数名推测最终行为。

Skill 与长上下文如何连接文件能力

SkillsMiddleware通过 Backend 读取 Skill 来源。路径相对于 Backend 根语义,后面的同名来源可以覆盖前面的。主体按需读取,避免所有长说明常驻。

摘要和工具结果外置减少模型上下文占用,但外置文件必须仍可读、仍属于当前任务权限。只返回一个不存在或不可访问的日志路径,不能称为成功压缩。

PatchToolCallsMiddleware 关注工具调用与结果消息关系的修补,不能理解成把未知外部副作用自动恢复为成功。消息结构完整与业务操作是否完成是两件事。

怎样扩展“交付前必须核验”

把“务必检查”写进 system prompt 可以解释流程,但强制交付门槛应在应用入口实现。配套示例的 submit_report 调用前面同一份 verify,只在通过时记录已提交结果;模型最终文字并不改变这个记录。

该示例使用默认状态 Backend,并显式加入计划中间件和模型配置。它是单进程教学扩展,提交记录保存在内存闭包中;进程退出不会保存,不能直接当成生产交付存储。要实现持续任务,应接入持久回执与输入版本绑定。

直接传入 tools 在这个版本是增加工具,而非替换全部内置工具。如果应用需要严格只读,必须按实际中间件和 Profile 接口限制能力,并控制底层 Backend;不能因为只传入一个只读业务工具,就宣称 Agent 只有这一项能力。

该借鉴什么

Deep Agents 展示了用中间件和后端分离工作能力的方式,适合学习“同一个模型循环怎样获得不同环境”。代价是读源码时必须跟踪装配顺序和配置,不再只看一个执行函数。

对于订单报告系统,业务字段、输入版本和交付判定仍由应用定义。框架提供了表达与组合这些过程的工具。下一篇的DeepSeek Harness会进一步把循环、存储和工具都放进插件体系。


分享这篇文章:

上一篇
读懂 Pi Agent:一个精简的 Coding Harness 怎样工作
下一篇
读懂 DeepSeek Harness:怎样用插件构建 Agent 运行环境