跳到主要内容

ai-cache

ai-cache 插件会缓存 LLM 服务的响应,使重复请求直接从缓存返回,而不再次调用上游模型。这可以降低重复提示词的响应延迟,并减少上游 Token 用量。

该插件支持精确匹配缓存:仅当规范化后的请求与此前缓存的某个请求完全相同时,才会复用其响应。它还可以启用语义缓存层,在精确匹配未命中后通过 Redis Search 比较提示词向量嵌入。

按请求格式处理​

插件会将检测到的不同请求格式保存在不同的缓存条目中。

网关会先检查 URI 特定规则,再检查仅基于请求体的规则,以识别请求格式:

  • Bedrock Converse 要求 URI 以 /converse 结尾且包含 messages 数组。
  • Anthropic Messages 要求 URI 以 /v1/messages 结尾。
  • Responses API 要求 URI 以 /v1/responses 结尾且包含 input 字段。
  • Chat Completions 使用 messages 数组。
  • 当前面的规则均不匹配时,Embeddings 使用 input。
  • 当前面的规则均不匹配时,其他非空 JSON 对象使用透传格式。
请求格式精确匹配缓存语义缓存
Bedrock Converse支持绕过
Anthropic Messages支持绕过
Responses API支持绕过
Chat Completions支持支持
Embeddings支持绕过
其他 JSON(透传)支持绕过

精确匹配缓存自 API7 企业版 3.9.16、3.10.2 以及 APISIX 3.18.0 起可用。

语义缓存和流式响应缓存自 API7 企业版 3.9.16、3.10.3 以及 APISIX 3.18.0 起可用。

工作原理​

ai-cache 插件必须与同一路由上的 ai-proxy 或 ai-proxy-multi 插件一起使用,因为它缓存的是这些插件所代理的 LLM 流量。

对于每个请求,插件会根据检测到的请求格式、请求体以及所选 AI 实例的配置计算缓存键。缓存键的作用域由 cache_key 配置。精确缓存条目存储在 Redis 中,并具有可配置的存活时间。

对于 passthrough 协议,缓存键还包含请求方法、URI 和查询字符串。请求方法会反映 proxy-rewrite 等前置插件所做的更改。

该隔离机制引入于 API7 企业版 3.9.20 和 3.10.7。APISIX 自 3.19.0 起支持该机制。

请求体相同但请求方法、URI 或查询参数值不同的请求会使用不同的精确缓存键。其他协议保持现有的缓存键结构。

对于 Chat Completions 请求,语义缓存会在精确缓存未命中后运行。插件对配置的提示词窗口生成向量嵌入,并在 Redis Search 向量索引中查询足够相似的缓存响应。

插件会将 X-AI-Cache-Status 响应头设置为以下值之一:

  • HIT:找到有效的缓存响应并直接返回,不调用上游。X-AI-Cache-Age 头会以秒为单位报告该缓存条目的存活时长。语义命中还会返回 X-AI-Cache-Similarity。
  • MISS:未找到缓存响应。请求被代理到上游,且大小在 max_cache_body_size 以内的成功响应(HTTP 200)会被缓存以供后续请求使用。
  • BYPASS:此请求跳过缓存,例如因为它匹配了某条 bypass_on 规则、未选中任何 AI 实例,或响应无法安全捕获。

完整的 SSE 流式响应可以被缓存,并使用其流式内容类型重放。只有当插件收到客户端协议的终止事件后,流式响应才会被缓存,例如 OpenAI Chat Completions 的 [DONE]、Anthropic Messages 的 message_stop,或 OpenAI Responses API 的 response.completed。被中断或因限制而截断的流式响应不会被缓存。流式请求和非流式请求使用不同的缓存条目;缓存的流式响应会立即重放,而不会保留原始 Token 的时间间隔。使用其他帧格式的流(例如 Bedrock ConverseStream 的 AWS 事件流格式)会绕过缓存。

警告

缓存的提示词和响应可能包含敏感数据。请限制对 Redis 的访问,选择合适的缓存存活时间;如果缓存响应不应在不同消费者或请求上下文之间共享,请使用 cache_key.include_consumer 或 cache_key.include_vars。

示例​

以下示例使用 OpenAI 作为上游 LLM 服务,并使用 Redis 存储缓存响应。在开始之前,请创建一个 OpenAI 账号 和 API Key。将 API Key 保存到环境变量:

export OPENAI_API_KEY=replace-with-openai-api-key

如果你使用其他 LLM 服务提供方,请参考该提供方的文档获取 API Key。

ADC 服务要求配置常规服务上游,因此 ADC 示例中包含该上游。匹配的 LLM 请求由 ai-proxy 插件处理。

启动 Redis​

Redis 8 包含 Redis Search,因此同一台服务器即可支持精确缓存和语义缓存示例。请选择与网关部署相匹配的环境。

将 GATEWAY_CONTAINER 设置为正在运行的 APISIX 或 API7 网关容器。创建专用网络,并将网关连接到该网络:

export GATEWAY_CONTAINER=replace-with-gateway-container-name

docker network create gateway-redis-net
docker network connect gateway-redis-net "$GATEWAY_CONTAINER"

使用示例密码,在共享网络中启动 Redis:

docker run -d \
--name redis-server \
--network gateway-redis-net \
redis:8.10.2-alpine \
redis-server --requirepass redis-password

验证 Redis 是否接受经过身份认证的连接:

docker exec -e REDISCLI_AUTH=redis-password redis-server redis-cli ping

该命令应返回 PONG。

设置 Admin API 和 ADC 示例使用的 Redis 主机名:

export REDIS_HOST=redis-server

这些评估用配置使用教程密码和内部服务地址。对于生产部署,请限制网络访问、使用 Secret 管理凭据,并在 Redis 服务支持 TLS 时配置 redis_ssl 和 redis_ssl_verify。

缓存 LLM 响应​

以下示例演示如何将 ai-cache 与 ai-proxy 配合配置,使重复的相同请求由 Redis 提供响应。

创建一个使用 ai-proxy 代理到 OpenAI、并使用 ai-cache 缓存响应的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d @- <<EOF
{
"id": "ai-cache-route",
"uri": "/anything",
"methods": ["POST"],
"plugins": {
"ai-proxy": {
"provider": "openai",
"auth": {
"header": {
"Authorization": "Bearer $OPENAI_API_KEY"
}
},
"options": {
"model": "gpt-4o-mini"
}
},
"ai-cache": {
"redis_host": "$REDIS_HOST",
"redis_port": 6379,
"redis_password": "redis-password",
"exact": {
"ttl": 3600
}
}
}
}
EOF

❶ 在 Authorization 请求头中以 Bearer Token 形式附带 OpenAI API Key。

❷ 指定模型名称。

❸ 将缓存指向你的 Redis 实例。

❹ 将每个响应缓存一小时。

向路由发送请求:

curl -i "http://127.0.0.1:9080/anything" -X POST \
-H "Content-Type: application/json" \
-d '{
"messages": [
{ "role": "user", "content": "What is 1+1?" }
]
}'

第一个请求是缓存未命中,会被代理到 OpenAI。你应该收到 HTTP/1.1 200 OK 响应,其中包含以下响应头:

X-AI-Cache-Status: MISS

再次发送相同的请求。这一次响应直接从缓存返回,不会调用上游,并包含缓存状态和存活时长响应头:

X-AI-Cache-Status: HIT
X-AI-Cache-Age: 2

缓存语义相似的提示词​

语义缓存会在精确缓存未命中后发起向量嵌入请求,并使用 Redis Search 查找足够相似的已缓存提示词。以下示例同时使用 OpenAI 处理 LLM 请求和向量嵌入请求。

语义缓存需要 Redis Search。上一节启动的 Redis 8 服务器已包含此功能。

创建一个同时启用两种缓存层并配置向量嵌入服务的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d @- <<EOF
{
"id": "ai-cache-semantic-route",
"uri": "/anything",
"methods": ["POST"],
"plugins": {
"ai-proxy": {
"provider": "openai",
"auth": {
"header": {
"Authorization": "Bearer $OPENAI_API_KEY"
}
},
"options": {
"model": "gpt-4o-mini"
}
},
"ai-cache": {
"redis_host": "$REDIS_HOST",
"redis_password": "redis-password",
"layers": ["exact", "semantic"],
"semantic": {
"similarity_threshold": 0.92,
"embedding": {
"openai": {
"model": "text-embedding-3-small",
"api_key": "$OPENAI_API_KEY"
}
},
"vector_search": {
"redis": {
"index": "ai-cache"
}
}
}
}
}
}
EOF

发送初始请求:

curl -i "http://127.0.0.1:9080/anything" -X POST \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "What is Apache APISIX?"
}
]
}'

第一个请求的精确缓存和语义缓存均未命中。插件会将请求代理到 LLM、对提示词生成向量嵌入,并存储两种缓存条目。你应该会收到包含以下响应头的 HTTP/1.1 200 OK 响应:

X-AI-Cache-Status: MISS

使用不同表述发送一个语义相似的请求:

curl -i "http://127.0.0.1:9080/anything" -X POST \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Can you explain what Apache APISIX is?"
}
]
}'

如果相似度分数达到所配置的阈值,请求的精确缓存会未命中,但会由语义缓存提供响应。你应该会收到包含类似以下响应头的 HTTP/1.1 200 OK 响应:

X-AI-Cache-Status: HIT
X-AI-Cache-Age: 12
X-AI-Cache-Similarity: 0.9487

相似度值取决于向量嵌入模型和提示词。如果请求未命中,请先比较应共享响应和不应共享响应的提示词分数,再降低阈值。