跳到主要内容

语义缓存

概述

语义缓存是 LLM Gateway 的高级功能,通过语义相似度匹配来缓存和复用 LLM 响应,显著降低 API 调用成本和响应延迟。与传统的精确匹配缓存不同,语义缓存能够识别语义相似的查询,即使文字表述不同也能命中缓存。

💰
降低成本
缓存命中的请求无需调用 LLM API,可节省高达 80% 的 API 费用
提升速度
缓存响应延迟 < 100ms,比 LLM API 调用快 10-50 倍
🧠
语义匹配
基于向量相似度,识别语义相同但表述不同的查询
⚙️
灵活配置
可配置相似度阈值、缓存时长、嵌入模型等参数

工作原理

核心概念

语义缓存 使用向量嵌入(Embeddings)技术将文本转换为高维向量,然后通过向量相似度比较来判断查询是否语义相似。

1

向量化

用户查询被转换为向量表示:

查询: "什么是人工智能?"
向量: [0.123, -0.456, 0.789, ..., 0.234] (1536 维)
2

相似度搜索

在向量数据库中搜索最相似的历史查询:

历史查询: "AI 是什么?"
相似度: 0.95 (余弦相似度)
3

阈值判断

如果相似度超过配置的阈值(如 0.90),判定为缓存命中:

相似度 0.95 >= 阈值 0.90 → 缓存命中
4

返回缓存

直接返回历史查询的缓存响应,无需调用 LLM API。

语义缓存工作流程图

技术架构

请求

语义缓存中间件

提取查询内容

嵌入模型转换 → 查询向量

Redis Stack 向量搜索

找到相似查询? ──┐
│ │
是 否
↓ ↓
返回缓存 调用 LLM API
↑ ↓
│ 缓存响应+向量
│ ↓
└──────────────┘
返回响应

关键组件

语义缓存中间件

位置: middleware/semantic_cache.go

功能:

  • 拦截 LLM 请求
  • 提取查询内容
  • 调用嵌入模型
  • 执行向量相似度搜索
  • 管理缓存生命周期

性能:

  • 缓存命中延迟: < 100ms
  • 未命中额外开销: 50-200ms(嵌入生成时间)
Redis Stack

作用: 向量数据库,存储查询向量和响应

特性:

  • 支持向量相似度搜索(Redis Vector Search)
  • 高性能内存存储
  • 支持 TTL 自动过期
  • 支持集群部署

要求:

  • 必须使用 Redis Stack,不能使用普通 Redis
  • 版本要求: >= 7.2
嵌入模型

作用: 将文本转换为向量

支持的模型:

  • OpenAI text-embedding-ada-002 (1536 维)
  • OpenAI text-embedding-3-small (1536 维)
  • OpenAI text-embedding-3-large (3072 维)
  • 其他兼容 OpenAI API 的嵌入模型

配置:

  • 嵌入模型可以独立配置
  • 支持使用不同的 API 密钥
  • 可以使用本地嵌入服务
缓存策略

缓存键生成:

cache_key = hash(model + messages + parameters)

缓存内容:

  • 查询向量
  • 完整响应内容
  • 元数据(时间戳、模型、token 使用等)

TTL 配置:

  • 默认: 24 小时
  • 可配置范围: 1 分钟 - 30 天
  • 支持永久缓存(TTL = -1)

配置指南

启用语义缓存

1

安装 Redis Stack

语义缓存需要 Redis Stack 支持,不能使用普通 Redis:

docker run -d \
--name redis-stack \
-p 6379:6379 \
-p 8001:8001 \
redis/redis-stack:latest
2

配置 Redis 连接

.env 文件中配置 Redis 连接字符串:

REDIS_CONN_STRING=redis://localhost:6379
# 或带密码的连接
REDIS_CONN_STRING=redis://:password@localhost:6379
# 或使用 Redis Sentinel
REDIS_CONN_STRING=redis-sentinel://sentinel1:26379,sentinel2:26379/mymaster
3

创建语义缓存配置

登录管理后台,导航到 系统设置 > 语义缓存:

语义缓存配置页面
4

配置嵌入模型

设置用于生成向量的嵌入模型:

  • 嵌入模型: 选择嵌入模型(推荐 text-embedding-3-small)
  • API 密钥: 填写 OpenAI API 密钥
  • API 端点: 自定义端点(可选)
嵌入模型配置
5

设置缓存参数

配置缓存行为:

  • 相似度阈值: 0.85 - 0.95(推荐 0.90)
  • 缓存 TTL: 3600 - 86400 秒(推荐 24 小时)
  • 最大缓存条目: 10000 - 100000(根据内存调整)
  • 向量维度: 1536(text-embedding-3-small)
缓存参数配置
6

启用缓存

勾选"启用语义缓存"选项,点击"保存"。

信息

启用后立即生效,无需重启服务。

7

验证配置

使用测试工具验证语义缓存是否正常工作:

# 第一次请求(缓存未命中)
curl -X POST https://your-gateway.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "什么是机器学习?"}]
}'

# 第二次请求(语义相似,应该命中缓存)
curl -X POST https://your-gateway.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "机器学习是什么?"}]
}'

查看响应头,应该包含:

X-Cache-Status: HIT
X-Cache-Similarity: 0.92

配置参数详解

相似度阈值 (Similarity Threshold)

说明: 判定缓存命中的最低相似度,范围 0-1

推荐值:

  • 严格匹配: 0.95 - 0.98
  • 平衡模式: 0.88 - 0.93(推荐 0.90)
  • 宽松匹配: 0.80 - 0.87

影响:

  • 阈值越高: 命中率越低,准确性越高
  • 阈值越低: 命中率越高,可能返回不相关结果

调优建议:

  1. 从 0.90 开始
  2. 监控缓存命中率和用户反馈
  3. 根据实际效果微调(每次调整 ±0.02)
缓存 TTL (Time To Live)

说明: 缓存条目的存活时间,单位秒

推荐值:

  • 实时内容: 300 - 1800 秒(5-30 分钟)
  • 一般内容: 3600 - 86400 秒(1-24 小时)
  • 静态内容: 86400 - 604800 秒(1-7 天)
  • 永久缓存: -1(不过期)

考虑因素:

  • 内容时效性
  • 内存容量
  • 业务需求

示例:

{
"news_bot": {"ttl": 1800}, // 新闻内容 30 分钟
"customer_service": {"ttl": 3600}, // 客服 FAQ 1 小时
"knowledge_base": {"ttl": 86400} // 知识库 24 小时
}
最大缓存条目 (Max Cache Entries)

说明: Redis 中存储的最大缓存条目数

内存估算:

每条缓存 ≈ 5-10 KB(包括向量、响应、元数据)
10000 条 ≈ 50-100 MB
100000 条 ≈ 500 MB - 1 GB

淘汰策略:

  • 使用 LRU(Least Recently Used)
  • 达到上限时,淘汰最久未使用的条目

推荐值:

  • 小规模部署: 10000 - 50000
  • 中等规模: 50000 - 200000
  • 大规模: 200000+
向量维度 (Vector Dimension)

说明: 嵌入向量的维度,取决于嵌入模型

常见维度:

  • text-embedding-ada-002: 1536
  • text-embedding-3-small: 1536
  • text-embedding-3-large: 3072

性能影响:

  • 维度越高: 语义表达越丰富,但搜索越慢,内存占用越大
  • 维度越低: 性能越好,但语义表达能力受限

推荐:

  • 大多数场景使用 1536 维(text-embedding-3-small)
  • 对语义准确性要求极高时使用 3072 维
嵌入模型选择

对比:

模型维度性能成本适用场景
ada-0021536通用,成本敏感
embedding-3-small1536较好推荐,性价比高
embedding-3-large3072最好高精度要求

建议:

  • 开发/测试: text-embedding-ada-002
  • 生产环境: text-embedding-3-small
  • 高精度场景: text-embedding-3-large

使用指南

客户端使用

语义缓存对客户端是透明的,无需修改代码即可享受缓存加速:

Python
import openai

openai.api_base = "https://your-gateway.com/v1"
openai.api_key = "YOUR_TOKEN"

# 正常调用,自动使用语义缓存
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "user", "content": "什么是深度学习?"}
]
)

print(response.choices[0].message.content)

# 检查缓存状态(通过响应头)
# response.headers['X-Cache-Status'] = 'HIT' or 'MISS'
JavaScript
import OpenAI from 'openai';

const openai = new OpenAI({
baseURL: 'https://your-gateway.com/v1',
apiKey: 'YOUR_TOKEN',
});

// 正常调用,自动使用语义缓存
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [
{ role: 'user', content: '什么是深度学习?' }
],
});

console.log(response.choices[0].message.content);
cURL
# 正常调用
curl -X POST https://your-gateway.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "user", "content": "什么是深度学习?"}
]
}' -i

# 查看响应头中的缓存状态:
# X-Cache-Status: HIT 或 MISS
# X-Cache-Similarity: 0.92 (命中时显示)

跳过语义缓存

某些场景下可能需要跳过缓存,直接调用 LLM:

cURL
curl -X POST https://your-gateway.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Skip-Semantic-Cache: true" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "今天的新闻"}]
}'
Python
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "今天的新闻"}],
headers={"X-Skip-Semantic-Cache": "true"}
)
JavaScript
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: '今天的新闻' }],
}, {
headers: {
'X-Skip-Semantic-Cache': 'true'
}
});

适用场景:

  • 实时数据查询(天气、新闻、股票等)
  • 需要最新结果的请求
  • 调试和测试
  • 强制刷新缓存
提示

详细的跳过缓存用法参考: docs/SEMANTIC_CACHE_SKIP.md

缓存状态响应头

每个响应都包含缓存状态信息:

响应头可能值说明
X-Cache-StatusHIT缓存命中,返回缓存内容
MISS缓存未命中,调用 LLM API
BYPASS跳过缓存(使用 X-Skip-Semantic-Cache)
DISABLED语义缓存未启用
X-Cache-Similarity0.00-1.00缓存命中时的相似度分数
X-Cache-Age整数(秒)缓存条目的年龄
X-Embedding-Time整数(毫秒)生成嵌入向量的耗时
X-Cache-Search-Time整数(毫秒)向量搜索耗时

示例响应头:

HTTP/1.1 200 OK
X-Cache-Status: HIT
X-Cache-Similarity: 0.92
X-Cache-Age: 3600
X-Embedding-Time: 120
X-Cache-Search-Time: 15

查看缓存统计

在管理后台的"语义缓存"页面查看统计信息:

  • 总缓存条目数
  • 缓存命中率
  • 节省的 API 调用次数
  • 节省的成本估算
  • 平均相似度分数
  • 缓存大小(内存占用)
缓存统计仪表板

管理缓存

清空所有缓存

在管理后台或通过 API 清空所有缓存:

Web 界面:

  1. 导航到 系统设置 > 语义缓存
  2. 点击"清空缓存"按钮
  3. 确认操作
清空缓存按钮

API:

curl -X DELETE https://your-gateway.com/api/semantic_cache/clear \
-H "Authorization: Bearer YOUR_TOKEN"
删除特定缓存条目

通过缓存键删除特定条目:

curl -X DELETE https://your-gateway.com/api/semantic_cache/entries/{cache_key} \
-H "Authorization: Bearer YOUR_TOKEN"

或使用 Redis CLI:

redis-cli DEL semantic_cache:abc123
调整缓存 TTL

修改现有缓存条目的 TTL:

# 设置特定条目的 TTL 为 1 小时
redis-cli EXPIRE semantic_cache:abc123 3600

# 设置永不过期
redis-cli PERSIST semantic_cache:abc123
导出缓存数据

导出缓存数据用于备份或分析:

# 使用 Redis RDB 快照
redis-cli BGSAVE

# 或导出为 JSON
curl -X GET https://your-gateway.com/api/semantic_cache/export \
-H "Authorization: Bearer YOUR_TOKEN" \
> cache_export.json

性能优化

提高命中率

⚙️
调整相似度阈值
降低阈值可以提高命中率,但需要平衡准确性
🕰️
增加缓存 TTL
延长缓存有效期,允许更多历史查询被复用
💬
优化查询模板
引导用户使用标准化的查询表述
🔥
预热缓存
提前缓存常见查询,提高初始命中率

降低延迟

  1. 使用更快的嵌入模型:

    • text-embedding-3-small 比 ada-002 快约 20%
    • 考虑使用本地嵌入服务进一步降低延迟
  2. Redis 性能优化:

    • 使用 Redis 集群提高并发能力
    • 启用 Redis 持久化保护数据
    • 配置足够的内存避免交换
  3. 并行处理:

    • 嵌入生成和缓存搜索并行执行
    • 使用异步 I/O 减少阻塞
  4. 缓存预热:

    • 启动时加载热门查询
    • 定期刷新常用缓存

内存管理

定期检查 Redis 内存使用:

# Redis CLI
INFO memory

# 关键指标
used_memory: 256MB
used_memory_peak: 512MB
maxmemory: 2GB
Redis 内存监控

成本优化

选择合适的嵌入模型

成本对比:

模型价格/1M tokens1536 维相对成本
ada-002$0.00011x
embedding-3-small$0.000020.2x(最优)
embedding-3-large$0.00013否(3072 维)1.3x

推荐: 使用 text-embedding-3-small,成本最低且性能优秀。

优化嵌入调用

减少不必要的嵌入生成:

  • 对完全相同的查询使用精确匹配(MD5 哈希)
  • 缓存嵌入向量,避免重复生成
  • 批量生成嵌入提高效率
// 伪代码
func getEmbedding(text string) ([]float32, error) {
// 先检查精确匹配缓存
hash := md5(text)
if cached := exactCache.Get(hash); cached != nil {
return cached.Embedding, nil
}

// 再调用嵌入模型
embedding := callEmbeddingAPI(text)
exactCache.Set(hash, embedding, ttl)
return embedding, nil
}
计算 ROI

评估语义缓存的投资回报:

成本:

  • 嵌入 API 调用费用
  • Redis 服务器成本

收益:

  • 节省的 LLM API 费用
  • 提升用户体验的价值

示例计算:

假设:
- 每天 10000 次请求
- 缓存命中率 70%
- GPT-3.5 平均成本 $0.002/请求
- 嵌入成本 $0.00001/请求

每日节省:
- LLM 节省: 10000 × 0.7 × $0.002 = $14
- 嵌入成本: 10000 × $0.00001 = $0.1
- 净节省: $14 - $0.1 = $13.9/天

每月节省: $13.9 × 30 = $417
年度节省: $417 × 12 = $5004

监控和调试

监控指标

关键监控指标和健康阈值:

指标健康范围警告阈值说明
命中率> 60%< 40%低于 40% 需要调整阈值或 TTL
平均相似度> 0.88< 0.85过低可能命中不相关内容
嵌入延迟< 150ms> 300ms考虑优化嵌入服务
搜索延迟< 20ms> 50msRedis 性能问题
内存使用率< 70%> 85%需要扩容或清理缓存
错误率< 1%> 5%检查 Redis 连接和嵌入服务
缓存监控仪表板

调试工具

在管理后台提供调试工具:

  1. 相似度测试: 输入两个查询,查看相似度分数
  2. 向量可视化: 可视化查询向量的分布
  3. 缓存检索测试: 输入查询,查看匹配的缓存条目
  4. 日志查看: 查看缓存命中/未命中的详细日志
缓存调试面板

常见问题排查

缓存命中率低

可能原因:

  1. 相似度阈值设置过高
  2. 缓存 TTL 过短,缓存过早过期
  3. 用户查询表述差异大
  4. 缓存数据不足(冷启动)

解决方案:

  1. 逐步降低相似度阈值(从 0.90 → 0.88 → 0.85)
  2. 延长缓存 TTL
  3. 预热常见查询
  4. 引导用户使用标准化表述
缓存返回不相关结果

可能原因:

  1. 相似度阈值过低
  2. 嵌入模型选择不当
  3. 查询文本过短,语义信息不足

解决方案:

  1. 提高相似度阈值
  2. 使用更高维度的嵌入模型
  3. 对过短查询禁用缓存(< 10 字符)
  4. 记录用户反馈,手动清除错误缓存
嵌入生成失败

可能原因:

  1. 嵌入 API 密钥无效
  2. 网络连接问题
  3. API 配额不足
  4. 请求超时

解决方案:

  1. 验证 API 密钥是否正确
  2. 检查网络连接和防火墙
  3. 检查嵌入 API 配额
  4. 增加超时时间
  5. 降级为不使用缓存
Redis 连接失败

可能原因:

  1. Redis Stack 未启动
  2. 连接字符串配置错误
  3. 防火墙阻止连接
  4. Redis 密码错误

解决方案:

  1. 确认 Redis Stack 运行正常: redis-cli ping
  2. 检查连接字符串格式
  3. 检查防火墙规则
  4. 验证 Redis 密码
向量搜索不工作

可能原因:

  1. 使用了普通 Redis 而非 Redis Stack
  2. Redis Stack 版本过低
  3. 向量索引未创建

解决方案:

  1. 确认使用 Redis Stack: redis-cli MODULE LIST
  2. 升级到 Redis Stack 7.2+
  3. 检查向量索引: FT.INFO semantic_cache_idx
  4. 手动重建索引

最佳实践

生产环境部署

1

使用 Redis 集群

生产环境推荐使用 Redis 集群提高可用性:

  • 主从复制保证数据安全
  • Sentinel 自动故障转移
  • 集群模式水平扩展
2

配置持久化

启用 Redis 持久化避免数据丢失:

# redis.conf
save 900 1
save 300 10
save 60 10000
rdbcompression yes
rdbchecksum yes
3

监控告警

配置监控告警及时发现问题:

  • Redis 内存使用率 > 85%
  • 缓存命中率 < 40%
  • 嵌入 API 错误率 > 5%
  • Redis 连接失败
4

定期备份

定期备份 Redis 数据:

# 每日备份脚本
redis-cli --rdb /backup/redis-$(date +%Y%m%d).rdb
5

性能测试

上线前进行充分的性能测试:

  • 压力测试验证高并发场景
  • 缓存命中率测试
  • 延迟测试
  • 故障恢复测试

安全建议

注意

语义缓存会存储用户查询和 LLM 响应,注意保护敏感数据:

  • Redis 认证: 设置 Redis 密码,启用 ACL
  • 网络隔离: Redis 仅允许内网访问,不暴露公网
  • 数据加密: 敏感数据加密后存储
  • 访问控制: 限制可以管理缓存的用户
  • 合规性: 遵守数据隐私法规(GDPR, CCPA 等)
  • 定期清理: 定期清理过期和敏感缓存

特殊场景处理

对于实时性要求高的查询,禁用缓存:

# 天气、新闻等实时数据
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "今天的天气"}],
headers={"X-Skip-Semantic-Cache": "true"}
)

常见问题

语义缓存和精确缓存有什么区别?

精确缓存:

  • 查询文本必须完全相同才能命中
  • 命中率低(通常 < 20%)
  • 实现简单,性能好

语义缓存:

  • 识别语义相似的查询,即使文字不同
  • 命中率高(通常 60-80%)
  • 需要嵌入模型和向量搜索

组合使用: 建议先精确匹配,未命中再语义搜索,兼顾性能和命中率。

语义缓存会影响响应的准确性吗?

有一定风险,但可以控制:

风险:

  • 相似但不完全相同的查询,返回的缓存响应可能不完全匹配
  • 阈值设置不当可能返回不相关的结果

控制方法:

  • 设置合适的相似度阈值(推荐 0.90+)
  • 记录和分析缓存命中质量
  • 提供反馈机制,允许用户报告不相关结果
  • 对关键业务禁用缓存或提高阈值
可以使用本地嵌入模型吗?

可以。如果有本地嵌入服务(如使用 Sentence Transformers):

  1. 部署本地嵌入服务,提供兼容 OpenAI 的 API
  2. 配置自定义端点:
    嵌入 API 端点: http://localhost:8000/v1/embeddings
  3. 设置 API 密钥(如果需要)

优势:

  • 降低嵌入 API 成本
  • 减少外部依赖
  • 更低的延迟
  • 数据隐私
缓存会占用多少内存?

估算公式:

总内存 ≈ 条目数 × (向量大小 + 响应大小 + 元数据)

示例:
- 向量: 1536 维 × 4 字节 = 6 KB
- 响应: 平均 2 KB
- 元数据: 1 KB
- 每条: ≈ 9 KB

10000 条 ≈ 90 MB
100000 条 ≈ 900 MB

实际占用取决于:

  • 嵌入维度
  • 响应长度
  • Redis 开销(约 1.2-1.5x)
如何处理缓存的过期和更新?

自动过期:

  • 设置 TTL,缓存自动过期
  • 适合时效性内容

手动刷新:

  • 内容更新时,清除相关缓存
  • 使用 API 或 Redis CLI 删除

版本控制:

  • 在缓存键中包含版本号
  • 更新内容时递增版本号
cacheKey := hash(query + model + contentVersion)
不同模型的响应可以共享缓存吗?

不推荐。因为:

  1. 不同模型的响应质量、风格可能不同
  2. 用户指定模型有特定需求
  3. 可能导致用户体验不一致

建议:

  • 为每个模型维护独立缓存
  • 缓存键包含模型名称
  • 模型升级时清除旧缓存

相关页面

⚙️
系统设置
📋
访问日志
📊
限流控制