DeerFlow

研究、执行与文件制作的 Agent 工作台
2 份简历参考3 道面试问答
反馈(在 GitHub 新标签页打开)

主要技术:LangChain、LangGraph、FastAPI、python-docx

项目目录

项目简介

DeerFlow 是把资料研究、代码执行和文件制作组织在一起的开源 Agent 工作台。用户给出任务并上传材料,主 Agent 调用工具或分派子任务,生成报告、图表、网页等产物;当前 2.x 版本基于 Python、LangGraph 和可扩展技能组织执行。

官方案例中,用户上传 Titanic CSV 数据并要求分析生存因素,Agent 读取数据、编写并运行分析脚本,交付文字总结和多张统计图。网页支持查看任务过程与输出文件;研究、写作和演示材料制作等任务还可按需读取对应技能及资源。

DeerFlow 官方数据分析案例生成的总体与分性别生存率图

项目亮点

  • 技能按需加载: 先提供技能名称和简介,需要执行时再读取 SKILL.md 及引用资料,把工具调用方法与制作要求按任务补入上下文。
  • 有额度的多任务委派: 普通 task 默认使用独立上下文,需要承接前文时可携带派发时的父会话快照;中间件同时约束单轮委派数量和本次运行总量,裁去超额调用。
  • 共享文件与执行租约: 主、子 Agent 可共用线程工作区,通过虚拟路径访问上传资料和产物。执行租约保留尚在使用的环境,在释放请求发出且最后一个持有者退出后,才调用 Provider 释放。
  • 产物检查独立于任务状态: 委派可附带 acceptance_criteria,在子任务完成后检查文件、JSON 语法、CSV 表头或已记录的测试证据,分别返回成立、不成立和未验证结果。

技术架构

Next.js 前端通过 FastAPI Gateway 上传材料、发起运行并接收流式事件,Agent 运行时内嵌于 Gateway。基于 LangGraph 的主 Agent 组合模型、技能、中间件和工具;普通子任务由执行器异步运行,将结果及文件引用交回主任务。

文件按用户、线程划分为 uploads、workspace 和 outputs,由 Sandbox Provider 解析统一虚拟路径。本地文件 Provider 和容器 Provider 分别承接不同执行方式;产物通过 present_files 登记当前线程输出路径,再交由文件接口供前端查看和下载。

代码位置 主要职责
frontend/、backend/app/gateway/ 任务界面、上传与产物接口、内嵌运行和事件传输
backend/packages/harness/deerflow/agents/lead_agent/、skills/ 组织主 Agent,发现和按需加载技能
backend/packages/harness/deerflow/tools/builtins/task_tool.py、backend/packages/harness/deerflow/subagents/executor.py 传递委派上下文,执行子任务并返回结果
backend/packages/harness/deerflow/sandbox/、backend/packages/harness/deerflow/config/paths.py 管理执行后端、线程目录、虚拟路径与共享租约
backend/packages/harness/deerflow/subagents/acceptance_checks.py、backend/packages/harness/deerflow/tools/builtins/present_file_tool.py 检查具体交付条件,登记可呈现的输出路径

如何写进简历

投标工作台 · 工业装备投标协作助手 | 工业装备场景示例

项目描述:面向工业装备投标团队的资料协作助手,将招标条款、设备手册和资质材料组织为响应矩阵,生成带依据的响应草稿及缺件清单,供投标人员复核。

技术栈:Python、LangChain、LangGraph、FastAPI、python-docx

主要工作:

  • Skill 渐进加载与条款证据组织:编写投标资料 Skill,把条款、证明材料及响应位置组织为任务模板;先加载技能元信息,执行时再读取 SKILL.md 与引用文件,按需组织设备手册和资质证据,减少整套材料常驻模型上下文。
  • 子任务委派与预算控制:通过 task 按条款范围分派商务、技术及资质整理,以文档版本和输出契约汇总响应;结合独立上下文、可选父会话 snapshot 及单轮/运行总量限制,控制重复拆分和背景传递规模。
  • Sandbox 文件生成与共享执行租约:通过 Sandbox Provider 的文件与命令接口生成 DOCX 响应稿和缺件清单,主、子任务共用线程资料;以执行租约协调共享环境的释放,避免子任务仍在生成文件时提前关闭客户端。
  • 产物三态检查与响应复核:为子任务附加 acceptance_criteria,完成后检查响应文件与缺件清单的 CSV 表头,以 checked/holds 区分成立、不成立和未验证;再按条款矩阵核对缺件与引用,衔接结构检查和业务复核。

稿易达 · 专题内容制作助手 | 传媒出版场景示例

项目描述:面向专题采编团队的内容制作助手,将采访笔记与公开资料组织为可追溯素材,围绕统一事实表生成专题稿、图表及演示材料,并按编辑意见更新相关产物。

技术栈:Python、LangChain、LangGraph、FastAPI、Matplotlib、python-pptx

主要工作:

  • Skill 渐进加载与采编工作流:编写专题采编 Skill,将素材索引、写作及配套制作组织为可复用流程;按任务读取 SKILL.md 和参考文件,分别保留原话、编辑概括及来源位置,避免整套制作说明一次性占满上下文。
  • 独立子任务与编辑背景快照:默认以独立上下文分派资料整理和内容制作,需要承接选题、受众或多轮修改意见时显式传入父会话 snapshot;按任务固定素材版本和输出范围,使子任务获得必要背景并保持各自执行上下文。
  • Sandbox 多产物生成与路径约束:通过 Sandbox Provider 在工作区处理素材,由同一事实表驱动 Matplotlib 图表和 python-pptx 演示稿;最终文件写入 outputs,通过 present_files 登记当前线程产物,减少跨文件数字及交付路径错配。
  • 依赖修订与确定性产物检查:为段落、图表和幻灯片维护事实依赖及版本,按编辑修改单定位重制范围;通过 acceptance_criteria 检查文件非空和事实表 JSON 语法,再由交付清单关联来源与待核项,保留可复查的修订结果。

面试题与回答

3 题
  1. 子 Agent 是否使用父 Agent 的全部上下文?

    展开收起

    这一版 DeerFlow 的普通 task 委派提供两种 context_mode:默认 isolated,只用当前委派任务作为对话输入;显式选择 snapshot 时,再附带父会话的保留历史和摘要。子 Agent 仍使用自己的角色、工具配置和执行链路。

    快照复制什么,何时复制

    task 校验委派类型与允许范围后,在子 Agent 初始化前调用 ParentContextSnapshot.from_state(...)。快照读取派发当时的 summary_text 与仍保留的对话消息,过滤父系统消息和框架注入等内容,再序列化为独立数据。

    因此,父会话若已经压缩,子 Agent 得到的是当前保留内容与摘要,不会恢复所有被压缩掉的原始消息。快照也不会因为父状态后来发生变化而自动更新;重新构建子消息时使用独立容器,不与父消息或兄弟任务共用可变历史。

    历史作为背景,当前任务单独传入

    子 Agent 初始输入的顺序是:自己的 SystemMessage、名为 parent_context_snapshot 的背景 HumanMessage,然后是当前任务的 HumanMessage。背景里保留用户要求、已知决定和观察,但不提升为子 Agent 的系统指令。

    父 Agent 已完成的工具调用会与保留结果匹配后,以“历史工具调用”的文本进入背景;尚未取得对应结果的调用不作为可重放操作传入。这样既能解释过去查到了什么,又避免子 Agent 把父方调用、回执或历史动作误认为自己本次已经执行的结果。

    怎样使用两种模式

    任务目标和资料位置已经明确时,选 isolated,减少重复携带父会话。接续复杂排障、需要理解多轮更正或失败尝试时,选 snapshot,同时在任务中指出当前要解决的部分。

    为避免多轮规划不断追加子任务,SubagentLimitMiddleware 在模型返回后统计本条消息的 task 调用,并结合委派账本中当前 run_id 的已用次数,只保留单次上限与本次运行剩余额度共同允许的调用。超出部分被裁去,不会转成等待队列;总量耗尽则附加限额说明,要求主 Agent 基于已有结果收敛。委派限额与是否传入父会话快照是独立控制。委派限额实现。

    两种模式都不等于复制父 Agent 的全部运行状态。工具所需的工作区、线程或上传文件信息可由宿主另外传入,这与复制聊天历史是两条路径。父方后来出现新要求时,需要明确补充或重新派发;子任务完成后,再将报告与产物交回父 Agent 汇总。

  2. Agent 沙箱工作区如何设计和实现?

    展开收起

    核心回答

    DeerFlow 把沙箱工作区拆成线程文件目录、沙箱 Provider 和统一文件工具:按用户与线程定位上传文件、过程文件和最终产物,工具通过 Provider 读写对应环境。目录解决文件归属,Provider 决定实际在哪执行;本地文件模式与容器模式的隔离能力不同。

    例如整理上传资料并生成报告时,输入放 uploads,中间笔记放 workspace,报告放 outputs。普通子 Agent 委派会带入父任务的线程和文件上下文,因此可以读取已有材料、交回文件产物;这不等于每个子 Agent 都获得一台独立虚拟机。

    线程路径怎样变成工具可用的工作区?

    Paths 在用户作用域下,将线程文件组织到 {base_dir}/users/{user_id}/threads/{thread_id}/user-data/,下分 workspace、uploads、outputs;不带用户的旧路径另有兼容逻辑。ThreadDataMiddleware 根据运行上下文中的用户与线程,生成三个实际路径,放入 thread_data。模型侧使用固定虚拟路径 /mnt/user-data/...,不需要知道部署机器的真实目录。路径定义、线程路径注入。

    默认可以延迟到首次文件或命令工具调用时再初始化沙箱。ensure_sandbox_initialized() 在复用或申请环境前检查沙箱执行授权,并结合当前用户、线程校验缓存的沙箱身份;不能只拿历史状态中的 sandbox_id 就认定环境仍适用。初始化与复用。

    本地文件模式和容器后端有什么区别?

    LocalSandboxProvider 在宿主进程侧操作文件。 它按用户、线程缓存 LocalSandbox,把虚拟路径映射到对应实际目录。文件工具先校验允许路径,再解析真实路径并检查是否仍在当前线程目录内;自定义挂载还要遵守只读标记。当前示例配置默认使用此 Provider,并设置 allow_host_bash: false。开启宿主命令后,命令可以使用宿主真实路径,所以这些映射不能替代进程隔离。本地 Provider、路径边界检查。

    AioSandboxProvider 的本机后端才负责创建独立容器。 无 provisioner_url 时选择 LocalContainerBackend,由它管理 Docker 或 Apple Container;配置 provisioner 时改用远端后端。这里的“本机容器后端”和上面的“宿主文件 Provider”是两种实现。后端选择、容器运行时。

    本机容器通过挂载把当前线程三个目录暴露为相同的虚拟路径,因此后端写入上传文件后,容器可以直接读取。远端环境若不共享存储,则需要文件同步,不能只把同一个路径字符串传过去。线程目录挂载、远端同步配置。

    文件怎样从生成走到交付?

    以 /mnt/user-data/outputs/report.md 为例,write_file 先取得沙箱并确保线程目录存在;本地模式完成路径映射和越界检查后调用 sandbox.write_file(),容器模式由对应 Provider 的文件接口处理。读取也走同一沙箱抽象。主 Agent 和普通子 Agent 可以共享线程文件,因此并行工作宜分配不同产物路径,最后再汇总,不能把共享工作区当成自动冲突合并机制。文件读写入口、写入处理、委派携带文件上下文。

    文件准备好后,present_files 将路径规范到当前线程的 outputs 范围,并把虚拟路径加入状态中的 artifacts,供客户端展示。它负责登记交付引用,不负责生成文件或判断报告结论是否正确。产物呈现。

    环境释放与文件保留怎样配合?

    并发的主、子 Agent 执行分别持有沙箱租约,最后一个使用者释放后才进行相应环境回收,避免主任务先结束就关闭仍被子任务使用的环境。AIO 的健康环境可以进入预热池复用;本地 Provider 的 release() 则不删除线程文件。线程文件与执行环境分开管理,才便于后续读取产物和继续任务。租约生命周期、AIO 释放、本地释放。

    这种设计的取舍是:本地模式启动和调试简单;容器或远端 Provider 提供更独立的执行环境,但增加镜像、挂载、同步和回收成本。应按代码可信程度与部署方式选后端,目录隔离、工具授权和进程隔离分别落实。

    参考资料

    • 国内:DeerFlow:Sandbox 配置:依据本地模式、宿主命令开关和 AIO 容器模式的区别说明后端选择,并采用远端是否共享目录决定同步方式的约定。
    • 国内:JavaGuide:Harness Engineering:采用文件系统保存产物、执行环境承接工具动作的分工,解释线程文件与运行资源为何分开管理。
    • 国内:zero2Agent:Agent 文件系统访问边界与工作目录隔离:采用路径校验与运行隔离各有职责的解释,说明本地路径映射不能视为宿主进程的安全边界。
    • 国内:WeThinkIn:Workspace 与抽象文件系统:采用统一文件接口与底层存储语义的区分,说明虚拟路径统一后,远端同步和并发文件归属仍需处理。
    • 国外:Docker Engine security:依据命名空间、资源限制与挂载配置的职责,说明使用容器后的隔离仍取决于实际权限和挂载范围。
    • 国外:Introduction to gVisor security:依据用户态应用内核及系统调用处理机制,解释通用答案中普通容器和更强隔离选项的区别。
  3. 如何校验 Agent 产生的确定性结果?

    展开收起

    核心回答

    DeerFlow 可以把确定性检查随子任务传递:主 Agent 在 task 中提供 acceptance_criteria,子任务执行结束后,程序读取实际文件或已记录的命令结果,逐项返回检查结果。模型的完成说明、子任务的 completed 状态和检查条件是否成立分别记录,不能互相替代。

    这条检查链适合确认文件存在、格式可解析和特定执行记录。研究结论是否有依据、数据口径是否正确,仍要依据需求另作核对;主 Agent 再根据具体未满足项安排修正或补证。

    把检查条件随子任务传递

    例如委派整理企业资料,要求交付结构化数据和摘要文件,可以附上两项条件:

    file:/mnt/user-data/outputs/companies.json json-valid
    file:/mnt/user-data/outputs/summary.md non-empty
    

    这是检查项示例。第一项检查文件能否按 UTF-8 JSON 解析;第二项检查摘要文件非空。即使两项都成立,数据也可能缺少企业名称、来源或日期,摘要也可能遗漏关键反证,因此不能把这两项扩成“研究结果正确”。必填字段可以再用 JSON Schema 校验,结论则需要对照原始来源;这些是业务侧应增加的判据。

    程序实际检查了什么

    普通 task 在子任务进入 COMPLETED,且委派携带检查条件时,才调用确定性检查器。检查器通过当前线程的工作区读取文件,归一化虚拟路径并核对可访问范围;支持的条件包括:

    • 文件存在、非空,以及 file_written 对应文件的读取确认。
    • json-valid 检查 JSON 语法,csv-columns 检查必需表头;它们不检查每行数据与业务含义。
    • tests_passed:<command> 对照已记录的 bash 执行,核对命令归属、状态与测试摘要;它不会在父任务里重新执行测试。

    检查有读取与证据边界。比如 JSON 文件超出该版 50,000 字节检查上限、测试命令或输出无法可靠归因、产生记录的 Shell 来源未知或为持久会话时,对应条件会保留为未验证。只看到退出码为零、构建成功或“没有测试”,都不应直接视为测试通过。检查器实现。

    失败、未知和执行结束怎样区分

    每项结果使用 checked 和 holds 记录,返回给主 Agent 的清单区分:

    • holds:这项执行条件已有证据支持。
    • does not hold:已检查,条件不成立,可以针对具体原因修正。
    • UNVERIFIED:现有检查或证据不足,需要补充验证。

    该版普通 task 的返回路径仍将子任务标为 completed,同时附上检查结果;条件不成立不会自动改成 failed,检查器异常时还可能没有清单。应用不能只读任务状态就宣布验收成功。检查调用与结果回传。

    主 Agent 的提示要求针对不成立的条件修补、对未知项补证,并复用已经确认的产物。这是后续决策要求,不是程序已经自动完成修复。若业务要求全部检查通过才能交付,应在应用交付层读取检查结果,并为字段、口径和来源等业务条件增加独立校验。主 Agent 的处置规则。