はじめに:AWSの仕様は「静的」ではないという前提

AWSの制限値や仕様は、開発者が一度確認すれば永続的に正しいわけではない。Service Quotasの値がリージョンやアカウント単位で異なり、ドキュメントの更新が実態より遅れることは日常的に起きている。にもかかわらず、多くのプロジェクトでは「ドキュメントにそう書いてある」という根拠だけで数値をコードに焼き付けている。

ここで問題になるのは、同一の制限値に対して複数の数値が存在し得ることである。EC2のOn-Demand Standard vCPUクォータを例にとると、旧User Guide、Service Quotasコンソール、実際のLive APIから取得した値が異なるケースがあり得る(参照記事)。どの値を「正しい」として実装に使うかで、デプロイ後の障害の有無が分かれる可能性がある。

本記事で扱うのは、LLMエージェントを用いて「現在有効な値」を動的に取得・検証する設計パターンである。ドキュメント検索やコンソール画面のスクリーンショットに依存するのではなく、読み取り専用のAWS APIから直接値を取得し、その結果を構造化された知識ベースと統合することで、実装時のエラーを未然に防ぐアプローチである。

対象読者は、制限値をハードコーディングせず、コード内で管理したい中級〜上級のインフラ・バックエンドエンジニアである。

問題定義:なぜ「現在の値」が分からないのか

AWSの仕様情報が「古さ」で迷走する構造には、いくつかの典型的なパターンがある。以下に具体的な事例を挙げる。

同一事実でソースごとに数値が分かれる

EC2 On-Demand Standard vCPUクォータは前述の通り、User Guide、Service Quotasコンソール、Live APIの間で値が異なるケースがある。コンソールに表示される値は特定アカウントの現在の割り当て値であり、User Guideの値はかつてのデフォルト上限や一般的な上限を示している可能性がある。しかし、どのソースが「正」かを判断する仕組みが標準的に存在しない。

キーワード検索の上位結果が古い値を返す

EBS gp3の最大IOPSについて、旧gp2ドキュメントの情報がキーワード検索で上位に表示され、現在の値が見逃されやすいケースがある。検索エンジンのランキングはページ権威やリンク数に依存するため、技術的に最新であることが上位表示を保証しない。

ブログ記事とLive APIの乖離

S3 Standardの価格について、コンソールとLive APIが一致している一方、古いブログ記事には異なる値が記載されているケースがある。RDS PostgreSQLのメジャーバージョンでも、リリースノートと実際のAPIやチュートリアルで示される情報が異なる場合があり、情報の鮮度管理が難しいケースが散見される。

ブラックボックス領域

Lambdaの同時実行制限は開発者ガイドで1000とされるが、Service Quotasに該当するエントリが存在しない場合、APIによるチェックが不可能である。つまり「APIで証明できない」領域が、AWS全体に存在し得る。

仕様項目ソースA(古い・非公式)ソースB(コンソール)ソースC(Live API)乖離の理由
EC2 vCPUクォータUser Guide: 32Service Quotas: 516アカウント別割り当てとデフォルトの混同
EBS gp3 最大IOPS旧gp2ドキュメント: 16,00080,000—旧世代ドキュメントの検索上位表示
S3 Standard 価格旧ブログ: 0.021ドル/GB-mo0.023ドル/GB-mo0.023ドル/GB-mo価格改定後のブログ未更新
RDS PostgreSQL 版リリースノート: 13—11リージョン別提供状況の差異
Lambda 同時実行開発者ガイド: 1000—エントリなしService Quotas未対応

これらの事例に共通するのは、「正しい値」が1つに収束していないことである。開発者が個別にソースを照合するコストは、リソース数が増えるほど線形以上に増加する可能性がある。

解決アプローチ:生API照合による「真実」の特定

上記の問題に対する解決策として、ドキュメントやコンソールUIに依存せず、読み取り専用のAWS APIから直接値を取得する手法を提案する。具体的には、Service Quotas API、Price List API、EC2 API、RDS APIなどを呼び出し、レスポンスから「現在有効な値」を抽出する(実装リポジトリ)。

生API照合の利点

APIレスポンスはアカウント・リージョン単位で即時反映されるため、コンソールのキャッシュ遅延やドキュメントの更新漏れを回避できる可能性がある。特にService Quotas APIは、アカウントの現在の割り当て値を直接返すため、「このアカウントで今、何まで許可されているか」を機械的に確認できる。

適用の限界

しかし、すべてのリソースに生APIが存在するわけではない。前述のLambda同時実行制限のように、Service Quotasにエントリが存在しない場合、APIによる検証自体が不可能である。この「ブラックボックス領域」は、API照合だけでは解決できないため、フォールバック戦略の設計が不可欠である。

「現在有効な事実」として扱う前提条件

APIから取得した値を「事実」として扱うためには、以下の前提を満たす必要がある。第一に、APIが読み取り専用であること(書き込みによる副作用がない)。第二に、レスポンスにタイムスタンプやバージョン情報が含まれ、取得時刻が追跡可能であること。第三に、APIの契約(SLA)が安定しており、レスポンス形式の破壊的変更が稀であること。

これらの条件を満たすAPIの値は、ドキュメントやブログ記事よりも信頼性が高い場合がある。ただし「APIが返した値」は「アカウント・リージョン単位での現在値」であり、全リージョン共通のデフォルト上限とは異なる場合がある。この区別を設計時に明確にしておくことが、誤解を防ぐ鍵となる。

flowchart TD
    A["クエリ受信"] --> B{"生APIが存在するか?"}
    B -->|Yes| C["読み取り専用APIを呼び出し"]
    C --> D["レスポンスから値を抽出"]
    D --> E["取得時刻・リージョンを記録"]
    E --> F["現在値として確定"]
    B -->|No| G["ドキュメント・コンソールにフォールバック"]
    G --> H{"ソースが特定できるか?"}
    H -->|Yes| I["値を記録し「未検証」フラグを付与"]
    H -->|No| J["値を確定できないと報告"]

このフローにより、APIで検証可能な値は「証明された事実」として、検証不可能な値は「未検証」として明確に区別される。この区別こそが、後続の統合・ガード処理の基盤となる。

アーキテクチャ設計:LLMエージェントの構成要素

このエージェントは4つのコンポーネントで構成される。Amazon Nova Pro on Bedrockが推論モデル、Strands Agents (Python)がエージェントフレームワーク、Sanity Context Knowledge Baseが構造化知識のストレージ、そして読み取り専用AWS APIが事実の一次ソースを担う。

なぜLLMが必要か

情報源の異質性が、LLMを必要とする根本的な理由である。生APIのレスポンスは構造化されたJSONだが、User Guideやブログ記事は非構造化な自然言語である。呼び出し側が「EC2のvCPUクォータは?」と自然言語で問いかける場合、どのAPIを呼び、レスポンスのどのフィールドを抽出すべきかを、呼び出し側が知る必要はない。LLMがこの意味的なブリッジを担い、構造化データと非構造化テキストを統一された文脈で統合する。

役割分担の設計判断

Strands Agentsはツール呼び出しのオーケストレーションを担い、Nova Proは推論と要約を、Sanity Context Knowledge Baseはtyped awsFactドキュメントとしての知識管理をそれぞれ担当する。ここで重要な設計判断は、LLMに「値の決定」をさせないことである。LLMの役割はあくまで統合と提示であり、値そのものは後述する決定論的ルールと生APIが保証する。

この分離により、モデルの推論品質が値の正確性に影響を与える経路が構造的に断たれる。モデルを差し替えた場合でも、ガード機能とKnowledge Baseの構造が値の整合性を維持する可能性がある。

核心:決定論的な統合と矛盾の排除

複数の情報源が競合する値を提示した場合(例:旧User Guideが32、Live APIが16)、このエージェントはLLMに「どちらが正しいか」を判断させない。代わりに、source.kindとeffectiveDateの2つのメタデータに基づいて、優先順位を決定論的に適用する。

優先順位ルールの構造

source.kindはソースの種類をエンコードする(live_api、console、user_guide、blog等)。effectiveDateは、その値が観測または公開された日時を記録する。統合ルールは単純で、source.kindの優先度(live_api > console > user_guide > blog)をまず適用し、同kind内ではeffectiveDateが新しい方を採用する。このルールはコード内で明示的に実装され、LLMの推論に委ねられない。

結果として、同一の入力に対して常に同一の出力が得られる可能性がある。これは再現性の観点で重要であり、デバッグや監査の文脈で「なぜこの値になったのか」を、LLMのブラックボックスな推論ではなく、明示的なルール参照で説明可能にする。

決定論的フィルタリングの限界

このアプローチのトレードオフとして、ドキュメントに記述された文脈的な例外ケースを見落とすリスクがある。例えば「このクォータは特定のアカウントタイプには適用されない」といった注記は、typed awsFactの構造化フィールドに十分に表現されない場合がある。そのため、Knowledge Baseのスキーマ設計時には、例外条件を表現するフィールド(例:applicableScope)を事前に定義しておくことが望ましい。

ガード機能:出力の整合性検証と修正

決定論的統合により「正解値」が確定したとしても、LLMがレスポンスを生成する際にハルシネーションや推測ミスによって誤った数値を出力する可能性は残る。ガード機能はこの残存リスクを排除する最終防衛線である。

検証と修正のメカニズム

動作は以下の通りである。LLMが候補値を含むレスポンスを生成すると、ガード機能がその候補値を抽出し、Sanity Context Knowledge Baseから取得した統合済み正解値と比較する。一致すればそのまま通過し、不一致であればLLMの出力を破棄し、正解値で強制置換する。この処理はモデルの推論を「上書き」するのではなく、モデルの出力を「事実の提示」という役割に収束させる制約として機能する。

これは「ヒューマンインザループ」ではなく「ルールインザループ」である。検証の基準が人間ではなく、事前定義された構造化データであるため、24時間無人で運用可能であり、検証結果も再現可能である可能性がある。

sequenceDiagram
    participant A as エージェント
    participant B as Nova Pro (LLM)
    participant C as Knowledge Base
    participant D as ガード機能

    A->>C: 統合済み正解値を取得
    C-->>A: 正解値(例: 16 vCPU)
    A->>B: プロンプト(正解値を含むコンテキスト)
    B-->>A: 候補値を含むレスポンス
    A->>D: 候補値と正解値を比較
    alt 一致
        D-->>A: 通過(出力をそのまま採用)
    else 不一致
        D-->>A: 候補値を破棄し正解値に置換
    end
    A->>A: 最終レスポンスを返却

この設計により、エージェントの出力は「LLMが推測した値」ではなく「生APIで証明された値をLLMが自然言語で提示したもの」として扱える可能性がある。推論の自由度と事実の正確性の両立を、アーキテクチャの構造で保証するのがこのガード機能の本質である。

実装パターン:Pythonエージェントの実装例

ここからは、実際にPythonでエージェントを動かすためのコードイメージを示す。使用するフレームワークはaws-source-of-truth-agentを参考に、Strands AgentsとAWS SDKを組み合わせる形になる。まずは、AWS SDK経由でService Quotas APIを呼び出し、生APIの値を取得する部分のコード例を以下に示す。

# 擬似コード:生APIからの値取得とawsFactドキュメントへの変換
import boto3
from datetime import datetime

def fetch_live_quota(quota_code):
    client = boto3.client('service-quotas')
    try:
        # QuotaCodeは必須パラメータ。QuotaActionCodeはオプションだが、
        # 特定のクォータタイプを指定する場合に使用されることがある。
        resp = client.get_quota(
            ServiceCode='ec2',
            QuotaCode=quota_code
        )
        return {
            "type": "awsFact",
            "metric": "ec2_vcpu_quota",
            "value": resp['Quota']['Value'],
            "source": {
                "kind": "live_api",
                "name": "service-quotas"
            },
            "effective_date": datetime.now().isoformat()
        }
    except Exception as e:
        print(f"API Error: {e}")
        return None

この関数が返す辞書が、前述のtyped awsFactドキュメントの本体である。source.kindを"live_api"とし、effective_dateを現在時刻にすることで、Knowledge Base側の決定論的フィルタリングで最優先されるメタデータが付与される可能性がある。次に、この値をLLMに渡すエージェント本体の呼び出し部分である。

# 擬似コード:エージェントへの入力とガード機能の適用
response = agent.invoke(
    input=f"現在の{metric}の値を教えてください。参考データ: {json.dumps(aws_fact)}"
)
final_answer = apply_guard(response, aws_fact)

apply_guard関数内で、LLMの出力(response)に含まれる数値とaws_factのvalueを比較し、不一致なら正解値に上書きする処理が走る。エラーハンドリングについても触れておく。Lambdaの同時実行制限のように、Service Quotasに該当エントリが存在しない場合、get_quotaはエラーを返す可能性がある。このときタイムアウトを短めに設定し、フォールバックとしてドキュメント検索の結果のみをLLMに渡す、あるいは「値を取得不可」と明示する処理が考えられる。ネットワーク障害やレートリミットによる一時的な失敗を想定し、リトライロジックを挟むのが実務上は安定する可能性がある。

適用範囲と限界:どこまで自動化できるか

このアプローチがどこまで機能するのか、具体的なAWSサービスを見ながら確認する。まず成功例として、Graviton4 (R8g)インスタンスの可用性がある。インスタンスタイプページとLive APIの両方で「Available」として一致しており、ソース間の乖離が起きにくい傾向にある。この種の情報は、生APIの照合だけで現在値を確定させるのが容易だ。

価格情報も同様に有効である。S3 Standardの価格は、コンソールとLive API(Price List API)で0.023ドル/GB-moとして一致している場合がある。一方で、古いブログ記事には0.021ドルという記載が残っていることがある。人間が検索結果を見て古い記事に引っかかるリスクを、API照合によって機械的に排除できる可能性がある。

ただし、すべてがAPIで解決できるわけではない。Lambdaの同時実行制限は、開発者ガイドで1000とされるものの、Service Quotasには該当するエントリが存在しない場合がある。生APIが存在せず、コンソールにも表示されない「ブラックボックス」領域である可能性がある。新規サービス立ち上げ直後で、API仕様がまだ未確定の場合も、同様にこの手法は適用できない可能性がある。以下の表に、代表的なサービス別の検証可能性をまとめる。

サービス生APIの有無コンソールUIの信頼性LLM統合の適性
EC2あり低(キャッシュ遅延)高い
S3あり中高い
RDSあり中高い
Lambdaなし低(非表示)低い

このように、APIが整備されているサービスには強く、そうでない場合はドキュメント解析やコンソールスクレイピングへのフォールバックを設計に組み込む必要がある場合がある。

トレードオフ分析:ハードコーディング vs 動的フェッチ

ここまでの議論を踏まえ、最終的に「値をコード内でハードコーディングする」のか、「実行時にAPIから動的にフェッチする」のかという設計判断のトレードオフを整理する。

観点ハードコーディング動的フェッチ
メンテナンス性低い(手動更新)高い(自動更新)
信頼性高い(オフライン動作)中(API依存)
パフォーマンス高い低い(レイテンシー増)
実装コスト低い中(エラー処理必要)

ハードコーディングの利点は、依存関係が最小限になり、テストが安定しやすい点にある。一方で、AWS側の仕様変更を追跡して手動で更新する手間が発生し、ドキュメントやコンソールと乖離したまま運用されてしまうリスクが常にある。

動的フェッチの利点は、常に最新値を取得でき、更新が自動化される点だ。ただし、実行時に外部APIを叩くためレイテンシーが増加する。また、AWS側の障害やネットワーク問題で値を取得できなくなった場合、アプリケーションが起動できないという脆弱性を持つ可能性がある。そのため、取得失敗時のフォールバック値(キャッシュやデフォルト値)を必ず用意する設計にするのが実装上の注意点である。

実際の運用では、両者を組み合わせたハイブリッドアプローチが現実的だ。例えば、一定期間(24時間など)のキャッシュを持つ動的フェッチ機能を実装し、キャッシュが有効な間はローカル値を使用し、期限が切れた時点でバックグラウンドでAPI照合を行う。小規模な単一プロジェクトであればハードコーディングでも許容される場合があるが、大規模で多環境構成のインフラを管理する場合は、この種の動的検証と監視の仕組みが必須になるだろう。

運用:監視と更新メカニズム

動的に取得した値を運用に組み込む場合、最も重要な課題は「値がいつ、何を変えたか」を把握することだ。AWS側の仕様変更が反映されると、エージェントが取得する値も変わる。この変化を検知し、関係者に通知する仕組みがなければ、値の乖離が静かに蓄積し、ある日突然インフラが想定外の制限にぶつかることになる可能性がある。

実装上の一般的なパターンとして、エージェントが取得した値をバージョン管理されたファイル(JSONやYAML)として出力し、変更が発生した際にGitのdiffを生成する方式がある。CI/CDパイプラインでこのdiffを検知し、Slackやメールで通知すれば、値の変化を人間が確認できる可能性がある。通知内容には、変更前の値・変更後の値・検出時刻・情報源(どのAPIから取得したか)を含めることで、対応の判断材料が揃う。

APIレスポンスのキャッシュ戦略については、取得頻度と鮮度のバランスが設計判断の分岐点になる。Service QuotasやEC2のインスタンスタイプ情報は数時間に一度の更新で十分だが、Price List APIのような価格情報は日次で確認するのが妥当だろう。キャッシュのTTLを短くすれば鮮度は上がるが、API呼び出しの頻度が増え、レート制限に抵触するリスクも高まる。TTLの設定は、その値が業務に与える影響の大きさに応じて決定するのが合理的だ。

テスト環境での検証は、本番AWSアカウントに依存しないことが前提だ。モックサーバやローカルのJSONファイルでAPIレスポンスを再現し、エージェントの統合ロジックやガード機能が正しく動作するかを確認する。特に重要なのは、競合するソースが同時に存在するケース(例えば、古いドキュメント値と新しいAPI値が両方Knowledge Baseに登録されている状態)を再現し、決定論的フィルタリングが正しく優先順位付けしているかを検証することである。

長期的なメンテナンスコストを考えると、エージェント自体のコードは比較的安定するが、AWS APIの仕様が変更された際にアダプタ層の更新が必要になることがある。例えば、Service Quotas APIのレスポンス構造が変われば、パース処理の修正が要る。この種の対応は頻度は低いが影響が大きい。エージェントのコードをモジュール化し、APIアダプタと統合ロジックを分離しておくことで、変更時の影響範囲を最小限に抑えられる可能性がある。

まとめ:インフラ制約を「動的な事実」として扱う

本記事では、AWSの制限値や仕様を静的なドキュメントとしてではなく、動的に変化する「事実」として扱う設計パターンを提示した。核心は、読み取り専用のAWS APIから直接値を取得し、LLMエージェントが複数の情報源を決定論的に統合して「現在有効な値」を証明するというアプローチにある。

EC2 vCPUクォータが32・5・16とソースによって異なること、EBS gp3のIOPS上限が旧ドキュメントと現行値で乖離していること、S3価格やRDSバージョンでも同様の問題が起きていること——これらの事例が示すのは、AWSの仕様は静的ではなく、ドキュメントと実際の値が一致しないことが日常茶飯事であるという事実だ。キーワード検索で上位に来る古い情報に頼る限り、この乖離は解消されない可能性がある。

このアプローチの価値は、インフラエンジニアが「推測」ではなく「証明された事実」に基づいて意思決定できるようになる点にある。ガード機能によってLLMのハルシネーションが排除され、決定論的フィルタリングによって情報源の優先順位が明確化されることで、出力された値は信頼できる根拠を持つ可能性がある。結果として、制限値をハードコーディングする際の「いつ更新したか分からない」という不安が解消され、コードベースの信頼性が向上する可能性がある。

今後の応用として、単一の制限値の検証にとどまらず、複数リソース間の依存関係(例えば、特定インスタンスのvCPU数がネットワークインターフェースの上限にどう影響するか)の解析や、コスト最適化のための制約条件の自動評価へと拡張できる可能性がある。インフラ制約を「動的な事実」として扱い続ける限り、システムは変化に追従し続けることができる。

関連記事

参考

本記事は海外の技術トレンド「Which AWS limit is actually current? An agent that proves it, 32 vs 5 vs 16」(DEV)で話題のテーマをもとに、両儀システムソリューションズが独自に解説したものです。