インフラ - TECH PLAY - TECH PLAY

TECH PLAY

インフラ

イベント

マガジン

技術ブログ

本ブログは株式会社 PLAY プラットフォーム本部 技術基盤部 技術推進グループ テックリード 市川 賢 様 の監修のもと、アマゾン ウェブ サービス ジャパン合同会社 金目 健二が執筆いたしました。 AI エージェントの運用における課題 生成 AI の普及により、インシデント対応に AI エージェントを利用する取り組みが各社で進められております。一方で、検証環境では有用性が確認できても、実際に本番運用へ組み込む段階になると、エージェントの分析精度とは別のところで検討が止まるケースが見られます。「どこまでをエージェントに任せてよいのか」が組織内で合意されていないこと、複数のプロダクトを持つ組織において「誰がエージェントの動作を定義するのか」が決まっていないことがその要因です。この 2 点が定まらないまま精度の議論を進めても、組織としての運用には結びつきません。 本記事では、株式会社 PLAY(以下、PLAY)が AWS DevOps Agent (以下、DevOps Agent)を全社共通のインシデント対応基盤として利用するにあたり、決めた 2 つの設計(委譲範囲、組織) をご紹介します。実装の詳細は PLAY の技術ブログ「 AWS DevOps Agent を活用したインシデント自動分析基盤 」にて公開されておりますので、本記事ではそこで語られていない「なぜその設計を選んだのか」と、公開後に進んだ展開を中心にお伝えします。 PLAY が取り組んだこと PLAY は、動画配信プラットフォームをはじめとする動画ソリューションを提供する企業です。「見たい人」と「見せたい人」がつながる世界をつくるためには、配信基盤に加え、コミュニケーションやインタラクション、認証や課金、視聴スタイルの変化に合わせた機能が必要となります。それぞれのコンテンツに適した仕組みを作り続けることをミッションとする PLAY では、性質の異なる複数のプロダクトが並行して稼働しており、その安定運用が事業の前提となっております。 本記事で取り上げる基盤は、プラットフォーム本部 技術基盤部 技術推進グループが中心となって構築しました。導入は 1 プロダクトから始まり、2026 年 9 月現在は 4 プロダクトと 1 つのソリューション案件で稼働しております。さらに 2 プロダクトで導入準備が進められております。 DevOps Agent は、AWS・マルチクラウド・オンプレミスを横断する運用支援エージェントです。インシデントの根本原因分析、予防的な対応、オンデマンドの SRE タスクを担います。マネージドサービスとして提供されているため、エージェント本体の実行環境やモデルを自前で構築する必要がなく、運用タスクに閉じた用途であれば短期間で利用を開始することができます。 PLAY においても、まず動かしてみるところから取り組みが始まりました。技術推進グループ テックリードの市川 様は「AWS DevOps Agent 自体は高い精度でインシデントを分析してくれる優秀なサービス」と述べています。分析精度は当初から十分な水準にありましたが、既存の運用フローにそのまま組み込めたわけではありませんでした。何が課題でどう解決していったのかを、次節から記します。 手作業で行われていた一次対応 本番環境でエラーが発生すると Slack にアラートが通知されます。エンジニアがログを確認し、コードを調査し、GitHub に Issue を起票し、修正を行う、という一連の作業を PLAY では毎回手動で行っておりました。一次調査だけで 1 件あたり 15 分から 1 時間を要しており、エラーの発生頻度が高い環境では、同様のエラーが発生するたびに同じコストが掛かる状況でした。 これらの作業をインシデント対応基盤に任せることを検討するにあたり、PLAY は 2 つの課題に直面しました。  課題  課題の実体 ① どこまで AI エージェントに任せてよいか分からない(委譲範囲の決定) 全工程を渡すことには不安がある。一方で人が全工程を行うと運用が回らない ② 複数のプロダクトにどのように展開するのか(組織間の分担範囲の決定) 調査手順はプロダクトの中身を知る担当者にしか書けない。一方で各チームに仕組みまで作らせると重複が生じる ①に対する答えが設計 1、②に対する答えが設計 2 となります。 これらの設計を反映した基盤の概要構成を以下に記します。 図 1: インシデント対応基盤 概要構成図 当基盤は、共通基盤チームが構築・運用する部分(図中「共通基盤」)と、各プロダクトチームが自身の AWS アカウントで運用する部分(図中「各プロダクトチームの AWS アカウント」)に分かれており、アラートの発生から人の判断までは次の流れで処理されます。 Slack のエラー通知(各種監視サービスからアラートが Slack へ飛ぶ)、または Amazon CloudWatch Alarm を共通基盤の AWS Lambda が受信し、正規化する(①) Amazon DynamoDB 上の過去アラートと SimHash を基に照合し、類似アラートかどうかを判定する(②) 類似アラートと判定された場合は、関連する Issue を元の Slack スレッドに返信し、DevOps Agent は起動しない(③) 類似アラートでない場合は、 AWS Systems Manager Parameter Store のルーティング設定を参照し(④)、該当サービスの AWS アカウントで動作する DevOps Agent へ Webhook で調査を依頼する(⑤) DevOps Agent はプロダクトチームが記述した調査スキルに従い、CloudWatch Logs、GitHub、New Relic を調査する(⑥) 調査の過程で、MCP 基盤を経由して Issue 番号の記録や過去インシデントの参照を行う(⑦) 調査の進捗と結果は、元の Slack スレッドへ 3 段階(開始 → 原因候補 → 完了)で報告される(⑧) 担当エンジニアが結果を確認し、修正に着手するかを判断する(⑨) この構成のうち、⑥〜⑨で「DevOps Agent がどこまでを担い、人がどこから引き継ぐか」を決めたのが設計 1(委譲範囲)、共通基盤と各プロダクトの AWS アカウントの境目で「誰が何を作るか」を決めたのが設計 2(組織)です。 設計 1: 委譲範囲の決め方 本節は、インシデント対応の自動化範囲についてチーム内の合意が取れていない方に向けた内容となります。 一次トリアージへの限定 PLAY が最終的に目指しているのは、DevOps Agent を起点に原因調査を行い、必要であればコード修正と Pull Request の作成まで自動で行い、承認を経てマージ、デプロイまでつなげることです。しかし、最初からそこまでを対象とはしませんでした。市川 様は次のように述べています。 「最初からそこまでを対象にすると、調査の精度が固まっていない段階で修正やデプロイまで自動化することになり、運用に乗せるのは難しいと考えました。そこで、まずは一次トリアージ(原因の調査と Issue 起票)に範囲を絞り、その精度を高めることに注力する判断をしました。」 全工程を渡すのでも、人が全工程を行うのでもなく、一次トリアージという範囲を切り出して任せる、という判断です。当初の線引きは「原因の調査と Issue 起票まで」でした。運用を重ねる中で、調査結果が Slack スレッドに返ってきた時点で対応が完結するケースが多いことが分かり、Issue は「起票されたが確認して閉じるだけ」になりがちでした。そのため、Issue が必要な場合のみ起票するよう DevOps Agent のスキルで調整しました。プロダクトによっては 7〜8 割のアラートが Slack 上の返信で完結しているとのことです。 現在の役割分担は以下の通りです。  担当  範囲  共通基盤(共通 AWS アカウント) アラートの受信と正規化、過去の類似エラーとの照合、エージェントへの受け渡し  DevOps Agent(各プロダクト AWS アカウント) ログの確認、コードの調査、根本原因の分析、調査結果の Slack スレッドへの報告、必要な場合の Issue 起票  人 調査結果の確認、修正に着手するかの判断、Pull Request のレビューとマージ、対応の優先度判断  修正の自動化(PLAY 自前の仕組み) Issue 上で @play fix とコメントされると、自動でコードレビューとコード修正を行い Pull Request を作成 エージェントが担うのは調査と報告までであり、修正に進むかどうかの判断は原則として人が行います。人が Issue に @play fix とコメントすることで、PLAY が自前で用意したコードレビュー・修正の仕組みが Pull Request を作成し、そのレビューとマージは人が行う流れとなっております。なお、調査結果の確度が高い一部のケースでは、この @play fix コメントを DevOps Agent 自身が行う運用も始まっております。詳細は「今後の展望」にて記します。 この線引きにより、エージェントの分析が的を外した場合でも業務に影響が及びません。Slack スレッドの報告を読んだ人が気づくことができます。一方、最も時間を要していた「どこを見るべきか探す」工程は人の手を離れることとなります。 スキルファイルによる範囲の明文化 DevOps Agent の動作は、マークダウン形式のスキルファイルで定義します。PLAY はこのスキルファイルに、エージェントに任せる範囲をそのまま記述しております。調査から起票・通知までを 9 つのステップに分け、それぞれで呼び出すツールを明示したものです。  Step  内容  使用するツール  1  エラー情報のパース + 「調査開始」の Slack 通知  slack_post_thread  2  対象リポジトリの特定  –  3  ナレッジベース検索  knowledge_base_query  4  デプロイとの相関確認  –  5  コードの調査 + 「原因候補特定」の Slack 通知  slack_post_thread  6  GitHub Issue / Backlog 課題の作成(必要な場合)  add_issue 等  7 「調査完了」の Slack 通知  slack_post_thread  8  Issue 番号のコールバック(起票した場合)  issue_callback  9  結果の報告  – ステップに分けておくことで、エージェントが何をどの順で行うかがドキュメントとして残ります。任せる範囲が曖昧なまま動かした場合、想定外の動作が起きた際に原因を追うことが困難となります。ステップ単位で定義することは、動作を安定させることに加え、何が起きたかを後から説明できる状態を作ることにもつながります。前述の「不要な Issue を作らない」という調整も、Step 6 の条件をスキルに書き足すことで実現しております。 スキルの具体性と調査の質 スキルファイルの書き方について、PLAY が運用を通じて得た知見があります。技術ブログには次のように記されています。 「スキルファイルの書き方そのものが調査の精度とスピードに直結する。Agent 自体は強力でも、スキルが曖昧だと毎回ゼロから調査範囲を探索することになり、結果として『分析に時間がかかる (= 課金が増える)』『見るべき場所を取りこぼす』といった問題が起きる。」 特に効果があったのは、「このアラートやエラーパターンが来たら、どこを最初に見に行くべきか」を具体的に記述することでした。 CloudWatch ロググループ — 「xxx というエラーメッセージが来たら、まず /aws/foo-handler のログを ${時刻} – 5 分 の範囲で確認する」のように、エラー文字列とロググループの対応を列挙する GitHub リポジトリとパス — 「このアラートの対象はモノレポの services/foo 配下なので、コード調査は該当リポジトリの services/foo に絞る」のように、ディレクトリ単位まで明示する メトリクス — 該当時刻のメトリクスを確認する場合は、ダッシュボードの URL や関連クエリも併記する この点は実装前の想定と最も異なっていた部分であったと、市川 様は次のように述べています。 「調査対象のモジュールに対して、どの CloudWatch のロググループや New Relic のエンティティが対応するかというマッピングをスキルに記載しておくと、エージェントが迷わず調査を進められます。エージェント自体の能力よりも、この対応関係をどれだけ具体的に渡せるかが結果を左右する、というのが実装前の想定と違った点でした。」 このマッピングは、PLAY のスキルでは「モジュールマップ」として次のような形で記述されております。以下はサービス名や識別子を架空のものに置き換え、内容を汎化した抜粋です。 モジュール リポジトリ CloudWatch ロググループ(本番) New Relic APM delivery-api <git>/delivery-api /ecs/delivery-api-blue, /ecs/delivery-api-green(用途別に分かれた複数の ECS サービスがこの 2 つを共有する) prod-delivery-api manifest-service <git>/manifest-service /ecs/manifest-service-{blue,green} に加え、Lambda 側 /aws/lambda/prod-manifest-service-Function-<SUFFIX> も稼働中。両方を確認する prod-manifest-service(アラート条件なし) edge-handler <git>/edge-handler /aws/lambda/us-east-1.edge-handler(Lambda@Edge のためリージョンが us-east-1) なし(CloudWatch Logs が唯一のテレメトリ) 表の各行には、リソースの対応関係だけでなく「ECS と Lambda の両方で動いており片方だけ見て再現しないと結論しない」、「APM が入っていないモジュールがある」、「Blue/Green のどちらが現用系かはタスク数と ALB の重みで判定する」といった、エージェントが単独では誤りやすい事実が注記として書き込まれております。命名規則から推測させると必ず間違える点を、稼働環境で確認した事実として先に潰しておく、というのがこのスキルの書き方です。 効果は調査時間に表れております。スキルをほとんど用意していなかった導入初期には 1 件の調査に 15〜20 分程度を要しておりましたが、スキルを具体化した現在は 5〜10 分程度で完了しております(初期の計測は数回分のため、参考値となります)。 Slack スレッドでの判断 エージェントに調査を任せると、人は「今どこを見ているのか」を把握しづらくなります。PLAY では、人が判断を行う場所を Slack のスレッドに置きました。 アラートが通知されたスレッドには、そのとき誰が何を話したかという文脈が残っております。調査結果が別の場所に投稿されると、その文脈から切り離されてしまいます。加えて、PLAY ではどのプロダクトにおいても、アラートが通知されたスレッドに担当者が調査の進捗を書き込んでいく運用が既に定着しておりました。エージェントの調査結果も同じスレッドに返すことで、担当者は普段と同じ場所を見るだけでよく、新しいツールや手順を覚えることなくエージェントを既存の運用に取り込むことができます。問題が起きた元のスレッドに結果が返ることは、文脈の維持と既存運用との接続の両面で重要でした。 調査の進捗は 3 段階で元のスレッドに返されます。 調査開始時 — 対象のエラーとリポジトリを提示 原因候補の特定時 — 対象ファイルと行番号、原因候補の説明 調査完了時 — 根本原因、影響のあるコード、関連コミット、修正の方針、Issue を作成した場合はその URL 段階を分けている理由は、人が途中で介入できるようにするためです。原因候補の時点で見当違いだと分かれば、完了を待たずに人が動くことができます。前述の通り、多くのアラートはこの 3 段階目の報告を読んだ時点で対応が完結しております。 原因を特定できなかった場合の出力 実運用において分かれ目となったのは、エージェントが答えを出せなかった際の出力を事前に決めておくことでした。 AI エージェントの導入検討では「どれくらい正しく答えられるか」に議論が集まりがちです。PLAY の運用では、プロダクトによっては 7〜8 割のケースで根本原因の特定に至っております。一方、運用に乗せた後は、残りの正解を出せなかったケースの扱いが品質を左右します。何も返ってこなかったり、自信のない推測だけが返ってきたりすると、人はエージェントの出力を信用しなくなります。 PLAY のスキルファイルには、根本原因を特定できなかった場合の専用フォーマットが用意されております。返す内容は以下の 3 点です。 調査内容 — 確認したファイル、参照したログ、確認したメトリクス 判明した事項 — 根本原因が不明であっても、分かった事実は記載する 修正の方針 — 次のステップ、追加で調査すべき領域 「分かりませんでした」で終わらせず、人が続きを引き継げる状態で返す設計です。特定に至らなかったケースでも調査済みの範囲が記録されているため、人による再調査を初手から始める必要がありません。任せる範囲を決めるということは、うまくいったときの範囲だけでなく、うまくいかなかったときの振る舞いまで決めることであったと言えます。 設計 2: 組織での分担 本節は、複数のプロダクトを抱える SRE / プラットフォームチームや CCoE の方に向けた内容となります。 難所となったサービス固有の調査手順 基盤を作る過程で分かったことは、DevOps Agent の活用において本当に難しいのは各サービス固有の調査スキルを書く部分である、という点でした。 どのリポジトリを見るのか、どの CloudWatch ロググループを参照するのか、どの New Relic エンティティが対応するのか、どの GitHub Organization に Issue を起票するのか。いずれもサービスの中身を最もよく知っている担当者にしか書けません。共通基盤を作るチームが全プロダクトのドメイン知識を持つことは現実的ではありません。 一方で、それ以外の部分は横展開が可能です。Slack イベントの受信、ルーティング、重複したアラートの排除、MCP によるツール提供、Slack スレッドへの戻し制御は、プロダクトが変わっても同じ仕組みで対応できます。 「基盤の仕組み」と「ドメイン知識」を分けることが、PLAY の組織設計の起点となりました。 共通基盤チームとプロダクトチームの線引き 担当 やること 共通基盤チーム Slack / CloudWatch の受信、ルーティング、重複アラート排除、MCP 基盤によるツール提供、Slack スレッドへの返信 各プロダクトチーム 自チームの AWS アカウントに DevOps Agent の Agent Space を作成、サービス固有の調査スキル(マークダウン)を記述、Webhook を共通基盤に向ける この分担により、プロダクトチームは自分たちが詳しい部分の記述に集中でき、共通基盤側のインフラ運用を意識する必要がありません。共通基盤チームも、プロダクト内部のドメイン知識を持たずに運用を成り立たせることができます。どちらのチームも自分が知っていることだけを担当すれば動く、という状態を作ったことが、複数プロダクトへの展開を可能にしました。 設計 1 で述べたスキルファイルを書くのはプロダクトチームであり、それに沿ってエージェントが動く土台を用意するのが共通基盤チームという関係になります。 スキルを記述したプロダクトチームは次のように述べています。 「これまでチーム内で統一してきた調査方法や報告フォーマットに、各システムの技術スタックや AWS のインフラ構成を加えてスキルに書き起こしたもので、いずれも普段から自分たちが持っている情報だったため自前で書き切れました。調査対象のシステムさえずれていなければ調査内容は基本的に信頼しており、報告される【確度】を見て、必要な部分だけ人が追加で事実確認を行っています。」 ここで述べられている確度は、調査結果の根本原因に付与される【確定 / 有力 / 推定】のラベルです。権限や経路の制約(参照する MCP がないなど)により取得できない情報がある場合は確度が下がるようにスキルで定義されており、人はこのラベルを見て事実確認の要否を判断しております。 アカウント分離による責任範囲の明確化 各プロダクトチームの DevOps Agent は、その AWS アカウントで動作します。 権限とリソースが分離されることに加え、責任の範囲がアカウント単位で定まります。どのサービスがどれだけエージェントを利用したかがアカウントごとに分かれるため、「誰の責任範囲か」を運用の都度議論する必要がありません。組織で共有する基盤ほど、この境目を仕組みで決めておくことの価値は高まります。 新規プロダクトの追加手順 新しいプロダクトを基盤に載せる手順は以下の 3 点です。 プロダクトチームが自アカウントに Agent Space を作成する プロダクトチームがサービス固有の調査スキルを記述する 共通基盤チームがルーティング設定に Webhook URL を 1 行追加する ルーティング設定は AWS Systems Manager Parameter Store の SecureString で管理されており、Lambda の再デプロイなしで変更することができます。Slack のチャンネル単位、CloudWatch のアラーム単位で、呼び出す DevOps Agent を切り替えることが可能です。 共通基盤側の作業がエントリ 1 件の追加で済むため、展開のボトルネックはプロダクトチーム側のスキル記述のみとなります。1 プロダクトから始まった導入は、現在 4 プロダクトと 1 つのソリューション案件に広がり、2 プロダクトで導入準備が進められております。 2 つの設計を支える作り込み(PLAY 技術ブログ公開時点) 2 つの設計を実運用に乗せるために、PLAY が自前で用意した部分をご紹介します。本節は技術ブログ公開時点(2026 年 6 月)の構成であり、その後の変化は「その後の発展」にて記します。 注記: 本節の内容は、執筆時点の DevOps Agent の機能を前提とした補い方です。DevOps Agent 側の機能追加により、将来的には自前で用意する必要がなくなる部分もあります。設計 1・設計 2 が経路や機能の変化に依存しないのに対し、本節は時点依存の内容としてお読みください。 MCP サーバーによる外部連携 DevOps Agent はセキュリティ上の理由から、スキルから直接 HTTP リクエストを発行したり Lambda を呼び出したりすることができません。この制約は実装して初めて分かったことの 1 つであったと、市川 様は次のように述べています。 「GA 直後で情報が少ない中、試行錯誤しながら触っていたため、当初はスキルの中で DevOps Agent から Slack に直接投稿するよう記述していましたが、これが動作せずしばらく詰まりました。その後、セキュリティ上の理由でスキルから直接 HTTP リクエストを発行できない仕様だと分かり、MCP サーバー経由であれば連携できると気づき、自前の MCP サーバーを作成して動作を確認しました。」 設計 1 を実現するには外部連携が必要となります。Slack スレッドへの進捗投稿、Issue 番号の記録、社内ナレッジベースの検索、Backlog への課題起票はいずれもエージェントの外側にあり、この連携を担うのが MCP (Model Context Protocol) サーバーです。技術ブログ公開時点では、PLAY は AWS Lambda の Function URL として MCP サーバーをデプロイし、DevOps Agent のコンソールでエンドポイントを登録しておりました。 自作 MCP のツール 接続先 対応する設計 slack_post_thread Slack API 設計 1(Slack スレッドでの判断) issue_callback Amazon DynamoDB 重複アラートの記録 knowledge_base_query Amazon Bedrock Knowledge Bases 過去インシデントの参照 Backlog 系(add_issue / update_issue / get_issues 等) Backlog REST API 設計 1(起票先の多様性) 自前の MCP サーバーに加え、GitHub 公式と New Relic 公式の MCP サーバーも登録しておりました。棲み分けとしては、サービス横断で必要なツールは公式の MCP サーバーをそのまま利用し、Slack スレッド返信・Backlog 起票・社内ナレッジ参照といった組織固有の要件は自前の MCP サーバーで提供する、という形です。 MCP サーバーを介した間接的な構成はセキュリティ上の制約に由来するものですが、副次的なメリットもありました。エージェントが呼び出せる操作の範囲が、MCP サーバーに登録したツールの集合として明示されることです。これは設計 1 で決めた任せる範囲を、技術的に境界づける仕組みとしても機能しております。 SimHash による重複アラートの排除 同一原因のエラーが連続して発生する環境では、同じ調査が繰り返されることとなります。人が読む報告が重複することに加え、エージェントの実行も無駄になります。 課題は、エラーメッセージの類似性をどのように判定するかでした。エラーメッセージには発生のたびに変わる要素(タイムスタンプ、PID、リクエスト ID など)が含まれるため、SHA-256 のような暗号学的ハッシュで完全一致を見ても、同じ原因のエラーを同一と判定することができません。 そこで PLAY は、類似したテキストに近いハッシュ値を生成する SimHash(Locality-Sensitive Hashing の一種)を採用しました。正規化したテキストから SimHash を計算し、ハミング距離が閾値以下のものを類似と判定します。DynamoDB 上で効率的に近傍検索を行うため、64 bit の SimHash を 4 つの 16 bit ブロックに分割し、各ブロックをグローバルセカンダリインデックス (GSI) のパーティションキーとしております。 類似と判定され、かつ過去の Issue が紐づいている場合は、Slack スレッドに「関連 Issue: #XX」を返信し、エージェントは起動しません。2 回目以降の同じエラーは、人にもエージェントにも渡さない設計です。 類似判定の手段としては、他に 2 つの選択肢がありました。1 つ目は、過去のインシデントを蓄積している Amazon Bedrock Knowledge Bases で検索する方法です。ただしこの場合、通知のたびに RetrieveAndGenerate を実行することになり、生成の部分にコストがかかるため、大量に通知が来た場合にコストが増大します。DynamoDB 上の SimHash 判定にて十分な精度が出たため、こちらを採用しております。2 つ目は、DevOps Agent 自身が持つインシデントのトリアージ機能です。この機能はルックバックウィンドウ(通常 20 分)の中で同時に起きた関連インシデントを 1 つの調査に統合したり抑制したりするもので、数か月前の過去障害との類似性を判定して抑制する用途には適していないため、採用を見送りました。 効果はチャンネルの性質により差があります。アラートのノイズが多いチャンネルでは、1 か月に 913 件のアラートのうち 719 件(78.7%)が類似判定によりスキップされました。ノイズの少ないチャンネルでは 18 件のうち 9 件(50%)でした。スキップされた分だけ、人が目を通す報告もエージェントの調査も削減されております。 調査に渡す文脈の拡充 重複と探索を削減する一方で、1 回の調査に渡すコンテキストは下記を用いることで厚くしております。 GitHub 公式 MCP サーバー — 該当リポジトリのソースコード、変更履歴、関連 Pull Request を直接参照 New Relic 公式 MCP サーバー — エラー発生時のメトリクス、トレース、Errors Inbox の情報を確認 社内ナレッジベース — Amazon Bedrock Knowledge Bases に蓄積した過去のインシデント報告を参照し、類似インシデントを踏まえて分析 SimHash の実装、GSI 設計、MCP ツールの定義コード、スキルファイル全文は PLAY の技術ブログに掲載されております。 成果 取り組み 指標 Before After 設計 1 委譲範囲 一次調査にかかる時間(1 件あたり) 人手で 15 分〜1 時間 エージェントで 5〜10 分 設計 1 委譲範囲 Slack 上の報告で対応が完結する割合 – 7〜8 割(プロダクトによる) 設計 1 委譲範囲 根本原因を特定できた割合 – 7〜8 割(プロダクトによる) 作り込み(SimHash) 類似判定によるスキップ率(1 か月) 毎回調査 78.7%(ノイズの多いチャンネル)/ 50%(ノイズの少ないチャンネル) 設計 2 組織 新規プロダクト追加時の基盤側作業 個別に作り込み ルーティング設定 1 行 「Slack 上の報告で対応が完結する割合」と「根本原因を特定できた割合」はプロダクトごとに差があり、上表は PLAY 社内で報告されている水準を記載しております。 その後の発展 技術ブログの公開後、PLAY は DevOps Agent の呼び出し経路と、エージェントが参照できるデータの範囲を広げております。 2 系統となった呼び出し経路 図 2: DevOps Agent の 2 つの呼び出し経路  経路  起点  ① 自動一次トリアージ  Slack のエラー通知、CloudWatch Alarm  ② Slack からの呼び出し  Slack App へのメンション Slack からの呼び出しである②において工夫されているのは、ユーザーが明示的にメンションする以外の導線です。PLAY では各プロダクトに問い合わせ専用の Slack チャンネルがあり、Slack ワークフローが設定されております。このワークフローに Slack App のメンションを付与しておくことで、問い合わせがそのままトリガーとなります。内容に応じてナレッジベースから回答するか、障害や問題が起こっていそうな内容であれば DevOps Agent が調査を行い、一次調査結果を Slack のスレッドに返します。 ②は Amazon Bedrock AgentCore (以下、AgentCore)Runtime 上で動作し、問い合わせ内容に応じてナレッジベースを呼ぶか DevOps Agent を呼ぶかをエージェンティックに振り分けております。②の経路では調査結果に対するフィードバックを Slack のボタンで収集しており、今後は①の自動一次トリアージにおいても同様に収集できるようにする予定です。 MCP 基盤による調査範囲の拡大 社内ツールへのアクセスを担う MCP の提供元は、全社共通の MCP 基盤へと発展しました。AgentCore Gateway を統一入口とし、DevOps Agent はここを経由してツールを呼び出します。 この基盤には、サービスのデータベース( Amazon RDS など)を参照する運用用途の MCP、プロダクトごとの業務データを参照する各プロダクト専用の MCP、外部 SaaS の情報を参照する MCP が含まれます。これらを DevOps Agent の呼び出し先としたことで、サービス内のデータを見なければ調査できない事象についても DevOps Agent で調査できるようになりました。ツールの認可は Cedar Policy で制御されており、プロダクトごとに参照できるツールの範囲を絞っております。 任せられる範囲は、エージェント自体ではなくその外側の整備によって広がっております。PLAY では、New Relic に新しいエンティティを追加した際や、セキュリティ SaaS の検知通知をこの仕組みで分析対象に加えた際に、調査対象を拡大しております。 変わらなかった 2 つの設計 経路と調査範囲が増えても、2 つの設計はそのまま機能しております。 設計 1(委譲範囲) — スキルファイルは共通のまま。Slack から呼び出した場合も、一次トリアージと同じスキルによってスレッドに調査状況が報告される 設計 2(組織) — 分担は変わらない。プロダクトチームがスキルを書き、共通基盤チームが受け口とツールを提供する 任せる範囲をスキルとして外に出し、組織の分担を仕組みで固定していたため、入口と参照先を増やすという変更を、設計を作り直すことなく吸収することができました。 今後の展望 PLAY では引き続き以下の取り組みが進められております。 対応プロダクトの拡大 — 現在 2 プロダクトで導入準備が進められております。プロダクトチーム側でスキルを準備できれば、共通基盤側はルーティング設定を 1 行追加するだけで連携が完了します。 委譲範囲の拡大 — 一次トリアージの精度が固まってきたことを受け、任せる範囲を段階的に広げております。1 つ目は自動 PR 作成です。New Relic のソースコードマップ連携でエラー箇所が行番号まで特定できる UI のバグなど、調査結果の確度が高いケースに限り、DevOps Agent が GitHub の MCP サーバー経由で Issue に @play fix とコメントし、Pull Request 作成まで人を介さずに進めております。2 つ目は、直近のデプロイが原因と判定された場合に Blue/Green デプロイメントを切り戻す自動対応です。いずれも、冒頭で述べた「コード修正から Pull Request 作成、承認を経たマージ・デプロイまで」という最終的な目標に向けた段階となります。 フィードバック収集の拡大 — 現在は Slack からの呼び出し経路で収集している調査結果へのフィードバックを、自動一次トリアージの経路においても収集できるようにする予定です。 PLAY からのコメント 株式会社 PLAY CTO 丸山 健一 様は次のように述べています。 「私が管掌する技術基盤部では、全社の業務効率化を目的として、社内ツールの開発および仕組みの整備を推進しております。今回構築したインシデント対応基盤もその一環であり、当初より複数チームへの展開を前提として設計いたしました。最初のチームへの導入後、その評価が社内に伝わり、他チームからも導入の要望が自発的に寄せられましたが、本設計により円滑な社内展開を実現できたものと考えております。今後は、お客様のプロダクト利用状況といったビジネスコンテキストを調査に加味しつつ、より多角的な観点からの分析を可能とすることを目指してまいります。」 終わりに PLAY は DevOps Agent を全社共通のインシデント対応基盤として利用するにあたり、2 つの設計を決めました。 委譲範囲 — 一次トリアージという範囲を切り出し、スキルファイルに記述し、人が判断を行う場所を Slack スレッドに置き、原因を特定できなかった場合の出力まで決めた 組織 — 「基盤の仕組み」と「ドメイン知識」を分け、共通基盤チームとプロダクトチームがそれぞれ自分の知っている範囲だけを担当する構造とした 2 つの設計は、スキルファイルという成果物でつながっております。仕様を書くのは現場であり、仕様が動く土台を用意するのは基盤チームである、という関係です。 市川 様は技術ブログの締めくくりにおいて「AI エージェントを業務に組み込むときの最大の難しさは、エージェント単体の精度よりも、『既存の運用フローやチーム構造にどう乗せるか』の部分にあると感じています。」と述べています。調査結果を人が既に見ているアラートスレッドに返すという設計 1 の判断は、この「既存の運用フローに乗せる」ことを、人の作業手順を変えずに実現した例と言えます。 DevOps Agent の分析精度は、それ単体で十分に高い水準にあります。まず動かしてみるところまでの敷居は高くありません。その精度を組織の日常業務に定着させるために必要となるのは、何を委ね、誰が書くかを決めることでした。 AWS では、AI エージェントを運用へ組み込むにあたってのご相談や、類似事例の共有を行っております。同じように AI エージェントを運用に組み込もうとされている読者の方は、下記の参考リンクもご参照ください。本記事が、AI エージェントの運用設計を検討されているお客様の一助となれば幸いです。 参考リンク AWS DevOps Agent を活用したインシデント自動分析基盤(PLAY 技術ブログ) — SimHash の実装、GSI 設計、MCP ツール定義、スキルファイル全文 AWS DevOps Agent による自律的インシデント対応 - その能力を引き出す設計のベストプラクティス -(AWS Summit Japan 2026 CNS319 登壇資料 PDF) この記事は ソリューションアーキテクト 金目、スペシャリストソリューションアーキテクト 加藤、テクニカルアカウントマネージャー 馬場が担当しました。 監修: 株式会社 PLAY プラットフォーム本部 技術基盤部 技術推進グループ テックリード 市川 賢 様
データシステム部データ基盤ブロックの小泉です。データ基盤ブロックでは、社内のさまざまな業務領域のリレーショナルデータベースを、DatastreamでBigQueryへリアルタイム連携しています。この基盤は各部門が分析やダッシュボード等で活用しており、私たちが横断的に管理しています。多くのチームが参照する基盤はクエリ費用がかさみやすく、かつ各テーブルの更新頻度を利用者側で一元的に把握することも難しいという課題がありました。 本記事では、staleness limit(データの更新頻度)の見直しによるクエリ費用削減の取り組みを紹介します。あわせて、その設定を一度きりで終わらせず、CI/CDによる自動適用と設定状況の可視化(カタログ化)まで仕組み化した内容も紹介します。 目次 目次 staleness limit(max_staleness)とは 取り組みの全体像 課題:デフォルトの更新頻度のままだと費用が膨らむ 業務に必要な更新頻度を見極める ── 利用者へのヒアリング staleness limitの調整と費用削減 手動運用の限界 設定をYAMLで宣言的に管理し、CIで自動適用する 新規テーブルの更新頻度を自動化する 設定状況をカタログで可視化する 注意点・工夫 まとめ staleness limit(max_staleness)とは まず、本記事で扱うstaleness limitを簡単に説明します。Datastreamを使ってリレーショナルデータベースの変更をBigQueryへリアルタイム連携すると、BigQuery側では変更データ(CDC)を随時マージしてテーブルを最新の状態に保ちます。このとき、どのくらいまでデータの更新を待ってよいかを決めるのが max_staleness というテーブルオプションです。Datastream側ではこの値をstaleness limit(data freshness)として設定します。 ポイントは、この値が「データの更新頻度」と「クエリ費用」のトレードオフである点です。 max_staleness を短くする(例:5分)と、常に最新に近いデータが返る一方、更新(マージ)が頻繁に走り費用が高くなる max_staleness を長くする(例:1時間)と、最大その時間だけ古いデータを許容する代わりに、更新が間引かれて費用が安くなる Datastream・BigQuery CDCにおける max_staleness の挙動については、以下の公式ドキュメントをご確認ください。 cloud.google.com 取り組みの全体像 費用削減から運用の仕組み化までをまとめると、以下の通りです。各工程の詳細は後述します。 リアルタイム連携テーブルの参照実績を確認し、利用者にヒアリングして「業務に必要なデータの更新頻度」を見極め、合意する 合意した内容にもとづき、テーブルごとに max_staleness を調整して費用を削減する max_staleness の設定をYAMLで宣言的に管理し、CIが自動でBigQueryへ適用する仕組みを構築する 新規に連携されるテーブルには、デフォルトの更新頻度が自動で付与される設計にする どのテーブルがどの更新頻度で運用されているかをカタログとして可視化する ここから各工程を詳しく紹介します。 課題:デフォルトの更新頻度のままだと費用が膨らむ Datastreamのリアルタイム連携テーブルは、デフォルトでは max_staleness が短い値(今回のケースでは5分)に設定されていました。これは「常に最新に近いデータが返る」という点ではメリットですが、その分だけバックグラウンドのマージが頻繁に走り、BigQueryのクエリ費用を押し上げる要因になっていました。 一方で、リアルタイム連携テーブルの利用実態を見てみると、必ずしも5分間隔の更新頻度を必要としていないケースが多くありました。「日次のダッシュボードで参照しているだけ」「多少古くても業務上問題ない」といった用途であれば、更新頻度を落として費用を下げられる余地があります。 そこで、利用者が本当に必要とする更新頻度を見極めた上で、 max_staleness を調整して費用を削減することにしました。 業務に必要な更新頻度を見極める ── 利用者へのヒアリング 費用削減のために更新頻度を落とすとはいえ、いきなり設定を変えてしまうと、リアルタイム性を前提に業務をしている利用者に影響が出かねません。そこで、利用者に確認して合意を得てから変更するという進め方を取りました。 まず、ヒアリングの準備として、各テーブルについて直近3か月の参照者と参照頻度をすべて洗い出し、ヒアリング用の一覧表(スプレッドシート)にまとめました。クエリの実行ログをもとに「誰が」「どのテーブルを」「どのくらいの頻度で」参照しているかを可視化することで、確認すべき相手と、更新頻度を落とせそうなテーブルの当たりを付けられるようにしました。 続いて、作成した一覧表をもとにテーブル参照者へのヒアリングを実施しました。確認範囲は、次のように広げていきました。 直近の参照者と、連携を立ち上げたときにやり取りしていた担当者へ確認する サービスアカウント経由で参照している連携元にも確認する 別プロジェクトから参照している利用者にも範囲を広げて確認する すべての参照者への確認を終え、合意を形成する この「参照実績の全数洗い出し → 一覧表化 → 参照者へのヒアリング → 合意形成」というプロセスを踏みました。その結果、各テーブルについて、業務に必要な更新頻度を利用者と見極めて合意できました。結果として、本番環境で短い更新頻度(5分)を維持したのはごく一部の数テーブルのみで、それ以外の大半のテーブルは更新頻度を延ばす方針を安全に決められました。 staleness limitの調整と費用削減 合意した内容にもとづき、 max_staleness をテーブルごとに調整しました。 max_staleness はテーブル単位で設定するオプションのため、テーブルごとに必要な更新頻度を設定できます。今回は、本番環境のテーブルは1時間、検証・開発環境のテーブルは8時間を基本とし、短い更新頻度が必要な一部のテーブルだけ5分を維持しました。 max_staleness は、既に連携済みのテーブルに対しては次のような ALTER TABLE で変更できます。 ALTER TABLE `<project>.<dataset>.< table >` SET OPTIONS (max_staleness = INTERVAL 1 HOUR); 当初は「更新頻度を5分から10分に延ばして約50%削減」という控えめな見込みを立てていましたが、ヒアリングの結果、業務上許容できる範囲まで思い切って更新頻度を延ばせることが分かりました。最終的に本番環境のテーブルは5分から1時間へ変更したため、理論上の削減率は 1 − 5/60 ≒ 約92% となり、見込みを大きく上回りました。 実際の効果は、適用前後それぞれ2か月の費用を比較して検証しました。その結果、環境別に次の費用削減を確認できました。 本番環境: 約92%削減 (5分 → 1時間) 検証環境: 約98%削減 (5分 → 8時間) 開発環境: 約98%削減 (5分 → 8時間) 本記事のタイトルにある「92%削減」は、このうち本番環境の対象テーブルでの実測値です。 なお、staleness limit(data freshness)をコンソール上で変更した場合、その値は新規に作成されるテーブルにしか反映されない点に注意が必要です。既に連携済みのテーブルには、前述の ALTER TABLE を実行する必要があります。詳細は以下をご確認ください。 cloud.google.com 手動運用の限界 費用削減そのものは達成できましたが、運用面では3つの課題が残っていました。 既存テーブルへの変更は1つずつ ALTER TABLE が必要で、依頼のたびに手作業も発生する 新規テーブルの扱いが分かりにくい:新しく連携されたテーブルの max_staleness がどうなるか把握しづらい 設定状況が見えない:各テーブルの更新頻度を一覧できる手段がない 今後も「このテーブルの更新頻度を変えたい」という依頼は継続的に発生します。そのたびに手作業でクエリを打ち、台帳もないまま設定状況を管理する運用は続けられません。 そこで、設定をコードで管理してCIで自動適用し、設定状況を誰でも見られるように可視化するところまで仕組み化することにしました。 設定をYAMLで宣言的に管理し、CIで自動適用する まず、 max_staleness の設定をYAMLで宣言的に管理する形にしました。設定用のリポジトリに、データベース・テーブルごとの更新頻度を記述します。 - database : <データベース名> default_max_staleness : 3600 # デフォルトは1時間 tables : - name : <テーブル名> max_staleness : 300 # デフォルトと異なる値にしたいテーブルのみ指定(例:5分) このYAMLをマージすると、CIが差分を検出し、変更のあったテーブルへ ALTER TABLE を自動で実行します。処理の流れは次の通りです。 設定リポジトリのYAMLを編集してマージする インフラ用リポジトリへ設定が同期され、変更のPRが自動で作成される PRをレビューしてマージする CIが差分を検出し、対象テーブルへ max_staleness を自動適用する ここで工夫した点として、この適用処理を他のインフラ適用(IaC)とは独立したジョブにしたことが挙げられます。仮に他のインフラ適用が別の理由で失敗しても、 max_staleness の適用だけは巻き込まれず実行される設計とし、運用の堅牢性を確保しました。 新規テーブルの更新頻度を自動化する 次に、新規に連携されるテーブルの更新頻度を自動化しました。前述の通り、Datastreamのstaleness limit(data freshness)は新規に作成されるテーブルへ自動的に反映されます。この性質を利用し、data freshnessを環境ごとのデフォルト値(本番は1時間、検証・開発は8時間)に設定しておきました。これにより、新しく連携されたテーブルには、作成時にデフォルトの更新頻度が自動で付与されます。 この設計により、YAMLで管理するのは「デフォルトと異なる更新頻度にしたいテーブル(override)」だけでよくなります。デフォルトの更新頻度でよいテーブルはYAMLに書く必要がなく、今後テーブルが増えても手作業が増えない運用を実現しました。 設定状況をカタログで可視化する 最後に、どのテーブルがどの更新頻度で運用されているかをカタログとして可視化しました。 max_staleness の実際の値は、BigQueryの INFORMATION_SCHEMA から取得できます。 SELECT table_name, option_value AS max_staleness FROM `<project>.region-us`.INFORMATION_SCHEMA.TABLE_OPTIONS WHERE option_name = ' max_staleness ' これを利用して、次のようなパイプラインを組みました。 各環境で、利用者が参照するテーブルと max_staleness を突き合わせたカタログテーブルを日次で生成する 全環境のカタログテーブルを束ねる横断ビューを用意する その横断ビューをスプレッドシートに接続し、日次で自動更新する こうすることで、非エンジニアを含む利用者が、スプレッドシートを開くだけで各テーブルの更新頻度を確認できるようになりました。棚卸しや「このテーブルの更新頻度は今どうなっているか」という問い合わせにも、カタログを見れば答えられます。 注意点・工夫 実装を通して得られた学びをいくつか共有します。 staleness limitの変更は「新規テーブルのみ」に効く:既存テーブルへは ALTER TABLE が必要になる。この性質を押さえておかないと「設定を変えたのに反映されない」と混乱しやすい YAMLから消しても、BigQueryの設定値は変わらない:YAMLからテーブルを削除しても、そのテーブルの max_staleness はデフォルト値のまま残る。デフォルト値に戻すには、YAMLに残したままoverrideの指定だけ外す(CIがデフォルト値を再適用する)か、手動で ALTER TABLE する 可視化は「実際に設定されている値」を読む:カタログはYAMLではなく INFORMATION_SCHEMA の実値を参照するため、手動で変更されたテーブルも含めて常に実態を反映できる まとめ 本記事では、Datastreamのリアルタイム連携におけるstaleness limit(データの更新頻度)の見直しによる費用削減と、それを継続的に運用するためのCI/CD化・可視化を紹介しました。 利用者にヒアリングして必要な更新頻度を見極め、安全に更新頻度を延ばすことで約92%の費用削減を実現した 設定をYAMLで宣言的に管理し、CIが自動で適用する仕組みにしたことで、手作業を不要化した 新規テーブルにはデフォルトの更新頻度が自動で付与される設計とし、将来の運用負荷を抑えた 設定状況をカタログとして可視化し、利用者自身が更新頻度を確認できるようにした 単発の費用削減で終わらせず、「効果の確認 → 自動適用 → 可視化」という再現性のある型として構築できた点が、今回の取り組みのポイントだと考えています。利用者へのヒアリングを挟むため多少の手間と時間はかかりますが、実施すれば確実に費用削減を実現できるのでおすすめです。リアルタイム連携の費用にお悩みの方の参考になれば幸いです。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
株式会社ユーザベースの平山です。 この記事では Rust のエラー処理ライブラリ anyhow と thiserror の使い分けについて Clean Architecture の視点から考えてみます。 一旦結論だけ ドメインエラー: 独自のエラー型を定義して thiserror を使う ユースケースエラー: 独自のエラー型を定義して thiserror を使う インフラエラー: anyhow::Error に変換するか、インフラ層でドメインエラーやユースケースエラーに変換する ユースケース や Port の signature では fn name(...) -> anyhow::Resul…

動画

書籍