> Discover all available pages from the documentation index: https://mastra.zisheng.pro/llms.txt # 后台任务 **添加于:** `@mastra/core@1.29.0` 后台任务让 Agent 可以分派长时间运行的 Tool 调用,而不会阻塞 Agent 循环。Tool 会立即返回确认,LLM 继续响应,任务则在后台运行至完成。任务结束后,结果会写入 memory;如果使用带 [`untilIdle`](https://mastra.zisheng.pro/reference/streaming/agents/stream) 选项的 `stream()`,Agent 会自动重新调用,使结果在同一次调用中得到处理。 ## 何时使用后台任务 当 Tool 调用可能耗时较长,不应让用户等到它结束才看到响应时,请使用后台任务。常见场景包括: - 自身需要执行多步骤研究或写作的子 Agent 委派。 - 访问缓慢外部服务、队列或大型数据作业的 Tool 调用。 - 由 Tool 调用触发、可能需要数分钟才能完成的 Workflow。 对于快速返回的 Tool 调用,使用 `agent.stream()` 和 `agent.generate()` 进行前台执行更简单。 > **备注:** 后台任务要求在 Mastra 实例上配置[存储](https://mastra.zisheng.pro/docs/storage/overview)后端。任务会持久化,因此可以在进程重启后继续存在。 ## 快速开始 后台任务默认关闭。在 Mastra 实例上设置 `backgroundTasks.enabled` 以启用: ```typescript import { Mastra } from '@mastra/core' import { LibSQLStore } from '@mastra/libsql' export const mastra = new Mastra({ storage: new LibSQLStore({ id: 'storage', url: 'file:mastra.db' }), backgroundTasks: { enabled: true, globalConcurrency: 10, perAgentConcurrency: 5, backpressure: 'queue', defaultTimeoutMs: 300_000, }, }) ``` 所有选项请参阅 [backgroundTasks 配置参考](https://mastra.zisheng.pro/reference/configuration)。 ## 在后台运行 Tool 启用 manager 本身不会使任何内容在后台运行,因为所有 Tool 默认都在前台执行。Tool 可以在以下两个层级之一选择加入: 1. **Tool 级配置**:Tool 自身声明可以在后台运行。 2. **Agent 级配置**:Agent 声明自己的哪些 Tool 可以在后台运行。 Tool 选择加入后,LLM 可以选择在 Tool 参数中包含 `_background` 字段,为特定调用覆盖解析后的配置(超时、重试,或将调用切回前台)。 ### Tool 级配置 在 Tool 定义上设置 `background.enabled: true`。在此层级选择加入的 Tool,只要由启用了 manager 的 Agent 调用,就会在后台运行。 ```typescript import { createTool } from '@mastra/core/tools' import { z } from 'zod' export const researchTool = createTool({ id: 'research', description: 'Run a long research job', inputSchema: z.object({ topic: z.string() }), background: { enabled: true, timeoutMs: 600_000, maxRetries: 1, }, execute: async ({ topic }) => { // Run the research job for topic }, }) ``` ### Agent 级配置 在 Agent 上使用 `backgroundTasks.tools` 选择特定 Tool 加入,或覆盖单个 Tool 的超时;也可以让所有符合后台运行条件的 Tool 都在后台运行。使用 `disabled: true` 可完全停止该 Agent 的后台分派。 ```typescript import { Agent } from '@mastra/core/agent' export const researcher = new Agent({ id: 'researcher', instructions: 'You research topics and answer questions.', model: 'openai/gpt-5.6-sol', tools: { researchTool, summarizeTool }, backgroundTasks: { tools: { researchTool: { enabled: true, timeoutMs: 600_000 }, summarizeTool: false, }, }, }) ``` 设置 `tools: 'all'`,让 Agent 的每个 Tool 都选择加入。 ### LLM 单次调用覆盖 当 Tool 注册到启用了后台任务的 Agent 时,模型可以在 Tool 参数中包含 `_background` 字段,为该次调用覆盖解析后的配置。模型只需包含要覆盖的内容,`_background` 中的所有字段均为可选。Tool 运行前,该覆盖字段会从参数中移除。 ```json { "topic": "solana", "_background": { "enabled": true, "timeoutMs": 900_000 } } ``` `_background` 覆盖是对开发者已在 Tool 或 Agent 层级选择加入的 Tool 的\_修饰\_,不是独立的加入机制。如果 Tool 尚未选择加入,模型提供的 `_background.enabled: true` 会被忽略,Tool 仍在前台运行。这样可防止确定性的纯前台 Tool(计算器、查询、schema validator)被静默分派为任务。 ### 解析顺序 分派 Tool 调用时,按以下优先级计算最终的后台配置: 1. 该 Tool 的 Agent 级 `backgroundTasks.tools` 条目。 2. Tool 级 `background` 配置。 3. LLM `_background.enabled` 覆盖(仅当 Tool 已在上述层级之一选择加入时,才用于启用后台分派)。 4. Manager 默认值(`defaultTimeoutMs`、`defaultRetries`)。 如果 Agent 设置了 `backgroundTasks.disabled: true`,无论上述层级如何,每个 Tool 调用都会同步运行。 ## 后台任务相关的 stream chunk Tool 调用作为后台任务分派时,两个 stream 可能公开其生命周期事件:Agent 自身的 stream,以及 [`backgroundTaskManager.stream()`](https://mastra.zisheng.pro/docs/long-running-agents/background-tasks) SSE stream。每个 stream 涵盖不同的 chunk 类型: | Chunk 类型 | 触发时机 | 发出方 | | --------------------------- | ------------------------------------------------- | -------------- | | `background-task-started` | 任务已进入队列并分配 `taskId`。 | Agent stream | | `background-task-running` | 任务获得 worker 并开始执行。 | Manager stream | | `background-task-progress` | 显示正在运行的后台任务数量。 | Agent stream | | `background-task-output` | 任务 `execute` 的流式输出 chunk。 | Manager stream | | `background-task-completed` | 任务成功完成。`payload.result` 与最终 Tool 结果一致。 | Manager stream | | `background-task-failed` | 任务抛出异常或超时。 | Manager stream | | `background-task-cancelled` | 任务在完成前被取消。 | Manager stream | | `background-task-suspended` | Tool 从其 execute 内调用了 `suspend()`。 | Manager stream | | `background-task-resumed` | 通过 `manager.resume(taskId, resumeData)` 恢复了暂停的任务。 | Manager stream | `agent.stream().fullStream` 本身只发出 Agent 循环的 chunk(`background-task-started`、`background-task-progress`)。使用 `untilIdle: true` 的 `agent.stream()` 会发出相同的两个 chunk,还会订阅该次运行 memory scope 的 manager pubsub,并将七个 manager chunk(`background-task-running`、`background-task-output`、`background-task-completed`、`background-task-failed`、`background-task-cancelled`、`background-task-suspended`、`background-task-resumed`)输送到同一个 `fullStream`。 `backgroundTaskManager.stream()` 只发出七个 manager chunk。 完整 payload 结构请参阅[后台任务 chunk 参考](https://mastra.zisheng.pro/reference/streaming/ChunkType)。 ## 使用 `untilIdle` 保持 Agent stream 打开 即使后台任务仍在运行,`agent.stream()` 也会在 LLM 发出最终响应后返回。如果希望 stream 一直保持打开,直到所有已分派的后台任务完成,并让 LLM 有机会响应结果,请传入 `untilIdle: true`: ```typescript const stream = await agent.stream('Research solana for me', { memory: { thread: 't1', resource: 'u1' }, untilIdle: true, }) for await (const chunk of stream.fullStream) { // chunks from the initial turn AND any continuation turns triggered by // background task completions flow through here } ``` 后台任务完成时,结果会注入 Agent memory,`stream()` 会重新进入 Agent 循环,让 LLM 可以对其作出反应。当没有任务运行,也没有完成事件排队时,stream 关闭。 要自定义空闲超时,请传入对象而不是 `true`。计时器只在 wrapper 处于轮次之间时运行,因此首个 token 较慢不会关闭 stream。默认为 5 分钟: ```typescript const stream = await agent.stream('Research solana for me', { memory: { thread: 't1', resource: 'u1' }, untilIdle: { maxIdleMs: 30_000 }, }) ``` 完整 API 请参阅 [`Agent.stream()`](https://mastra.zisheng.pro/reference/streaming/agents/stream)。 ### 聚合属性 使用 `untilIdle` 的 `stream()` 返回与普通 `stream()` 调用类似的 `MastraModelOutput`,但只有 `fullStream` 会跨越初始轮次和所有自动续接。聚合属性(`text`、`toolCalls`、`toolResults`、`finishReason`、`messageList`、`getFullOutput()`)仍针对**第一轮**的内部缓冲区解析。如果需要跨续接的聚合视图,请自行消费 `fullStream` 并累积。 ## 后台运行子 Agent 子 Agent 调用在底层作为 Tool 调用分派,因此适用相同的后台配置。建议在 supervisor 上为每个子 Agent 选择加入;这样更清晰,也可以在同一位置为每个子 Agent 调整 `timeoutMs`: ```typescript import { Agent } from '@mastra/core/agent' const supervisor = new Agent({ id: 'supervisor', instructions: 'Coordinate research and writing using the available agents.', model: 'openai/gpt-5.6-sol', agents: { researchAgent, writingAgent }, backgroundTasks: { tools: { researchAgent: { enabled: true, timeoutMs: 900_000 }, writingAgent: { enabled: true, timeoutMs: 900_000 }, }, }, }) const stream = await supervisor.stream('Research AI in education and write an article', { memory: { thread: 't1', resource: 'u1' }, untilIdle: true, }) ``` ### 从子 Agent 继承 如果子 Agent 未列在 supervisor 的 `backgroundTasks.tools` 下,但它自身有符合后台运行条件的 Tool(通过 Tool 级 `background.enabled: true` 或自身的 `backgroundTasks.tools` 条目),框架仍会将整个子 Agent 调用作为后台任务分派。Supervisor 会继承子 Agent 的意图:子 Agent 自身成为后台任务,其内部 Tool 则在子 Agent 循环中以前台方式运行。 继承分派所用的后台配置(例如 `waitTimeoutMs`)派生自子 Agent 自身的 `backgroundTasks` 配置。 ```typescript const researchAgent = new Agent({ id: 'research-agent', description: 'Gathers factual information.', model: 'openai/gpt-5-mini', tools: { deepResearchTool }, backgroundTasks: { tools: { deepResearchTool: { enabled: true, timeoutMs: 600_000 }, }, waitTimeoutMs: 900_000, }, }) ``` 当 supervisor 没有为 `researchAgent` 配置 backgroundTask,却委派给该 `researchAgent` 时,supervisor 仍会将整个 `researchAgent` 调用作为后台任务分派,而 `deepResearchTool` 会在该调用内部以前台方式运行,不会分派自己的嵌套后台任务。 如果希望子 Agent 无论由哪个 supervisor 调用,都始终以一致方式在后台运行,请使用此模式。如果希望按 supervisor 集中调整后台行为,请使用上面的 supervisor 端选择加入方式。 ## 暂停和恢复 后台任务可以在执行过程中自行暂停,等待外部 Signal 后再继续。这适合人工审批、webhook,或下一步依赖稍后到达的数据的任何流程。 Tool 会从其 `execute` 内调用 `suspend(data)`,该调用会: - 在任务记录上持久化 `status: 'suspended'` 和 `data` payload。 - 保存 Workflow snapshot,使运行可以在进程重启后继续。 - 在 manager stream 上发出 `background-task-suspended` chunk。 - 释放并发槽位,让其他任务可以运行。 使用 `mastra.backgroundTaskManager.resume(taskId, resumeData)` 恢复任务。`resumeData` 会在恢复后的运行中进入 Tool 的 `execute` 选项,任务则转换回 `running`。 ```typescript import { createTool } from '@mastra/core/tools' import { z } from 'zod' export const reviewTool = createTool({ id: 'review', description: 'Submit a draft for human review.', inputSchema: z.object({ draft: z.string() }), outputSchema: z.object({ approvedBy: z.string(), edits: z.string().optional() }), background: { enabled: true }, execute: async ({ draft }, context) => { const { suspend, resumeData } = context.agent if (!resumeData) { await suspend?.({ awaiting: 'approval', draft }) return { approvedBy: '', edits: undefined } } const { reviewer, edits } = resumeData as { reviewer: string; edits?: string } return { approvedBy: reviewer, edits } }, }) ``` 第一次调用 `execute` 时会看到 `resumeData === undefined` 并调用 `suspend`。恢复任务后,运行时会在填充了 `resumeData` 的情况下重新启动 Tool。此时 `if` 条件为 false,因此 Tool 返回真正的结果。 审批到达后,按如下方式恢复任务: ```typescript await mastra.backgroundTaskManager?.resume(taskId, { reviewer: 'alice@example.com', edits: 'Reworded paragraph 3.', }) ``` ### Agent 循环会发生什么 任务在使用 `untilIdle` 的 `stream()` 中途暂停时,wrapper 会将其视为当前迭代的终止状态并关闭。拿到恢复 payload 后,如果要立即继续 Agent,请调用 `agent.resumeStream(resumeData, { runId, toolCallId, memory, untilIdle: true })`:恢复的后台任务会运行至完成,其结果添加到消息列表,Agent 再运行一个后续轮次,所有内容都通过同一 SSE 连接传输。如果希望带外驱动恢复,请直接调用 `mastra.backgroundTaskManager.resume(taskId, resumeData)`;结果仍会写入 thread,供下一轮用户交互获取。 ### 恢复时重新注册 executor Manager 将 Tool executor 保存在进程内存中。如果任务暂停期间进程重启,executor closure 会丢失,`resume()` 的调用方必须先通过 `manager.registerTaskContext(taskId, ...)` 重新注册。任务在同一进程内分派和恢复时不需要这样做。 ### 取消暂停的任务 `manager.cancel(taskId)` 对暂停任务和运行中任务同样有效。记录会变为 `cancelled`,Workflow snapshot 会被清理。随后触发 `task.cancelled` 事件。 ## 生命周期回调 每个层级都可以注册终止状态回调。它们不会相互替代,成功/失败 hook 会针对各自结果触发: - Tool 级 `background.onComplete` / `onFailed`:作用于一个 Tool。 - Agent 级 `backgroundTasks.onTaskComplete` / `onTaskFailed`:作用于该 Agent 分派的所有任务。 - Manager 级 `onTaskComplete` / `onTaskFailed`:全局生效。 ```typescript export const mastra = new Mastra({ storage, backgroundTasks: { enabled: true, onTaskComplete: task => { logger.info('Background task complete', { taskId: task.id, toolName: task.toolName }) }, onTaskFailed: task => { logger.error('Background task failed', { taskId: task.id, error: task.error }) }, }, }) ``` ## Streaming ### 订阅所有任务事件 不带过滤器调用 `stream()` 会返回系统中每个任务事件的 stream。建立连接时,stream 会先发出所有当前运行任务的 snapshot,然后实时转发后续事件。 ```typescript const bgManager = mastra.backgroundTaskManager if (!bgManager) throw new Error('Background tasks are not enabled') const controller = new AbortController() const stream = bgManager.stream({ abortSignal: controller.signal }) for await (const chunk of stream) { switch (chunk.type) { case 'background-task-running': console.log('started', chunk.payload.taskId, chunk.payload.toolName) break case 'background-task-completed': console.log('done', chunk.payload.taskId, chunk.payload.result) break case 'background-task-failed': console.error('failed', chunk.payload.taskId, chunk.payload.error) break } } ``` Stream 会一直保持打开,直到调用方的 `AbortSignal` 触发。请始终传入 `abortSignal`,以便干净地断开连接。 ### 过滤 stream 传入过滤选项的任意组合,以缩小接收的事件范围。过滤器同时应用于初始 snapshot 和实时事件订阅。 ```typescript const stream = bgManager.stream({ agentId: 'researcher', threadId: 't1', resourceId: 'u1', abortSignal: controller.signal, }) ``` | 过滤器 | 说明 | | ------------- | --------------------------- | | `agentId` | 仅显示由该 Agent 分派的任务事件 | | `runId` | 仅显示该次特定 Agent 运行的事件 | | `threadId` | 仅显示限定到该 memory thread 的任务事件 | | `resourceId` | 仅显示限定到该 resource 的任务事件 | | `taskId` | 仅显示单个任务的事件 | | `abortSignal` | Signal 中止时关闭 stream | ### 直接查找任务状态 要进行一次性查询而不是使用实时 stream,请使用 `getTask` 和 `listTasks`: ```typescript const task = await mastra.backgroundTaskManager?.getTask(taskId) const { tasks, total } = await mastra.backgroundTaskManager?.listTasks({ status: 'running', agentId: 'researcher', }) ``` 这些方法从存储而不是 pubsub stream 读取数据,因此适合分页列表和详情视图。 ## 相关内容 - [`Agent.stream()` 参考](https://mastra.zisheng.pro/reference/streaming/agents/stream) - [backgroundTasks 配置参考](https://mastra.zisheng.pro/reference/configuration) - [持久化 Agent](https://mastra.zisheng.pro/docs/long-running-agents/durable-agents) - [Supervisor Agent](https://mastra.zisheng.pro/docs/capabilities/subagents) - [Stream chunk 类型](https://mastra.zisheng.pro/reference/streaming/ChunkType) - [存储](https://mastra.zisheng.pro/docs/storage/overview)