ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

AI Agent 项目赏析:DeerFlow 2.0 的 Harness 设计——一个能“长跑”的 SuperAgent 如何用 LangGraph 编排?

AI Agent 项目赏析:DeerFlow 2.0 的 Harness 设计——一个能“长跑”的 SuperAgent 如何用 LangGraph 编排? 1. 为什么“长跑型” Agent 必须围绕失败来设计DeerFlow 2.0 是一个开源的 long-horizon SuperAgent Harness简单说它能让一个 Agent 连续跑几分钟到几小时去完成调研、写代码、生成报告这类长任务。它适合谁适合已经写过单轮 Agent Demo、想进一步复刻“能长跑、跑不死”的 Agent 系统的开发者。它最核心的能力不是让模型多转几圈而是把模型放进一个能容忍失败的执行环境里。我先把结论放前面普通 Agent 和长跑型 Agent 的分界线不是模型强弱而是失败恢复能力。一个 Demo 级 Agent 的假设是“一次执行很快结束”所以它只关心成功路径而一个真正要跑几十分钟甚至几小时的 Agent必须假设“中途一定会失败”——网络会抖、模型会抽风、进程会被 kill、租约会过期、子代理会卡住、用户会中途取消。DeerFlow 2.0 的设计哲学可以压缩成一句话长任务的本质不是“跑得快”而是“跑不死”。它建在 LangGraph 之上用create_agent返回标准的CompiledStateGraph把推理循环、状态图、检查点、流式、human-in-the-loop 全部交给 LangGraph自己只专注叠加 Harness 层的能力Sub-Agent、Run、Worker、RunStore、Memory、Skills、Sandbox、Guardrail、容错。这篇文章我会拆三块第一DeerFlow 的 Harness 分层和 LangGraph 编排骨架第二Run / Worker / RunStore 这套“把长任务当一等公民”的设计第三给你一份可复制的 LangGraph 节点配置骨架并用 TaoToken 统一 Key/API 通道接入模型调用最后跑一次多轮任务验证。2. TaoToken 前置统一 Key 与 API 通道在复刻长跑型 Agent 之前先把模型调用通道理顺。DeerFlow 这类 Harness 会频繁调用模型如果每个子代理、每个 Worker 都各自维护一套 Key 和 Base URL排障会非常痛苦。我的做法是用 TaoToken 统一 Key/API 通道让 Harness 里所有模型调用都走同一个入口。TaoToken 官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 地址不加 UTMhttps://taotoken.net/api你需要先拿到一个 API Key然后把它作为环境变量注入到 Harness 进程里。这样无论是主 Agent 还是 Sub-Agent读的都是同一个OPENAI_API_KEY和OPENAI_BASE_URL切换模型时只改配置不动代码。# 在 Harness 运行环境里注入统一通道 export OPENAI_API_KEYsk-你的TaoTokenKey export OPENAI_BASE_URLhttps://taotoken.net/api注意Base URL 用https://taotoken.net/api不要在后面拼多余的路径具体模型名以控制台可用列表为准。如果你要长期跑编码类 Agent建议单独看一下 Coding Plan它更适合高频、长时的编码任务如果只是验证模型连通性用模型对话页面点几下就能确认。Key 管理在 API Keys 页面接入细节在接入文档里。3. 可复制的 LangGraph 节点配置骨架下面这份骨架是我按 DeerFlow 的 Harness 思路抽出来的最小可跑版本。它不追求功能全而是把“长跑”需要的几个关键节点先立起来状态定义、模型节点、工具节点、检查点、以及一个带租约语义的 Run 记录。先定义状态。长任务的状态必须显式持久化不能只放在内存里from typing import Annotated, TypedDict from langgraph.graph.message import add_messages class AgentState(TypedDict): messages: Annotated[list, add_messages] run_id: str owner_worker_id: str | None lease_expires_at: str | None ownership_lost: bool stop_reason: str | None然后是模型节点。这里用create_agent组装和 DeerFlow 的做法一致把推理循环交给 LangGraphimport os from langchain.agents import create_agent from langchain_openai import ChatOpenAI def build_lead_agent(tools): model ChatOpenAI( modelgpt-4o-mini, # 以控制台可用模型为准 api_keyos.environ[OPENAI_API_KEY], base_urlos.environ[OPENAI_BASE_URL], temperature0, ) graph create_agent( modelmodel, toolstools, state_schemaAgentState, ) return graph接着是检查点。长跑型 Agent 的“后悔药”就在这里用户取消或进程崩溃后要能回滚from langgraph.checkpoint.memory import InMemorySaver checkpointer InMemorySaver() graph build_lead_agent(tools).compile(checkpointercheckpointer)生产环境不要用InMemorySaver换成持久化后端。DeerFlow 在backend/langgraph.json里把 checkpointer 指向async_provider.py:make_checkpointer思路就是“检查点必须可持久化、可恢复”。最后是 Run 记录。这是 DeerFlow 最值钱的设计把一次执行建模成可租赁、可恢复的对象from dataclasses import dataclass, field from datetime import datetime, timedelta dataclass class RunRecord: run_id: str owner_worker_id: str | None None lease_expires_at: str | None None ownership_lost: bool False status: str pending # pending/running/interrupted def renew_lease(self, worker_id: str, lease_seconds: int 30): self.owner_worker_id worker_id self.lease_expires_at ( datetime.utcnow() timedelta(secondslease_seconds) ).isoformat()owner_worker_id、lease_expires_at、ownership_lost这三个字段构成了基于租约的所有权模型。Worker 每隔lease_seconds/3续一次租约一旦进程死亡租约过期其他 Worker 就能接管这个孤儿 Run。4. 验证请求跑一次多轮任务配置好之后跑一次多轮任务来验证。我构造一个需要多步执行的任务观察检查点是否生效、Run 状态是否正确流转。import uuid from langchain_core.messages import HumanMessage run_id str(uuid.uuid4()) config {configurable: {thread_id: run_id}} record RunRecord(run_idrun_id) record.renew_lease(worker_idworker-1) result graph.invoke( { messages: [HumanMessage(content分三步调研 LangGraph 的检查点机制每步输出一句结论)], run_id: run_id, owner_worker_id: record.owner_worker_id, lease_expires_at: record.lease_expires_at, ownership_lost: False, stop_reason: None, }, configconfig, ) print(run_id:, run_id) print(status:, record.status) print(stop_reason:, result.get(stop_reason)) print(messages:, len(result[messages]))成功时你会看到run_id稳定输出messages数量随多轮递增stop_reason为None。如果中途触发循环检测或预算护栏stop_reason会变成loop_capped或token_capped而不是无限空转。再验证一次恢复能力。用同一个thread_id重新 invoke检查点会从上次状态继续resumed graph.invoke( {messages: [HumanMessage(content继续上一步补充第四步结论)]}, configconfig, ) print(resumed messages:, len(resumed[messages]))如果resumed的消息数在上次基础上继续增长说明检查点持久化生效这就是长跑型 Agent 的“断点续跑”能力。5. 本篇常见错排查报错一OPENAI_BASE_URL拼错导致 404。常见写法是https://taotoken.net/api/v1多拼了/v1。正确写法是https://taotoken.net/api模型名以控制台为准。报错二create_agent返回的不是 CompiledStateGraph。检查 LangChain 版本create_agent是新一代 Agent 原语不是langgraph.prebuilt的create_react_agent两者签名不同。报错三检查点没生效每次 invoke 都从头开始。大概率是thread_id每次变了或者 compile 时没传checkpointer。thread_id必须稳定它对应一个 Run 的谱系。报错四Run 状态卡在running不流转。检查心跳循环是否在跑。租约续期间隔是lease_seconds/3如果心跳线程被阻塞租约会过期其他 Worker 会误判为孤儿并接管。报错五子代理把父 Agent 卡死。DeerFlow 的做法是让子代理跑在独立的 asyncio 事件循环上。如果你在同一个循环里同步等待子代理父 Agent 会被阻塞。用run_on_isolated_subagent_loop这类隔离手段。报错六记忆无限膨胀。不要什么都记。DeerMem 的_fact_scope_gate_reason只保留“关于用户、持久、描述性”的事实自动丢弃临时信息和结论性信息。记忆的核心难题是“记什么”不是“怎么存”。6. 下一步把容错机制按需引入复刻长跑型 Agent最忌讳一上来就把 DeerFlow 全套组件搬过来。如果你的项目只有一个 Agent、三个 Tool、跑一次就结束你只需要 Agent Tool Registry Checkpointer。等问题真的出现——进程会死、任务会卡、用户会取消、要并行子任务、记忆会膨胀——再逐步增加 Run/Worker、RunStore、租约与孤儿恢复、Sub-Agent、记忆门控。验证模型连通性可以去模型对话页面长期编码或 Agent 任务建议看 Coding PlanKey 和接入细节分别在 API Keys 与接入文档。把模型调用通道统一到 TaoToken 之后你就能把精力集中在 Harness 的容错设计上而不是浪费在到处维护 Key 上。
返回列表