CloudWatch Pipelines の AI アシスト設定はどこまで使える?生成された Grok 設定を実機で採点してみた

CloudWatch Pipelines の AI アシスト設定はどこまで使える?生成された Grok 設定を実機で採点してみた

CloudWatch Pipelines の AI アシスト設定(自然言語からの Grok・プロセッサー生成)を、複雑度の異なる3レベルのログで検証しました。机上ではほぼ満点だった生成設定が実機では全行素通しになった原因(CloudWatch Pipelines の Grok 方言仕様)と、実用に必要な運用ワークフローを紹介します。
2026.07.14

はじめに

こんにちは!ひろたこです。
今回は 2026年4月に追加された CloudWatch Pipelines の AI アシスト設定(自然言語からプロセッサー設定を自動生成する機能)が、実務のログでどこまで正確な設定を生成するかを検証してみました!

Grok パターンの手書き、つらくないですか?パターン名を覚えられない、エスケープでハマる、テストが面倒...。「自然言語で書けば AI が Grok を作ってくれる」というこの機能、そのまま信用していいのか気になりますよね。

先に検証結果の要約です。生成された Grok は、一般的な Grok の常識で採点すると机上ほぼ満点(24/25 など)。ところが**実機では3レベルとも全行 processing error で素通し(0行変換)**でした。原因は精度ではなく CloudWatch Pipelines の Grok 方言仕様で、手書きの設定でも同じ罠を踏みます。最小修正を入れれば全レベル変換成功まで持っていけたので、「どう使えば実用になるか」の運用ワークフローまで含めてまとめています。

CloudWatch Pipelines とは

CloudWatch Pipelines は、テレメトリ(ログ・メトリクス)を取り込み時に変換するフルマネージドのデータコレクターです。ログの出力先は CloudWatch Logs のみで、Grok・CSV・JSON などのパーサーやフィールド加工のプロセッサーを直列に適用できます。

主な仕様・制限は以下の通りです。

項目 内容
プロセッサー数 1パイプライン最大20個を直列適用
プロセッサーのカテゴリ Parser(Grok/CSV/JSON/OCSF 等)/ Transformer / String / Filter(Drop Events)
パイプライン数の上限 Logs pipelines 330/アカウント(CloudWatch Logs ソース 300 + その他 30)、Metrics pipelines 最大300
設定形式 YAML。パーサーはパイプラインの先頭に置く
Grok の制約 1パイプラインに1個まで、パターン最大512文字
条件付き実行 when 式(最大256文字)

なお、プロセッサーカタログに複数ログイベントを1つに結合するプロセッサーは存在しません(マルチライン結合は CloudWatch Agent 側の multi_line_start_pattern 等の責務)。このため今回の検証では、複雑ケースは「複合要件」に絞っています。

AI アシスト設定とは

2026年4月のアップデートで、パイプライン作成ウィザードの Processor ステップに AI assisted トグルが追加されました。

  • 入力欄は Prompt(要件の自然言語)と Sample log(ログ例)の2欄
  • 生成は体感1〜2秒。生成結果には「Generated by AI」バッジが付き、レビュー・編集可能
  • 追加課金なし。IAM 権限として logs:GeneratePipeline が必要

image-7

また、AI アシストはコンソール限定です。

$ aws logs generate-pipeline help
aws: [ERROR]: An error occurred (ParamValidation): argument operation: Found invalid choice 'generate-pipeline'

ただし、パイプライン自体の管理 API は aws observabilityadmin 名前空間にあります(list-telemetry-pipelines / get-telemetry-pipeline / create-telemetry-pipeline / update-telemetry-pipeline)。デプロイ済み設定の YAML 全文は以下で取得できます。

$ aws observabilityadmin get-telemetry-pipeline --pipeline-identifier <ARN> \
    --query "Pipeline.Configuration.Body" --output text

環境・前提条件

項目 内容
AWS CLI aws-cli/2.35.16 Python/3.14.6 Darwin/25.5.0 source/arm64
OS macOS 26.5.2(Darwin 25.5.0, arm64)
リージョン ap-northeast-1(東京)※公式ドキュメントの Region availability で東京対応を確認
IAM AdministratorAccesslogs:GeneratePipeline を含む)
検証日 2026年7月13日〜14日

検証の設計

「なんとなく試して、なんとなく良さそう」で終わらせないために、採点基準を事前に固定してから検証しました。

3レベルのサンプルログ(個人情報はダミー):

レベル 内容 意地悪ポイント
1: Apache combined 形式のアクセスログ 25行 304 で bytes が - の行、URLエンコード日本語パス、HEAD メソッド
2: 独自形式 日本語メッセージのアプリログ 25行 code= が有る行10行/無い行15行の任意項目
3: 複合要件 Apache combined + 認証ユーザー名 20行(2xx 12行/非2xx 8行) 「パース + 2xx を drop + user をマスク」の複合要件

レベル2のログはこんな感じです。

2026-07-13T10:00:05+09:00 [ERROR] user=12345 req_id=a1b2c3-003 決済処理に失敗しました code=E4002

比較軸は3つ用意しました。

  • 日本語 vs 英語プロンプト
  • 同一入力×3回の再現性
  • 手書きリファレンス設定(事前作成)との比較

プロンプトの与え方は「Prompt 欄 = 要件文のみ / Sample log 欄 = ログ例」に統一しています。レベル1の日本語プロンプトはこちらです。

Apache の combined 形式のアクセスログです。クライアントIP、HTTPメソッド、リクエストパス、ステータスコード、レスポンスサイズ(バイト)をフィールドとして抽出してください。

やったこと

1. 準備: サンプルログ投入とデータソースタグ

まずサンプルログをロググループに投入します。

$ ./scripts/put-logs.sh sample-logs/level1-apache.log /ai-assist-test/level1-apache
created log group: /ai-assist-test/level1-apache
done: 25 events -> /ai-assist-test/level1-apache / test-20260713-151810

そして重要なのがここ。CloudWatch Logs をソースにする場合、パイプラインの対象はロググループ名の直接指定ではなく、ロググループへのタグ付けで決まります

$ aws logs tag-resource \
    --resource-arn "arn:aws:logs:ap-northeast-1:<ACCOUNT_ID>:log-group:/ai-assist-test/level1-apache" \
    --tags "cw:datasource:name=apache_level1,cw:datasource:type=default"
  • cw:datasource:name は15文字以内、aws/amazon 始まり不可
  • カスタムログの cw:datasource:typedefault
  • 同一の name + type の組は2つのパイプラインで併用不可

なお、CloudWatch Logs ソースは元のロググループのメッセージを直接書き換えます(ウィザードにも "the original log messages will not be preserved" の警告バナーが出ます。Keep original log トグルは vended ログソース限定)。取り込み時処理なので、パイプライン作成前の既存イベントは変換されません。

全体の流れを図にするとこうです。別の場所に出力されるのではなく、同じロググループに「変換後の姿」で格納されるのがポイントです。

【CloudWatch Logs ソースの処理フロー】

ログ投入 ──► 取り込み時にインターセプト ──► プロセッサー処理 ──► 同じロググループに格納
             (cw:datasource タグで対象判定)        │
                                                  ├─ 成功: 変換後の JSON(原文は残らない)
                                                  └─ 失敗: 原文のまま素通し
                                                   (@pipeline.processing.status = error が付く)

2. レベル1: Apache アクセスログの生成結果

日本語プロンプトで生成された Grok がこちらです。

image-5

%{IPORHOST:client_ip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "%{WORD:http_method} %{URIPATHPARAM:request_path} HTTP/%{NUMBER:http_version}" %{NUMBER:status_code} %{NUMBER:response_size}

正直、これを見た瞬間は「すごい…」と思いました。

  • フィールド命名はスネークケースで妥当(日本語の「クライアントIP」もちゃんと client_ip に)
  • パスに %{URIPATHPARAM} を採用しており、クエリ付きパスや URL エンコードされた日本語パスも机上では正しく抽出

このパターンを一般的な正規表現として展開し、サンプル25行に適用した机上採点は 24/25。唯一の不一致は bytes が - になる 304 の行(%{NUMBER:response_size}- にマッチしない)だけでした。事前に用意した手書きリファレンスはここを吸収済みだったので、そこだけ人間の勝ちです。

英語プロンプトでも試したところ、同じ要件なのに referrer / user_agent まで抽出する7フィールド構成になりました(要求外まで拾う)。机上採点は同じく 24/25 です。

image-9

3. レベル2: 独自フォーマット(日本語ログ)の生成結果

%{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:log_level}\] user=%{NUMBER:user} req_id=%{DATA:req_id} %{GREEDYDATA:msg}(?: code=%{DATA:code})?

image-12

机上採点は match 25/25。ただし code の抽出は 0/10 でした。%{GREEDYDATA} の実体は正規表現の .* で、**貪欲マッチ(できるだけ長い範囲にマッチする挙動。最長一致とも)**をします。末尾の (?: code=%{DATA:code})? は「あってもなくてもよい」任意指定のため、msg が 決済処理に失敗しました code=E4002 を丸ごと飲み込んでも全体のマッチは成立してしまい、code は常に空になります。正規表現ではよく知られたパターンミスです。

英語版もフィールド名(level/log_level)以外は同一で、同じ欠陥も共通でした。

4. レベル3: 複合要件の生成結果

「パース + 2xx を drop + user をマスク」の複合要件では、Grok + 条件付き delete_entries + substitute_string の3プロセッサー構成が生成されました。以下は aws observabilityadmin get-telemetry-pipeline で取得したデプロイ済み YAML です(source/sink 部分は省略)。

processor:
  - grok:
      match:
        - '%{IPORHOST:client_ip} %{USER:ident} %{USER:user}
          [%{HTTPDATE:timestamp}] "%{WORD:method} %{URIPATHPARAM:request_path}
          HTTP/%{NUMBER:http_version}" %{NUMBER:status_code}
          %{NUMBER:response_size}'
  - delete_entries:
      with_keys:
        - ident
        - http_version
      when: status_code >= 200 and status_code < 300
      handleWhenFailure: skip
  - substitute_string:
      entries:
        - source: user
          target: user
          from: .*
          to: "****"

image-14

要件を3プロセッサーに分解する構造は見事なのですが、よく見ると要件充足に問題があります。

  • 「2xx を drop」が drop_events として生成されていない。AI は「2xx のとき delete_entries を実行」という条件付きフィールド削除として実装しました。when 式は公式ドキュメントの例文と完全一致なのですが、プロセッサーの選択が誤りです(delete_entries はフィールドを消すだけで、イベント自体は破棄されません)
  • マスクの from: .* では、**** ではなく ********(二重適用)になります。substitute_string はマッチした箇所をすべて置換する仕様のため(公式リファレンス。後方互換元の Log transformer 版 substituteString には "replaces all matches" と明記)、.* だと文字列全体にマッチしたあと末尾の空文字列にもう一度マッチして置換が2回走るのです。正しくは .+ です

英語版はさらに要件充足度が低く、「2xx を drop」がどこにも実装されていませんでした(drop_events なし、delete_entries にも条件なし)。Grok も %{IP}(IPORHOST でなく)、パスに %{DATA} と設計思想が別物です。

5. 生成の再現性と日本語 vs 英語

同一入力×3回の再現性はこうなりました。

レベル 3回の生成結果の差分
1 構造・採用パターンは3回とも同一。フィールド名だけ揺れるmethod / http_method
2 同上(log_level / level の揺れのみ)。GREEDYDATA の欠陥は3回とも共通
3 Grok は3回とも同一。delete_entries のキーが揺れる(2回目のみ timestamp 追加)。drop_events 欠落は3回とも共通

生成の骨格は安定していて、揺らぎは「フィールド命名」と「補助的な削除キー」という周辺部分に出ます。裏返すと間違いも安定して再現されるので、ガチャを回し直しても構造的な誤りは直りません。

またフィールド名が生成のたびに揺れるため、再生成すると下流(メトリクスフィルター・ダッシュボード・アラーム)のフィールド参照が壊れうる点は運用上の注意です。

日英比較はこちらです。

レベル 日英差
1 ja: 要求5フィールド+補助のみ / en: referrer・user_agent まで7フィールド。要求充足はほぼ互角
2 命名以外同一。同じ欠陥
3 ja: 2xx 条件を誤ったプロセッサーに付与 / en: 2xx 要件が未実装。ja のほうが修正距離が近い

単純な抽出では「日本語プロンプトだと精度が落ちる」傾向は見られませんでした。複合要件になると生成の設計自体が別物になり、言語差より「要件の複雑さ」が支配的という印象です。

6. 実機テスト: まさかの全行素通し

ここまでの机上採点なら「304 の - と GREEDYDATA だけ直せば十分実用」という結論になりそうです。ところが。

パイプライン3本(ai-assist-level1/2/3、いずれも ACTIVE)を作成してログを再投入したところ、3レベルすべてで1行も変換されず、原文のまま格納されました。コンソール上は作成成功・ACTIVE で、エラーはどこにも通知されません。

7. 原因の切り分け

切り分けはこう進めました。

  1. IAM は正常(パイプラインロールに logs:processWithPipeline、信頼ポリシーも logs.amazonaws.com
  2. CloudTrail で、ログ投入のたびに logs.amazonaws.com がパイプラインロールを Assume していることを確認。インターセプト自体は動いている
  3. get-log-record(Logs Insights の @ptr 経由)でシステムフィールドを取得すると、決定的な証拠が出ました
"@data_source_name": "apache_level1",
"@data_source_type": "default",
"@pipeline.arn": "arn:aws:observabilityadmin:...:telemetry-pipeline/...",
"@pipeline.processing.status": "error"

プロセッサー処理がエラーで落ちて素通しになっていたのです。エラーでもイベントは失われず原文のまま格納される、フェイルオープンな挙動でした。ログが消えないのは安心設計なのですが、静かに失敗されると気づけないんですよね...。

「素通し」に気づくには Logs Insights のシステムフィールドが決定的です。

fields @timestamp, @logStream, @message, @pipeline.processing.status, @pipeline.arn, @data_source_name
| sort @timestamp desc | limit 20

処理失敗イベントには @pipeline.processing.status = error が付きます。パイプライン作成に投入したイベントには @pipeline.* フィールド自体が付かないので、この1クエリで「適用前 / 適用後 / 失敗」を見分けられます。

image-22

image-23

なお、メトリクスでの検知は現状できないようです(AWS/Logs 名前空間に処理エラー系メトリクスは見当たらず、流量系のみ)。アラームで failsafe を張る手段がないので、Insights での定期確認が現実解になります。

原因の特定には、原因調査専用の使い捨てパイプラインaws observabilityadmin create/update-telemetry-pipeline で別途作り、Grok パターンを1要素ずつ差し替えながらテスト投入する二分探索を使いました。地道ですが、これで原因がすべて仕様として確定できました。

8. 判明した Grok 方言仕様

この対照実験の結果、CloudWatch Pipelines の Grok には一般的な Grok と異なる方言仕様があることがわかりました。この記事で一番持ち帰ってほしい表です。

# 仕様 実証方法 引っかかった設定
1 [ ] はエスケープ不可。リテラルとしてそのまま書く(\[ と書くとパターン全体がエラー) \[...\] 版→素通し / [...] 版→変換成功の対照実験 AI の level1・level2(level3 だけ未エスケープ=正しかった)
2 行全体の完全一致が必要(部分一致ではない)。行末まで消費しないと不一致 level1 パターンそのまま→素通し / 末尾に %{GREEDYDATA:rest} を足す→成功 AI の level1-ja・level3(referrer/user_agent 未カバー)
3 (?:...) などのカスタム正規表現構文は不可(事前定義パターンの組み合わせのみ。公式にも "Creating custom patterns is not allowed") (?: code=...)? を含む版→素通し / 外す→成功 AI の level2。手書きリファレンスも同罪
4 drop_events は cloudwatch_logs ソースでは使用不可 作成が PROCESSOR_SOURCE_INCOMPATIBLE: drop_events processor is not supported for cloudwatch_logs sources で拒否される レベル3の「2xx を drop」はこのソースタイプでは実現不能。手書きリファレンスも無効
5 公式ドキュメントの Grok 設定例 match: {source_key: [...]} は cloudwatch_logs ソースでは無効(ソースキー無しの配列が正) API 作成時に cloudwatch_logs source parsers must not have source key defined and grok match should be array AI 生成の配列形式が正しかった(ドキュメントと実装の乖離)

ここで強調したいのは、事前に用意した手書きリファレンス((?:...|-) / (?: code=...)? / \[ / drop_events 使用)も、この方言では level1〜3 すべて無効だったことです。つまり「AI だから間違えた」のではなく、「一般的な Grok の知識がこの方言に通用しない」 が正しいそうです。

ちなみに生成の揺らぎはエスケープの有無にも出ていて、level1/2 はエスケープ済み、level3 は未エスケープでデプロイされました。level3 の角括弧は「揺らいだ結果たまたま方言に合っていた」のですが、行末未消費(仕様2)で結局エラーになっています。

9. 最小修正で再デプロイ → 全レベル変換成功

方言仕様がわかれば、修正は最小限で済みます。aws observabilityadmin update-telemetry-pipeline で以下の修正版に差し替えて再投入しました。

  • level1: \[ \][ ]%{NUMBER:response_size}%{NOTSPACE:response_size}(304 の - 対応)、末尾に "%{DATA:referrer}" "%{DATA:user_agent}" を追加
  • level2: \[ \][ ](?: code=%{DATA:code})? を削除し、代わりに extract_value プロセッサーを追加
  • level3: 末尾に "%{DATA:referrer}" "%{DATA:user_agent}" を追加、substitute_string の from を .*.+

level2 の任意項目 code= は、Grok 本体では方言的に書けないので extract_value に逃がすのが正解でした。

- extract_value:
    entries:
      - source: msg
        target: code
        from: 'code=(?<c>\S+)'
        to: '${c}'

extract_valuefrom は本物の正規表現で、named capture も使えます。Grok で書けない「任意項目の抽出」はこちらの担当、という役割分担ですね。

実測結果はこの通り、全勝です!

level1: JSON化 25/25、必須5フィールド 25/25(304行も {"status_code": "304", "response_size": "-"} で抽出成功)
level2: JSON化 25/25、code 抽出 10/10(E4002×2, E4013, E5001, E6001, E9001, W1001, W2003, W3002, W5001)
  日本語 msg も正常: {"log_level": "ERROR", "msg": "決済処理に失敗しました code=E4002", "code": "E4002"}
level3: JSON化 20/20、user マスク 20/20(.+ 修正により正しく "****")
  ※ drop は要件ごと不成立(プラットフォーム制約)のため 20行残るのは想定どおり

変換成功時は message が抽出フィールドの JSON に置き換わります。

{"client_ip":"192.0.2.10","ident":"-","auth":"-","timestamp":"13/Jul/2026:10:00:01 +0900",...}

ハマったポイント

検証中のハマりを正直に共有します。同じことを試す方の参考になれば!

ウィザードの「ログソース名」はロググループ名ではない

最初、ログソース名に /ai-assist-test/level1-apache と入力したら Invalid になりました("Must start and end with a lowercase letter or number. Can contain lowercase letters, numbers, and underscores.")。

正体は「やったこと 1.」で書いた通り、ロググループへのタグ付け(cw:datasource:name / cw:datasource:type)で対象を決める設計だからです。ロググループ名を入れる欄ではありませんでした。データソースという抽象を挟む設計だと理解してからは腹落ちしましたが、初見では戸惑うポイントだと思います。

AI assisted モードでは Test processors が表示されない(最重要)

今回の検証で最も重要な運用上の注意点がこれです。

ウィザード/編集画面には設定をサンプルログでテストできる Test processors ペインがあるのですが、これは Manual モードでしか表示されず、AI assisted トグルが ON の間はボタンが消えます。つまり「AI で生成 → そのまま作成」の動線では、テストを一度も促されません。

私は今回まさにこの動線に乗って、1行もパースできない設定をノーチェックでデプロイしていました。Manual に切り替えて生成済み設定を Test processors に流すと、ちゃんと Processor 0: Grok pattern was unable to capture any fields の警告と空の Output が出ます。作成前に気づけたはずなんですよね...。

image-21

学びはシンプルで、「AI で生成 → Manual に切り替え → Test processors でサンプルを流す → 作成」を必須ワークフローにすること。Manual に切り替えても生成済み設定はそのまま引き継がれるので、手間は数クリックです。この一手間で「机上満点・実機全滅」は確実に防げます。

数値比較の when は convert_entry_type が必須

レベル3の修正後にもう1つ追加検証をしました。条件付き delete_entries の when: status_code >= 200 and status_code < 300 が、2xx の行でも発火していなかったのです。

原因は型です。Grok の抽出値は文字列(公式にも "Grok patterns don't support type conversions" とあります)なので、文字列 "200" と数値の比較評価が失敗し、handleWhenFailure: skip によって黙ってスキップされていました。2xx の12行すべてで ident / http_version が残存していたわけです。

grok と delete_entries の間に以下を挟むと解決することを実機で確認しました。

- convert_entry_type:
    key: status_code
    type: integer
{"client_ip":"192.0.2.10","user":"****",...,"status_code":200,...}

200 の行では ident / http_version が削除され(発火)、500 の行では残存(条件どおりスキップ)と、期待通りの動きになりました。

公式ドキュメントの式例そのままでは、grok 直後の文字列フィールドには効きません。評価失敗はデフォルト skip で黙殺されるため気づく手段がなく、AI 生成にも convert_entry_type は含まれていませんでした。数値比較の when を書くときは convert_entry_type とセット、と覚えておくのがよさそうです。

そのほか小ネタ

ログ例を Sample log 欄ではなく Prompt 本文に埋め込むと、同じ要件文でも生成結果が変わります(レベル1で Grok のみ → Grok + delete_entries の2プロセッサー構成に)。生成結果は入力の「置き場所」にも依存するので、Prompt = 要件のみ、Sample log 欄 = ログ例に統一するのがおすすめです。

結果

採点表の確定値です。

レベル 机上採点(正規表現として) 実機(そのまま) 動くまでに必要な修正 修正後
1: Apache 24/25 0/25(全行 processing error) 2箇所(エスケープ除去・行末まで消費)+1(304対応) 25/25
2: 独自 25/25(code 0/10) 0/25 2箇所 + extract_value 追加 25/25、code 10/10
3: 複合 20/20 相当 0/20 2箇所(行末消費・.+)。drop 2xx は実現不能 パース&マスク 20/20

精度の問題ではなく方言適合の問題で全滅した、というのが今回の核心です。AI アシストへの評価をひとことで言うと、パターン構造・フィールド命名・要求の解釈は良質。ただし自身が動くランタイムの方言を知らない、です。

まとめ

今回は CloudWatch Pipelines の AI アシスト設定を、3レベルのログ×日英プロンプト×再現性3回で検証しました!

わかったこと:

  • 生成品質そのものは良質。構造・命名・要求解釈は机上ほぼ満点で、単純な抽出なら日本語プロンプトでも精度は落ちない
  • ただし CloudWatch Pipelines の Grok は方言。角括弧エスケープ不可・行全体完全一致・カスタム正規表現不可・cloudwatch_logs ソースでは drop_events 非対応。一般的な Grok の常識で書いた設定は、人間でも AI でも実機で全滅する
  • 処理エラーはフェイルオープンで素通しになり、コンソールは ACTIVE のまま通知なし。検知は Logs Insights の @pipeline.processing.status が決定的(メトリクスでの検知は現状不可のようです)
  • AI assisted モード中は Test processors が表示されない。「生成 → Manual に切り替え → Test processors → 作成」を必須ワークフローにすれば、この機能は十分実用になる
  • 数値比較の whenconvert_entry_type とセットで。評価失敗は黙ってスキップされる

個人的には、Grok パターンの叩き台を1〜2秒で出してくれる体験自体はかなり好印象でした! 手で書くより明らかに速いですし、URIPATHPARAM の選択や3プロセッサーへの要件分解など「わかってる」感のある生成が返ってきます。だからこそ、Test processors での実機確認を挟む一手間だけは省かないでほしい、というのが検証を終えた私からのお願いです。生成 AI の出力は「動くまで信用しない」——今回ほどこれを体で覚えた検証はありませんでした。

この記事が、CloudWatch Pipelines でログ変換を組む誰かの助けになれば幸いです。
最後まで読んでいただき、誠にありがとうございました。

参考記事

この記事をシェアする

関連記事