皆さんこんにちは、アソビューでエンジニアをしている李です。
アソビューでは、レジャー施設向けのDX支援SaaS「ウラカタチケット」を提供しています。施設運営の効率化から、エンドユーザーのチケット購入・利用体験までを支えるプラットフォームです。
今回は、このウラカタチケットの開発・運用の中で取り組んだ、LLMを使ったエラーアラートの自動分析について紹介します。
1. はじめに:なぜ作ったのか
ウラカタチケットでは、日々さまざまなエラーが発生し、それらを Slack や Datadog で監視しています。
アラートが発生すると、Slack を確認し、Datadog でログを調べ、必要に応じて GitHub のコードや変更履歴を確認する。そして、そのエラーが対応すべきものなのか、様子を見てよいものなのかを判断します。
しかも、届くアラートの多くは既知のエラーや一時的なものです。
「またこのエラーか」と経験的に判断できるようになる一方で、そうした経験則に頼るほど、本当に重要なアラートを見落とすリスクもあります。
特に深夜や休日は、常に誰かが監視しているわけではありません。
そこで、
アラートが来たら、人が調査を始める前に、ログとコードを調べて一次分析を返してくれる仕組みが欲しい。
と考えました。
LLM を使えばできそうです。
ただ、ここで一つ問題がありました。
「この仕組みをどこで動かすのか?」
普通に考えれば、ECS などにボットを常駐させる方法があります。しかし、それでは専用の実行環境や認証情報、運用のためのインフラが必要になります。
まだ効果が分からない段階で、そこまでの仕組みを用意するのは少し大げさに感じました。
そこで発想を変えました。
エンジニアの手元には、すでに Claude Code が入ったノートPCがある。
Datadog や GitHub にもログイン済みだ。
それをそのまま使えばいいのでは?
こうして作ったのが、アラート自動分析ツールの pp_sentinel です。
2. システム概要
アラートチャンネルにチームメンション付きのアラートが流れると、ボットが検知して、そのスレッドに分析結果を返信します。
分析の流れはシンプルです。
- Datadog MCP でアラート前後のエラーログを検索
- GitHub MCP で関連するコードや変更履歴を確認
- Claude Code がログとコードを統合して、原因・影響範囲・対応方針を整理
- Slack のスレッドに結果を返信
ポイントは、pp_sentinel 自体にログ解析ロジックを大量に実装していないことです。
Datadog と GitHub を操作できる MCP を Claude Code に渡し、調査そのものを任せる構成にしました。
全体像は次のようになります。
Slack
│
┌─────────────┴─────────────┐
│ │
Socket Mode Channel History
(realtime) (catchup)
│ │
└─────────────┬─────────────┘
│
pp_sentinel
│
Claude Code CLI
│
┌────────┴────────┐
│ │
Datadog MCP GitHub MCP
実行環境は各エンジニアの Mac です。
共有状態も、ボット専用の Slack プライベートチャンネルのピン留めメッセージに持たせ、アラートの処理状態はリアクションで確認できるようにしました。
つまり、専用サーバーや専用の管理画面を作らず、すでにチームが使っている Slack と開発環境をそのまま組み合わせています。
3. 設計上の重要な決断
3-1. なぜサーバーを使わないのか
一番大きな理由は、まず小さく試したかったからです。各開発者の Mac で動かせば、既存の Datadog / GitHub の認証環境をそのまま利用できます。
一方で、「PCを閉じたら止まる」という問題があります。
そこで今回は、常に動いていることを目指すのではなく、
止まることを前提にして、止まっている間のアラートは後から拾う
という設計にしました。
誰か1人でも起動していればリアルタイムに処理し、全員が停止している時間帯のアラートは後から補完します。
3-2. Slack を「インフラ」として使う
サーバーを持たないなら、共有状態をどこに置くのか。
そこで Slack を利用しました。
- ピン留めメッセージ → 共有状態や heartbeat
- リアクション → 処理状態
- チャンネル履歴 → 取りこぼしたアラートの補完
厳密には Slack は KVS やメッセージキューではありません。
ただ、今回のように1日数十件程度のアラートを数台のノードで処理する規模であれば、必要な機能を十分に提供してくれました。
特にリアクションは便利でした。
👀 → 処理中 ✅ → 処理済み
Slack を開くだけで、人間にも処理状態が分かります。専用の管理画面を作らなくても、「このアラートは処理されたのか?」を確認できます。
3-3. 複数ノードをどう協調させるか
複数人が同時に Bot を起動するため、全員が同じアラートを処理しないようにする必要があります。
ノードには PRIMARY / STANDBY / RESTING の3状態を持たせ、PRIMARY が定期的に heartbeat を更新します。
一定時間 heartbeat が更新されなければ、STANDBY が PRIMARY に昇格します。
また、同じ PC が長時間処理を抱え続けないよう、一定時間経過したら休止するローテーションや、Claude Code の連続失敗時に PRIMARY を降りる仕組みも入れました。
ここで重要なのは、今回の仕組みを厳密な分散システムとして作ろうとしなかったことです。
Slack API を利用しているため、厳密な分散ロックやリーダー選出ではありません。
多少の競合が起こる可能性は許容し、今回必要な範囲で協調できればよいと割り切りました。
3-4. 「リアルタイム」と「補完」を分ける
この仕組みで特に重要だったのが、Socket Mode だけに頼らなかったことです。
Socket Mode はリアルタイム処理には向いていますが、PC がスリープしたりネットワークが切れたりすると、その間のイベントを受け取れません。
そこで、
リアルタイム経路:速いけれど取りこぼす可能性がある
補完経路:遅いけれど履歴から確認できる
を分けました。
リアルタイム経路は Socket Mode。
補完経路は Slack API のチャンネル履歴取得(conversations.history)を利用して、最後に処理した位置から履歴を確認します。
ここで重要なのが、最後に処理したメッセージの位置を記録する last_processed_ts(最終処理タイムスタンプ)です。
この値は補完経路だけが更新し、リアルタイム経路(Socket Mode)では更新しません。
Socket Mode が先にオフセットを進めてしまうと、後続の Claude Code の処理に失敗した際、そのアラートが「処理済みの範囲」に埋もれてしまう可能性があります。
そのため、リアルタイム経路は処理済みマーカーを付けるだけ。
補完処理が後から履歴を確認し、処理済みマーカーがないアラートを拾います。
これによって、PC が停止していた時間帯のアラートも、後から処理できるようにしました。
4. Claude Code + MCP による自動分析
4-1. 分析を LLM に任せる
最初は、ログを取得するコードや GitHub のコード検索を自分で実装しようとしていました。
しかし MCP が使えるのであれば、そこまで自分で書く必要はありません。
Claude Code に Datadog と GitHub を操作する道具を渡し、調査そのものを任せることにしました。
その結果、ボット側で必要なのは、
「今回のアラートについて何を知りたいか」
をプロンプトとして渡すことだけになりました。
分析結果を改善したい場合も、Python のロジックではなく、Claude Code への指示内容をまとめたプロンプトファイルを変更するだけで済みます。
この構成にしたことで、試行錯誤のサイクルをかなり速く回せました。
4-2. 「何をさせないか」をプロンプトに書く
実際に運用してみて、特に重要だったのが探索範囲の制御です。
最初は、
「関連するログを調べてください」
程度の指示でした。
すると Claude Code が必要以上に Datadog を検索し続け、実行時間やトークン消費が膨らむことがありました。
そこで、
前後15分 status:error のみ 最大10件
のように具体的な上限を設定しました。
また、「必要なら GitHub を調べる」ではなく、
スタックトレースに具体的なクラス名や行番号がある場合のみ確認する
というように、スキップ条件も明示しました。
ここで得た教訓は、
LLM に何をさせるかだけでなく、何をさせないかを書くことが、コストと実行時間の管理につながる
ということでした。
4-3. 事業ドメインをプロンプトに入れる
もう一つ効果が大きかったのが、事業ドメインに合わせて判断基準を与えることです。
例えば私たちの場合、
- ゲストの購入・入場・チケット利用
- パートナーの管理画面・設定・CSV
では、同じエラーでも緊急度が異なります。
そこで、
ゲスト側 CUJ への影響を優先度判断の主軸にする
というルールをプロンプトに入れました。
汎用的な「Critical / Warning」だけを使うよりも、チームが普段使っている言葉と判断基準をそのまま LLM に渡したほうが、実際の運用感覚に近い結果になることが分かりました。
5. 構築中に遭遇した問題と解決策
実装中には、いくつか想定外の問題にも遭遇しました。
5-1. Datadog のアラートだけ届かない
手動で Slack に投稿したメッセージには反応するのに、本物の Datadog アラートには反応しない。
原因は、
bot_messageを無条件で除外していた- Datadog のメンションが
textではなくattachmentsに入っていた
という2点でした。
送信元によって Slack の payload が異なるため、手動メッセージだけでテストしていたことが原因でした。実際の payload を確認することの重要性を実感した問題です。
5-2. 共有状態が突然消える
最終処理タイムスタンプ(last_processed_ts)を保存しているはずなのに、しばらくすると消えている。
原因は、heartbeat と共有状態を同じピン留めメッセージに持たせ、heartbeat 更新時に状態全体を上書きしていたことでした。
heartbeat と共有状態を別メッセージに分けることで解決しました。
この問題から、
更新頻度や責務が違う状態を、同じデータに詰め込まない
という、ごく基本的なことを改めて学びました。
6. 運用してわかったこと
実際に運用してみて、一番価値があったのは「原因を当てること」ではありませんでした。
むしろ、
「これは見なくていい」と判断してくれること、「このエラーにはどんな傾向があるのか」を把握すること
のほうが大きかったです。
アラートの多くは既知のエラーや自動回復するもので、エンジニアは「重要な問題を解決する」だけでなく、「重要ではないことを確認する」ためにも時間を使っていました。
LLM が一次調査をして、
ゲストへの影響なし / 自動回復済み / 優先度低
と判断してくれるだけでも、毎日数十件発生するアラートの監視負担は大きく変わります。
一方で、LLM が常に正しいわけではありません。
単一サービス内のスタックトレースは比較的追いやすい一方、複数サービスをまたぐ因果関係や、ネットワーク・リソース起因の問題などは苦手です。
そのため、チームでは分析結果を最終的な結論ではなく「一次調査のたたき台」として扱っています。
また、同じ issue の再分析をスキップしたり、ログ取得範囲を制限したりすることで、トークン消費も抑えました。
7. 今後の展望
今回の開発で、一番大きかったのは「サーバーを使わない」という制約そのものだったと思います。
常に動いていることを諦め、
止まることを前提にして、後から回収する
という設計にしたことで、専用サーバーや複雑なインフラを用意せずに済みました。
また、Slack を単なる通知先としてではなく、
- 共有状態
- 処理状態
- 履歴ベースのイベント取得
のための媒体として使うことで、複数のノートPCを協調させることもできました。
もちろん Slack は本格的な KVS やメッセージキュー、分散ロックの代替ではありません。
それでも、小規模な社内ツールであれば、すでにチームが使っているサービスを「インフラの部品」として見直すことで、専用基盤を作らずに解決できる問題があると感じました。
今後は、
- 分析結果へのフィードバックをプロンプト改善に利用する
- 過去の類似インシデントを参照できるようにする
- 分析結果を蓄積して、そもそものアラートを減らす
といった改善を考えています。
特に最後の「アラートそのものを減らす」は重要です。
アラート対応を自動化するだけではなく、
「なぜこのアラートは毎日鳴っているのか?」
まで分析できれば、監視そのものの改善につながります。
LLM を実務に組み込むとき、ついモデルの性能に目が行きがちです。
しかし実際に動かしてみると、状態管理、リトライ、冪等性、障害時の振る舞いなど、従来のソフトウェアエンジニアリングも同じくらい重要でした。
そして何より、
動かしてみないと踏めない地雷が、思っていた以上にたくさんある。
これが今回の開発で一番強く感じたことです。
この記録が、同じように LLM を日々の運用に組み込もうとしている方の参考になれば幸いです。