大規模言語モデル(LLM)を、メールによる人間の承認を必要とするワークフローに組み込む際、モデル自体が原因で問題が起こることは稀です。問題は、コードが終わり、受信トレイが始まる境界で発生します。ある自律的な実行(run)がリクエストを送信し、その最初の実行が完了する前に次の実行が始まってしまう。共有受信トレイには、異なるプロセスからのスレッドが蓄積される。12時間遅れて届いたメッセージに対して、誰かが「承認」をクリックする。これで、出力があり、決定もあります。しかし、どの実行が何を生み出したのか、あるいはその承認がそもそもこの生成に対するものだったのかを証明することができません。私はこれまで多くの社内自動化パイプラインの整理を行ってきたため、このパターンを熟知しています。これは、多くのチームが予想するよりもはるかに速く、混乱からインシデントへと発展します。
The Operational Boundary
オーケストレーターとメールプロバイダーの境界は、単なるネットワークのホップではありません。それは「状態(state)の境界」なのです。LLMがドラフトの生成を終えても、その実行(run)はまだ生きています。待機しているのです。もしシステムが送信を「送りっぱなし(fire-and-forget)」のイベントとして扱うなら、その時点で文脈(thread)を見失っています。
リトライポリシーが強引すぎて、単一の実行が2つの別々の承認リクエストを生成してしまうパイプラインを見てきました。また、先週のメッセージが残っているメールボックスを、別の実行が再利用してしまうケースも見てきました。人間の承認者は、run IDは見えません。見えるのは件名とボタンだけです。構造がなければ、彼らはマーケティングのニュースレターや監視アラートが混在する受信トレイの中で、推測に基づいて判断することになります。
The Neglected Step
チームはプロンプトの調整、ガードレールの追加、出力のベンチマークに何週間も費やします。そして、承認ステップをSlackチャンネルや共有サポート用受信トレイに接続して、「完了」とみなします。これにより、予測可能な3つの弊害が生じます。
- 共有受信トレイが、複数の実行からのイベントのゴミ捨て場になります。文脈が崩壊(Context collapse)します。スレッドを開いてタイムスタンプを手動で解析しない限り、どのメッセージがどのビジネス・トランザクションに属していたのかを再構成することはできません。
- リトライが証拠を上書きします。実行が承認リクエストを再送すると、元のメッセージは埋もれたり、削除されたり、あるいは過剰に反応するメールクライアントによって重複としてマークされたりする可能性があります。監査証跡(audit trail)が断片化します。
- 人間の決定がシステムの外に漂います。チケットやダイレクトメッセージで誰かが「良さそうですね」と返信しても、その意思表示がワークフロー内の構造化データになることはありません。エージェントには、誰が何を、いつ言ったのかを検証する方法がありません。
何か問題が発生して調査が必要になったとき、得られるのは伝聞だけです。「あれは正しいメールだったと思います」といったものです。記憶はトレーサビリティ(追跡可能性)ではありません。監査ログは勘を消化することはできないのです。
From Delivery Detail to Checkpoint
これを解決するには、設計思想の転換が必要です。メールを単なる「配信の詳細(delivery detail)」と考えるのをやめ、「システムのチェックポイント」として扱い始めてください。つまり、すべてのメッセージは「状態遷移(state transition)」であり、すべての状態遷移には「アイデンティティ」「認可」「証拠」が必要であるということです。
この考え方を採用すると、問いが変わります。「メールが正常に送信されたか」を問うのをやめ、「どの実行がそれを送信したのか」「どのような証拠が残されたのか」「どのルールがワークフローの継続を認可したのか」を問い始めるのです。エージェントがメールの本文を書くことは全く問題ありません。しかし、プラットフォームはアイデンティティと検証パスを強制しなければなりません。LLMは「執筆者」であり、インフラストラクチャは「公証人」なのです。
A Minimum Design
これを構築するのに巨額の費用は必要ありません。私の提案する最小限の構成(minimum viable version)は、5つの意図的な要素で成り立っています。
- オーケストレーターは、ワークフローが開始された瞬間に run_id を発行(mint)します。この識別子は、その後のあらゆるアクションの背骨となります。これは決して変わらず、再利用もされません。
- すべてのメールアクションには、3つのフィールドが含まれます。run_id、approval_request や evidence_notification といった message_type ラベル、そしてどのガバナンスルールが有効であるかを特定する policy_version 文字列です。これにより、単なるメッセージが型付けされたイベント(typed event)へと変わります。
- 証拠は、実行ごとに隔離された受信トレイに保持されます。これは必ずしも実行ごとに個別のメールアカウントを用意することを意味しません。専用のラベル、サブフォルダ、あるいはスレッドを分割してある実行のやり取りが別の実行と混ざらないようにするルーティングルールを指すこともあります。
- 承認のレスポンスは、自由形式の「ok」ではなく、構造化されたイベントである必要があります。人間は従来通りクリックや返信を行いますが、システムはそのアクションを、run_id、決定内容、タイムスタンプを明記した、マシン読み取り可能なペイロードへと変換します。
- 証拠と決定が一致する場合にのみ、フローは継続します。ワークフローは承認を単独で信頼することはありません。LLMの出力を本番環境に到達させる前に、承認ペイロードを元のリクエストと照合して検証します。
What a Useful Checkpoint Validates
有用なチェックポイントは、人間の判断を受け入れる前に4つの条件を強制します。
- 受信者は実行コンテキストに属している必要があります。承認者がこの特定のワークフローインスタンスに対して割り当てられたレビュアーでない場合、システムはそのシグナルを拒否します。
- 対象またはルーティングメタデータが現在のフローの状態と一致している必要があります。ステップ3に対する承認がステップ2をバイパスすることはできません。
- タイムスタンプが想定される時間枠内である必要があります。タイムアウト後に届いた決定は、自動的に通過させるのではなく、新たなレビューをトリガーすべきです。
- エビデンスが他の実行によって再利用されていない必要があります。同じメッセージIDまたはトークンが2つの異なる承認リクエストに現れた場合、それは衝突であり、システムは停止しなければなりません。
真のコスト
このパターンには代償が伴います。より多くのメタデータを保存することになります。誰かが維持管理しなければならないポリシーレイヤーが追加されます。チームに対して、人間の判断を何気ないコメントとしてではなく、構造化データとして記録することを強いることになります。一見すると官僚主義のように見えるかもしれません。しかし実際には、これは非常に優れたトレードオフです。
スピードを明快さと引き換えているのです。
