Kong の AI Gateway についてのお話です。
AI Gateway(以下、AIGW)はLLMに対してのガバナンスやセキュリティなどという面で非常に便利で強力なソリューションではありますが、「Gateway」というからには通信がここを通らないと話が進みません。
例えば最近利用が進んでいるAIコーディングエディタ的なものはLLMにコードを書く支援をしてもらうことになります。
この際 「AIのキーが流出しないようにどこかで一元管理しよう。そうだ、AIGWでキーを埋め込む形にしよう!」 となる企業の皆様も多いと思います。
その第一歩として エディタ <-> AIGW <-> LLM という形にして、エディタとLLMの間にAIGWを挟む必要があります。
(ここでは、コーディングツール系のことをとりあえずエディタと呼ぶことにして進めます)
この記事ではその実際の方法をお話ししていきます。
実際にやってみる
エディタによって設定の方法は異なりますが今回は Roo Code を例にします。
AIGWは「Kong AI Gateway」を、LLMはChat GPTを使用します。
Roo CodeはVScode のExtensionとして利用可能なツールです。インストール方法や利用方法などは今回は飛ばします。
今回、Roo Codeを使う上でお話ししたい点は「どうやってLLMへの通信を、AIGW経由にするか」です。
となるとRoo Codeの通信先を変更する必要があるのでSetting -> Providersを見ていきます。
このように、API Provider として「OpenAI」を選択し、「OpenAI API Key」選択、あとはModelなどを入力したら普通に使えることができます。
今回はそもそもの宛先をOpenAIではなくAIGWにしたいので、ここを変更していきます。

ポイントとしては以下の2つです。
①宛先(URLやポートなど)を変更する
②ヘッダなどの通信の中身の足並みを揃える
準備 - Kong Konnectでの設定
実際のRoo codeの設定に入る前にKongのSaaS型制御基盤であるKonnect側の設定に移りますが、ここも今回詳しくは説明しません。
※トラブルシュート的なところも書いていくので、こちらの内容をそのまま設定したとしても、この時点ではうまく動作しません。
AIGWのメニューから、ポチポチやっていくと出来上がる仕組みになっていますが、ひとまず必要な部分のみ設定していきます。
Konnectの左から AI Gatewayを選択します。

本番ではなくテストなので、AIGWの名前やDescription, Pathなどを適当に設定していき、Saveを選択します。今回はPathとしては「/test」を入力します。
(後々、ここは変更することになりますのでどんな内容を入力してもOKです)

LLM Provider はOpenAI、Route Typeには llm/v1/chat を指定していきます。
さらに、「Provided by the AI Gateway」のところには自分が契約しているOpenAIのAIキーを入れていきます。「sk-proj-xxxxx-xxx....」みたいなアレです。

ここまで作成すると先ほどの AI Gateway のところに今作成したものが出てきます。

実際に設定ができているかの確認するために該当のGateway Manager(API Gatewayの項目から選択) -> Gateway Servicesをみると、AIManagerModelService_xxx という名前のサービスができています。

その下にはRouteもできています。
Kongの仕組みとして、このRouteに対してAPI Callをすることにより、AIGW経由でLLMに接続できるようになっています。

AIのキーを埋め込むために必要なプラグインも出来上がっています。

このAI Proxy Advanced プラグインの中を見てみると、ちゃんと先ほど設定したOpenAI のキーが入っていることが見て取れます。

ところで今回、これ実施するモチベーションとしては「AIのキーをどこかで一元管理したい!」というものでした。
例えばOpenAIのキーをユーザーやAgentが直接利用することを許可する場合、そのキーが流出してしまったら誰でもその企業のOpenAIのLLMを使い放題になってしまいます。すると短時間のうちに大きなコストが発生したり、その対応として、キーの再発行や再配布など再発防止策の検討する必要があるなど、考えるだけでキツい作業が待っています。
Kongを通せばKong AIGWのレイヤで、AI利用者(Consumer - ユーザーやAgent)に対してAIのキーを差し込むことができます。さらにここで流量制限をかけておけば、短時間で大量の利用が行われるようなことを防ぐことができます。
ということをやろうと思うとConsumerを判別=認証する必要があります。ここではKey Authの機能を使ってKongのAPIキーでの認証をします。
そのために、Key Authの設定と、Consumer設定をしていきます。
先ほどのRouteを選択し、New Pluginを押します

今回は設定を簡単にするために、「test-key」というキーをつけます。

準備は整いましたのでまずはCurlで動作を確かめてみます。
今回はDataPlane(実際にAPIを処理する、Gateway)を自分のPCのDockerにたてているので、URLは「http://localhost:8000」となります。
リクエスト内容
~ % curl -X POST \
http://localhost:8000/test \
-H "Content-Type: application/json" \
-H "apikey: test-key" \
-d '{
"model": "gpt-4o-mini",
"messages": [
{"role": "user", "content": "こんにちは"}
]
}'
レスポンス
{
"id": "chatcmpl-Ck1sGuNFzgJuKtEBq3ddM0FxT6gSW",
"object": "chat.completion",
"created": 1765086328,
"model": "gpt-4o-mini-2024-07-18",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "こんにちは!どうかされましたか?何かお手伝いできることがあれば教えてください。",
"refusal": null,
"annotations": []
},
"logprobs": null,
"finish_reason": "stop"
}
レスポンスは長いので省略しましたが、「localhost:8000/test」に対してリクエストを送り、ChatGPTから返事が返ってきているのがわかると思います。
AIGWの動作確認としてはうまくいきましたので、Roo Codeの設定にすすみます。
Roo Codeでの設定
冒頭、以下のように書きました。こちらを順番に設定していきます。
①宛先(URLやポートなど)を変更する
②ヘッダなどの通信の中身の足並みを揃する
①宛先(URLやポートなど)を変更する
まずは①から見ていきたいと思います。
Roo CodeのSetting -> Providersの設定から「API Provider」を選択しようとすると、Bedrock, Cloude Code, Geminiなど数十個の選択肢が出てきます。
ここで宛先を自由に設定できるCustom的なものを探していくと「OpenAI Compatible」があります。

Kongは以下のプラグインの説明にある通りOpenAIのフォーマットでの通信が可能です。
https://developer.konghq.com/plugins/ai-proxy/
「AI Proxy plugin accepts requests in one of a few defined and standardized OpenAI formats」
選んでみると、想定通り「Base URL」を自由に入力できそうな画面になりました。

早速リクエストを投げてみると、バッチリFailしました。どんなソリューションでも一度のチャレンジでうまくいくとは思っていないので、想定の範囲内です。

401 ということで、ステータスコード的にはUnauthorized です。
とはいえここで少なくとも「ちゃんとKongのDataPlaneにリクエストを投げているぽいな」ということが分かりました。
②ヘッダなどの通信の中身の足並みを揃えられるか
足並みというと曖昧ですが、要はフォーマットなど、送信するリクエストの内容を、期待するリクエストの内容に合わせることができるのかという話になります。
早速 Docker LogsコマンドでDataPlaneのログを見てみると以下の内容でした。
172.66.0.243 - - [07/Dec/2025:06:15:03 +0000] "POST /test/chat/completions HTTP/1.1" 401 96 "-" "RooCode/3.36.2" kong_request_id: "40006a11af83834cc4329858f466e833"
まずは「/test/chat/completions」の部分を見ると、Roo Codeのリクエストは Base URL +「/chat/completions」へ投げる仕様であるということが分かりました。
この辺り、Roo Code側の設定ファイルみたいなのをいじれば色々変更できるのかもしれませんが、今回は素直に調整していきます。
http://localhost:8000/chat/completions へ投げられたリクエスを Kongが処理して欲しいので、まずはKong KonnectでこのPathを作るために、Routeで「/test」 と適当な名前でつくっていたPathを /chat/completions に変更します。
これで「http://localhost:8000/chat/completions」への通信を処理できるようになりました。
次にRoo Code側でも、Base URLからPath情報などを抜いて「http://localhost:8000」のみにします。

これでRoo Codeは http://localhost:8000/chat/completions へアクセスしに行って、Kong AIGWはそれを受け取って処理することができるはずです。
やってみると、エラーコードが代わりました。Internal Server Errorです。
一歩前進しました。

まだ情報が足りません。そもそもどういう形でRoo Codeがヘッダを送るのかを確認したいと思います。
OpenAI compatibleを選択しているので大体想像はつくのですが「見てないものはあまり信じない」の信条に基づいて見ていきましょう。
こういう、ちょっとした困った時のトラシューなどに大活躍するのが、Pre-functionsプラグイン(正式名:kong functions (pre-plugins))です。
なんでもできるというワケではありませんが、Luaでコードを書いてNginxのそれぞれのフェーズ(Access, Functions など)で実行できる優れものです。

以下のような内容をpre-functionsのaccessフェーズに書き込みます。
-- request の全ヘッダをログに出力
local headers = kong.request.get_headers()
-- ヘッダ一覧を文字列として整形
local header_string = ""
for k,v in pairs(headers) do
-- v がテーブル(複数値ヘッダ)の場合も考慮
if type(v) == "table" then
v = table.concat(v, ", ")
end
header_string = header_string .. k .. ": " .. v .. "\n"
end
-- Kong のログに出力
kong.log.notice("=== Incoming Request Headers ===\n" .. header_string)
すると、docker logsに以下の内容が出てきます。
2025/12/07 07:51:59 [notice] 2683#0: *9781 [kong] [string "-- request の全ヘッダをログに出力..."]:15 [pre-function] === Incoming Request Headers ===
authorization: Bearer test-key
host: localhost:8000
x-stainless-retry-count: 0
x-stainless-lang: js
x-stainless-package-version: 5.12.2
connection: keep-alive
x-stainless-arch: arm64
accept: application/json
x-stainless-runtime-version: v22.20.0
user-agent: RooCode/3.36.2
http-referer: https://github.com/RooVetGit/Roo-Cline
x-title: Roo Code
accept-language: *
sec-fetch-mode: cors
x-stainless-runtime: node
accept-encoding: gzip, deflate
content-type: application/json
content-length: 51888
x-stainless-os: MacOS
, client: 192.168.65.1, server: kong, request: "POST /chat/completions HTTP/1.1", host: "localhost:8000", request_id: "48c63000ed66e66b74dbe02ee3c8db19"
192.168.65.1 - - [07/Dec/2025:07:51:59 +0000] "POST /chat/completions HTTP/1.1" 401 96 "-" "RooCode/3.36.2" kong_request_id: "48c63000ed66e66b74dbe02ee3c8db19"
これを見たところだと、
「authorization: Bearer test-key」
がKongのAIGWの期待するapi keyとあってないことが分かります。
OpenAIに直接通信する場合はこのBearerの書き方でいいのですが、Kongとしてはマニュアルにあるように以下のヘッダを期待してます。
https://developer.konghq.com/plugins/key-auth/
-H 'apikey: $APIKEY'
この差をどう埋めるか、というところなのですが、これも2種類やり方があります。
・Roo Codeで埋める
・Kong AIGWで埋める
Roo Codeでも、例えば以下のCustom Headers などを使えばできる気はするのですが、今回はやりません。
実運用を考えると、従業員側のRoo Codeの設定を変えていくのは面倒だからです。
もしかするとMDMなどで設定ファイルを配るようなこともできるかもしれませんが、それはそれでこの作業に関わる部署が増えたりして手間だと思います。

なので、Kong側の変更にて対応してみます。
これも2つやり方があります。
・Consumerに設定した Key Authプラグインの API KeyなどをRoo Codeの出すヘッダの形に合わせる
・プラグインで変更する
一つ目はちょっとカッコよくないですが、簡単に設定できますのでテストにはこれも利用できると思います。
まず、デフォルト「apikey」となっている Kay Auth プラグインのこの設定を、「authorization」に変更します。

そしてConsumerに設定した「test-key」を「Bearer test-key」にすることで、認証のために期待する内容が以下のようになります。
変更前:「apikey: test」 → 変更後:「authorization: Bearer test-key」
これはつまりRoo Codeが送ってくる内容と一致するため、認証が通るようになります。
よりおしゃれかつ実用的な方法としては後者の、ここを「Request Transformer Advanced」プラグインで書き換えることです。
そんなにグイグイ書き換えていいの?と思うかもしれませんが、実際のAPI利用においては期待するヘッダの不一致というのはよくあることであり、これをアプリ側でのコード書き換えで修正するのは辛いため Kong API GWを入れることでいわゆる疎結合(通信元のアプリの変更に、通信先の相手が影響されないようにする)を実現する、というのはAPI/AI GWを入れる大きなメリットの一つです。
早速、Routeに対して Request Transformer Advancedを適用していきます。

書く内容:
apikey:$((function()
local v = headers.Authorization
if not v then
return
end
local token = v:match("[Bb]earer%s+(.+)")
return token
end)())
これはしなくてもそう動作に影響はないのですが、スッキリするために Remove.Headersを利用して元々のAuthorizationヘッダーを削除します。

実際のHeaderのRemove自体はこのプラグインの最後に実行されるため、これをしたとしても先ほどのAdd.Headersの動作に影響はありません。
Replace.Headersでまとめてできるんじゃないの?と思われるかもしれませんがこちらは基本的に「同じヘッダ名の値を上書きする」ような使い方なので、今回のユースケースには向きません。
ちなみに、このやり方は「Kong Docs AI」さんが教えてくれました。
KongのDeveloperサイト(https://developer.konghq.com/) の右下に住んでいて、大体のことに答えてくれる24時間体制で働く超人なので、ぜひ活用してください。

次にやるのは、プラグイン実行順序の変更です。
Kongのプラグインは、以下のサイトのにあるように、それぞれ固定のPriorityを持ってその順序通りに動きます。
https://developer.konghq.com/gateway/entities/plugin/#plugin-priority
Pre-functionのような特殊なものが高いPriority、基本的にはその後にセキュリティ系や認証や認可・・・というふうに続くのですが、Transformer系はKey Authなどの認証系の後です。
しかし今回のケースでは、認証の前にこのRequest Transformer Advancedを実行しないと認証が通りません。
そこで、プラグインの実行順序を変更可能なDynamic Ordering機能を使います。
ちなみに実環境では、やりすぎると何がどの順序で実行されるのかの管理が難しくなるため多用はお勧めできません。
その場合には、Priorityを決め打ちしてプラグインを作成できるカスタムプラグインなどの利用が良いかとは思います。
以下のように該当Plug-inのOrderingから「Pre-functionsの前」に実行することができます。

本来は「Key Authの前」でいいのですが、今回においては次のように、ヘッダのログを出すPre-funtionsの前に実行することにします。
動作確認していきます。Roo Codeからリクエストを打った後にDocker Logsで見ると
2025/12/07 09:08:27 [notice] 2682#0: *25494 [kong] [string "-- request の全ヘッダをログに出力..."]:15 [pre-function] === Incoming Request Headers ===
x-stainless-runtime: node
apikey: test-key <-- 追加されてる!!
host: localhost:8000
connection: keep-alive
x-stainless-runtime-version: v22.20.0
accept: application/json
http-referer: https://github.com/RooVetGit/Roo-Cline
user-agent: RooCode/3.36.2
x-title: Roo Code
accept-language: *
sec-fetch-mode: cors
accept-encoding: gzip, deflate
x-stainless-retry-count: 1
x-stainless-lang: js
x-stainless-package-version: 5.12.2
content-type: application/json
x-stainless-os: MacOS
content-length: 55374
x-stainless-arch: arm64
, client: 172.66.0.243, server: kong, request: "POST /chat/completions HTTP/1.1", host: "localhost:8000", request_id: "cea7fe4eacb1d5112f3e394681b2036b"
authorization: Bearer が消えて、「apikey: test-key」が入っているのが見てとれると思います。
これで認証は通るようになりました。
しかし、またFailしました。後一歩です。
もう慣れたものなので、ログを見ていきます。

ログは以下でした。
2025/12/07 08:31:19 [warn] 2688#0: *17613 [kong] parse-request.lua:138 [ai-proxy-advanced] unable to parse request body, consider increase plugin's config.max_request_body_size or Kong's nginx_http_client_body_buffer_size; the request content-length is 47407 bytes, client: 192.168.65.1, server: kong, request: "POST /chat/completions HTTP/1.1", host: "localhost:8000", request_id: "777cf63c05b774277eb02473bd9ab977"
これはもう内容を見ても分かる通り、AI proxy advanced プラグインの以下の「Max Request Body Size」の制限によるものです。デフォルトでは8192なので、これをとりあえず今回は100000まで上げておきます。
あまりに大きなリクエストを制限するための項目なので、実際の運用においては適宜調整してください。
確認作業 - 成功!
通りました!
Roo Code上で、返事が来ていることが分かります。

Kong AI GatewayによりAIのキーが埋め込まれ、OpenAIにリクエストが届き、レスポンスが来ています。
Docker Logsにも Status code = 200 のログが出てます。
"POST /chat/completions HTTP/1.1" 200 471 "-" "RooCode/3.36.2" kong_request_id: "47fdb00906064313b2c088242179382a"
もちろん、このリクエスト・レスポンスのテレメトリデータはDataPlaneからKong Konnectにも送られ、Konnect側でも成功したことが見て取れます。
(メトリクスのみ収集し、リクエストの中のPayloadなどは送られないためご安心ください)
ありがとうございました!
より汎用的に使えるように、トラブルシュートのやり方なども書いておきたかったので長くなりましたが、やっていることは実はシンプルです。
Kong API / AI Gatewayの「だいたいのことに対応できる」というハンパじゃない柔軟性を感じていただけると幸いです。











