はじめに:LLMエージェントの「曖昧さ」がシステム信頼性を損なう理由

LLMエージェントの設計において、多くのチームがプロンプトの出力フォーマット指定やFew-shot例示の最適化に注力している。しかし、これらは「出力の外観」を整える手法であり、対話の意味的性質——すなわち、エージェントの発言が他者の行動に対してどのような責任を伴うか——を無視している。

言語行為におけるコミットメントの概念は、Floresらによる1980年代の研究で示唆されている。言語のうち物理的行動を実際に調整するものは全体の5%に過ぎず、残りの95%はコミットされていないノイズであるという見方がある(FloresのCommitted Speech理論に基づく解説記事)。エージェント設計において重要なのは、この「コミットメント」を厳密に制御することである。残りのノイズ的な出力は、設計上無視してよい場合が多い。

問題の本質は、LLMが出力するテキストの「種類」を区別せずに処理している点にある。ユーザーからの「この機能を追加して」という要求(Request)、エージェントの「実装します」という返答(Promise)、テスト結果の報告(Assertion)、そしてプロジェクトの完了宣言(Declaration)は、すべて同じ「テキスト出力」として扱われている。しかし、これらはシステムに対して全く異なる義務を課す。

本記事では、Belnapの4値論理とコミットメント・スピーチ理論を組み合わせ、LLMの出力をRequest・Promise・Assertion・Declarationに分類する設計フレームワークを提示する。このアプローチにより、ハルシネーションによる根拠のない約束や、矛盾した状態遷移を防ぐための堅牢なワークフロー基盤を構築する方法を解説する。

理論的基盤:コミットメント・スピーチとBelnapの4値論理の融合

Floresのコミットメント分類とCSPによる形式化

Flores(1986)は、コミットメント・スピーチを「他者の行動に対する責任の受容」と定義し、これを5つの基本型に分類した。エージェント設計では、この分類をステートマシンの遷移条件として直接利用する。つまり、エージェントがどの型のコミットメントを発行できるかは、現在の内部状態によって制約される。

この通信プロトコルを形式化するために、Hoare(1978)のCSP(Communicating Sequential Processes)のチャネル接頭辞を用いる。CSPでは、?がCustomerからの入力(要求の受信)、!がPerformerからの出力(実行結果の返送)を意味する(CSP接頭辞を用いたエージェント通信の解説)。Customer(要求側)とPerformer(実行側)間のすべてのやり取りが、この2つの操作の組み合わせとして記述可能になる。

Belnapの4値論理:不確実性の2ビット表現

Belnap(1977)の4値論理は、不完全かつ矛盾したデータベース上の推論のために設計された(Belnap 4値論理の解説記事)。通常のブール論理が「真か偽か」の2値で事象を記述するのに対し、Belnapの論理は2ビットの証拠ペア(True/False, Known/Unknown)を用いる。

これにより、事象は以下の4状態のいずれかに分類される:

  • 既知の真(True ∧ Known):証拠が確認済みで肯定される
  • 既知の偽(False ∧ Known):証拠が確認済みで否定される
  • 未知(Unknown):証拠がまだ存在しない
  • 矛盾(True ∧ False):相互に矛盾する証拠が同時に存在する

LLMの不確実性処理にこの4値論理が適合する理由は明確である。LLMの出力は、訓練データに基づいた確率的生成であり、出力が「真」であることの保証はない。また、異なるコンテキストから矛盾する情報を受け取る可能性もある。2値のブール論理では、この「未知」や「矛盾」の状態を表現できないため、エージェントが根拠なく判断を下してしまう。4値論理を導入することで、「証拠がない状態では判断しない」という制約を論理的に強制できる。

両理論の融合:コミットメントの発行条件を状態に紐づける

両理論を組み合わせると、コミットメントの発行条件が内部状態の関数として定義される。具体的には、Belnapの論理値がエージェントの内部状態を決定し、その状態がどの型のコミットメント(Request, Promise, Assertion, Declaration)を発行可能にするかを制御する。この「状態→許可されるコミットメント型」のマッピングが、後述する状態遷移モデルの核心となる。

エージェントの状態遷移モデル:4値論理に基づく5つの状態

エージェントの内部状態を、Belnapの4値論理とコミットメントの種類に対応させて5つ定義する:UNKNOWN, ESTABLISHED, CONTRACTED, RETRACTED, DECLARED。各状態での許可されるコミットメント型は異なる。

UNKNOWN:証拠未確認の初期状態

UNKNOWN状態では、物理的プローブが未検査であり、証拠が存在しない。Belnapの論理で言えば、Knownビットが0の状態である。この状態ではPromiseを発することが禁止される(UNKNOWN状態でのPromise禁止の解説)。これは「根拠のない約束」を防ぐための必須制約であり、LLMがデフォルトで「はい、実装します」と応答してしまう挙動を構造的に抑制する。

UNKNOWN状態で許可されるのは、Request(追加情報の要求)のみである。エージェントは「この処理の前提条件は何ですか?」と問い返すことで、証憑の収集を開始する。

ESTABLISHED:証憑で検証済みの状態

ESTABLISHED状態は、file:lineやgit logなどの証憑で検証済みの状態である(ESTABLISHED状態の定義)。Belnapの論理で言えば、True ∧ Knownの状態に対応する。ここで初めてPromiseまたはAssertionを発行できる権限が与えられる。

設計上の判断基準として、ESTABLISHEDへの遷移条件は「証憑が独立して検証可能であること」である。具体的には、エージェント自身ではなく外部の検証プロセス(テスト実行、静的解析、差分比較)が証憑を確認した場合にのみ、この状態への遷移を許可する。エージェント自身が「検証した」と主張するだけでは不十分である。

CONTRACTEDとRETRACTED:矛盾と偽証の処理

CONTRACTED状態は、チケットの主張とコードベースの現実が矛盾している状態である(CONTRACTED状態の定義)。Belnapの論理で言えば、True ∧ False(矛盾)の状態に対応する。この状態では、単にエラーを返すのではなく、Counter-Offer(反提案)を発行し、再交渉プロセスを開始する必要がある。

RETRACTED状態は、テスト実行中に前提が偽証されたことを示す(RETRACTED状態の定義)。ESTABLISHED状態で発行したPromiseを取り消し、新しい条件で再交渉を行う。この状態への遷移は、一度承諾したコミットメントを撤回する唯一の正当な経路である。

DECLARED状態は、Customerが宣言(Declaration)を発行した終端状態である。Performerはこの状態を単に受信し、記録するのみである。

stateDiagram-v2
    [*] --> UNKNOWN
    UNKNOWN --> ESTABLISHED : 証憑検証成功
    UNKNOWN --> CONTRACTED : 矛盾検出
    ESTABLISHED --> RETRACTED : 前提偽証
    CONTRACTED --> UNKNOWN : 再交渉開始
    RETRACTED --> UNKNOWN : 条件再設定
    ESTABLISHED --> DECLARED : Customer宣言

この状態遷移モデルの設計意図は、コミットメントの「発行権限」を状態に紐付けることで、エージェントが不適切なタイミングで不適切な型のコミットメントを発行することを構造的に不可能にすることにある。プロンプトレベルで「約束しないように」と指示するのではなく、状態機械の遷移ルール自体が制約として機能する。

役割の厳密化:CustomerとPerformerの境界と宣言権限

Floresの理論において、Declaration(宣言)はCustomerのみが発行できる権限のある行為であり、Performerはこれを発することができない。この制約は単なる役割分担の記述ではなく、エージェント設計において「誰が状況の定義権限を持つのか」を構造的に決めるものである(Committed Speech Agent Workflow)。

宣言権限の分離がもたらす設計上の効果

LLMエージェントの設計にこの境界を適用すると、「実行ロジック」と「要件定義」が明確に分離される。Performer役のモデルは、コードの生成やテストの実行といった物理的行動を行うが、「このタスクは完了した」「この要件は満たされた」といった状況の定義(宣言)を行う権限を持たない。これは、モデルの出力が常に「証拠の提示」に留まることを意味する。

具体的には、Performerが「このコードは完了しました」と出力するのは、宣言権限の侵害にあたる。正しい振る舞いは、「テストケースA・B・Cをパスしたという証拠(Assertion)を提供する」ことである。完了の判定(Declaration)は、Customer側——すなわち人間の開発者や上位オーケストレーター——が証拠を評価した後に発行する。

この分離がもたらす実務上の効果は2点ある。第一に、要件変更時の影響範囲が限定される。Performerの出力が常に証拠に留まるため、要件が変更されても「完了」と宣言された状態を遡って取り消す必要がなく、Customer側が宣言を再発行するだけで済む。第二に、複数エージェント間の干渉を防げる。Performer同士が互いの宣言を前提として動作する構造を避けることで、エージェント間の依存関係が単方向(Customer→Performer)に固定される。

sequenceDiagram
    participant C as Customer(要件定義側)
    participant P as Performer(実行側)
    C->>P: Request(タスクの要求)
    P->>C: Assertion(テストA・B・Cパスの証拠)
    C->>C: 証拠の評価
    C->>P: Counter-Offer(要件の修正提案)
    P->>C: Assertion(修正後の証拠)
    C->>C: Declaration(完了の宣言)
    Note over P: PerformerはDeclarationを発しない

このシーケンスで重要なのは、Performerの応答が常にAssertion(証拠の提示)またはCounter-Offer(制約に基づく反提案)に限定されている点である。PerformerがDeclarationを発した瞬間、CustomerとPerformerの境界が崩れ、エージェント間の責任の所在が不明確になる。実装上は、Performer役のモデルに宣言的な出力(「完了」「達成」などの断定表現)をさせるプロンプトを設計せず、代わりに「証拠のリストを出力せよ」という指示を固定する。

設計実践:SKILL.mdとMakefile DAGによるオーケストレーション

エージェントの振る舞いを定義するドキュメントとして「SKILL.md」を用いる場合、そのルートにはDepth-0 OrchestratorのMakefile DAGが含まれるべきである。このDAGはエージェントの初期化順序とタスク間の依存関係を単一の真理源として表現する(Randal's Public Workflow Gist)。

サイズ制約の設計意図

このMakefile DAGは42KB以下・750行以内で構成されるべきである。この制約は恣意的なものではなく、以下の理由から導かれる。第一に、LLMのコンテキストウィンドウ内にDAG全体を収容し、モデルが依存関係の全体像を一度の推論で把握できるようにする必要がある。第二に、人間がレビューとデバッグを行える規模に抑えることで、DAG自体が「契約書」として機能するための可読性を確保する。750行を超えると、依存関係の追跡が困難になり、コミットメントの連鎖が断片化する。

Makefile DAGを用いることの技術的な利点は、コミットメントの伝播を構造的に可視化できる点にある。Makefileのターゲット依存関係は、どのステップでPromiseが成立し、どのステップでCONTRACTED状態に遷移したかを追跡するための自然な構造を提供する。各ターゲットのprerequisiteが「前提条件の証拠」に対応し、ターゲット本体が「コミットメントの発行」に対応する。

flowchart TD
    A["Depth-0 Orchestrator"] --> B["要件解析(Request)"]
    A --> C["環境検証(Evidence収集)"]
    B --> D["実装タスク(Promise発行)"]
    C --> D
    D --> E["テスト実行(Assertion生成)"]
    E --> F{"矛盾検出?"}
    F -->|"なし"| G["完了(Customer判定待ち)"]
    F -->|"あり"| H["Counter-Offer発行"]
    H --> B

この構造により、プロンプト工程書は「スクリプト」ではなく「契約書」として機能する。スクリプトは実行順序を記述するだけだが、契約書は「誰が何を約束し、その前提条件が何であり、前提が崩れた場合の再交渉手順が何か」を定義する。Makefile DAGのprerequisite構造が後者の表現に自然に適合する。

実装パターン:セッション分離と状態管理の実装

エージェントの内部状態を永続化するためのディレクトリ構造として、<brain>/state/はセッションごとに分離され、<appDataDir>または~/.cache/agent-scarsに配置される(Randal's Public Workflow Gist)。この分離は、異なるコミットメントの文脈が混線することを防ぐための必須要件である。

状態ファイルの設計

各セッションの状態ファイルには、Belnapの4値論理に基づく証拠ペア(True/False, Known/Unknown)を保存する。JSON形式での例を示す。

{
  "session_id": "feat-auth-042",
  "commitments": [
    {
      "id": "c-001",
      "type": "promise",
      "target": "implement_token_refresh",
      "evidence": {
        "truth": true,
        "known": true
      },
      "status": "ESTABLISHED",
      "proof": ["src/auth/refresh.ts:42", "git:abc1234"]
    },
    {
      "id": "c-002",
      "type": "assertion",
      "target": "test_coverage_above_80",
      "evidence": {
        "truth": null,
        "known": false
      },
      "status": "UNKNOWN",
      "proof": []
    }
  ]
}

ここで重要なのは、evidenceフィールドが2ビットのペアとして設計されている点である。truth: true, known: trueは「既知の真」(ESTABLISHED)、truth: null, known: falseは「未知」(UNKNOWN)に対応する。truth: true, known: false(矛盾)の状態も表現可能であり、これがCONTRACTED状態の検出に使用される。

起動時の状態復元ロジック

エージェントが次回起動時にこの状態を読み込み、各コミットメントのステータスを判定する。ESTABLISHEDであれば前回のPromiseを引き継ぎ、その前提条件が依然として有効か再検証を行う。UNKNOWNであれば、新規にRequestを受け付けるか、前回のRequestを再送するかの判断を行う。RETRACTEDであれば、取り消されたPromiseの範囲を特定し、新しい条件での再交渉を開始する。

この設計により、コンテキストウィンドウの制限を超えた長期記憶とコミットメントの永続化を実現する。LLMの推論は毎回コンテキストウィンドウ内に収まる必要があるが、コミットメントの「履歴」はウィンドウ外に永続化されることで、セッションをまたいだ一貫性を保つ。特に、複数日かけて進行するタスクにおいて、前セッションでESTABLISHEDになったPromiseを後セッションでUNKNOWNとして扱うことがないよう、状態ファイルの整合性チェック(証憑のfile:lineが依然として存在するか、git commitがrevertされていないか)を起動時に実行することが望ましい。

トレードオフ分析:理論的厳密性と開発速度のバランス

コミットメント理論に基づく設計を適用した場合、まず直面するのは初期実装コストの上昇である。状態遷移ロジック(UNKNOWN → ESTABLISHED → CONTRACTED / RETRACTEDの判定)と、各状態での許可されるコミットメントの種類を検証するレイヤーをエージェントの外側に追加する必要がある。プロトタイピング段階では、この検証層が「なぜ動かないのか」のデバッグ時間を増大させる。特に、ESTABLISHED状態への遷移条件を厳格に設定した場合、証憑の収集と整合性チェックがボトルネックになりやすい。

しかし、このオーバーヘッドがリターンに転じるのは、複雑な業務ロジックや複数エージェント間の協調が求められる場面である。単体のLLMが「テストが通ったので完了」と宣言してしまう問題を、状態遷移ルールによって構造的に封じられる。エラーハンドリングが「try-catchで握りつぶす」のではなく、CONTRACTED状態への遷移として明示的に処理されるため、障害の検知と対応が追跡可能になる。

一方で、単純なチャットボットや単発の情報検索タスクにはこの設計は過剰である。意思決定を伴わない出力生成では、UNKNOWN/ESTABLISHEDの区別自体が意味を持たない。適用範囲は「複数のコミットメントが連鎖し、状態がセッションをまたいで永続化するワークフロー」に限定するのが合理的な判断基準となる。

以下の表に、従来のプロンプト設計と本アプローチの設計思想の差異をまとめる。

観点従来のプロンプト設計コミットメント理論ベース設計
不確実性処理出力フォーマットで曖昧さを吸収Belnap 4値で状態を明示し、UNKNOWNでは行動禁止
エラーハンドリング例外を捕捉・リトライCONTRACTED状態への遷移として構造化
複数エージェント連携プロンプトで役割を説明Customer/Performerの宣言権限を構造的に分離
実装コスト低(プロンプト記述のみ)高(状態管理・検証層・DAG追加)

実務上の判断基準として、以下の条件が2つ以上当てはまる場合に本アプローチの適用を検討するのが妥当である。第一に、エージェントの出力が後続の処理(デプロイ、通知、データ書き込み)に直接影響を与えること。第二に、単一セッション内で完結せず、複数セッションにわたって状態が持続すること。第三に、複数エージェントが同一リソースに対して並行してコミットメントを発行し得ること。これらが満たされない場合、従来のプロンプト設計で十分であり、検証層の維持コストだけが負担になる。

落とし穴:UNKNOWN状態でのPromise発行と検証不足

この設計フレームワークを実装した際に最も頻発する失敗は、UNKNOWN状態でPromiseが漏れ出ることである。Belnapの4値論理では、証拠ペアが(Unknown, Unknown)の状態では物理的プローブが未検査であり、Promiseを発することが禁止されている。しかし、LLMのデフォルト動作は「与えられた文脈から最善の推論を生成する」であり、証拠がなくても自信を持って結論を出力する傾向がある。これはモデルの欠陥というより、設計上の制約が明示されていないことによる結果である。

対策として、システムプロンプトに以下のような厳格な制約を記述する必要がある。「証憑(file:line、git log、テスト実行結果)が存在しない場合、AssertionもPromiseも発行しない。Requestとして再問い合わせを行うこと。」ただし、プロンプトレベルの制約はモデルのコンプライアンスに依存するため、信頼性が高いとは限らない。より堅牢なアプローチは、エージェントの出力をパースする外部の検証レイヤーで、Promiseの発行時に証憑の存在を機械的にチェックし、証憑がないPromiseを拒否・再クエリに変換することである。

もう一つの落とし穴は、ESTABLISHED状態への遷移条件を甘く設定することである。例えば、ユニットテストがパスしたことをESTABLISHEDの証憑とみなした場合、「テストは通っているが要件を満たしていない」という偽の証憑に基づいてPromiseが成立してしまう。テストのパスは「実装が意図通りに動作している」ことの証拠ではあるが、「要件が正しく理解されている」ことの証拠ではない。証憑の検証ロジックは、エージェントの外で独立して実行することが望ましい。具体的には、チケットの受け入れ基準(Acceptance Criteria)とコードベースの差分を突き合わせ、両者が整合していることを確認したうえでESTABLISHEDと判定するプロセスを、エージェントとは別プロセスで走らせる設計が有効である。

さらに、RETRACTED状態への遷移を検知するタイミングも設計上の判断材料となる。テスト実行後に前提が偽証された場合、既に発行済みのPromiseを取り消す必要がある。この取り消しが後続のPerformerに伝播されない場合、取り消された前提に基づいた処理が進行し続ける。Promiseの取り消しは単なるフラグ変更ではなく、依存関係を持つ全コミットメントに対する再交渉のトリガーとして扱わなければならない。

運用と拡張:Counter-Offerによる再交渉プロセスの自動化

CONTRACTED状態(チケットの主張とコードベースの現実が矛盾している状態)でエージェントが取るべき行動は、単にエラーを返すことではない。Counter-Offer(反提案)を発行することで、「不可能です」ではなく「この制約下ではこれなら可能です」という代替案をCustomerに提示する。これは Flores の理論におけるコミットメントの再交渉プロセスであり、対話の膠着を避けるための設計パターンである。

Counter-Offerの設計において重要なのは、その内容が具体的かつ検証可能であることである。「より多くのリソースが必要」のような抽象的な提案ではなく、「現在の制約(メモリ上限、実行時間)下ではステップ3の並列化を放弃し、代わりにステップ4のキャッシュを導入することでスループットを確保できます」という形式で、代替案とトレードオフを明示する。Customer側はこのCounter-Offerに対して、受け入れる・拒否する・さらにCounter-Offerを返す、の3択から判断する。この判断ロジックもまたコミットメント理論に基づいて定義される。Customerが受け入れた場合、Performerは新しい条件でPromiseを再発行し、ESTABLISHED状態への遷移を試みる。

この再交渉プロセスを自動化するための具体的な設計パターンとして、Git Diffとチケット定義の差分計算を用いた自動Contradiction Detectionが挙げられる。具体的には、チケットに記述された変更範囲(対象ファイル、期待される動作)と、実際のコードベースの差分(git diffの出力)を比較し、両者の不一致を検出する。不一致が検出された場合、エージェントはCONTRACTED状態に遷移し、不一致の内容(どのファイルのどの行がチケットの主張と矛盾しているか)を構造化された形式でCustomerに報告する。

このプロセスを完全に自動化した場合、人間の介入なしに要件の微調整や制約条件の変更に対応できる。ただし、自動化の範囲には注意が必要である。Counter-Offerの受け入れ判断を完全に自動化すると、エージェントが自己判断で要件を変更してしまうリスクがある。初期段階では、Counter-Offerの生成は自動化しつつ、受け入れ判断は人間の承認を経るハイブリッド方式が安全である。承認履歴を監査ログとして保持することで、要件変更の経緯を後から追跡可能にする。

まとめ:エージェント設計を「契約」から「ノイズ」へ

Flores の Committed Speech Act 理論と Belnap の4値論理を組み合わせることで、LLMエージェントの設計は「曖昧な出力の生成」から「厳密なコミットメントの管理」へとパラダイムシフトする可能性がある。物理的行動を調整する言語は全体の5%に過ぎず、残りはコミットされていないノイズであるというFloresの指摘は、エージェント設計において「何を制御し、何を制御しないか」の境界を考える際の参考になる。

本記事で提示した設計ルールを整理すると、以下の3点が信頼性の向上に寄与する可能性がある。第一に、UNKNOWN状態ではPromiseの発行を抑制し、証拠の収集を優先すること。第二に、ESTABLISHED状態でのみAssertionとPromiseを発行し、証憑の整合性を外部プロセスで検証すること。第三に、CONTRACTED状態ではエラーではなくCounter-Offerを発行し、再交渉プロセスを構造化すること。これらはそれぞれ、ハルシネーションの防止、偽の完了報告の防止、対話の膠着の回避に対応する設計指針として機能しうる。

実装面では、SKILL.mdのルートにDepth-0 Orchestrator Makefile DAGを配置することでコミットメントの連鎖を可視化し、<brain>/state/ディレクトリによるセッション分離で状態の混線を防ぐアプローチが考えられる。これらのパターンは、理論と実装を接続するための具体的な足場となり得る。

今後の発展方向として、このフレームワークを基盤としたオープンソースのオーケストレータや、Belnap 4値論理に基づくコミットメント検証ツールの登場が期待される。エージェント開発における「信頼性の設計」が属人的な知見から構造化された標準へと移行する可能性がある。特に、複数エージェントが同一リソースに対して並行してコミットメントを発行する環境では、状態遷移ルールの機械的検証が不可欠であり、この領域でのツール整備が次の課題となるだろう。

関連記事

参考

本記事は海外の技術トレンド「Requests, Promises, Assertions, and Declarations: Rewriting Our AI Agent Workflow as a 1986 'Committed Speech' Machine」(DEV)で話題のテーマをもとに、両儀システムソリューションズが独自に解説したものです。