Code Reader
首页
帮助
设计文档
首页
帮助
设计文档
  • Task 工具

Task 工具

Task 工具用于启动子 Agent 处理复杂多步骤任务,支持并行执行和实时进度追踪。

概述

Task 工具是 OpenCode 中实现 Agent 通信的核心工具。它允许父 Agent 启动子 Agent 处理特定任务,创建独立的子会话,并通过事件机制实现实时进度追踪。Task 工具支持并行启动多个 Agent,以及继续现有的任务会话。

定义位置

packages/opencode/src/tool/task.ts:23-91

参数说明

参数名类型必填说明
descriptionstring是任务简短描述(3-5 个词)
promptstring是Agent 要执行的任务描述
subagent_typestring是要使用的 Agent 类型
session_idstring否现有任务会话 ID(用于继续)
commandstring否触发此任务的命令

TypeScript 类型定义

export type TaskInput = {
  description: string
  prompt: string
  subagent_type: string
  session_id?: string
  command?: string
}

export type TaskOutput = {
  title: string
  metadata: {
    summary: ToolPartSummary[]
    sessionId: string
    model: {
      modelID: string
      providerID: string
    }
  }
  output: string
}

工作流程

Task 工具的完整执行流程:

详细步骤

1. 权限检查(行 46-55)

if (!ctx.extra?.bypassAgentCheck) {
  await ctx.ask({
    permission: "task",
    patterns: [params.subagent_type],
    always: ["*"],
    metadata: {
      description: params.description,
      subagent_type: params.subagent_type,
    },
  })
}

2. 创建或获取子 Session(行 62-98)

const session = await iife(async () => {
  if (params.session_id) {
    const found = await Session.get(params.session_id).catch(() => {})
    if (found) return found
  }

  return await Session.create({
    parentID: ctx.sessionID,
    title: params.description + ` (@${agent.name} subagent)`,
    permission: [
      /* 限制权限 */
    ],
  })
})

3. 设置事件监听(行 117-38)

const parts: Record<string, {...}> = {}
const unsub = Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
  if (evt.properties.part.sessionID !== session.id) return
  if (evt.properties.part.messageID === messageID) return
  if (evt.properties.part.type !== "tool") return

  const part = evt.properties.part
  parts[part.id] = { id, tool, state }

  ctx.metadata({
    title: params.description,
    metadata: {
      summary: Object.values(parts),
      sessionId: session.id,
      model,
    },
  })
})

4. 执行子 Agent(行 147-62)

const result = await SessionPrompt.prompt({
  messageID,
  sessionID: session.id,
  model: { modelID, providerID },
  agent: agent.name,
  tools: {
    todowrite: false,
    todoread: false,
    ...(hasTaskPermission ? {} : { task: false }),
  },
  parts: promptParts,
})

5. 实时更新元数据 通过事件监听持续更新父 Agent 知道的子任务进度。

6. 收集结果(行 164-76)

const messages = await Session.messages({ sessionID: session.id })
const summary = messages
  .filter((x) => x.info.role === "assistant")
  .flatMap((msg) => msg.parts.filter((x: any) => x.type === "tool"))
  .map((part) => ({
    id: part.id,
    tool: part.tool,
    state: { status, title },
  }))

7. 取消事件监听(行 163)

unsub()

8. 返回结果(行 178-88)

const text = result.parts.findLast((x) => x.type === "text")?.text ?? ""
const output = text + "\n\n" + ["<task_metadata>", `session_id: ${session.id}`, "</task_metadata>"].join("\n")

return {
  title: params.description,
  metadata: {
    summary,
    sessionId: session.id,
    model,
  },
  output,
}

事件监听机制

Task 工具通过监听 MessageV2.Event.PartUpdated 事件实现实时进度追踪。

监听的事件

事件名类型说明
PartUpdated{part: MessageV2.Part}消息部分更新

事件过滤逻辑

const unsub = Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
  // 只监听子会话的事件
  if (evt.properties.part.sessionID !== session.id) return

  // 排除初始消息
  if (evt.properties.part.messageID === messageID) return

  // 只处理工具类型
  if (evt.properties.part.type !== "tool") return

  // 更新进度
  const part = evt.properties.part
  parts[part.id] = {
    id: part.id,
    tool: part.tool,
    state: {
      status: part.state.status,
      title: part.state.status === "completed" ? part.state.title : undefined,
    },
  }

  ctx.metadata({
    title: params.description,
    metadata: {
      summary: Object.values(parts).sort((a, b) => a.id.localeCompare(b.id)),
      sessionId: session.id,
      model,
    },
  })
})

进度更新结构

type ToolPartSummary = {
  id: string
  tool: string
  state: {
    status: "running" | "completed" | "error"
    title?: string // 仅在 status === "completed" 时存在
  }
}

结果返回格式

Task 工具返回给父 Agent 的结果包含以下内容:

{
  title: string  // 任务标题
  metadata: {
    summary: ToolPartSummary[]  // 工具执行摘要
    sessionId: string             // 子会话 ID
    model: {
      modelID: string
      providerID: string
    }
  }
  output: string  // 子 Agent 的文本结果 + session_id 元数据
}

输出示例

子 Agent 返回的文本内容...

<task_metadata>
session_id: sess_child123
</task_metadata>

上下文传递

传递给子 Session 的内容

参数来源说明
parentIDctx.sessionID父会话 ID
titleparams.description + ...子会话标题
permission限制规则权限规则(通常限制 task、todo)

继承的全局上下文

子会话通过 Instance 单例继承以下全局上下文:

上下文来源说明
projectIDInstance.project.id项目 ID
directoryInstance.directory当前工作目录
worktreeInstance.worktree项目根目录

子会话独立内容

子会话有独立的内容,不继承自父会话:

  • 消息历史(Messages)
  • summary(代码摘要)
  • share(分享信息)
  • time(时间戳)

典型使用场景

场景 1:启动单个子 Agent

// 父 Agent 调用
const result = await TaskTool.execute(
  {
    description: "分析代码库结构",
    prompt: "分析当前项目的代码组织方式,返回主要模块和它们的职责",
    subagent_type: "explore",
  },
  ctx,
)

console.log(result.output)
// 子 Agent 返回:
// "项目采用标准的 MVC 架构:
// - src/controllers/: 包含路由和控制器逻辑
// - src/models/: 包含数据模型和数据库交互
// - src/views/: 包含视图组件
//
// <task_metadata>
// session_id: sess_child123
// </task_metadata>"

console.log(result.metadata.sessionId) // "sess_child123"
console.log(result.metadata.summary)
// [
//   { id: "part1", tool: "glob", state: { status: "completed", title: "查找文件" } },
//   { id: "part2", tool: "read", state: { status: "completed", title: "读取文件" } }
// ]

场景 2:并行启动多个 Agent

// 父 Agent 并行启动多个子 Agent
const results = await Promise.all([
  TaskTool.execute(
    {
      description: "分析前端架构",
      prompt: "分析前端代码的组织方式和技术栈",
      subagent_type: "explore",
    },
    ctx,
  ),
  TaskTool.execute(
    {
      description: "分析后端架构",
      prompt: "分析后端 API 的设计和实现",
      subagent_type: "explore",
    },
    ctx,
  ),
  TaskTool.execute(
    {
      description: "检查测试覆盖",
      prompt: "检查项目的测试覆盖率和测试文件",
      subagent_type: "explore",
    },
    ctx,
  ),
])

// 每个结果包含对应的子会话信息
for (const result of results) {
  console.log(`${result.title}: ${result.metadata.sessionId}`)
}

场景 3:继续现有的任务会话

// 第一次调用,创建子会话
const result1 = await TaskTool.execute(
  {
    description: "复杂的多步骤任务",
    prompt: "执行步骤 1...",
    subagent_type: "general",
  },
  ctx,
)

const sessionId = result1.metadata.sessionId

// 后续继续同一个会话
const result2 = await TaskTool.execute(
  {
    description: "继续任务",
    prompt: "执行步骤 2...",
    subagent_type: "general",
    session_id: sessionId, // 👈 指定现会话 ID
  },
  ctx,
)

// result1 和 result2 使用同一个子会话

场景 4:自定义子 Agent

// 假设配置中定义了自定义 Agent
// {
//   "agent": {
//     "my-custom-agent": {
//       "description": "专门处理文档生成",
//       "mode": "subagent",
//       "permission": { "*": "allow" }
//     }
//   }
// }

const result = await TaskTool.execute(
  {
    description: "生成 API 文档",
    prompt: "根据 API 端点代码生成详细的 API 文档",
    subagent_type: "my-custom-agent",
  },
  ctx,
)

场景 5:执行自定义命令

// 使用 Task 工具执行自定义 slash 命令
const result = await TaskTool.execute(
  {
    description: "检查文件",
    prompt: "/check-file path/to/file.py", // 👈 完整的命令调用
    subagent_type: "command",
    command: "/check-file", // 👈 触发命令
  },
  ctx,
)

权限处理

Task 工具创建子会话时应用默认的权限限制:

默认禁用的工具

permission: [
  {
    permission: "todowrite",
    pattern: "*",
    action: "deny", // 👈 防止子 Agent 管理父 Agent 的 todo
  },
  {
    permission: "todoread",
    pattern: "*",
    action: "deny", // 👈 防止子 Agent 读取父 Agent 的 todo
  },
  ...(hasTaskPermission
    ? []
    : [
        {
          permission: "task",
          pattern: "*",
          action: "deny", // 👈 防止无限递归调用
        },
      ]),
]

权限说明

工具动作原因
todowritedeny防止子 Agent 管理父 Agent 的 todo
todoreaddeny防止子 Agent 读取父 Agent 的 todo
taskdeny(可选)防止子 Agent 再次调用 task,避免递归

实验性工具权限

...(config.experimental?.primary_tools?.map((t) => ({
  pattern: "*",
  action: "allow",
  permission: t,  // 👈 允许实验性的主要工具
})) ?? []

工具描述

Task 工具的描述会动态包含可用子 Agent 列表:

模板:packages/opencode/src/tool/task.txt:1

Launch a new agent to handle complex, multistep tasks autonomously.

Available agent types and tools they have access to:
{agents}

When using Task tool, you must specify a subagent_type parameter to select which agent type to use.

动态生成的描述:packages/opencode/src/tool/task.ts:32-37

const description = DESCRIPTION.replace(
  "{agents}",
  accessibleAgents
    .map((a) => `- ${a.name}: ${a.description ?? "This subagent should only be called manually by user."}`)
    .join("\n"),
)

实际效果:

Launch a new agent to handle complex, multistep tasks autonomously.

Available agent types and tools they have access to:
- general: General-purpose agent for researching complex questions and executing multi-step tasks.
- explore: Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns...
- compaction: [hidden]
- title: [hidden]
- summary: [hidden]

When using Task tool, you must specify a subagent_type parameter to select which agent type to use.

对象关系

最佳实践

1. 明确的任务描述

prompt 参数应包含详细的任务要求:

// ✅ 好
const result = await TaskTool.execute({
  description: "分析代码架构",
  prompt: "分析当前项目的代码组织方式。请返回:
  1. 主要模块列表及其职责
  2. 模块之间的依赖关系
  3. 使用的架构模式(如 MVC、微服务等)
  4. 任何值得注意的设计决策",
  subagent_type: "explore",
}, ctx)

// ❌ 差
const result = await TaskTool.execute({
  description: "分析",
  prompt: "分析代码",
  subagent_type: "explore",
}, ctx)

2. 并行执行最大化性能

尽可能并行启动多个独立的任务:

// ✅ 好:并行执行
const [result1, result2] = await Promise.all([
  TaskTool.execute({ description: "分析前端", prompt: "...", subagent_type: "explore" }, ctx),
  TaskTool.execute({ description: "分析后端", prompt: "...", subagent_type: "explore" }, ctx),
])

// ❌ 差:顺序执行
const result1 = await TaskTool.execute({ description: "分析前端", ... }, ctx)
const result2 = await TaskTool.execute({ description: "分析后端", ... }, ctx)

3. 利用事件监听提供实时反馈

父 Agent 通过监听事件实时获得子 Agent 的执行进度:

// Task 工具内部自动实现监听
// 父 Agent 的 metadata 会实时更新:
// result.metadata.summary 会包含所有已完成的工具执行

4. 合理限制子 Agent 权限

根据子 Agent 的性质合理限制其工具权限:

  • 研究型 Agent(explore):禁用写操作
  • 代码生成型 Agent(general):允许读写操作
  • 特殊用途 Agent:细粒度权限控制

5. 处理结果并反馈用户

子 Agent 的结果不会直接显示给用户,父 Agent 应整合并反馈:

const result = await TaskTool.execute({
  description: "分析代码",
  prompt: "分析代码库结构",
  subagent_type: "explore",
}, ctx)

// 父 Agent 将结果反馈给用户
return `代码分析完成:${result.output}

错误处理

常见错误

1. 未知的 Agent 类型

Error: Unknown agent type: invalid-agent is not a valid agent type

解决:检查 Agent 配置,确保 subagent_type 正确。

2. 权限被拒绝

如果用户拒绝 task 权限,Task 工具会抛出权限错误。

解决:检查用户权限配置,确保允许 task 工具调用指定的 subagent_type。

3. 子会话执行失败

子 Agent 执行失败时,错误会通过事件传播到父会话。

解决:检查子 Agent 的错误日志和输出。

相关文档

  • Agent - Agent 定义
  • Session - 会话对象
  • Message - 消息对象
  • Permission - 权限系统
  • Events - 事件系统

变更历史

版本变更内容日期
v1初始 Task 工具架构-
v1.1添加实时进度追踪(事件监听)-
v1.2添加 session_id 参数支持继续任务-