ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Harness DeepAgent 实战:长任务 Agent 的任务编排、中间件与 Demo 落地(TaoToken 配置骨架)

Harness DeepAgent 实战:长任务 Agent 的任务编排、中间件与 Demo 落地(TaoToken 配置骨架) 1. 长任务 Agent 为什么总在第三步就崩如果你用普通create_agent跑过稍微长一点的任务大概率见过这些场面跑到第五轮工具调用上下文已经塞满模型开始胡编某个搜索工具返回两万字后面每一轮 ReAct 都把这坨内容重新带一遍token 账单肉眼可见地涨主 Agent 一会儿自己搜、一会儿自己评分子任务边界糊成一团中途想加个人工确认结果消息历史里留下一个没有对应ToolMessage的AIMessage(tool_calls[...])下一轮直接报协议错误。这些不是模型不够聪明是 Harness 层没搭好。DeepAgent 就是冲着这类问题来的它本质上是基于 LangChaincreate_agent/ LangGraph 的长任务 Agent Harness在普通「模型 工具循环」外面套了一层工程外壳——文件工作区、任务列表、子代理、上下文压缩、大结果 offload、skills、memory、human-in-the-loop、prompt caching。它解决的不是「模型会不会更聪明」而是「长任务怎么规划、大上下文怎么管、工具结果太大怎么办、子任务怎么隔离、多步骤怎么持续跑」。这篇聚焦一件事在 Harness DeepAgent 里把任务编排和中间件设计跑通并用 TaoToken 作为统一 Key / API 通道交付一份可复制的settings.json/config.toml配置骨架、中间件挂载示例和端到端验证动作。适合已经在写 Agent、但被长任务上下文和子任务编排卡住的开发者也适合想先跑一个能演示的 Demo 再决定要不要上生产的人。2. 前置TaoToken 统一 Key 与 API 通道DeepAgent 本身不绑定模型供应商它通过 LangChain 的模型标识比如openai:gpt-5.5这种字符串去调模型。问题在于长任务 Demo 往往要试好几个模型、切不同通道如果每个供应商都单独配一套 Key 和环境变量配置会散得到处都是。TaoToken 在这里的角色是统一入口一个 Key、一个 API 地址兼容 OpenAI 风格的调用方式模型对话、Coding Plan、API Keys 管理都在同一套控制台里。先把通道准备好后面所有配置都指向它。第一步去控制台拿 Key。打开https://taotoken.net/console登录后在 API Keys 页面创建一个新 Key复制出来。这个 Key 只显示一次建议直接写进环境变量而不是硬编码进代码。第二步确认 API 基地址。TaoToken 的 API 入口是https://taotoken.net/api注意这个地址不带任何查询参数配置里就写这个。第三步把 Key 和 Base URL 落到环境变量。Linux / macOS 下export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows PowerShell$env:TAOTOKEN_API_KEYsk-你的key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你更习惯用配置文件管理可以建一个settings.json把通道信息集中放进去后面 DeepAgent 初始化时读它{ llm: { provider: openai-compatible, base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, default_model: gpt-5.5, timeout_seconds: 120, max_retries: 3 }, agent: { workspace_root: ./agent_workspace, skills_dir: ./skills, memory_file: ./memory/AGENTS.md } }如果你用 TOML 风格比如某些 CLI 工具或自建 runner等价写法[llm] provider openai-compatible base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY default_model gpt-5.5 timeout_seconds 120 max_retries 3 [agent] workspace_root ./agent_workspace skills_dir ./skills memory_file ./memory/AGENTS.md这里有个容易踩的点base_url不要写成带/v1或带 UTM 参数的地址LangChain 的 OpenAI 兼容客户端会自己拼路径多写一段就 404。另外api_key_env存的是环境变量名不是 Key 本身这样配置文件可以进版本库而不泄露密钥。通道就绪后先做一次最小连通性验证别等 DeepAgent 跑起来才发现 Key 不对import os from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], ) resp client.chat.completions.create( modelgpt-5.5, messages[{role: user, content: 只回复两个字通了}], ) print(resp.choices[0].message.content)看到「通了」就说明通道没问题可以进 DeepAgent 了。想先在网页里手动试模型可以直接用模型对话页面https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite不用写代码就能确认某个模型在当前通道下是否可用。3. 可复制配置DeepAgent 初始化与中间件挂载DeepAgent 的入口是create_deep_agent它底层还是调create_agent但会预先组装一套 middleware。理解这一点很关键你看到的「默认能力」其实都是中间件堆出来的所以想定制就得知道每个中间件管什么。先看主 Agent 的默认中间件栈中间件是否默认主要作用TodoListMiddleware默认提供write_todos维护任务列表FilesystemMiddleware默认文件工具、虚拟文件系统、大结果 offload、权限SubAgentMiddleware通常默认提供task工具调用子 AgentSummarizationMiddleware默认历史过长时压缩上下文PatchToolCallsMiddleware默认修补悬空 tool call避免协议报错条件性中间件按需触发传了skills[...]才有 SkillsMiddleware传了memory[...]才有 MemoryMiddleware传了interrupt_on才有 HumanInTheLoopMiddleware异步子代理、prompt caching、工具排除同理。下面是一份可以直接跑的初始化骨架把通道配置、backend、子代理、skills、memory、人工审批都挂上import json import os from deepagents import create_deep_agent from deepagents.backends.filesystem import FilesystemBackend from langgraph.checkpoint.memory import MemorySaver with open(settings.json, r, encodingutf-8) as f: cfg json.load(f) llm_cfg cfg[llm] agent_cfg cfg[agent] # 把通道信息注入环境LangChain 的 openai 兼容客户端会读取 os.environ.setdefault(OPENAI_API_KEY, os.environ[llm_cfg[api_key_env]]) os.environ.setdefault(OPENAI_BASE_URL, llm_cfg[base_url]) backend FilesystemBackend(root_diragent_cfg[workspace_root]) checkpointer MemorySaver() agent create_deep_agent( modelfopenai:{llm_cfg[default_model]}, backendbackend, tools[], # 主 Agent 不直接持有业务工具避免绕过子代理 subagents[researcher_subagent, critic_subagent], skills[agent_cfg[skills_dir]], memory[agent_cfg[memory_file]], checkpointercheckpointer, interrupt_on{ write_file: True, delete: True, }, system_prompt( 你是一名研究项目主管必须遵守\n 1. 论文搜索必须通过 task 调用 subagent_typeresearcher。\n 2. 论文评分必须通过 task 调用 subagent_typecritic。\n 3. 你不得亲自搜索或评分。\n 4. 子代理返回前不得撰写最终报告。\n 5. 最终中文综合报告只能由你根据子代理结果撰写。\n 6. 写入或删除文件前必须等待人工确认。 ), )子代理定义要遵循「职责单一 工具隔离」原则。researcher 只拿搜索工具critic 只拿评分工具主 Agent 一个业务工具都不拿from langchain.tools import tool tool def search_papers(query: str, limit: int 5) - list[dict]: 根据研究主题搜索论文返回候选论文的结构化列表。 return [ { title: fPaper about {query}, year: 2025, url: https://example.com/paper, abstract: A short abstract..., } ][:limit] tool def score_paper(paper: dict) - dict: 对单篇论文进行质量评分返回多维度评分和原因。 return { title: paper.get(title, ), novelty_score: 8, method_score: 7, evidence_score: 7, overall_score: 7.5, reason: 示例评分主题相关方法完整。, } researcher_subagent { name: researcher, description: 负责搜索论文并返回结构化候选列表不负责评分和最终报告。, system_prompt: ( 你是论文研究员。根据任务描述搜索论文返回结构化列表。 不要评分不要写最终综合报告。 ), tools: [search_papers], } critic_subagent { name: critic, description: 负责根据论文信息评分不负责搜索和最终报告。, system_prompt: ( 你是论文评审员。根据输入的论文列表逐篇评分输出结构化结果。 不要搜索新论文不要写最终综合报告。 ), tools: [score_paper], }这里的设计意图值得说清楚提示词决定模型「愿不愿意」调用子代理工具权限决定它「能不能」绕过子代理。只靠 prompt 写「请不要自己搜索」模型在压力下照样会自己调把search_papers从主 Agent 的tools里拿掉它想绕也没得绕。这是长任务编排里最容易被忽略的一层。如果你不想用create_deep_agent想理解它到底组装了什么可以用create_agent手动拼一遍from langchain.agents import create_agent from langchain.agents.middleware import TodoListMiddleware from deepagents.middleware.filesystem import FilesystemMiddleware from deepagents.middleware.subagents import SubAgentMiddleware from deepagents.middleware.patch_tool_calls import PatchToolCallsMiddleware from deepagents.middleware.skills import SkillsMiddleware agent create_agent( modelfopenai:{llm_cfg[default_model]}, tools[], system_prompt你是研究项目主管通过 task 协调子代理完成研究任务。, middleware[ TodoListMiddleware(), SkillsMiddleware(backendbackend, sources[agent_cfg[skills_dir]]), FilesystemMiddleware(backendbackend), SubAgentMiddleware( backendbackend, subagents[researcher_subagent, critic_subagent], ), PatchToolCallsMiddleware(), ], )生产里一般不建议为了「像 DeepAgent」而手动拼全部中间件除非你确实要深度定制。大多数情况下create_deep_agent更省事手动拼的价值在于调试和理解。4. 端到端验证从 invoke 到 offload 落盘配置写完跑一次完整调用看它是不是真的按编排走。result agent.invoke( { messages: [ { role: user, content: 请调研最近两年关于 RAG 评估方法的论文并输出中文综合报告。, } ] }, config{configurable: {thread_id: demo-001}}, ) for msg in result[messages]: print(type(msg).__name__, -, getattr(msg, content, )[:200])预期行为是这样的主 Agent 先调write_todos建任务列表然后发task(subagent_typeresearcher, ...)researcher 在独立上下文里跑搜索只把结构化列表作为ToolMessage回传主 Agent 再发task(subagent_typecritic, ...)critic 逐篇评分回传最后主 Agent 综合两份结果写报告。如果报告要落盘会触发write_file因为配了interrupt_on这里会暂停等人工确认。验证 offload 是否生效看工作区目录ls -R ./agent_workspace如果某个工具返回结果特别大你应该能在./agent_workspace/large_tool_results/下看到以tool_call_id命名的文件而主 Agent 的 messages 里只保留了 head/tail 预览和路径。需要读全文时Agent 会调read_file(file_path/large_tool_results/call_xxx, offset0, limit100)注意read_file本身不是 offload 工具它只是分页读取真正把大结果写出去的是 FilesystemMiddleware。这个区分很多人会搞混。验证子代理隔离可以在 trace 里看主 Agent 的 messages 里不应该出现 researcher 内部的搜索过程只有一条最终的ToolMessage。如果主 Agent 的上下文里塞满了搜索中间步骤说明子代理没隔离好或者主 Agent 自己把工具调了。验证人工审批观察write_file触发时是否暂停。用MemorySaver做 checkpointer 时恢复要继续传同一个thread_idfrom langgraph.types import Command resumed agent.invoke( Command(resume{approved: True}), config{configurable: {thread_id: demo-001}}, )跑通这一轮你就有了一个能演示的长任务 Agent任务列表可见、子任务隔离、大结果 offload、关键写入有人工闸门。想换模型再验证一遍直接在settings.json里改default_model通道不用动。5. 本篇常见错排查报错一AIMessage有tool_calls但没有对应ToolMessage。典型场景是中途用户插话、工具调用被取消、或从检查点恢复时历史不完整。PatchToolCallsMiddleware 会补一条说明性ToolMessage兜住协议但它不会重新执行失败工具也不会修错误参数。如果频繁触发说明你的中断 / 恢复链路有问题要去查 trace而不是指望中间件擦屁股。报错二主 Agent 不调task自己把活干了。两个原因prompt 太软或者主 Agent 手里有子代理专属工具。把「你可以在需要时使用 researcher」改成「论文搜索必须调用 tasksubagent_typeresearcher你不得直接执行搜索」同时把search_papers从主 Agent 的tools里移除。提示词管意愿工具权限管能力两个都要。报错三subagents[...]传了但没反应。传subagents只是注册告诉主 Agent 有哪些子代理可用真正执行要主 Agent 主动调task(subagent_typeresearcher, description...)。如果 profile 禁用了默认子代理task工具可能根本不暴露检查一下 harness profile 配置。报错四找不到 offload 的文件。默认StateBackend是虚拟文件系统路径/large_tool_results/call_xxx是虚拟路径不代表本机磁盘上真有这个目录。要落到真实磁盘得配FilesystemBackend(root_dir...)像上面骨架里那样。报错五上下文还是爆。SummarizationMiddleware 会压缩历史但压缩是有损的。订单 ID、审批状态、金额、权限这类强一致状态不要只存在自然语言历史里要落结构化 state 或数据库。另外检查 skills 和 memory 是不是塞太多——memory 是启动时常驻上下文比 skills 更容易污染旧 memory 过时了要清理。报错六并行子代理写同一文件冲突。同一轮发多个task时底层可以并行执行。给每个子代理设计独立的输出路径别复用同一个临时文件外部 API 要限流数据库写入要有事务或幂等。报错七审批太频繁拖慢任务。HumanInTheLoopMiddleware 不是所有工具都要挂。只对真正高风险的动作开审批删文件、写生产库、发邮件、调支付、改线上配置。审批提示要让人看懂「改什么、为什么改、风险是什么」否则审批人只能盲点通过等于没审。报错八prompt caching 没效果。caching 依赖模型供应商支持不支持的模型或接入方式下不要指望它降本。把降本重心放在 offload 和子代理隔离上这两个是 Harness 层自己能控制的。6. 下一步把 Demo 变成能长期跑的东西跑通 Demo 之后最该做的不是加更多工具而是把「确定性流程」和「开放性探索」分开。实际业务里不建议让一个 DeepAgent 从头管到尾更稳的是用 LangGraph / StateGraph 做骨架DeepAgent 只做复杂智能节点Graph 负责校验输入、判断任务类型、校验输出结构、人工审核、落库DeepAgent 节点负责开放性搜索、分析、长上下文、多工具探索。这样重试、审批、状态机这些确定性逻辑不会被塞进 prompt 里赌模型发挥。如果你要长期跑编码类或 Agent 类任务可以看下 Coding Plan 这条线https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite它更适合持续性的开发场景接入细节和参数说明在接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteKey 管理在 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite。通道地址统一是https://taotoken.net/api配置里就写这个别加多余路径。最后留一个判断标准帮你决定要不要上 DeepAgent如果 1–2 次工具调用能解决用普通 Agent如果需要 10 步、读写文件、多角色协作、长期上下文DeepAgent 会比普通 Agent 更接近可落地的工程方案。中间件不是越多越好每一个都对应一类长任务问题挂之前先问自己「不挂它会不会出事」答案是不会就别挂。
返回列表