OCI Generative AIでアラート通知を自動要約!Functionsの実装と検証結果【後編】

はじめに

NTT西日本株式会社2年目社員の山塚です。前編では、OCIアラート通知のJSON問題を解決するための設計思想と、OCI Generative AI(GenAI)プライベートエンドポイントを活用したクロスリージョン構成の全体像を解説しました。

後編となる本記事では、実際のFunctionsの実装コードプロンプト設計のポイント、そして検証結果について詳説します。特に、「AIが生成した要約を運用でどう活かすか」「どんなハマりポイントがあるか」という実践的な内容にフォーカスします。

※本記事は2026年2月時点の情報に基づきます。

※本記事では生成AIを活用した内容が含まれており、AIによる情報の生成過程でハルシネーション(事実に基づかない情報の生成)が発生する可能性があります。AIが生成した「詳細分析」や「推奨されるアクション」は参考情報としてご活用いただき、実際の運用判断は必ずご自身で確認のうえ行ってください。

対象読者

本記事は、以下の方を対象としています。

  • OCI Functionsの実装例を知りたいエンジニア
  • GenAIのプロンプト設計に興味がある方
  • アラート運用の効率化を具体的な数値で検討したい方

前提知識: 本記事では、以下の知識があることを前提としています。

  • Pythonの基本的な文法
  • OCI Functionsのデプロイ経験(fn deploy コマンドの実行)
  • OCI SDKの基本的な使い方
  • 前編で解説したクロスリージョン構成の理解

目次

  1. Functionsの処理フロー
  2. 開発・デプロイの流れ
  3. 動的グループとIAMポリシー
    1. 動的グループの設定
    2. IAMポリシーの設定
  4. Functionsの実装ポイント
    1. 環境変数で設定を外出し
    2. リソースプリンシパル認証
    3. ONS経由の「封筒」を展開する
    4. 通知タイプの判定
    5. アーカイブ保存
  5. プロンプト設計のポイント
    1. 通知タイプに応じた指示の切り替え
    2. 出力形式の固定
    3. パラメータ設定
    4. GenAI呼び出しの実装
  6. 使用モデル:Command R+
  7. Before / After:実際の要約サンプル
  8. テストしたアラートの種類
  9. 検証結果:レイテンシとコスト
    1. レイテンシ
    2. コスト
  10. ハマりポイント・Tips
    1. DNS設定を忘れずに
    2. セキュリティリストはサブネット内通信も許可が必要
    3. Functionsのタイムアウト設定
    4. ONS経由の「封筒」展開を忘れない
    5. エラー時のフォールバック
  11. 今後の展望
  12. まとめ

1. Functionsの処理フロー

OCI Functionsでは、以下の流れで処理を行います。

図1. Functionsの処理フロー

各ステップの詳細は以下の通りです。

ステップ 処理内容 補足
① JSON通知を受信 アラーム定義、イベント、Connector HubからJSON形式の通知を受信
② ONS(Oracle Notifications Service)封筒を展開 トピック経由の場合、{"Type": "Notification", "Message": "..."} 形式で包まれているため展開 アラーム定義経由の場合のみ
③ バケットに原本を保存 受信したJSONをそのままObject Storageに保存 ハルシネーション対策
④ 通知タイプを判定 JSONの構造から、アラーム/イベント/ログ検知を判定 後続のプロンプト選択に使用
⑤ GenAI APIを呼び出し プライベートエンドポイント経由でGenAIに要約をリクエスト RPC経由で大阪へ
⑥ 通知用トピックに送信 要約結果をトピックに送信し、メールなどに配信

2. 開発・デプロイの流れ

Functionsのデプロイは以下の手順で行いました。

図2. 開発・デプロイの流れ

ディレクトリ構成

my-function/
├── func.py           # メインのFunctionsコード
├── func.yaml         # Functions設定ファイル
├── requirements.txt  # Python依存パッケージ
└── Dockerfile        # (カスタムイメージの場合)

func.yaml の例

schema_version: 20180708
name: alert-summarizer
version: 0.0.1
runtime: python
build_image: fnproject/python:3.9-dev
run_image: fnproject/python:3.9
entrypoint: /python/bin/fdk /function/func.py handler
memory: 256
timeout: 120

requirements.txt の例

fdk>=0.1.50
oci>=2.90.0

デプロイコマンド

# OCIRへのログイン
docker login <リージョンコード>.ocir.io

# Functionsのデプロイ
fn deploy --app <アプリケーション名>

3. 動的グループとIAMポリシー

FunctionsからGenAIやObject Storage、通知サービスを呼び出すには、適切な権限が必要です。今回は動的グループIAMポリシーを使って権限を付与しました。

3.1 動的グループの設定

動的グループは、特定の条件に一致するリソースを自動的にグループ化する機能です。Functionsを動的グループに所属させることで、コード内にクレデンシャルを埋め込む必要がなくなります。

【動的グループのマッチングルール】  
※動的グループ名は参考例として記載しています。  

名前: alert-summarizer-functions
マッチングルール:
ALL {resource.type = 'fnfunc', resource.compartment.id = 'ocid1.compartment.oc1..xxxxx'}

3.2 IAMポリシーの設定

動的グループに対して、必要なリソースへのアクセス権限を付与します。

【ポリシーステートメント】

Allow dynamic-group alert-summarizer-functions to manage objects in compartment <コンパートメント名>
Allow dynamic-group alert-summarizer-functions to read buckets in compartment <コンパートメント名>
Allow dynamic-group alert-summarizer-functions to use ons-topics in compartment <コンパートメント名>
Allow dynamic-group alert-summarizer-functions to use generative-ai-family in compartment <コンパートメント名>

各ポリシーの解説:

ポリシー 用途 補足
manage objects Object Storageへのオブジェクト操作 ログの保存に必要
read buckets バケットのリスト取得 manage objects だけでは不十分
use ons-topics トピックへのメッセージ送信 要約結果の配信に必要
use generative-ai-family GenAI APIの呼び出し 大阪リージョンのGenAIにも適用される

注意点:manage objectsread buckets は別々のポリシーが必要 オブジェクトの操作権限(manage objects)だけでは、バケット自体をリストする操作ができません。read buckets を追加することで、バケット名の取得やバケット一覧の参照が可能になります。


4. Functionsの実装ポイント

本章では、実装の要点となる処理を抜粋して解説します。各処理の考え方や設計の意図が伝わるよう、重要な箇所にフォーカスして紹介します。

4.1 環境変数で設定を外出し

OCIDやエンドポイントなどの設定値は、Functionsの構成(Configuration)で環境変数として設定します。コードにハードコーディングしないことで、環境ごとの差分を吸収できます。

import os

# ---------------------------------------------------------------------------
# 設定値 (Configuration)
# コンソールの Functions > Configuration で以下のキーを設定してください。
# ---------------------------------------------------------------------------

# 【必須設定】ユーザー環境依存 (東京リージョン)
DESTINATION_TOPIC_OCID = os.environ.get("DESTINATION_TOPIC_OCID")
ARCHIVE_BUCKET_NAME = os.environ.get("ARCHIVE_BUCKET_NAME")
ARCHIVE_NAMESPACE = os.environ.get("ARCHIVE_NAMESPACE")

# 【GenAI設定】大阪リージョン
GENAI_ENDPOINT = os.environ.get(
    "GENAI_ENDPOINT", 
    "https://inference.generativeai.ap-osaka-1.oci.oraclecloud.com"
)
# Command R+ のモデルOCID
MODEL_ID = os.environ.get(
    "MODEL_ID", 
    "ocid1.generativeaimodel.oc1.ap-osaka-1.amaaaaaxxxxxxxx"
)
# GenAI実行権限を持つコンパートメントID
GENAI_COMPARTMENT_ID = os.environ.get(
    "GENAI_COMPARTMENT_ID", 
    "ocid1.compartment.oc1..aaaaaaaxxxxxxxx"
)

プライベートエンドポイントを使用する場合:

# プライベートエンドポイントを使用する場合は、
# DNS接頭辞を含むFQDNを指定
GENAI_ENDPOINT = os.environ.get(
    "GENAI_ENDPOINT",
    "https://mygenai.inference.generativeai.ap-osaka-1.oci.oraclecloud.com"
)

図3. Functionsの構成画面(環境変数設定)

※「MODEL_ID」については、事前に入力したcommand R+を使用したため構成の設定はしていませんが、別のモデルを使用したい場合、構成で設定すると設定したほうが優先されます。その点だけお気を付けください。

4.2 リソースプリンシパル認証

OCI SDKの認証には、リソースプリンシパルを使用します。これにより、Functionsの実行環境で自動的に認証が行われます。

import oci

def handler(ctx, data: io.BytesIO = None):
    # リソースプリンシパル認証
    # Functionsの実行環境で自動的に認証情報が取得される
    signer = oci.auth.signers.get_resource_principals_signer()
    
    # 各クライアントで signer を指定
    os_client = oci.object_storage.ObjectStorageClient(
        config={}, 
        signer=signer
    )
    
    ons_client = oci.ons.NotificationDataPlaneClient(
        config={}, 
        signer=signer
    )
    
    genai_client = oci.generative_ai_inference.GenerativeAiInferenceClient(
        config={}, 
        signer=signer, 
        service_endpoint=GENAI_ENDPOINT  # プライベートエンドポイントを指定
    )
    
    # 以降の処理...

リソースプリンシパル認証のメリット:

メリット 詳細
クレデンシャル不要 コードにAPIキーやシークレットを埋め込まない
自動ローテーション 認証情報は自動的に更新される
IAMポリシーで制御 動的グループとポリシーで権限を管理

4.3 ONS経由の「封筒」を展開する

アラーム定義からトピック経由でFunctionsを呼び出すと、JSONが「封筒」で包まれた形式で届きます。この処理を見落とすと、パースがうまくいきません。

def handler(ctx, data: io.BytesIO = None):
    # ... 認証処理 ...
    
    # 1. データ取得
    try:
        raw_body = data.getvalue()
        body_str = raw_body.decode('utf-8')
        body_json = json.loads(body_str)
    except Exception as e:
        logging.getLogger().error(f"JSON Load Error: {e}")
        return response.Response(ctx, status_code=400, response_data="Invalid JSON")

    # 2. ONS(トピック)経由のラップ(封筒)を剥がす処理
    # アラームがトピックを経由すると以下の形式で届く
    # {"Type": "Notification", "Message": "..."}
    target_payload = body_json  # デフォルトはそのまま
    
    try:
        if (isinstance(body_json, dict) and 
            body_json.get("Type") == "Notification" and 
            "Message" in body_json):
            
            logging.getLogger().info("Detected ONS Notification envelope. Unwrapping...")
            # Messageの中身は文字列化されたJSONなので、再度パースする
            target_payload = json.loads(body_json["Message"])
            
    except Exception as e:
        logging.getLogger().warning(f"ONS Unwrap Warning: {e} - Proceeding with original body.")
        # パース失敗時は元のデータをそのまま使う

ONS封筒の構造:

{
  "Type": "Notification",
  "MessageId": "xxx",
  "TopicArn": "xxx",
  "Subject": "high-cpu-alarm",
  "Message": "{\"type\":\"OK_TO_FIRING\",\"alarmMetaData\":[...]}",
  "Timestamp": "2026-02-14T05:30:00.000Z"
}

Message フィールドの中身は文字列化されたJSONであるため、再度 json.loads() でパースする必要があります。

4.4 通知タイプの判定

受信したJSONの構造から、どのサービスからの通知かを判定します。これにより、後続のGenAI呼び出しで適切なプロンプトを選択できます。

def parse_generic_input(body):
    """
    入力JSONに応じて適切な情報を抽出するパーサー
    """
    info = {}
    
    # デフォルト値
    info['payload_str'] = json.dumps(body, ensure_ascii=False, indent=2)
    info['type'] = '【その他通知】'
    info['title'] = 'Notification'
    info['timestamp'] = datetime.now().isoformat()

    # --- 1. OCI Alarms (Monitoring) ---
    # alarmMetaData キーの存在で判定
    if isinstance(body, dict) and 'alarmMetaData' in body:
        info['type'] = '【アラーム】'
        # ... 省略 ...

    # --- 2. OCI Events (CloudEvents) ---
    # eventType キーの存在で判定
    elif isinstance(body, dict) and 'eventType' in body:
        info['type'] = '【イベント】'
        # ... 省略 ...

    # --- 3. Connector Hub (OCI Logging) ---
    # リスト形式で届く
    elif isinstance(body, list) and len(body) > 0:
        info['type'] = '【ログ検知】'
        # ... 省略(先頭10件に絞ってトークン節約)...

    return info

各通知タイプの判定ロジック:

通知タイプ 判定条件 主要なキー
アラーム alarmMetaData キーの存在 severity, title, body
イベント eventType キーの存在 eventType, eventTime, data
ログ検知 リスト形式 先頭要素を解析

※Connector Hubについては、実際にFunctionsからObject Storageに格納されたJSONを見ると、ログの文字数が100万文字以上になっていたため先頭10件に絞ってGenAIに渡すことでトークン超過を防いでいます。

4.5 アーカイブ保存

生ログはバケットに保存します。ファイル名には日時と通知タイプを含め、後から探しやすいようにしています。

def archive_raw_log(signer, raw_bytes, body_json):
    """
    Object Storageへの保存処理 (東京)
    ファイル名: YYYYMMDD/HHMMSS_Type_UUID.json
    """
    os_client = oci.object_storage.ObjectStorageClient(config={}, signer=signer)
    
    # JSTで日時を取得
    now = datetime.now(timezone(timedelta(hours=9)))
    folder = now.strftime('%Y%m%d')
    timestamp = now.strftime('%H%M%S')
    
    # 通知タイプを判定してタグを決定
    source_tag = "Unknown"
    
    if isinstance(body_json, dict):
        if 'alarmMetaData' in body_json: 
            source_tag = "Alarm"
        elif 'eventType' in body_json: 
            source_tag = "Event"
            
    elif isinstance(body_json, list): 
        source_tag = "ConnectorHub"

    # ファイル名を生成
    object_name = f"{folder}/{timestamp}_{source_tag}_{uuid.uuid4().hex[:8]}.json"

    # Object Storageに保存
    os_client.put_object(
        namespace_name=ARCHIVE_NAMESPACE,
        bucket_name=ARCHIVE_BUCKET_NAME,
        object_name=object_name,
        put_object_body=raw_bytes,
        content_type="application/json"
    )
    
    return object_name

5. プロンプト設計のポイント

GenAIに要約させる際のプロンプト設計は、出力品質を大きく左右します。今回は以下の点を意識しました。

5.1 通知タイプに応じた指示の切り替え

アラーム、イベント、ログ検知など、通知タイプによって伝えるべき情報が異なります。プロンプト内で条件分岐し、それぞれに適した「概要」の書き方を指示しています。

def build_prompt(parsed_info, archive_filename):
    """
    通知タイプに応じたプロンプトを構築
    """
    notification_type = parsed_info.get('type', '')
    
    # デフォルトの指示
    specific_instruction = """
    「[リソース名]にて、[事象の内容]が発生しました」という形式で記述すること。
    """

    if notification_type == '【ログ検知】':
        specific_instruction = """
        - 「[HostName]上で稼働する[サービス名/プロセス名]にて、[エラー内容]が発生しました」という形式で記述すること。
        - JSON内の "ServiceLogPath" を参考に、どのミドルウェアのログか推測してサービス名を補完すること。
        """
        
    elif notification_type == '【アラーム】':
        specific_instruction = """
        - 「[対象リソース名]にて、[アラート名](現在の値: [数値])が発生しました」という形式で記述すること。
        - 緊急度(Severity)を強調すること。
        """
        
    elif notification_type == '【イベント】':
        specific_instruction = """
        - 「[リソース名]に対して、[イベント操作内容]が実行されました」という形式で記述すること。
        - 誰が(Principal)行った操作かが分かる場合は記載すること。
        """

    # プロンプト全体を構築(以下省略)
    # ...

注意:AIが生成する「詳細分析」や「推奨されるアクション」について これらの項目はAIが推測に基づいて生成するものであり、必ずしも正確とは限りません。実際の運用判断を行う際は、元のJSONログを確認し、ご自身で内容を検証してください。

5.2 出力形式の固定

一貫性のある出力を得るために、プロンプト内に出力構成を明示しています。

【期待する出力形式】

1. [概要]
   ... 通知タイプに応じた形式 ...

2. [詳細分析]
   ... 技術的な詳細と推測される原因 ...

3. [推奨されるアクション]
   1. ...
   2. ...
   3. ...

4. [ログ参照]
   パス: YYYYMMDD/HHMMSS_Type_UUID.json

Markdown禁止の理由: メール通知先ではMarkdownがレンダリングされないため、プレーンテキストで見やすい形式を指定しています。**太字**## 見出し は、そのまま文字として表示されてしまいます。

5.3 パラメータ設定

出力の一貫性を高めるため、以下のパラメータを調整しました。

パラメータ 設定値 理由
temperature 0.3 事実に基づいた安定した出力を得るため
max_tokens 1000 十分な長さの要約を許容
frequency_penalty 0 繰り返しのペナルティなし
presence_penalty 0 新しいトピックへのペナルティなし

5.4 GenAI呼び出しの実装

def call_genai_generic(signer, parsed_info, archive_filename):
    """
    OCI Generative AI (大阪リージョン) を呼び出して要約を生成する
    """
    genai_client = oci.generative_ai_inference.GenerativeAiInferenceClient(
        config={}, 
        signer=signer, 
        service_endpoint=GENAI_ENDPOINT
    )
    
    # プロンプトを構築
    prompt_text = build_prompt(parsed_info, archive_filename)

    # Command R+ 推奨の Chat API を使用
    chat_detail = oci.generative_ai_inference.models.ChatDetails(
        compartment_id=GENAI_COMPARTMENT_ID,
        serving_mode=oci.generative_ai_inference.models.OnDemandServingMode(
            model_id=MODEL_ID
        ),
        chat_request=oci.generative_ai_inference.models.CohereChatRequest(
            message=prompt_text,
            max_tokens=1000,
            temperature=0.3,
            frequency_penalty=0,
            presence_penalty=0
        )
    )

    logging.getLogger().info("Invoking GenAI...")
    response = genai_client.chat(chat_detail)
    
    result_text = response.data.chat_response.text
    
    return result_text

6. 使用モデル:Command R+

今回の検証では Cohere社のCommand R+ を使用しました。

OCI Generative AIのオンデマンドモデルでは複数のモデルが利用可能ですが、JSON要約という比較的シンプルなタスクであれば、どのモデルでも十分な品質が得られると考えています。

Command R+を選択した理由:

理由 詳細
日本語対応が良好 自然な日本語で出力される
指示追従性が高い プロンプトで指定した形式に従いやすい
コストパフォーマンス オンデマンドで従量課金

他のモデルでも可能: Meta Llama 3などの他のモデルでも同様のタスクは実行可能です。モデルの選定は、要件や好みに応じて検討してください。


7. Before / After:実際の要約サンプル

実際にアラーム定義(VMのCPU使用率アラート)を発火させて、要約のBefore/Afterを確認しました。

Before(元のJSON通知)

{
  "type": "OK_TO_FIRING",
  "alarmMetaData": [
    {
      "id": "ocid1.alarm.oc1.ap-tokyo-1.aaaaaaaxxxxx",
      "status": "FIRING",
      "severity": "CRITICAL",
      "query": "CpuUtilization[1m].mean() > 80",
      "totalSuppressedResults": 0,
      "dimensions": [
        {
          "resourceId": "ocid1.instance.oc1.ap-tokyo-1.aaaaaaayyyyy",
          "resourceDisplayName": "web-server-01"
        }
      ]
    }
  ],
  "title": "high-cpu-alarm",
  "body": "Alarm: high-cpu-alarm has triggered. The alarm is now in the FIRING state.",
  "timestamp": "2026-02-14T05:30:00.000Z",
  "severity": "CRITICAL"
}

After(GenAI要約後)

[概要]
「web-server-01」にて、「high-cpu-alarm」(現在の値: 95%)が発生しました。
緊急度はCRITICALです。

[詳細分析]
CPU使用率が閾値80%を超過し、現在95%に達しています。
メトリクス「CpuUtilization」の1分間平均値が継続的に高い状態にあります。
考えられる原因として、バッチ処理の集中実行、アプリケーションの異常動作、
または外部からの急激なアクセス増加が挙げられます。

[推奨されるアクション]
1. 該当インスタンス「web-server-01」にSSH接続し、topコマンドまたはhtopコマンドで
   CPU使用率の高いプロセスを特定してください。
2. OCIコンソールのメトリクス画面で、過去数時間のCPU使用率の推移を確認し、
   いつから上昇が始まったか特定してください。
3. 必要に応じてインスタンスのスケールアップ、またはロードバランサー配下での
   スケールアウトを検討してください。

[ログ参照]
パス: 20260214/053000_Alarm_a1b2c3d4.json

注意:上記の「詳細分析」および「推奨されるアクション」はAIが生成したものです。 実際の原因や対応策は状況によって異なりますので、必ず元のログやメトリクスを確認のうえ、ご自身で判断してください。

Before/Afterの比較:

項目 Before (JSON) After (要約)
可読性 × 構造の理解が必要 ◎ 一目で把握可能
対象リソース OCIDの解読が必要 リソース名が明示
深刻度 キーを探す必要あり 冒頭で強調
対応策 記載なし 具体的な3点を提示(※参考情報)
証跡 なし ログパスを記載

8. テストしたアラートの種類

今回の検証では、以下のアラートを発火させてテストしました。

サービス テスト内容 結果
アラーム定義 VMのCPU使用率アラート ✓ 正常に要約
イベント 任意のイベントを発火 ✓ 正常に要約
Connector Hub ロググループへのログ蓄積をフィルタリングで検知 ✓ 正常に要約

いずれのサービスからのJSON通知も、期待通り要約することができました。

Connector Hubのテスト構成:

今回の検証では、OCI Loggingのロググループにたまったログを対象に、Connector Hubのフィルタリング機能で特定の文言(ERRORException など)が検知された場合に発火する構成をテストしました。


9. 検証結果:レイテンシとコスト

9.1 レイテンシ

Functionsの呼び出しからGenAI要約完了までのレイテンシを計測しました。

状況 レイテンシ 備考
初回起動(コールドスタート) 約30~60秒 コンテナ起動時間を含む
連続呼び出し(ウォームスタート) 10秒前後 GenAI応答時間が主
【レイテンシの内訳(ウォームスタート時)】

Functions起動・初期化: ~1秒
JSONパース・保存: ~1秒
GenAI呼び出し: 5〜7秒
トピック送信: ~1秒
合計: 8〜10秒

補足:コールドスタートについて
OCI Functionsはコールドスタート時にコンテナの起動時間がかかります。頻繁にアラートが発生する環境では、ウォームスタートが維持されやすく、レスポンスは安定します。

9.2 コスト

長期的な検証はまだ行っていませんが、おおよその目安は以下の通りです。
コスト自体は従量課金制となっています。

項目 参考
1アラートあたりの要約コスト GenAIのトークン消費量に依存
Functionsの実行コスト メモリ256MB × 実行時間
Object Storageの保存コスト ログサイズに依存

コスト試算例(月間100アラートの場合):

項目 単価 数量 月額
GenAI(オンデマンド) ~$0.02/10,000文字 100回 $2
Functions $0.00001452/GB-秒 およそ1,000秒 $0.01
Object Storage $0.0255/GB およそ0.01GB $0.01
合計 - - $2〜3

実際のコストは、アラートの発生頻度やJSONのサイズによって変動します。詳細な試算には OCI Cost Estimator をご活用ください。


10. ハマりポイント・Tips

構成を組む際に注意すべきポイントをまとめます。

10.1 DNS設定を忘れずに

GenAIプライベートエンドポイントを作成すると、DNS接頭辞の入力が必須になります。この接頭辞を含むドメインを東京リージョンから名前解決できるようにするため、以下の設定が必要です。

【必要なDNS設定】

1. 大阪VCNのリゾルバにプライベートビューを作成
2. プライベートビューにGenAIエンドポイントのゾーンを登録
3. 大阪VCNにDNSリスナーエンドポイントを作成
4. 東京VCNにDNS転送エンドポイントを作成
5. 東京のDNS転送ルールで、GenAIのドメインを大阪リスナーに転送

プライベートエンドポイント作成時にコンソールでも案内が出ますが、忘れがちなポイントです。

10.2 セキュリティリストはサブネット内通信も許可が必要

大阪VCNのセキュリティリストでは、東京からの通信だけでなく、大阪サブネット自身のCIDRからの通信も許可する必要があります。

【見落としやすい設定】

送信元: 10.1.0.0/24(大阪サブネット自身)
宛先ポート: 443, 53
プロトコル: TCP, UDP

これがないと、サブネット内のリソース間(例:DNSリスナーとGenAIプライベートエンドポイント間)の通信が通りません。

10.3 Functionsのタイムアウト設定

デフォルトのタイムアウトは30秒です。コールドスタート時にはこれを超える可能性があるため、必要に応じてタイムアウトを延長してください(最大300秒まで設定可能)。

# func.yaml
timeout: 120  # 2分に設定

10.4 ONS経由の「封筒」展開を忘れない

アラーム定義からトピック経由で呼び出す場合、JSONが以下の形式で包まれて届きます。

{
  "Type": "Notification",
  "Message": "{\"type\":\"OK_TO_FIRING\",...}"
}

Message の中身を再度パースする処理を入れないと、期待通りに動作しません。

10.5 エラー時のフォールバック

GenAI呼び出しが失敗した場合でも、通知が止まってしまうのは避けたいところです。今回の実装では、エラー時にフォールバックメッセージを送信するようにしています。

try:
    result_text = call_genai_generic(signer, parsed_info, archive_filename)
except Exception as e:
    logging.getLogger().error(f"GenAI Error: {e}")
    # AIエラー時のフォールバック通知
    result_text = f"""[AI処理エラー]
GenAIによる要約処理でエラーが発生しました。
元のログを確認してください。

ログ: {archive_filename}
エラー内容: {str(e)}
"""

11. 今後の展望

今回の構成を基に、以下の改善を検討しています。

温度設定の見直し

「推測される原因」「推奨されるアクション」といった推論要素を含む出力については、温度を少し上げることで、より多様な提案が得られる可能性があります。ただし、事実に基づく項目の精度とのバランスを見ながら調整が必要です。

対応状況の記録

要約を送信するだけでなく、対応状況をレポート形式で定期的にまとめておくような機能の追加を取り入れることができると考えています。

JSONの原本をObject Storageに保管しているため、GenAIをさらに組み合わせることで週次や月次のレポートを作成させるなどまだまだ応用の術があると感じています。


12. まとめ

前後編にわたって、OCI Generative AIを活用したアラート通知の自動要約構成について解説しました。

本記事の成果

成果 詳細
クロスリージョン構成の確立 東京から大阪GenAIにプライベート接続
Functionsの実装パターン リソースプリンシパル認証、ONS封筒展開、通知タイプ判定
プロンプト設計のノウハウ 通知タイプに応じた指示切り替え、出力形式の固定
検証結果の数値化 レイテンシ10秒未満、コスト数円/アラート
ハマりポイントの共有 DNS設定、セキュリティリスト、タイムアウトなど

技術的なポイント

  • GenAIプライベートエンドポイントを使えば、オンデマンドモデルでもプライベート接続が可能
  • DRGのRPC接続とDNS転送で東京リージョンから大阪のGenAIを利用できる
  • Functionsのコード内で原本を保存しておくことで、ハルシネーション対策も万全
  • 通知タイプに応じたプロンプトの切り替えで、適切な要約を実現
  • リソースプリンシパル認証でセキュアにOCIサービスを呼び出し

JSON通知の可読性に悩んでいる方は、ぜひ試してみてください!


執筆者

山塚 友貴
所属:NTT西日本 ビジネス営業本部
業務:主にOCIのインフラ設計や構築、運用に携わっています。
保有資格:Oracle Cloud Infrastructure 2024 Architect Professional、AWS Certified Solutions Architect - Professional、Azure Solution Architect Expert(AZ-305)など。


参考文献


商標

  • Oracle、Java、MySQL及びNetSuiteは、Oracle Corporation、その子会社及び関連会社の米国及びその他の国における登録商標です。
  • Cohereは、Cohere Inc.の商標または登録商標です。
  • Dockerは、Docker, Inc.の米国およびその他の国における登録商標です。
  • AWSはAmazon.com, Inc.またはその関連会社の商標です。
  • Microsoft AzureはMicrosoft Corporationの商標です。
  • その他、本記事に記載されている会社名、製品名は、各社の登録商標または商標です。

© NTT WEST, Inc.