AWS DevOps Agent の調査完了イベントを使って、根本原因と緩和策だけを Slack に通知してみた

AWS DevOps Agent の調査完了イベントを使って、根本原因と緩和策だけを Slack に通知してみた

少しでも通知内容を見やすくしたい方へ
2026.10.14

こんにちは!クラウド事業本部のおつまみです。

みなさん、AWS DevOps Agent を Slack と連携したとき、調査の途中経過までスレッドに大量に投稿されて見づらいなと困ったことはありませんか?

スレを開くと、大量の調査内容。。

調査の過程が見えるのは便利な一方で、運用チームのチャンネルに流すには「結局、原因は何で、どうすればいいの?」だけ分かれば十分、というケースも多いと思います。

そのため、DevOps Agent が Amazon EventBridge に送る Investigation Completed イベントを使って、調査が完了したときだけ「根本原因」と「緩和策」を1件のメッセージで Slack に通知する仕組みを作ってみました!今回はその方法をご紹介します。

前提:標準の Slack 連携で困ること

DevOps Agent には標準で Slack 連携があり、調査を開始すると指定したチャンネルにスレッドが作られ、調査の進捗が投稿されていきます。

今回の検証では、約9分の調査1回でスレッドに12〜14件の投稿がありました(2回実施)。

調査の過程を追いたい担当者には便利ですが、アラート通知用のチャンネルに流すと次のような課題があります。

  • 1回の調査でスレッドに10件以上投稿され、ほかの通知が埋もれる
  • 結論(根本原因と緩和策)にたどり着くまでスクロールが必要
  • 調査が終わったかどうかがひと目で分からない

そこで「調査が完了したら、結論だけを1件で通知する」仕組みを考えました。

既存の方法との違い

既存の方法としてDevelopersIO に、GuardDuty の検出通知メールに DevOps Agent の調査結果を追加する記事がありました。

https://dev.classmethod.jp/articles/developersio-2026-osaka-guardduty-notification-email-with-devops-agent-investigation/

この記事では、Lambda から GetBacklogTask を繰り返し呼んで(ポーリングして)調査の完了を待ち、15分を超える調査には Lambda の Durable Functions で対応しています。
一方、現在の DevOps Agent は調査のライフサイクルに合わせて EventBridge にイベントを送るようになっています。完了イベントをきっかけに Lambda を起動すれば、完了を待つ処理そのものが要りません。

項目 ポーリング方式 イベント方式(今回)
完了の検知 GetBacklogTask を定期的に呼ぶ Investigation Completed イベントを受け取る
長時間の調査への対応 Durable Functions などで待機 不要(完了時に起動されるだけ)
Lambda の実行時間 調査時間に比例して長くなる 要約の取得と Slack への投稿だけ

DevOps Agent の EventBridge イベント

DevOps Agent は、調査(Investigation)と緩和策(Mitigation)の状態が変わったときに、EventBridge の default event bus にイベントを送ります。source はすべて aws.aidevops です。
イベントの送信に追加の権限設定は要りません。

主なイベントは次のとおりです。

detail-type 内容
Investigation Created 調査が作成された
Investigation In Progress 調査が開始された
Investigation Completed 調査が正常に完了した
Investigation Failed 調査がエラーで完了できなかった
Investigation Timed Out 調査が最大実行時間を超えた
Mitigation Completed 緩和策が正常に完了した

このほかにも Investigation Cancelled や Investigation Skipped、Mitigation Failed などがあります。一覧は参考のドキュメントをご覧ください。

参考:AWS DevOps エージェントと Amazon EventBridge の統合 - AWS DevOps エージェント

以下はInvestigation Completed イベントの例です。

{
  "detail-type": "Investigation Completed",
  "source": "aws.aidevops",
  "detail": {
    "version": "1.0.0",
    "metadata": {
      "agent_space_id": "8f6187a7-0388-4926-8217-3a0fe32f757c",
      "task_id": "a1b2c3d4-5678-90ab-cdef-example11111",
      "execution_id": "b2c3d4e5-6789-01ab-cdef-example22222"
    },
    "data": {
      "task_type": "INVESTIGATION",
      "priority": "CRITICAL",
      "status": "COMPLETED",
      "created_at": "2026-03-12T18:00:00Z",
      "updated_at": "2026-03-12T18:10:00Z",
      "summary_record_id": "d4e5f6g7-6789-01ab-cdef-example44444"
    }
  }
}

ポイントは、イベントに要約の本文が含まれていないことです。本文を読むには、イベントの値を使って ListJournalRecords を呼び出します。

イベントの値 ListJournalRecords のパラメータ
detail.metadata.agent_space_id agentSpaceId
detail.metadata.execution_id executionId
(イベントには含まれない) recordType:調査は investigation_summary_md、緩和策は mitigation_summary_md

返ってきたレコードのうち、recordId が detail.data.summary_record_id と一致するものの content が要約(Markdown)です。
今回の検証では、1つの実行の investigation_summary_md は1件だけでした。それでも、どのレコードを読むかを確実にするため、ID で絞り込んでいます。

構成

以下の構成で実装しました。

  • リージョンは東京(ap-northeast-1)です。イベントは Agent Space と同じリージョンの default event bus に届くため、EventBridge ルールと通知用 Lambda も同じリージョンに作ります
  • Slack の Webhook URL は Secrets Manager に保存します

調査開始から Slack に通知が届くまでの流れは、次のとおりです。

  1. 調査を開始する(①)
    AWS CLI の create-backlog-task で、DevOps Agent に調査を依頼します。DevOps Agent は、テスト用の Lambda や CloudWatch アラームのメトリクスとログを調べて、根本原因と緩和策をまとめます。
  2. 完了イベントが届く(②)
    調査が終わると、DevOps Agent が EventBridge の default event bus に Investigation Completed イベントを送ります。イベントには、要約のレコード ID(summary_record_id)は含まれますが、要約の本文は含まれません。
  3. 通知用 Lambda が起動する(③)
    EventBridge ルールが、対象の Agent Space の Investigation Completed だけを拾って、通知用 Lambda を起動します。
  4. 要約とタイトルを取得する(④)
    Lambda は、ListJournalRecords で次の2つを取得し、GetBacklogTask で調査のタイトルを取得します。
    • 根本原因:summary_record_id と一致する investigation_summary_md
    • 緩和策:同じ実行で最新の mitigation_summary_md
  5. Webhook URL を取得する(⑤)
    Secrets Manager から Slack の Webhook URL を取り出します。
  6. Slack に投稿する(⑥)
    根本原因と緩和策の部分だけを Slack の書式に整えて、1件のメッセージとして投稿します。

やってみた

1. Agent Space とテスト用の障害を用意する

東京リージョンで Agent Space を作成し、検証用アカウントを関連付けました。Agent Space の作成手順は以下の記事が参考になります。

https://dev.classmethod.jp/articles/aws-devops-agent-fast-handson/#1.-%25E3%2582%25A8%25E3%2583%25BC%25E3%2582%25B8%25E3%2582%25A7%25E3%2583%25B3%25E3%2583%2588%25E3%2582%25B9%25E3%2583%259A%25E3%2583%25BC%25E3%2582%25B9%25E3%2581%25AE%25E4%25BD%259C%25E6%2588%2590

Agent Space の言語は日本語(Japanese)にしています。

次に調査対象として、必ずエラーを返す Lambda 関数と、Errors >= 1 で ALARM になる CloudWatch アラームを作成しました。

関数は、存在しない DynamoDB テーブル devops-agent-verify-orders から get_item するだけのものです。実行するたびに ResourceNotFoundException が発生します。

import os

import boto3

dynamodb = boto3.resource("dynamodb")
TABLE_NAME = os.environ.get("TABLE_NAME", "devops-agent-verify-orders")

def lambda_handler(event, context):
    table = dynamodb.Table(TABLE_NAME)
    item = table.get_item(Key={"order_id": event.get("order_id", "0001")})
    return item.get("Item")

そしてアラームは、1分間の Errors の合計が1以上で ALARM になるように設定しました。関数を5回実行してアラームを ALARM にしたあと、調査を開始します。

2. Slack 側の準備

Slack 側では、次の2つを用意します。

  • 比較用の標準の Slack 連携
  • 今回の仕組みで投稿に使う Incoming Webhook

2-1. 比較用:標準の Slack 連携を設定する

通常と比べるため、標準の Slack 連携も設定しておきます。今回の仕組みだけを使う場合は、この手順は要りません。

  1. DevOps Agent のコンソールで Agent Space を開き、「コミュニケーション」から「統合を追加」を選んで Slack を登録する。ワークスペースを選び、「許可する」を押す

  1. Slack で通知先のチャンネルを開き、チャンネルの詳細の「インテグレーション」タブで「アプリを追加する」を押して、「AWS DevOps Agent」を追加する。メッセージ欄で /invite @AWS DevOps Agent と送っても追加できる

チャンネルにアプリを追加していないと、調査を始めても投稿が届きません。

2-2. Incoming Webhook を作る

今回の通知の投稿には、Slack の Incoming Webhook を使います。

参考:【手順付き】Slack App を作成して Incoming Webhooks でメッセージを投稿する方法 | DevelopersIO

  1. https://api.slack.com/apps で「Create New App」→「From scratch」を選び、App の名前と投稿先のワークスペースを指定して作成する
  2. 左のメニューの「Incoming Webhooks」を開き、「Activate Incoming Webhooks」をオンにする
  3. 「Add New Webhook to Workspace」を押し、投稿先のチャンネルを選んで許可する
  4. 表示された https://hooks.slack.com/services/... の Webhook URL をコピーする

Webhook URL を知っていれば、誰でもそのチャンネルに投稿できます。コードや環境変数に直接書かず、Secrets Manager に保存します。

read -rs WEBHOOK_URL   # Webhook URL を貼り付けて Enter(画面には表示されません)
aws secretsmanager create-secret \
  --name devops-agent-verify/slack-webhook \
  --secret-string "$WEBHOOK_URL" \
  --region ap-northeast-1
unset WEBHOOK_URL

通知用 Lambda の環境変数 SLACK_WEBHOOK_SECRET_ID には、このシークレットの名前(devops-agent-verify/slack-webhook)を指定します。

3. まずはイベントと要約の中身を確認する

通知の仕組みを作る前に、どんなイベントがどの順番で届くのか、要約がどんな形式なのかを確認しました。すべての DevOps Agent イベントを CloudWatch Logs に流す EventBridge ルールを作ります。

{
  "source": ["aws.aidevops"],
  "detail-type": [{"prefix": "Investigation"}, {"prefix": "Mitigation"}]
}

AWS CLI から調査を開始します。

aws devops-agent create-backlog-task \
  --agent-space-id <AGENT_SPACE_ID> \
  --task-type INVESTIGATION \
  --priority HIGH \
  --title "検証: Lambda エラー増加" \
  --description "<関数名> でエラーが発生しています。原因を調査してください"

届いたイベントは次のとおりでした。

時刻(UTC) detail-type data.status
02:14:14 Investigation Created PENDING_START
02:14:26 Investigation In Progress IN_PROGRESS
02:23:18 Investigation Completed COMPLETED

各イベントが1回ずつ届き、Investigation Completed にだけ summary_record_id が入っていました。Mitigation で始まるイベントは届いていません。

調査が完了したら、要約を取得します。

aws devops-agent list-journal-records \
  --agent-space-id <AGENT_SPACE_ID> \
  --execution-id <EXECUTION_ID> \
  --record-type investigation_summary_md

summary_record_id と一致するレコードが1件見つかりました。取得した要約の一部です。

# Investigation Summary

## Symptoms

### Lambda 関数 devops-agent-verify-faulty でエラーバースト(エラー率100%)
**Description:** 2026-10-14 01:57 UTC(10:57 JST) に5回の呼び出しがすべて失敗(Errors=5/Invocations=5)。…

## Findings

### Root Cause: 存在しない DynamoDB テーブルへの get_item で未捕捉例外
**Description:** Lambda `devops-agent-verify-faulty` のハンドラ app.py 11行目で `table.get_item(Key={"order_id": ...})` を実行する際、参照先の DynamoDB テーブルが存在しないため `ResourceNotFoundException` が発生。…
**Cascades to:** symptom-lambda-errors

## Other Gaps

### Lambda コード本体を直接取得できず(lambda:GetFunction 権限不足)
…

Agent Space の言語を日本語(ja-JP)にしていたため、本文は日本語でした。

一方、見出しは Symptoms / Findings / Root Cause: / Other Gaps と英語の固定文言です。根本原因の要旨は ### Root Cause: の見出しの中に、詳しい説明はその下の **Description:** に書かれています。

困ったのは、この要約に緩和策が含まれていなかったことです。

緩和策は、同じ実行(execution_id)の中に mitigation_summary_md という別のレコードとして書かれていました。Investigation Completed イベントの約13秒前に、調査の要約とほぼ同時に作られています。Mitigation のイベントは届かなかったので、Investigation Completed を受け取った時点で緩和策も読めます。

aws devops-agent list-journal-records \
  --agent-space-id <AGENT_SPACE_ID> \
  --execution-id <EXECUTION_ID> \
  --record-type mitigation_summary_md
# Mitigation Summary

## Action
欠落している DynamoDB テーブル devops-agent-verify-orders を、パーティションキー order_id(String)・オンデマンド課金(PAY_PER_REQUEST)で ap-northeast-1 に作成し、Lambda 関数 devops-agent-verify-faulty の GetItem が ResourceNotFoundException を発生させない状態に復旧する。

## Reasoning
…

## Execution Plan
### Step 1: Prepare
…

## Action に緩和策の要旨があり、その後に理由(Reasoning)、実行手順(Execution Plan)、コードの修正方針(Code Change Specification)が続きます。手順には確認、適用、ロールバックの CLI コマンドまで書かれており、読みごたえがありました。

この結果から、通知に使う部分を次のように決めました。

通知の項目 取得元
根本原因 summary_record_id と一致する investigation_summary_md の ### Root Cause: の見出しと Description
緩和策 同じ実行で最新の mitigation_summary_md の ## Action

なお、画面表示用とみられる ui_investigation_summary というレコードもあります。この JSON にも summary__cause(原因)と summary__mitigation(緩和策)のノードがあり、短く整った日本語の文章でした。ただ、ドキュメントに載っていない recordType のため、今回はドキュメントに載っている Markdown 版を使います。

4. 通知用 Lambda を作成する

要約の Markdown から、見出しを手がかりに「根本原因」と「緩和策」のセクションだけを切り出して Slack に投稿します。

ランタイムは Python 3.13 です。

根本原因は、### Root Cause: の見出しにある要旨と、その下の Description を取り出します。緩和策は、同じ実行の中で最新の mitigation_summary_md から ## Action の本文を取り出します。通知の見出しには、GetBacklogTask で取得した調査のタイトルを入れています。

環境変数 DRY_RUN を true にすると、Slack には投稿せず、送る予定の内容をログに出します。

import json
import os
import re
import urllib.request

import boto3

devops_agent = boto3.client("devops-agent")
_secrets = None

MAX_SECTION_CHARS = 1500
DRY_RUN = os.environ.get("DRY_RUN") == "true"
NOT_FOUND_TEXT = "(要約から見つかりませんでした)"

def list_journal_records(agent_space_id, execution_id, record_type):
    records = []
    kwargs = {
        "agentSpaceId": agent_space_id,
        "executionId": execution_id,
        "recordType": record_type,
    }
    while True:
        page = devops_agent.list_journal_records(**kwargs)
        records.extend(page.get("records", []))
        token = page.get("nextToken")
        if not token:
            return records
        kwargs["nextToken"] = token

def get_summary(agent_space_id, execution_id, summary_record_id):
    for record in list_journal_records(agent_space_id, execution_id, "investigation_summary_md"):
        if record["recordId"] == summary_record_id:
            return record["content"]
    return None

def get_mitigation_summary(agent_space_id, execution_id):
    records = list_journal_records(agent_space_id, execution_id, "mitigation_summary_md")
    if not records:
        return None
    return max(records, key=lambda r: r["createdAt"])["content"]

def get_task_title(agent_space_id, task_id):
    try:
        res = devops_agent.get_backlog_task(agentSpaceId=agent_space_id, taskId=task_id)
        return res.get("task", res).get("title")
    except Exception as e:
        print(f"タスクタイトルを取得できませんでした: {e}")
        return None

def extract_section(markdown, pattern):
    lines = markdown.splitlines()
    start = level = None
    for i, line in enumerate(lines):
        m = re.match(r"^(#{1,6})\s+(.*)$", line)
        if not m:
            continue
        if start is None:
            if re.search(pattern, m.group(2), re.IGNORECASE):
                start, level = i + 1, len(m.group(1))
        elif len(m.group(1)) <= level:
            return "\n".join(lines[start:i]).strip()
    if start is None:
        return None
    return "\n".join(lines[start:]).strip()

def extract_root_causes(markdown):
    lines = markdown.splitlines()
    causes = []
    i = 0
    while i < len(lines):
        m = re.match(r"^(#{1,6})\s+Root Cause:\s*(.*)$", lines[i], re.IGNORECASE)
        i += 1
        if not m:
            continue
        level, headline = len(m.group(1)), m.group(2).strip()
        body = []
        while i < len(lines):
            h = re.match(r"^(#{1,6})\s", lines[i])
            if h and len(h.group(1)) <= level:
                break
            body.append(lines[i])
            i += 1
        body = [re.sub(r"^\*\*Description:\*\*\s*", "", l) for l in body
                if not l.startswith("**Cascades to:**")]
        text = "\n".join(body).strip()
        causes.append(f"*{headline}*\n{text}" if text else f"*{headline}*")
    return "\n\n".join(causes)

def to_slack_mrkdwn(text):
    text = re.sub(r"^#{1,6}\s+(.*)$", r"*\1*", text, flags=re.MULTILINE)
    text = re.sub(r"\*\*(.+?)\*\*", r"*\1*", text)
    text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", r"<\2|\1>", text)
    if len(text) > MAX_SECTION_CHARS:
        text = text[:MAX_SECTION_CHARS] + "\n…(続きは調査画面をご確認ください)"
    return text

def post_to_slack(blocks, fallback_text):
    global _secrets
    payload = {"text": fallback_text, "blocks": blocks}
    if DRY_RUN:
        print(json.dumps(payload, ensure_ascii=False, indent=2))
        return None
    if _secrets is None:
        _secrets = boto3.client("secretsmanager")
    webhook_url = _secrets.get_secret_value(SecretId=os.environ["SLACK_WEBHOOK_SECRET_ID"])["SecretString"]
    body = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        webhook_url, data=body, headers={"Content-Type": "application/json"}
    )
    with urllib.request.urlopen(req, timeout=10) as res:
        return res.status

def lambda_handler(event, context):
    metadata = event["detail"]["metadata"]
    data = event["detail"]["data"]

    summary_record_id = data.get("summary_record_id")
    execution_id = metadata.get("execution_id")
    if not summary_record_id or not execution_id:
        print(f"要約がないため通知しません: {json.dumps(event['detail'])}")
        return

    summary = get_summary(metadata["agent_space_id"], execution_id, summary_record_id)
    if summary is None:
        print(f"要約レコードが見つかりません: {summary_record_id}")
        return

    agent_space_id = metadata["agent_space_id"]
    priority = data.get("priority", "-")
    task_title = get_task_title(agent_space_id, metadata["task_id"])
    title = f"[{priority}] 調査完了: {task_title}" if task_title else f"[{priority}] DevOps Agent の調査が完了しました"
    blocks = [{"type": "header", "text": {"type": "plain_text", "text": title[:150]}}]

    mitigation_md = get_mitigation_summary(agent_space_id, execution_id)
    sections = {
        "根本原因": extract_root_causes(summary),
        "緩和策": extract_section(mitigation_md, r"^action$") if mitigation_md else None,
    }
    for label, section in sections.items():
        blocks.append({
            "type": "section",
            "text": {"type": "mrkdwn", "text": f"*■ {label}*\n{to_slack_mrkdwn(section or NOT_FOUND_TEXT)}"},
        })

    blocks.append({
        "type": "context",
        "elements": [{"type": "mrkdwn", "text": f"Agent Space: `{agent_space_id}` / Task: `{metadata['task_id']}`"}],
    })

    post_to_slack(blocks, title)

実行ロールには、次の権限を付与しました。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "aidevops:ListJournalRecords",
        "aidevops:GetBacklogTask"
      ],
      "Resource": "arn:aws:aidevops:ap-northeast-1:<ACCOUNT_ID>:agentspace/<AGENT_SPACE_ID>"
    },
    {
      "Effect": "Allow",
      "Action": "secretsmanager:GetSecretValue",
      "Resource": "arn:aws:secretsmanager:ap-northeast-1:<ACCOUNT_ID>:secret:<SECRET_NAME>-*"
    }
  ]
}

Agent Space の ARN を指定したこのポリシーで、ListJournalRecords と GetBacklogTask の両方を呼べることを確認しました。また、Python 3.13 のランタイムに入っている boto3 で devops-agent クライアントが使えたので、boto3 を同梱する必要はありませんでした。

CloudWatch Logs への書き込みは、マネージドポリシー AWSLambdaBasicExecutionRole で付与しています。

5. EventBridge ルールを作成する

通知用のルールは、対象の Agent Space の Investigation Completed だけに絞ります。

{
  "source": ["aws.aidevops"],
  "detail-type": ["Investigation Completed"],
  "detail": {
    "metadata": {
      "agent_space_id": ["<AGENT_SPACE_ID>"]
    }
  }
}

ターゲットに通知用 Lambda を指定して完成です。CLI でターゲットを登録する場合は、aws lambda add-permission で EventBridge(events.amazonaws.com)に Lambda の呼び出しを許可しておく必要があります。コンソールでターゲットに指定すると、この権限は自動で追加されます。

結果

通知用の EventBridge ルールを有効にした状態で、テスト用の Lambda でエラーを発生させ、調査を実行しました。

調査が完了すると、これまでのDevOps Agentの通知とは別に根本原因と緩和策を含む通知が届きました。時系列は次のとおりです(JST)。

時刻 出来事
13:56:47 Investigation Created
13:56:55 Investigation In Progress
14:01 標準の Slack 連携のスレッドへの最後の投稿
14:05:24 mitigation_summary_md が作成される
14:05:37 Investigation Completed
14:05:38〜40 通知用 Lambda が起動し、Slack に投稿

完了イベントが届いてから1〜2秒で通知が届いています。
標準の Slack 連携と比べると、次のようになりました。

項目 標準の Slack 連携 今回の仕組み
1回の調査での投稿数 12件 / 14件 1件
結論の見つけやすさ スレッドを最後まで読む必要がある 通知を開けばすぐ分かる
調査完了の分かりやすさ 投稿が止まっても完了とは限らない(今回は約4分の差) 通知が来たら完了

調査は2回とも約9分で完了しました。1回目の調査について、get-account-usage で確認した利用時間は、調査(Investigation)が約0.15時間(約9分)でした。このほか、Agent Space を作ってからの学習(System Learning)に約0.6時間かかっていました。

DevOps Agent は、エージェントが作業した時間に応じた秒単位の課金です。調査(Investigations)の単価は 0.0083 USD/秒です(2026年10月時点)。1回目の調査は約532秒だったので、約4.42 USD になります。

0.1478 時間 × 3,600 秒 × 0.0083 USD ≒ 4.42 USD

料金ページの課金項目に System Learning はありませんでした。新規の AWS アカウントには、2か月の無料トライアル(調査は月20時間まで)もあります。最新の料金は料金ページをご確認ください。

https://aws.amazon.com/devops-agent/pricing/

通知用 Lambda は1回あたり2秒弱で終わるので、通知の仕組みそのものの費用はほぼかかりません。

ハマりどころ・注意点

要約がない完了イベントもありうる

ドキュメントによると、summary_record_id は要約レコードが書き込まれた場合にだけイベントに含まれます。今回の2回の検証ではどちらも含まれていましたが、Investigation Completed でも含まれない場合がありうるので、存在をチェックしています。execution_id も値がないと省略されるため、同じようにチェックしています。

失敗・タイムアウトは別途通知したほうがよい

Investigation Failed / Timed Out / Cancelled には summary_record_id が含まれません。今回の検証では失敗やタイムアウトは起きませんでしたが、完了イベントだけを拾う設定だと、調査が失敗したことに気づけません。「調査に失敗しました」程度の短い通知を、別のルールで送ると安心です。

緩和策は調査の要約とは別のレコードにある

investigation_summary_md には緩和策が含まれず、同じ実行の mitigation_summary_md に書かれていました。Investigation Completed イベントの summary_record_id が指すのは調査の要約だけで、緩和策のレコード ID はイベントに含まれません。そのため、同じ実行の mitigation_summary_md のうち、作成日時がいちばん新しいものを使っています。

今回の検証では Mitigation のイベントは届かず、緩和策のレコードは Investigation Completed より前に作られていました。Mitigation Completed を待たなくても、完了イベントだけで根本原因と緩和策の両方を通知できます。

イベントが重複して届く可能性がある

EventBridge のイベント配信は「少なくとも1回」のため、まれに同じイベントが複数回届く可能性があります。重複投稿を確実に防ぎたい場合は、summary_record_id をキーに DynamoDB で投稿済みかを記録するなどの対策が必要です。

今回の2回の検証では、Investigation Completed は各1回ずつしか届かず、重複はありませんでした。

要約の見出しに依存する

今回は Markdown の見出しを手がかりに切り出しているため、要約の書式が変わると抽出できなくなる可能性があります。見つからなかった場合でも通知自体は送られるよう、「要約から見つかりませんでした」と表示するようにしています。

なお、Agent Space の言語を日本語(ja-JP)にしても、見出しは英語のままでした。見出しが本文の言語に左右されないため、見出しを手がかりに切り出す方法と相性がよいです。

Slack の文字数上限

Slack の section ブロックのテキストは3,000文字、header ブロックは150文字までです。要約が長い場合に備えて、各セクションを1,500文字で、見出しを150文字で切り詰めています。今回の2回の検証では、根本原因が約400〜950文字、緩和策が約120〜210文字だったので、切り詰めは発生しませんでした。

まとめ

今回は、DevOps Agent の Investigation Completed イベントを使い、調査が完了したときだけ「根本原因」と「緩和策」を Slack に通知する仕組みを作りました。

EventBridge のネイティブイベントを使うことで、完了を待つためのポーリングや Durable Functions が不要になり、Lambda は要約の取得と投稿だけのシンプルな処理で済みます。実際に、完了イベントが届いてから1〜2秒で通知が届きました。

検証で分かったポイントは次のとおりです。

  • 根本原因は investigation_summary_md に、緩和策は同じ実行の mitigation_summary_md に書かれている。緩和策のレコードは完了イベントより前に作られるため、Investigation Completed だけで両方を通知できる
  • 要約の見出しは、Agent Space の言語を日本語にしても英語のまま変わらない
  • 標準の Slack 連携では、投稿が止まってから調査が完了するまで約4分の差があった。調査が完了したかどうかは、完了イベントで判断するのが確実

標準の Slack 連携の投稿が多すぎると感じている方は、ぜひ試してみてください。

最後までお読みいただきありがとうございました!
どなたかのお役に立てれば幸いです。

以上、おつまみでした!

参考

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事