新規投稿のお知らせを受信されたい方は、サブスクリプションをご登録ください:

当社がCloudflareのデータプラットフォームとその上にAIエージェントを構築した経緯

2026-05-28

12分で読了
この投稿はEnglishおよび한국어でも表示されます。

このコンテンツは自動機械翻訳サービスによる翻訳版であり、皆さまの便宜のために提供しています。原本の英語版と異なる誤り、省略、解釈の微妙な違いが含まれる場合があります。ご不明な点がある場合は、英語版原本をご確認ください。

Cloudflareは毎秒10億件以上のイベントを処理しています。当社のネットワークは120か国以上、330都市以上に広がっています。すべてのHTTPリクエスト、すべてのWorker呼び出し、すべてのR2読み取り操作の背後にはデータがあり、その多くが存在します。

長年にわたり、このデータに簡単にアクセスできるものではありませんでした。数十の本番用データベース、ClickHouseクラスタ、Kafkaストリーム、Google Cloudバケット、BigQueryデータセット、およびパイプラインのロングテールに存在していました。「本日サインアップしたドメインの数は、トラフィックで上位100に入っていますか?」といった単純な質問に答えるために、Cloudflareのアナリストは、どのシステムから質問するか、どのような認証情報を使用するか、どのようなクエリ言語を作成するか、サンプリングされた、新しい、あるいは7日間古いデータでした。その結果、データから情報に基づいたインサイトを得ることが困難でした。

この問題を解決するために、当社は2つの社内ツールを構築しました。Cloudflareの統合データ分析プラットフォームであるTown Lakeと、その上で動作するAIデータエージェントであるSkipperです。Town Lakeは、Cloudflareが知るすべての単一のSQLインターフェースです。そして、Skipperは、Cloudflareの誰もが簡単な英語で質問し、正しい監査可能な回答を数秒で返すことができる手段です。

このような両方を構築した経緯についてお話しします。

問題の形態

急成長中の企業で働いたことがある人なら、データの無秩序拡散がどのようなものかご存知でしょう。当社には、いくつかの特定の症状が見られました。

  1. 多すぎる別個システム。お客様の問題を調査したい製品エンジニアは、Postgresに対してアカウントのメタデータを取得し、ClickHouseで分析イベントを取得し、BigQueryに問い合わせて使用状況ログを取得し、R2では未加工ログを取得し、Kafkaトピックではリアルタイムシグナルを確認する必要があります。各システムには、独自の認証情報、独自の言語、独自の保持ポリシーがありました。

  2. サンプリングされたデータ。これはダッシュボードでは問題ありませんが、請求などのドメインでは機能しません。当社の分析パイプラインはダウンサンプルを使用し、毎秒7億件以上のイベントを処理しています。これは、分析ダッシュボードを読み込む場合には正しい動作ですが、請求書を発行するのに必要な誰かの使用量を計算しようとする場合は、まったく間違った動作です。

  3. 内部データの外部依存関係。以前の内部レポートスタックの一部は、外部ベンダーによって稼働していました。コストだけでなく、重要なデータの一部を別のクラウドに大きく依存していることもありました。

  4. データを見つけることはできなかったのです。たとえ適切な認証情報を持っていたとしても、「アカウント別の請求可能なWorkersリクエスト」の正しいテーブルが特定のClickHouseクラスターに存在し、特定のスキーマに存在し、特定のPostgresディメンションテーブルに結合されていること、および結合が必要であることを知る必要がありました顧客IDの翻訳です。属人的な知識は多すぎました。

当社にも文化的な課題がありました。データインフラストラクチャはこれまで、重要なインフラストラクチャではなく、ビジネスにサービスを提供するバックオフィス機能として扱われていたのです。

求めていたもの

私たちは、適切な権限を持つ情報を得るための社内の誰もが、Cloudflareに関する質問に答えられるようにしました。「前四半期の売上高上位100社を表示してください」、「ボット管理MLのスコアリングイベントをすべてリストアップしてください」などの質問に答えてほしいと考えました。過去48時間のスコアが0.9を超える特定の自律システム番号から来ている」、「$100を支払った顧客からの上位100件の請求サポートチケットを見つける」などです。

弊社は、必要なクエリ(請求やセキュリティ調査など)には新鮮で正確な非サンプルデータを、不要なクエリ(ダッシュボードやエクスプロイトなど)には高速でダウンサンプルしたデータを提供することを目指していました。

個人を特定できる情報(PII)を自動的に検出し、機密テーブルをデフォルトでロックダウンするなど、セキュリティとガバナンスが組み込まれたものを求めていました。すべてのアクセスは監査可能であり、ユーザーが必要なタスクに積極的に取り組んでいる時だけデータにアクセスできるように、時間制限付きのアクセス許可も与えるべきです。

当社は、Cloudflare独自のプラットフォーム上に構築することを望んでいました。R2をストレージに、Workersをコンピューティングに、Cloudflare Accessを認証に、Workflowsをオーケストレーションに。もしデータインフラストラクチャに大規模な投資をするのであれば、お客様に販売するのと同じ製品を土台として構築されることになります。

そして最終的には、SQLを知る必要のないインターフェイスを求めました。目標は、アナリストだけでなく、ネットワークを通過するデータの流れを見ることを知るための適切な権限と、社内の誰もが使用できるようにすることでした。

最後の要件こそが、Skipperの誕生です。

Town Lakeのプラットフォームが

その中核となるデータプラットフォームのアーキテクチャは、オブジェクトストレージから読み込むクエリエンジンと、ストレージをデータベースのように動作させるメタデータレイヤーを備えたデータレイクハウスです。私たちはこれをTown Lakeと呼んでいます。オースティン(テキサス州)の名前に由来するでしょう。

その最も重要な構成要素は次の通りです。

クエリーエンジン。当社は、そのためにApache Trinoを選択しました。単一のSQLクエリで、R2上のPostgresテーブル、ClickHouseテーブル、Icebergテーブルに参加することができ、中間結果を別のシステムに具体化する必要はありません。「今週のWorkersリクエストの有料顧客上位100社は何か」を尋ねるクエリを、ClickHouseでフィルタをプッシュし、Postgresのアカウントディメンションと照合し、R2の請求ロールアップに対して一度にランク付けするプランにコンパイルします。

当社のマネージドApache Icebergサービス、R2 Data Catalogは、コールドデータとウォームデータが存在する場所です。Icebergは、スキーマの進化、タイムトラベル、パーティションの進化、そしてデータの進化に伴ってデータを圧縮する能力を提供します。先週の1分単位の使用量が時間単位になり、前四半期から時間単位が日単位になります。再試行が増加するにつれて、ストレージコストは減少し、データはクエリ可能な状態に保たれます。R2のParquetファイルは、OLAPデータベースに同じデータを保持することと比較してはるかに安価です。

DataHubは当社のメタデータカタログです。すべての表、列、所有者、ラインナップのエッジ、用語集がそこにあります。ユーザーが“what's in townlake.dim.accounts”と尋ねると、DataHubは、テーブル説明、列説明、所有チーム、それを供給する上流テーブル、それを消費する下流テーブルなどの答えを提供します。

Lifeguardは、当社のアクセス制御サービスです。D1にアクセスルールを保存し、内部アクセス管理システムからユーザーとグループメンバーシップを動的に引き出し、TrinoがHTTP経由で読み込む組み合わせのJSONポリシーをレンダリングします。Lifeguardは基本的なアクセス情報もSkipperとGatewayに送信します。そのため、ユーザーはクエリ時ではなく正面玄関でブロックされます。

SkimmerはPII検出スキャナーです。継続的に実行され、すべてのテーブルのすべての列から行をサンプリングし、Workers AIを使用して、各列にPIIが含まれているかどうかを分類します。これは、2つのパスで行われます。1つ目は、列ごとの高速分類子です。そして、何かにフラグが立てられた場合、エージェントの2回目のパスが、テーブルのコンテキストを取得し、Trinoに直接クエリーを問い合わせて検証することができます。検出結果はDataHubとLifeguardの許可リストに流れ、ヒューマンインザループレビューが可能となります。

Transformerは、Workflows上に構築されたELT(抽出、読み込み、変換)エンジンです。ユーザーは、YAMLフロントマッター(ターゲットテーブル、具体化モード、依存関係、スケジュール)を使用して、SQL変換のDirected Acycle Graph(DAG)を定義します。Transformerはグラフをコンパイルし、Trino上で実行します。ステートはDurable Objectsで管理され、定義はR2に保存され、実行履歴はD1で実行されます。

取り込みは、運用システムからレイクへの橋渡しです。オーケストレーターは長期間維持されるKubernetesデプロイメントとして実行され、パイプライン設定を読み取り、短時間のWorkerジョブを生成してPostgresまたはClickHouseから抽出し、Parquetに変換し、IcebergテーブルとしてR2に読み込みます。各パイプラインは、完全置き換えまたは増分追加のいずれかとして実行されます。

デフォルトでクローズ:ガバナンス:構築

統合データプラットフォームを構築するときによく懸念されるのは、大規模な機密データ領域を構築しただけということです。これに対する従来の答えは、「デフォルトでオープン、例外では制限する」というものでした。すべてへのアクセスを許可し、誰かが気づいたときに機密テーブルを監査し、ロックダウンするのです。

Town Lakeは逆のアプローチをとります。レビューが完了するまで、テーブルはクエリーにアクセスできません。新しいデータベースがTrinoに接続されると、新しいテーブルが作成されると、Skimmerはそれをスキャンし、列を分類し、中央許可リストに保留中として登録します。レビュー担当者がテーブルとその中の特定の列を承認するまで、ユーザーはそのテーブルに対してクエリを実行できません。これは苦痛に聞こえますが、2つの点を除いては存在します。

まず、自動化されています。Skimmerの分類機能は比較的優れたものです:明白なPII(メール、IP、名前、電話番号)や、明らかでない機密データのロングテール(特定のプレフィックスに一致するAPIトークン、ユーザーに追跡できる不透明なID)を捕捉します。レビュー担当者は検出された内容を確認し、承認、上書き、または却下します。ほとんどのレビューは数秒で完了します。

2つ目は、ワークフローがセルフサーブであることです。アクセス権のないテーブルに対してクエリを実行した場合、エラーメッセージは「permission denied (アクセス許可が拒否)」ではありません。「この表はレビューが必要です。ここをクリックしてリクエストしてください」というものです。AIエージェントのSkipperは、適切なRBACグループを提案し、そこに直接リンクします。

当社では、スキーマの検出とデータアクセスを分離しています。ユーザーはどのテーブルが存在するかを見ることができますが、レビューされていない列はDESCRIBESHOW COLUMNS、そしてSELECT *から非表示になっています。この微妙な区別は、重要です。レビューされていない新しい列が、承認されたテーブルの残りの部分に構築された既存のダッシュボードを壊すことがないということです。

PIIはセッションごとにオプトインします。Trinoは、デフォルトで機密列が画面に表示される前に編集して表示します。生の個人を特定できる情報(詐欺行為など)に対する正当なニーズがある場合(詐欺の調査など)、セッションをオンにすると、アクセス許可が確認され、編集が解除されます。裏返せば、すべてのクエリーがログ記録されます。

スキパー:AIデータエージェント

クエリーエンジンだけでは不十分です。SQLは依然としてバリアであり、何万というテーブルのうちどのテーブルをクエリーするかを知ることもできます。正規のスキーマを知る必要があるのです。

Skipperは、自然言語の質問から検証済みの回答まで、同社の実際のデータ、コード、組織的な知識に基づいた会話型AIエージェントに関する当社の見解です。Town Lakeの上、そして開発者向けプラットフォーム(Workers、Workers AI、Durable Objects、D1、R2、Workflows、KV)の上に構築しました。

インターフェースはチャットボックスです。質問する:

過去30日間のR2ストレージコスト別上位10社の顧客と、その前の30日間との変化を表示してください。

Skipperは適切なテーブルを見つけ(DataHub検索)、そのスキーマとラインナップをプルし、SQLを書き、Trinoに送信し、結果をポーリングして、テーブルまたはチャートを表示します。フォローアップ:

次は地域ごとに分類し、内部のCloudflareアカウントは無視してください。

コンテキストを伝送し、クエリを微調整し、再実行します。例えば、結合がゼロ行を生成したり、フィルターが期待したものを除外したりするなど、何かがおかしいと思われる場合、Skipperはクローズドループ推論において調査、調整、再試行を行います。困難だったのは、適切なコンテキストを保つことでした。

Skipperでは、チャートをダッシュボードにパッケージ化して社内で共有し、他の社内アプリケーションに埋め込むこともできます。また、Transformerを介してトランスフォーメーショングラフを作成したり、Lifeguardを介してアクセスや許可をチェックするためのツールもあります。

Skipperは、ユーザーがどこにいても対応します。これらのツールはすべて、Workers AIを活用した組み込み型のエージェント機能に支えられたWorkerを通じて利用できます。裏返せば、社内ユーザーの多くがローカルエージェントフローで作業しており、SkipperのツールはMCPサーバー経由でも利用可能であることです。

コンテキストの層

LLMは、SQLプロンプトとテーブル名のリストを与えられると、ジョインのハルシネーションを起こしたり、列を誤用したり、完全に間違った数値を生成したりする可能性があります。初期の実験中に、私たちはこのことを痛感しました。この問題を修正するのは、モデルが検索時にモデルが使える、複数のレイヤーに基づいたコンテキストを作成することです。

レイヤー1:スキーマと使用状況メタデータDataHubは、すべての列、すべてのタイプ、すべてのプライマリキー、すべてのテーブルのすべての外部キーを把握しています。また、過去のクエリーパターンから、どのテーブルが一般的に結合されているかも把握できます。Skipperのsearch_datasetsツールとget_entity_detailsツールは、これを直接表示します。

レイヤー2:人間によるアノテーション。dim.accountsを所有するチームが"アカウントレベルのエンティティ。account_idごとに1行。すべてのアカウントが1つの顧客に属している(customer_id FK)"のような説明を書くと、その説明はDataHubに存在し、最終的にSkipperのコンテキストに反映されます。キュレートされたマーク検証済みテーブルのようなタグは、スキパーがゼロトラストスペースより優先するべきです。

レイヤー3:コードから得られるナレッジ。最も価値のあるコンテキストの一部は、どのカタログにもありません。それは、テーブルを生成するSQLにあります。Transformerパイプラインは、実行が成功するたびに、ノードごとの.meta.jsonドキュメントをDataHubに出力します。つまり、Skipperがfct.billings_allocatedを見ると、スキーマだけでなく、dim.accountsdim.customersseed.product_classificationから構築された事前に結合されたファクトテーブルであり、そのalloc_amount列がbilled_amount / 12 for annual; billed_amount for monthlyとして計算されることがわかります。これは、正しい答えと確実に間違った答えを区別するような微妙な違いです。

レイヤー4:厳選されたデータモデル。Cloudflareでは、請求、顧客、アカウント、ゾーンに関する考え方を説明した短い人間が書いた文書(「キュレーション済み」とタグ付けされたテーブルを優先します。scratch_r2や「内部」とタグ付けされたテーブルを回避してください。自然言語ではなく、データモデルの用語(例:「請求製品売上」)で検索してください。」これらは、質問が一致した場合にエージェントが取得できるMCPリソースとして表示されます。

レイヤー5:ランタイムの洞察。他のすべてが失敗した場合、SkipperはTrinoへのライブクエリーを発行できます:DESCRIBE table, SELECT DISTINCT col LIMIT 20, SELECT COUNT(*)。ランタイムコンテキストが高価なため、これらはあまり使用していませんが、システムの他の部分を堅牢にするセーフティネットです。

Skipper as MCP:コードモード

1つの具体的な実装の詳細を取り上げる価値があると思います。これはCloudflareならではの形のソリューションだからです。

ツールを備えたAIエージェントを構築する際の標準的なパターンは、プロンプトでツールを定義し、モデルにツールを1つずつ呼び出しさせ、レスポンスを解析し、実行し、結果を返すことです。これは問題ありませんが、発表がややこしく、5つのツールを使用したワークフローは5つのモデルの往復であり、それぞれがコンテキストを再確立する必要があります。

当社のMCPサーバーでは、コードモードを使用しています。30もの個別ツールを定義する代わりに、私たちは検索実行の2つのツールを公開します。モデルは、ツールセット全体をプログラム的に呼び出すJavaScriptスニペットを書きます。

const datasets = await skipper.search_datasets({ query: "billing product revenue" })
const queryId = await skipper.start_query({ sql: "SELECT ..." })
const results = await skipper.fetch_results({ queryId, mode: "inject" })
return skipper.create_chart({ chartType: "bar", data: results.rows, ... })

そのJavaScriptは、WorkerLoaderを経由して、サンドボックス化されたDynamic Worker分離で実行されます。モデルは、複雑な複数ステップのワークフローを1回のラウンドトリップで、すでに非常に慣れ親しんでいる言語で表現することができます。より速く、より安価で、生成されるワークフローはコードとして監査可能です。

セキュリティモデルはデータモデル

Skipperの処理はすべて、呼び出し元のユーザーとして実行されます。テーブルへのアクセス権限がない場合、Skipperは代わりにクエリを実行できません。PIIの送信を要求すると、許可が確認されます。保存したクエリがチームメンバーと共有される場合、グループメンバーシップが変更されるため、保存時ではなく閲覧時にアクセスが確認されます。

共有ダッシュボードには独自のひねりがあります。一つのプレースホルダーdivとスクリプトタグで、あらゆるCloudflareツールに埋め込むことができます。

<div data-skipper-dashboard="dash-123"></div>
<script src="https://skipper.cloudflare.com/embed.js" async></script>

iframeはコンテンツに合わせて自動サイズ変更します。コンテンツセキュリティポリシー(CSP)frame-ancestors は、企業ドメイン外からの埋め込みをブロックします。Cloudflare Accessは依然としてiframeコンテンツにアクセスできます。そのため、認証されていない閲覧者はデータを見るのではなく、iframeでAccessログインページにアクセスします。所有者以外の閲覧者は、元のテーブルに対してチェックされます。アクセス権限がない場合は、リクエストするために適切なグループに指し示されます。

力となるもの:真に迅速な回答

請求に関する問い合わせ。これが本来のユースケースなのです。Cloudflareの請求可能な使用量ダッシュボードは、従量課金制のユーザーの明確な支払い額を表示する顧客向けダッシュボードで、従量課金パイプラインで稼働しています。従量課金パイプラインは、Trinoを介してクエリされるR2のIcebergテーブルのセットです。ダッシュボードのAPIは、請求システムが使用するのと同じコンパクトな(日付、アカウント_id、metric_name、使用状況)行を引き出すため、ダッシュボードの数値は請求書の数値と一致します。

Town Lakeが提供するすべてのクエリのうち、請求関連のクエリは、最近の測定期間に324人の異なるCloudflare従業員からの91,760件のクエリを占めています。顧客ごとの収益ロールアップを計算するために使われていた200~300行のレガシーSQLクエリーは、現在は5行になっています。

ビジネスインテリジェンス。「売上高上位100社」という質問は、今ではSkipperで約3秒になっています。「本日登録したドメイン数が上位100位に入っています」も同様です。Jiraチケットを提出するために使用したデータ関連の質問のほとんどがこれに該当します。

セキュリティ分析。当社のボット管理チームはTown Lakeを使って、自律システム番号と地域によってフィルタリングされた過去48時間のスコアが0.9を超える機械学習のスコアリングイベントにクエリを実行します。脅威リサーチャーは、それに独自のクエリーツールキットを構築しています。Trust & Safetyが信号をプルして、警察の不正行為を阻止。

カスタマーサポート。「$100以上を費やした顧客からの請求サポートチケット上位100件を探す」は、以前は複数日にわたるプロジェクトでした。今回はSkipperクエリです。

Cloudflareが学んだこと

いくつかのことに驚かされました。

プロンプトが少ないほど効果的です。Skipperの初期バージョンには、精巧で規範的なシステムプロンプトがありました。「まず、search_datasetsを使用します。次に、get_entity_detailsを使用します。その後、必要に応じてlist_schema_fieldsを使用します...」品質は低下しました。このモデルは、分析的なワークフローに関する推論に長けています。細かく管理する必要はありません。私たちは、規範的なプロンプトを高レベルのガイダンスに置き換え、モデルに独自の道を選択できるようにしました。結果は改善されました。

ツール重複は有害です。当社は当初、あらゆるツールのあらゆる亜種を公開しました。3つの異なる「フェッチ結果」ツール、2つの「検索」ツール、複数の「リスト」ツールです。モデルは混乱し、間違ったモデルを呼び出してしまいます。統合したのです。現在、fetch_results には、3つの個別のツールではなく、モード パラメーター (インジェクト / 表示 / 両方) があります。すべてのツールには、単一の存在理由があります。

メタデータではなく、コードが意味を捉えます。最大の精度向上は、スキーマだけでなく、テーブルを生成する実際のSQLを取り込み始めたときです。customer_typeという列は、contractpaygofreeという値を持っており、どちらのコンテキストでも同じように見えますが、SQLは、Salesforceデータが不足している場合、customer_typeのデフォルトがpaygoであることを示しています。この種のコンテキストは列説明には決してありません。

メモリは、予想以上に重要です。「このようにXをフィルタリングしなければならない」または「Yのタグが付けられたテーブルを無視する」ような修正のロングテールがあります。メモリ層がなければ、エージェントはこれらの会話を再発見し、再学習します。一つを使えば、チームが実際に尋ねる質問を繰り返し、より良いものになります。

つまらないインフラストラクチャは、難しい部分です。Trino + Icebergは新しい技術ではありません。行ごとのアクセス制御、デフォルトでクローズされたテーブルの許可リスト、クエリの監査、タイムバウンドの認証情報、PIIの検出、識別情報の取り込み、スキーマの進化など、つまらない作業なのです。これらは、データプラットフォームを実際に使用しても安全であるものにします。

今後の展開は?

エージェント対象領域を拡大しています。SkipperはすでにMCPサーバーとして、それをサポートするIDEに統合されています。次のステップは、社内チャットおよびチケットシステムとのより深い統合です。これにより、「データを尋ねる」ことは、インシデントのデバッグ、プロジェクトの範囲設定、または仮説の健全性チェックを行う人にとって、自然な最初の動きとなるようにします。

Transformersパイプラインには、多額の投資を行っています。Cloudflareのすべてのチームが、いくつかのSQLファイルと.meta.json記述を含むキュレートされたデータセットを構築し、Workflowとしてデプロイし、自動的にスケジュールおよび監視され、追加作業なしでDataHubとSkipperに表示されるようにすることが目標です。アイデアは、セルフサービスデータエンジニアリングであり、セルフサービスソフトウェアエンジニアリングと同じ形です。

R2 SQL、Cloudflareのサーバーレスな分散型分析クエリエンジンは、日を増すごとに堅牢化していきます。機能セットの拡大に伴い、Town Lakeのワークフローの多くをCloudflareに移行する予定です。

次の画期的な製品は、データを見て誰も見ない人が出現するという賭けに賭けています。Town Lakeは、ユーザーがそれを見つけられるようにする方法のことです。

Cloudflareは企業ネットワーク全体を保護し、お客様がインターネット規模のアプリケーションを効率的に構築し、あらゆるWebサイトやインターネットアプリケーションを高速化し、DDoS攻撃を退けハッカーの侵入を防ぎゼロトラスト導入を推進できるようお手伝いしています。

ご使用のデバイスから1.1.1.1 にアクセスし、インターネットを高速化し安全性を高めるCloudflareの無料アプリをご利用ください。

より良いインターネットの構築支援という当社の使命について、詳しくはこちらをご覧ください。新たなキャリアの方向性を模索中の方は、当社の求人情報をご覧ください。
エンジニアリング分析開発者ストレージAIデータプラットフォーム

Xでフォロー

Cloudflare|@cloudflare

関連ブログ投稿