工具审批流程允许您在执行敏感工具之前需要用户批准,让用户控制诸如发送电子邮件、进行购买或删除数据等操作。工具在审批过程中会经历以下状态
当工具需要批准时,典型的流程是
可以通过在定义中设置 needsApproval: true 来标记工具需要批准
import { toolDefinition } from "@tanstack/ai";
import { z } from "zod";
// Step 1: Define tool with approval requirement
const sendEmailDef = toolDefinition({
name: "send_email",
description: "Send an email to a recipient",
inputSchema: z.object({
to: z.string().email(),
subject: z.string(),
body: z.string(),
}),
outputSchema: z.object({
success: z.boolean(),
messageId: z.string(),
}),
needsApproval: true, // This tool requires approval
});
// Step 2: Create server implementation
const sendEmail = sendEmailDef.server(async ({ to, subject, body }) => {
// Only executes if approved
await emailService.send({ to, subject, body });
return { success: true, messageId: "..." };
});
在服务器上,具有 needsApproval: true 的工具将暂停执行并等待批准
import { chat, toServerSentEventsResponse } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
import { sendEmail } from "./tools";
export async function POST(request: Request) {
const { messages } = await request.json();
const stream = chat({
adapter: openaiText("gpt-5.2"),
messages,
tools: [sendEmail],
});
return toServerSentEventsResponse(stream);
}
客户端接收审批请求并可以做出响应
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
function ChatComponent() {
const { messages, sendMessage, addToolApprovalResponse } = useChat({
connection: fetchServerSentEvents("/api/chat"),
});
return (
<div>
{messages.map((message) => (
<div key={message.id}>
{message.parts.map((part) => {
// Check for approval requests
if (
part.type === "tool-call" &&
part.state === "approval-requested" &&
part.approval
) {
return (
<div key={part.id} className="approval-prompt">
<p>Approve: {part.name}</p>
<pre>{JSON.stringify(part.arguments, null, 2)}</pre>
<button
onClick={() =>
addToolApprovalResponse({
id: part.approval!.id,
approved: true,
})
}
>
Approve
</button>
<button
onClick={() =>
addToolApprovalResponse({
id: part.approval!.id,
approved: false,
})
}
>
Deny
</button>
</div>
);
}
// ... render other parts
})}
</div>
))}
</div>
);
}
这是一个更完整的审批 UI 组件
function ApprovalPrompt({ part, onApprove, onDeny }) {
const args = JSON.parse(part.arguments);
return (
<div className="border border-yellow-500 rounded-lg p-4 bg-yellow-50">
<div className="font-semibold mb-2">
🔒 Approval Required: {part.name}
</div>
<div className="text-sm text-gray-600 mb-4">
<pre className="bg-gray-100 p-2 rounded text-xs overflow-x-auto">
{JSON.stringify(args, null, 2)}
</pre>
</div>
<div className="flex gap-2">
<button
onClick={onApprove}
className="px-4 py-2 bg-green-600 text-white rounded-lg"
>
✓ Approve
</button>
<button
onClick={onDeny}
className="px-4 py-2 bg-red-600 text-white rounded-lg"
>
✗ Deny
</button>
</div>
</div>
);
}
客户端工具也可以需要批准
// tools/definitions.ts
const deleteLocalDataDef = toolDefinition({
name: "delete_local_data",
description: "Delete data from local storage",
inputSchema: z.object({
key: z.string(),
}),
outputSchema: z.object({
deleted: z.boolean(),
}),
needsApproval: true, // Requires approval even on client
});
// Client: Create implementation
const deleteLocalData = deleteLocalDataDef.client((input) => {
// This will only execute after approval
localStorage.removeItem(input.key);
return { deleted: true };
});
const { messages, addToolApprovalResponse } = useChat({
connection: fetchServerSentEvents("/api/chat"),
tools: [deleteLocalData], // Automatic execution after approval
});
// Define tool with approval requirement
const purchaseItemDef = toolDefinition({
name: "purchase_item",
description: "Purchase an item from the store",
inputSchema: z.object({
itemId: z.string(),
quantity: z.number(),
price: z.number(),
}),
outputSchema: z.object({
orderId: z.string(),
total: z.number(),
}),
needsApproval: true,
});
// Create server implementation
const purchaseItem = purchaseItemDef.server(async ({ itemId, quantity, price }) => {
const order = await createOrder({ itemId, quantity, price });
return { orderId: order.id, total: price * quantity };
});
用户将在购买之前看到一个审批提示,显示商品、数量和价格。只有在用户批准后,工具才会执行。