Higress AI Cache 插件深度指南:基于向量检索与字符串匹配的 LLM 结果缓存实践
【免费下载链接】higress🤖 AI Gateway | AI Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/hi/higress
AI Cache 是 Higress 内置的 LLM 结果缓存 Wasm 插件,默认配置即可直接用于 OpenAI 协议的流式与非流式响应缓存,支持基于向量数据库的语义缓存和基于缓存服务的字符串匹配缓存两种模式。本文以插件官方配置参考为基础,结合仓库源码(plugins/wasm-go/extensions/ai-cache/目录)深入解析其配置参数、provider 生态、缓存键设计原理与完整实战配置,帮助你在 AI 网关场景下显著降低 LLM 调用成本、提升响应速度。
功能概述与核心特性
AI Cache 插件的核心职责是对 LLM 的请求/响应做缓存:当用户提问与历史问题相同或语义相近时,直接返回缓存中的答案,避免重复调用大模型服务,从而节省 token 成本并降低响应延迟。
- 双缓存模式:既支持向量数据库(vector)驱动的语义缓存(相似问题命中),也支持缓存服务(cache)驱动的字符串精确匹配缓存;
- 流式与非流式全兼容:对 OpenAI 协议的非流式 JSON 响应与 SSE 流式响应均能提取答案并重建缓存命中响应;
- 可跳过机制:携带请求头
x-higress-skip-ai-cache: on时,当前请求将不使用缓存内容、直接转发给后端服务,同时该请求的响应内容也不会被写入缓存。该请求头常量在 main.go 中定义为SKIP_CACHE_HEADER,并在请求头阶段(onHttpRequestHeaders)被检测后直接跳过请求体读取与缓存流程。
运行属性
- 插件执行阶段:认证阶段(Authentication Phase);
- 插件执行优先级:
10。
配置总体结构
插件的配置分为 3 大部分:向量数据库服务(vector)、文本嵌入服务(embedding)、缓存服务(cache),此外还提供针对 LLM 请求/响应字段提取的细粒度参数配置。从源码看,配置在 config/config.go 的FromJson中被逐项解析,并在Validate(配置合法性校验)与Complete(按类型实例化各 provider)中完成加载,最终由 main.go 的parseConfig串起FromJson → Validate → Complete完整初始化链路。
插件支持"向量数据库语义缓存"与"字符串匹配缓存"两种缓存方法:若同时配置了向量库与缓存库,优先使用缓存库(精确匹配),缓存未命中时再使用向量库(语义检索)能力。
注意:向量数据库(vector)与缓存数据库(cache)不能同时为空,否则插件无法提供缓存服务。从源码校验看,Validate 要求 vector、embedding、cache 三者的 provider 类型不能全部为空。
顶层配置参数
| 名称 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| vector | object | 否 | - | 向量存储服务配置,见下文"向量数据库服务" |
| embedding | object | 否 | - | 文本嵌入服务配置,见下文"文本嵌入服务" |
| cache | object | 否 | - | 缓存服务配置,见下文"缓存服务" |
| cacheKeyStrategy | string | 否 | "lastQuestion" | 缓存键生成策略。可选值:"lastQuestion"(使用最后一个问题)、"allQuestions"(拼接所有问题)、"disabled"(禁用缓存)。策略常量定义于 config.go,非法值会在Validate阶段直接报错 |
| enableSemanticCache | bool | 否 | false | 是否启用语义缓存。禁用时使用字符串匹配查找缓存,需配置缓存服务。配置了向量 provider 时自动启用。源码逻辑:显式配置时以配置值为准,否则在有向量 provider 时默认置为 true(见 config.go) |
三种组件组合模式
根据是否需要语义缓存,可以按以下方式组合组件:
cache:仅启用字符串匹配缓存。适合问题必须逐字一致才命中缓存的场景;vector (+ embedding):仅启用语义缓存。若vector未提供字符串检索服务(即未实现 StringQuerier 接口),则需要单独配置embedding服务将问题文本转成向量后再检索;vector (+ embedding) + cache:同时启用语义缓存,并借助缓存服务存储 LLM 响应以加速命中。
若某组件未配置,可忽略该组件下的required字段。缓存键的查找决策逻辑集中在 core.go 的CheckCacheForKey与handleCacheResponse中:先查缓存服务,未命中且启用了语义缓存时,再根据向量 provider 实现的是StringQuerier还是EmbeddingQuerier接口选择对应的检索路径(见 core.go)。
向量数据库服务(vector)
| 名称 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| vector.type | string | 是 | - | 向量存储服务提供者类型,例如 dashvector、chroma、elasticsearch、weaviate、pinecone、qdrant、milvus。支持类型注册表见 vector/provider.go |
| vector.serviceName | string | 是 | - | 向量存储服务名称(通常为 K8s 集群内的 DNS 服务名,需携带.dns等类型后缀) |
| vector.serviceHost | string | 否 | - | 向量存储服务域名。部分 provider 必填(如 dashvector、pinecone) |
| vector.servicePort | int64 | 否 | 443 | 向量存储服务端口 |
| vector.apiKey | string | 否 | - | 向量存储服务 API Key |
| vector.topK | int | 否 | 1 | 返回 TopK 结果数 |
| vector.timeout | uint32 | 否 | 10000 | 请求向量存储服务的超时时间(毫秒),默认 10000(10 秒) |
| vector.collectionID | string | 否 | - | 向量存储服务 Collection ID/名称 |
| vector.threshold | float64 | 否 | 1000 | 向量相似度度量阈值 |
| vector.thresholdRelation | string | 否 | "lt" | 相似度度量比较方式。相似度度量方法有Cosine、DotProduct、Euclidean等:前两者值越大相似度越高,后者值越小相似度越高,因此Cosine与DotProduct应使用gt,Euclidean应使用lt。可选值:lt(小于)、lte(小于等于)、gt(大于)、gte(大于等于)。非法值会在Validate阶段被拒绝(见 vector/provider.go) |
| vector.esUsername | string | 否 | - | ElasticSearch 用户名,仅 elasticsearch 类型使用 |
| vector.esPassword | string | 否 | - | ElasticSearch 密码,仅 elasticsearch 类型使用 |
从源码可以确认上述默认值的实现:servicePort缺省为 443、topK缺省为 1、timeout缺省为 10000、threshold缺省为 1000、thresholdRelation缺省为 "lt",均位于 vector/provider.go 的FromJson解析逻辑中。
各向量数据库 provider 专项配置
Chroma
设置vector.type为chroma,无专项配置字段。需提前创建 Collection,并将 Collection ID 填入vector.collectionID。Collection ID 示例:52bbb8b3-724c-477b-a4ce-d5b578214612。
DashVector
设置vector.type为dashvector,无专项配置字段。需提前创建 Collection,并将Collection Name填入vector.collectionID。从 vector/dashvector.go 的校验逻辑看,DashVector 要求apiKey、collectionID、serviceName、serviceHost四项均必填。
ElasticSearch
设置vector.type为elasticsearch。需提前创建 Index,并将 Index Name 填入vector.collectionID。
目前依赖 ES 的KNN检索能力,请确保 ES 版本支持KNN(已在8.16版本上测试通过)。
专项配置字段:
| 名称 | 数据类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
vector.esUsername | string | 否 | - | ElasticSearch 用户名 |
vector.esPassword | string | 否 | - | ElasticSearch 密码 |
vector.esUsername与vector.esPassword用于 Basic 认证;同时支持 API Key 认证——当填写vector.apiKey时启用 API Key 认证,SaaS 版本需填入encoded值。
Milvus
设置vector.type为milvus,无专项配置字段。需提前创建 Collection,并将 Collection Name 填入vector.collectionID。
Pinecone
设置vector.type为pinecone,无专项配置字段。需提前创建 Index,并将 Index 访问域名填入vector.serviceHost。从 vector/pinecone.go 的校验逻辑看,Pinecone 要求serviceHost、serviceName、apiKey三项均必填。
Pinecone 的Namespace参数通过插件的vector.collectionID配置;若vector.collectionID未填写,则默认使用 Default Namespace。从实现看,Pinecone 的向量元数据(pineconeMetadata)包含question与answer两个字段(见 vector/pinecone.go),这为"向量命中时直接返回存储的答案"提供了支撑。
Qdrant
设置vector.type为qdrant,无专项配置字段。需提前创建 Collection,并将 Collection Name 填入vector.collectionID。
Weaviate
设置vector.type为weaviate,无专项配置字段。需提前创建 Collection,并将 Collection Name 填入vector.collectionID。
注意:Weaviate 会自动将首字母大写,因此填写collectionID时首字母应大写。若使用 SaaS 版本,需填写vector.serviceHost参数。
文本嵌入服务(embedding)
| 名称 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| embedding.type | string | 是 | - | 文本嵌入服务类型,例如 dashscope、openai、azure、cohere、ollama、huggingface、textin、xfyun。支持类型注册表见 embedding/provider.go |
| embedding.serviceName | string | 是 | - | 文本嵌入服务名称 |
| embedding.serviceHost | string | 否 | - | 文本嵌入服务域名 |
| embedding.servicePort | int64 | 否 | 443 | 文本嵌入服务端口。默认值随 provider 不同而不同,ollama 默认 11434 |
| embedding.timeout | uint32 | 否 | 10000 | 请求文本嵌入服务的超时时间(毫秒),默认 10000(10 秒) |
| embedding.model | string | 否 | - | 文本嵌入服务使用的模型名称 |
| embedding.apiKey | string | 否 | - | 文本嵌入服务的 API Key |
各 embedding provider 的servicePort/serviceHost等默认值在各自的CreateProvider中兜底:例如 DashScope 默认端口 443、默认域名dashscope.aliyuncs.com(见 embedding/dashscope.go)。
各嵌入 provider 专项配置
Azure OpenAI
设置embedding.type为azure。使用前需先创建 Azure OpenAI 账号,再在 Azure AI Foundry 中选择并部署模型;在已部署模型的 endpoint 区域可查看目标 URI 与 key——将 URI 中的 host 填入embedding.serviceHost,将 key 填入embedding.apiKey。
一个完整的 URI 示例为https://YOUR_RESOURCE_NAME.openai.azure.com/openai/deployments/YOUR_DEPLOYMENT_NAME/embeddings?api-version=2024-10-21,你需要将YOUR_RESOURCE_NAME.openai.azure.com填入embedding.serviceHost。
专项配置字段:
| 名称 | 数据类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
embedding.apiVersion | string | 是 | - | API 版本,取自 URI 中的 api-version 值 |
注意:必须指定embedding.serviceHost(如YOUR_RESOURCE_NAME.openai.azure.com)。默认模型为text-embedding-ada-002,如需使用其他模型,在embedding.model中指定。
Cohere
设置embedding.type为cohere,无专项配置字段。需自行创建 API Key 并填入embedding.apiKey。
OpenAI
设置embedding.type为openai,无专项配置字段。需创建 API Key 并填入embedding.apiKey,例如sk-xxxxxxx。
Ollama
设置embedding.type为ollama,无专项配置字段。注意其默认端口为 11434。
Hugging Face
设置embedding.type为huggingface,无专项配置字段。需创建hf_token并填入embedding.apiKey,示例:hf_xxxxxxx。
embedding.model默认值为sentence-transformers/all-MiniLM-L6-v2。
DashScope
设置embedding.type为dashscope。需创建 API Key 并填入embedding.apiKey(该字段在 embedding/dashscope.go 中为必填校验项)。
embedding.model默认值为text-embedding-v2,也可使用text-embedding-v1等其他模型。实现层面,DashScope 请求固定走/api/v1/services/embeddings/text-embedding/text-embedding端点、text_type固定为query,并以Bearer方式携带 apiKey(见 embedding/dashscope.go)。
TextIn
设置embedding.type为textin。使用前需先获取app-id与secret-code。
专项配置字段:
| 名称 | 数据类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
embedding.textinAppId | string | 是 | - | 应用 ID,取自获取到的 app-id |
embedding.textinSecretCode | string | 是 | - | 调用 API 的密钥,取自获取到的 secret-code |
embedding.textinMatryoshkaDim | int | 是 | - | 返回单个向量的维度 |
Xfyun(讯飞星火)
设置embedding.type为xfyun。使用前需先创建应用获取APPID、APISecret与APIKey,并将APIKey填入embedding.apiKey。
专项配置字段:
| 名称 | 数据类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
embedding.appId | string | 是 | - | 应用 ID,取自获取到的 APPID |
embedding.apiSecret | string | 是 | - | 调用 API 的密钥,取自获取到的 APISecret |
缓存服务(cache)
| 名称 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| cache.type | string | 是 | - | 缓存服务类型,例如 redis |
| cache.serviceName | string | 是 | - | 缓存服务名称 |
| cache.serviceHost | string | 否 | - | 缓存服务域名 |
| cache.servicePort | int | 否 | 6379 | 缓存服务端口。若 serviceName 以.static结尾,默认端口为 80 |
| cache.username | string | 否 | - | 缓存服务用户名 |
| cache.password | string | 否 | - | 缓存服务密码 |
| cache.timeout | uint32 | 否 | 10000 | 缓存服务超时时间(毫秒),默认 10000(10 秒) |
| cache.cacheTTL | int | 否 | 0 | 缓存过期时间(秒),默认 0(永不过期) |
| cache.cacheKeyPrefix | string | 否 | "higress-ai-cache:" | 缓存 Key 前缀 |
| cache.database | int | 否 | 0 | 使用的数据库 ID,仅 Redis 使用。例如配置为 1 表示SELECT 1 |
以上默认值均在 cache/provider.go 的FromJson中落实;其中.static后缀判定逻辑为:未显式配置servicePort时,若serviceName以.static结尾则默认 80,否则默认 6379。
Redis 实现细节(cache/redis.go):cacheTTL为 0 时使用 Redis 的Set(不设过期),大于 0 时使用SetEx(按秒过期);插件通过wrapper.NewRedisClusterClient创建 Redis 集群客户端,并以serviceName作为 FQDN、serviceHost作为 Host 解析。
其他配置(响应提取与模板)
| 名称 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| cacheKeyFrom | string | 否 | "messages.@reverse.0.content" | 使用 GJSON PATH 语法从请求 Body 中提取字符串作为缓存键 |
| cacheValueFrom | string | 否 | "choices.0.message.content" | 使用 GJSON PATH 语法从响应 Body 中提取缓存值 |
| cacheStreamValueFrom | string | 否 | "choices.0.delta.content" | 使用 GJSON PATH 语法从流式响应 Body 中提取缓存值 |
| cacheToolCallsFrom | string | 否 | "choices.0.delta.content.tool_calls" | 使用 GJSON PATH 语法从流式响应 Body 中提取 tool_calls |
| responseTemplate | string | 否 | {"id":"from-cache","choices":[{"index":0,"message":{"role":"assistant","content":"%s"},"finish_reason":"stop"}],"model":"from-cache","object":"chat.completion","usage":{"prompt_tokens":0,"completion_tokens":0,"total_tokens":0}} | 返回 HTTP 响应的模板,%s标记待被缓存值替换的部分 |
| streamResponseTemplate | string | 否 | data:{"id":"from-cache","choices":[{"index":0,"delta":{"role":"assistant","content":"%s"},"finish_reason":"stop"}],"model":"from-cache","object":"chat.completion","usage":{"prompt_tokens":0,"completion_tokens":0,"total_tokens":0}}\n\ndata:[DONE]\n\n | 返回流式 HTTP 响应的模板,%s标记待被缓存值替换的部分 |
这些默认值与模板字符串在 config/config.go 中完整实现。缓存命中后的响应重建在 core.go 的processCacheHit中完成:流式命中返回text/event-stream; charset=utf-8,非流式命中返回application/json; charset=utf-8,状态码均为 200,并以fmt.Sprintf将缓存内容(经strconv.Quote转义后)填入模板的%s占位符;同时通过用户属性cache_status=hit输出到 AI 日志(AILogKey)中,便于可观测性分析。
旧版本配置兼容
从源码(config/config.go 与 cache/provider.go)可以确认,插件对旧版配置做了兼容处理:
- 顶层
redis字段会被转换为cache配置(ConvertLegacyJson); - 旧字段
cacheKeyFrom.requestBody、cacheValueFrom.requestBody、cacheStreamValueFrom.requestBody、returnResponseTemplate、returnStreamResponseTemplate会被自动映射为新字段cacheKeyFrom、cacheValueFrom、cacheStreamValueFrom、responseTemplate、streamResponseTemplate。
配置示例
基础配置(DashScope 嵌入 + DashVector 向量库 + Redis 缓存)
embedding: type: dashscope serviceName: my_dashscope.dns apiKey: [Your Key] vector: type: dashvector serviceName: my_dashvector.dns collectionID: [Your Collection ID] serviceHost: [Your domain] apiKey: [Your key] cache: type: redis serviceName: my_redis.dns servicePort: 6379 timeout: 100该组合对应上文"组合模式 3":先查 Redis 精确匹配,未命中时由 DashScope 将问题文本转为向量、再在 DashVector 中做语义检索。仓库测试用例 main_test.go 中的completeConfig即采用与此一致的 Redis + DashScope + DashVector 完整配置,可作为校验参考。
若仅需字符串匹配缓存(组合模式 1),可只保留cache段,参考 main_test.go 中的basicRedisConfig:
cache: type: redis serviceName: redis.static servicePort: 6379 timeout: 10000 cacheTTL: 3600 cacheKeyPrefix: "higress-ai-cache:" cacheKeyStrategy: lastQuestion缓存键生成策略(cacheKeyStrategy)与 GJSON PATH 高级用法
插件默认通过 GJSON PATH 表达式messages.@reverse.0.content提取缓存键,即反转 messages 数组后取第一项的 content(也就是用户最近一次提问)。这一默认表达式在 config/config.go 中设置。
三种cacheKeyStrategy的底层行为(见 main.go):
- lastQuestion(默认):按
cacheKeyFrom的 GJSON PATH 提取单条内容作为缓存键; - allQuestions:遍历请求体中的
messages数组,将其中所有role == "user"的消息 content 以换行符拼接作为缓存键; - disabled:跳过缓存键生成,直接放行请求且不读取响应体(不写入缓存)。
GJSON PATH 支持条件语法,以下均为官方支持的写法示例:
- 取最后一条
role为user的消息内容作为键:messages.@reverse.#(role=="user").content - 将所有
role为user的消息内容拼接成数组作为键:messages.@reverse.#(role=="user")#.content - 使用管道语法取倒数第二条
role为user的消息内容作为键:messages.@reverse.#(role=="user")#.content|1
更多用法可参考 GJSON 官方 SYNTAX 文档,并可使用 GJSON Playground 在线工具进行语法测试与验证。
缓存读写全流程(源码视角)
结合 main.go、core.go 与 util.go,缓存流程可归纳为:
- 请求阶段:
onHttpRequestHeaders检测跳过头x-higress-skip-ai-cache: on与content-type(非 JSON 直接放行),并停止请求头迭代等待读取请求体;onHttpRequestBody按策略生成缓存键,调用CheckCacheForKey查缓存/相似检索,命中则暂停请求并直接回包; - 命中回包:非流式命中走
responseTemplate(JSON),流式命中走streamResponseTemplate(SSE),同时记录用户属性cache_status=hit; - 未命中转发:
proxywasm.ResumeHttpRequest()恢复请求,继续转发给后端 LLM; - 响应写缓存:
onHttpResponseBody对非流式响应按cacheValueFrom提取完整内容;对流式响应则按 SSE 分块(\n\n分隔)累积 partial message,拼接data:事件中的cacheStreamValueFrom字段,遇[DONE]结束;若响应中出现 tool_calls 或解析异常,则跳过缓存写入(见 util.go)。最终通过cacheResponse写入缓存服务,并通过uploadEmbeddingAndAnswer将"问题-答案"的嵌入写入向量库,供后续语义命中直接返回(core.go)。
值得注意的细节:当向量检索命中的记录本身带有Answer字段时,插件会直接返回该答案并同步写入缓存服务(见 core.go),实现"一次 LLM 调用、多次语义命中复用"。
常见问题(FAQ)
- 返回错误
error status returned by host: bad argument:请检查serviceName是否正确包含服务类型后缀(如.dns等)。
小结
AI Cache 插件通过"缓存服务精确匹配 + 向量库语义检索"的双层设计,为 AI 网关提供了开箱即用的 LLM 结果缓存能力;配置上以 vector/embedding/cache 三大组件为核心,配合 GJSON PATH 实现灵活的缓存键与缓存值提取,并可针对 OpenAI、Azure OpenAI、DashScope、Cohere、Ollama、Hugging Face、TextIn、讯飞星火等主流嵌入服务以及 DashVector、Chroma、ElasticSearch、Milvus、Pinecone、Qdrant、Weaviate 等主流向量库即插即用。需要进一步深入实现细节的读者,可继续阅读 核心逻辑 core.go、配置解析 config.go 及 完整测试 main_test.go。
【免费下载链接】higress🤖 AI Gateway | AI Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/hi/higress
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考