3行まとめ
このテーマをもう少し広げて見るなら、OpenAI Agents SDKのtool guardrailsを実務に入れる前に:handoff・built-in tools・人間承認の分け方 と MCP ElicitationをAIエージェントに入れる前に:form・URL・OAuth・人間承認の分け方 も合わせて確認してください。Vercel AI SDKのtool approvalと、OpenAI Agents SDK側のtool guardrailsやhandoff設計を比較できる。
generateText/streamTextは2回呼び出し前提。
approval stateをUIで扱います。
長時間停止と再開はWorkflowAgentへ。
承認はボタンを出すだけでなく、どこで状態を保持するかまで決めます。
- Vercel AI SDKのtool approvalは、短時間の
generateText/streamText、chat UI上の承認、長時間停止できるWorkflowAgentで分けて設計します。 needsApprovalを付けたtoolは、Coreでは承認要求を返して一度止まり、approval responseをmessagesへ追加して2回目のmodel callで続行する前提です。- 承認まで数時間空く、retryや画面更新をまたぐ、step単位で観測したい場合は、in-memoryの
ToolLoopAgentではなくWorkflowAgentへ寄せます。
本文の事実確認には、AI SDK公式Docs、Vercel Knowledge BaseのWorkflowAgent記事、VercelのChat SDK/Workflow SDK human-in-the-loop記事、OpenAI Agents SDKのhuman-in-the-loop Docsを使っています。Xで見かけるhuman-in-the-loopやtool approvalへの投稿は需要シグナルとして扱い、本文の根拠にはしていません。
この記事でわかること
どの操作に承認を付けるか。
2回目のmodel callを組みます。
approve/deny後の続行を扱います。
長時間承認ならworkflowへ寄せます。
tool approvalはUX、状態管理、監査をまとめて扱います。
needsApprovalを付けるべきtoolの条件generateText/streamTextでapprovalが2回呼び出しになる理由useChatでapproval UIを扱うときのstateToolLoopAgentを短いagent loopとして使うときの注意WorkflowAgentへ寄せるべき長時間承認の条件- subagent toolsで
needsApprovalを使えない制限
AI agentのtool approvalは、単に「危ない操作の前にボタンを出す」だけでは足りません。承認者が何を見て判断するのか、否認されたらどう会話を続けるのか、承認待ちの状態をどこに保存するのかまで決めます。
この記事では、Vercel AI SDKで承認付きtool callingを作るときの置き場所を、実務の運用単位で分けます。
前提知識
| 項目 | 内容 | 見方 |
|---|---|---|
| needsApproval | tool実行前に承認を要求します。 | |
| toolApproval | Core/agent側の承認経路です。 | |
| ToolLoopAgent | in-memoryのagent loopです。 | |
| WorkflowAgent | workflow runtimeで再開できます。 |
同じ承認でも、保持する状態と再開方法が違います。
AI SDKのtool referenceでは、tool()のneedsApprovalはbooleanまたはtool argumentsを受け取る関数として指定でき、tool実行前にuser approvalが必要かを表すと説明されています。
AI SDK Coreのtool calling Docsでは、needsApproval: trueのtoolは、generateTextやstreamTextで自動実行されず、approval requestを返す流れが説明されています。approval responseをmessagesへ追加して、もう一度generateTextを呼び出すと、承認ならtoolが実行され、否認ならmodelへ否認が伝わります。
VercelのWorkflowAgent記事では、ToolLoopAgentはin-memoryで、process crash、function timeout、refreshで進行状態を失う一方、WorkflowAgentはWorkflow SDK runtime上でstatefulness、resumability、human-in-the-loop、observabilityを補うと説明されています。
この記事の扱う範囲
| 領域 | 役割 |
|---|---|
needsApproval | tool実行前に承認を要求する |
| Core approval | generateText/streamTextで2回呼び出しを組む |
useChat | UI stateとして承認/否認を扱う |
ToolLoopAgent | 短いin-memory agent loop |
WorkflowAgent | 長時間停止、retry、再開、観測を扱う |
| subagent | context分離とapproval制限を確認する |
条件
2026年5月31日時点で公開されているAI SDK/Vercel公式情報を確認しています。AI SDKはversionごとの変更が起きやすいため、実装時には利用version、Next.js/Vercel runtime、Chat SDK/Workflow SDKの組み合わせを確認してください。
注意点
承認は安全性を上げますが、承認者が入力、対象、影響、rollback可否を読めなければレビューになりません。承認UIには「何を実行するか」を必ず出します。
approvalを3つの経路に分ける
| 項目 | 内容 | 見方 |
|---|---|---|
| Core | 短時間の2回呼び出しで承認します。 | |
| Chat UI | message partのstateを画面で扱います。 | |
| ToolLoopAgent | 短いagent loop向けです。 | |
| WorkflowAgent | 長時間停止、retry、再開向けです。 |
人がすぐ押す承認と、数時間後に戻る承認は分けて扱います。
tool approvalは、3つの経路に分けます。
| 経路 | 向く場面 | 状態の持ち方 |
|---|---|---|
Core generateText/streamText | 短い承認、1画面で完結 | messagesへapproval responseを追加 |
Chat UI useChat | ユーザーが画面でapprove/denyするchat | message part stateとUI |
WorkflowAgent | 数分から数日待つ承認、retry、resume | Workflow SDK runtime |
短時間ならCoreでよい
たとえば「このメールを送信してよいか」「このコマンドを実行してよいか」を同じ画面で確認するなら、Coreのapproval flowで十分なことがあります。
chat体験ならUI stateを中心にする
チャット画面で承認するなら、承認要求、承認済み、否認、tool結果、tool失敗を見せます。承認ボタンだけを出して、否認後のmodel応答を設計しないと、会話が不自然になります。
長時間ならWorkflowAgentへ寄せる
承認まで時間が空く、function timeoutをまたぐ、画面更新やdeployをまたぐ、toolごとにretryしたいなら、WorkflowAgentへ寄せます。
判断基準
「承認待ちの状態をメモリに置いて失ってもよいか」で分けます。失ってはいけないならworkflow runtimeへ寄せます。
needsApprovalで止める操作
課金、返金、購入。
DB更新、削除、外部API変更。
メール、Slack、通知送信。
非公開データや社内情報へのアクセス。
承認対象は、失敗時に取り消しにくい操作から選びます。
needsApprovalは、tool実行前にユーザーの明示判断を入れたい操作に付けます。
承認を付けるtool
| 操作 | 例 |
|---|---|
| 金銭 | 購入、返金、課金、送金 |
| データ変更 | DB更新、削除、外部API mutation |
| 外部送信 | メール、Slack、通知、SNS投稿 |
| コマンド実行 | shell、deploy、file write |
| 非公開情報 | private data取得、社内ログ参照 |
| 権限変更 | invite、role変更、API key発行 |
AI SDK Docsでも、sensitive operationsとしてpayment、deletion、external API callsなどにtool execution approvalを使う説明があります。
動的approvalを使う
AI SDKのtool referenceでは、needsApprovalに関数を渡せるため、tool inputに応じて承認要否を変えられます。
import { tool } from "ai";
import { z } from "zod";
export const processRefund = tool({
description: "Process a refund after user approval",
inputSchema: z.object({
orderId: z.string(),
amount: z.number(),
reason: z.string(),
}),
needsApproval: async ({ amount }) => amount > 1000,
execute: async ({ orderId, amount, reason }) => {
return { orderId, amount, reason, status: "refunded" };
},
});
この例では、高額返金だけ承認にします。すべてのtoolに承認を付けるとUXが壊れるので、取り消しにくい操作から選びます。
承認UIに出す情報
承認者には、最低限次の情報を出します。
- tool名
- 実行対象
- 入力値
- 影響範囲
- 取り消し可否
- 実行者
- 否認した場合の次の挙動
ただの確認ボタンにしない
「AIがtoolを使おうとしています。承認しますか」だけでは弱いです。何が変わるのか、誰に影響するのか、戻せるのかを表示します。
generateTextとstreamTextの2回呼び出し
- 11st call
tool approval requestが返ります。
- 2Review
ユーザーが内容を確認します。
- 3Message
approval responseをmessagesへ追加。
- 42nd call
承認ならtool実行、否認なら説明へ。
Coreでは承認要求を返して終わり、続行はアプリ側が組み立てます。
AI SDK Coreのtool calling Docsでは、generateTextやstreamTextはapprovalが必要なtoolをその場でpauseするのではなく、approval requestを返して完了すると説明されています。続行するにはapproval responseをmessagesへ追加し、もう一度model callを行います。
基本フロー
needsApproval: trueのtoolを含めてgenerateTextを呼ぶ- modelがtool callを生成する
- result contentにapproval requestが返る
- アプリがユーザーへ承認を求める
- approval responseをmessagesへ追加する
- もう一度
generateTextを呼ぶ - 承認ならtoolが実行され、否認ならmodelが否認を受け取る
否認後の再試行を止める
AI SDK Docsでは、tool executionがdeniedされたときに、同じtoolを再試行しないようsystem instructionを検討する説明があります。これは実務でかなり大事です。
const system = `
When a tool execution is not approved, do not retry the same tool call.
Explain what was not done and ask the user for a safer next step.
`;
否認されたのにmodelが少し引数を変えて再度tool callする、という体験は避けます。
streamTextでも設計は同じ
streamingだから承認が自動的に安全になるわけではありません。approval requestを受け取り、UIで確認し、approval responseをmessage historyへ入れ、続きのcallを走らせます。
実務例
「このSlack通知を送って」と言われたら、1回目のcallでsendSlackMessageのapproval requestを返します。UIで送信先、本文、影響を表示し、承認されたら2回目のcallで実行します。
useChatでapproval UIを扱う
| 項目 | 内容 | 見方 |
|---|---|---|
| input-available | tool入力が確定した状態。 | |
| approval-requested | 承認待ちを表示します。 | |
| approval-responded | ユーザー判断が入った状態。 | |
| output-denied | 否認時の表示も用意します。 |
承認UIは、承認ボタンだけでなく否認後の会話も設計します。
AI SDK UIのChatbot Tool Usage Docsでは、tool execution approvalはserver-side toolの実行前にユーザー確認を入れる用途として説明されています。needsApprovalをtoolに設定し、UIではapproval stateを見てボタンを出します。
UI stateの例
| state | 画面でやること |
|---|---|
input-streaming | 入力生成中として表示 |
input-available | tool入力を表示 |
approval-requested | approve/denyボタンを表示 |
approval-responded | 判断済みとして表示 |
output-available | tool結果を表示 |
output-denied | 否認されたことを表示 |
output-error | 実行失敗を表示 |
approve/deny後の続行
Chat UIでは、addToolApprovalResponseでapproval responseを追加し、承認後に自動続行する条件を作る流れが紹介されています。
承認後だけでなく、否認後の会話も用意します。否認されたら、「実行しませんでした。必要なら条件を変えて再依頼してください」と返す方が自然です。
表示すべき情報
承認カードには、tool nameよりユーザーが理解できるtitleを出します。たとえばsend_emailではなく「メールを送信」、delete_recordではなく「顧客レコードを削除」です。
UIで避けること
- 入力JSONだけを出す
- 送信先や金額を省く
- denyボタンを目立たなくする
- 否認後に同じtoolをすぐ再提案する
- 誰が承認したか残さない
ToolLoopAgentで使うときの注意
tool結果を使って次stepへ進みます。
stopWhenで上限を決めます。
短時間の承認に向きます。
長時間停止はWorkflowAgentを検討。
ToolLoopAgentは便利ですが、長時間承認を永続化する層ではありません。
AI SDK Agents overviewでは、agentはLLM、tools、loopで構成され、ToolLoopAgentがcontext managementとstopping conditionsを扱うと説明されています。ToolLoopAgent referenceでは、tool resultを集め、completionまたはuser approvalが必要になるまでloopできる説明があります。
ToolLoopAgentに向く場面
| 向く場面 | 理由 |
|---|---|
| 短いmulti-step | memory上で完結しやすい |
| すぐ承認される操作 | approval待ちが短い |
| UI session内の作業 | 画面が残っている |
| retryを自前で扱う | workflow化するほどではない |
stopWhenを決める
AI SDKのstepCountIs()は、generateTextやstreamTextのstopWhenでtool-calling loopを止める条件として使える説明があります。ToolLoopAgentでも、loopが伸びすぎないようstop conditionを決めます。
import { ToolLoopAgent, stepCountIs } from "ai";
const agent = new ToolLoopAgent({
model,
instructions: "Use tools only when needed. Ask before sensitive actions.",
tools,
stopWhen: stepCountIs(6),
});
長時間承認には弱い
VercelのWorkflowAgent記事では、standard ToolLoopAgentはin-memoryで、process crash、function timeout、user refreshにより進行状態を失うと説明されています。
だから、ToolLoopAgentは「短く終わるloop」に使います。承認まで何時間も待つ業務フローは、WorkflowAgentの方が自然です。
実務例
「検索して候補を3つ出し、選ばれた候補で下書きメールを作る」程度ならToolLoopAgentでよいです。「承認者が翌日に確認して、その後deployする」ならWorkflowAgentへ寄せます。
WorkflowAgentへ寄せる条件
| 項目 | 内容 | 見方 |
|---|---|---|
| Hours | 承認まで時間が空きます。 | |
| Retry | tool callをworkflow stepとしてretryしたい。 | |
| Refresh | 画面更新やtimeoutをまたぎたい。 | |
| Audit | step単位でinput/outputを見たい。 |
長時間止まる承認は、in-memory loopではなくworkflow runtimeへ寄せます。
VercelのWorkflowAgent記事では、WorkflowAgentはToolLoopAgentと同じagent loopをworkflow runtime上で動かし、tool callをdurable stepにすることで、statefulness、resumability、tool retries、human-in-the-loop、observabilityを補うと説明されています。
WorkflowAgentにするサイン
| 条件 | 理由 |
|---|---|
| 承認まで時間が空く | hours/days待てる |
| function timeoutをまたぐ | workflow stateへ保存できる |
| refreshやdeployをまたぐ | stateを失いにくい |
| tool retryが必要 | step単位でretryできる |
| 承認履歴を見たい | workflow dashboardで観測しやすい |
needsApprovalの違い
WorkflowAgent記事では、WorkflowAgentではtoolにneedsApprovalを設定でき、booleanまたはasync functionでper-input decisionsができる説明があります。一方、generateText、streamText、ToolLoopAgentのequivalent featureはtoolApproval optionと説明されています。
ここを混ぜると実装が崩れます。Core/ToolLoopAgentのapproval flowと、WorkflowAgentのworkflow runtime上の承認を分けます。
retryと副作用に注意
workflow stepはretryされる可能性があります。外部APIや決済のような副作用toolは、idempotency key、重複実行防止、実行済み記録を必ず設計します。
実務例
「本番deploy承認」はWorkflowAgent向きです。承認カードを出し、webhookで待ち、承認者と時刻を記録し、deploy stepを実行し、結果をthreadへ返します。
subagentとapprovalの制限
subagent toolsではneedsApprovalを使えません。
subagent contextは独立します。
承認が必要な操作は親側へ戻します。
subagentにはread-only toolを渡します。
subagentに危険toolを持たせて承認で止める、という設計にはしません。
AI SDKのSubagents Docsでは、subagent toolsではneedsApprovalを使えず、すべてのtoolsはuser confirmationなしに自動実行される必要があると説明されています。
subagentに渡すtool
| tool種別 | 渡すか |
|---|---|
| read-only search | 渡しやすい |
| summarization | 渡しやすい |
| local transform | 条件付き |
| DB write | 渡さない |
| email送信 | 渡さない |
| payment/refund | 渡さない |
承認が必要なら親に戻す
subagentに危険toolを持たせて、needsApprovalで止める設計にはしません。承認が必要な操作は、親agentやworkflowへ戻して承認します。
context isolationも見る
Subagents Docsでは、subagent contextは独立し、main agentの蓄積contextを継承しないことが利点として説明されています。承認判断に必要な情報をsubagentだけに持たせると、親側の承認UIで説明が不足します。
実務例
research subagentには検索と要約だけを渡します。メール送信やDB更新は親agentの承認付きtoolとして残します。
最小実装の形
| 項目 | 内容 | 見方 |
|---|---|---|
| Tool schema | 入力を具体的にします。 | |
| needsApproval | 危険toolだけ承認にします。 | |
| UI state | 承認/否認/失敗を表示します。 | |
| Audit log | 誰が何を承認したか残します。 |
承認は安全弁なので、説明とログも一緒に作ります。
最初は、危険toolを1つだけ承認付きにします。
import { tool } from "ai";
import { z } from "zod";
export const sendEmail = tool({
title: "メールを送信",
description: "Send an email after the user reviews the recipient and body.",
inputSchema: z.object({
to: z.string().email(),
subject: z.string().max(120),
body: z.string().max(4000),
}),
needsApproval: true,
execute: async ({ to, subject, body }) => {
return {
status: "sent",
to,
subject,
messageId: "sample-message-id",
};
},
});
approval cardで見せる内容
type ApprovalView = {
toolTitle: string;
target: string;
summary: string;
irreversible: boolean;
requestedBy: string;
};
UIには、to、subject、bodyの要約、取り消し可否、要求者を出します。body全文が長い場合は折りたたみますが、承認前に確認できるようにします。
audit log
type ToolApprovalAudit = {
approvalId: string;
toolName: string;
approved: boolean;
approverId: string;
requestedAt: string;
decidedAt: string;
inputHash: string;
reason?: string;
};
実行結果だけでなく、否認も残します。否認が多いtoolは、説明不足かtool description不足の可能性があります。
最初に作るテスト
- 承認されたらtoolが1回だけ実行される
- 否認されたらtoolが実行されない
- 否認後に同じtoolを再試行しない
- approvalIdが不正ならエラーになる
- UIに対象、入力、影響、否認理由が出る
導入初週の進め方
- 1日目
承認対象toolを3つに絞ります。
- 2日目
Core flowで2回呼び出しを確認。
- 3日目
UIでdeny時の表示を作る。
- 5日目
長時間承認をWorkflowAgentへ分離。
- 7日目
audit logと失敗ケースを追加。
最初から全部承認にせず、取り消しにくい操作から始めます。
tool approvalは、最初から全toolに入れません。UXが重くなり、承認者が流れ作業で押すようになります。
1日目: 承認対象を3つに絞る
payment、delete、external sendのように、取り消しにくいtoolだけを承認対象にします。read-only toolには承認を付けません。
2日目: Core flowで2回呼び出しを確認する
generateTextまたはstreamTextでapproval requestが返り、approval responseをmessagesへ追加して続行できることを確認します。
3日目: deny時のUXを作る
否認後に同じtoolを再試行しないsystem instruction、否認理由の表示、代替案の提示を作ります。
5日目: 長時間承認をWorkflowAgentへ分ける
承認まで時間が空く業務をWorkflowAgentへ分けます。workflow stepのretryと副作用のidempotencyも確認します。
7日目: audit logと失敗ケースを追加する
誰が何を承認したか、否認したか、toolが実行されたかを残します。不正なapprovalId、二重クリック、期限切れ承認もテストします。
評価するログ
- 承認者が実行内容を理解できたか
- deny後にagentが同じtoolを再試行していないか
- toolが二重実行されていないか
- long-running approvalがrefreshやtimeoutをまたげたか
- subagentへ危険toolが渡っていないか
FAQ
UXが壊れるので危険操作だけにします。
同じtoolを再試行しない指示を入れます。
WorkflowAgentへ寄せます。
承認toolを持たせない設計にします。
迷ったら、承認者が何を見て判断できるかに戻ります。
すべてのtoolにapprovalを付けるべきですか
不要です。read-only searchやformat変換まで承認にすると、ユーザーは内容を読まずに押すようになります。承認は、取り消しにくい操作や外部影響のある操作へ絞ります。
needsApprovalだけで監査になりますか
なりません。承認機能と監査ログは別です。誰が、いつ、どの入力で、何を承認または否認したかをアプリ側で残します。
否認されたらどうしますか
toolを実行せず、modelへ否認を伝えます。さらに、同じtool callをすぐ再試行しないようsystem instructionを入れます。否認理由があるなら、次の安全な選択肢を提示します。
長時間承認はCoreだけでできますか
短い承認ならCoreで十分ですが、数時間から数日待つ、function timeoutやpage refreshをまたぐ、step単位でretryしたいならWorkflowAgentを検討します。
subagentに承認付きtoolを持たせられますか
AI SDKのSubagents Docsでは、subagent toolsではneedsApprovalを使えないと説明されています。承認が必要なtoolは親agentやworkflow側に残します。
MCP toolにも同じ考え方を使えますか
使えます。read-only toolから始め、write toolには承認、権限、audit logを置きます。MCPの権限設計は、MCP仕様・SDK更新時の実務影響チェックとTypeScriptで作る最小MCP Serverも合わせて確認してください。
次に読むなら
参照した主な情報源
- AI SDK Docs: Tool Calling / Tool Execution Approval
https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling
- AI SDK Docs:
tool()reference
https://ai-sdk.dev/docs/reference/ai-sdk-core/tool
- AI SDK Docs: Chatbot with Tool Calling
https://ai-sdk.dev/docs/ai-sdk-ui/chatbot-with-tool-calling
- AI SDK Docs: Subagents
https://ai-sdk.dev/docs/agents/subagents
- Vercel Knowledge Base: What is WorkflowAgent?
https://vercel.com/kb/guide/what-is-workflowagent
- Vercel Knowledge Base: How to build AI Agents with Vercel and the AI SDK
https://vercel.com/kb/guide/how-to-build-ai-agents-with-vercel-and-the-ai-sdk
- Vercel Knowledge Base: Human-in-the-Loop with Chat SDK and Workflow SDK
https://vercel.com/kb/guide/human-in-the-loop-with-chat-sdk-and-workflow-sdk
次に読むなら
更新履歴
- 2026年5月31日
AI SDK Docs、Vercel WorkflowAgent KB、Chat/Workflow HITL記事、OpenAI Agents SDK HITL Docsを確認して初版を作成しました。
導入時にはAI SDKのversionと利用runtimeを確認してください。
- 2026年5月31日: AI SDK Docs、Vercel WorkflowAgent記事、Vercel Chat/Workflow human-in-the-loop記事を確認し、初版を公開しました。
