kintone AI ラボ のバックエンドを OpenTelemetry と AWS CloudWatch Application Signals で可観測性を向上させた話 – Cybozu Inside Out

こんにちは! kintone 開発の 齋藤 ( K.Saito (@SightSeekerTw) / X ) です。
最近では、以前、立山より公開した記事 kintone AI ラボリリース!大規模 SaaS への AI 機能導入で意識した設計と運用の工夫 でも紹介しました、kintone の 生成 AI 機能、「kintone AI ラボ」 のバックエンドの開発を担当しています。

本記事では、生成 AI 特有の不確実性によって発生する問題やテナント毎のトークン消費量など、 OpenTelemetry と AWS CloudWatch Application Signals を活用して可観測性を強化した事例を紹介します。

blog.cybozu.io

kintone における生成 AI 機能の概要

AI 機能のアーキテクチャ

kintone から Bedrock の LLM の呼び出し、その生成結果を取得する仕組みは以下のようになっております。

kintone AI Architecture
kintone AI Architecture

  1. LLM の呼び出し
    1. クライアントから kintone に対して LLM の呼び出しを必要とするリクエストを行うとLambda 関数 AIServiceFunction に LLM の呼び出しをリクエストします。
    2. AIServiceFunction は別の Lambda 関数 AIServiceAsyncEventFunction に LLM 呼び出しを行う非同期処理リクエストをします。
    3. AIServiceAsyncEventFunction が Bedrock (LLM) に対して Invoke を行い生成結果を受け取ります
    4. AIServiceAsyncEventFunction は LLM の生成したテキストを ElastiCache (Valkey) に順次書き込みます
  2. LLM 生成結果の取得
    1. 生成結果の取得は生成リクエスト時にレスポンスされた ID を使いリクエストをします。
    2. 生成結果は Valkey に順次書き込まれるため、ポーリングをして最新の生成結果を取得しクライアント(ブラウザ)に表示させます。

補足

ここで改めて LLM の呼び出しから生成結果の取得の説明を簡単にしていますが、ポーリング方式を採用した理由や詳細については kintone AI ラボリリース!大規模 SaaS への AI 機能導入で意識した設計と運用の工夫 を参照いただければと思います。

大規模 SaaS における生成 AI 運用の課題

Amazon Bedrock を活用した 生成 AI 機能を本格運用する中で、従来の Web アプリケーションとは異なる特有の課題が浮き彫りになりました。

生成 AI 特有の不確実性

従来の Web アプリケーションでは、同じ入力に対して常に同じ出力が期待できます。しかし、生成 AI では同じプロンプトでも毎回異なる結果が生成される可能性があり、「昨日は正常に動作したのに今日はエラーになる」といった問い合わせが発生する可能性があります。

エラーの根本原因特定の困難さ

生成 AI の処理フローは複雑で、プロンプト生成、 情報検索処理、 LLM 呼び出し、後処理など複数のステップを経ます。どの段階で問題が発生したのか、従来のログだけでは特定が困難でした。既存の kintone 環境で使用していた監視手法では、この複雑な処理チェーンの可視化が十分ではありませんでした。

特に LLM 呼び出しにおいては、 Bedrock Guardrails による入力または生成結果の保護機能が働いたり、スロットリングが発生してエラーになったりすることもあります。

テナント毎のトークン消費量の可視化

Bedrock に限らず、一般的に LLM の利用においては、トークンの使用量や呼び出し回数に制限があるため、テナント毎の利用状況を正確に把握する必要がありました。一部のユーザー、テナントで過剰に利用してしまい、他のユーザー、テナントに影響を与えてしまわないよう、適切な監視とケアをできるようにする必要がありました。また、 Amazon Bedrock の料金体系はトークン使用量に基づいているため、コスト管理の観点からもテナント毎の消費量を可視化することが重要でした。

非同期処理による複雑性

生成 AI 処理は時間がかかるため非同期で実装していますが、kintone の本体とされるアプリケーションサーバや複数の Lambda 関数を経由し、ElastiCache や Bedrock などのコンポーネントへのアクセスもあるため、処理の全体像を把握することが困難でした。

複雑なログ分析による運用負荷の増大

顧客からの問い合わせが発生した際の調査では、複数の Lambda 関数間でのログを横断的に分析する必要があります。特に、生成のリクエストと結果取得が別々のリクエストとして処理されるため、一連の処理フローを追跡することが困難でした。従来の単一のリクエスト-レスポンス内で完結する処理とは異なり、複数の Lambda 関数にまたがる非同期処理の関連付けが必要で、調査の難易度が大幅に上昇しました。各 Lambda 関数のログを個別に確認し、時系列やリクエスト ID を手動で照合する作業が必要となり、運用負荷が増大する懸念がありました。

これらの課題を解決するため、私たちは既存の AWS 運用経験を活かしつつ、生成 AI 特有の要件に対応した分散トレーシングによる可観測性の向上に取り組むことにしました。次章では、AWS CloudWatch Application Signals を活用した具体的な解決策について詳しく解説します。

AWS CloudWatch Application Signals の活用

前章で述べた生成 AI 運用における課題を解決するため、AWS CloudWatch Application Signals を導入しました。Application Signals は、分散システムにおけるアプリケーションの健全性とパフォーマンスを監視するためのフルマネージドサービスです。

従来運用では、主に CloudWatch Logs と CloudWatch Metrics を活用した監視を行っていましたが、 生成 AI 機能の複雑な非同期処理フローに対しては、より高度な可観測性が必要でした。 Application Signals は、OpenTelemetry ベースの分散トレーシング機能を提供し、複数の AWS サービスにまたがる処理の全体像を自動的に可視化できます。

特に AI 機能 のアーキテクチャにおいては、以下の点で Application Signals が威力を発揮しました。

  • Lambda 関数間の処理フローの可視化: AIServiceFunction から AIServiceAsyncEventFunction への非同期処理の流れの追跡
  • 外部サービス呼び出しの監視: Amazon Bedrock や ElastiCache へのアクセス状況の把握
  • エラー伝搬の追跡: 処理チェーンのどの段階でエラーが発生し、どのように伝搬したかを詳細に分析
  • テナント毎の利用状況の把握: トレースデータに埋め込んだテナントの識別情報により、特定のテナントに関連する処理のみを抽出して分析
  • エンドポイント毎のパフォーマンス監視: 各 API エンドポイントの可用性やレイテンシーを継続的に監視
  • ボトルネック処理の特定: トレースマップ上で処理時間の長いコンポーネントを視覚的に特定し、最適化対象を明確化

本章では、 Application Signals を効果的に活用するための以下の内容について段階的に解説します

  1. 分散トレーシングの基本概念:トレース、スパン、属性などの基礎知識
  2. OpenTelemetry による計装: 計装から CloudWatch へのトレース送信まで
  3. 非同期処理の課題解決: スパンリンクによるトレース関連付けの実装テクニック
  4. 可観測性の実現: トレースマップやトランザクション検索の活用方法
  5. カスタム属性の活用: テナント情報やトークン数などの効果的な監視手法

分散トレーシングの基本概念

分散トレーシングとは

分散トレーシングは、マイクロサービスアーキテクチャや分散システムにおいて、一つのリクエストが複数のサービスやコンポーネントを横断する際の処理の流れを追跡・可視化する技術です。従来の単一アプリケーション内でのログ記録とは異なり、システム全体を通じた処理の全体像を把握することができます。

分散トレーシングの構成要素

分散トレーシングは以下の主要な概念で構成されており、これらを組み合わせたコンテキスト情報を伝播させることで、分散システム全体での処理の追跡を実現しています。

トレース (Trace)

一つのリクエストに対する処理の全体を表す単位です。例えば、 kintone の生成 AI 機能において「ユーザーがAI検索を実行してから結果を取得するまで」の一連の処理全体が一つのトレースとなります。

スパン (Span)

トレース内の個別の処理単位を表します。各スパンは開始時刻、終了時刻、処理名、属性情報などを持ちます。

スパンの属性情報には、処理の詳細を表す様々な情報を含めることができます。kintone の生成 AI 機能では、以下のような属性を活用しています:

  • テナント/ユーザー識別情報: どのテナントやユーザーからのリクエストかを特定するための情報
  • AI 機能識別情報: 「検索AI」や「アプリ作成AI」など、どの AI 機能が使用されたかを示す情報
  • LLM 関連情報: Amazon Bedrock への呼び出しを行うスパンでは、入力トークン数や出力トークン数、使用したモデル名などの詳細情報

これらの属性情報により、単なる処理の流れだけでなく、ビジネス的な観点からの分析や、コスト管理、顧客サポートに必要な情報を効率的に取得できるようになります。

補足

kintone 本体のアプリケーションサーバー側でも分散トレーシングの仕組みを導入しており、本体側で生成されたトレース ID が AI Service 側にまで伝搬されるようにしています。 kintone 側のログにもそのトレース ID が紐づけて出力されているため、ユーザーが kintone の画面上で AI 機能を利用した際の一連の処理を、本体側の処理から AI 機能側の処理まで途切れることなく追跡できるようになっています。

OpenTelemetry の計装と CloudWatch へのトレース送信をするための実装

生成 AI 機能における分散トレーシングを実現するため、 OpenTelemetry を活用した計装を実装しました。本節では、実際の導入までのステップ、特に非同期処理が多用される 生成 AI 機能特有の課題に対する解決策について解説します。

生成 AI 機能における分散トレーシングを実現するため、以下の手順で OpenTelemetry の環境を構築しました。

補足

kintone の 生成 AI 機能では、AIServiceFunction および AIServiceAsyncEventFunction の両方を FastAPI フレームワークを用いたアプリケーションとして実装しており、Lambda Web Adapter を用いることで Lambda 関数として動作させています。詳細な設定については本記事では割愛しますが、 加えて Lambda Function URL を有効化することで HTTP リクエスト/レスポンスの処理を Lambda 環境で実現しています。

OpenTelemetry によるアプリケーションの計装

FastAPI アプリケーションのライブラリに対して行う OpenTelemetry の計装の基礎は非常に軽微なもので、 以下のライブラリをインストールして、数行のコードを埋め込むことで実現できます。

インストールしたライブラリ

OpenTelemetry の基本的な機能を提供するコアライブラリ

  • opentelemetry-sdk: トレース、メトリクス、ログの収集・処理を行う SDK
  • opentelemetry-exporter-otlp-proto-grpc: OpenTelemetry Collector へトレースデータを送信するためのエクスポーター

各フレームワーク・ライブラリ用の計装ライブラリ

  • FastAPI の計装ライブラリ: opentelemetry-instrumentation-fastapi
    FastAPI アプリケーションの HTTP リクエスト/レスポンスを自動的にトレースし、エンドポイント毎のスパンを生成。
  • botocore の計装ライブラリ: opentelemetry-instrumentation-botocore
    AWS SDK (boto3/botocore) の呼び出しに対して自動的にスパンコンテキストを埋め込み、のほか Lambda 関数 や Bedrock など AWS のリソースの呼び出しにあたり自動的にトレース情報の伝搬。
    特に Bedrock の呼び出しなどにおいては、スパンに入出力トークン数を始めとしたメトリクスやモデルの識別情報などをスパンの属性として付与することが標準化されたフォーマットで設定されるメリットもあります。詳しくは OpenTelemetry の Semantic conventions for AWS Bedrock operations をご参照ください。
  • logging の計装ライブラリ: opentelemetry-instrumentation-logging
    Python の標準ログとトレースを関連付け、ログメッセージにトレース情報を自動追加
基礎的な FastAPI アプリケーションへの計装のコード例
trace.set_tracer_provider(TracerProvider())
tracer_provider = trace.get_tracer_provider()


otlp_exporter = OTLPSpanExporter(endpoint="localhost:4317", insecure=True)
span_processor = SimpleSpanProcessor(otlp_exporter)
tracer_provider.add_span_processor(span_processor)


app = FastAPI()
BotocoreInstrumentor().instrument()
FastAPIInstrumentor.instrument_app(app)
LoggingInstrumentor().instrument()

💡 ヒント

OpenTelemetry にはコードの記述を必要とせず自動計装する手法がありますが、kintoneの 生成 AI 機能では以下の理由により手動計装を選択しました。

  • Lambda 環境での起動時間への影響
    自動計装は起動時にアプリケーション全体をスキャンして計装ポイントを特定するため Lambda 関数の起動時間が大幅に増加します。手動計装では必要最小限の計装をのみを行うため、この影響を最小化できます。
  • カスタム属性の柔軟な制御
    生成 AI 機能では、テナント/ユーザー識別情報や AI 機能識別情報などの属性をスパンに付与させることができます。手動計装により、処理の各段階で必要な属性を精密に制御できます。

CloudWatch での OTLP エンドポイントの有効化

基本的には以下のようにアプリケーションから OpenTelemetry Collector を用いて OpenTelemetry の標準プロトコルの OTLP レシーバーを提供するオブザーバビリティバックエンドに対してスパンを送信することで、分散システムの可観測性を実現できます。

CloudWatch では OTLP エンドポイントを有効化するために、 Transaction Search を有効化する必要があります。有効化手順は AWS 公式ドキュメント Enable Transaction Search を参照してください。

OpenTelemetry Collector の組み込み

Lambda 関数としてデプロイするコンテナイメージには、 OpenTelemetry Collector を同じコンテナイメージ内に組み込み、その OpenTelemetry Collector を経由して CloudWatch の OTLP エンドポイントにトレース情報を送信するように構成します。

ローカル開発環境では OpenTelmetry Collector はアプリケーションとは別のコンテナとして起動し、Jaeger をバックエンドとして利用していました。

Distrubited Tracing Architecture
Distrubited Tracing Architecture

OpenTelemetry Collector にはいくつかのディストリビューションがいくつかあり、当初は AWS Distro for OpenTelemetry Collector (ADOT Collector) を採用しておりましたが、OpenTelemetry 導入に伴うパフォーマンスの劣化がありました。

現在は、 OpenTelemetry のコミュニティで提供されている OpenTelemetry Collector AWS Lambda Extension layer を採用しています。

ADOT Collector でのパフォーマンス劣化の原因は、 Lambda 関数で利用する場合の特有の問題で、バックエンドへのスパンの送信をアプリケーションの処理と同期して行わなければならなかったことが起因でした。AIServiceFunction の各種エンドポイントにおいては 200ms 以上のレイテンシーのオーバヘッドがありました。

OpenTelemetry Collector AWS Lambda Extension Layer には decouple プロセッサというものが内包されており、完全に非同期でスパンの送信を行ってくれるため、レイテンシーにかかるオーバヘッドが 150ms 程度改善しました。

ただし、decouple プロセッサは完全に非同期でのスパンの送信になるため、Lambda 関数が動作していない間にはスパンが送信されなくなるというトレードオフがあることに注意してください。

詳しくは Observing Lambdas using the OpenTelemetry Collector Extension Layer を参照してください。

以下は、 OpenTelemetry Collector の設定です。

extensions:
  sigv4auth:
    region: ${env:AWS_REGION}
    service: xray


receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317


processors:
  batch:
    timeout: 1s
  decouple:
    max_queue_size: 10


exporters:
  otlphttp:
    traces_endpoint: https://xray.${env:AWS_REGION}.amazonaws.com/v1/traces
    compression: gzip
    auth:
      authenticator: sigv4auth


service:
  extensions: [sigv4auth]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, decouple]
      exporters: [otlphttp]

LLM 呼び出しのトレースと結果取得のトレースをリンクさせる実装テクニック

AI 機能のアーキテクチャにおいて、 LLM の呼び出しと生成結果の取得は異なるリクエストになっていることについて説明しました。加えて、分散トレーシングの構成要素におけるトレースにて解説したように、通常、一つのクライアントリクエストに対して一つのトレースが生成されます。つまり、生成結果の取得リクエストは、 LLM 呼び出しのトレースとは別のトレースとして扱われ、通常の方法では両者の関連性を持たせることができません。

この課題により、以下のような運用上の問題が発生します。

  • 生成リクエストでエラーが発生した場合、結果取得リクエストでの調査時に元のエラー原因を特定できない
  • 顧客からの問い合わせ時に、一連の処理フローを追跡することが困難

本節では、この課題を解決するため、Valkey を介したスパンコンテキストの伝搬スパンリンクの活用により、異なるリクエスト間でのトレース関連付けを実現する実装テクニックについて解説します。

スパンリンクとは

スパンリンクは、OpenTelemetry において異なるトレース間のスパンを関連付けるための仕組みです。通常の親子関係は同一トレース内でのスパン間の関係を表しますが、スパンリンクは異なるトレース間での論理的な関連性を示すために使用されます。

kintone の生成 AI 機能のように、一つの処理が複数の独立したリクエストに分かれる場合、スパンリンクを活用することで以下が実現できます。

  • トレース間の関連付け: 生成リクエストのトレースと結果取得リクエストのトレースを論理的に結び付け
  • 調査の効率化: CloudWatch上で関連するトレースを辿ることで、一連の処理フローを包括的に分析
  • 因果関係の可視化: 異なるリクエスト間での処理の依存関係や影響範囲を明確化

次に、このスパンリンクを実現するための具体的な実装方法について解説します。

Valkey を用いたスパンリンクの実現方法

1. LLM の生成結果を Valkey に逐次書き込んでいきますが、書き込むオブジェクトに LLM 呼び出しのリクエストのスパンコンテキストを持たせるようにします。

from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator


def init_carrier():
  carrier: dict = {}
  TraceContextTextMapPropagator().inject(carrier)


2. 生成結果取得のリクエストの処理において、Valkey から生成結果を取り出しを行うスパンに取り出されたオブジェクトのスパンコンテキストをリンクさせます。

from opentelemetry import trace
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
    

    

def add_span_link_from_carrier(carrier):
  propagator = TraceContextTextMapPropagator()
  ctx = propagator.extract(carrier)
  referrer_span_context = trace.get_current_span(ctx).get_span_context()
  trace.get_current_span().add_link(referrer_span_context)

CloudWatch でのスパンリンクされたトレースの可視化

CloudWatch はスパンリンクされているトレースがある場合、それらを関連付けて次のように可視化されます。

スパンリンクが設定されたスパンのあるトレースのビューでは、次のようにスパンリンクされたスパンを含むトレースも一つのビューで参照することができます。

Trace View
Trace View

Application Signals による可観測性

本節では、OpenTelemetry Collector からスパンが CloudWatch に送られたことによって、どのようなことが可観測になったのかということを解説します。

トレースマップによるワークロードの可視化

スパンが送信されることにより、以下のように、ワークロードの全体構成が可視化され、凡例にあるように、クライアントエラーやサーバーエラーがどの程度発生しているかを特定することができます。

Trace Map
Trace Map

発生事象を調査したいサービスを選択することで、いつ、どの程度、エラーがあったのか、どの程度のリクエストがあったのかを把握することもできます。

Error Service Drilled Down
Error Service Drilled Down

トランザクション検索による障害調査

トランザクション検索を使うことで、実際に障害となっているリクエストについてドリルダウンして原因を見つけることができます。トランザクション検索のコンソールから Span status が ERROR のものにフォーカスして調査したり、任意のスパンに紐づけた属性で対象となるリクエストを見つけることができます。

Error spans search by transaction search
Error spans search by transaction search

traceId を選択すると、エラーのあったリクエストの一連のスパンを時系列ですべて参照することができ、どのサービスでエラーが発生しているのかだったり、どの程度のレイテンシーがどの処理であったのかなどを追うことができます。

Error Trace
Error Trace

Span timeline より、障害となっているスパンを選択すると、その例外で出力されたメッセージであったり、スタックトレースを参照することができます。

Stack Trace on Trace View
Stack Trace on Trace View

カスタム属性の効果的な活用 (テナント情報や利用された AI 機能の識別, Bedrock の入出力トークン数など)

前述の通り、Span には属性を付与することができるため、ここではどういった属性情報をどのように付加したのか、その属性を使うことでどういったことの可観測性が上がったのかということを解説します。

スパンに付加する属性について

補足
kintone.* の属性名や属性値例は説明しやすいように実際に使用している名前から変更しています。

テナント/ユーザー識別情報

顧客を識別する情報を kintone Application Server からリクエストヘッダにて伝搬させ、それをスパンに埋め込むことで、スパンを確認する際にどのテナントにどのような影響があったのかを即座に特定したり、集計したりすることもできます。

属性名 属性値例 説明
kintone.tenant_id z9999999 kintone のテナント識別子
kintone.user_id 12345 リクエストを実行したユーザーの ID

これらの属性により、特定のテナントや特定のユーザーに関連する処理のみを抽出して分析することが可能になります。

AI 機能の識別とモデル情報

kintone の生成 AI 機能では、複数の AI 機能と複数の LLM を使い分けているため、どの機能でどのモデルが使用されたかを追跡することが重要です。

属性名 属性値例 説明
kintone.ai.llm.agent_type RAG_CHAT_ANSWER kintone 内での AI 機能の種類(属性値例は RAG の回答生成機能を指す)
gen_ai.request.model us.anthropic.claude-3-5-sonnet-20241022-v2:0 AI 機能で使用したモデルの識別子 (属性名は OpenTelemetry 標準)

これらの属性により、機能別・モデル別のパフォーマンス分析やエラー率の比較が可能になります。

Bedrock の入出力トークン数と処理パラメータ

前述の Botocore の計装ライブラリを用いることで、 Bedrock の呼び出しを行ったスパンに自動的に gen_ai から始まる属性名で入出力トークン数を始めとしたモデルの呼び出しにまつわる様々な属性が付与されます。

属性名 属性値例 説明
gen_ai.usage.input_tokens 3527 LLM への入力トークン数
gen_ai.usage.output_tokens 251 LLM からの出力トークン数

これらの属性により、以下のような分析が可能になります。

  • テナント別トークン消費量の監視: tenant_id と組み合わせることで、特定の顧客のトークン使用量を集計し、コスト配分や利用制限の管理に活用
  • 機能別コスト分析: agent_type と組み合わせることで、どの AI 機能が最もコストを消費しているかを特定

属性を用いた可視化の方法

前述のカスタム属性は、単にスパンに情報を付与するだけでなく、CloudWatch Application Signals の強力な分析機能と組み合わせることで、 生成 AI 機能の運用において重要な洞察を得ることができます。

トレースのビューではメタデータとして属性が表示される

どのテナントがどの AI 機能で、どの程度の利用をしているのか、トークン数を消費したのかなどを参照することができます。

Span Attributes on a trace view
Span Attributes on a trace view

トランザクション検索での属性フィルタリング

特定のテナントがどの程度使っているのかを可視化している例

Transaction Search Console
Transaction Search Console

カスタムダッシュボードの構築

カスタムダッシュボードを作成し、機能毎の LLM の呼び出し回数や、入力トークン数の他、機能ごとにテナントの利用傾向なども可視化できるようにして、利用傾向などを確認できるようにしています。

AI Service Utilizations with CloudWatch Custom Dashboard
AI Service Utilizations with CloudWatch Custom Dashboard

運用改善への活用事例

「大規模 SaaS における生成 AI 運用の課題」にて紹介した生成 AI 運用における5つの主要課題に対して、OpenTelemetry と CloudWatch Application Signals を活用することでどのような解決をもたらしたかを解説します。

生成 AI 特有の不確実性への対応

  • 課題: 同じプロンプトでも異なる結果が生成されるため、「昨日は正常に動作したのに今日はエラーになる」といった問い合わせの調査が困難
  • 解決: テナントの識別子(tenant_id) でフィルタリングし、成功時と失敗時のトレースを比較。トレースに紐づけられたスタックトレースの情報や属性情報により即座に原因を特定できるように。

エラーの根本原因特定の困難さの解消

  • 課題: LLM 呼び出しや後処理など複数ステップでの問題発生箇所の特定が困難
  • 解決: トレースマップとスパンタイムラインにより、処理フローとエラー箇所を視覚的に確認。各スパンの詳細でスタックトレースと例外メッセージを即座に取得。

テナント毎のトークン消費量の可視化

  • 課題: テナント毎の利用状況把握とコスト管理、過剰利用の防止
  • 解決: 入出力トークン数のスパン属性とテナントの識別子(tenant_id) の組み合わせにより集計した結果を CloudWatch のカスタムダッシュボードのウィジェットにすることで把握できるように。

非同期処理による複雑性の解決

  • 課題: 複数の Lambda 関数と AWS サービスを経由する処理の全体像把握が困難
  • 解決: トレースマップによる処理フローの視覚化と、スパンリンクによる生成リクエストと結果取得リクエストの関連付け。

複雑なログ分析による運用負荷の軽減

  • 課題: 複数 Lambda 関数のログ横断分析と手動照合による運用負荷の増大
  • 解決: 直感的なトランザクション検索とスパンタイムラインにより、複雑なログクエリが不要に。新メンバーでも迅速な調査が可能。

これらの事例が示すように、 OpenTelemetry と CloudWatch Application Signals の導入により、従来のログベースの分析では解決困難だった生成 AI 特有の運用課題を包括的に解決することができました。

特に重要な点は、複雑なログクエリの習得や属人的な調査スキルに依存することなく、直感的な UI と豊富な属性情報により、運用チーム全体の調査能力が底上げされたことです。これにより、 生成 AI 機能の障害対応が迅速化されただけでなく、予防的な監視とコスト管理も実現**し、大規模SaaSとしての安定運用を支える重要な基盤となっています。

導入の注意点

CloudWatch Application Signals の導入は多くの運用改善をもたらしますが、実際の導入時には以下の点に注意が必要です。

導入に伴う性能の変化

OpenTelemetry の計装とトレース送信により、アプリケーションのレスポンス時間に若干のオーバーヘッドが発生します。特に Lambda 環境では、ADOT Collector使用時にレイテンシー増加が確認されました。この問題は、OpenTelemetry Collector AWS Lambda Extension Layer の decouple プロセッサを活用することで改善できます。

計装ライブラリによる振る舞いの変化

自動計装ライブラリ(FastAPI, botocore等)の導入により、既存のアプリケーションの動作に予期しない変更が生じる可能性があります。特に、HTTP リクエストの処理順序やエラーハンドリングの挙動が変わる場合があるため、十分なテストを実施してから本番環境に適用することを推奨します。

実際に、botocore の計装ライブラリ導入時に、Bedrock のストリーミングレスポンスの処理において生成結果の取得に不具合が発生したケースがありました。このような予期しない動作変更を早期に発見するため、計装ライブラリのバージョンアップ時には特に注意深いテストが必要です。

スパンリンクを含むトレースの表示制限

トレースのビューでは、スパンリンクが付いている場合関連するトレースも一つのビューで参照できますが、最大5件までしか表示できず、6件以上の場合にはエラーになります。現在、改善については AWS にリクエストをしておりますが、スパンリンクを多用している場合には注意が必要です。これらの注意点を事前に把握し、適切な対策を講じることで、Application Signals の効果を最大限に活用できます。

まとめと今後の展望

本記事のポイント

本記事では、 kintone における生成 AI 機能の運用課題を、AWS CloudWatch Application Signalsによる分散トレーシングで解決した事例を紹介しました。

技術的な成果として、OpenTelemetryによる計装とカスタム属性の効果的な活用により、複雑な非同期処理フローの可視化と、テナント毎のリソース消費量の詳細な把握を実現しました。

運用面での成果として、属人性を下げ、直感的な障害調査環境を構築し、技術的な改善が運用効率化という価値に直結する結果となったと思います。

Application Signals を用いた指標活用の展望

現在の分散トレーシング活用により運用改善を実現できましたが、Application Signals には SLO 機能をはじめとした、さらなる指標活用の可能性があります。

Application Signals の SLO 機能による品質管理の可能性

Application Signals では、収集したトレースデータを基に SLO(Service Level Objective)を直接設定し、自動的に監視することが可能です。 生成 AI 機能においては、従来の Web アプリケーションの「応答時間」「可用性」に加えて、AI 機能特有の指標での SLO 設定が考えられます。「アプリ作成 AI 機能の可用性が一定以上を維持する」「トークン消費量が想定の範囲内に収まる」といった具体的な目標値を設定し、継続的な品質監視を行う仕組みの構築を検討したいと考えています。

データ駆動型運用への発展

蓄積されたトレースデータとメトリクスを組み合わせた分析により、トークン消費パターンの予測、コスト最適化、キャパシティプランニングなど、より戦略的な運用判断を支援する仕組みの構築を今後検討していきたいと思います。

今後の拡張計画

Valkey クライアントの OpenTelemetry 計装

現在、Valkey(ElastiCache)へのアクセスについては、スパンリンクの実現に必要なトレースコンテキストの保存・取得処理のみを手動で計装していますが、 Valkey クライアント自体は計装されておらず、トレースマップにも表示されていない状況です。

今後は Valkey クライアントライブラリをValkey GLIDEに置き換えることで、 OpenTelemetry の自動計装に対応する予定です。

Valkey GLIDE 2.0 という別のクライアントライブラリでは、OpenTelemetry サポートが組み込まれており、トレースマップでの可視化や、キャッシュアクセスパターンの可視化、エラー追跡性の向上が期待できます。

これにより、 LLM 呼び出しから結果取得までの処理チェーン全体をより詳細に監視し、運用品質のさらなる向上を目指します。

AI 機能のユーザーフィードバック機能との連携

将来的な展望として、ユーザーからのフィードバック(「役に立った」「役に立たなかった」など)を収集する機能と、現在のトレースデータを連携させる仕組みの構築ができると良いと考えています。

特に、プロンプト改善やモデル変更などの施策効果を定量的に評価するため、以下のような活用を想定しています。

  • 施策効果の定量評価: プロンプト改善前後でのユーザーフィードバック変化をトレース ID と紐付けて分析し、改善効果を客観的に測定
  • 品質劣化の早期検知: フィードバック評価の低下傾向を技術指標(レイテンシー、トークン消費量など)と組み合わせて分析し、品質問題の根本原因を特定
  • データ駆動型の機能改善: 実際のユーザー体験データに基づいた継続的な 生成 AI 機能の最適化

このような仕組みにより、技術的な監視指標だけでは把握できないユーザー満足度の観点から、 生成 AI 機能の品質向上を図れると良いと考えます。

最後に

生成 AI 技術の急速な進歩に伴い、運用における可観測性の重要性はますます高まっています。本記事で紹介した手法が、同様の課題に直面する開発・運用チームの参考となり、より安定した生成 AI サービスの提供に貢献できれば幸いです。

大規模SaaSにおける 生成 AI 機能の運用は、適切な可観測性により大きな事業価値を生み出す機会でもあります。今後も継続的な改善を通じて、より良いユーザー体験の提供を目指していきます。

Appendix

紹介しきれなかったいくつかの CloudWatch Application Signals のビューを紹介します。

スパンリンクされている場合のトレースのビュー

リンクされているスパンが複数ある場合は次のように表示されます。

Linked Traces
Linked Traces

Bedrock の呼び出しでスロットリングが発生し、結果取得のリクエストに影響した場合には以下のように一つのビューで原因となった Bedrock 呼び出しのリクエストのトレースを確認することができます。

Linked Trace (Throttled Trace)
Linked Trace (Throttled Trace)

トランザクション検索のその他の活用例

全体でどの機能がどの程度利用されているのかを可視化した例

Linked Trace (Throttled Trace)
Linked Trace (Throttled Trace)

テナント、機能毎のエラー発生件数とそのトレースの参照

Error Span Search
Error Span Search

採用しています!

生成 AI 技術を活用した機能開発に取り組むエンジニアを募集中です!

cybozu.co.jp

今回紹介したような可観測性の向上や、最新の AI 技術を使ったプロダクト開発に興味のある方、ぜひお気軽にエントリーください🚀




Source link

関連記事

コメント

この記事へのコメントはありません。