Skip to main content

代理商批准

🌐 Agent Approval

在调用处理敏感操作(如删除资源或执行长期运行的流程)的工具时,代理有时需要与工作流中使用的人类参与(human-in-the-loop)监督相同的监督。有了代理的批准,你可以暂停工具调用并向用户提供反馈,或者根据特定应用条件批准或拒绝工具调用。

🌐 Agents sometimes require the same human-in-the-loop oversight used in workflows when calling tools that handle sensitive operations, like deleting resources or performing running long processes. With agent approval you can suspend a tool call and provide feedback to the user, or approve or decline a tool call based on targeted application conditions.

工具调用批准
Direct link to 工具调用批准

🌐 Tool call approval

工具调用审批可以在代理级别启用,并适用于代理使用的每个工具,或在工具级别启用,从而对单个工具调用提供更精细的控制。

🌐 Tool call approval can be enabled at the agent level and apply to every tool the agent uses, or at the tool level providing more granular control over individual tool calls.

存储
Direct link to 存储

🌐 Storage

代理审批使用快照来捕捉请求的状态。请确保你已在主 Mastra 实例中启用了存储提供程序。如果未启用存储,你将看到与找不到快照相关的错误。

🌐 Agent approval uses a snapshot to capture the state of the request. Ensure you've enabled a storage provider in your main Mastra instance. If storage isn't enabled you'll see an error relating to snapshot not found.

src/mastra/index.ts
import { Mastra } from "@mastra/core/mastra";
import { LibSQLStore } from "@mastra/libsql";

export const mastra = new Mastra({
storage: new LibSQLStore({
id: "mastra-storage",
url: ":memory:"
})
});

代理级批准
Direct link to 代理级批准

🌐 Agent-level approval

在使用 .stream() 调用代理时,将 requireToolApproval 设置为 true,这将阻止代理调用其配置中定义的任何工具。

🌐 When calling an agent using .stream() set requireToolApproval to true which will prevent the agent from calling any of the tools defined in its configuration.

const stream = await agent.stream("What's the weather in London?", {
requireToolApproval: true
});

批准工具调用
Direct link to 批准工具调用

🌐 Approving tool calls

要批准工具调用,请从 agent 访问 approveToolCall,并传入流的 runId。这将让代理知道现在可以调用它的工具。

🌐 To approve a tool call, access approveToolCall from the agent, passing in the runId of the stream. This will let the agent know its now OK to call its tools.

const handleApproval = async () => {
const approvedStream = await agent.approveToolCall({ runId: stream.runId });

for await (const chunk of approvedStream.textStream) {
process.stdout.write(chunk);
}
process.stdout.write("\n");
};

减少工具调用
Direct link to 减少工具调用

🌐 Declining tool calls

要拒绝工具调用,请从 agent 访问 declineToolCall。你会看到代理的流式响应,但它不会调用其工具。

🌐 To decline a tool call, access the declineToolCall from the agent. You will see the streamed response from the agent, but it won't call its tools.

const handleDecline = async () => {
const declinedStream = await agent.declineToolCall({ runId: stream.runId });

for await (const chunk of declinedStream.textStream) {
process.stdout.write(chunk);
}
process.stdout.write("\n");
};

使用 generate() 的工具批准
Direct link to 使用 generate() 的工具批准

🌐 Tool approval with generate()

工具审批同样适用于非流式使用场景下的 generate() 方法。当将 generate()requireToolApproval: true 一起使用时,当工具需要审批时,该方法会立即返回,而不是执行该工具。

🌐 Tool approval also works with the generate() method for non-streaming use cases. When using generate() with requireToolApproval: true, the method returns immediately when a tool requires approval instead of executing it.

它是如何工作的
Direct link to 它是如何工作的

🌐 How it works

当工具在 generate() 调用期间需要批准时,响应包括:

🌐 When a tool requires approval during a generate() call, the response includes:

  • finishReason: 'suspended' - 表示代理正在等待批准
  • suspendPayload - 包含工具调用详情(toolCallIdtoolNameargs
  • runId - 需要批准或拒绝工具调用

批准工具调用
Direct link to 批准工具调用

🌐 Approving tool calls

要批准与 generate() 的工具调用,请使用 approveToolCallGenerate 方法:

🌐 To approve a tool call with generate(), use the approveToolCallGenerate method:

const output = await agent.generate("Find user John", {
requireToolApproval: true,
});

if (output.finishReason === "suspended") {
console.log("Tool requires approval:", output.suspendPayload.toolName);
console.log("Arguments:", output.suspendPayload.args);

// Approve the tool call and get the final result
const result = await agent.approveToolCallGenerate({
runId: output.runId,
toolCallId: output.suspendPayload.toolCallId,
});

console.log("Final result:", result.text);
}

减少工具调用
Direct link to 减少工具调用

🌐 Declining tool calls

要拒绝工具调用,请使用 declineToolCallGenerate 方法:

🌐 To decline a tool call, use the declineToolCallGenerate method:

if (output.finishReason === "suspended") {
const result = await agent.declineToolCallGenerate({
runId: output.runId,
toolCallId: output.suspendPayload.toolCallId,
});

// Agent will respond acknowledging the declined tool
console.log(result.text);
}

流式与生成对比
Direct link to 流式与生成对比

🌐 Stream vs Generate comparison

方面stream()generate()
响应类型流式分块完整响应
审核检测tool-call-approval 分块finishReason: 'suspended'
审核方法approveToolCall({ runId })approveToolCallGenerate({ runId, toolCallId })
拒绝方法declineToolCall({ runId })declineToolCallGenerate({ runId, toolCallId })
结果流式迭代完整输出对象

工具级批准
Direct link to 工具级批准

🌐 Tool-level approval

工具调用批准有两种类型。第一种使用 requireApproval,这是工具定义上的一个属性,而 requireToolApproval 是传递给 agent.stream() 的参数。第二种使用 suspend,并让代理提供上下文或确认提示,以便用户决定工具调用是否应继续。

🌐 There are two types of tool call approval. The first uses requireApproval, which is a property on the tool definition, while requireToolApproval is a parameter passed to agent.stream(). The second uses suspend and lets the agent provide context or confirmation prompts so the user can decide whether the tool call should continue.

使用 requireToolApproval 的工具批准
Direct link to tool-approval-using-requiretoolapproval

🌐 Tool approval using requireToolApproval

在这种方法中,requireApproval 是在工具定义(如下所示)上配置的,而不是在代理上配置的。

🌐 In this approach, requireApproval is configured on the tool definition (shown below) rather than on the agent.

export const testTool = createTool({
id: "test-tool",
description: "Fetches weather for a location",
inputSchema: z.object({
location: z.string()
}),
outputSchema: z.object({
weather: z.string()
}),
resumeSchema: z.object({
approved: z.boolean()
}),
execute: async (inputData) => {
const response = await fetch(`https://wttr.in/${inputData.location}?format=3`);
const weather = await response.text();

return { weather };
},
requireApproval: true
});

当工具的 requireApproval 为真时,流将包括类型为 tool-call-approval 的数据块,以表示通话已暂停。要继续通话,请使用所需的 resumeSchemarunId 调用 resumeStream

🌐 When requireApproval is true for a tool, the stream will include chunks of type tool-call-approval to indicate that the call is paused. To continue the call, invoke resumeStream with the required resumeSchema and the runId.

const stream = await agent.stream("What's the weather in London?");

for await (const chunk of stream.fullStream) {
if (chunk.type === "tool-call-approval") {
console.log("Approval required.");
}
}

const handleResume = async () => {
const resumedStream = await agent.resumeStream({ approved: true }, { runId: stream.runId });

for await (const chunk of resumedStream.textStream) {
process.stdout.write(chunk);
}
process.stdout.write("\n");
};

使用 suspend 的工具批准
Direct link to tool-approval-using-suspend

🌐 Tool approval using suspend

通过这种方法,代理和工具都不使用 requireApproval。相反,工具的实现会调用 suspend 来暂停执行,并向用户返回上下文或确认提示。

🌐 With this approach, neither the agent nor the tool uses requireApproval. Instead, the tool implementation calls suspend to pause execution and return context or confirmation prompts to the user.

export const testToolB = createTool({
id: "test-tool-b",
description: "Fetches weather for a location",
inputSchema: z.object({
location: z.string()
}),
outputSchema: z.object({
weather: z.string()
}),
resumeSchema: z.object({
approved: z.boolean()
}),
suspendSchema: z.object({
reason: z.string()
}),
execute: async (inputData, context) => {
const { resumeData: { approved } = {}, suspend } = context?.agent ?? {};

if (!approved) {
return suspend?.({ reason: "Approval required." });
}

const response = await fetch(`https://wttr.in/${inputData.location}?format=3`);
const weather = await response.text();

return { weather };
}
});

采用这种方法,流将包含一个 tool-call-suspended 块,而 suspendPayload 将包含由工具的 suspendSchema 定义的 reason。要继续调用,请使用所需的 resumeSchemarunId 调用 resumeStream

🌐 With this approach the stream will include a tool-call-suspended chunk, and the suspendPayload will contain the reason defined by the tool's suspendSchema. To continue the call, invoke resumeStream with the required resumeSchema and the runId.

const stream = await agent.stream("What's the weather in London?");

for await (const chunk of stream.fullStream) {
if (chunk.type === "tool-call-suspended") {
console.log(chunk.payload.suspendPayload);
}
}

const handleResume = async () => {
const resumedStream = await agent.resumeStream({ approved: true }, { runId: stream.runId });

for await (const chunk of resumedStream.textStream) {
process.stdout.write(chunk);
}
process.stdout.write("\n");
};

自动工具恢复
Direct link to 自动工具恢复

🌐 Automatic tool resumption

在使用调用 suspend() 的工具时,你可以启用自动恢复功能,使代理根据用户的下一条消息恢复已暂停的工具。这会创造一种对话流,让用户自然地提供所需信息,而无需你的应用显式调用 resumeStream()

🌐 When using tools that call suspend(), you can enable automatic resumption so the agent resumes suspended tools based on the user's next message. This creates a conversational flow where users provide the required information naturally, without your application needing to call resumeStream() explicitly.

启用自动恢复
Direct link to 启用自动恢复

🌐 Enabling auto-resume

在代理的默认选项中或调用 stream() 时,将 autoResumeSuspendedTools 设置为 true

🌐 Set autoResumeSuspendedTools to true in the agent's default options or when calling stream():

import { Agent } from "@mastra/core/agent";
import { Memory } from "@mastra/memory";

// Option 1: In agent configuration
const agent = new Agent({
id: "my-agent",
name: "My Agent",
instructions: "You are a helpful assistant",
model: "openai/gpt-4o-mini",
tools: { weatherTool },
memory: new Memory(),
defaultOptions: {
autoResumeSuspendedTools: true,
},
});

// Option 2: Per-request
const stream = await agent.stream("What's the weather?", {
autoResumeSuspendedTools: true,
});

它是如何工作的
Direct link to 它是如何工作的

🌐 How it works

当启用 autoResumeSuspendedTools 时:

🌐 When autoResumeSuspendedTools is enabled:

  1. 一个工具通过调用 suspend() 并携带负载(例如,请求更多信息)来暂停执行
  2. 挂起状态会连同对话一起保存到内存中
  3. 当用户在同一线程中发送下一条消息时,代理将会:
    • 从消息历史中检测挂起的工具
    • 根据工具的 resumeSchema 从用户的消息中提取 resumeData
    • 自动使用提取的数据恢复工具

示例
Direct link to 示例

🌐 Example

import { createTool } from "@mastra/core/tools";
import { z } from "zod";

export const weatherTool = createTool({
id: "weather-info",
description: "Fetches weather information for a city",
suspendSchema: z.object({
message: z.string(),
}),
resumeSchema: z.object({
city: z.string(),
}),
execute: async (_inputData, context) => {
// Check if this is a resume with data
if (!context?.agent?.resumeData) {
// First call - suspend and ask for the city
return context?.agent?.suspend({
message: "What city do you want to know the weather for?",
});
}

// Resume call - city was extracted from user's message
const { city } = context.agent.resumeData;
const response = await fetch(`https://wttr.in/${city}?format=3`);
const weather = await response.text();

return { city, weather };
},
});

const agent = new Agent({
id: "my-agent",
name: "My Agent",
instructions: "You are a helpful assistant",
model: "openai/gpt-4o-mini",
tools: { weatherTool },
memory: new Memory(),
defaultOptions: {
autoResumeSuspendedTools: true,
},
});

const stream = await agent.stream("What's the weather like?");

for await (const chunk of stream.fullStream) {
if (chunk.type === "tool-call-suspended") {
console.log(chunk.payload.suspendPayload);
}
}

const handleResume = async () => {
const resumedStream = await agent.stream("San Francisco");

for await (const chunk of resumedStream.textStream) {
process.stdout.write(chunk);
}
process.stdout.write("\n");
};

对话流程:

User: "What's the weather like?"
Agent: "What city do you want to know the weather for?"

User: "San Francisco"
Agent: "The weather in San Francisco is: San Francisco: ☀️ +72°F"

第二条消息会自动恢复被暂停的工具——代理从用户的消息中提取 { city: "San Francisco" } 并将其作为 resumeData 传递。

🌐 The second message automatically resumes the suspended tool - the agent extracts { city: "San Francisco" } from the user's message and passes it as resumeData.

要求
Direct link to 要求

🌐 Requirements

要使自动工具恢复功能正常工作:

🌐 For automatic tool resumption to work:

  • 内存已配置:代理需要内存来跟踪跨消息挂起的工具
  • 相同线程:后续消息必须使用相同的内存线程和资源标识符
  • resumeSchema 定义:该工具必须定义一个 resumeSchema,以便代理知道从用户消息中提取哪种数据结构

手动与自动恢复
Direct link to 手动与自动恢复

🌐 Manual vs automatic resumption

方法使用场景
手动 (resumeStream())程序化控制、Webhook、按钮点击、外部触发
自动 (autoResumeSuspendedTools)用户以自然语言提供简历数据的对话流程

两种方法都使用相同的工具定义。只有当消息历史中存在已暂停的工具且用户在同一对话线程中发送新消息时,自动恢复才会触发。

🌐 Both approaches work with the same tool definitions. Automatic resumption triggers only when suspended tools exist in the message history and the user sends a new message on the same thread.

🌐 Related