进阶 mastra.ai 2026-10-12 10:15:56 · 29 阅读
第11章 子代理(Subagents)
# 子代理(Subagents)
新增于:@mastra/core@1.8.0
子代理是专门化的代理,主代理可以把任务委派给它们。将子代理添加到父代理的 agents 属性中,然后调用 Agent.stream() 或 Agent.generate() 即可。父代理会根据自己的指令和每个子代理的描述,决定何时以及如何委派任务。
## 何时使用子代理
当任务需要多个不同专长的代理协同完成时,就可以使用子代理。由父代理决定何时委派,并向每个子代理传递上下文,最后汇总它们的结果。
常见场景:
- 研究与写作工作流:一个代理负责收集数据,另一个负责产出内容
- 各阶段需要不同专长的多步骤任务
- 需要对委派行为进行细粒度控制的任务
注意:负责协调子代理的父代理通常称为 supervisor(监督者)。supervisor 模式是 Mastra 中构建多代理系统的一种方式,其他模式可参阅概念概览。
## 快速上手
先定义带有清晰描述的子代理,再把它们添加到父代理中:
src/mastra/agents/parent-agent.tsimport { Agent } from '@mastra/core/agent'
import { Memory } from '@mastra/memory'
import { LibSQLStore } from '@mastra/libsql'
const researchAgent = new Agent({
id: 'research-agent',
description: 'Gathers factual information and returns bullet-point summaries.',
model: 'openai/gpt-5-mini',
})
const writingAgent = new Agent({
id: 'writing-agent',
description: 'Transforms research into well-structured articles.',
model: 'openai/gpt-5-mini',
})
const parentAgent = new Agent({
id: 'parent-agent',
instructions: `You coordinate research and writing using specialized agents.
Delegate to research-agent for facts, then writing-agent for content.`,
model: 'openai/gpt-5.6-sol',
agents: { researchAgent, writingAgent },
memory: new Memory({
storage: new LibSQLStore({ id: 'storage', url: 'file:mastra.db' }),
}),
})
const stream = await parentAgent.stream('Research AI in education and write an article', {
maxSteps: 10,
})
for await (const chunk of stream.textStream) {
process.stdout.write(chunk)
}
委托钩子 直接链接至委托钩子 委托钩子允许你在委托发生时拦截、修改或拒绝该委托。你可以在 agent 的 `defaultOptions` 中或在每次调用时,通过 `delegation` 选项进行配置。 onDelegationStart 直接链接至 onDelegationStart 在父 agent 委托给子 agent 之前调用。返回一个对象以控制委托行为: * `proceed: true`:允许委托(默认行为) * `proceed: false`:附带 `rejectionReason` 拒绝委托 * `modifiedPrompt`:重写发送给子 agent 的提示词 * `modifiedMaxSteps`:限制子 agent 的迭代次数 * `threadMetadata`:在创建子 agent 线程时附加元数据(例如 `tenantId`),以便你可以使用 `memory.listThreads({ filter: { metadata } })` 过滤线程。 src/mastra/agents/parent-agent.ts const stream = await parentAgent.stream('Research AI trends', { maxSteps: 10, delegation: { onDelegationStart: async context => { console.log(`Delegating to: ${context.primitiveId}`) // 针对特定 agent 修改提示词 if (context.primitiveId === 'research-agent') { return { proceed: true, modifiedPrompt: `${context.prompt}\n\nFocus on 2024-2025 data.`, modifiedMaxSteps: 5, } } // 迭代次数过多时拒绝委托 if (context.iteration > 8) { return { proceed: false, rejectionReason: 'Max iterations reached. Synthesize current findings.', } } return { proceed: true } }, }, }) context 对象包含以下内容: * `primitiveId`:被委托的子 agent 的 ID * `prompt`:父 agent 发送的提示词 * `iteration`:当前迭代编号 * `requestContext`:子 agent 运行时将接收的请求上下文 委托边界处的请求上下文 直接链接至委托边界处的请求上下文 每个委托都会接收一个请求上下文,其条目是从父级运行中浅拷贝过来的,但不包括运行级别的标识键。在子 agent 运行期间设置或删除条目不会影响父级的上下文。要在 `onDelegationStart` 中向被委托的运行传递值,请在 `context.requestContext` 上设置条目: src/mastra/agents/parent-agent.ts const stream = await parentAgent.stream('Research AI trends', { maxSteps: 10, delegation: { onDelegationStart: async context => { context.requestContext.set('audience', 'technical')
},
},
})
子代理会读取工具及动态配置(如 instructions: ({ requestContext }) => ...)中的这些条目。详情请参阅“请求上下文”。为了确保在持久化代理中正常工作,这些值必须是可 JSON 序列化的。 onDelegationComplete 直接链接至 ondelegationcomplete 委派任务完成后触发。可用于检查结果、提供反馈,或停止执行: context.bail():立即停止父代理的循环 返回 { feedback: '...' }:添加一条反馈,保存至父代理的记忆中,供下一轮使用 返回 { resultText: '...' }:在当前运行中,替换父模型看到的该委派任务的工具结果文本 当子代理自身的结果会向父代理传递误导性信号时,请设置 resultText。例如,如果子代理在工具调用步骤被停止且返回空文本,父模型会误判为一次成功但内容为空的委派。你也可以用更有用的信息替换委派失败时的错误文本。这并不会使失败的委派恢复成功,父代理仍会收到一个表示工具执行失败的结果。与下一轮生效的 feedback 不同,resultText 会立即影响父代理的推理过程。 若需要在委派失败时停止父代理并为后续轮次保存反馈,请调用 bail() 并返回反馈: src/mastra/agents/parent-agent.tsconst stream = await parentAgent.stream('Research AI trends', {
maxSteps: 10,
delegation: {
onDelegationComplete: async context => {
console.log(`Completed: ${context.primitiveId}`)
// Bail on errors
if (context.error) {
context.bail()
return {
feedback: `Delegation to ${context.primitiveId} failed: ${context.error}. Try a different approach.`,
}
}
},
},
})
上下文对象包含以下属性: | 属性 | 说明 | | primitiveId | 运行的 subagent 的 ID | | result | subagent 的响应,可用时包括 text、usage、finishReason、subAgentToolResults、subAgentPendingToolCalls、subAgentThreadId 和 subAgentResourceId | | success | 委派是否成功 | | error | 委派失败时的原始错误 | | messages | 收集到的 subagent 对话记录,可用时包括部分消息 | | bail() | 用于停止父 agent 循环的函数 | 失败处理 subagent 运行失败时,父模型会收到一个带通用错误信息的失败工具结果。这不会自动停止父 agent——它可以选择向用户解释失败原因,或尝试其他方案。若要直接停止其循环,调用 context.bail()。 钩子会收到 success: false、原始错误,以及已收集的部分结果和消息。返回 resultText 可以替换父模型看到的错误文本,但不会改变委派的失败状态,也不会丢弃原始错误原因。空字符串也是有效的替换值。 如果希望父 agent 用一条定制化的失败信息继续运行而不是停止循环,只需返回 resultText 而不调用 bail(): src/mastra/agents/parent-agent.ts const stream = await parentAgent.stream('Research AI trends', { maxSteps: 10, delegation: { onDelegationComplete: ({ success }) => { if (!success) { return { resultText: 'The delegated task failed. Try another approach.', } } }, }, }) 优先使用经过筛选的消息,如下例所示。返回 `{ resultText: error.message }` 会将底层错误消息直接转发给父模型的 Provider,模型可能会将其原样复述给终端用户。Provider 返回的错误信息可能包含敏感数据,而通用的 `resultText` 并不能作为日志、追踪或流式错误负载的脱敏策略。 对于以后台任务形式运行的委派,配置的每次重试都可能触发一次 `onDelegationComplete`。如果你的钩子存在副作用,需考虑这种重复调用的影响。 Hook 错误 如果委派钩子抛出异常,委派默认仍会继续:`onDelegationStart` 将携带原始提示词继续执行,`messageFilter` 会回退到未过滤的上下文,`onDelegationComplete` 则保留子 agent 的原始结果。若要改为使委派失败,请设置 `hookErrorStrategy: 'throw'`。 src/mastra/agents/parent-agent.ts const stream = await parentAgent.stream('Research AI trends', { maxSteps: 10, delegation: { hookErrorStrategy: 'throw', onDelegationComplete: async ({ result }) => { // 如果此处抛出异常,父 agent 看到的是委派失败, // 而不是结果被静默丢弃的成功委派。 await reportToAnalytics(result) }, }, }) 无论采用哪种策略,所有钩子失败记录都会保存在该次运行的 `requestContext` 中,键为 `__mastra_delegationHookErrors`,内容是一个 `{ hook, primitiveId, toolCallId, runId, name, message }` 列表,方便你以编程方式检测钩子失败: src/mastra/agents/parent-agent.ts const requestContext = new RequestContext() await parentAgent.generate('Research AI trends', { maxSteps: 10, requestContext }) const hookErrors = requestContext.get('__mastra_delegationHookErrors') ?? [] 当 `hookErrorStrategy` 设为 `'throw'` 时,`onDelegationStart` 抛出异常会完全阻止子 agent 运行;而 `messageFilter` 或 `onDelegationComplete` 抛出异常时,父 agent 会将该委派视为工具调用失败。若 `onDelegationComplete` 在处理失败的委派时抛出异常,最终上抛的仍是原始委派错误,钩子自身失败不会再次触发。 消息过滤 默认情况下,子 agent 会接收到父 agent 的完整对话上下文。使用 `messageFilter` 可以控制共享哪些消息,例如移除敏感数据或限制上下文大小。 src/mastra/agents/parent-agent.ts const stream = await parentAgent.stream('Research AI trends', {
maxSteps: 10,
delegation: {
messageFilter: ({ messages, primitiveId, prompt }) => {
// Remove messages containing sensitive data
return messages
.filter(msg => {
const content =
typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)
return !content.includes('confidential')
})
.slice(-10) // Only pass the last 10 messages
},
},
})
回调接收完整对话历史(messages)、子智能体 ID(primitiveId)以及委托提示词(prompt),并返回筛选后的消息数组。 子智能体结果上下文 直接链接到子智能体结果上下文 当子智能体执行完毕后,父智能体的模型会在后续迭代中接收该子智能体的文本响应。嵌套工具调用及子智能体元数据(如线程和资源 ID)不会纳入父智能体的模型上下文。 应用代码和 UI 集成仍可检查工具结果负载中的 subAgentToolResults 及其他原始委托结果。 这样做既保留了调试和展示所需的数据,又避免了将嵌套工具参数或输出传回父智能体的下一次模型调用。 子智能体无法运行客户端工具(即没有 execute 函数的工具)。若子智能体调用了此类工具,其执行将中止于该步骤,finishReason 设为 'tool-calls'。委托结果会在 subAgentPendingToolCalls 中列出未处理的调用,父智能体的模型将获知哪些工具从未被执行,Mastra 也会记录警告。应将客户端工具交给父智能体,或在需要用户输入时于子智能体工具中使用 suspend()。 设置 includeSubAgentToolResultsInModelContext 可将完整子智能体结果(包括嵌套工具结果和子智能体元数据)纳入父智能体的模型上下文。 src/mastra/agents/parent-agent.tsawait parentAgent.generate('Research AI trends', {
delegation: {
includeSubAgentToolResultsInModelContext: true,
},
})
# 复用先前的子代理结果 默认情况下,子代理的结果会作为工具结果返回给父代理,但这些结果在转发给后续子代理的上下文中会被移除。父代理必须在下一次委派提示词中复述先前的结果,这既消耗 token,又会丢失细节。 设置 enableResultReferences 可让后续委派原样复用先前的结果: src/mastra/agents/parent-agent.tsawait parentAgent.generate('Find and fix the token refresh bug', {
delegation: {
enableResultReferences: true,
},
})
启用后: 每次成功且非空的子代理结果都会获得一个引用 ID,例如 explorer-1。父代理的模型会在子代理文本之后看到一行 [ref: explorer-1]。 委派工具会新增一个 contextFromRefs 输入。父代理可以传入先前的 ID,既可以作为字符串(["explorer-1"]),也可以作为带有可选 label 和 note 的对象([{ ref: "explorer-1", as: "investigation", note: "bug location" }])。 被引用的文本会插入到委派提示词之前,每个结果放在各自带标签的块中,内容与先前子代理生成时完全一致。onDelegationStart 和 messageFilter 接收到的是展开后的提示词。 如果 onDelegationComplete 返回了 resultText,后续委派收到的将是替换后的文本。 引用仅在父代理单次运行期间保存在内存中,不会被持久化。被拒绝、失败、为空以及后台任务的委派不会获得引用 ID。未知 ID 会被跳过并给出警告,委派继续进行。 被引用的文本来自另一个代理的输出。每个块都会使用全新的、不可预测的标签,并告知接收的子代理将内容视为数据。但如果子代理需要处理不受信任的输入,请在 onDelegationStart 或通过处理器添加你自己的校验。 # 迭代监控 父代理循环的每次迭代结束后都会调用 onIterationComplete。可以用它来监控执行情况或引导下一轮迭代,也可以提前终止执行。 src/mastra/agents/parent-agent.tsconst stream = await parentAgent.stream('Research AI trends', {
maxSteps: 10,
onIterationComplete: async context => {
console.log(`Iteration ${context.iteration}/${context.maxIterations}`)
console.log(`Finish reason: ${context.finishReason}`)
// Inject feedback to guide the agent
if (!context.text.includes('recommendations')) {
return {
continue: true,
feedback: '请在分析中加入具体的建议。',
}
}
// 当响应内容足够充分时提前终止
if (context.text.length > 1000 && context.finishReason === 'stop') {
return { continue: false }
}
return { continue: true }
},
})
返回 `{ continue: true }` 表示继续迭代,返回 `{ continue: false }` 表示停止。可以附带可选的 `feedback` 来向对话注入引导指令。如果同时设置 `feedback` 和 `continue: false`,模型可能会获得最后一次机会生成融合了反馈内容的文本响应。这种额外回合仅在当前迭代活跃时(例如工具调用完成后)才会被授予。 Memory isolation Mastra 在委托过程中隔离 Subagent 的记忆。Subagent 会接收完整的对话上下文以支持更好的决策,但仅其特定的委托提示词和响应会被保存到记忆中。 其工作机制如下: * 全量上下文转发:当父 Agent 进行委托时,Subagent 会接收父 Agent 对话中的所有消息 * 作用域内存保存:仅委托提示词和 Subagent 的响应会被保存到 Subagent 的记忆中 * 每次调用使用独立线程:每次委托都使用唯一的线程 ID,确保隔离性 因此,Subagent 既拥有必要的上下文,又避免了父 Agent 整个对话污染其记忆空间。欲了解更多细节,请访问多智能体系统(multi-agent systems)中的记忆机制说明。 Tool approval propagation 工具审批请求会沿委托链向上传播。当 Subagent 使用带有 `requireApproval: true` 的工具或调用 `suspend()` 时,审批请求会在父 Agent 的数据流中呈现。 const sensitiveDataTool = createTool({
id: 'get-user-data',
requireApproval: true,
execute: async input => {
return await database.getUserData(input.userId)
},
})
const dataAgent = new Agent({
id: 'data-agent',
tools: { sensitiveDataTool },
})
const parentAgent = new Agent({
id: 'parent-agent',
agents: { dataAgent },
memory: new Memory(),
})
const stream = await parentAgent.stream('Get data for user 123')
for await (const chunk of stream.fullStream) {
if (chunk.type === 'tool-call-approval') {
console.log('Tool requires approval:', chunk.payload.toolName)
}
}
取消机制 为“取消”部分提供直接链接 当把 abortSignal 传递给父 agent 的 stream() 或 generate() 调用时,Mastra 会将相同的信号转发给委派出去的 subagent。调用 AbortController.abort() 会在 subagent 的下一个执行步骤时取消正在运行的任务,而不是让它们继续运行到结束。 const controller = new AbortController()
const stream = await parentAgent.stream('Research AI trends', {
abortSignal: controller.signal,
})
// 取消父 agent 以及任何正在运行的 subagent
controller.abort()
任务完成度评分 为“任务完成度评分”部分提供直接链接 Agent 并非总能在第一次尝试时就生成完整且正确的输出。任务完成度评分器可以在每次迭代后验证任务是否已完成。如果验证失败,父 agent 将继续迭代。评分失败产生的反馈会包含在对话上下文中,以便 subagent 能看到缺失的内容。 import { createScorer } from '@mastra/core/evals'
const taskCompleteScorer = createScorer({
id: 'task-complete',
name: 'Task Completeness',
}).generateScore(async context => {
const text = (context.run.output || '').toString()
const hasAnalysis = text.includes('analysis')
const hasRecommendations = text.includes('recommendation')
return hasAnalysis && hasRecommendations ? 1 : 0
})
const stream = await parentAgent.stream('Research AI in education', {
maxSteps: 10,
isTaskComplete: {
scorers: [taskCompleteScorer],
strategy: 'all',
onComplete: async result => {
console.log('Task complete:', result.complete)
},
},
})
评分标准评分器 为“评分标准评分器”部分提供直接链接 内置的评分标准评分器允许你将“正确”定义为一个检查清单,让 agent 进行自我评估并迭代,直到满足所有标准或达到 maxSteps 限制。 它作为一种 LLM 裁判评分器工作。每次迭代后,一个独立的评判模型会根据评分标准审查 agent 的输出。当所有必要条件都通过时,循环结束。未通过的标准会将其反馈添加到对话中,以便 agent 重试。 这种方法在具有明确、可验证成功标准的任务中最为有效。您可以这样使用: src/mastra/agents/rubric-scorer.tsimport { Agent } from '@mastra/core/agent'
import { createRubricScorer } from '@mastra/evals/scorers/prebuilt'
const parentAgent = new Agent({
id: 'parent-agent',
instructions: '你使用专门的智能体来协调研究与写作。', model: 'openai/gpt-5.6-sol', agents: { researchAgent, writingAgent }, }) const rubricScorer = createRubricScorer({ model: 'openai/gpt-5-mini', criteria: [ { description: '响应中包含分析部分' }, { description: '响应中包含具体建议' }, ], }) const stream = await parentAgent.stream('研究 AI 在教育领域的应用', { maxSteps: 10, isTaskComplete: { scorers: [rubricScorer], strategy: 'all', }, }) 完整 API 细节请参阅 rubric scorer 参考文档。 编写有效的指令 清晰明确的指令是高效委派的前提。 父智能体的指令应说明有哪些可用资源、分别在什么情况下使用,同时还要定义协调行为和成功标准。 每个 subagent 都需要有一段清晰的描述,说明它的用途、返回格式,以及父智能体何时应该使用它。父智能体正是根据这些描述来做委派决策的。 const parentAgent = new Agent({ id: 'parent-agent', instructions: `你负责协调研究与写作任务。 可用资源: - researchAgent:收集事实数据和资料来源(返回要点列表) - writingAgent:把研究结果转化为叙事内容(返回完整段落) 委派策略: 1. 研究类请求:先委派给 researchAgent 2. 写作类请求:委派给 writingAgent 3. 复杂请求:先委派给 researchAgent,再委派给 writingAgent 成功标准: - 用户的所有问题都得到完整回答 - 响应格式良好、内容完整`, agents: { researchAgent, writingAgent }, }) 在后台运行 subagent Subagent 调用是以 tool call 的形式分发的,因此可以作为后台任务运行。当某个委派任务耗时较长、又不希望阻塞父智能体的响应时,这一点很有用。 在 Mastra 实例上启用 backgroundTasks 管理器,然后在父智能体上为 subagent 开启后台运行: src/mastra/agents/parent-agent.ts const parentAgent = new Agent({ id: 'parent-agent', instructions: '使用可用的智能体协调研究与写作。', model: 'openai/gpt-5.6-sol', ```html agents: { researchAgent, writingAgent },
backgroundTasks: {
tools: {
researchAgent: { enabled: true, timeoutMs: 900_000 },
writingAgent: { enabled: true, timeoutMs: 900_000 },
},
},
})
const stream = await parentAgent.streamUntilIdle('Research AI in education and write an article', {
memory: { thread: 't1', resource: 'u1' },
})
请改用 streamUntilIdle() 而非 stream(),以确保在子 Agent 完成且父 Agent 处理完其结果前,流保持打开状态。 如果某个子 Agent 未列在父 Agent 的 backgroundTasks.tools 中,但拥有支持后台执行的工具,父 Agent 仍会将其作为后台任务调度并继承其配置。详见下文关于从子 Agent 继承配置的说明。 子 Agent 版本管理直接链接到子 Agent 版本管理 在使用编辑器时,可以控制父 Agent 在运行时使用哪个已存储的子 Agent 版本。在 Mastra 实例或单次调用中设置版本覆盖: const result = await parentAgent.generate('Research and write about AI safety', {
versions: {
agents: {
'research-agent': { status: 'published' },
'writing-agent': { versionId: 'draft-456' },
},
},
})
版本覆盖会自动通过委派传递。有关解析顺序和服务器 API 用法的详细信息,请参阅子 Agent 版本管理文档。 相关链接直接链接到相关链接 后台任务 子 Agent 版本管理 指南:研究协调器 Agent.stream() 参考 Agent.streamUntilIdle() 参考 Agent.generate() 参考 Agent 审批 多智能体系统中的内存 概念:多智能体系统 📹 Mastra 监督者智能体研讨会 ```
import { Memory } from '@mastra/memory'
import { LibSQLStore } from '@mastra/libsql'
const researchAgent = new Agent({
id: 'research-agent',
description: 'Gathers factual information and returns bullet-point summaries.',
model: 'openai/gpt-5-mini',
})
const writingAgent = new Agent({
id: 'writing-agent',
description: 'Transforms research into well-structured articles.',
model: 'openai/gpt-5-mini',
})
const parentAgent = new Agent({
id: 'parent-agent',
instructions: `You coordinate research and writing using specialized agents.
Delegate to research-agent for facts, then writing-agent for content.`,
model: 'openai/gpt-5.6-sol',
agents: { researchAgent, writingAgent },
memory: new Memory({
storage: new LibSQLStore({ id: 'storage', url: 'file:mastra.db' }),
}),
})
const stream = await parentAgent.stream('Research AI in education and write an article', {
maxSteps: 10,
})
for await (const chunk of stream.textStream) {
process.stdout.write(chunk)
}
委托钩子 直接链接至委托钩子 委托钩子允许你在委托发生时拦截、修改或拒绝该委托。你可以在 agent 的 `defaultOptions` 中或在每次调用时,通过 `delegation` 选项进行配置。 onDelegationStart 直接链接至 onDelegationStart 在父 agent 委托给子 agent 之前调用。返回一个对象以控制委托行为: * `proceed: true`:允许委托(默认行为) * `proceed: false`:附带 `rejectionReason` 拒绝委托 * `modifiedPrompt`:重写发送给子 agent 的提示词 * `modifiedMaxSteps`:限制子 agent 的迭代次数 * `threadMetadata`:在创建子 agent 线程时附加元数据(例如 `tenantId`),以便你可以使用 `memory.listThreads({ filter: { metadata } })` 过滤线程。 src/mastra/agents/parent-agent.ts const stream = await parentAgent.stream('Research AI trends', { maxSteps: 10, delegation: { onDelegationStart: async context => { console.log(`Delegating to: ${context.primitiveId}`) // 针对特定 agent 修改提示词 if (context.primitiveId === 'research-agent') { return { proceed: true, modifiedPrompt: `${context.prompt}\n\nFocus on 2024-2025 data.`, modifiedMaxSteps: 5, } } // 迭代次数过多时拒绝委托 if (context.iteration > 8) { return { proceed: false, rejectionReason: 'Max iterations reached. Synthesize current findings.', } } return { proceed: true } }, }, }) context 对象包含以下内容: * `primitiveId`:被委托的子 agent 的 ID * `prompt`:父 agent 发送的提示词 * `iteration`:当前迭代编号 * `requestContext`:子 agent 运行时将接收的请求上下文 委托边界处的请求上下文 直接链接至委托边界处的请求上下文 每个委托都会接收一个请求上下文,其条目是从父级运行中浅拷贝过来的,但不包括运行级别的标识键。在子 agent 运行期间设置或删除条目不会影响父级的上下文。要在 `onDelegationStart` 中向被委托的运行传递值,请在 `context.requestContext` 上设置条目: src/mastra/agents/parent-agent.ts const stream = await parentAgent.stream('Research AI trends', { maxSteps: 10, delegation: { onDelegationStart: async context => { context.requestContext.set('audience', 'technical')
},
},
})
子代理会读取工具及动态配置(如 instructions: ({ requestContext }) => ...)中的这些条目。详情请参阅“请求上下文”。为了确保在持久化代理中正常工作,这些值必须是可 JSON 序列化的。 onDelegationComplete 直接链接至 ondelegationcomplete 委派任务完成后触发。可用于检查结果、提供反馈,或停止执行: context.bail():立即停止父代理的循环 返回 { feedback: '...' }:添加一条反馈,保存至父代理的记忆中,供下一轮使用 返回 { resultText: '...' }:在当前运行中,替换父模型看到的该委派任务的工具结果文本 当子代理自身的结果会向父代理传递误导性信号时,请设置 resultText。例如,如果子代理在工具调用步骤被停止且返回空文本,父模型会误判为一次成功但内容为空的委派。你也可以用更有用的信息替换委派失败时的错误文本。这并不会使失败的委派恢复成功,父代理仍会收到一个表示工具执行失败的结果。与下一轮生效的 feedback 不同,resultText 会立即影响父代理的推理过程。 若需要在委派失败时停止父代理并为后续轮次保存反馈,请调用 bail() 并返回反馈: src/mastra/agents/parent-agent.tsconst stream = await parentAgent.stream('Research AI trends', {
maxSteps: 10,
delegation: {
onDelegationComplete: async context => {
console.log(`Completed: ${context.primitiveId}`)
// Bail on errors
if (context.error) {
context.bail()
return {
feedback: `Delegation to ${context.primitiveId} failed: ${context.error}. Try a different approach.`,
}
}
},
},
})
上下文对象包含以下属性: | 属性 | 说明 | | primitiveId | 运行的 subagent 的 ID | | result | subagent 的响应,可用时包括 text、usage、finishReason、subAgentToolResults、subAgentPendingToolCalls、subAgentThreadId 和 subAgentResourceId | | success | 委派是否成功 | | error | 委派失败时的原始错误 | | messages | 收集到的 subagent 对话记录,可用时包括部分消息 | | bail() | 用于停止父 agent 循环的函数 | 失败处理 subagent 运行失败时,父模型会收到一个带通用错误信息的失败工具结果。这不会自动停止父 agent——它可以选择向用户解释失败原因,或尝试其他方案。若要直接停止其循环,调用 context.bail()。 钩子会收到 success: false、原始错误,以及已收集的部分结果和消息。返回 resultText 可以替换父模型看到的错误文本,但不会改变委派的失败状态,也不会丢弃原始错误原因。空字符串也是有效的替换值。 如果希望父 agent 用一条定制化的失败信息继续运行而不是停止循环,只需返回 resultText 而不调用 bail(): src/mastra/agents/parent-agent.ts const stream = await parentAgent.stream('Research AI trends', { maxSteps: 10, delegation: { onDelegationComplete: ({ success }) => { if (!success) { return { resultText: 'The delegated task failed. Try another approach.', } } }, }, }) 优先使用经过筛选的消息,如下例所示。返回 `{ resultText: error.message }` 会将底层错误消息直接转发给父模型的 Provider,模型可能会将其原样复述给终端用户。Provider 返回的错误信息可能包含敏感数据,而通用的 `resultText` 并不能作为日志、追踪或流式错误负载的脱敏策略。 对于以后台任务形式运行的委派,配置的每次重试都可能触发一次 `onDelegationComplete`。如果你的钩子存在副作用,需考虑这种重复调用的影响。 Hook 错误 如果委派钩子抛出异常,委派默认仍会继续:`onDelegationStart` 将携带原始提示词继续执行,`messageFilter` 会回退到未过滤的上下文,`onDelegationComplete` 则保留子 agent 的原始结果。若要改为使委派失败,请设置 `hookErrorStrategy: 'throw'`。 src/mastra/agents/parent-agent.ts const stream = await parentAgent.stream('Research AI trends', { maxSteps: 10, delegation: { hookErrorStrategy: 'throw', onDelegationComplete: async ({ result }) => { // 如果此处抛出异常,父 agent 看到的是委派失败, // 而不是结果被静默丢弃的成功委派。 await reportToAnalytics(result) }, }, }) 无论采用哪种策略,所有钩子失败记录都会保存在该次运行的 `requestContext` 中,键为 `__mastra_delegationHookErrors`,内容是一个 `{ hook, primitiveId, toolCallId, runId, name, message }` 列表,方便你以编程方式检测钩子失败: src/mastra/agents/parent-agent.ts const requestContext = new RequestContext() await parentAgent.generate('Research AI trends', { maxSteps: 10, requestContext }) const hookErrors = requestContext.get('__mastra_delegationHookErrors') ?? [] 当 `hookErrorStrategy` 设为 `'throw'` 时,`onDelegationStart` 抛出异常会完全阻止子 agent 运行;而 `messageFilter` 或 `onDelegationComplete` 抛出异常时,父 agent 会将该委派视为工具调用失败。若 `onDelegationComplete` 在处理失败的委派时抛出异常,最终上抛的仍是原始委派错误,钩子自身失败不会再次触发。 消息过滤 默认情况下,子 agent 会接收到父 agent 的完整对话上下文。使用 `messageFilter` 可以控制共享哪些消息,例如移除敏感数据或限制上下文大小。 src/mastra/agents/parent-agent.ts const stream = await parentAgent.stream('Research AI trends', {
maxSteps: 10,
delegation: {
messageFilter: ({ messages, primitiveId, prompt }) => {
// Remove messages containing sensitive data
return messages
.filter(msg => {
const content =
typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)
return !content.includes('confidential')
})
.slice(-10) // Only pass the last 10 messages
},
},
})
回调接收完整对话历史(messages)、子智能体 ID(primitiveId)以及委托提示词(prompt),并返回筛选后的消息数组。 子智能体结果上下文 直接链接到子智能体结果上下文 当子智能体执行完毕后,父智能体的模型会在后续迭代中接收该子智能体的文本响应。嵌套工具调用及子智能体元数据(如线程和资源 ID)不会纳入父智能体的模型上下文。 应用代码和 UI 集成仍可检查工具结果负载中的 subAgentToolResults 及其他原始委托结果。 这样做既保留了调试和展示所需的数据,又避免了将嵌套工具参数或输出传回父智能体的下一次模型调用。 子智能体无法运行客户端工具(即没有 execute 函数的工具)。若子智能体调用了此类工具,其执行将中止于该步骤,finishReason 设为 'tool-calls'。委托结果会在 subAgentPendingToolCalls 中列出未处理的调用,父智能体的模型将获知哪些工具从未被执行,Mastra 也会记录警告。应将客户端工具交给父智能体,或在需要用户输入时于子智能体工具中使用 suspend()。 设置 includeSubAgentToolResultsInModelContext 可将完整子智能体结果(包括嵌套工具结果和子智能体元数据)纳入父智能体的模型上下文。 src/mastra/agents/parent-agent.tsawait parentAgent.generate('Research AI trends', {
delegation: {
includeSubAgentToolResultsInModelContext: true,
},
})
# 复用先前的子代理结果 默认情况下,子代理的结果会作为工具结果返回给父代理,但这些结果在转发给后续子代理的上下文中会被移除。父代理必须在下一次委派提示词中复述先前的结果,这既消耗 token,又会丢失细节。 设置 enableResultReferences 可让后续委派原样复用先前的结果: src/mastra/agents/parent-agent.tsawait parentAgent.generate('Find and fix the token refresh bug', {
delegation: {
enableResultReferences: true,
},
})
启用后: 每次成功且非空的子代理结果都会获得一个引用 ID,例如 explorer-1。父代理的模型会在子代理文本之后看到一行 [ref: explorer-1]。 委派工具会新增一个 contextFromRefs 输入。父代理可以传入先前的 ID,既可以作为字符串(["explorer-1"]),也可以作为带有可选 label 和 note 的对象([{ ref: "explorer-1", as: "investigation", note: "bug location" }])。 被引用的文本会插入到委派提示词之前,每个结果放在各自带标签的块中,内容与先前子代理生成时完全一致。onDelegationStart 和 messageFilter 接收到的是展开后的提示词。 如果 onDelegationComplete 返回了 resultText,后续委派收到的将是替换后的文本。 引用仅在父代理单次运行期间保存在内存中,不会被持久化。被拒绝、失败、为空以及后台任务的委派不会获得引用 ID。未知 ID 会被跳过并给出警告,委派继续进行。 被引用的文本来自另一个代理的输出。每个块都会使用全新的、不可预测的标签,并告知接收的子代理将内容视为数据。但如果子代理需要处理不受信任的输入,请在 onDelegationStart 或通过处理器添加你自己的校验。 # 迭代监控 父代理循环的每次迭代结束后都会调用 onIterationComplete。可以用它来监控执行情况或引导下一轮迭代,也可以提前终止执行。 src/mastra/agents/parent-agent.tsconst stream = await parentAgent.stream('Research AI trends', {
maxSteps: 10,
onIterationComplete: async context => {
console.log(`Iteration ${context.iteration}/${context.maxIterations}`)
console.log(`Finish reason: ${context.finishReason}`)
// Inject feedback to guide the agent
if (!context.text.includes('recommendations')) {
return {
continue: true,
feedback: '请在分析中加入具体的建议。',
}
}
// 当响应内容足够充分时提前终止
if (context.text.length > 1000 && context.finishReason === 'stop') {
return { continue: false }
}
return { continue: true }
},
})
返回 `{ continue: true }` 表示继续迭代,返回 `{ continue: false }` 表示停止。可以附带可选的 `feedback` 来向对话注入引导指令。如果同时设置 `feedback` 和 `continue: false`,模型可能会获得最后一次机会生成融合了反馈内容的文本响应。这种额外回合仅在当前迭代活跃时(例如工具调用完成后)才会被授予。 Memory isolation Mastra 在委托过程中隔离 Subagent 的记忆。Subagent 会接收完整的对话上下文以支持更好的决策,但仅其特定的委托提示词和响应会被保存到记忆中。 其工作机制如下: * 全量上下文转发:当父 Agent 进行委托时,Subagent 会接收父 Agent 对话中的所有消息 * 作用域内存保存:仅委托提示词和 Subagent 的响应会被保存到 Subagent 的记忆中 * 每次调用使用独立线程:每次委托都使用唯一的线程 ID,确保隔离性 因此,Subagent 既拥有必要的上下文,又避免了父 Agent 整个对话污染其记忆空间。欲了解更多细节,请访问多智能体系统(multi-agent systems)中的记忆机制说明。 Tool approval propagation 工具审批请求会沿委托链向上传播。当 Subagent 使用带有 `requireApproval: true` 的工具或调用 `suspend()` 时,审批请求会在父 Agent 的数据流中呈现。 const sensitiveDataTool = createTool({
id: 'get-user-data',
requireApproval: true,
execute: async input => {
return await database.getUserData(input.userId)
},
})
const dataAgent = new Agent({
id: 'data-agent',
tools: { sensitiveDataTool },
})
const parentAgent = new Agent({
id: 'parent-agent',
agents: { dataAgent },
memory: new Memory(),
})
const stream = await parentAgent.stream('Get data for user 123')
for await (const chunk of stream.fullStream) {
if (chunk.type === 'tool-call-approval') {
console.log('Tool requires approval:', chunk.payload.toolName)
}
}
取消机制 为“取消”部分提供直接链接 当把 abortSignal 传递给父 agent 的 stream() 或 generate() 调用时,Mastra 会将相同的信号转发给委派出去的 subagent。调用 AbortController.abort() 会在 subagent 的下一个执行步骤时取消正在运行的任务,而不是让它们继续运行到结束。 const controller = new AbortController()
const stream = await parentAgent.stream('Research AI trends', {
abortSignal: controller.signal,
})
// 取消父 agent 以及任何正在运行的 subagent
controller.abort()
任务完成度评分 为“任务完成度评分”部分提供直接链接 Agent 并非总能在第一次尝试时就生成完整且正确的输出。任务完成度评分器可以在每次迭代后验证任务是否已完成。如果验证失败,父 agent 将继续迭代。评分失败产生的反馈会包含在对话上下文中,以便 subagent 能看到缺失的内容。 import { createScorer } from '@mastra/core/evals'
const taskCompleteScorer = createScorer({
id: 'task-complete',
name: 'Task Completeness',
}).generateScore(async context => {
const text = (context.run.output || '').toString()
const hasAnalysis = text.includes('analysis')
const hasRecommendations = text.includes('recommendation')
return hasAnalysis && hasRecommendations ? 1 : 0
})
const stream = await parentAgent.stream('Research AI in education', {
maxSteps: 10,
isTaskComplete: {
scorers: [taskCompleteScorer],
strategy: 'all',
onComplete: async result => {
console.log('Task complete:', result.complete)
},
},
})
评分标准评分器 为“评分标准评分器”部分提供直接链接 内置的评分标准评分器允许你将“正确”定义为一个检查清单,让 agent 进行自我评估并迭代,直到满足所有标准或达到 maxSteps 限制。 它作为一种 LLM 裁判评分器工作。每次迭代后,一个独立的评判模型会根据评分标准审查 agent 的输出。当所有必要条件都通过时,循环结束。未通过的标准会将其反馈添加到对话中,以便 agent 重试。 这种方法在具有明确、可验证成功标准的任务中最为有效。您可以这样使用: src/mastra/agents/rubric-scorer.tsimport { Agent } from '@mastra/core/agent'
import { createRubricScorer } from '@mastra/evals/scorers/prebuilt'
const parentAgent = new Agent({
id: 'parent-agent',
instructions: '你使用专门的智能体来协调研究与写作。', model: 'openai/gpt-5.6-sol', agents: { researchAgent, writingAgent }, }) const rubricScorer = createRubricScorer({ model: 'openai/gpt-5-mini', criteria: [ { description: '响应中包含分析部分' }, { description: '响应中包含具体建议' }, ], }) const stream = await parentAgent.stream('研究 AI 在教育领域的应用', { maxSteps: 10, isTaskComplete: { scorers: [rubricScorer], strategy: 'all', }, }) 完整 API 细节请参阅 rubric scorer 参考文档。 编写有效的指令 清晰明确的指令是高效委派的前提。 父智能体的指令应说明有哪些可用资源、分别在什么情况下使用,同时还要定义协调行为和成功标准。 每个 subagent 都需要有一段清晰的描述,说明它的用途、返回格式,以及父智能体何时应该使用它。父智能体正是根据这些描述来做委派决策的。 const parentAgent = new Agent({ id: 'parent-agent', instructions: `你负责协调研究与写作任务。 可用资源: - researchAgent:收集事实数据和资料来源(返回要点列表) - writingAgent:把研究结果转化为叙事内容(返回完整段落) 委派策略: 1. 研究类请求:先委派给 researchAgent 2. 写作类请求:委派给 writingAgent 3. 复杂请求:先委派给 researchAgent,再委派给 writingAgent 成功标准: - 用户的所有问题都得到完整回答 - 响应格式良好、内容完整`, agents: { researchAgent, writingAgent }, }) 在后台运行 subagent Subagent 调用是以 tool call 的形式分发的,因此可以作为后台任务运行。当某个委派任务耗时较长、又不希望阻塞父智能体的响应时,这一点很有用。 在 Mastra 实例上启用 backgroundTasks 管理器,然后在父智能体上为 subagent 开启后台运行: src/mastra/agents/parent-agent.ts const parentAgent = new Agent({ id: 'parent-agent', instructions: '使用可用的智能体协调研究与写作。', model: 'openai/gpt-5.6-sol', ```html agents: { researchAgent, writingAgent },
backgroundTasks: {
tools: {
researchAgent: { enabled: true, timeoutMs: 900_000 },
writingAgent: { enabled: true, timeoutMs: 900_000 },
},
},
})
const stream = await parentAgent.streamUntilIdle('Research AI in education and write an article', {
memory: { thread: 't1', resource: 'u1' },
})
请改用 streamUntilIdle() 而非 stream(),以确保在子 Agent 完成且父 Agent 处理完其结果前,流保持打开状态。 如果某个子 Agent 未列在父 Agent 的 backgroundTasks.tools 中,但拥有支持后台执行的工具,父 Agent 仍会将其作为后台任务调度并继承其配置。详见下文关于从子 Agent 继承配置的说明。 子 Agent 版本管理直接链接到子 Agent 版本管理 在使用编辑器时,可以控制父 Agent 在运行时使用哪个已存储的子 Agent 版本。在 Mastra 实例或单次调用中设置版本覆盖: const result = await parentAgent.generate('Research and write about AI safety', {
versions: {
agents: {
'research-agent': { status: 'published' },
'writing-agent': { versionId: 'draft-456' },
},
},
})
版本覆盖会自动通过委派传递。有关解析顺序和服务器 API 用法的详细信息,请参阅子 Agent 版本管理文档。 相关链接直接链接到相关链接 后台任务 子 Agent 版本管理 指南:研究协调器 Agent.stream() 参考 Agent.streamUntilIdle() 参考 Agent.generate() 参考 Agent 审批 多智能体系统中的内存 概念:多智能体系统 📹 Mastra 监督者智能体研讨会 ```