dot Redis 8 已发布——并且它是开源的

了解更多

使用 Kong AI 网关和 Redis 进行语义处理和向量相似性搜索

从最早的版本开始,Kong 就已支持 Redis。如今,Kong API 网关和 Redis 集成是一种强大的组合,可以在三个主要用例组中增强 API 管理

  • Kong API 网关:Kong 通过插件与 Redis 集成,使其能够利用 Redis 的能力来增强 API 功能,包括缓存、速率限制和会话管理。
  • Kong AI 网关:从 Kong Gateway 3.6 开始,几个新的基于 AI 的插件利用 Redis 向量数据库来实现 AI 用例,例如基于 LLM token 的速率限制策略、语义缓存、语义提示保护和语义路由。
  • 检索增强生成 (RAG) 和代理应用:Kong AI 网关和 Redis 可以使用 LangChain 和 LangGraph 等框架协同构建基于 AI 的应用。

Kong 支持适用于所有用例的多种类型的 Redis 部署,包括 Redis 社区版(包括在使用 Redis Cluster 进行水平扩展或使用 Redis Sentinel 实现高可用性时)、Redis Software(提供生产工作负载常需要的企业级功能)以及 Redis Cloud(可在 AWS、GCP 上使用,也可作为 Azure 中的 Azure 管理型 Redis 使用)。 

在这篇文章中,我们将重点介绍如何使用 Kong 和 Redis 来解决语义处理用例,包括跨多个 LLM 环境的相似性搜索和语义路由。

Kong AI 网关参考架构

首先,让我们看看 Kong AI 网关的高级参考架构。如您所见,负责处理传入流量的 Kong Gateway 数据平面可以配置两种类型的 Kong 插件

Kong API 网关插件

Kong Gateway 提供的主要能力之一是可扩展性。丰富的插件列表允许您实施特定的策略来保护和控制部署在网关中的 API。插件负责分担通常由后端服务和应用实现的复杂而关键的处理工作。通过使用网关及其插件,后端服务可以只专注于业务逻辑,从而加快应用开发过程。每个插件负责特定的功能,包括

  • 认证/授权:用于实施安全机制,例如基本认证、LDAP、相互 TLS (mTLS)、API 密钥、基于开放策略代理 (OPA) 的访问控制策略等。
  • 与基于 Kafka 的数据/事件流基础设施集成。
  • 日志处理:将网关处理的所有请求外部化到第三方基础设施。
  • 分析和监控:向外部系统(包括基于 OpenTelemetry 的系统和 Prometheus)提供指标。
  • 流量控制:用于实现金丝雀发布、模拟端点以及基于请求头的路由策略等。
  • 转换:用于在将请求路由到上游之前对其进行转换,以及在将响应返回给消费者之前对其进行转换。
  • WebSockets 大小限制和 WebSockets 验证器插件:用于控制设备发送的事件,适用于广泛使用基于 WebSockets 的 MQTT 连接的物联网项目。

此外,Kong API 网关还提供了多个与 Redis 集成的插件,包括

Kong AI 网关插件

另一方面,Kong AI 网关利用现有的 Kong API 网关可扩展性模型来提供特定的基于 AI 的插件,更精确地保护 LLM 基础设施,包括

  • AI 代理高级 AI 代理插件:利用多 LLM 能力根据策略(包括延迟时间、模型使用、语义等)抽象和负载均衡多个 LLM 模型。
  • 提示工程模板
    • AI 提示模板插件:负责为用户预配置 AI 提示。
    • AI 提示装饰器插件:在调用者的聊天历史的开始或结束处注入消息。
    • AI 提示保护插件:允许您配置一系列与 PCRE 兼容的正则表达式来允许和阻止特定的提示、单词、短语等,从而对 LLM 服务有更多控制。
    • AI 语义提示保护插件:实现可自配置的语义(或模式匹配)提示保护。
  • AI 语义缓存插件:基于阈值缓存响应以提高性能(从而改善最终用户体验),同时优化成本。
  • 高级 AI 速率限制:允许您根据 LLM 提供商返回的 token 量定制每个用户或每个模型的策略,或创建自定义函数来计算请求的 token。
  • AI 请求转换器AI 响应转换器插件:与 LLM 无缝集成,允许在将请求体代理到上游服务之前对其进行内省和转换,然后在将响应转发给客户端之前再次进行转换。

通过利用 Kong Gateway 相同的底层核心,并结合这两类插件,我们可以实现强大的策略,并降低部署 AI 网关功能的复杂性。

我们要关注的第一个用例是语义缓存,其中 AI 网关插件与 Redis 集成以执行相似性搜索。然后,我们将探讨高级 AI 代理插件如何利用 Redis 来实现跨多个 LLM 模型的语义路由。 

Kong 和 Redis 的另外两个值得注意的用例是高级 AI 速率限制和 AI 语义提示保护插件,但我们不会在本文中详细介绍它们。

在深入探讨第一个用例之前,让我们强调并总结一下 Kong AI 网关和 Redis 所依赖的主要概念。

嵌入 (Embeddings)

嵌入,也称为向量或向量嵌入,是文本、图像等非结构化数据的表示形式。在 LLM 环境中,嵌入的维度是指在给定句子的向量表示中捕获的特征数量——嵌入的维度越多,它就越好、越有效。

NLP 中有多种基于机器学习的嵌入方法,例如

这里有一个使用Sentence Transformers模块(也称为 SBERT 或 Sentence-BERT,由Hugging Face维护),并使用“all-mpnet-base-v2”嵌入模型将一个简单句子编码为嵌入的 Python 脚本示例

from sentence_transformers import SentenceTransformer
from sentence_transformers.util import truncate_embeddings

model = SentenceTransformer('all-mpnet-base-v2', cache_folder="./")
embeddings = model.encode("Who is Joseph Conrad?")
embeddings = truncate_embeddings(embeddings, 3)
print(embeddings.size)
print(embeddings)

“all-mpnet-base-v2”嵌入模型将句子编码为 768 维向量。作为实验,我们仅将向量截断为 3 维。

输出应如下所示

3
[ 0.06030013 -0.00782523  0.01018228]

向量数据库

向量数据库存储和搜索向量嵌入。它们对于支持图像、文本等的基于 AI 的应用至关重要,这些应用提供向量存储、向量索引以及——更重要的是——实现向量相似性搜索的算法。

我们已经撰写了一些关于将 Redis 用于向量嵌入向量数据库的介绍。我们非常适合这些用例的原因在于 Redis Query Engine——Redis 内置的一项功能,提供向量搜索功能(以及全文、数字等其他类型的搜索),并提供行业领先的性能。Redis 利用内存数据结构和高级优化,以亚毫秒级的延迟提供了无与伦比的性能,为大规模实时应用提供支持。这对于网关用例至关重要,因为部署发生在 LLM 查询的“热路径”中。 

此外,Redis 可以部署为企业软件和/或云服务,从而增加了多项企业级功能,包括

  • 可扩展性。Redis 可以轻松地水平扩展,轻松处理动态工作负载,并在分布式架构中管理海量数据集。
  • 高可用性和持久性。Redis 通过内置支持多可用区部署、无缝故障转移、通过备份实现数据持久性以及主动-主动架构来实现强大的灾难恢复和一致的应用性能,从而支持高可用性。
  • 灵活性。Redis 原生支持多种数据结构,例如 JSON、哈希、字符串、流等,以满足不同的应用需求。
  • 广泛的生态系统。作为世界上最受欢迎的数据库之一,我们提供丰富的客户端库生态系统(用于 GenAI 用例的redisvl)、开发者工具和集成。  

向量相似性搜索

通过相似性搜索,我们可以在通常是非结构化的数据集中找到与某个呈现项目相似(或不相似)的项目。例如,给定一张手机图片,尝试查找与其形状、颜色等相似的手机图片。或者,给定两张图片,检查它们之间的相似度得分。

在我们的 NLP 环境中,我们关注应用向 LLM 发送提示时返回的相似响应。例如,以下两个句子:“谁是约瑟夫·康拉德?”和“告诉我更多关于约瑟夫·康拉德的信息”,从语义上讲,它们的相似度得分应该很高。

我们可以扩展我们的 Python 脚本来试试看

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-mpnet-base-v2', cache_folder="./")

sentences = [
    "Who is Joseph Conrad?",
    "Tell me more about Joseph Conrad.",
    "Living is easy with eyes closed.",
]

embeddings = model.encode(sentences)
print(embeddings.shape)

similarities = model.similarity(embeddings, embeddings)
print(similarities)

输出应如下所示。嵌入是

(3, 768)
tensor([[1.0000, 0.8600, 0.0628],
        [0.8600, 1.0000, 0.1377],
        [0.0628, 0.1377, 1.0000]])

“shape”由 3 个各 768 维的嵌入组成。代码要求交叉检查所有嵌入的相似度。它们越相似,得分越高。请注意,如预期,当自检查给定嵌入时,会返回“1.0000”的得分。

similarity”方法返回一个“Tensor”对象,该对象由 Sentence Transformer 使用的 ML 库PyTorch实现。

有几种计算相似度的技术,包括向量之间的距离或角度。最常用的方法是

在向量数据库环境中,向量相似性搜索 (VSS) 是指在向量数据库中查找与给定查询向量相似的向量的过程。

Redis Query Engine 和向量相似性搜索

2022 年,Redis 推出了搜索——一个基于 Redis 数据存储构建的文本搜索引擎,带有 RedisVSS(向量相似性搜索)。

为了更好地理解 RedisVSS 的工作原理,请看这个实现基本相似性搜索的 Python 脚本

import redis
from redis.commands.search.field import TextField, VectorField
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
from redis.commands.search.query import Query
import numpy as np
import openai
import os

### Get environment variables
openai.api_key = os.getenv("OPENAI_API_KEY")
host = os.getenv("REDIS_LB")

### Create a Redis Index for the Vector Embeddings
client = redis.Redis(host=host, port=6379)

try:
    client.ft('index1').dropindex(delete_documents=True)
except:
    print("index does not exist")


schema = (
    TextField("name"),
    TextField("description"),
    VectorField(
        "vector",
        "FLAT",
        {
            "TYPE": "FLOAT32",
            "DIM": 1536,
            "DISTANCE_METRIC": "COSINE",
        }
    ),
)

definition = IndexDefinition(prefix=["vectors:"], index_type=IndexType.HASH)
res = client.ft("index1").create_index(fields=schema, definition=definition)


### Step 1: call OpenAI to generate Embeddings for the reference text and stores it in Redis

name = "vector1"
content = "Who is Joseph Conrad?"
redis_key = f"vectors:{name}"

res = openai.embeddings.create(input=content, model="text-embedding-3-small").data[0].embedding

embeddings = np.array(res, dtype=np.float32).tobytes()

pipe = client.pipeline()
pipe.hset(redis_key, mapping = {
  "name": name,
  "description": content,
  "vector": embeddings
})
res = pipe.execute()


### Step 2: perform Vector Range queries with 2 new texts and get the distance (similarity) score

query = (
    Query("@vector:[VECTOR_RANGE $radius $vec]=>{$yield_distance_as: distance_score}")
     .return_fields("id", "distance_score")
     .dialect(2)
)

# Text #1
content = "Tell me more about Joseph Conrad"
res = openai.embeddings.create(input=content, model="text-embedding-3-small").data[0].embedding
new_embeddings = np.array(res, dtype=np.float32).tobytes()

query_params = {
    "radius": 1,
    "vec": new_embeddings
}
res = client.ft("index1").search(query, query_params).docs
print(res)

# Text #2
content = "Living is easy with eyes closed"
res = openai.embeddings.create(input=content, model="text-embedding-3-small").data[0].embedding
new_embeddings = np.array(res, dtype=np.float32).tobytes()

query_params = {
    "radius": 1,
    "vec": new_embeddings
}
res = client.ft("index1").search(query, query_params).docs
print(res)

首先,脚本创建一个索引来接收 OpenAI 返回的嵌入。我们使用的是“text-embedding-3-small” OpenAI 模型,它有 1536 个维度,因此索引定义了一个 VectorField 来支持这些维度。

接下来,脚本有两个步骤

  • 存储由 OpenAI 嵌入模型生成的参考文本的嵌入。
  • 执行向量范围查询,传入两个新的文本来检查它们与原始文本的相似度。

这是一张表示这些步骤的图表

此代码假定您已有一个可用的 Redis 环境。请查阅Redis 产品文档以了解更多信息。它还假定您已定义了两个环境变量:OpenAI API 密钥和 Redis 可用的负载均衡器地址。

该脚本使用两个主要库编写

  • “Python client for Redis” (redis-py) 库:用于进行 Redis 调用。
While executing the code, you can monitor Redis with, for example, redis-cli monitor. The code line res = client.ft("index1").search(query, query_params).docs 

should log a message like this one:

"FT.SEARCH" "index1" "@vector:[VECTOR_RANGE $radius $vec]=>{$YIELD_DISTANCE_AS: score}" "RETURN" "2" "id" "score" "DIALECT" "2" 
"LIMIT" "0" "10" "params" "4" "radius" "1" "vec" "\xcb9\x9c<\xf8T\x18=\xaa\xd4\xb5\xbcB\xc0.=\xb5………."

让我们检查一下命令。隐式地,.ft(“index1”)方法调用使我们能够支持Redis 搜索命令,因为 .search(query, query_params) 调用发送的是使用 FT.SEARCH Redis 命令的实际搜索查询。 

FT.SEARCH 命令接收在 queryquery_params 对象中定义的参数。使用 Query 对象定义的 query parameter 指定实际的命令以及返回字段和方言。

query = (
    Query("@vector:[VECTOR_RANGE $radius $vec]=>{$yield_distance_as: distance_score}")
     .return_fields("id", "distance_score")
     .dialect(2)
)

我们要返回距离(相似度)得分,因此必须通过 $yield_distance_as 属性来获取它。

查询方言允许增强查询 API,在保持与现有应用兼容性的同时引入新功能。对于像我们这样的向量查询,查询方言的值应设置为等于或大于 2。请查阅特定的查询方言文档页面以了解更多信息。

另一方面,query_params 对象定义了额外的参数,包括半径和应该考虑用于搜索的嵌入。

query_params = {
    "radius": 1,
    "vec": new_embeddings
}

最终的 FT.SEARCH 还包括定义偏移量和结果数量的参数。请查阅我们的文档以了解更多信息。

事实上,脚本发送的 FT.SEARCH 命令只是 Redis 支持的向量搜索的一个示例。基本上,Redis 支持两种主要类型的搜索

  • KNN 向量搜索。此算法查找与查询向量最接近的“k个近邻”。
  • 向量范围查询。在此查询类型中,脚本 根据半径参数过滤索引。半径定义了两个向量之间的语义距离:输入查询向量和索引向量。

我们脚本的目的是检查两个向量之间的距离,而不是实现任何过滤。这就是为什么它将向量范围查询的 “radius” 设置为 1。

运行脚本后,其输出应为

[Document {'id': 'vectors:vector1', 'payload': None, 'distance_score': '0.123970687389'}]
[Document {'id': 'vectors:vector1', 'payload': None, 'distance_score': '0.903066933155'}]

这意味着,正如预期,与参考文本“谁是约瑟夫·康拉德?”相关的存储嵌入,与第一个新文本“告诉我更多关于约瑟夫·康拉德的信息”更接近,而不是与第二个新文本“闭着眼睛生活很轻松”。

现在我们已经初步了解了如何使用 Redis 实现向量相似性搜索,接下来我们将研究负责实现语义缓存的 Kong AI 网关语义缓存插件。我们将看到它执行的搜索与我们使用 Python 脚本执行的搜索类似。

Kong AI 语义缓存插件

首先,从逻辑上讲,我们可以从两个不同的角度分析缓存流程

  • 请求 #1。我们没有缓存任何数据。
  • 请求 #2。Kong AI 网关已经在 Redis 向量数据库中存储了一些数据。

这是一张说明这些场景的图表

Konnect 数据平面部署

在探讨 Kong AI 网关语义缓存插件和 Redis 如何协同工作之前,我们必须部署一个 Konnect 数据平面(基于 Kong Gateway)。请参考Konnect 文档来注册并启动您的第一个数据平面。

Kong Gateway 对象创建

接下来,我们需要创建 Kong Gateway 对象(Kong Gateway 服务、Kong 路由和 Kong 插件)来实现用例。有几种方法可以做到这一点,包括 Konnect RESTful API、Konnect GUI 等。使用decK(Kong 的声明),我们可以以声明方式管理 Kong Konnect 配置和创建 Kong 对象。请查阅decK 文档以了解如何将其与 Konnect 结合使用

AI 代理和 AI 语义缓存插件

以下是我们将提交给 Konnect 以实现语义缓存用例的 decK 声明

_format_version: "3.0"
_info:
  select_tags:
  - semantic-cache
_konnect:
  control_plane_name: default
services:
- name: service1
  host: localhost
  port: 32000
  routes:
  - name: route1
    paths:
    - /openai-route
    plugins:
    - name: ai-proxy
      instance_name: ai-proxy-openai-route
      enabled: true
      config:
        auth:
          header_name: Authorization
          header_value: Bearer <your_OPENAI_APIKEY>
        route_type: llm/v1/chat
        model:
          provider: openai
          name: gpt-4
          options:
            max_tokens: 512
            temperature: 1.0
    - name: ai-semantic-cache
      instance_name: ai-semantic-cache-openai
      enabled: true
      config:
        embeddings:
          auth:
            header_name: Authorization
            header_value: Bearer <your_OPENAI_APIKEY>
          model:
            provider: openai
            name: text-embedding-3-small
            options:
              upstream_url: https://api.openai.com/v1/embeddings
        vectordb:
          dimensions: 1536
          distance_metric: cosine
          strategy: redis
          threshold: 0.2
          redis:
            host: redis-stack.redis.svc.cluster.local
            port: 6379

该声明在“default” Konnect 控制平面中创建了以下 Kong 对象

  • Kong Gateway 服务“service1”。这是一个伪服务。实际上,接下来创建的 Kong AI 代理插件将决定实际的上游目标。
  • Kong 路由“route1”,路径为“/openai-route”。这是暴露 LLM 模型的路由。
  • AI 代理插件。它配置为使用 OpenAI 的“gpt-4”模型。“route_type”参数设置为“llm/v1/chat”,指向 OpenAI 的“https://api.openai.com/v1/chat/completions”端点。Kong 建议将 API 密钥存储在秘密管理器中作为秘密,例如 AWS Secrets Manager 或 HashiCorp Vault。当前配置(在声明中包含 OpenAI API 密钥)仅用于实验环境,不建议用于生产。请参考官方AI 代理插件文档页面以了解更多配置信息。
  • AI 语义缓存插件。它有两个设置。请查阅文档页面以了解更多关于这些和其他配置参数的信息。
    • embeddings: 使用了“text-embedding-3-small”嵌入模型,这与我们在 Python 脚本中使用的模型相同。
    • vectordb: 指向现有的 Redis 基础设施,用于存储嵌入并处理 VSS 请求。同样,它配置了与我们之前使用的相同的维度和距离度量。阈值将在 Kong Gateway 数据平面发送的 VSS 查询中被翻译为“半径”参数。

将 decK 声明提交到 Konnect 后,您应该可以使用 Konnect UI 查看新对象

请求 #1

配置好新的 Kong 对象后,Kong 数据平面将刷新并加载这些对象,然后我们就可以开始向其发送请求了。这是第一个请求,内容与我们在 Python 脚本中使用的相同

curl -i -X POST \
  --url $DATA_PLANE_LB/openai-route \
  --header 'Content-Type: application/json' \
   --data '{
   "messages": [
     {
       "role": "user",
       "content": "Who is Joseph Conrad?"
     }
   ]
 }'

您应该会收到如下所示的响应,这意味着网关已成功将请求路由到 OpenAI,后者向我们返回了实际消息。从语义缓存和相似度的角度来看,最重要的 header 是

  • X-Cache-Status: Miss:告诉我们网关未能在缓存中找到任何数据来满足请求。
  • X-Kong-Upstream-LatencyX-Kong-Proxy-Latency:显示延迟时间。
HTTP/1.1 200 OK
Content-Type: application/json
Connection: keep-alive
X-Cache-Status: Miss
x-ratelimit-limit-requests: 10000
CF-RAY: 8fce86cde915eae2-ORD
x-ratelimit-limit-tokens: 10000
x-ratelimit-remaining-requests: 9999
x-ratelimit-remaining-tokens: 9481
x-ratelimit-reset-requests: 8.64s
x-ratelimit-reset-tokens: 3.114s
access-control-expose-headers: X-Request-ID
x-request-id: req_29afd8838136a2f7793d6c129430b341
X-Content-Type-Options: nosniff
openai-organization: user-4qzstwunaw6d1dhwnga5bc5q
Date: Sat, 04 Jan 2025 22:05:00 GMT
alt-svc: h3=":443"; ma=86400
openai-processing-ms: 10002
openai-version: 2020-10-01
CF-Cache-Status: DYNAMIC
strict-transport-security: max-age=31536000; includeSubDomains; preload
Server: cloudflare
Content-Length: 1456
X-Kong-LLM-Model: openai/gpt-4
X-Kong-Upstream-Latency: 10097
X-Kong-Proxy-Latency: 471
Via: 1.1 kong/3.9.0.0-enterprise-edition
X-Kong-Request-Id: 36f6b41df3b74f78f586ae327af27075

{
  "id": "chatcmpl-Am6YEtvUquPHdHdcI59eZC3UfOUVz",
  "object": "chat.completion",
  "created": 1736028290,
  "model": "gpt-4-0613",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Joseph Conrad was a Polish-British writer regarded as one of the greatest novelists to write in the English language. He was born on December 3, 1857, and died on August 3, 1924. Though he did not speak English fluently until his twenties, he was a master prose stylist who brought a non-English sensibility into English literature.\n\nConrad wrote stories and novels, many with a nautical setting, that depict trials of the human spirit in the midst of what he saw as an impassive, inscrutable universe. His notable works include \"Heart of Darkness\", \"Lord Jim\", and \"Nostromo\". Conrad's writing often presents a deep, pessimistic view of the world and deals with the theme of the clash of cultures and moral ambiguity.",
        "refusal": null
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 163,
    "total_tokens": 175,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "audio_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  },
  "system_fingerprint": null
}

Redis 命令检查

Kong Gateway 创建了一个新索引。您可以使用 redis-cli ft._list. 来检查它。索引应命名如下:idx:vss_kong_semantic_cache:511efd84-117b-4c89-87cb-f92f9b74a6c0:openai-gpt-4

并且 redis-cli ft.search idx:vss_kong_semantic_cache:511efd84-117b-4c89-87cb-f92f9b74a6c0:openai-gpt-4 “*” return 1 – 应返回 OpenAI 响应的 id。例如

1

kong_semantic_cache:511efd84-117b-4c89-87cb-f92f9b74a6c0:openai-gpt-4:fcdf7d8995a227392f839b4530f8d8c3055748b96275fa9558523619172fd2a8

以下 json.get 命令应返回从 OpenAI 收到的实际响应

redis-cli json.get kong_semantic_cache:511efd84-117b-4c89-87cb-f92f9b74a6c0:openai-gpt-4:fcdf7d8995a227392f839b4530f8d8c3055748b96275fa9558523619172fd2a8 | jq ‘.payload.choices[].message.content’

更重要的是,redis-cli monitor 命令告诉我们插件为了实现缓存而发送给 Redis 的所有命令。主要的命令包括

  1. “FT.INFO”:检查索引是否存在。
  2. 没有索引,所以创建它。实际命令如下。注意该索引与我们在 Python 脚本中使用的索引类似。
"FT.CREATE" "idx:vss_kong_semantic_cache:511efd84-117b-4c89-87cb-f92f9b74a6c0:openai-gpt-4" "ON" "JSON" "PREFIX" "1" "kong_semantic_cache:511efd84-117b-4c89-87cb-f92f9b74a6c0:openai-gpt-4:" "SCORE" "1.0" "SCHEMA" "$.vector" "AS" "vector" "VECTOR" "FLAT" "6" "TYPE" "FLOAT32" "DIM" "1536" "DISTANCE_METRIC" "COSINE"

  1. 运行 VSS 以检查是否存在满足请求的键。命令如下所示。同样,注意该命令与我们在 Python 脚本中使用的命令相同。“range”参数反映了 decK 声明中使用的“threshold”配置。
"FT.SEARCH""idx:vss_kong_semantic_cache:511efd84-117b-4c89-87cb-f92f9b74a6c0:openai-gpt-4" "@vector:[VECTOR_RANGE $range $query_vector]=>{$YIELD_DISTANCE_AS: vector_score}" "SORTBY" "vector_score" "DIALECT" "2" "LIMIT" "0" "4" "PARAMS" "4" "query_vector" "\x981\x87<bE\xe4<b\xa3\..........\xbc" "range" "0.2"

  1. 由于索引刚刚创建,VSS 未能找到任何键,因此插件发送一个 “JSON.SET” 命令,添加一个包含从 OpenAI 接收的嵌入的新索引键。
  1. “expire” 命令:用于设置键的过期时间,默认为 300 秒。

您可以使用 Redis 控制面板检查新的索引键

请求 #2

如果我们发送另一个内容相似的请求,网关应该返回相同的响应,因为它会从缓存中获取,正如 X-Cache-Status: Hit header 中所指出的。此外,响应还包含与缓存相关的特定 header:X-Cache-Key 和 X-Cache-Ttl。

响应应该会更快返回,因为网关无需将请求路由到 OpenAI。

curl -i -X POST \
  --url $DATA_PLANE_LB/openai-route \
  --header 'Content-Type: application/json' \
   --data '{
   "messages": [
     {
       "role": "user",
       "content": "Tell me more about Joseph Conrad"
     }
   ]
 }'
HTTP/1.1 200 OK
Date: Sun, 05 Jan 2025 14:28:59 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
X-Cache-Status: Hit
Age: 0
X-Cache-Key: kong_semantic_cache:511efd84-117b-4c89-87cb-f92f9b74a6c0:openai-gpt-4:fcdf7d8995a227392f839b4530f8d8c3055748b96275fa9558523619172fd2a8
X-Cache-Ttl: 288
Content-Length: 1020
X-Kong-Response-Latency: 221
Server: kong/3.9.0.0-enterprise-edition
X-Kong-Request-Id: eef1373a3a688a68f088a52f72318315

{"object":"chat.completion","system_fingerprint":null,"id":"fcdf7d8995a22…….

如果您发送另一个内容不相似的请求,插件将创建一个新的索引键。例如

curl -i -X POST \
  --url $DATA_PLANE_LB/openai-route \
  --header 'Content-Type: application/json' \
   --data '{
   "messages": [
     {
       "role": "user",
       "content": "Living is easy with eyes closed"
     }
   ]
 }'

再次检查索引键,使用

redis-cli --scan
"kong_semantic_cache:511efd84-117b-4c89-87cb-f92f9b74a6c0:openai-gpt-4:fcdf7d8995a227392f839b4530f8d8c3055748b96275fa9558523619172fd2a8"
"kong_semantic_cache:511efd84-117b-4c89-87cb-f92f9b74a6c0:openai-gpt-4:22fbee1a1a45147167f29cc53183d0d2eef618c973e4284ad0179970209cf131"

高级 Kong AI 代理插件

除了缓存之外,Kong AI 网关还提供了多种基于语义的功能。其中一个强大的功能是语义路由。通过此功能,我们可以让网关决定处理给定请求的最佳模型。例如,您可能有在特定主题(如数学或古典音乐)上训练过的模型,因此根据呈现的内容路由请求会很有趣。通过分析请求的内容,插件可以将其匹配到已知在类似上下文中表现更好的最合适的模型。此功能增强了模型选择的灵活性和效率,尤其是在处理各种 AI 提供商和模型时。

事实上,语义路由是高级 AI 代理插件支持的负载均衡算法之一。其他支持的算法包括

  • 轮询
  • 基于权重
  • 最低使用率
  • 最低延迟
  • 一致性哈希(基于给定 header 值的粘性会话)

为了本文的目的,我们将探讨语义路由算法。

下图展示了高级 AI 代理插件的工作原理

  • 在配置时,插件会根据定义的描述向嵌入模型发送请求。返回的嵌入存储在 Redis 向量数据库中。
  • 在请求处理时,插件获取请求内容并将 VSS 查询发送到 Redis 向量数据库。根据相似度得分,插件将请求路由到网关背后表现最佳的目标 LLM 模型。

这是新的 decK 声明

_format_version: "3.0"
_info:
  select_tags:
  - semantic-routing
_konnect:
  control_plane_name: default
services:
- name: service1
  host: localhost
  port: 32000
  routes:
  - name: route1
    paths:
    - /openai-route
    plugins:
    - name: ai-proxy-advanced
      instance_name: ai-proxy-openai-route
      enabled: true
      config:
        balancer:
          algorithm: semantic
        embeddings:
          auth:
            header_name: Authorization
            header_value: Bearer <your_OPENAI_APIKEY>
          model:
            provider: openai
            name: text-embedding-3-small
            options:
              upstream_url: "https://api.openai.com/v1/embeddings"
        vectordb:
          dimensions: 1536
          distance_metric: cosine
          strategy: redis
          threshold: 0.8
          redis:
            host: redis-stack.redis.svc.cluster.local
            port: 6379
        targets:
        - model:
            provider: openai
            name: gpt-4
          route_type: "llm/v1/chat"
          auth:
            header_name: Authorization
            header_value: Bearer <your_OPENAI_APIKEY>
          description: "mathematics, algebra, calculus, trigonometry"
        - model:
            provider: openai
            name: gpt-4o-mini
          route_type: "llm/v1/chat"
          auth:
            header_name: Authorization
            header_value: Bearer <your_OPENAI_APIKEY>
          description: "piano, orchestra, liszt, classical music"

主要配置部分包括

  • balancer,其中 algorithm: semantic 表示负载均衡器将基于语义路由。
  • embeddings,包含访问嵌入模型所需的设置。之前关于 API 密钥的注意事项在此仍然适用。
  • vectordb,包含 Redis 主机和索引配置,以及用于驱动 VSS 查询的阈值。
  • targets: 每个目标都代表一个 LLM 模型。请注意,description 参数用于根据模型训练的主题配置负载均衡算法。

如您所见,为了方便起见,配置使用 OpenAI 的模型作为嵌入和目标。此外,仅用于本次探索,我们还将使用 OpenAI 的 gpt-4gpt-4o-mini 模型作为目标。

将 decK 声明提交到 Konnect 控制平面后,Redis 向量数据库应该会定义一个新的索引,并为每个目标创建一个键。然后我们就可以开始向网关发送请求了。前两个请求的内容与古典音乐相关,因此响应应该来自相关的模型 gpt-4o-mini-2024-07-18。

% curl -s -X POST \
  --url $DATA_PLANE_LB/openai-route \
  --header 'Content-Type: application/json' \
  --data '{
    "messages": [ 
      { 
        "role": "user", 
        "content": "Who wrote the Hungarian Rhapsodies piano pieces?"
      } 
    ] 
  }' | jq '.model' 
"gpt-4o-mini-2024-07-18"

% curl -s -X POST \
  --url $DATA_PLANE_LB/openai-route \
  --header 'Content-Type: application/json' \
  --data '{
    "messages": [
      {
        "role": "user",
        "content": "Tell me a contemporary pianist of Chopin"
      }
    ]
  }' | jq '.model'
"gpt-4o-mini-2024-07-18"

现在,下一个请求与数学相关,因此响应来自另一个模型,

"gpt-4-0613".

% curl -s -X POST \
  --url $DATA_PLANE_LB/openai-route \
  --header 'Content-Type: application/json' \
  --data '{
     "messages": [
       {
         "role": "user",
         "content": "Tell me about Fermat''s last theorem"
       }
     ]
   }' | jq '.model'
"gpt-4-0613"

结论

历史上,Kong 一直支持 Redis 来实现各种关键策略和用例。最近由 Kong AI 网关实现的合作,侧重于语义处理,其中 Redis 的向量相似性搜索能力发挥着重要作用。

这篇博文探讨了两个主要的基于语义的用例:语义缓存和语义路由。请查阅 Kong 和 Redis 的文档页面,以了解更多关于使用这两种技术可以实现的广泛的 API 和 AI 网关用例。