Task 工具
Task 工具用于启动子 Agent 处理复杂多步骤任务,支持并行执行和实时进度追踪。
概述
Task 工具是 OpenCode 中实现 Agent 通信的核心工具。它允许父 Agent 启动子 Agent 处理特定任务,创建独立的子会话,并通过事件机制实现实时进度追踪。Task 工具支持并行启动多个 Agent,以及继续现有的任务会话。
定义位置
packages/opencode/src/tool/task.ts:23-91
参数说明
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
description | string | 是 | 任务简短描述(3-5 个词) |
prompt | string | 是 | Agent 要执行的任务描述 |
subagent_type | string | 是 | 要使用的 Agent 类型 |
session_id | string | 否 | 现有任务会话 ID(用于继续) |
command | string | 否 | 触发此任务的命令 |
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 的内容
| 参数 | 来源 | 说明 |
|---|---|---|
parentID | ctx.sessionID | 父会话 ID |
title | params.description + ... | 子会话标题 |
permission | 限制规则 | 权限规则(通常限制 task、todo) |
继承的全局上下文
子会话通过 Instance 单例继承以下全局上下文:
| 上下文 | 来源 | 说明 |
|---|---|---|
projectID | Instance.project.id | 项目 ID |
directory | Instance.directory | 当前工作目录 |
worktree | Instance.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", // 👈 防止无限递归调用
},
]),
]
权限说明
| 工具 | 动作 | 原因 |
|---|---|---|
todowrite | deny | 防止子 Agent 管理父 Agent 的 todo |
todoread | deny | 防止子 Agent 读取父 Agent 的 todo |
task | deny(可选) | 防止子 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 参数支持继续任务 | - |