Read OSS

Kong 作为 AI 网关:LLM 驱动架构详解

中级

前置知识

  • 第 1 篇:架构与 Nginx 集成
  • 第 4 篇:插件系统与迭代器(插件 handler 模式)
  • 对 LLM API 的基本了解(Chat Completions、流式传输、Token 计数)

Kong 作为 AI 网关:LLM 驱动架构详解

Kong 近期最重要的新能力是 AI 网关功能——一个专门用于代理、转换和观测大型语言模型(LLM)请求的子系统。Kong 没有另起炉灶构建一个独立的 AI 代理,而是将 LLM 支持直接嵌入其插件架构,复用了本系列文章中反复介绍的 phase 管道、配置系统和可观测性基础设施。

整个设计的核心是驱动模式(driver pattern):每个 LLM provider(OpenAI、Anthropic、Azure、AWS Bedrock、Google Gemini、Cohere、Hugging Face)都以一个驱动模块实现,并遵循统一的接口规范。公共的 HTTP 转换逻辑由共享工具模块处理,云平台认证则被解耦到独立的 adapter 模块中。

LLM 模块架构与格式检测

LLM 子系统位于 kong/llm/ 目录下,入口文件为 kong/llm/init.lua。该模块的首要职责是格式检测——判断传入请求是 chat completion 还是 text completion:

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 provider:

驱动 文件 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(自托管)
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]

驱动模式:Provider 抽象

每个驱动模块都实现了包含 to_formatfrom_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

云 Adapter 与认证

云托管 LLM 服务的认证逻辑与驱动逻辑解耦,由专用的云 adapter 负责凭证管理,不会污染格式转换代码。

Adapter 模块位于 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 中的认证 schema 提供了灵活的认证配置,支持基于请求头的认证(API key)、查询参数认证以及云原生认证方式:

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 = truereferenceable = true 这两个注解。encrypted 标志表示该字段在数据库中静态加密存储(企业版功能)。referenceable 标志则表示字段值可以是 Kong Vault 引用,例如 {vault://env/OPENAI_API_KEY},从而与 Kong 的密钥管理系统集成。

提示: 对于 AWS Bedrock 等云 provider,无需显式配置 API key。Adapter 会遵循标准的 AWS SDK 凭证链——环境变量、IAM Role 或实例配置文件均可。只需设置 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 phase 的"阶段(stages)"系统:

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 标准格式转换为目标 provider 的格式;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 现有的指标基础设施无缝集成。每个请求的 token 计数、延迟和费用均被追踪,并通过 Kong 标准的日志插件暴露出来——无需额外配置,即可使用 http-logdatadogprometheus 监控 AI 网关流量。

kong/llm/plugin/ctx.lua 中的上下文模块提供了命名空间隔离的请求级状态管理。每个 AI 插件拥有独立的上下文命名空间,避免多个 AI 插件在同一请求上运行时(例如 ai-prompt-guard 后接 ai-proxy)产生状态冲突。

完整的 AI 请求流程

一次完整的 AI 网关请求在 Kong 中的处理流程如下:

  1. 客户端向 POST /ai/chat 发送一个与 OpenAI 兼容的请求体
  2. Kong 的路由器将请求匹配到配置了 ai-proxy 插件的路由
  3. ai-proxy 的 access handler 执行:
    • parse-request:解码 JSON,将格式识别为 llm/v1/chat
    • normalize-request:选择已配置的驱动(如 anthropic),对请求进行转换
    • enable-buffering:为非流式请求启用响应缓冲
  4. Kong 的负载均衡器将转换后的请求发送至 LLM provider 端点
  5. ai-proxy 的响应 handler 依次执行:
    • normalize-response-header:调整 content-type 响应头
    • parse-json-responseparse-sse-chunk:解析 provider 的响应
    • normalize-json-responsenormalize-sse-chunk:将响应翻译回 Kong 格式
    • serialize-analytics:记录 token 用量、延迟和费用
  6. 将标准化后的响应返回给客户端

对于流式响应,STREAMING 阶段的过滤器会在 body_filter phase 中对每个 SSE chunk 执行——这也是 REPEATED_PHASES 将流式阶段标记为可重复执行的原因。

提示: kong/llm/schemas/init.lua 中的 LLM schema 定义了 provider 专属选项,例如 bedrock_options_schema(AWS 区域覆盖)和 gemini_options_schema(Vertex AI 项目/位置)。这些选项均为可选——当 schema 未设置时,adapter 模块会自动回退到环境变量。

系列总结

在这七篇文章中,我们从 Nginx 基础出发,依次深入了解了 Kong 的初始化流程、请求处理、插件执行、基于 schema 的数据管理、分布式集群机制,以及 AI 网关能力。贯穿始终的主线,是 Kong 对几个核心抽象的坚守:用于生命周期管理的 phase、用于数据建模的 schema、用于插件执行的迭代器,以及用于 provider 抽象的驱动模式。

无论你是在为 Kong 开发自定义插件、排查生产问题,还是评估 Kong 能否承载你的 API 基础设施,理解这些内部机制都能让 Kong 从一个黑盒变成一个清晰可读的系统。代码库体量庞大——仅 kong/init.lua 就接近 2000 行——但模式始终一致,命名清晰直观,深入阅读源码会让你收获颇丰。