Cloudflare AI GatewayのpatchLogでLLMの評価ログを育てる
AI Gatewayのログにfeedback、score、metadataを後付けし、LLMのリクエストログを改善対象の評価データへ変えるための小さな検証メモ。
はじめに
Cloudflare AI Gatewayは、LLMアプリの入口に置くだけでprovider、model、status、duration、tokens、cost、prompt、responseを見やすくしてくれます。以前の記事では、AI Gatewayを経由したLLMリクエストがどこまでログとして残るかを整理しました。
AI GatewayでLLMのリクエストとレスポンスをどこまで記録できるか試す
ただ、ログを眺められるだけでは改善サイクルになりません。運用で本当に欲しいのは、「この回答は役に立ったのか」「再生成すべきだったのか」「あとから評価データとして抽出できるのか」という評価の戻し先です。
ここで気になったのが、Cloudflare AI GatewayのログAPIに用意されている Patch Gateway Log です。公式APIリファレンスでは、AI Gatewayの個別ログに対して feedback、score、metadata をPATCHできるようになっています。2026年7月3日時点のAPIリファレンスでは、feedback は -1 から 1、score は 0 から 100、metadata は文字列、数値、真偽値のmapとして扱われます。CloudflareのCustom metadataドキュメントでは、リクエストに渡せるmetadataは最大5件で、6件目以降は保存されないとも説明されています。
これ、かなり面白い。
ログを保存して終わりではなく、LLM呼び出しの結果に評価を戻せるなら、AI Gatewayを「出口ログ」から「評価データの入口」に少し近づけられます。本記事では、patchLog 相当のPATCHリクエストを使って、AI Gatewayのログへ評価情報を戻す設計を小さく検証します。
今回の到達点
この記事で確認するのは、AI Gatewayだけで本格的なAgentOps基盤を完成させることではありません。Langfuseのようなtrace、span、generation、score、datasetを持つ観測基盤と比べると、AI Gatewayはアプリ内部の処理構造までは持ちません。
今回の到達点は、もう少し手前です。
LLM request
↓
AI Gateway log
↓
feedback / score / metadataをPATCH
↓
改善対象を検索・抽出する

AI Gatewayのログは、LLMプロバイダへ出ていったリクエストの事実を持ちます。そこへ人間の評価、簡易採点、評価軸のmetadataを戻せるなら、少なくとも次の用途には使えます。
- 評価が低かったLLM呼び出しだけをあとから抽出する
- モデル変更前後でscoreの傾向を見る
- ユーザー評価とtokens、cost、durationを同じログ上で見る
- payloadを保存しない運用でも、評価値とmetadataだけは残す
一方で、RAGでどの文書を検索したか、Agentがどのtoolを呼んだか、回答前にどの分岐を通ったかはAI Gatewayだけでは分かりません。ここを混ぜると設計を誤ります。AI Gatewayに戻す評価は、あくまで「LLM呼び出し単位の評価」として扱うのがよさそうです。
公式APIで確認したこと
CloudflareのAPIリファレンスでは、AI Gateway Logsに次の操作が用意されています。
今回使うのは Patch Gateway Log です。エンドポイントは次の形です。
PATCH /accounts/{account_id}/ai-gateway/gateways/{gateway_id}/logs/{id}
API tokenにはAI Gateway Write権限が必要です。bodyには、必要な項目だけを入れます。
{
"feedback": 1,
"score": 92,
"metadata": {
"app": "support-bot",
"rubric": "answer-usefulness-v1",
"reviewed": true
}
}
ここで大事なのは、feedback と score の役割を分けることです。feedback はユーザーが押した高評価・低評価に近い粗い信号として扱い、score はrubricに基づく採点値として扱う方が運用しやすいです。たとえば、UIの👍は feedback: 1、👎は feedback: -1 とし、あとからバッチ評価で score: 0..100 を付与する、という分担が考えられます。
metadataは、評価の検索軸です。app、env、rubric、experiment、user_segment のような値を残しておくと、あとから「本番の問い合わせBotで、回答有用性rubricのscoreが低いログ」だけを抽出しやすくなります。ただし、metadataにも個人情報や顧客情報を入れない方がよいです。ログを便利にするほど、ログ自体が管理対象になります。
もう1つ、metadataは増やしすぎない方がよいです。CloudflareのCustom metadataドキュメントでは、1リクエストあたり最大5件まで保存され、5件を超えた分は無視されると説明されています。設計上は、app、env、feature、rubric、failure_case のように、検索で本当に使うキーだけに絞る方がよさそうです。
Worker Bindingで書く場合
本記事の実検証は、Cloudflare APIを直接呼ぶREST APIベースで行っています。一方、実アプリをCloudflare Workers上に置くなら、AI bindingからログIDを受け取り、同じWorker内で patchLog() へつなぐ形の方が自然です。
CloudflareのCustom metadataドキュメントでは、Workers AI bindingの env.AI.run() に gateway.metadata を渡す例が示されています。また、Workersの型定義には env.AI.aiGatewayLogId と env.AI.gateway("...").patchLog(logId, data) が用意されています。これを組み合わせると、UIの👍👎や失敗分類をAI Gatewayログへ戻す実装は、概念的には次の形になります。
export interface Env {
AI: Ai;
}
export default {
async fetch(_request, env): Promise<Response> {
const gatewayId = "patch-log-feedback-loop";
const answer = await env.AI.run(
"@cf/meta/llama-3.1-8b-instruct-fast",
{
prompt: "問い合わせへの回答案を短く作ってください。",
},
{
gateway: {
id: gatewayId,
metadata: {
app: "support-bot",
env: "production",
rubric: "answer-usefulness-v1",
},
},
},
);
const logId = env.AI.aiGatewayLogId;
if (logId) {
await env.AI.gateway(gatewayId).patchLog(logId, {
feedback: 1,
score: 92,
metadata: {
app: "support-bot",
env: "production",
rubric: "answer-usefulness-v1",
reviewed: true,
},
});
}
return Response.json({ answer, logId });
},
} satisfies ExportedHandler<Env>;
ここでのポイントは、LLM呼び出しのログIDをアプリのイベントとつなげることです。たとえば、回答直後に logId をmessage idと一緒に保存しておき、ユーザーが👍👎を押したタイミングで patchLog() を呼べば、AI Gatewayのログに人間の評価を戻せます。
ただし、このWorker Binding版は本記事では未実行です。今回実際に確認したのは、この後に説明するREST API経由の Patch Gateway Log です。ここは、「patchLogという名前のAPIをWorkerではどう使うのか」を示すための実装スケッチとして扱います。
検証用スクリプトを用意する
今回の検証では、記事用に小さなNode.jsスクリプトを用意しました。これはCloudflare公式CLIではなく、patchLog に投げるリクエストbodyを安全に組み立て、実APIを叩く前に値の範囲を確認するための自作検証スクリプトです。
このスクリプトが入力として受け取るのは、AI GatewayのAccount ID、Gateway ID、Log ID、Cloudflare API token、そして feedback、score、metadata です。--dry-run ではAPIに送らず、PATCH先の形とbodyだけを確認します。--apply を付けた場合だけ、Cloudflare APIへPATCHします。
まず、資格情報なしでdry runします。
npm run dry-run
出力は次のような形になります。
# AI Gateway patchLog dry run
case: helpful
endpoint: https://api.cloudflare.com/client/v4/accounts/<account_id>/ai-gateway/gateways/<gateway_id>/logs/<log_id>
request body:
{
"feedback": 1,
"score": 92,
"metadata": {
"app": "blog-ex",
"env": "local",
"rubric": "answer-usefulness-v1",
"reviewed": true
}
}
実際にPATCHする場合は、Cloudflare API tokenと実ログIDを設定したうえで --apply を使います。
node scripts/patch-log-feedback-loop.mjs --apply --case helpful
ただ、最初から有効な外部providerのAPI keyを使うと、今回の主題ではないprovider課金やモデル応答の論点が混ざります。そこで、この記事ではCloudflare上に検証用Gatewayを作り、あえて無効なOpenAI API keyでリクエストして401ログを作りました。その実ログに対して patchLog を実行し、再取得で feedback、score、metadata が反映されるかを確認します。
一連の検証は、記事用に用意した自作スクリプトで実行しました。これは、Gatewayがなければ作成し、AI Gateway経由でエラーログを1件作り、そのログIDへPATCHし、最後にログ詳細を再取得するためのものです。
npm run live:patch-log
実行結果は次のとおりです。

検証では、patch-log-feedback-loop というGatewayが作成され、AI Gateway経由のOpenAIリクエストは意図どおり 401 Unauthorized になりました。これは失敗ではなく、外部providerの有効なAPI keyを使わずにAI Gateway上へ実ログを作るための手順です。
その直後にLogs APIで直近ログを取得すると、providerはopenai、modelはgpt-4o-mini、status codeは401として記録されていました。最初のログには、リクエスト時に付けたmetadataはありましたが、feedbackとscoreはまだありません。
そこへ次の評価値をPATCHしました。
{
"feedback": -1,
"score": 24,
"metadata": {
"app": "blog-ex",
"env": "live",
"rubric": "provider-error-observability-v1",
"failure_case": "invalid-provider-key"
}
}
PATCH後に同じログを再取得すると、feedback: -1、score: 24、metadata.failure_case: "invalid-provider-key" が反映されていました。つまり、少なくともCloudflare API経由では、AI Gatewayの実ログに評価値を後付けできることを確認できました。
今回確認したのは、Dashboard表示ではなくAPIでの再取得結果です。また、有効なprovider API keyを使った正常応答ログではなく、意図的に作った401ログに対するPATCHです。正常応答ログでも同じ設計で使えるはずですが、成功ログ、Dashboard上の表示、List Logsでのfilter条件、Datasets/Evaluations APIとの接続は別途確認する余地があります。
なぜ最初にvalidationを挟むのか
patchLog は小さなAPIですが、評価ログとして使うならvalidationをアプリ側に寄せた方がよいです。
理由は単純で、評価値はあとから集計されるからです。score に 101 が入ったり、metadataにネストしたオブジェクトや配列が混ざったりすると、その場では小さな実装ミスでも、後段の集計や抽出で地味に効いてきます。
今回の検証スクリプトでは、次を実行前に止めます。
| 項目 | 許可する値 | 止める値 |
|---|---|---|
feedback | -1 から 1 の数値 | 範囲外、数値でない値 |
score | 0 から 100 の数値 | 範囲外、数値でない値 |
metadata | 文字列、数値、真偽値のmap。最大5件 | 配列、オブジェクト、null、6件目以降に依存する設計 |
このvalidationは、AI GatewayのAPI仕様をなぞっているだけです。しかし、ここをアプリケーション側にも置くことで、評価データの形が崩れにくくなります。ログはあとから直すのが面倒です。最初に狭くしておく方が、運用では楽です。
payload保存なし運用との組み合わせ
前回の記事では、AI Gatewayでpromptとresponseのpayload保存を抑止するヘッダーも確認しました。
cf-aig-collect-log-payload: false
本文を保存しない場合、ログから具体的な失敗理由を読む力は落ちます。一方で、model、tokens、cost、duration、statusのような運用メタデータは残せます。そこへ feedback、score、metadata を戻せるなら、「本文は残さないが、品質傾向は追う」という折衷案が作れます。
たとえば、業務データを含む問い合わせではpayloadを残さず、ユーザーが押した👍👎と、アプリ側で抽象化したカテゴリだけをmetadataに残す。
{
"feedback": -1,
"score": 32,
"metadata": {
"app": "support-bot",
"env": "production",
"topic": "billing",
"rubric": "answer-usefulness-v1"
}
}
この形なら、prompt本文を見ずに「billing系の低評価が増えている」「特定モデルに切り替えたあとscoreが落ちている」といった傾向を見られます。もちろん、原因分析には別の安全なサンプリングや、匿名化された再現データが必要です。それでも、何も残らないよりは改善の入口になります。
Langfuseとどう使い分けるか
AI Gatewayの patchLog を見て最初に思ったのは、「これでLangfuseの代わりになるのか」ということでした。
結論としては、代替というより役割が違います。
AI Gatewayは、LLMプロバイダへ出ていくリクエストのログを持ちます。provider、model、tokens、cost、duration、status、payload、metadataに強いです。patchLog でfeedbackやscoreを戻せるため、LLM呼び出し単位の評価には使えます。
一方、LangfuseのようなAgentOpsツールは、アプリケーション内部のtraceを表現できます。RAGの検索、Agentのtool call、複数stepの実行、dataset、eval run、scoreの履歴を扱うなら、そちらの方が自然です。
そのため、個人的には次の分担がよさそうだと感じています。
| 領域 | AI Gateway | Langfuseなど |
|---|---|---|
| LLMプロバイダへの出口ログ | 得意 | 連携次第 |
| tokens、cost、duration | 得意 | 実装次第 |
| prompt / responseの確認 | 得意 | 得意 |
| RAGやAgentの内部trace | 弱い | 得意 |
| ユーザー評価の粗い付与 | patchLog で可能 | 得意 |
| dataset化、eval run管理 | 限定的 | 得意 |
AI Gatewayだけで閉じるなら、最初の評価ループはかなり軽く作れます。ただし、AgentやRAGのどの工程が悪かったかまで見たいなら、AI Gatewayのlog idをアプリ内部のtrace idやsession idとひも付けて、別の観測基盤へ渡す設計が必要になります。
使うときの設計メモ
実装に入る前に、少なくとも次の4点は決めた方がよいです。
1つ目は、feedback と score の意味です。feedback は人間の粗い反応、score はrubricに基づく採点、と決めておくと混乱しにくくなります。
2つ目は、metadataの語彙です。app、env、feature、rubric、experiment のような共通キーを決めずに自由入力にすると、あとから検索できないログが増えます。さらに、metadataは最大5件という制約があるため、検索軸を増やす前に「本当に集計で使うか」を決める必要があります。
3つ目は、payload保存の方針です。本文を残すとデバッグは楽になりますが、ログの取り扱いは重くなります。本文を残さない場合でも、評価値とmetadataで傾向を見る設計はできます。
4つ目は、log idの受け渡しです。アプリのUIで👍👎を押したとき、どのAI Gateway logへPATCHするのかを特定できなければ、評価を戻せません。LLMリクエスト、アプリ側のmessage id、ユーザー操作、AI Gateway log idをどう対応させるかが、この方式の実装上の論点になります。
ここで止まるのか、という感じがあります。
APIとしては小さいのですが、実際に評価ループへ組み込むには、ログIDの管理、UIイベント、payload保存方針、metadata設計が一気に出てきます。これは機能紹介というより、運用設計の話です。
まとめ
Cloudflare AI Gatewayの patchLog は、LLMリクエストログに feedback、score、metadata を後付けできる小さなAPIです。派手な機能ではありませんが、LLMの出口ログを評価データへ育てる入口としてはかなり実用的に見えます。
今回の検証では、API仕様に合わせてPATCH bodyを組み立て、feedback、score、metadata の制約を実行前に検査したうえで、Cloudflare上の実ログにPATCHできることまで確認しました。対象は、外部providerの有効なAPI keyを使わずに作った401ログです。正常応答ログやDashboard上の表示、List Logsでのfilter、DatasetsやEvaluations APIとの接続は追加検証が必要です。
それでも、設計上の示唆はあります。
AI Gatewayは「ログを見る道具」で終わらせるより、評価を戻す口を最初から用意した方が価値が出ます。ユーザー評価、簡易採点、metadataを同じログに返せるようにしておくと、LLMアプリは後から改善しやすくなります。
正直、基本ログの記事を書いた時点では、AI Gatewayは「便利なプロキシ兼ログビューア」くらいに見ていました。でも patchLog を見ると、少し印象が変わります。軽量な評価ループの部品として使える余地があります。
残る検証範囲は、実ログIDをアプリ側に返す小さなWorkerを作り、UIの👍👎から patchLog へつなぐところです。そこまで動けば、AI Gatewayだけで始める最小のフィードバックループとして、もう一段具体的に評価できます。