1. ChatGPT Apps の世界におけるインシデントとは
古典的な Web では、インシデントとはたとえば「サーバが落ちている」「500 エラーが大幅に増えた」「レイテンシが2倍になった」といったものを指すのが一般的です。ITIL の形式的定義では、インシデントとはサービスの計画外の中断、またはサービス品質の低下です。
ChatGPT Apps と GiftGenius の世界では話が少し複雑になります。モデルというレイヤーが存在し、次のようなことが起き得ます。
- 必要なツールを呼ばない(すべて利用可能なのに);
- ツールを誤ったパラメータで呼ぶ;
- MCP を無視して結果を「ハルシネーション」する。
そのため、インシデントは HTTP 500 だけではありません。すべてのバックエンド指標がグリーンでも、ユーザーから「ボットの調子が悪く、ギフトを表示しない」と大量の苦情が来る状況—つまりモデルが suggest_gifts を呼ばなくなったり、引数を取り違えたりしている場合—もあります。これは 品質インシデント(Quality incident) です。
インシデントをカテゴリで考えると便利です。
| カテゴリ | 症状の例 | メトリクス例(SLI) |
|---|---|---|
| Availability | MCP が応答しない、ChatGPT に「Error talking to app」 | /mcp の成功応答率 |
| Latency | ギフトの提案に 10 秒以上かかる | suggest_gifts 呼び出し時間の p95 |
| Quality | モデルが必要なツールを呼ばない、通貨を取り違える | 明示的なリクエストがあるのに tool call がないリクエストの割合 |
| Commerce | checkout が通らない、資金が動かない | checkout_success_rate |
インシデントとは、実測のメトリクスが事前に合意した SLO の範囲を外れた瞬間です。例:
- 合意: ギフト提案の p95 は 4 秒未満。実際は 9 秒になった;
- 週次の checkout 成功率は 99% を目標にしているが、94% になった;
- 購入シナリオではモデルがほぼ必ず create_checkout_session を呼ぶはずだが、ログで「未呼び」の急増が見える。
重要: インシデントは「誰かがチャットで文句を言った」こと自体ではありません。苦情はトリガーであり、「はい、これはインシデントだ」という判断は SLO/SLI とダッシュボードに基づいて行います。
2. SLO/SLI はどうやってインシデントに結びつくか
可観測性のモジュールで、すでに latency、availability、error rate、checkout 成功などの主要メトリクスを定義しました。これらを「入口のガード」として使います。
最も単純なシナリオ: checkout_success_rate に SLO があるとします。構造化されたイベントログを記録します。
// MCP サーバにおける checkout ログイベントの例
logger.info({
event: 'checkout_result',
request_id,
user_id,
checkout_session_id,
status: 'success', // または 'failed'
error_code: null,
});
このログに基づいてメトリクスを作ります。直近 N 分/時間における、すべての checkout_result の中で status = "success" の割合です。この割合がしきい値(例: 10 分で 95%)を下回ると、監視が on‑call チャンネルにアラートを送ります。これが インシデントの検知 です。SLI が SLO の範囲外に出ました。
同様に、次のようなアラートも発火します。
- suggest_gifts、search_products といったツールの error_rate 上昇;
- p95/p99 レイテンシの上昇;
- workflow_completed 数の異常な低下(人がシナリオ完了まで到達していない);
- トラフィックの増加なしに LLM コストだけが異常に増える(経済的インシデント)。
これらは、私たちが構造化ログを書いているからこそ可能です。「checkout がまたおかしい」とだけログに書いていては、こうはなりません。メトリクスとアラートを整えれば、「何かがおかしい」を検知できるようになります。次の問いは、検知後に何が起こり、誰がどう反応するのか、です。
3. インシデントのライフサイクル:検知からポストモーテムまで
常時火消し状態で生きないために、標準的なパイプラインとしてインシデントを記述すると便利です。多くの SRE チームは次の鎖として形式化します。
flowchart TD
D["Detection (検知)"] --> T["Triage (重大度の評価)"]
T --> M["Mitigation (迅速な封じ込め)"]
M --> R["Resolution (最終修正)"]
R --> P["Post-mortem (事後分析と改善)"]
GiftGenius を例に各段階を見ていきます。
Detection — どう悪化に気づくか
問題の把握は自動と手動の 2 つがあります。
自動検知は、SLO/SLI に基づく監視からのアラートです。
- PagerDuty / Opsgenie / メール / Slack ボットが叫ぶ: SEV-1: checkout_success_rate が 10 分で 60% 未満;
- レイテンシのアラート: p95(suggest_gifts) > 10 秒;
- コストの異常: 「同じ workflow_completed 数にもかかわらず LLM コストが 2 倍に」。
手動検知は、サポート(あるいは Telegram であなた個人)に「決済が通らない」「ウィジェットが永遠に回っている」といった大量のメッセージが届くケースです。監視が追いつく前に問題を炙り出すこともあります。
実践的な示唆: まだ理想的な監視がなくても、ユーザーからの大量の苦情は必ずメトリクスの観点で見る癖をつけましょう。「この背後にあるメトリクスは何で、どう測れるか?」です。
Triage — 分類と優先度付け
検知の後は 2 つの問いに答えます。どれほど悪いか、そして 誰が修復に走るか。
簡単な重大度のスケールを用意すると便利です。
- SEV-1: クリティカル — ユーザーが購入できない、App が主要シナリオで動かない(例: 生きたトラフィックなのに checkout=0)。
- SEV-2: 深刻だが劣化状態 — 一部のユーザーが完了できない、レイテンシが大幅に悪化したがゼロではない。
- SEV-3: マイナーな不具合 — 追加ツールの 1 つが時々落ちる、エッジケースだけが壊れる。
GiftGenius では、commerce 系インシデントはほぼ常に SEV-1 です。お金が流れないのは、単なる技術的問題ではなく、売上と信頼への直接的な打撃だからです。
この段階で on‑call(1 人チームならあなた自身)を指名し、「これは公式な SEV‑1 インシデントだ。ランブック N に従って進める」と決定します(ランブックとは、事前に用意した手順書。構成は別セクションで扱います)。
Mitigation — 「出血」を止める
Mitigation は根本原因の追及ではなく、ユーザーの被害を素早く減らすための対処です。例:
- MCP/Agents/ACP の直近リリースをロールバック;
- 問題のあるフィーチャーフラグを無効化;
- GiftGenius を「閲覧モード」へ:おすすめは表示するが、購入手続きはさせない;
- 一時的な負荷低減(レート制限)や重いツールの停止。
私たちの MCP における「ディグレードモード」の典型コード例:
// 疑似コード:すぐ切り替えられるグローバルフラグ
let checkoutDisabled = false;
export function setCheckoutDisabled(value: boolean) {
checkoutDisabled = value;
}
export async function createCheckoutSession(args: CheckoutArgs) {
if (checkoutDisabled) {
// モデルに対して、決済が一時的に利用できないことを伝える
return {
error: 'checkout_temporarily_disabled',
message: '現在、決済は一時的に利用できません。ユーザーに説明を表示してください。',
};
}
// 通常のセッション作成ロジック
}
フィーチャーフラグの仕組みから setCheckoutDisabled(true) を切るのが mitigation の一部になり得ます。少なくともユーザーは 500 やハングした決済を食らうのではなく、正直なメッセージを見ることができます。
Resolution — 最終的な修正
「出血を止めた」ら、根本原因を突き止めて修正する時間が生まれます。
- MCP/ACP のコードバグ;
- 外部プロバイダ(Stripe、決済ゲートウェイ)の問題;
- OpenAI API のレート制限や過負荷(429);
- 壊れたプロンプトや、ツールを呼ばなくなったモデル変更。
Resolution に通常含まれること:
- 修正(パッチ/ロールバック/設定);
- staging へのデプロイ、続いて production へ;
- すべての SLI/SLO の確認;
- フラグを通常状態へ戻す。
Post-mortem — 失敗から学ぶ
とくに SEV‑1/SEV‑2 の後はポストモーテムを行います。以下に正直に答える文書です。
- 何が起きたか(事実とタイムライン);
- どう気づいたか;
- どう対応したか;
- うまくいったこと/いかなかったこと;
- 再発防止のために何を変えるか。
ポストモーテムは犯人探しではなく、システムとプロセスの改善です。これを基にランブック、アラート、場合によってはアーキテクチャまで更新します。
4. 役割と責任:たとえ「ひとりチーム」でも
上で述べたインシデント・パイプラインを現実に機能させるには、火事の最中に誰がどの意思決定を担うのかを事前に決めておくことが重要です。エレベーターに収まる規模のチームでも、インシデント時の役割を形式化する意味があります。混乱を減らせます。
一般的には次の役割を分けます。
- オンコールエンジニア — 最初にアラートを受け取り、安定化のための技術的判断(ロールバック、フィーチャーフラグ、暫定スタブ)を行う人。
- インシデントコマンダー — プロセスを進行する人。タイムラインを記録し、タスクの優先順位を決め、チームが右往左往しないようにする。マイクロチームではオンコールと同一人物だが「別の帽子」をかぶる。
- コミュニケーション担当 — ユーザーやビジネスのステークホルダー対応。Slack、ステータスページ、App の UI(ウィジェット/チャット)、ChatGPT ストアでの告知など。
- スクリブ(記録担当) — 重要なステップと事実を記録し、後にこのメモからポストモーテムを書く。
1 人チームなら 4 役すべてがあなたです。ただし意識的にモードを切り替えると良いでしょう。「今はエンジニアとして直す」「今は対外連絡をする」「今はタイムラインを記録する」。
5. ランブック:記憶ではなく憲法を
ランブックは、特定タイプのインシデントで何をするかを段階的に記した文書です。どのグラフを見るか、どのボタンを押すか、何を犠牲にできるか。即興の割合とストレスを大きく減らします。
ランブックの構成
一般にランブックは次を含みます。
- インシデントの簡潔な説明と検知方法。例:「ACP の checkout エラー率が 5 分間で 5% 超」または「Error talking to app が 20% 超のリクエストで発生」。
- スコープ — 影響範囲:全トラフィックか、特定地域か、特定のツールのみか。
- 見る場所:ダッシュボードへのリンク(checkout の SLO、MCP の error rate、tool_name = create_checkout_session のログ)、MCP Inspector など。
- 迅速な mitigation 手順:「Stripe のステータスを確認」「直近の ACP リリースをロールバック」「購入なしの推薦モードを有効化」。
- 最終的な調査と修正の手順。
- 結果として更新すべきもの:アラート、コード、ドキュメント。
GiftGenius 向けランブックのミニ例(checkout が失敗する)
コードに寄せて、構造化データの形で書いてみます。
type Severity = 'SEV-1' | 'SEV-2' | 'SEV-3';
interface RunbookStep {
title: string;
description: string;
}
interface Runbook {
id: string;
title: string;
severity: Severity;
detection: string;
steps: RunbookStep[];
}
export const checkoutFailureRunbook: Runbook = {
id: 'rb-checkout-failure',
title: 'GiftGenius における checkout エラーの増加',
severity: 'SEV-1',
detection: 'アラート: checkout_success_rate < 60%(直近 10 分)',
steps: [
{
title: '外部ステータスの確認',
description: 'Stripe と ACP backend のステータスを開き、グローバル障害がないことを確認する。',
},
{
title: '直近リリースの確認',
description: '直近 30 分で MCP/ACP のデプロイがあったかを確認。必要に応じてロールバック。',
},
],
};
実際のランブックでは、read‑only フラグの有効化、ウィジェットでのバナー表示、ポストモーテム用のログ収集など、より多くの手順を追加してください。
commerce インシデント時のウィジェット文言例
ランブックにはユーザー向け文言も事前に用意しておくと良いでしょう。たとえば GiftGenius のウィジェットでは次のように表示できます。
「現在、決済に一時的な技術的問題が発生しています。気に入ったギフトのアイデアは保存できます。購入手続きは後ほど行えます。」
この文言は UI の状態に埋め込めます。
// ウィジェット状態の疑似コード
const [checkoutAvailable, setCheckoutAvailable] = useState(true);
if (!checkoutAvailable) {
return (
<Alert>
現在、決済は一時的に利用できません。ギフトのアイデアは引き続き閲覧・保存できます。
</Alert>
);
}
6. GiftGenius での実践:インシデント周りのコード
話を組織論だけで終わらせないために、インシデントマネジメントに直結するコード断片をいくつか見てみましょう。
MCP/Backend の Health‑check エンドポイント
最も単純ながら重要な道具がヘルスチェックです。Next.js 16 では route handler で実装できます。
// app/api/health/route.ts
import { NextRequest, NextResponse } from 'next/server';
export function GET(_req: NextRequest) {
// DB、キュー等のチェックを追加してもよい
return NextResponse.json({
status: 'ok',
mcp: 'healthy',
timestamp: new Date().toISOString(),
});
}
監視システムが定期的に /api/health をポーリングします。200 OK の代わりにタイムアウトや 5xx が出るようなら、Availability インシデント(MCP が死んでいる)の明確なシグナルです。
メトリクスに基づくインシデントの分類
分析サービス側や管理用バックエンドスクリプトで、重大度を決める簡単なロジックを持てます。
type Severity = 'SEV-1' | 'SEV-2' | 'SEV-3';
interface IncidentContext {
checkoutSuccessRate: number; // 0..1
giftSearchErrorRate: number; // 0..1
p95GiftSearchMs: number;
}
export function classifyIncident(ctx: IncidentContext): Severity | null {
if (ctx.checkoutSuccessRate < 0.6) return 'SEV-1'; // お金が流れない
if (ctx.giftSearchErrorRate > 0.3 || ctx.p95GiftSearchMs > 8000) return 'SEV-2';
return null; // まだインシデントではない
}
この処理は cron で回すか監視からのトリガーで動かします。SEV‑1 を返したら、あなたのシステムで自動的にインシデントを作成し、オンコールへ通知します。
インシデントの主要イベントのロギング
インシデントはメトリクスだけでなくイベントでも捉えます。作成、緩和、解決といったイベントは、専用ログに持つと便利です。
function logIncidentEvent(event: {
incidentId: string;
type: 'created' | 'mitigated' | 'resolved';
severity: Severity;
requestId?: string;
message: string;
}) {
logger.warn({
level: 'WARN',
service: 'incident-manager',
...event,
timestamp: new Date().toISOString(),
});
}
たとえば、GiftGenius を「read‑only」モードに切り替えたとき:
setCheckoutDisabled(true);
logIncidentEvent({
incidentId: 'inc-2025-11-21-001',
type: 'mitigated',
severity: 'SEV-1',
message: 'Checkout disabled, app switched to recommendations-only mode',
});
この種のイベントは、後で簡単に見つけて時系列のメトリクスと照合できます。
7. 運用カレンダー:「やった直った」の先へ
インシデントマネジメントは火消しだけではなく、定期的な予防も含みます。SRE の実践では、運用カレンダー(SLO、コスト、セキュリティの定期レビュー)として運用サイクルを記述することが多いです。
アクティビティをおおまかに頻度ごとに分けられます。
週次
週 1(または隔週)で以下を行うと良いでしょう。
- 主要 SLO のレビュー:latency、error rate、checkout 成功、カテゴリ別インシデント比率;
- その週に「勝手に収束した」アラートがあったかを見て、しきい値の強化/緩和を判断;
- 少なくとも 1 件のインシデントを簡単にふりかえり(SEV‑3 でも可)— ポストモーテムの筋肉を鍛える。
月次
月 1 回は次を行うのが望ましいです。
- コストレビュー(LLM、ACP/Stripe 手数料、インフラ)を行い、売上と突き合わせる — モジュール 19 の第 1–2 回と関連;
- プロダクト指標を見る:activation、retention、workflow_completed → checkout_success のコンバージョン — マーケとグロースのモジュールと関連;
- セキュリティログの異常をざっと確認:不審なログインパターン、認可エラー、リクエストの異常スパイク(セキュリティモジュールへの橋渡し)。
四半期ごと
四半期に 1 回は以下を行います。
- シークレットのローテーション:OpenAI、Stripe の API キー、OAuth クライアントなど;
- SLO の陳腐化確認:App が成長して p95 が 1 秒から 2 秒になったのが今の通常か、逆に目標を厳しくできるのか;
- ランブックの見直し:新しいインシデントタイプ、更新された依存関係(SDK、MCP 仕様など)。
カレンダーは GiftGenius リポジトリの Wiki ページや README として管理しても十分です。重要なのは、それが「生きて」更新されることです。
8. インシデント・お金・プロダクト:commerce 火災が最も熱い理由
モジュール 19 は App の経済性と「運用の暮らし」がテーマで、ここではインシデントがお金と密接に結びつきます。checkout が通らない、資金がブロックされる、二重引き落としといった commerce インシデントは、ギフト検索の単発タイムアウトなどより、ほぼ常に優先度が高くなります。
理由は単純です。
- 現時点の売上の直接損失;
- 信頼喪失のリスク(課金されたのに商品が得られなかったユーザーはまず戻りません);
- 法的・レピュテーション上の潜在的影響。
したがって、GiftGenius のインシデントカタログでは、commerce インシデントを明示的に SEV‑1 とし、厳しい応答 SLO(例:「オンコールは 15 分以内に対応、1 時間以内に mitigation」)を設定すべきです。
経済的な異常(売上が増えていないのに LLM コストだけが急増)もインシデントではありますが、通常は SEV‑2 レベルです。UX を即座に壊すわけではないものの、気づかなければ粗利を食い尽くす可能性があります。
プロダクトの観点では、大きなインシデントは次の検討のきっかけでもあります。
- ワークフローが複雑すぎないか(よりシンプル = より堅牢かもしれない);
- フォールバックシナリオを足すべきではないか:たとえば MCP が応答しないとき、少なくとも外部データなしの助言を返すなど;
- 問題を隠さず正直に伝えるために、UX を変える必要はないか。
9. ミニ演習(自習用)
講義はハンズオンではありませんが、ぜひ自分の GiftGenius で以下を本当にやってみてください。
- 少なくとも 2 つのランブック を 1 つの文書にまとめる:
- 「決済(checkout)の大量エラー」;
- 「MCP が応答しない/ChatGPT が Error talking to app を表示する」。
- 1 か月の運用カレンダー を作る:
- 毎週見る SLO は何か;
- 月末にどんなコストレビューをするか;
- どのセキュリティチェックを入れるか(最低限でも)。
数時間の投資ですが、あなたのアプリの見え方は大きく変わります。単なるコードではなく、生きたサービスとして捉えられるようになります。
ChatGPT Apps におけるインシデントマネジメントの典型的な落とし穴
落とし穴 №1: 「インシデントは“全部落ちた”ときだけ」
多くの人は、MCP や DB の完全停止だけをインシデントとみなしがちです。AI Apps では、より痛いのは「ソフトな」品質インシデントです。モデルが必要なツールを呼ばなくなる、checkout フローが混乱する、ユーザーが最後まで到達しないのに HTTP 指標はグリーン、といった状況です。これらをインシデント扱いせずふりかえらないなら、App の品質は気づかれないまま劣化します。
落とし穴 №2: 明確な SLO と「正常動作の境界」がない
形式的な SLO がなければ、インシデントに関する議論は「遅い気がする」対「ローカルでは速い」の言い合いになります。だからこそ SLO はインシデントマネジメントの土台です。問題の重大さを客観化します。
落とし穴 №3: ランブックの代わりに即興
ありがちな光景:アラートが鳴り、皆が慌てて本番に飛び込み、誰かがリリースを戻し、誰かが設定をいじり、1 時間後に「たぶん直った」。しかし何が効いたか誰も覚えていない。ランブックがなければ各インシデントはミニ混乱で、チームは学習しません。checkout インシデント向けのシンプルなランブックが 1 つあるだけでも、ストレスは大きく下がります。
落とし穴 №4: ユーザーへのコミュニケーション軽視
エンジニアが無言で修復している間、ユーザーは「ぐるぐる回る表示」と「何かがうまくいきません」のエラーしか見えないことがあります。commerce シナリオではとくに有害です。ユーザーはお金の心配をします。ウィジェット、App 説明、必要なら外部チャネル用に、問題と復旧見込みを正直に伝えるメッセージのテンプレートをあらかじめ用意しておきましょう。
落とし穴 №5: 自分たちの部分を検証せず「OpenAI が悪い」で済ませる
「OpenAI が不調だから」で片付けるのは簡単ですが、上流に問題があっても自分たちにできることは多いのが実情です。タイムアウトやエラーの適切処理、MCP なしモードへの切替、再試行回数の抑制(事態の悪化防止)など。shared responsibility の考え方では、プロバイダの 1 つが不安定でも、あなたは自分のチェーンの一部に責任を持つのです。
落とし穴 №6: ポストモーテムと運用サイクルがない
「たぶん直った、次行こう」で終わり、ドキュメント・アラート・コードのいずれも変わらない—そんな運用では、同じ過ちを繰り返します。ポストモーテム、SLO・コスト・セキュリティの定期レビューは官僚的手続きではなく、未来の自分とチームに対する約束の取り決めです。1 年後の GiftGenius をより信頼性高く、脆くないものにするために。
GO TO FULL VERSION