源内 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は以下の内容で修正します。
| Key | Valueの設定値 |
|---|---|
| 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アプリの作成画面
以下を入力します。
| 項目名 | 入力内容 |
|---|---|
| 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アプリの利用
メトリクスチェッカーにアクセスすると以下の画面が表示されたので、質問部分を以下の文に修正しました。
Lambda のメトリクスを確認し、気をつけるべき点があれば教えてください。

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

生成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/