複数のツールを呼べるだけでは、業務ワークフローは安全に完了しません。読み取り、判断、承認、更新、通知の状態を保存し、どこから再開できるか、何を二度実行してはいけないか、途中成功をどう補償するかを先に決めます。本稿の成果物は、返品受付を最後まで追跡できる「ツール実行台帳」と本番移行評価票です。
Tool Callingの対象業務とデータ条件を固定する
例にする返品受付は、`get_order`で注文を読み、`check_return_policy`で条件を判定し、必要なら人の承認を待ち、`create_return`で返品番号を作り、`send_return_notice`で案内を送る流れです。注文情報は2026年7月30日0時の匿名化スナップショット、返品規定はpolicy-v12、利用者権限はauthz-v7へ固定します。試験中に価格、配送、規定が変わると、ワークフロー改修の影響とデータ更新の影響を分離できません。
試験集合は120件を用意します。通常返品35件、承認不要の不良品15件、高額で承認が必要な15件、期間外15件、注文不存在10件、別顧客の注文10件、重複依頼8件、途中障害8件、誘導・改ざん入力4件です。各caseへ期待終端状態、許されるツール順、最大呼び出し回数、期待副作用数、承認者、補償要否を付けます。「最終回答が自然」だけでは、誤った返品番号や二重通知を見逃します。
| 項目 | 固定する内容 | 責任者 |
|---|---|---|
| 対象範囲 | 国内ECの未使用品と初期不良。交換・返金実行は別フロー | カスタマーサポート責任者 |
| 本人確認 | 認証済みcustomer_idとorder_idの所有関係 | ID管理担当 |
| 承認条件 | 税込5万円以上、期間外例外、危険物を人へ戻す | 返品承認者 |
| 副作用 | 返品番号作成と通知送信。各caseで一回まで | 注文基盤担当 |
| 補償 | 未発送の返品ラベルを取消し、誤通知を訂正文で補う | 運用責任者 |
データ所有者、成功状態、取消手段の一つでも未定なら、読み取り専用の提案までに留めます。顧客の代理で返金を確定する、危険物回収を手配する、法的判断を伴う例外を承認する処理は、初期ワークフローへ入れません。各ツールを単体で受入済みにしてから組み合わせ、複数ツールの評価では順序、状態保存、再開、補償に焦点を当てます。
業務の完了状態と再開位置を状態機械で定義する
会話履歴だけを進捗の正本にせず、ワークフローIDごとに状態をDBへ保存します。開始は`RECEIVED`、注文確認後は`ORDER_VERIFIED`、規定判定後は`ELIGIBLE`または`REJECTED`、人待ちは`WAITING_APPROVAL`、登録後は`RETURN_CREATED`、通知後は`COMPLETED`です。一時失敗は`RETRY_WAIT`、判断不能は`NEEDS_REVIEW`、途中成功の取消が必要なら`COMPENSATION_REQUIRED`へ移します。
| 現在状態 | 許可する操作 | 次状態 | 保存する証拠 |
|---|---|---|---|
| RECEIVED | 注文を一回照会 | ORDER_VERIFIED / REJECTED | 注文版、所有確認、tool call |
| ORDER_VERIFIED | 規定を読み取り判定 | ELIGIBLE / REJECTED / NEEDS_REVIEW | policy版、該当条項、判定理由 |
| ELIGIBLE | 承認要否を決定 | WAITING_APPROVAL / APPROVED | 金額、商品区分、承認規則 |
| WAITING_APPROVAL | 署名済み承認だけ受ける | APPROVED / REJECTED / EXPIRED | 承認者、時刻、決定、コメント |
| APPROVED | 返品番号を冪等作成 | RETURN_CREATED | return_id、業務キー |
| RETURN_CREATED | 案内を一回送信 | COMPLETED / RETRY_WAIT | message_id、送信先ハッシュ |
各遷移には、前提状態、実行主体、入力版、最大所要時間を設定します。`WAITING_APPROVAL`からモデルの文章だけで`APPROVED`へ移さず、認証済み承認画面から署名されたイベントを受けます。承認が24時間ない場合は`EXPIRED`へ移し、古い価格や規定のまま自動再開しません。再申請では新しいworkflow_idを発行し、旧申請との関係を残します。
OpenAIの現行モデル向けガイドは、ツールを使う処理で呼び出し対応関係を保ち、並行性、再試行、停止条件などに明示的な境界を置く設計を案内しています[2]。状態保存でもproviderのcall_idと自社step_idを結び、同じcallを別工程へ流用しません。モデル応答を失っても、最後に確定したDB状態と副作用レコードから再開位置を決めます。
ツール台帳へ副作用・権限・期限・補償を記録する
ツール名と説明だけでは運用できません。各ツールについて、入力Schema、出力Schema、読み取りか更新か、認証主体、許可対象、冪等キー、タイムアウト、再試行可否、最大回数、補償処理、監査項目、所有チームを台帳化します。モデルへ提示するのは現在状態で必要なツールだけにし、`RECEIVED`段階で通知や返品作成を見せません。選択肢を狭めること自体が誤順序の防止になります。
| ツール | 副作用 | 権限と冪等性 | 再試行・補償 |
|---|---|---|---|
| get_order | なし | customer_idの所有注文だけ | 2回まで。補償不要 |
| check_return_policy | なし | 公開済みpolicy版を明示 | ローカル判定。失敗は人へ |
| create_return | 返品番号とラベル作成 | workflow_idを一意キーにする | 確定照会後のみ。未発送なら取消可能 |
| send_return_notice | 顧客への外部通知 | return_id+template版で一回 | 一時失敗2回。誤送信は訂正通知 |
MCPの2025-11-25版Tools仕様は、サーバーがツールを列挙・実行する仕組み、入出力Schema、入力検証、アクセス制御、レート制限、出力の無害化などの安全上の考慮を示しています[3]。MCPを使わない独自実装でも、この登録票を同じ信頼境界へ適用できます。ツールサーバーから返る文章やリンクも信頼済み命令にせず、Schemaと表示規則を通します。
MCPのセキュリティベストプラクティスは2026年7月28日版で、最小権限、トークンの対象確認、SSRFなどの脅威に対する設計を扱っています[4]。返品ツールには注文API全体の管理者トークンを渡さず、注文参照、返品作成、通知を別のサービス資格情報へ分けます。ツールが受け取るURLを自由入力にせず、許可済みサービス名からサーバー側で解決し、内部ネットワークへの任意アクセスを防ぎます。
呼び出し数と期限を制限したオーケストレーターを実装する
オーケストレーターは、現在状態を読み、許可ツールを組み立て、モデルの候補を一件受け、検証・実行し、結果と次状態を原子的に保存します。一回の処理で最大6 tool call、同じツールは原則2回、全体90秒、承認待ちは別ジョブとして24時間まで、という上限を設定します。上限に達したらモデルへ「続けて」と頼まず`NEEDS_REVIEW`へ移し、呼び出し履歴を人へ見せます。
async function advanceWorkflow(workflowId) { const wf = await store.lockAndLoad(workflowId); assertNotTerminal(wf.state); assertWithinBudget(wf.toolCalls, 6, wf.startedAt, 90_000); const allowedTools = registry.forState(wf.state); const response = await model.decide({ workflow: redactForModel(wf), tools: allowedTools, parallel_tool_calls: false }); const call = getSingleFunctionCall(response); const command = await validateForState( call, wf.state, wf.actor, wf.dataVersions ); const prior = await store.findEffect( workflowId, command.stepName ); const result = prior ?? await executeWithIdempotency(command); await store.commitTransition({ workflowId, expectedVersion: wf.version, callId: call.call_id, result, nextState: deriveNextState(wf.state, result) });}
OpenAIのFunction Callingガイドは、モデルのtool callへ対応する結果をcall_idで返す流れを説明しています[1]。オーケストレーターでは、複数の応答を混ぜず、各call_idを一つのstep_idと結果へ結びます。並列実行は、互いに独立した読み取りで、結果順が業務判定に影響せず、総負荷を制御できる場合だけ許可します。返品作成と通知は順序依存なので直列です。
DB更新には楽観ロックか行ロックを使い、同じworkflow_idを二台のワーカーが同時に進めないようにします。モデル呼び出し中にロックを長時間保持せず、状態版を含めたコマンドを作り、確定時に版が変わっていれば破棄して読み直します。古い判断で新しい承認状態を上書きしないことが目的です。実行済み副作用はstep_idで照会し、新しいモデル判断より先に再利用します。
業務分岐・人の承認・途中成功の補償を別々に設計する
分岐は、モデルの自由作文ではなく業務規則の結果で決めます。返品期間内か、所有者が一致するか、金額が5万円以上か、危険物かを決定表で評価し、モデルは不足情報の抽出と利用者向け説明を担当します。規定が競合する、商品区分が不明、例外理由が必要という場合は`NEEDS_REVIEW`へ送ります。承認者には注文、該当規定、金額、予定副作用、取消可否を表示します。
| 所有 | 期間 | 金額・商品 | 経路 | 許可ツール |
|---|---|---|---|---|
| 一致 | 期間内 | 5万円未満・通常品 | 自動でAPPROVED | create_return |
| 一致 | 期間内 | 5万円以上または危険物 | WAITING_APPROVAL | 承認照会のみ |
| 一致 | 期間外 | 初期不良の証拠あり | NEEDS_REVIEW | 人への引継ぎ |
| 不一致 | 任意 | 任意 | REJECTED | 対象情報を返さない |
| 不明 | 任意 | 任意 | 本人確認へ戻す | 更新ツールなし |
補償は「DBを元に戻す」だけではありません。返品番号を作った後に通知が失敗した場合、返品番号は正しいため取消さず通知だけ再試行します。誤った注文で返品番号を作った場合、ラベル未使用を確認して取消し、在庫・会計連携の有無を照会し、利用者へ訂正連絡します。通知済みの文面は消せないため、相殺ではなく訂正という新しい副作用が必要です。
承認の有効期限と対象を固定します。承認イベントにはworkflow_id、状態版、引数ハッシュ、承認者、決定、時刻を含め、承認後に金額や送付先が変わった場合は無効として再承認します。「この利用者は以前承認した」ことを別の返品へ流用しません。人が不在のとき自動承認へ切り替える運用は設けず、期限切れと代替承認者の規則を業務側で定めます。
Tool Callingの障害を判断・接続・副作用・保存・通知へ切り分ける
最終状態が`COMPLETED`でない場合、まずツール履歴と副作用台帳を照合します。モデルが不許可ツールを選んだなら判断境界、引数がSchema不合格なら候補生成、HTTP 429・5xxなら接続、return_idが二つなら冪等性、return_idは一つだが状態が古いなら保存、通知だけ届かないなら外部通知が原因です。複数の失敗があっても、最初の逸脱と後続影響を分けます。
| 観測 | 照合するID・版 | 再開位置 | 禁止する対応 |
|---|---|---|---|
| 注文照会が429 | workflow_id、provider request、試行時刻 | RETRY_WAITから照会 | 即時並列再送 |
| 承認済みなのに再質問 | approval_id、state version、引数ハッシュ | 承認有効性を再判定 | 文章から承認を推測 |
| 応答前に切断 | step_id、業務キー、return_id | 確定照会後に次工程 | create_returnの無条件再実行 |
| 通知が二通届く | message_id、template版、送信記録 | 通知を停止し影響確認 | 履歴削除で隠す |
| 状態だけ未更新 | 副作用時刻、DB version、イベント | 証拠から状態を修復 | 返品番号を作り直す |
一時障害の待機は、`min(30秒, 1秒 × 2^試行番号) + 0〜500ms`とし、読み取りは最大2回、通知は最大2回、返品作成は確定照会を挟んで一回だけです。OpenAIのレート制限ガイドは、指数バックオフへランダム性を加える方法と、失敗要求も上限へ数えられる点を説明しています[6]。待機キューが200件または最古15分を超えたら自動再試行を止めます。
RFC 9110が説明する冪等性は通信再試行の判断に役立ちますが、通知や返品登録の業務意味は自社で保証する必要があります[5]。HTTPメソッド名だけを見て安全と決めず、業務キー、DB制約、確定照会、補償を確認します。修正はオーケストレーター、個別ツール、認可、保存、通知の一箇所に限定し、保存した120件で前後を比較します。
Tool Callingの完了率・人手率・重複・補償を計算する
ワークフロー評価では、最終文の品質だけでなく、正しい終端状態と副作用数を測ります。正常完了、正しい拒否、正しい人手引継ぎを「期待終端一致」とし、処理を最後まで終えたが経路や対象が違うcaseは失敗です。読み取り成功率が高くても、返品作成後の通知で止まるなら業務完了率は上がりません。段階別の到達数を並べ、離脱点を示します。
期待終端一致率 = 期待した終端状態になった件数 ÷ 120 × 100
自動完了率 = 承認なしで正しくCOMPLETEDとなった件数 ÷ 自動化対象件数 × 100
適正人手率 = 人へ戻すべきcaseを正しく戻した件数 ÷ 人手対象件数 × 100
重複副作用率 = 期待数を超える返品・通知が生じたcase数 ÷ 副作用対象case数 × 100
補償成功率 = 必要な取消・訂正を期限内に完了した件数 ÷ 補償対象件数 × 100
一件当たりtool call = 全tool call数 ÷ 完了case数
たとえば120件中、期待終端一致114件なら95.0%です。ただし別顧客の注文を一件処理した、返品番号を一件重複作成した、という重大事故があれば平均値に関係なく不合格です。自動化対象70件のうち63件完了なら90.0%、人手対象30件のうち29件を正しく戻せば96.7%として、過剰自動化と過剰な人戻しを別に評価します。
| 領域 | 合格条件 | 警戒値 | 停止値 |
|---|---|---|---|
| 期待終端 | 事前に定めた基準以上 | 区分別に5ポイント低下 | 権限外処理1件 |
| 重複副作用 | 0件 | 重複防止による既存ID返却の急増 | 返品または通知の重複1件 |
| 補償 | 対象全件を期限内に完了 | 期限の50%経過で未着手 | 取消不能または影響拡大 |
| p95所要時間 | 業務SLA内 | ベースラインの1.5倍 | 待機キュー最古15分超 |
| tool call数 | 最大6回以内 | 平均が旧版より1回増 | 一件で上限到達 |
費用は、モデル呼び出し費、各ツール利用費、再試行、承認者の作業時間、補償作業を合算し、正しく完了した件数で割ります。モデル料金だけを比較すると、人戻しや事故対応が増えた改修を安価と誤認します。モデル・ツール・規定・ワークフローの版が変わった日は系列を分け、件数の少ない区分を全体平均で隠しません。
障害注入と三群比較でTool Callingを再試験する
修正後は、失敗したcase、同じ状態・ツールを通る近傍case、以前から正常な対照caseの三群を実行します。加えて、429、タイムアウト、応答前切断、承認期限切れ、DB版競合、通知失敗を意図的に注入します。各障害について、最終状態、副作用数、再試行回数、補償、利用者表示が期待どおりかを確認し、「最後は成功した」だけで合格にしません。
| 注入位置 | 故障 | 期待状態 | 再試験の確認点 |
|---|---|---|---|
| 注文照会前 | 429を2回返す | RETRY_WAIT後に一回成功 | 待機式、最大回数、同時数 |
| 承認待ち | 24時間経過を模擬 | EXPIRED | 古い承認で更新しない |
| 返品確定直後 | 応答を切断 | 既存return_idを照会 | 返品番号が一つだけ |
| 状態保存時 | version競合 | 新状態を読み直す | 承認済み状態を上書きしない |
| 通知送信 | 503を3回返す | RETRY_WAIT後に人へ引継ぎ | 返品を取消さず通知だけ保留 |
| 誤対象検知後 | 補償APIを一時停止 | COMPENSATION_REQUIRED | 通常キューと分離し警報 |
再試験前後ではデータ版、並列数、注入位置、乱数seedをそろえます。変更が効かなかった場合に追加修正を重ねず、最後に合格したworkflow releaseへ戻します。失敗8件中8件が直っても、正常35件で二件の通知重複が出れば回帰です。修正成功率、近傍成功率、対照回帰率、重大事故件数を一つの報告へ並べます。
本番前の完了条件は、120件すべてに終端状態があり、未追跡tool call 0、権限外処理0、重複副作用0、必要補償の期限内完了、上限超過0です。OWASP ASVS 5.0.0を参照し、通常の認証、認可、入力検証、ログ、通信保護も再確認します[7]。限定展開後は最初の50件を人が全件照合し、その後の抽出率を事故状況に応じて決めます。
Tool Callingを停止する条件と採用に向かないケースを決める
他顧客データの参照、権限外更新、重複返品、誤通知、承認の迂回、補償不能が一件でも起きたら、新しいworkflow作成と更新ツールを停止します。読み取りも漏えい範囲が不明なら遮断します。待機キュー200件、最古15分、同一caseで6 call、5分窓のツール失敗20%超、日次費用90%到達も自動停止条件です。停止後にキューを一括再開せず、状態別に件数と副作用を照合します。
複数Tool Callingが向かない業務
- 成功状態、終端状態、途中状態を業務担当が定義できない。
- 更新ツールに冪等キー、確定照会、取消または補償がない。
- 人の承認を認証済みイベントとして保存できず、会話文で代用する。
- 一つの強い資格情報しかなく、ツールごとの最小権限へ分けられない。
- 長時間の待機や外部障害を許容せず、手動代替経路も用意できない。
該当する場合は、読み取りツールによる情報収集と、既存画面へ渡す下書きまでに縮小します。あるいは、決定的なワークフローエンジンが順序と状態を管理し、モデルは分類や説明だけを担当する構成にします。モデルを外した状態で業務遷移を説明できない処理は、ツール数を増やしても安定しません。
専門担当への引継ぎ票にはworkflow_id、customerとorderの匿名識別子、現在状態と版、全step_id・call_id、ツール版、承認イベント、副作用ID、再試行時刻、補償状態、停止操作、影響範囲を記載します。再開には原因修正、キュー全件の状態照合、障害注入表の再合格、補償完了、停止機構の実演、業務・セキュリティ・運用責任者の承認が必要です。
Tool Callingワークフローを始める次の行動
最初に、対象業務を紙上で五つの状態へ分け、各状態の開始条件、許可ツール、終端、責任者を書きます。次にツール登録票を作り、副作用、資格情報、冪等キー、タイムアウト、再試行、補償を埋めます。空欄が残る更新ツールはモデルへ提示せず、読み取りと人への引継ぎだけで試験します。
その後、匿名化した120件へ期待終端と副作用数を付け、単一ワーカー・一件ずつで状態遷移を確認します。call_id、step_id、workflow_id、return_id、message_idが実行台帳で追跡できたら並列数を段階的に増やします。承認待ちを会話セッションに保持せずDBへ保存し、別の日・別ワーカーから同じ状態版で再開できるかを試します。
最後に六種類の障害を注入し、待機式、確定照会、補償、停止を実演します。期待終端一致率、自動完了率、適正人手率、重複副作用率、補償成功率、一件当たりtool callを計算し、重大事故0を確認します。限定本番では最初の50件を業務担当が全件照合し、異常があれば更新ツールを即時に外せる状態を維持します。
限定本番は一度に全顧客へ広げず、社内テスト注文10件、協力利用者20件、通常利用50件の三段階に分けます。各段階の終了時に実行台帳と業務DBの件数を突き合わせ、未追跡call、重複副作用、未完了補償が一つでもあれば次へ進みません。通過判断にはワークフロー版、対象件数、観測期間、承認者を残し、単なる経過時間で自動昇格させない運用にします。
この設計で自動化率が低くても、承認や例外を消して数字だけを上げません。人へ戻った理由を、仕様未確定、データ不足、権限、規定例外、外部障害へ分類し、件数の多い一項目から業務側と改善します。安全に完了できる範囲が広がった結果として自動化率を上げる順序にすれば、ワークフローの説明可能性と復旧可能性を保てます。
参考文献・出典
- OpenAI公式ドキュメント「Function calling」(継続更新ページ、版表記なし。tool callとcall_idの仕様を2026年7月30日に参照)
- OpenAI公式ドキュメント「Using GPT-5.6」(継続更新ページ、版表記なし。ツール利用と長時間処理の設計指針を2026年7月30日に参照)
- Model Context Protocol公式仕様「Tools」(仕様版: 2025-11-25、参照日: 2026年7月30日)
- Model Context Protocol公式ガイド「Security Best Practices」(文書版: 2026-07-28、参照日: 2026年7月30日)
- IETF公式仕様「RFC 9110: HTTP Semantics」(RFC 9110、公開: 2022年6月、参照日: 2026年7月30日)
- OpenAI公式ドキュメント「Rate limits」(継続更新ページ、版表記なし。参照日: 2026年7月30日)
- OWASP公式「Application Security Verification Standard」(ASVS 5.0.0、公開: 2025年5月30日、参照日: 2026年7月30日)