5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AI Gateway 経由で通信する! - コーディングツールとLLMの間で -

Last updated at Posted at 2025-12-08

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にしたいので、ここを変更していきます。
CleanShot 2025-12-07 at 10.23.31.png

ポイントとしては以下の2つです。
①宛先(URLやポートなど)を変更する
②ヘッダなどの通信の中身の足並みを揃える

準備 - Kong Konnectでの設定

実際のRoo codeの設定に入る前にKongのSaaS型制御基盤であるKonnect側の設定に移りますが、ここも今回詳しくは説明しません。

※トラブルシュート的なところも書いていくので、こちらの内容をそのまま設定したとしても、この時点ではうまく動作しません。

AIGWのメニューから、ポチポチやっていくと出来上がる仕組みになっていますが、ひとまず必要な部分のみ設定していきます。
Konnectの左から AI Gatewayを選択します。
CleanShot 2025-12-07 at 11.01.22.png

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

次の画面で、LLMsの設定などをやっていきます。
CleanShot 2025-12-07 at 14.16.46.png

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

ここまで作成すると先ほどの AI Gateway のところに今作成したものが出てきます。
CleanShot 2025-12-07 at 14.17.59.png

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

その下にはRouteもできています。
Kongの仕組みとして、このRouteに対してAPI Callをすることにより、AIGW経由でLLMに接続できるようになっています。
CleanShot 2025-12-08 at 09.45.14@2x.png

AIのキーを埋め込むために必要なプラグインも出来上がっています。
CleanShot 2025-12-07 at 14.22.42.png

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

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

そのために、Key Authの設定と、Consumer設定をしていきます。
先ほどのRouteを選択し、New Pluginを押します
CleanShot 2025-12-07 at 14.30.05.png

ひとまずデフォルト設定でSaveします。
CleanShot 2025-12-07 at 14.32.10.png

New Consumerを作成し
CleanShot 2025-12-07 at 14.34.31.png

Key Authenticationを設定します。
CleanShot 2025-12-07 at 14.35.23.png

今回は設定を簡単にするために、「test-key」というキーをつけます。
CleanShot 2025-12-07 at 14.36.18.png

準備は整いましたのでまずは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」があります。
CleanShot 2025-12-07 at 10.31.55.png

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」を自由に入力できそうな画面になりました。
CleanShot 2025-12-07 at 10.49.28.png

早速、Base URLとAPI keyを入力してみます。
CleanShot 2025-12-07 at 14.59.13.png

適当な名前でProfileを保存しておきます。
CleanShot 2025-12-07 at 15.06.57.png

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

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」への通信を処理できるようになりました。

CleanShot 2025-12-07 at 15.53.59.png

次にRoo Code側でも、Base URLからPath情報などを抜いて「http://localhost:8000」のみにします。
CleanShot 2025-12-07 at 15.55.18.png

これでRoo Codeは http://localhost:8000/chat/completions へアクセスしに行って、Kong AIGWはそれを受け取って処理することができるはずです。

やってみると、エラーコードが代わりました。Internal Server Errorです。
一歩前進しました。
CleanShot 2025-12-07 at 15.58.10.png

まだ情報が足りません。そもそもどういう形でRoo Codeがヘッダを送るのかを確認したいと思います。
OpenAI compatibleを選択しているので大体想像はつくのですが「見てないものはあまり信じない」の信条に基づいて見ていきましょう。

こういう、ちょっとした困った時のトラシューなどに大活躍するのが、Pre-functionsプラグイン(正式名:kong functions (pre-plugins))です。

CleanShot 2025-12-07 at 15.30.58.png

なんでもできるというワケではありませんが、Luaでコードを書いてNginxのそれぞれのフェーズ(Access, Functions など)で実行できる優れものです。
CleanShot 2025-12-07 at 15.32.44.png

以下のような内容を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などで設定ファイルを配るようなこともできるかもしれませんが、それはそれでこの作業に関わる部署が増えたりして手間だと思います。
CleanShot 2025-12-07 at 17.06.04.png
なので、Kong側の変更にて対応してみます。
これも2つやり方があります。
・Consumerに設定した Key Authプラグインの API KeyなどをRoo Codeの出すヘッダの形に合わせる
・プラグインで変更する

一つ目はちょっとカッコよくないですが、簡単に設定できますのでテストにはこれも利用できると思います。
まず、デフォルト「apikey」となっている Kay Auth プラグインのこの設定を、「authorization」に変更します。
CleanShot 2025-12-07 at 17.18.43.png

そして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を適用していきます。
CleanShot 2025-12-07 at 17.41.36.png

Add.Headers に以下を書いていきます。
CleanShot 2025-12-07 at 18.12.05.png

書く内容:

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ヘッダーを削除します。
CleanShot 2025-12-07 at 18.31.27.png
実際のHeaderのRemove自体はこのプラグインの最後に実行されるため、これをしたとしても先ほどのAdd.Headersの動作に影響はありません。
Replace.Headersでまとめてできるんじゃないの?と思われるかもしれませんがこちらは基本的に「同じヘッダ名の値を上書きする」ような使い方なので、今回のユースケースには向きません。

ちなみに、このやり方は「Kong Docs AI」さんが教えてくれました。

CleanShot 2025-12-07 at 18.10.32.png

KongのDeveloperサイト(https://developer.konghq.com/) の右下に住んでいて、大体のことに答えてくれる24時間体制で働く超人なので、ぜひ活用してください。
CleanShot 2025-12-08 at 12.46.51.png

次にやるのは、プラグイン実行順序の変更です。
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の前」に実行することができます。
CleanShot 2025-12-07 at 18.17.14.png
本来は「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しました。後一歩です。
もう慣れたものなので、ログを見ていきます。
CleanShot 2025-12-07 at 17.31.48.png

ログは以下でした。

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まで上げておきます。
あまりに大きなリクエストを制限するための項目なので、実際の運用においては適宜調整してください。

CleanShot 2025-12-07 at 17.33.34.png

確認作業 - 成功!

通りました!
Roo Code上で、返事が来ていることが分かります。
CleanShot 2025-12-07 at 17.35.38.png

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などは送られないためご安心ください)

CleanShot 2025-12-07 at 17.37.04.png

ありがとうございました!

より汎用的に使えるように、トラブルシュートのやり方なども書いておきたかったので長くなりましたが、やっていることは実はシンプルです。
Kong API / AI Gatewayの「だいたいのことに対応できる」というハンパじゃない柔軟性を感じていただけると幸いです。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?