后台任务
添加于: @mastra/core@1.29.0
后台任务让 Agent 可以分派长时间运行的 Tool 调用,而不会阻塞 Agent 循环。Tool 会立即返回确认,LLM 继续响应,任务则在后台运行至完成。任务结束后,结果会写入 memory;如果使用带 untilIdle 选项的 stream(),Agent 会自动重新调用,使结果在同一次调用中得到处理。
何时使用后台任务何时使用后台任务的直接链接
当 Tool 调用可能耗时较长,不应让用户等到它结束才看到响应时,请使用后台任务。常见场景包括:
- 自身需要执行多步骤研究或写作的子 Agent 委派。
- 访问缓慢外部服务、队列或大型数据作业的 Tool 调用。
- 由 Tool 调用触发、可能需要数分钟才能完成的 Workflow。
对于快速返回的 Tool 调用,使用 agent.stream() 和 agent.generate() 进行前台执行更简单。
后台任务要求在 Mastra 实例上配置存储后端。任务会持久化,因此可以在进程重启后继续存在。
快速开始快速开始的直接链接
后台任务默认关闭。在 Mastra 实例上设置 backgroundTasks.enabled 以启用:
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 配置参考。
在后台运行 Tool在后台运行 Tool的直接链接
启用 manager 本身不会使任何内容在后台运行,因为所有 Tool 默认都在前台执行。Tool 可以在以下两个层级之一选择加入:
- Tool 级配置:Tool 自身声明可以在后台运行。
- Agent 级配置:Agent 声明自己的哪些 Tool 可以在后台运行。
Tool 选择加入后,LLM 可以选择在 Tool 参数中包含 _background 字段,为特定调用覆盖解析后的配置(超时、重试,或将调用切回前台)。
Tool 级配置Tool 级配置的直接链接
在 Tool 定义上设置 background.enabled: true。在此层级选择加入的 Tool,只要由启用了 manager 的 Agent 调用,就会在后台运行。
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 级配置的直接链接
在 Agent 上使用 backgroundTasks.tools 选择特定 Tool 加入,或覆盖单个 Tool 的超时;也可以让所有符合后台运行条件的 Tool 都在后台运行。使用 disabled: true 可完全停止该 Agent 的后台分派。
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 单次调用覆盖LLM 单次调用覆盖的直接链接
当 Tool 注册到启用了后台任务的 Agent 时,模型可以在 Tool 参数中包含 _background 字段,为该次调用覆盖解析后的配置。模型只需包含要覆盖的内容,_background 中的所有字段均为可选。Tool 运行前,该覆盖字段会从参数中移除。
{
"topic": "solana",
"_background": { "enabled": true, "timeoutMs": 900_000 }
}
_background 覆盖是对开发者已在 Tool 或 Agent 层级选择加入的 Tool 的_修饰_,不是独立的加入机制。如果 Tool 尚未选择加入,模型提供的 _background.enabled: true 会被忽略,Tool 仍在前台运行。这样可防止确定性的纯前台 Tool(计算器、查询、schema validator)被静默分派为任务。
解析顺序解析顺序的直接链接
分派 Tool 调用时,按以下优先级计算最终的后台配置:
- 该 Tool 的 Agent 级
backgroundTasks.tools条目。 - Tool 级
background配置。 - LLM
_background.enabled覆盖(仅当 Tool 已在上述层级之一选择加入时,才用于启用后台分派)。 - Manager 默认值(
defaultTimeoutMs、defaultRetries)。
如果 Agent 设置了 backgroundTasks.disabled: true,无论上述层级如何,每个 Tool 调用都会同步运行。
后台任务相关的 stream chunk后台任务相关的 stream chunk的直接链接
Tool 调用作为后台任务分派时,两个 stream 可能公开其生命周期事件:Agent 自身的 stream,以及 backgroundTaskManager.stream() 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 参考。
使用 untilIdle 保持 Agent stream 打开keep-the-agent-stream-open-with-untilidle的直接链接
即使后台任务仍在运行,agent.stream() 也会在 LLM 发出最终响应后返回。如果希望 stream 一直保持打开,直到所有已分派的后台任务完成,并让 LLM 有机会响应结果,请传入 untilIdle: true:
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 分钟:
const stream = await agent.stream('Research solana for me', {
memory: { thread: 't1', resource: 'u1' },
untilIdle: { maxIdleMs: 30_000 },
})
完整 API 请参阅 Agent.stream()。
聚合属性聚合属性的直接链接
使用 untilIdle 的 stream() 返回与普通 stream() 调用类似的 MastraModelOutput,但只有 fullStream 会跨越初始轮次和所有自动续接。聚合属性(text、toolCalls、toolResults、finishReason、messageList、getFullOutput())仍针对第一轮的内部缓冲区解析。如果需要跨续接的聚合视图,请自行消费 fullStream 并累积。
后台运行子 Agent后台运行子 Agent的直接链接
子 Agent 调用在底层作为 Tool 调用分派,因此适用相同的后台配置。建议在 supervisor 上为每个子 Agent 选择加入;这样更清晰,也可以在同一位置为每个子 Agent 调整 timeoutMs:
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 继承的直接链接
如果子 Agent 未列在 supervisor 的 backgroundTasks.tools 下,但它自身有符合后台运行条件的 Tool(通过 Tool 级 background.enabled: true 或自身的 backgroundTasks.tools 条目),框架仍会将整个子 Agent 调用作为后台任务分派。Supervisor 会继承子 Agent 的意图:子 Agent 自身成为后台任务,其内部 Tool 则在子 Agent 循环中以前台方式运行。
继承分派所用的后台配置(例如 waitTimeoutMs)派生自子 Agent 自身的 backgroundTasks 配置。
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'和datapayload。 - 保存 Workflow snapshot,使运行可以在进程重启后继续。
- 在 manager stream 上发出
background-task-suspendedchunk。 - 释放并发槽位,让其他任务可以运行。
使用 mastra.backgroundTaskManager.resume(taskId, resumeData) 恢复任务。resumeData 会在恢复后的运行中进入 Tool 的 execute 选项,任务则转换回 running。
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 返回真正的结果。
审批到达后,按如下方式恢复任务:
await mastra.backgroundTaskManager?.resume(taskId, {
reviewer: 'alice@example.com',
edits: 'Reworded paragraph 3.',
})
Agent 循环会发生什么Agent 循环会发生什么的直接链接
任务在使用 untilIdle 的 stream() 中途暂停时,wrapper 会将其视为当前迭代的终止状态并关闭。拿到恢复 payload 后,如果要立即继续 Agent,请调用 agent.resumeStream(resumeData, { runId, toolCallId, memory, untilIdle: true }):恢复的后台任务会运行至完成,其结果添加到消息列表,Agent 再运行一个后续轮次,所有内容都通过同一 SSE 连接传输。如果希望带外驱动恢复,请直接调用 mastra.backgroundTaskManager.resume(taskId, resumeData);结果仍会写入 thread,供下一轮用户交互获取。
恢复时重新注册 executor恢复时重新注册 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:全局生效。
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 })
},
},
})
StreamingStreaming的直接链接
订阅所有任务事件订阅所有任务事件的直接链接
不带过滤器调用 stream() 会返回系统中每个任务事件的 stream。建立连接时,stream 会先发出所有当前运行任务的 snapshot,然后实时转发后续事件。
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过滤 stream的直接链接
传入过滤选项的任意组合,以缩小接收的事件范围。过滤器同时应用于初始 snapshot 和实时事件订阅。
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:
const task = await mastra.backgroundTaskManager?.getTask(taskId)
const { tasks, total } = await mastra.backgroundTaskManager?.listTasks({
status: 'running',
agentId: 'researcher',
})
这些方法从存储而不是 pubsub stream 读取数据,因此适合分页列表和详情视图。