Skip to main content
自作のエージェント、またはFailproof AIがアダプターを提供していないフレームワーク向けの説明です。インストルメンテーションの設定は不要です。イベントを自分で送出するだけです。 これは、4つのフレームワークアダプターが内部で呼び出しているのと同じAPIです。それらのアダプターは、このAPIの変換テーブルにすぎません。

インストール

追加パッケージも依存関係もありません。

インストルメンテーション

上から読んでいくと、その意味がそのまま伝わります: 各スコープが実際に送出するイベント: スコープ内のコードは session_idagent_id を省略できます。スコープはコンテキスト変数にIDをバインドし、すべてのイベント呼び出しがそこから読み取るため、関数にIDを引き回す必要はありません。 3つのスコープはいずれも async withwith の両方で動作します。 エージェントをネストするとツリーが構築されます。parent_id と深さはスタックから自動計算されます:

スコープの終了方法

agent() は例外を自動で処理します: エラーは agent_end の前に送出されます。これはダッシュボードが agent_end でスパンを閉じるため、それ以降に発生したイベントはどのスパンにも帰属しなくなるためです。キャンセルは失敗ではないため、キャンセルされた実行はエラーサーフェスを汚染しません。例外は常に再送出されます。スコープが例外を握りつぶすことはありません。

イベントメソッド

6つのファミリーに分類された15のメソッドがあります。ほとんどはペアで提供されます — オープナーを送出し、次にクローザーを送出すると、SDKがその間のスパンを計測します。
可能な限りスコープ — agent()tool_call() — を優先してください。本体が例外を送出した場合でも、クローズイベントの送出を保証します。制御フローがネストされない場合(ヘルパー内のモデル呼び出しなど)は、これらのメソッドを直接使用してください。
2つの人間ファミリーは方向が逆です。どのフレームワークも後者のペアを通知しないため、常に自分で送出する必要があります。
モデル呼び出しを並行実行する場合は request_id を渡してください。 指定しない場合、リクエストとレスポンスはエージェントごとの受信順にペアリングされます。並行呼び出しでは順序が保証されないため、各レスポンスが誤ったリクエストに紐付く可能性があります。

使用例

エージェントフレームワークなしで、OpenAI APIに対してツール呼び出しループを実行する例:
これにより、アダプターが生成するのと同じ6種類のイベントタイプが生成されます。ツール定義を含む完全な実行可能バージョンは、SDKリポジトリの docs/manual/examples/ に含まれています。

スレッドと非同期

コンテキスト変数はasyncioタスクに自動的に伝播します。新しいスレッドには伝播しません。スレッドは空のコンテキストで開始されるためです。
propagate() なしでは、ワーカーのイベントがセッションなしで着信する代わりに、修正方法を示す TypeError が発生します。これは意図的な設計です。セッションのないイベントはインジェスト側でスキップされつつ 200 が返されるため、無音の失敗となります。それを防ぐためにIDレイヤーが存在します。

アダプターのないフレームワークへのインストルメンテーション

どのエージェントフレームワークにも同じ3つの接合点があります。それらをマッピングすれば、完全なトレースが得られます — 4つの既存アダプターもこれ以上のことはしていません。
1

実行を囲む

2

各ツールを囲む

フレームワークのツールラッパーまたはミドルウェアに相当する箇所に追加します。
3

各モデル呼び出しをペアにする

ノード、ステップ、ミドルウェア境界を可視化したい場合は? ネストされた agent() ではなく、フックペア — hook_triggered / hook_completed — でラップしてください。agent_id は低カーディナリティのファセットであり、ノードごとに1エントリ追加するとすぐに埋め尽くされます。フックスパンは同様にレンダリングされ、ノードごとのレイテンシを確認できます。
手動計装と自動計装は組み合わせ可能です。 手書きスコープ内で実行されるアダプターは、そのセッションに参加し、そのエージェントを親として設定します。2つのツリーではなく1つのツリーが得られるため、対応フレームワークと独自フレームワークを同時に計装する際に便利です。
2つの理由があります。上記の3つの接合点が、その両方に対する答えです:
  • autogen-core は2025年9月以降メンテナンスされていません。
  • AG2には他のフレームワークのフックに相当するプロセス全体の登録ポイントがないため、計装するにはすべての構築箇所でエージェントをラップする必要があります。
接合点を手動でマッピングすることで、既存アダプターと同じイベントが同じ精度で記録されます。

詳細

記録の実際の仕組みについて。始めるために必要な知識ではありません。
すべての記録は同じ形をしています。スパンが開き、その中に作業がネストされ、各オープンイベントに対応するクローズイベントがあります。ペアが基本単位です。各クローズイベントには、SDKがオープンイベントからの経過時間として計測したdurationが含まれます。以下は各フレームワークの実際の1回の実行 — SDKに同梱されているサンプルから取得、モデル名は正規化済み。1回の呼び出しでどれだけ多くの情報が返されるかに注目してください。
14 events
ノードがフックペアになるため、エージェントリストを埋め尽くすことなくノードごとのレイテンシを確認できます。
セッション終了イベントは存在しません。 セッションは閉じるものではなく、同じ session_id を共有するイベントのグループです。ステータスはトレースの形状から導出されます:つまり、すべてのペアが閉じられるとセッションが終了します。アダプターは agent_end を自動送出し、テアダウン時にはまだ開いているものをすべて閉じてincompleteとしてマークします — クラッシュした実行は永遠にハングするのではなく、visible gapを持つ done として落ち着きます。
これが、1つのセッションが2回の呼び出しにまたがれる理由です。LangGraphの interrupt() は実行を一時停止し、ルートスパンを意図的に開いたままにします。再開する呼び出しがそれを閉じます。両方の呼び出しが1つのセッションです。
session_idagent_id はすべてのイベントメソッドでオプションです。省略した場合、囲んでいるスコープから解決されます:
明示的に渡すことも可能で、その場合は優先されます。何もバインドされておらず何も渡されない場合、セッションなしでイベントを送出する代わりに、修正方法を示す TypeError が発生します(インジェストはセッションなしのイベントをスキップしつつ 200 を返します)。スコープはコンテキスト変数にIDをバインドします。asyncioタスクには自動的に伝播しますが、新しいスレッドには伝播しません — ワーカーを failproofai_sdk.propagate() でラップしてください。

どのIDを誰が発行するか

アダプターが session_id を解決する方法

最初のマッチが優先されます:
  1. 明示的な session_id オプション
  2. 呼び出しごとのメタデータ
  3. 囲んでいる session() スコープ
  4. フレームワークのメタデータ
  5. フレームワーク独自の実行ID
これらのいずれかが存在する間は、IDが新規生成されることはありません — 合成されたIDは1回の実行を複数のセッションに分割してしまうためです。

agent_id は低カーディナリティに保つ

すべてのダッシュボードサーフェスの主要ファセットであり、LowCardinality(String) カラムです。実行ごとの値を使用するとカラムが劣化し、フィルターのドロップダウンが実行1件につき1エントリで埋まります。アダプターはそのカラムを守ります:実際のIDは fw_agent_id / fw_run_id に保持されるため、ファセットにならずともクエリ可能です。
このガードはフレームワークが選んだラベルにのみ適用されます。 event.*failproofai_sdk.agent(...) に自分で渡す agent_id は、渡したとおりに記録されます。明示的な引数を暗黙的に書き換えることは、防ごうとするカーディナリティの問題よりも悪いため、スパン名は適切に命名してください。
上記の実行から計測した、フレームワークごとの記録内容:ダッシュはそのフレームワークにその概念がないことを意味します。human_pausehuman_interrupt人間がエージェントに働きかけることを表しており、どのフレームワークも通知しません — これらは自分で送出してください。
イベントは単独では届きません。1つがスパンを開き、1つが閉じます。クローズイベントには、SDKがオープンイベントからの経過時間として計測したdurationが含まれます。
クローズイベントのないオープンイベントは、永遠に終わらないスパンです。セッションはずっと実行中としてレンダリングされ、アクティブdurationが増え続けます。これは手動で計装する際に注意すべき失敗パターンです。

相関ルール

  • マッチするクロージングイベントには同じ tool_call_idhook_idpause_id、または input_id を再利用してください。
  • SDKは tool_resulthook_completedagent_resumehuman_inputduration_ms を計算します。これらのメソッドに渡すと ValueError が発生します。
  • duration_msmodel_response では受け付けられます。これは実際のプロバイダーレイテンシを知っているのが呼び出し側だけだからです。整数でなければなりません — floatを渡すと呼び出し箇所で ValueError が発生します(サーバーはそのカラムを符号なし32ビット整数として読み取るため、それ以外はNULLとして保存されます)。
  • 相関キーはkindとsessionでスコープされます。ツール呼び出しとフックは同じIDを安全に共有でき、2つの並行セッションは衝突なく同じIDを再利用できます。agentによるスコープはありません。あるエージェントで開かれ別のエージェントで閉じられたペアも相関します。これはマルチエージェントフレームワークでは通常のケースです。
  • request_idmodel_requestmodel_response をペアリングします。指定しない場合、モデルイベントはエージェントごとの順序でペアリングされるため、並行呼び出しではペアが誤ります。
  • プロセスをまたいで分割されたペアはダウンストリームで相関しますが、SDKはプロセス内のdurationを計算できません。
  • ペンディングマップは最大10,000エントリを保持し、満杯になると最古のエントリを削除します。
failproofai-sdk をインストールすると、4つのアダプターを含むすべてがインストールされます。extrasが引き込むのはアダプターではなくフレームワークです。
import failproofai_sdk は契約上ゼロ依存であり、--no-deps でビルド済みwheelをインストールするテストと、どのフレームワークも sys.modules に到達しないことを証明するテストによって強制されます。
failproofai_sdk.crewai という属性は存在しません。アダプターはトップレベルパッケージに意図的に公開されていません。属性アクセスの副作用としてフレームワークがインポートされ、ゼロ依存の約束が破られるためです。instrument() を使用してください。
自動検出はインストール済みパッケージリストではなく sys.modules を読み取ります。インストールはしてあるがインポートしていないフレームワークは計装されず、代わりにインポートされることもありません。現在の状態を確認するには:
CrewAIがインストールされていないマシンで instrument("crewai") を呼び出しても例外は発生しません。 警告をログに記録して () を返すため、1つのフレームワークが欠けていても他を計装するプロセスが停止することはありません。警告には元の ImportError が含まれており、そのメッセージに正確なインストールコマンドが示されています — 修正方法はログに記録されており、隠されていません。
代わりに例外を発生させるには FAILPROOFAI_SDK_STRICT=1 を設定してください。このフラグは一度だけ読み取られてキャッシュされます。実行中に設定するのではなく、プロセス起動前にエクスポートしてください。
instrument() はフレームワークのインポートのに呼び出す必要があります。 自動検出は sys.modules を読み取るため、インポートより前に呼び出すと何も見つからず、何もインストールされず、() が返されます。
これを誤ると、SDKがインポートされアダプターが一見インストールされているのに、イベントが1件も送出されない状態になります。ログにその旨の警告が記録されます — 実行が何も記録しない場合、まずログを確認してください。
Spoolがこれを安全にする理由です。エージェントはネットワークをブロックすることなく動作し、Cloudの障害は消失したイベントではなくディレクトリの増大として現れます。各フラッシュは1つのバッチファイルを書き込みます。.tmp で書き始め、次に fsync、そしてアトミックリネームを行います:
デーモンは .jsonl のみを読み取るため、書き込み途中のファイルを読むことは決してありません。ファイル名にはタイムスタンプ、プロセスID、シーケンス番号が含まれるため、2つのプロセスが同じミリ秒にフラッシュしても衝突しません。キューの上限は10,000イベントで、それを超えると最古のものを削除してログに記録します。
collector.redact はSDKイベントには適用されません。 SDKイベントは collector.redact の処理対象外です。
デーモンはバッチを送信します。バッチを開いたり書き換えたりしません。編集はデーモンが自身のイベントを書き込む場所で実行されます — バッチが送信される場所ではありません。そのため、APIキーを含むプロンプトやツール引数は、到着時もそのままの状態です。これは意図的な設計です。これらはご自身の計装コールであり、送受信中に書き換えることは、送出したイベントと受け取るイベントが異なるものになることを意味します。
ペイロードはソースの2箇所で制御できます:
  • アダプターでコンテンツキャプチャを無効にする。オプション名はアダプターによって異なり、対応していないアダプターもあります — 共通の単一スイッチではありません:
    • LangChain / LangGraph、Pydantic AI — capture_content=False
    • LlamaIndex — capture_messages=False
    • CrewAI — コンテンツスイッチなし。読み取るオプションは session_id のみのため、プロンプトと補完は常に記録されます。
    instrument() はアダプターが読み取らないオプションを無視するため、誤った名前を渡しても何も起きず、何も変わりません。
  • そもそもシークレットを input= に渡さない。
collector.redact はどちらの代替手段にもなりません。
Spoolディレクトリが空の状態が正常です。 配信確認のために使用しないでください。
デーモンは送信後数ミリ秒以内に各バッチを削除するため、ls はコレクターと競合し、実際に送出したイベントのごく一部しか表示されません — 何も記録していないSDKと区別がつきません。イベントが実際に届いたかどうかはダッシュボードで確認してください。Spoolが埋まる様子を観察するには、先にデーモンを停止してください。
すべてのコールバックは再送出のみを行うラッパー内で実行されます。コードは1つの try の中に置かれ、SDKが行うすべての処理はその外側で実行されます。デフォルトは本番環境では適切ですが、デバッグ時には不適切です。「クラッシュしなかった」ことしか証明できないためです。隠れた失敗を顕在化させるには FAILPROOFAI_SDK_STRICT=1 を設定してください。

よくある問題

オープンイベントに対応するクローズイベントがありません。model_requestmodel_response がない、または tool_usetool_result がない状態です。スコープを使用してください。本体が例外を送出した場合でもペアが保証されます。イベントメソッドを直接呼び出す場合は tryfinally を使用してください。
tool_resulthook_completedagent_resumehuman_input では、対応するオープンイベントからの経過時間として計測されるため拒否されます。model_response では受け付けられます。実際のプロバイダーレイテンシを知っているのが呼び出し側だけだからです。整数でなければなりません。
スレッドがコンテキストを継承していません。callableを failproofai_sdk.propagate() でラップしてください。スレッドと非同期 を参照してください。
追加フィールドは最後にマージされます。modeloutcome など実際のフィールドと同じ名前を使用すると上書きされ、保存されるカラムが変わります。独自フィールドには名前空間を付けてください。アダプターは fw_ プレフィックスを使用しています。
agent_id は低カーディナリティのファセットですが、実行IDを設定しています。ロール名やノード名を使用し、実際のIDはペイロードフィールドに格納してください。

次のステップ

仕組みを理解する

ペア、ID、セッションのライフサイクル、配信の詳細。

トレースを読む

記録したセッションの因果関係をたどる。

フレームワークアダプター

LangGraph、CrewAI、LlamaIndex、Pydantic AI。