AI ゲートウェイとしての Kong:LLM ドライバーアーキテクチャ
前提知識
- ›第1回:アーキテクチャと Nginx インテグレーション
- ›第4回:プラグインシステムとイテレーター(プラグインハンドラーパターン)
- ›LLM API の基本的な理解(チャット補完、ストリーミング、トークン)
AI ゲートウェイとしての Kong:LLM ドライバーアーキテクチャ
Kong の最新の大型機能が、AI ゲートウェイとしての機能です。これは、大規模言語モデル(LLM)プロバイダーへのリクエストをプロキシ・変換・観測するためのサブシステムです。別途 AI プロキシを構築するのではなく、Kong は LLM サポートをプラグインアーキテクチャへと直接組み込みました。これにより、本シリーズで紹介してきたフェーズパイプライン・設定システム・オブザービリティ基盤をそのまま活用できます。
設計の中核となるのがドライバーパターンです。各 LLM プロバイダー(OpenAI、Anthropic、Azure、AWS Bedrock、Google Gemini、Cohere、Hugging Face)は、標準インターフェースを持つドライバーモジュールとして実装されています。共通の HTTP 変換処理は共有ユーティリティモジュールが担い、クラウド固有の認証はアダプターモジュールへと分離されています。
LLM モジュールのアーキテクチャとフォーマット検出
LLM サブシステムは kong/llm/ に配置されており、エントリーポイントは kong/llm/init.lua です。このモジュールの最初の役割はフォーマット検出です。受信リクエストがチャット補完かテキスト補完かを判別します。
local function identify_request(request)
local formats = {}
if type(request.messages) == "table" and #request.messages > 0 then
table.insert(formats, "llm/v1/chat")
end
if type(request.prompt) == "string" then
table.insert(formats, "llm/v1/completions")
end
-- ...
end
Kong の標準フォーマットは OpenAI 互換です。メッセージ配列を使うリクエストには llm/v1/chat、単一プロンプトのリクエストには llm/v1/completions を使用します。67〜82行目にある is_compatible 関数は、リクエストが想定されるルートタイプに合致するかを確認します。フォーマット検証をスキップしてリクエストをそのまま通す preserve モードも備えています。
利用可能なドライバーモジュールは、主要な LLM プロバイダーをカバーしています:
| Driver | File | Provider |
|---|---|---|
openai |
kong/llm/drivers/openai.lua |
OpenAI API |
anthropic |
kong/llm/drivers/anthropic.lua |
Anthropic Claude |
azure |
kong/llm/drivers/azure.lua |
Azure OpenAI |
bedrock |
kong/llm/drivers/bedrock.lua |
AWS Bedrock |
gemini |
kong/llm/drivers/gemini.lua |
Google Gemini |
cohere |
kong/llm/drivers/cohere.lua |
Cohere |
huggingface |
kong/llm/drivers/huggingface.lua |
Hugging Face |
mistral |
kong/llm/drivers/mistral.lua |
Mistral AI |
llama2 |
kong/llm/drivers/llama2.lua |
Llama 2 (self-hosted) |
flowchart TD
A[Incoming Request] --> B{Format Detection}
B --> C["llm/v1/chat<br>(messages array)"]
B --> D["llm/v1/completions<br>(prompt string)"]
C --> E{Route Type Match?}
D --> E
E -->|Compatible| F[Select Driver]
E -->|Incompatible| G[400 Error]
F --> H[Transform to Provider Format]
H --> I[Send to LLM Provider]
I --> J[Transform Response to Kong Format]
ドライバーパターン:プロバイダーの抽象化
各ドライバーモジュールは to_format と from_format のトランスフォーマー関数で構成された標準インターフェースを実装しています。kong/llm/drivers/openai.lua は最もシンプルなドライバーです。Kong の標準フォーマットそのものが OpenAI フォーマットだからです。
local transformers_to = {
["llm/v1/chat"] = function(request_table, model_info, route_type)
request_table.model = model_info.name or request_table.model
request_table.stream = request_table.stream or false
request_table.top_k = nil -- unsupported by OpenAI
return request_table, "application/json", nil
end,
}
kong/llm/drivers/anthropic.lua ドライバーは対照的に、フォーマット間の変換が必要です。旧来の Claude モデルでは、Kong のメッセージ配列を Anthropic の Human:/Assistant: プロンプト形式に変換します:
local function kong_messages_to_claude_prompt(messages)
local buf = buffer.new()
for _, v in ipairs(messages) do
if v.role == "assistant" then
buf:put("Assistant: ")
elseif v.role == "user" then
buf:put("Human: ")
end
buf:put(v.content)
buf:put("\n\n")
end
buf:put("Assistant:")
return buf:get()
end
kong/llm/drivers/shared.lua の共有ドライバーユーティリティは、すべてのドライバーが利用する共通機能を提供しています。HTTP クライアント管理、ストリーミングのコンテントタイプ検出、SSE パース、そしてオブザービリティ用のログエントリーキー定数などが含まれます。このモジュールは利用状況を追跡するための標準キーを定義しています:
local log_entry_keys = {
USAGE_CONTAINER = "usage",
PROMPT_TOKENS = "prompt_tokens",
COMPLETION_TOKENS = "completion_tokens",
TOTAL_TOKENS = "total_tokens",
TIME_PER_TOKEN = "time_per_token",
COST = "cost",
}
classDiagram
class SharedDriver {
+_CONST: SSE_TERMINATOR, etc.
+_SUPPORTED_STREAMING_CONTENT_TYPES
+log_entry_keys
+HTTP utilities
}
class OpenAIDriver {
+to_format(request, model_info)
+from_format(response, model_info)
+DRIVER_NAME: "openai"
}
class AnthropicDriver {
+to_format(request, model_info)
+from_format(response, model_info)
+kong_messages_to_claude_prompt()
+DRIVER_NAME: "anthropic"
}
class BedrockDriver {
+to_format(request, model_info)
+from_format(response, model_info)
+DRIVER_NAME: "bedrock"
}
SharedDriver <|-- OpenAIDriver
SharedDriver <|-- AnthropicDriver
SharedDriver <|-- BedrockDriver
クラウドアダプターと認証
クラウドホスト型 LLM サービスへの認証処理は、ドライバーロジックとは分離されています。クラウド固有のアダプターが認証情報の管理を担うため、フォーマット変換コードが複雑になりません。
アダプターモジュールは kong/llm/adapters/ に配置されています:
bedrock.lua—resty.awsを使った AWS SigV4 リクエスト署名gemini.lua—resty.gcpを通じた Google Cloud サービスアカウント認証
kong/llm/drivers/shared.lua の共有ドライバーモジュールは、ロード時にクラウド SDK を初期化します:
local GCP = require("resty.gcp.request.credentials.accesstoken")
local aws_config = require "resty.aws.config"
local AWS = require("resty.aws")
local AWS_REGION = os.getenv("AWS_REGION") or os.getenv("AWS_DEFAULT_REGION")
kong/llm/schemas/init.lua の認証スキーマは、ヘッダーベース認証(API キー)、クエリパラメーター認証、クラウドネイティブ認証に対応した柔軟な設定を提供します:
local auth_schema = {
type = "record",
fields = {
{ header_name = { type = "string", referenceable = true }},
{ header_value = { type = "string", encrypted = true, referenceable = true }},
{ param_name = { type = "string", referenceable = true }},
{ param_value = { type = "string", encrypted = true, referenceable = true }},
},
}
encrypted = true と referenceable = true というアノテーションに注目してください。encrypted フラグはデータベースへの保存時に暗号化するフィールドをマークします(Enterprise 機能)。referenceable フラグは、{vault://env/OPENAI_API_KEY} のような Kong Vault 参照を値として使えることを意味し、Kong のシークレット管理システムとの連携を可能にします。
ヒント: AWS Bedrock などのクラウドプロバイダーでは、API キーを明示的に設定する必要はありません。アダプターは標準の AWS SDK 認証チェーン(環境変数、IAM ロール、インスタンスプロファイル)を使用します。
AWS_REGIONを設定し、Kong インスタンスに適切な IAM 権限を付与するだけで動作します。
AI プラグインファミリー
kong/plugins/ai-proxy/handler.lua の ai-proxy プラグインはわずか 19 行と驚くほどコンパクトです。それは ai_plugin_base モジュールをベースにしたフィルター構成のアーキテクチャに処理を委ねているためです:
local ai_plugin_base = require("kong.llm.plugin.base")
local NAME = "ai-proxy"
local PRIORITY = 770
local AIPlugin = ai_plugin_base.define(NAME, PRIORITY)
local SHARED_FILTERS = {
"parse-request", "normalize-request", "enable-buffering",
"normalize-response-header", "parse-sse-chunk", "normalize-sse-chunk",
"parse-json-response", "normalize-json-response",
"serialize-analytics",
}
for _, filter in ipairs(SHARED_FILTERS) do
AIPlugin:enable(AIPlugin.register_filter(require("kong.llm.plugin.shared-filters." .. filter)))
end
return AIPlugin:as_kong_plugin()
kong/llm/plugin/base.lua モジュールは、Kong のフェーズにマッピングする独自の「ステージ」システムを持つメタプラグインフレームワークを提供します:
local STAGES = {
SETUP = 0,
REQ_INTROSPECTION = 1,
REQ_TRANSFORMATION = 2,
REQ_POST_PROCESSING = 3,
RES_INTROSPECTION = 4,
RES_TRANSFORMATION = 5,
STREAMING = 6,
RES_PRE_PROCESSING = 7,
RES_POST_PROCESSING = 8,
}
各共有フィルターは特定のステージに登録されます。parse-request フィルターは REQ_INTROSPECTION ステージで実行され、受信リクエストボディをデコードします。normalize-request フィルターは REQ_TRANSFORMATION ステージで実行され、Kong の標準フォーマットからプロバイダーのフォーマットへと変換します。serialize-analytics フィルターは RES_POST_PROCESSING ステージで実行され、利用状況メトリクスを出力します。
このコンポーザブルなフィルターアーキテクチャにより、異なる AI プラグイン(ai-proxy、ai-request-transformer、ai-response-transformer)が共通ロジックを共有しながら、それぞれ異なる高レベルの動作を実装できます。ai-proxy はすべての標準フィルターを有効にする一方、ai-request-transformer はリクエスト側のフィルターと LLM イントロスペクションフィルターのみを有効にする、といった使い分けが可能です。
sequenceDiagram
participant Client
participant AIProxy as ai-proxy (access)
participant ParseReq as parse-request filter
participant NormReq as normalize-request filter
participant LLM as LLM Provider
participant ParseRes as parse-json-response filter
participant NormRes as normalize-json-response filter
participant Analytics as serialize-analytics filter
Client->>AIProxy: POST /llm/v1/chat
AIProxy->>ParseReq: STAGE: REQ_INTROSPECTION
ParseReq->>ParseReq: Decode JSON body
ParseReq->>NormReq: STAGE: REQ_TRANSFORMATION
NormReq->>NormReq: Transform to provider format
NormReq->>LLM: Forward transformed request
LLM-->>ParseRes: Provider response
ParseRes->>ParseRes: Decode provider JSON
ParseRes->>NormRes: STAGE: RES_TRANSFORMATION
NormRes->>NormRes: Normalize to Kong format
NormRes->>Analytics: STAGE: RES_POST_PROCESSING
Analytics->>Analytics: Record token usage
Analytics-->>Client: Normalized response
kong/llm/plugin/observability.lua のオブザービリティモジュールは、Kong の既存のメトリクス基盤と連携します。トークン数・レイテンシ・コストはリクエストごとに追跡され、Kong の標準ログプラグインを通じて公開されます。追加設定なしで http-log、datadog、prometheus を使って AI ゲートウェイのトラフィックを監視できます。
kong/llm/plugin/ctx.lua のコンテキストモジュールは、リクエストごとの名前空間付き状態管理を提供します。各 AI プラグインは独自のコンテキスト名前空間を持つため、同一リクエスト上で複数の AI プラグイン(例:ai-prompt-guard の後に ai-proxy)が動作する際にも競合が発生しません。
AI リクエストの完全なフロー
Kong を通じた AI ゲートウェイリクエストの全体的な流れは次のとおりです:
- クライアントが OpenAI 互換のリクエストボディで
POST /ai/chatを送信 - Kong のルーターが
ai-proxyプラグインを設定したルートにマッチング - ai-proxy の
accessハンドラーが実行:parse-request:JSON をデコードし、フォーマットをllm/v1/chatと判別normalize-request:設定済みのドライバー(例:anthropic)を選択し、リクエストを変換enable-buffering:非ストリーミングリクエスト向けにレスポンスバッファリングを有効化
- Kong のバランサーが変換済みリクエストを LLM プロバイダーエンドポイントへ送信
- ai-proxy のレスポンスハンドラーが実行:
normalize-response-header:コンテントタイプヘッダーを調整parse-json-responseまたはparse-sse-chunk:プロバイダーのレスポンスをパースnormalize-json-responseまたはnormalize-sse-chunk:Kong フォーマットへ変換serialize-analytics:トークン使用量・レイテンシ・コストを記録
- 正規化されたレスポンスがクライアントへ送信
ストリーミングレスポンスの場合、STREAMING ステージのフィルターは body_filter フェーズで SSE チャンクごとに実行されます。これが REPEATED_PHASES でストリーミングステージが繰り返し可能とマークされている理由です。
ヒント:
kong/llm/schemas/init.luaの LLM スキーマには、bedrock_options_schema(AWS リージョンオーバーライド)やgemini_options_schema(Vertex AI のプロジェクト/ロケーション)といったプロバイダー固有のオプションが定義されています。これらはオプションであり、スキーマオプションが設定されていない場合、アダプターモジュールは環境変数へフォールバックします。
シリーズの結びに
この7本のシリーズを通じて、Kong の Nginx 基盤から始まり、初期化処理・リクエスト処理・プラグイン実行・スキーマ駆動のデータ管理・分散クラスタリング・AI ゲートウェイ機能まで、その内部構造を追いかけてきました。一貫して見えてきたのは、Kong がいくつかの強力な抽象化に忠実であることです。ライフサイクル管理のためのフェーズ、データモデリングのためのスキーマ、プラグイン実行のためのイテレーター、プロバイダー抽象化のためのドライバー——これらが Kong を支える骨格です。
カスタムプラグインの開発・本番環境でのデバッグ・API インフラとしての Kong の評価、どの場面においても、この内部構造への理解が Kong を「ブラックボックス」から「把握できるシステム」へと変えてくれるはずです。コードベースは大規模です(kong/init.lua 単体で約 2,000 行)。しかしパターンは一貫しており、命名は明快で、アーキテクチャは読み込むほどに理解が深まります。