マルチエージェントの仕組みを作っていると、エージェントの指示や役割だけでなく、それを動かす側の仕組みも少しずつ増えていきます。
私の環境でも、入力資料の量を調べる処理や、成果物を保存する処理を共通化しています。その中で、エージェントが必要な報告を書こうとしたところ、共通の制御に止められる問題が起きました。
今回は、この問題を調べて、モデルへの送信とファイルへの書込みを扱う部分を直した話です。サンドボックスを無効にするのではなく、処理に必要な範囲を具体的に扱えるようにしました。
どんな仕組みで起きたのか
自作の仕組みでは、情報収集やPodcast作成などを、複数の工程に分けて実行しています。
この記事では、一つの作業を Job、Jobを起動して結果を受け取る共通の実行役を Runner と呼びます。
エージェントには、成果物のほかに「渡された必須入力をどう扱ったか」という利用報告も返してもらいます。Runnerはその報告や成果物を確認し、正式な保存処理につなげます。
また、モデルに渡す入力が大きくなりすぎないように、送信前に入力予算を調べています。ここでいう予算は金額ではなく、モデルへ渡す入力のトークン量と、出力用に確保しておく余裕です。
このように、エージェントの前後には、業務そのものとは別の制御があります。今回の不具合は、そこにありました。
利用報告を書こうとするとモデル変更として止まった
2026年10月9日の実行で、Web資料を準備する工程が利用報告を書こうとした際に停止しました。記録されていたエラーは、model_changed_budget_required です。
調べると、業務を担当するモデルとは別に、自動承認の審査を行うモデルへの通信が発生していました。共通の入力制御が、この通信を「モデルが変わった」として拒否していました。
つまり、外からは一つのJobとして呼び出していても、その内部では別の役割のモデルへ通信する場合がありました。ところが共通制御は、業務側で想定したモデルと異なる通信をうまく扱えていませんでした。
エージェントへの指示を言い換えても、この前提のずれは残ります。直す必要があったのは、モデルが報告の意味を理解できるかどうかではなく、共通の実行処理でした。
修正1 送信ごとに実際のモデルで入力予算を調べる
修正後は、モデルの識別子が変わったことだけを理由に止めるのをやめました。その代わり、実際に送信するたびに、その送信先のモデルに対する入力予算を調べるようにしました。
例えば、次のような通信がある場合です。
- 業務モデルへ作業を依頼する
- 内部の審査モデルへ確認を依頼する
- 業務モデルへ戻って作業を続ける

図1:内部の審査モデルへの通信も、実際の送信ごとに測定・判定
最初の業務モデルだけ調べて終わりにはせず、それぞれの送信を検査します。追加で読み込んだ資料や、複数ターンにわたる通信も、検査対象から外しません。
ここで、別モデルなら何でも送ってよいという扱いにはしていません。共通の上限とモデル別の上限がある場合は、小さい方を使います。明示された出力用の予約量は、大きい方を採用します。呼出し中にモデルごとの上限が変わらないようにもしています。
入力の測定はトークン数の推定を含むため、モデルの内部処理を厳密に再現した数字ではありません。その前提で余裕を取り、予算を超えた送信は引き続き止めます。
修正したのは「モデル名が同じでなければ止める」という判定です。入力量の検査や、送信の記録まで外したわけではありません。
修正2 報告を書く場所をJob専用の領域に限定する
もう一つ整理したのが、利用報告を書き出す権限です。
報告先のパスを指示文に書いても、それだけでは実際に書き込めるとは限りません。特に、作業ディレクトリとは別の一時領域に報告先を置く場合、実行時の権限と食い違う可能性があります。
そこで、Runnerが、そのJob専用の一時領域を実行側へ渡すようにしました。ここではこの領域を private staging と呼んでいます。エージェントが報告を置く場所と、その後に正式保存する場所を分けています。
追加で書込みを許可する範囲は、このJobのstaging内だけです。次の場所は、追加許可の対象にしません。
- 入力資料や設定ファイルのディレクトリ
- 別のJobの領域や、似た名前の兄弟ディレクトリ
- Runの管理記録
- 正式な成果物の保存先
- Tempディレクトリ全体
報告先が専用stagingの内側にあることを確かめ、絶対パスの追加領域だけをCLIへ渡します。相対パスで曖昧に指定することも避けました。
また、通常の成果物を生成しないJobでも、利用報告だけは必要な場合があります。そのようなJobにも、報告を書ける専用領域を渡すようにしています。
正式保存は、これまでどおりRunnerと保存担当の仕組みが行います。エージェントに正式保存先まで広く書かせる構成には変えていません。

図2:追加の書込み許可はJob専用stagingに限定し、正式保存はRunner側で実行
なお、これは追加許可の範囲を限定する変更です。もともとの作業ディレクトリに対する権限を、すべて読み取り専用へ変更したという意味ではありません。従来の workspace-write、通信制限、自動承認の審査も維持しています。
共通部分を直したので複数の経路を確認した
今回直した部分は、一つのWorkflowだけの専用処理ではありません。固定Workflowや動的な実行が使う共通のラッパーです。
そのため、失敗した工程が一度通っただけで終わりにせず、確認内容を分けました。
モデルが切り替わる通信の検査
共通ラッパーをローカルHTTPプロキシへ接続し、「業務モデル→審査モデル→業務モデル」の3通信を通しました。
モデルごとの明示予算を追加しなくても各送信を測定できることと、予算超過などを引き続き拒否することを確認しています。これは、送信境界を調べる結合テストです。
実モデルによる報告の書込み
別の独立した試験では、本番と同じ作業ディレクトリと、一時領域側のstagingという配置で実モデルを動かしました。
必須入力2件の利用報告が書かれ、Runnerが2種類の成果物を生成し、Schema検証を通るところまで確認しました。
ただし、この成功例で観測されたモデル通信は、業務モデルのみです。審査モデルへの切替まで実モデルで通った証拠としては扱っていません。モデルが切り替わる経路は、先ほどの結合テストで別に確認しています。
他のWorkflowへの影響
新規実行可能な16の固定Workflowについて、隔離したテスト用の正常経路が完了することも確認しました。
ただし、取得・GPU・音声・公開先などにモックを使っています。「16種類すべてを本番で動かし、実公開まで成功した」という結果ではありません。共通部分の変更で、ほかの経路を壊していないかを調べるための試験です。
実モデルの試験も、短い記事2件と取得モックを使っています。本番記事全量の再試行、音声合成、実公開は今回の検証には含めていません。
エージェントの外側にある前提も調べる
今回の問題では、エージェントに「利用報告を返す」という仕事を与えていました。一方で、それを実行する側には、内部の審査モデルへの通信を扱えないことと、必要な報告先の書込み権限を渡せない経路がありました。
役割を決めるだけでは、その役割を実行できる状態にはなりません。何を入力として受け取り、どこへ書き、途中でどのモデルへ通信するのかまで、実行側の前提をそろえる必要がありました。
同じようにエージェントが途中で止まるときは、私は次の点を分けて見るのがよいと思っています。
- モデルへ依頼した内容に問題があるのか
- 周辺のラッパーが正しい処理を拒否していないか
- 必要な出力先に、実際の書込み権限があるか
- 成功した試験は、どの経路まで確認できているのか
権限を広く渡せば、その場の書込みだけは通るかもしれません。ただ、それでは入力資料や正式成果物まで書き換えられる範囲を広げてしまいます。
今回は、追加で必要な場所をJob専用stagingに絞り、モデル通信は送信ごとに検査する形に直しました。こうした実行境界の前提を見直すことも、マルチエージェントの仕組みを使い続けるための改善でした。