如何用 AI SDK 的 toolApproval 为 Agent 的工具调用添加审批流程
如何用 AI SDK 的 toolApproval 为 Agent 的工具调用添加审批流程【免费下载链接】aiThe AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents项目地址: https://gitcode.com/GitHub_Trending/ai/ai在 AI SDK 中带execute函数的工具默认在模型调用时直接执行。如果你的 Agent 里有会修改数据、扣款、执行代码、发送消息或访问私有数据的工具你需要在工具真正执行前加一道人工或自动审批。toolApproval就是为此提供的配置项它可以挂在ToolLoopAgent上也可以传给generateText和streamText。需要明确两点边界toolApproval只约束由 AI SDK 在本地执行的工具Provider 侧执行provider-executed的工具在提供方运行不受该设置影响。旧版tool()定义上的needsApproval属性已弃用新代码应把审批逻辑迁移到toolApproval当两者同时存在时toolApproval优先于工具自身的needsApproval默认值。准备条件是一个 TypeScript 项目依赖ai包、任一模型提供方包示例使用ai-sdk/openai和zod。下文中model统一写作openai(gpt-4o)你可以替换为任提供方包导出的模型实例。四种审批状态及其返回形式每条审批规则最终返回四种状态之一既可以传字符串也可以传带type字段的对象not-applicable不带审批元数据地正常执行工具这是默认行为。approved记录一次自动批准然后执行工具。denied记录一次自动拒绝返回 denied 的工具输出。user-approval发出审批请求等待明确的人工响应。审批函数也可以返回undefined效果等同于not-applicable。自动批准和自动拒绝想附带原因时使用对象形式手动审批的对象形式则会把reason放进审批请求中展示给审批人toolApproval: { deleteFile: { type: denied, reason: Deleting files is disabled in this workspace, }, }主路径ToolLoopAgent 上要求手动审批最简单的用法是按工具名配置一个状态映射。以下示例来自 ToolLoopAgent 参考文档 的 Approved Tool Execution 示例完整走一遍首次调用返回审批请求 → 记录批准 → 第二次调用执行工具的流程import { ToolLoopAgent, ModelMessage, ToolApprovalResponse, tool } from ai; import { openai } from ai-sdk/openai; import { z } from zod; const agent new ToolLoopAgent({ model: openai(gpt-4o), instructions: You are an agent with access to a weather API., tools: { weather: tool({ description: Get the weather in a location, inputSchema: z.object({ location: z.string(), }), execute: async ({ location }) ({ location, temperature: 72, }), }), }, toolApproval: { weather: user-approval, }, }); const messages: ModelMessage[] [ { role: user, content: Is it raining in Paris today? }, ]; const result await agent.generate({ messages }); const approvals: ToolApprovalResponse[] []; for (const part of result.content) { if (part.type tool-approval-request) { approvals.push({ type: tool-approval-response, approvalId: part.approvalId, approved: true, }); } } messages.push(...result.responseMessages); messages.push({ role: tool, content: approvals }); const approvedResult await agent.generate({ messages }); console.log(approvedResult.text);关键机制当工具需要手动审批时agent.generate()、generateText和streamText并不会暂停等待而是直接完成并返回包含tool-approval-request部分的结果。因此手动审批天然需要两次模型调用第一次拿到审批请求第二次带着审批响应再调用批准后工具执行拒绝则模型收到拒绝信息后自行回应。验证方式就是检查result.content出现part.type tool-approval-request的条目说明审批流程生效其中part.approvalId是本次请求的唯一 IDpart.toolCall包含toolName与inputpart.reason是要求审批的原因。isAutomatic为true的条目对应自动批准/拒绝不需要你的程序再收集人工决定只有!part.isAutomatic的条目才需要构造tool-approval-response。批准时把approved置为true拒绝时置为falsereason字段是可选的作为给模型的上下文。把拒绝路径也走通在tool-approval-response中设置approved: false第二次调用后工具不会执行模型会基于拒绝信息生成回复。文档建议此时在 Agent 的instructions中加入类似 When a tool execution is not approved, do not retry it 的指令防止模型反复重试同一动作。根据工具输入动态决定审批当审批决策依赖解析后的工具输入时每个工具可以配置一个SingleToolApprovalFunction。函数收到类型化的工具输入以及toolCallId、messages、toolContext、runtimeContext选项runtimeContext是generateText/streamText/Agent 共用的运行时上下文import { ToolLoopAgent, tool } from ai; import { openai } from ai-sdk/openai; import { z } from zod; const agent new ToolLoopAgent({ model: openai(gpt-4o), tools: { processPayment: tool({ inputSchema: z.object({ amount: z.number(), recipient: z.string(), }), execute: async ({ amount, recipient }) processPayment({ amount, recipient }), // 替换为你自己的支付实现 }), }, toolApproval: { processPayment: async ({ amount }, { runtimeContext }) { if (runtimeContext.role ! admin) { return { type: denied, reason: Only admins can send payments }; } return amount 1000 ? user-approval : undefined; }, }, });这个示例的行为非管理员的支付被自动拒绝金额大于 1000 的管理员支付需要人工审批小额管理员支付直接执行函数返回undefined等同于not-applicable。如果需要的是一个策略管所有工具——决策依赖完整的toolCall、跨工具共享状态或整个工具集——则直接把GenericToolApprovalFunction作为toolApproval传入const agent new ToolLoopAgent({ model: openai(gpt-4o), tools: { readFile: tool({ inputSchema: z.object({ path: z.string() }), execute: async ({ path }) readFile(path), // 替换为你的实现 }), deleteFile: tool({ inputSchema: z.object({ path: z.string() }), execute: async ({ path }) deleteFile(path), // 替换为你的实现 }), }, toolApproval: ({ toolCall }) { if (toolCall.dynamic) { return user-approval; } if (toolCall.toolName deleteFile) { return user-approval; } return undefined; }, });通用函数收到toolCall含toolName、toolCallId、input及是否 dynamic、tools、toolsContext、messages和runtimeContext。如果审批策略还取决于每次调用的选项租户策略、用户权限等可以在prepareCall中按请求返回toolApproval例如根据callOptionsSchema里的canRunCommands布尔值在user-approval和{ type: denied, reason: Command access is disabled }之间切换。在 useChat 聊天界面中处理审批把 Agent 以流式方式接到聊天界面时审批请求表现为state: approval-requested的工具 part通过addToolApprovalResponse回传决定。最小客户端示例来自 Tool Approvals 文档use client; import { useChat } from ai-sdk/react; import { lastAssistantMessageIsCompleteWithApprovalResponses } from ai; export default function Chat() { const { messages, addToolApprovalResponse } useChat({ sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses, }); return messages.map(message message.parts.map(part { if (part.type ! tool-runCommand) { return null; } if (part.state approval-requested !part.approval.isAutomatic) { return ( div key{part.toolCallId} {part.approval.requestReason ( p{part.approval.requestReason}/p )} button onClick{() addToolApprovalResponse({ id: part.approval.id, approved: true, }) } Approve /button button onClick{() addToolApprovalResponse({ id: part.approval.id, approved: false, }) } Deny /button /div ); } }), ); }几个执行层面的判断点addToolApprovalResponse只对手动审批调用。自动批准/拒绝的part.approval.isAutomatic为true其状态已经包含在流里直接渲染即可。手动审批请求携带的原因读part.approval.requestReason你通过addToolApprovalResponse传入的reason则单独存放在part.approval.reason。sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses会在最后一步的所有审批都有响应后自动发送下一条消息如果批准之后界面没有动静文档给出的排查点是确认配置了sendAutomaticallyWhen或在批准后手动调用sendMessage。后续工具 part 的状态流转批准则执行并出现output-availablepart.output为工具结果拒绝则进入output-denied。服务端配合 Chatbot Tool Usage 文档 中的模式在streamText上设置toolApproval用createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }) })返回流即可。用 experimental_toolApprovalSecret 防止审批被伪造在标准useChat模式里服务器每轮都根据客户端发来的消息重建会话并不在请求之间持久化会话状态也就是说消息历史是客户端可控的输入。审批在重放前会重新校验——工具输入会对照工具 schema 检查、审批策略会重新求值——但没有额外保护时客户端仍可能为符合 schema 的输入伪造一个看似有效的审批绕过人工审批环节。对执行敏感操作的场景修改数据、扣款、调用外部 API、访问私有资源配置experimental_toolApprovalSecret服务器在签发审批请求时做 HMAC 签名重放时验证签名伪造或被篡改的审批会在工具执行前被拒绝。const agent new ToolLoopAgent({ model: openai(gpt-4o), tools: { deleteFile, runQuery }, // 你的工具定义 toolApproval: { deleteFile: user-approval, runQuery: user-approval }, experimental_toolApprovalSecret: process.env.TOOL_APPROVAL_SECRET, }); const result await agent.generate({ messages, });配置 secret 的步骤生成一个至少 32 字节的高熵随机串openssl rand -base64 32将其存为所有服务器实例都可读的环境变量例如TOOL_APPROVAL_SECRET上一步生成的值占位值需替换为你实际生成的字符串。通过experimental_toolApprovalSecret传给ToolLoopAgent、generateText或streamText。配置后的行为签名无效的审批请求会被拒绝fail-closed未配置 secret 时行为与之前一致向后兼容secret 不会发送给客户端也不会出现在流里。签名把审批绑定到确切的工具名、toolCallId和输入参数签发之后改动其中任何一项都会使审批失效。对于 serverless 部署每个可能处理请求的实例都需要配置同一个 secret因为签发和验证可能发生在不同实例上。另外WorkflowAgent也支持该 secret但它要求传入环境变量引用如{ environmentVariable: TOOL_APPROVAL_SECRET }让原始 secret 只在签名与验证步骤内读取只有签名会被持久化和发给客户端。遇到审批相关错误如何判断文档列出了两个与审批直接相关的错误类型都可以在抛出异常后用isInstance判断AI_InvalidToolApprovalError——工具审批响应引用了未知的approvalId消息历史中找不到对应的tool-approval-requestimport { InvalidToolApprovalError } from ai; if (InvalidToolApprovalError.isInstance(error)) { // 处理未知 approvalId 的审批响应 }AI_InvalidToolApprovalSignatureError——配置了experimental_toolApprovalSecret后从消息历史重放的审批缺少或携带无效的 HMAC 签名服务器在工具执行前拒绝。文档给出的常见原因审批由客户端伪造无签名审批签名后工具参数被修改签发与验证之间服务器 secret 发生了变化例如密钥轮换没有重叠期import { InvalidToolApprovalSignatureError } from ai; if (InvalidToolApprovalSignatureError.isInstance(error)) { // 处理签名校验失败的审批 }限制与适用范围Provider 侧执行的工具不受toolApproval控制它只作用于 AI SDK 本地执行的工具。Subagent 的工具不能使用toolApproval。needsApproval仅配合WorkflowAgent使用审批会挂起并恢复持久化的 workflow 执行普通ToolLoopAgent、generateText、streamText场景使用toolApproval。审批策略的更细粒度写法可以参考 Policy-Based Tool Approvalsai-sdk/policy-opa。相关文档Tool ApprovalsAgent 审批的完整说明含prepareCall按请求配置审批。Tools and Tool CallingtoolApproval在generateText/streamText上的配置与手动审批流程。Chatbot Tool UsageuseChat侧的审批 UI 状态与addToolApprovalResponse。ToolLoopAgent 参考toolApproval参数定义与可运行的批准执行示例。Human-in-the-Loop with Next.jsNext.js 聊天机器人中完整的前后端审批示例。【免费下载链接】aiThe AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents项目地址: https://gitcode.com/GitHub_Trending/ai/ai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考