DeepSeek Harness 系列(08):多 Agent 协作——Subagent 与 Agent Teams

一个工具解决不了的问题

假设有个任务:对一个大型代码库做全面重构------检查所有文件的命名规范、找出所有循环依赖、整理接口定义。文件数量超过一千个。

你可以把这个任务交给一个 Agent,让它一个文件一个文件地处理。但问题很快出现:

  • 上下文窗口有限:处理到第 300 个文件时,前面的内容已经被推出窗口了
  • 工具没有 reasoning 能力:工具只能执行固定逻辑,无法根据"这个文件属于哪个子系统"来调整策略
  • 工具没有独立的对话历史:工具执行完就结束了,没有状态保留,下一次调用完全不记得上次看到了什么

多 Agent 的本质,就是把子任务委派给另一个有独立对话历史的 Agent。子 Agent 有自己的 Session,有自己的 reasoning 过程,可以用工具,有自己的上下文窗口。父 Agent 只需要告诉子 Agent"去处理这一批文件,告诉我结果"。

这和单纯调用工具的区别,就像是把一件事"外包给另一个人",而不是"启动一台机器"。


dsh 的 Subagent 机制

dsh 通过 Subagent 实现 Agent 委派。有两种启动方式:

  1. 模型调用 :模型决定委派某个子任务时,调用内置工具 subagent_spawn
  2. 代码调用 :在工具的 execute 函数里,通过 ctx.subagents.start() 直接启动

启动子 Agent 时,需要提供一个 SubagentStartRequest,关键字段如下:

typescript 复制代码
// 精简自 packages/subagent/subagent/src/types.ts 的 SubagentStartRequest
// 实际字段更多,这里只列最核心的部分

interface SubagentStartRequest {
  // 子 Agent 的初始提示词(ContentBlock 数组,支持文本、图片等)
  prompt: ContentBlock[]

  // 父 Agent 对象(提供工作目录、血缘关系信息)
  parent: Agent

  // 取消信号(允许父 Agent 中止子 Agent 的执行)
  signal: AbortSignal

  // 可选:限制子 Agent 可用的工具(只给它用得到的工具)
  toolFilter?: ToolRestriction

  // 可选:给子 Agent 一个专属的 Persona(覆盖全局部署 persona)
  persona?: string

  // 可选:结构化输出 schema(让子 Agent 返回 JSON 对象而非纯文本)
  outputSchema?: ObjectJsonSchema

  // 可选:最大委派深度(防止子 Agent 再启动子 Agent 形成无限递归)
  maxDepth?: number
}

重点看几个字段:

toolFilter:子 Agent 默认继承父 Agent 的所有工具,但大多数情况下你只想给它一个受限的工具集。比如做代码分析的子 Agent,只需要读文件,不需要写文件。

persona:子 Agent 可以有自己的系统提示词前缀。你可以给它一个专门的角色定义,让它在那个角色里工作,而不是继承全局 Persona。

outputSchema :这个字段让子 Agent 的输出不再是纯文本,而是结构化的 JSON 对象。父 Agent 拿到的 result.structured 是类型安全的。


一次性 vs 可继续

Subagent 有两种生命周期模式,区别在于父 Agent 是否需要多次和子 Agent"对话"。

一次性 Subagent(One-shot)

启动 → 子 Agent 完成所有工作 → 返回结果 → 结束。整个过程就一个来回,适合独立的子任务。

可继续 Subagent(Continuable)

子 Agent 启动后,会创建一个持久化的 Session。父 Agent 可以继续向它发消息,子 Agent 每次收到消息都会继续执行。适合需要多次往返的协作。

两种模式的对比:

scss 复制代码
一次性 Subagent:
  Parent ──start()──► Child 执行完整任务 ──result──► Parent
  (子 Agent 执行完毕后生命周期结束)

可继续 Subagent:
  Parent ──startContinuable()──► Child Session 创建,进入等待
  Parent ──sendMessage('第一批文件')──► Child 处理第一批 ──result──► Parent
  Parent ──sendMessage('第二批文件')──► Child 处理第二批 ──result──► Parent
  Parent ──sendMessage('汇总结果')───► Child 汇总 ──final result──► Parent
  (子 Agent Session 在整个过程中保持活跃,有完整的对话历史)

可继续 Subagent 的优势在于:子 Agent 的对话历史是连续的,它能记住前几轮处理了什么,在汇总阶段可以利用这些记忆。


通讯路径:谁能给谁发消息

多 Agent 场景下,一个自然的问题是:Agent 之间可以任意通讯吗?

答案是不能。dsh 对通讯路径有明确的约束:

通讯方向 是否允许 说明
父 → 子 ✅ 允许 要求子 Agent 的 parentSession 指向父 Agent
子 → 父 ✅ 允许 子 Agent 可以向父 Agent 发消息
兄弟之间 ❌ 不允许 同父的两个子 Agent 不能互发消息
跨代(祖父 → 孙子) ❌ 不允许 隔代通讯被拒绝
css 复制代码
       Parent
      /      \
  Child A   Child B
     |
   Grandchild

✅ Parent ──► Child A
✅ Child A ──► Parent
❌ Child A ──► Child B(兄弟不能通讯)
❌ Parent ──► Grandchild(跨代不行)

这个约束是有意为之的。允许任意通讯会让 Agent 网络变成一个难以追踪的消息图,调试时完全不知道消息从哪里来的。层级清晰,每个 Agent 的责任边界才清晰。


toolFilter 与 persona

工具过滤

给子 Agent 只分配它需要的工具,是多 Agent 系统里最重要的权限控制手段:

typescript 复制代码
// 子 Agent 只能使用以下三个工具
// 父 Agent 拥有的其他工具(比如 write_file、execute_shell)对它不可见
toolFilter: {
  allow: ['read_file', 'search_files', 'list_files'],
  // 也可以用 deny 来排除特定工具
  // deny: ['write_file', 'execute_shell']
}

为什么要这样做?

原因不只是安全,更是任务专注度 。一个分析代码的子 Agent,如果工具列表里有 write_file,模型可能会"顺手"修改它觉得有问题的代码,这不是你期望的行为。给它只读工具,它就只能读。

Persona

typescript 复制代码
// 子 Agent 使用专属的系统提示词前缀
// 这会覆盖全局部署 persona,让子 Agent 在一个专门的角色里工作
persona: 'You are a specialized test writer. Focus only on writing unit tests for the given code. Do not modify existing source files, do not suggest refactoring.'

Persona 的意义在于:父 Agent 可以是一个通用助手,而它启动的子 Agent 可以是一个极度专注于单一任务的专家。每次创建子 Agent 都重新定义角色,不受全局配置的干扰。


结构化输出(outputSchema)

子 Agent 默认返回文本。但在自动化流水线里,父 Agent 需要的往往是可以直接处理的数据,而不是一段自然语言描述。

outputSchema 解决这个问题:

typescript 复制代码
// 让子 Agent 返回结构化 JSON------父 Agent 不需要解析文本(伪代码)
const result = await ctx.subagents.start({
  prompt: [{ type: 'text', text: '分析这段代码,找出所有 bug。' }],
  parent: currentAgent,
  signal,
  outputSchema: {
    type: 'object',
    properties: {
      bugs: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            file:        { type: 'string' },
            line:        { type: 'number' },
            severity:    { type: 'string', enum: ['critical', 'major', 'minor'] },
            description: { type: 'string' },
          },
        },
      },
    },
  },
})

// result.text 是子 Agent 的文本输出(如果有)
// result.structured 是类型安全的 JSON 对象------直接用,不需要解析
console.log(result.structured.bugs)
// → [{ file: 'auth.ts', line: 42, severity: 'critical', description: '...' }, ...]

这让多 Agent 系统的数据流变得干净:子 Agent 的分析结果直接成为父 Agent 下一步决策的输入,不需要再加一层文本解析。


六种 Subagent Provider

dsh 支持多种底层实现,满足不同的部署场景:

Provider 说明 适用场景
spawn-in-process 在同一进程内创建新 Agent 实例 本地开发、测试
fork-in-process Fork 当前 Session,子 Agent 继承前缀历史 需要继承上下文的子任务
dsh-sdk 通过 dsh SDK 启动独立运行时 需要完全隔离的子 Agent
acp 通过 ACP 协议与远程 Agent 通讯 远程 Agent 集群
codex 调用 Codex Agent 代码专项任务
claude-code 调用 Claude Code 代码专项任务

大多数本地开发场景用 spawn-in-process 就够了。需要隔离(比如不同子 Agent 有不同的文件系统权限)时,考虑 dsh-sdk


实验性功能:Agent Teams

Subagent 是层级模型------父子关系明确,通讯路径严格。但有些协作场景天然是"平等的":多个专家 Agent 围绕同一个任务并行工作,谁的结果先出来谁先汇报。

Agent Teams 是 dsh 正在开发的多 Agent 协作框架,通过 ctx.agentTeams(实验性服务)访问:

  • roster(花名册):注册团队成员,每个成员有名字和角色
  • mailbox(邮箱):成员之间通过邮箱收发消息
  • task board(任务板):团队共享的任务列表,成员可以认领和提交任务
  • 协调者模式:可以有一个协调者 Agent 分发任务、汇总结果

和 Subagent 的层级模型相比,Teams 更接近"团队协作":没有严格的父子层级,成员之间可以平等通讯,任务可以动态分配。

css 复制代码
Subagent 模型(层级):        Agent Teams 模型(平等):

     Orchestrator                   ┌──────────────────┐
    /     |      \                  │    task board     │
  子A    子B    子C                 └──────────────────┘
(严格父子关系,                  Agent A ◄──► Agent B
 单向委派)                         │              │
                                   └────► Agent C ◄┘
                                   (平等通讯,动态协作)

注意:Agent Teams 目前是实验性功能,API 可能在后续版本中变化。生产环境中使用前请确认版本稳定性。


实战:主 Agent 调用子 Agent

下面是一个完整的模式示例:主 Agent 定义一个工具,该工具内部启动子 Agent 完成专项分析任务。

typescript 复制代码
// 伪代码:主 Agent 的工具定义,委派子 Agent 做代码分析
const analyzeCodebaseTool = defineTool({
  name: 'analyze_codebase',
  description: '启动一个专门的子 Agent,对指定代码库目录做全面分析。',
  parameters: {
    directory:   { type: 'string', required: true, description: '要分析的目录路径' },
    focus:       { type: 'string', required: true, description: '分析重点(例如:命名规范、依赖关系)' },
  },

  async execute(args, exec) {
    // 启动子 Agent(伪代码,实际由 ctx.subagents.start() 实现)
    const result = await ctx.subagents.start({
      prompt: [{
        type: 'text',
        text: `分析目录 ${args.directory},重点关注:${args.focus}。按文件逐一检查,汇总问题清单。`,
      }],
      parent: exec.agent,   // 传入父 Agent,建立血缘关系
      signal: exec.signal,  // 传入取消信号,父 Agent 取消时子 Agent 也停止

      // 子 Agent 只能读文件,不能修改代码
      toolFilter: {
        allow: ['read_file', 'list_files', 'search_files'],
      },

      // 专属角色:代码审查专家
      persona: 'You are a code review expert. Read code carefully, identify issues, and provide a structured summary. Do not modify any files.',

      // 要求返回结构化 JSON
      outputSchema: {
        type: 'object',
        properties: {
          issues: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                file:        { type: 'string' },
                issue:       { type: 'string' },
                severity:    { type: 'string', enum: ['high', 'medium', 'low'] },
              },
            },
          },
          summary: { type: 'string' },
        },
      },
    })

    // result.structured 是类型安全的 JSON,直接使用
    const { issues, summary } = result.structured
    return `分析完成。共发现 ${issues.length} 个问题。\n${summary}`
  },
})

这个模式的核心价值:父 Agent 不需要知道子 Agent 怎么分析代码,它只需要拿到结构化结果,继续做后续决策。


和其他框架的对比

框架 多 Agent 模型 与 dsh 的主要区别
Claude Code Agent tool 也是 spawn subagent 接口更简单,但没有 toolFilter / persona / outputSchema
LangGraph 图状工作流,固定拓扑 dsh Subagent 是动态的------由模型在运行时决定何时委派,而不是预先定义的图
AutoGen 对话式多 Agent,Agent 直接互发消息 dsh 是层级模型,有明确父子关系;兄弟 Agent 不能直接通讯

简单说:LangGraph 适合流程固定的场景,dsh Subagent 适合流程由模型动态决定的场景。


系列下一篇

下一篇讲 可观测性:Agent 在运行时做了什么,怎么知道?dsh 提供了哪些机制来追踪工具调用、Token 消耗、子 Agent 血缘树,以及如何在生产环境里接入日志系统。


PrimeSkills 可以找到已在真实企业场景验证过的 AI Agent 技能和工作流,不是演示级的,是用在实际项目里的。

更多内容见我的个人主页

相关推荐
xiaoduo AI1 小时前
电商用智能客服机器人后,7×24 接待是怎么跑起来的
人工智能·智能客服·电商·ai客服·智能客服机器人
2601_954811821 小时前
人工智能教学设备云桌面集控:GPU算力共享与教学环境隔离方案 — 架构
人工智能
Csvn1 小时前
第 23 章 性能、成本与部署
人工智能·aigc·agent
一切皆是因缘际会2 小时前
边缘端轻量化
人工智能
海宇AI2 小时前
零信任架构实战:基于海宇柠檬查出险-登记证构建自动化车抵贷核保网关
java·人工智能·架构·自动化
海带紫菜菠萝汤2 小时前
大模型本地部署踩坑实录:显存不足、依赖冲突、推理慢的完整排查
人工智能·ai·大模型
斯维赤2 小时前
AI Agent学习之路 | Prompt Engineering(提示词工程)的三板斧核心技巧
人工智能·学习·prompt
日常通勤穿搭3 小时前
电商 AI 生图工具横评|AI 生成主图、详情套图工具哪家性价比高
人工智能·aigc·电商美工