Home AWS解説: LangGraph×Amazon EKSによるステートフルITサービスデスクエージェントの構築
投稿
キャンセル

✍️ AWS解説: LangGraph×Amazon EKSによるステートフルITサービスデスクエージェントの構築

ブログ概要

本記事は Building a Stateful IT Service Desk Agent with LangGraph on Amazon EKS の解説記事です。

AWSオープンソースブログにおいて、Sunil Ramachandra氏とShashidhar Kolur氏は、LangGraph(MITライセンス)とAmazon EKSを組み合わせてステートフルなITサービスデスクエージェントを構築する方法を解説しています。このシステムは、社内IT問い合わせに対してRAG(Retrieval-Augmented Generation)で自動回答を試み、信頼度が閾値未満の場合はinterrupt()によってグラフ実行を一時停止し、DynamoDBにフルコンテキストを永続化したうえで人間のL2/L3エンジニアにエスカレーションするという、L1自動化とHuman-in-the-Loop(HITL)を統合したアーキテクチャです。

情報源

項目内容
種別企業テックブログ(AWS Open Source Blog)
URLaws.amazon.com/blogs/opensource/…
組織Amazon Web Services
著者Sunil Ramachandra, Shashidhar Kolur
発表日2026年6月26日

関連記事: LangGraphチェックポイント機構で社内ヘルプデスクの中断復帰を実装するでは、LangGraphのチェックポイント機構をPostgreSQLベースで実装する方法を解説しています。本記事のAWSブログはDynamoDBをチェックポイントバックエンドとして採用しており、マネージドサービスの運用性とスケーラビリティの観点で異なるアプローチを取っています。

技術的背景

社内ITサービスデスクでは、VPN接続障害、SSO認証エラー、オンボーディング手続きなど、定型的な問い合わせが大量に発生します。L1サポート(一次対応)の多くはrunbook(手順書)に基づく定型回答で解決可能ですが、未知の問題や複雑なケースではL2/L3エンジニアへのエスカレーションが必要になります。

従来のチャットボットはステートレスであり、エスカレーション時にコンテキストが失われる問題がありました。LangGraphは有向グラフベースのワークフローエンジンであり、各ノード遷移時に状態をチェックポイントとして永続化できます。これにより、Pod再起動、水平スケーリング、マルチレプリカルーティングを跨いでも会話状態を保持できます。2026年時点でLangGraphはKlarna、Replit、Elastic、Cloudflare、Lyft、Uber、Coinbase、NVIDIAなど多くの組織で本番利用されており、エージェントフレームワークとしてのデファクトスタンダードの一つとなっています。

著者らは、このLangGraphのinterrupt()機構とAWSマネージドサービス群を組み合わせることで、L1自動化とHITLエスカレーションをシームレスに統合する実用的なアーキテクチャを提示しています。

実装アーキテクチャ

システム全体像

著者らが提示するアーキテクチャは、以下の3層構造で構成されています。

graph TD
    User[社員 / IT問い合わせ] --> ALB[ALB Ingress]
    ALB --> FastAPI[FastAPI on EKS Pod]
    FastAPI --> Graph[LangGraph StateGraph]
    Graph --> Retrieve[Retrieve Node]
    Graph --> Generate[Generate Node]
    Graph --> Escalate[Escalate Node]
    Retrieve --> Titan[Amazon Titan Embeddings v2]
    Retrieve --> OpenSearch[OpenSearch Serverless]
    OpenSearch --> S3[S3 - Runbook Storage]
    Generate --> Bedrock[Amazon Bedrock - Claude Sonnet]
    Escalate --> Interrupt[interrupt - 一時停止]
    Graph --> DynamoDB[DynamoDB Checkpoint]
    FastAPI --> OTEL[OpenTelemetry Collector]
    DynamoDB --> Resume[L2/L3エンジニア Resume]
    Resume --> FastAPI

3ノードグラフの設計

LangGraphのStateGraphは3つのノードで構成されています。

  1. Retrieve: ユーザーの質問をAmazon Titan Text Embeddings v2でベクトル化し、OpenSearch ServerlessのkNN検索でrunbookからTop 5ドキュメントを取得
  2. Generate: 取得したコンテキストとともにClaude Sonnet(Amazon Bedrock)に送信し、回答・信頼度スコア・カテゴリを構造化出力として抽出
  3. Escalate: 信頼度が閾値未満の場合、interrupt()でグラフ実行を一時停止し、DynamoDBにフルコンテキストを永続化

条件分岐のロジックは以下の通りです。

1
2
3
4
5
6
7
8
9
def route_after_generate(state: SupportState) -> str:
    """Generate後の条件分岐: エスカレーション判定。

    信頼度が閾値未満の場合はエスカレーションノードへ、
    それ以外は処理完了としてENDへルーティングする。
    """
    if state.needs_escalation:
        return "escalate_to_engineer"
    return END

信頼度評価の二重閾値

著者らのシステムでは、エスカレーション判定に2つの独立した閾値を用いています。

  • 検索類似度閾値: $\theta_{\text{retrieval}} = 0.7$(Titan Embeddingsのコサイン類似度)
  • LLM自己評価閾値: $\theta_{\text{confidence}} = 7/10$(Claude Sonnetが出力する信頼度スコア)

いずれか一方でも閾値を下回った場合にエスカレーションが発動します。

\[\text{needs\_escalation} = (\max_{d \in D} \text{score}(d) < \theta_{\text{retrieval}}) \lor (\text{confidence} < \theta_{\text{confidence}})\]

ここで $D$ は取得されたドキュメント集合、$\text{score}(d)$ は各ドキュメントのkNN検索スコアです。

著者らは「ナレッジベースがまだ薄い段階では閾値を上げて(例: 0.8/8)積極的にエスカレーションし、カバレッジが充実するにつれて緩和する」というチューニング指針も述べています。

状態管理: SupportState

全ノードを流れる状態はSupportStateデータクラスで定義されています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from dataclasses import dataclass, field


@dataclass
class Document:
    """検索で取得したドキュメントを表すデータクラス。"""

    content: str
    source: str
    score: float = 0.0


@dataclass
class SupportState:
    """LangGraphの全ノード間で共有されるサポート状態。

    Attributes:
        question: ユーザーからのIT問い合わせ内容
        documents: 検索で取得したrunbookドキュメントのリスト
        generation: LLMが生成した回答テキスト
        confidence_score: LLMの自己評価による信頼度(1-10)
        needs_escalation: エスカレーション要否フラグ
        engineer_response: L2/L3エンジニアからの回答(エスカレーション時)
        category: 問い合わせカテゴリ(vpn, sso, onboarding等)
    """

    question: str = ""
    documents: list[Document] = field(default_factory=list)
    generation: str = ""
    confidence_score: float = 0.0
    needs_escalation: bool = False
    engineer_response: str | None = None
    category: str = ""  # vpn, sso, onboarding, hardware, access, other

Production Deployment Guide

AWS実装パターン: Small / Medium / Large

著者らのブログで提示されたアーキテクチャを基に、利用規模別の構成パターンを整理します。以下のコスト試算は2026年7月時点のAWS ap-northeast-1(東京リージョン)料金に基づく概算値であり、最新料金はAWS料金計算ツールで確認を推奨します。

Small構成(月間1,000件以下の問い合わせ)

コンポーネント構成月額概算 (USD)
EKS Control Plane1クラスタ~$72
EC2 (m7i.large x2)On-Demand~$180
DynamoDBOn-Demand (PAY_PER_REQUEST)~$5
OpenSearch Serverless2 OCU (最小構成)~$350
Bedrock (Claude Sonnet)~1,000リクエスト/月~$15
Bedrock (Titan Embeddings)~1,000リクエスト/月~$1未満
ALB1台~$25
ECRイメージストレージ~$1
合計 ~$650/月

Large構成(月間100,000件以上の問い合わせ)

コンポーネント構成月額概算 (USD)
EKS Control Plane1クラスタ (XL Provisioned)~$1,200
EC2 (m7i.large x5-10)Spot + Karpenter自動スケーリング~$400-800
DynamoDBOn-Demand + DAX キャッシュ~$250
OpenSearch Serverless8-16 OCU (自動スケーリング)~$1,400-2,800
Bedrock (Claude Sonnet)~100,000リクエスト/月~$1,500
Bedrock (Titan Embeddings)~100,000リクエスト/月~$20
ALB + WAF + ShieldDDoS対策込み~$100
CloudWatch + X-Ray + Dashboards完全監視~$80
合計 ~$5,000-6,800/月

注意: 上記はあくまで概算です。Bedrockの料金はリクエストあたりの入出力トークン数に大きく依存します。Claude Sonnet 4.6の料金は入力$3/100万トークン、出力$15/100万トークンです(リージョナルエンドポイント使用時は約10%増)。Titan Text Embeddings v2は入力$0.2/100万トークンです。

Terraformインフラコード: Small構成

以下は著者らのブログのアーキテクチャをTerraformで再現するSmall構成の主要リソースです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# main.tf - Small構成: DynamoDB + EKS (ap-northeast-1)

# DynamoDB: チェックポイントテーブル(TTL + PITR有効)
resource "aws_dynamodb_table" "checkpoints" {
  name         = "it-support-checkpoints"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "PK"
  range_key    = "SK"
  attribute { name = "PK"; type = "S" }
  attribute { name = "SK"; type = "S" }
  ttl { attribute_name = "expiry"; enabled = true }
  point_in_time_recovery { enabled = true }
}

# EKS Cluster: terraform-aws-modules/eks/aws ~> 20.0
module "eks" {
  source          = "terraform-aws-modules/eks/aws"
  version         = "~> 20.0"
  cluster_name    = "it-support-agent"
  cluster_version = "1.31"
  vpc_id          = module.vpc.vpc_id
  subnet_ids      = module.vpc.private_subnets
  eks_managed_node_groups = {
    default = {
      instance_types = ["m7i.large"]
      min_size = 2; max_size = 5; desired_size = 2
      capacity_type = "ON_DEMAND"
    }
  }
}

Large構成の追加要素

Large構成ではSmall構成に加えて以下を追加します。

  • Karpenter NodePool: karpenter.sh/capacity-type: [spot, on-demand]でSpot優先のノードプロビジョニング。対象インスタンスはm7i.large, m7i.xlarge, m6i.large, m6i.xlargeconsolidationPolicy: WhenEmptyOrUnderutilizedで未使用ノードを60秒後に自動回収
  • DAXキャッシュ: dax.t3.small(レプリケーションファクター2)でDynamoDBチェックポイントの読み取りを高速化
  • CPU/メモリ上限: Karpenterのlimitsでcpu: 100, memory: 400Giに制限し、コストの暴走を防止

運用・監視設定

OpenTelemetry統合

著者らのブログではOpenTelemetryによる分散トレーシングが組み込まれています。各ノードの実行を以下のスパンで計装しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor


def init_telemetry(app: "FastAPI") -> None:
    """OpenTelemetryの初期化とFastAPIの自動計装を行う。

    Args:
        app: FastAPIアプリケーションインスタンス
    """
    provider = TracerProvider(
        resource=Resource.create({"service.name": "it-support-agent"})
    )
    provider.add_span_processor(
        BatchSpanProcessor(
            OTLPSpanExporter(
                endpoint="http://otel-collector:4317",
                insecure=True,
            )
        )
    )
    trace.set_tracer_provider(provider)
    FastAPIInstrumentor.instrument_app(app)

計装されるスパンは以下の通りです。

スパン名記録される属性
support.retrievequestion, documents_retrieved, top_score, runbooks_searched
support.generateconfidence_score, best_retrieval_score, needs_escalation, category
support.escalateconfidence_score, category, engineer_resolved, resolution_time_ms

CloudWatch + X-Ray推奨アラート

本番運用では以下の3つのCloudWatch Alarmを設定することを推奨します。

アラート名メトリクス閾値評価期間
エスカレーション率異常ITSupportAgent/EscalationRate Average> 0.51時間 x 3回
P95レイテンシ超過AWS/ApplicationELB/Duration p95> 10,000ms5分 x 3回
DynamoDBスロットリングAWS/DynamoDB/ThrottledRequests Sum> 105分 x 1回

CloudWatch Agentはcwagentconfig.jsonでKubernetesメトリクス収集(60秒間隔)とX-Ray/OTLPトレース受信を設定します。

コスト最適化チェックリスト(20項目)

#カテゴリ施策
1ComputeKarpenter + Spotで非クリティカルワーカーのコスト削減
2ComputeHPAのtargetCPUUtilization 70%で過剰プロビジョニング回避
3ComputeKarpenter consolidation policy WhenEmptyOrUnderutilizedで未使用ノード回収
4ComputeEKS標準サポート期間内のバージョン使用(延長は6倍料金)
5Compute安定ワークロードにCompute Savings Plans適用
6BedrockClaude Sonnet max_tokens 1024制限(ブログ設定値)
7BedrockTitan Embeddings v2使用(v1比5倍低コスト、精度97%維持)
8Bedrock月間10万リクエスト超はProvisioned Throughput検討
9Bedrockプロンプトキャッシュでシステムプロンプトのトークンコスト削減
10Bedrockcross-region inference回避(リージョナルは約10%プレミアム)
11DataDynamoDB TTLで古いチェックポイント自動削除
12Datalanggraph-checkpoint-awsのS3オフロード(350KB以上自動退避)
13DataOpenSearch Serverless最小2 OCU(約$350/月)の常時課金に注意
14Data開発/テスト環境は非冗長構成(約$174/月)を使用
15DataDAXキャッシュで頻繁な状態読み取りのReadコスト削減
16NetworkPrivateLinkでNAT Gatewayデータ転送コスト回避
17NetworkECRライフサイクルポリシーで古いイメージ自動削除
18OpsCloudWatch Logs保持90日、長期はS3+Glacier
19OpsOTelサンプリングレート調整(全トレースではなく一定割合)
20OpsCost Explorerタグ分析 + AWS Budgets 80%アラート

パフォーマンス最適化

著者らのブログでは明示的なレイテンシやスループットの数値は提示されていませんが、アーキテクチャから読み取れるパフォーマンス最適化ポイントは以下の通りです。

検索性能: OpenSearch ServerlessのkNN検索はTop 5に制限しており、検索結果の網羅性とレイテンシのトレードオフを取っています。OCU数はワークロードに応じて自動スケーリングされるため、検索クエリ量の増減にも対応できます。

LLM推論: Claude Sonnetのmax_tokensを1024に制限することで、応答生成の時間とコストを抑制しています。構造化出力パーシング(正規表現によるCONFIDENCE:CATEGORY:の抽出)により、後処理のオーバーヘッドを最小限にしています。

チェックポイント永続化: langgraph-checkpoint-awsライブラリ(v1.1.1、2026年6月17日リリース)は、350KB未満のチェックポイントはDynamoDBに直接保存し、350KB以上はS3にオフロードする仕組みを備えています。DynamoDBのPAY_PER_REQUEST(オンデマンド)モードにより、スパイク時にも自動スケーリングされます。

水平スケーリング: HPAのtargetCPUUtilization 70%、minReplicas 2、maxReplicas 10の設定により、負荷に応じたPod数の自動調整が行われます。DynamoDBチェックポイントがPod間で共有されるため、どのPodにリクエストがルーティングされても中断/再開が正しく動作します。

運用での学び

著者らのブログから読み取れる運用上の重要な知見を整理します。

IRSA(IAM Roles for Service Accounts)の採用: 静的なAWSクレデンシャルをPodに注入する代わりに、EKSのOIDCプロバイダとIAMロールを連携させています。これにより、クレデンシャルのローテーション管理が不要になり、最小権限の原則をKubernetes ServiceAccount単位で適用できます。著者らのIAMポリシーでは、Bedrock(InvokeModel)、DynamoDB(GetItem/PutItem/Query/UpdateItem/DeleteItem)、OpenSearch Serverless(APIAccessAll)の3サービスのみに権限を絞り込んでいます。

信頼度閾値の段階的チューニング: 著者らは閾値について「ナレッジベースの充実度に応じて調整する」という実践的な指針を示しています。初期段階では高めの閾値(0.8/8)でエスカレーション率を上げ、回答品質を保護します。runbookの拡充に伴い、閾値を下げて(0.7/7)自動解決率を向上させるという段階的なアプローチです。

interrupt()による状態の完全保存: エスカレーション時にはLangGraphのinterrupt()関数が質問文、AI試行回答、信頼度スコア、カテゴリ、参照ソース一覧をペイロードとしてDynamoDBに永続化します。L2/L3エンジニアはこのコンテキストを参照しながら対応でき、ユーザーに再度状況説明を求める必要がありません。

エスカレーション後の再開: FastAPIの/support/{ticket_id}/resolveエンドポイントでCommand(resume=engineer_response)を発行すると、DynamoDBからチェックポイントが復元され、Escalateノードのinterrupt()の戻り値としてエンジニアの回答が注入されます。

学術研究との関連

本アーキテクチャは、Human-in-the-Loop AIエージェントに関する近年の研究動向と密接に関連しています。2024年のHuman-in-the-Loop Software Development Agentsに関するサーベイ(arXiv:2411.12924)では、LLMベースのエージェントフレームワークにおいて人間が計画段階やコーディング段階でレビュー・承認を行うことで、開発時間と工数が削減されることが報告されています。また、2025年のTowards Effective Human-in-the-Loop Assistive AI Agents(arXiv:2507.18374)では、AIエージェントにおける適切なエスカレーション判断の重要性が議論されています。

著者らのシステムにおける二重閾値(検索類似度 + LLM自己評価)によるエスカレーション判定は、単純なルールベースと完全自律の中間に位置する実用的なアプローチであり、信頼度キャリブレーションの研究とも関連しています。

まとめと実践への示唆

著者らのブログは、LangGraphのinterrupt()+DynamoDBチェックポイントによるステートフルHITLエージェントの実用的な構築パターンを示しています。特に、二重閾値によるエスカレーション判定、IRSAによるセキュアなクレデンシャル管理、OpenTelemetryによる可観測性の3点は、同種のシステムを構築する際の参考になります。一方で、ブログでは具体的なレイテンシやスループットの実測値、エスカレーション率の実績データは提示されていないため、本番導入時には自社環境での性能検証が必要です。OpenSearch Serverlessの最低月額約$350という固定コストも、Small構成では全体コストの過半を占める点に注意が必要です。

参考文献


本記事はAIによって生成されました。記事中のコード例・技術的見解は原著者の記述に基づいています。コスト試算は2026年7月時点の概算値であり、最新情報は公式ドキュメントをご確認ください。

この投稿は CC BY 4.0 でライセンスされています。

ブログ解説: Azure API Management統合AIゲートウェイ — LLMトークン監視とチャージバック基盤の構築

論文解説: ALAS — ステートフルマルチLLMエージェントフレームワークによる障害認識型プランニング