本文へ
記事一覧
  • AIエージェント
  • 計測
  • 開発運用

AIエージェントの実行ログから、測れること・測れないことを整理した

既存のAIエージェント実行ログを集計し、測れるものと不明なものを分けて次の計測を選ぶ小さな試行。

記事制作エージェントのログを調べると、草稿を保存できた実行は約225秒でした。しかし、その前には約461秒かかった失敗実行もありました。成功した実行だけを見ていると、この時間を見落とします。

そこで、既存ログだけで何を測れるか、小さな台帳を作って確かめました。小規模なAIエージェントを運用するエンジニアが、手元のログから記録を作り、次に必要な計測を選ぶための試行です。

今回は、取れる情報を5つにまとめた

今回の実装では、実行ID・結果・実行経過時間・成果物参照・不明事項をログから取り出します。追加で測る対象を決めるための最小台帳です。

この5つは今回の実装上の選択であり、共通の必須項目ではありません。

実際の制作ログで試した結果

実際に使っている記事制作処理から、同じ制作対象を含む失敗実行と再実行を選び、保存済みのログをスクリプトで集計しました。以下は架空例ではなく、その事後計測を公開用に整理したものです。内部の識別子、日時、テーマ、入力資料は省いています。集計と記録確認はAIエージェントが行い、利用者に台帳の追加入力は求めていません。

実行 ログ上の結果 開始から終了まで 対象の草稿ファイル
初回 失敗 約461秒 保存なし
再実行 完了 約225秒 保存あり

秒数はログの終了時刻から開始時刻を引き、表示用に丸めました。資料の取得などを含む実行全体の経過時間で、AIモデル単体の応答時間でも、人が作業した時間でもありません。2回分の合計は約686秒ですが、実行間の空白時間は含めていません。また、これらの実行は複数の提案を扱うため、対象記事だけの制作時間とは呼べません。

完了した実行だけを見ると、失敗した実行の負担が消えます。一方、草稿ファイルがあるだけでは「読者に役立つ品質で完成した」とはいえません。今回は、その両方を区別して記録できました。

公開サンプルに残した項目

残す項目 自動取得するもの 不明として扱うもの
実行ID ログのID 仕事との対応・記事別の時間配分
結果 ログに記録された状態 エラー詳細・成果の品質
実行経過時間 開始・終了時刻の差 人の実作業・レビュー・修正時間
成果物参照 ログに記録された参照先 ファイルの実在・採用・品質
不明事項と理由 経過時間を計算できない理由。人の時間と料金は空値 利用量・後続不具合はこの最小例の収集対象外

当初の広い計測案から、取得できる情報と不足の記録に絞りました。表は後掲コードの範囲です。実ログの調査ではこれに加え、仕事との対応と保存ファイルの実在をAIが確認しました。公開サンプルはその確認を自動化しません。

記録負担はどこに残ったか

利用者による台帳入力は今回ゼロでした。ただし、記録の仕組みを作る負担までゼロではありません。ログ形式を調べ、時刻の意味と成果物の対応を確かめ、抽出スクリプトを作る作業が必要でした。これらはAI側で実施しましたが、所要時間は計測していません。

人のレビュー時間を知るには別の観測が必要です。ファイル更新時刻やチャットの間隔から推定して実測扱いすることはできません。今後そこまで測る場合も、先に判断に必要かを決め、入力負担を増やす価値があるか確認する必要があります。

次の改善判断に使えたか

今回できた判断は、ROIや人件費削減の評価を保留し、次に補う計測を絞ることでした。コードも確認したところ、モデル呼出しの処理は構造化された回答を取り出しますが、利用量や料金をこの実行台帳へ保存していません。

次の候補は、呼出しごとの利用量と、取得できる場合の推計料金を実行IDにひもづけることです。値が提供されなければ不明のままにします。定額契約では推計料金を実際の追加請求額と混同しないことも必要です。この追加計装は今後の候補です。

この試行の限界と、背景にした資料

今回は同じ制作対象を含む2件を事後計測しただけです。人の実作業時間、料金、品質の合格、後続不具合、AIなしとの比較は確認できていないため、ROIや平均性能の評価には使えません。成果物の保存と品質の合格は別です。

時間・品質・料金を分ける背景として、Solomon Shalom Lijoのプレプリント Counting the Bandages v1(新しいタブで開きます) の第2節と第8節を参照しました。同論文は作業時間と成果物に残った欠陥を合わせて評価し、推論料金を別に報告することを提案しています。今回の5群の台帳を検証した論文ではありません。本文・付録は確認しましたが、論文の実験は再実行していません。

コピーして試せる最小例

以下の入力は形式を示す架空データです。上の約461秒・約225秒の実測ログとは別です。標準ライブラリだけを使うPython 3.9以降向けの例で、今回の動作確認はPython 3.11.17で行いました。

input.json として保存します。artifact はログに記録された参照先であり、この例ではファイルの実在や品質を検証しません。実作業の試行では、別途保存ファイルを確認しています。

input.json · JSON
[
  {"id":"demo-1","status":"failed","startedAt":"2026-01-01T00:00:00Z","finishedAt":"2026-01-01T00:01:00Z","artifact":null},
  {"id":"demo-2","status":"completed","startedAt":"2026-01-01T00:02:00Z","finishedAt":"2026-01-01T00:02:30Z","artifact":"draft.txt"}
]

次を extract.py として保存します。自分のログでキー名が異なる場合は、対応箇所を変更してください。人の時間や料金は、この入力形式では取得しないため空値です。

extract.py · Python
import json
import sys
from datetime import datetime

def elapsed(start, end):
    if not start or not end:
        return None, '開始または終了時刻がない'
    try:
        a, b = (datetime.fromisoformat(t.replace('Z', '+00:00'))
                for t in (start, end))
        if a.utcoffset() is None or b.utcoffset() is None:
            return None, 'タイムゾーンがない'
        seconds = (b - a).total_seconds()
        if seconds < 0:
            return None, '終了が開始より前'
        return round(seconds, 3), None
    except (ValueError, TypeError, AttributeError):
        return None, '日時形式が不正'

def extract(row):
    seconds, reason = elapsed(row.get('startedAt'), row.get('finishedAt'))
    return {
        'runId': row.get('id'),
        'status': row.get('status'),
        'elapsedSeconds': seconds,
        'elapsedUnknownReason': reason,
        'artifactReference': row.get('artifact'),
        'humanMinutes': None,
        'money': None,
        'unknownReason': '人の時間と料金はこの入力形式では収集していない'
    }

if __name__ == '__main__':
    print(json.dumps([extract(r) for r in json.load(sys.stdin)],
                     ensure_ascii=False, indent=2))

実行方法:

Shell
python3 extract.py < input.json > output.json

期待する出力は次のとおりです。失敗した実行も残り、経過時間は60秒と30秒になります。欠けた時刻、不正な日時、タイムゾーンなし、時刻の逆転は空値と理由で返します。

expected.json · JSON
[
  {
    "runId": "demo-1",
    "status": "failed",
    "elapsedSeconds": 60.0,
    "elapsedUnknownReason": null,
    "artifactReference": null,
    "humanMinutes": null,
    "money": null,
    "unknownReason": "人の時間と料金はこの入力形式では収集していない"
  },
  {
    "runId": "demo-2",
    "status": "completed",
    "elapsedSeconds": 30.0,
    "elapsedUnknownReason": null,
    "artifactReference": "draft.txt",
    "humanMinutes": null,
    "money": null,
    "unknownReason": "人の時間と料金はこの入力形式では収集していない"
  }
]

自分のログで始めるなら

失敗を含むログを選び、キー名を上のコードに合わせて実行します。その後、成果物の実在と仕事との対応を別に確認してください。今回のように人の時間や料金が空欄なら、推測で埋めず、次の判断に必要な計測を1つ選ぶところから始められます。

出典

  1. Counting the Bandages v1

    第2節・第8節(時間・品質・料金を分ける背景)

    arxiv.org/html/2610.35942v1(新しいタブで開きます)

添付ファイル

  • input.json

    形式を示す架空の入力。実測ログではありません。

    JSON261 バイトSHA-256 73f76c00e197

  • extract.py

    本文と同じ標準ライブラリのみの抽出例。

    Python1.2 KBSHA-256 c88afa6d0550

  • expected.json

    架空入力に対応する期待出力。

    JSON587 バイトSHA-256 752dcf55a06a

版 3d1e8ad4a9d1