Managed Agentsのアウトカム評価イベントを3段階で追う
Managed Agentsのアウトカム評価はstart/ongoing/endの3イベントで進みます。運用中に何を監視すればよいか、成果物の取得方法までを扱います。
Managed Agentsでアウトカム(達成すべき成果物の定義)を設定したセッションは、進捗をspan.outcome_evaluation_*という専用のイベント群でストリームに流します。このイベントはstart・ongoing・endの3段階しかなく、構造は単純です。ただし、何を監視し、いつ次のアクションを取るべきかは実装側の判断に委ねられています。3段階それぞれの意味と、ステータスをポーリングで確認する方法、成果物を取り出す手順までを扱います。
アウトカム評価だけが専用イベントを持つ理由
アウトカム指向のセッションでは、通常のagent.*イベント(メッセージやツール呼び出し)と、アウトカム評価専用のspan.outcome_evaluation_*イベントが同じストリームに混在します。前者はエージェントがアウトカムに向けて何をしているかを示し、後者はグレーダー(採点役)が何回反復し、どう判定したかを示します。
この2系統が分かれているのは、アウトカムを設定していない通常のセッションには評価という概念自体が存在しないからです。span.outcome_evaluation_*はアウトカム指向のセッションでのみ発生し、それ以外のセッションのストリームには一切現れません。
アウトカムが進行中でも、user.messageイベントを送ってエージェントの作業を追加で方向づけることは可能です。ただし必須ではありません。エージェントは成功するか反復回数を使い切るまで、アウトカムに向けて自律的に作業を続けます。
start — 評価サイクルの開始を示す
グレーダーが1回のイテレーションループの評価を開始するとspan.outcome_evaluation_startが発火します。
{
"type": "span.outcome_evaluation_start",
"id": "sevt_01def...",
"outcome_id": "outc_01a...",
"iteration": 0,
"processed_at": "2026-03-25T14:01:45Z"
}iterationは0始まりの改訂カウンターです。0が最初の評価、1が最初の改訂後の再評価というように、Rubric側の反復回数と対応しています。この番号を記録しておくと、あとから「何回目の改訂で合格したか」を追跡できます。
ongoing — グレーダーが動作中であることのハートビート
span.outcome_evaluation_ongoingは、グレーダーが実行中である間に送られるハートビートです。
{
"type": "span.outcome_evaluation_ongoing",
"id": "sevt_01ghi...",
"outcome_id": "outc_01a...",
"iteration": 0,
"processed_at": "2026-03-25T14:02:10Z"
}グレーダーの内部推論は非公開です。動いていることは分かっても、何を考えているかは分かりません。UIで進捗インジケーターを出したい場合、このongoingイベントの受信をトリガーにするのが実装として素直です。逆に、このイベントの中身から採点の途中経過を推測しようとしても得られる情報はありません。
end — 評価サイクルの結果と次のアクション
span.outcome_evaluation_endは、グレーダーが1回の評価を終えたとき、またはアウトカムが有効な間にセッションが割り込まれたときに発火します。resultフィールドの値によって、監視側が取るべき次のアクションが変わります。
| result | 次に起きること | 監視側で取るべき対応 |
|---|---|---|
satisfied | 次に起きることセッションがidleへ遷移 | 監視側で取るべき対応成果物の取得に進んでよい |
needs_revision | 次に起きること新しいイテレーションが自動的に開始 | 監視側で取るべき対応待機を継続する。何もしなくてよい |
max_iterations_reached | 次に起きること最終確認ターンのあとidleへ | 監視側で取るべき対応未達成のまま終了。人手での確認が必要 |
failed | 次に起きることidleへ遷移 | 監視側で取るべき対応RubricとDescriptionの矛盾を疑う |
interrupted | 次に起きること割り込み時に発火。評価が始まる前の割り込みではoutcome_evaluation_start_idが空文字になる | 監視側で取るべき対応新しいアウトカムを再定義できる状態 |
{
"type": "span.outcome_evaluation_end",
"id": "sevt_01jkl...",
"outcome_evaluation_start_id": "sevt_01def...",
"outcome_id": "outc_01a...",
"result": "satisfied",
"explanation": "All 12 criteria met: revenue projections use 5 years of historical data, WACC assumptions are stated, sensitivity table is included...",
"iteration": 0,
"usage": {
"input_tokens": 2400,
"output_tokens": 350,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 1800
},
"processed_at": "2026-03-25T14:03:00Z"
}explanationには、どの基準を満たしどの基準を満たさなかったかの説明文が入ります。needs_revisionのとき、この説明文がそのまま次のイテレーションでエージェントへ渡るフィードバックになります。usageフィールドで評価1回あたりのトークン消費も追跡できるため、反復回数が多いセッションのコスト管理にも使えます。
3イベントが実際にどの順序で流れるか
agent.*イベントとspan.outcome_evaluation_*イベントは同じストリームに混在するため、実際のログでは次のように交互に並びます。グレーダーの実行時間が長いほどongoingは複数回発火し得るハートビートなので、1回しか来ないと仮定して実装しないでください。
agent.message ← エージェントが成果物を作る作業を報告
span.outcome_evaluation_start ← iteration: 0、グレーダーが採点開始
span.outcome_evaluation_ongoing ← ハートビート(複数回来ることがある)
span.outcome_evaluation_ongoing
span.outcome_evaluation_end ← result: needs_revision
agent.message ← エージェントが改訂作業を報告
span.outcome_evaluation_start ← iteration: 1、再評価
span.outcome_evaluation_ongoing
span.outcome_evaluation_end ← result: satisfied
session.status_idle自分の監視コードでspan.outcome_evaluation_*だけを取り出したい場合、イベントタイプの接頭辞で単純にフィルタできます。
jq -r 'select(.type | startswith("span.outcome_evaluation_")) | "\(.type) iteration=\(.iteration // "-")"'agent.*側を見ればエージェントが何をしたか、span.outcome_evaluation_*側を見ればグレーダーがどう判定したかが分かります。両方をまとめて1つのタイムラインとして描画したいUIでは、processed_atでソートして描画すれば、上記のような時系列順が再現できます。
usageフィールドで評価コストを追跡する
span.outcome_evaluation_endのusageフィールドには、その評価1回にかかったトークン数が入ります。input_tokens・output_tokensに加え、cache_creation_input_tokens・cache_read_input_tokensでキャッシュの利用状況も分かります。Rubricや成果物の内容がキャッシュされていればcache_read_input_tokensが伸び、初回評価や大幅な改訂後はcache_creation_input_tokens側が伸びる、という読み方になります。
needs_revisionが何度も続くセッションでは、このusageをiterationごとに積算しておくと、「Rubricの粒度が細かすぎて反復コストが膨らんでいないか」を後から検証できます。反復のたびにコストが積み上がる一方でexplanationの指摘内容がほぼ変わらない場合は、Rubricの基準そのものが成果物に対して厳しすぎる可能性を疑うべきサインです。
ステータスをポーリングで確認する
イベントストリームを常時開いておけない構成では、セッションを取得するAPIを直接叩いてoutcome_evaluations[].resultを読む方法もあります。評価が完了するまではpending・running・evaluatingのいずれかが返ります。
session=$(curl -fsSL "https://api.anthropic.com/v1/sessions/$session_id" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01")
jq -r '.outcome_evaluations[] | "\(.outcome_id): \(.result)"' <<<"$session"
# outc_01a...: satisfiedストリームでイベントを受け取るか、ポーリングで状態を確認するかは実装方針次第です。UIにリアルタイム進捗を出す用途ならストリーム、バッチ処理でまとめて結果を確認する用途ならポーリングが向きます。両方とも同じoutcome_evaluationsデータを参照しているため、途中で切り替えても情報の非一致は起きません。
成果物の取得 — Files APIからscope_idで絞り込む
エージェントはサンドボックス内の/mnt/session/outputs/に出力ファイルを書き込みます。取得するにはFiles APIをセッションIDで絞り込んでリスト化し、IDでダウンロードします。
# このセッションが生成したファイルを一覧取得
files=$(curl -fsSL "https://api.anthropic.com/v1/files?scope_id=$session_id" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01")
jq -r '.data[] | "\(.id) \(.filename)"' <<<"$files"
# ファイルをダウンロード
file_id=$(jq -r '.data[0].id // empty' <<<"$files")
if [[ -n $file_id ]]; then
curl -fsSL "https://api.anthropic.com/v1/files/$file_id/content" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-o /tmp/output.txt
fiscope_idによるフィルタリングにはmanaged-agents-2026-04-01ベータヘッダーが必須です。エージェントが書き込みを終えてからファイルがリストに現れるまで数秒のタイムラグが生じることがあります。期待したファイルがまだ一覧に出ない場合は、少し待ってから再度リストを取得してください。一度リストに現れれば、そのファイルのアップロードは完了済みです。
Webhookで監視する場合の名前の違いに注意する
イベントストリームを常時開いておかず、Webhook通知で運用状態を監視する構成を選ぶこともできます。その場合に押さえておきたいのは、Webhookのイベント名はストリームのイベント名とは別物だという点です。たとえばセッションがidleになったことを示すイベントは、ストリーム上ではsession.status_idleですが、Webhook側ではsession.status_idledという異なる名前で届きます。
アウトカム評価専用のspan.outcome_evaluation_*系イベントについても、監視をストリームからWebhookへ切り替える、あるいは両方を併用する場合は、それぞれの命名規則が独立している前提でコードを書く必要があります。片方の名前をもう片方にそのまま流用すると、イベントが来ているのに条件分岐がマッチせず「イベントが来ない」ように見える不具合につながります。
実運用では、ダッシュボードのようにリアルタイム性が求められる面はストリーム、監査ログや通知のように後から参照できればよい面はWebhook、という役割分担にするケースが多くなります。この場合でも参照するデータの実体(outcome_id・result・iteration)は共通なので、命名規則の差だけを吸収する薄いマッピング層を1つ用意しておくと、後からもう一方の経路を追加するときの手戻りが小さくなります。
satisfied後にセッションをどう扱うか
satisfiedでセッションがidleになったあと、そのセッションは終わりではありません。通常の会話セッションとして続けることも、新しいuser.define_outcomeを送って次のアウトカムへ連鎖させることもできます。過去のアウトカムの履歴は保持されたままなので、「前のアウトカムで作ったDCFモデルを踏まえて、感応度分析シートを追加して」のような継続作業も自然に行えます。ただし、同時に評価できるアウトカムは常に1つだけです。新しいアウトカムを送るのは、直前のspan.outcome_evaluation_endを確認したあとにしてください。
Rubricの設計とは別の関心事
アウトカム評価イベントの監視は、Rubric(採点基準)の設計そのものとは別の作業です。何を基準に採点させるかはManaged AgentsのRubricでアウトカムを定義する方法で扱っており、本記事はその評価が進行しているときに「何が起きているか」をどう追うかに絞っています。アウトカム以外の一般的なイベント(メッセージ・ツール呼び出し)をリアルタイムに追いたい場合は、Managed Agentsのセッションイベントストリームを実装するがSSE(Server-Sent Events)の実装手順を扱っています。
Managed Agentsのセッション・ハーネス・サンドボックス分離という基盤設計は、Agent SDKのManaged Agentsの設計思想で解説しています。
まとめ
アウトカム評価はstart・ongoing・endの3イベントだけで構成される単純な仕組みです。監視で押さえるべきはendのresult値で、satisfiedなら成果物取得へ、needs_revisionなら待機継続、failedならRubricとDescriptionの矛盾を疑う、という分岐だけを実装すれば運用が回ります。ストリームとポーリングのどちらで状態を追うかは用途次第で、同じoutcome_evaluationsデータを参照するため併用しても矛盾は起きません。
構造の単純さに反して、実運用で判断が必要になるのはneeds_revisionが何度続いたら人手を挟むかという線引きです。usageの積算とセットで監視しておけば、その線引きを勘ではなくコストの推移から決められます。max_iterationsに到達する前に自前のしきい値でアラートを出す運用にしておくと、無駄な反復に気づかないまま上限まで走り切ってしまう事態も避けやすくなります。監視の設計そのものは単純な作りでも、イベントの構造をどう読むかより、この種の運用判断をどこまで自動化するかのほうが実際の難所になります。