跳到主要内容

后台任务

添加于: @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 以启用:

src/mastra/index.ts
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 可以在以下两个层级之一选择加入:

  1. Tool 级配置:Tool 自身声明可以在后台运行。
  2. Agent 级配置:Agent 声明自己的哪些 Tool 可以在后台运行。

Tool 选择加入后,LLM 可以选择在 Tool 参数中包含 _background 字段,为特定调用覆盖解析后的配置(超时、重试,或将调用切回前台)。

Tool 级配置
Tool 级配置的直接链接

在 Tool 定义上设置 background.enabled: true。在此层级选择加入的 Tool,只要由启用了 manager 的 Agent 调用,就会在后台运行。

src/mastra/tools/research.ts
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 的后台分派。

src/mastra/agents/researcher.ts
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 调用时,按以下优先级计算最终的后台配置:

  1. 该 Tool 的 Agent 级 backgroundTasks.tools 条目。
  2. Tool 级 background 配置。
  3. LLM _background.enabled 覆盖(仅当 Tool 已在上述层级之一选择加入时,才用于启用后台分派)。
  4. Manager 默认值(defaultTimeoutMsdefaultRetries)。

如果 Agent 设置了 backgroundTasks.disabled: true,无论上述层级如何,每个 Tool 调用都会同步运行。

Tool 调用作为后台任务分派时,两个 stream 可能公开其生命周期事件:Agent 自身的 stream,以及 backgroundTaskManager.stream() SSE stream。每个 stream 涵盖不同的 chunk 类型:

Chunk 类型触发时机发出方
background-task-started任务已进入队列并分配 taskIdAgent 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-suspendedTool 从其 execute 内调用了 suspend()Manager stream
background-task-resumed通过 manager.resume(taskId, resumeData) 恢复了暂停的任务。Manager stream

agent.stream().fullStream 本身只发出 Agent 循环的 chunk(background-task-startedbackground-task-progress)。使用 untilIdle: trueagent.stream() 会发出相同的两个 chunk,还会订阅该次运行 memory scope 的 manager pubsub,并将七个 manager chunk(background-task-runningbackground-task-outputbackground-task-completedbackground-task-failedbackground-task-cancelledbackground-task-suspendedbackground-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

src/mastra/run.ts
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()

聚合属性
聚合属性的直接链接

使用 untilIdlestream() 返回与普通 stream() 调用类似的 MastraModelOutput,但只有 fullStream 会跨越初始轮次和所有自动续接。聚合属性(texttoolCallstoolResultsfinishReasonmessageListgetFullOutput())仍针对第一轮的内部缓冲区解析。如果需要跨续接的聚合视图,请自行消费 fullStream 并累积。

后台运行子 Agent
后台运行子 Agent的直接链接

子 Agent 调用在底层作为 Tool 调用分派,因此适用相同的后台配置。建议在 supervisor 上为每个子 Agent 选择加入;这样更清晰,也可以在同一位置为每个子 Agent 调整 timeoutMs

src/mastra/agents/supervisor.ts
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 配置。

src/mastra/agents/researcher.ts
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

src/mastra/tools/approval.ts
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 返回真正的结果。

审批到达后,按如下方式恢复任务:

src/server/approvals.ts
await mastra.backgroundTaskManager?.resume(taskId, {
reviewer: 'alice@example.com',
edits: 'Reworded paragraph 3.',
})

Agent 循环会发生什么
Agent 循环会发生什么的直接链接

任务在使用 untilIdlestream() 中途暂停时,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:全局生效。
src/mastra/index.ts
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
Streaming的直接链接

订阅所有任务事件
订阅所有任务事件的直接链接

不带过滤器调用 stream() 会返回系统中每个任务事件的 stream。建立连接时,stream 会先发出所有当前运行任务的 snapshot,然后实时转发后续事件。

src/mastra/run.ts
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仅显示单个任务的事件
abortSignalSignal 中止时关闭 stream

直接查找任务状态
直接查找任务状态的直接链接

要进行一次性查询而不是使用实时 stream,请使用 getTasklistTasks

const task = await mastra.backgroundTaskManager?.getTask(taskId)
const { tasks, total } = await mastra.backgroundTaskManager?.listTasks({
status: 'running',
agentId: 'researcher',
})

这些方法从存储而不是 pubsub stream 读取数据,因此适合分页列表和详情视图。