← 기사 목록
日本語https://zenn.dev/topics/ai/feed

源内 Webに接続する生成AIアプリ(メトリクスチェッカー)を作ってみた

추출된 키워드

28
生成AIアプリ·5メトリクスチェッカー·5源内 Web·5Bedrock·4AssumeRole·4CloudWatchメトリクス·4API Gateway·4IAMロール·4AWS·4生成AI·4源内AIアプリ·4デジタル庁·4AWS/ApiGateway·3APIキー·3AWS/Lambda·3Lambda·3ExternalId·3AWS WAF·3CloudWatchクロスアカウントオブザーバビリティ·3CDK·3GitHub·3OSS·3NAT Gateway·2EIP·2Google Cloud·2AWS/Cognito·2Azure·2JSON·2

원문

11,222
源内 Webに接続する生成AIアプリ(メトリクスチェッカー)を作ってみた

源内 Webに接続する生成AIアプリ(メトリクスチェッカー)を作ってみた

概要

デジタル庁が生成AI利活用基盤として源内Webと源内AIアプリをOSSとして公開しています。https://github.com/digital-go-jp/genai-web https://github.com/digital-go-jp/genai-ai-api

源内WebにはWebアプリケーションと組込の生成AIチャット等が含まれています。
この源内Webは別途作られた生成AIアプリと接続できるように、API仕様が公開されています。
源内AIアプリはこのAPI仕様に基づいた生成AIアプリの実装例で、AWS、Azure、Google Cloudの生成AIアプリが公開されています。

今回の検証ではパブリックなAWS(ガバクラではない)のアカウントAに源内Webをデプロイし、別のAWS(アカウントB)に生成AIアプリをデプロイして、アカウントAからアカウントBの生成AIアプリを利用する検証を実施します。
作成する生成AIアプリはアクセス元アカウントであるアカウントAのメトリクスをBedrockで読み取って、稼働状況をチェックするものです。

なお、源内Webはv1.0.7で動作を確認しています。

作成したコード類

作成したコード類はGitHubに格納しています。

https://github.com/jay34986/genai-metrics-check/tree/9c211b444cbf5d1a6978becfe808522cfcd5ef00

源内Webと生成AIアプリのデプロイ・設定・実行確認

源内Webのデプロイ

源内Webのデプロイは、源内WebのReadmeを参考に実施します。
本記事では詳細は割愛しますが、

packages/cdk/env-parameters/self-hosting-dev.ts
は以下のように修正しました。

12行目は、この後設定する

packages/cdk/parameter.ts
の記載と合わせる必要があるので、公開されている手順に合わせて変更しています。
diff packages/cdk/env-parameters/{self-hosting-template.ts,self-hosting-dev.ts}
12c12
< export const selfHostingTemplateParams: Partial<StackInput> = {
---
> export const selfHostingDevParams: Partial<StackInput> = {
23c23
<   appEnv: 'your-environment-name',
---
>   appEnv: 'dev',

packages/cdk/parameter.ts
packages/cdk/env-parameters/self-hosting-dev.ts
で設定した
selfHostingDevParams
をimportします。
diff --git a/packages/cdk/parameter.ts b/packages/cdk/parameter.ts
index 515a773..dc38d88 100644
--- a/packages/cdk/parameter.ts
+++ b/packages/cdk/parameter.ts
@@ -1,5 +1,6 @@
 import * as cdk from 'aws-cdk-lib';
 import { preprocessContextValues, StackInput, stackInputSchema } from './lib/stack-input';
+import { selfHostingDevParams } from "./env-parameters/self-hosting-dev";
 
 // CDK Context からパラメータを取得する場合
 const getContext = (app: cdk.App): StackInput => {
@@ -12,6 +13,7 @@ const getContext = (app: cdk.App): StackInput => {
 
 // デプロイ先環境ごとのパラメータを定義する
 const deploy_envs: Record<string, Partial<StackInput>> = {
+  "-selfHostingDev": selfHostingDevParams,
   // 開発環境のサンプルパラメータ
   // '-dev': {
   //   appEnv: 'dev',

一時認証情報の取得

アカウントAとアカウントBの一時認証情報を取得します。
account-aプロファイルには源内WebをデプロイしたAWSアカウントで、account-bは生成AIアプリをデプロイするAWSアカウントです。

aws login --profile account-a
aws login --profile account-b

アカウントAのIAMロール設定

以下のコマンドで環境変数

ACCOUNT_A_ID
ACCOUNT_B_ID
にAWSアカウントのIDを設定します。
export ACCOUNT_A_ID=$(aws --profile account-a sts get-caller-identity --query Account --output text)
export ACCOUNT_B_ID=$(aws --profile account-b sts get-caller-identity --query Account --output text)

echo "ACCOUNT_A_ID=${ACCOUNT_A_ID}"
echo "ACCOUNT_B_ID=${ACCOUNT_B_ID}"

以下のコマンドでアカウントAにIAMロールを作ります。

EXTERNAL_ID=tenant-a-external-id PROFILE_A=account-a npm run deploy:account-a

IAMロールが正常に作成されると、以下のように出力されます。

MetricsSourceAccountStack.RequiredExternalId = tenant-a-external-id
MetricsSourceAccountStack.SourceRoleArn = arn:aws:iam::${アカウントAのID}:role/MetricsSourceAccountStack-MetricsReadRole2EDEAB43-Ubie04dK4bfD

アカウントBに生成AIアプリをデプロイ

config/tenants.sample.json
をコピーして
config/tenants.json
を作成し、生成AIアプリを使用可能とするAWSアカウントAの情報を記載します。
cp config/tenants.sample.json config/tenants.json

tenants.json
は以下のようなファイルです。
[
  {
    "tenantId": "tenant-a",
    "sourceRoleArn": "arn:aws:iam::111111111111:role/replace-with-source-role",
    "externalId": "tenant-a-external-id",
    "sourceRegion": "ap-northeast-1",
    "allowedMetricNamespaces": ["AWS/Lambda", "AWS/ApiGateway"]
  }
]

tenants.json
は以下の内容で修正します。
KeyValueの設定値
tenantIdテナントを識別するために一意となる任意のID
sourceRoleArnアカウントAにIAMロールをデプロイした際に出力された
MetricsSourceAccountStack.SourceRoleArn
の値
externalIdアカウントAにIAMロールをデプロイした際に出力された
MetricsSourceAccountStack.RequiredExternalId
の値
sourceRegionメトリクスをチェックする対象のリージョン
allowedMetricNamespacesアカウントAでアクセスを許可するメトリクスのネームスペース

生成AIアプリをデプロイする前に、デプロイ先のアカウントIDを確認します。

echo ${ACCOUNT_B_ID}
${アカウントBのID}

以下のようにコマンドを実行して生成AIアプリをアカウントBにデプロイします。

PROFILE_B=account-b TENANTS_FILE=config/tenants.json ALLOWED_SOURCE_CIDRS=198.51.100.10/32,198.51.100.11/32 npm run deploy:account-b

デプロイが完了したら、アカウントBにデプロイされたAPI GatewayのAPIキーIDを使用して、APIキーの値を取得します。
ここで取得したAPIキーを使って、源内Webに生成AIアプリを登録します。

AWS_PROFILE=account-b aws apigateway get-api-key --api-key ${APIキーのID} --include-value --query value --output text --region ap-northeast-1
Gxu**********************************

源内Webへの生成AIアプリ登録

源内Webで公開されているドキュメントにしたがってAIアプリの作成画面に進むと、以下のような画面が表示されます。

AIアプリの作成画面
AIアプリの作成画面

以下を入力します。

項目名入力内容
APIエンドポイントのURLアカウントB構築時の
ReportEndpointUrl
出力の値
APIキー上記で取得したAPIキーの値

APIリクエストのデータ形式(JSON)
には以下のような内容を入力します。
{
  "question": {
    "title": "質問",
    "desc": "何を確認したいかを入力してください",
    "type": "textarea",
    "required": true,
    "default_value": "Lambda のエラー傾向を教えてください"
  },
  "metric_namespace": {
    "title": "Namespace",
    "desc": "例: AWS/Lambda",
    "type": "text",
    "required": true,
    "default_value": "AWS/Lambda"
  },
  "metric_name": {
    "title": "Metric name",
    "desc": "例: Errors",
    "type": "text",
    "required": true,
    "default_value": "Errors"
  },
  "metric_dimensions": {
    "title": "Dimensions",
    "desc": "key=value をカンマ区切りで入力してください。例: FunctionName=my-function",
    "type": "text",
    "required": false,
    "default_value": ""
  },
  "stat": {
    "title": "Stat",
    "type": "select",
    "required": true,
    "items": [
      { "title": "Average", "value": "Average" },
      { "title": "Sum", "value": "Sum" },
      { "title": "Maximum", "value": "Maximum" },
      { "title": "Minimum", "value": "Minimum" }
    ],
    "default_value": "Sum"
  },
  "period_seconds": {
    "title": "Period (seconds)",
    "type": "number",
    "required": true,
    "min": 60,
    "max": 3600,
    "default_value": 300
  },
  "lookback_minutes": {
    "title": "Lookback (minutes)",
    "type": "number",
    "required": true,
    "min": 5,
    "max": 1440,
    "default_value": 60
  },
  "conversation_history": {
    "title": "会話履歴",
    "desc": "前回までの文脈を渡す場合に使用します",
    "type": "textarea"
  }
}

その他の必須項目を入力して、

作成
ボタンをクリックします。
作成に成功すると、AIアプリに作成したメトリクスチェッカーが表示されるようになります。

AIアプリ画面
AIアプリ画面

生成AIアプリの利用

メトリクスチェッカーにアクセスすると以下の画面が表示されたので、質問部分を以下の文に修正しました。

Lambda のメトリクスを確認し、気をつけるべき点があれば教えてください。

メトリクスチェッカー画面
メトリクスチェッカー画面

実行ボタンを押すと、以下のように回答してくれました。

### Lambda メトリクスの確認

**サマリー:**
Lambda のメトリクスを確認しました。エラー数はゼロであり、Lambda 関数が正常に実行されていることを示しています。

**観察:**
1. **エラーの発生が確認されていません:** Lambda 関数のエラー数がゼロであることから、関数が正常に実行されていることがわかります。継続的な監視が必要ですが、現時点では特に注意すべき点はありません。
2. **データの不足:** 提供されたデータポイントがわずか8個であり、より長期的な傾向を評価するには不十分です。将来的には、より多くのデータポイントを収集して、より詳細な分析を行うことをお勧めします。

現時点では、特に注意すべき点はありませんが、継続的な監視とデータの収集を推奨します。

生成AIアプリ実行結果
生成AIアプリ実行結果

構築時に

allowedMetricNamespaces
で指定していない
AWS/Cognito
ネームスペースについて実行しようとするとエラーとなりました。

実行不可なネームスペースでの確認
実行不可なネームスペースでの確認

気をつけた点

アカウントBからアカウントAのCloudWatchメトリクスの参照

クロスアカウントでCloudWatchメトリクスを確認するのに使える主な方式・選択肢として、以下の3つがあります。

  • AssumeRole
  • CloudWatchクロスアカウントオブザーバビリティ
  • クロスアカウントクロスリージョンCloudWatchコンソール

この生成AIアプリを複数の源内Webと接続したときのことを考えると、CloudWatchクロスアカウントオブザーバビリティのように監視アカウントから複数のソースアカウントのメトリクスを横断参照できる構成では、アプリ側でテナント境界をより厳密に扱う必要があります。
今回は、リクエストごとに参照先アカウントを明確に切り替えられるように、AssumeRoleでCloudWatchの読み取り用IAMロールを引き受ける方針としました。
AssumeRoleするための情報は、源内Webから送られたAPIキーをAPI Gatewayが検証したうえで解決する

apiKeyId
をキーとして、対応するIAMロールと
ExternalId
を取得しています。

API Gatewayのアクセス制限

API GatewayにAWS WAFをつければ、API Gatewayへのアクセスを制限できますが、検証環境で費用を抑えたかったのでWAFを使わない方針とし、以下を組み合わせました。

  • API GatewayのエンドポイントはAPIキーを必須化し、APIキーがわからない・設定されていない通信でAPI Gatewayに統合されたLambdaが実行されないように設定
  • APIキーが漏れた時に大量アクセスを防ぐため、使用量プランで10アクセス/秒に制限
  • リソースポリシーでアクセス元IPアドレスをアカウントAのNAT GatewayのEIPに制限

参考

以下の記事を参考にさせていただきました。

https://dev.classmethod.jp/articles/enabled-cloudwatch-cross-account-observability/