【AI】利用语义缓存,优化AI Agent性能 - 实践

在传统缓存体系中,Redis 的核心价值是 Key 精确命中
但在 大模型 / RAG / 智能问答 场景下,问题的本质已经发生变化:

用户的问题不是“是否完全一样”,而是“语义是否相近”。

这催生了一种新的缓存形态:语义缓存(Semantic Cache)


一、Redis 语义缓存

1.1 传统缓存的问题

Q1: 更换轮胎需要多少钱?
Q2: 汽车换一个轮胎大概费用?

在 Key-Value 缓存里:

key1 != key2 → cache miss

但在语义上:

sim(Q1, Q2) ≈ 0.95 → 应该命中

1.2 语义缓存的定义

语义缓存 = 向量化 + 相似度检索 + Redis 高速存储

基本流程:

Query
 ↓ embedding
Vector
 ↓ ANN search
TopK 相似问题
 ↓ 阈值判断
命中 → 直接返回
未命中 → LLM 推理 + 写入缓存

二、Redis 发展史与为什么它能做语义缓存

2.1 Redis 版本演进(关键节点)

版本时间关键能力
Redis 2.x2012单线程 KV,引入基础数据结构
Redis 3.x2015Cluster,分片与高可用
Redis 4.x2017Modules API(模块化能力基石)
Redis 6.x2020IO 多线程,提升网络吞吐
Redis 7.x2022内存 / 协议 / Cluster 优化
Redis 8.x2024+核心性能与内存模型持续优化(未引入原生向量能力)
Redis Stack2023Search / JSON / Vector(语义检索实际落地形态)

真正让 Redis 能做语义缓存的,是:

Redis Modules + Vector Similarity Search

Redis 从 RediSearch 2.4(2022)开始支持向量相似度检索(FLAT / HNSW),这才真正具备“语义检索”能力。

Redis 7.x + Redis Stack 支持语义检索(2022–至今)
  • Redis 核心版本:7.x

  • 通过 Redis Stack 统一集成:

    • RediSearch
    • RedisJSON
    • Vector Search

工程上我们通常说:

“Redis 7 + Redis Stack = 可用的语义检索 Redis”

什么是Redis Stack

Redis Stack 是 Redis 官方推出的“能力集合发行版”,在不修改 Redis 内核的前提下,通过官方模块,把搜索、JSON、向量检索等高级能力标准化、产品化。

它不是一个新 Redis,也不是一个新版本。

Redis Stack 为什么出现?

过去 Redis 生态是这样的:

  • 模块版本碎片化
  • 编译复杂
  • 线上环境难以维护

Redis Stack 的目标是:

把“可用于生产的模块组合”标准化

Redis 内核(Redis OSS)
分类内容
负责- 内存管理
- 网络 IO
- 数据结构(String / Hash / ZSet …)
目标- 极致性能
- 核心最小化
不直接做- 搜索
- 向量索引
- JSON 查询
Redis Stack 包含哪些组件(核心)
1️⃣ RediSearch

最重要的模块

  • 全文检索
  • 二级索引
  • 聚合查询
  • 向量相似度检索(FLAT / HNSW)
FT.CREATE
FT.SEARCH
FT.AGGREGATE

语义检索能力 = 100% 来自 RediSearch


2️⃣ RedisJSON
  • 原生 JSON 存储

  • 支持:

    • JSONPath
    • 局部更新
  • 解决 Hash 表达能力不足的问题

适合:

  • RAG 元数据
  • 结构化 Prompt
  • 会话状态

3️⃣ RedisTimeSeries(可选)
  • 高性能时序数据

  • 常用于:

    • 监控
    • 指标
    • 特征统计

4️⃣ RedisBloom(可选)
  • Bloom / Cuckoo Filter
  • HyperLogLog
  • TopK

常用于:

  • 去重
  • 防缓存穿透
  • 热点统计

三、Redis 底层数据结构

3.1 Redis 对象模型

redisObject
 ├── type   (string / hash / zset / module)
 ├── encoding (int / embstr / ziplist / skiplist)
 └── ptr

Vector 本质是:

module 类型对象

3.2 向量在 Redis 中的存储

在 Redis Stack(Search 模块)中:

  • 向量字段并非 String

  • 而是 专用 Vector Index

  • 支持:

    • FLAT(暴力)
    • HNSW(近似)

四、Redis中向量检索算法

一、FLAT:暴力但准确的向量搜索

对于Flat索引,我们引入查询向量 xq,并将其与索引中所有向量进行比较,即计算 xq 到每个向量的距离。
在这里插入图片描述
在计算完所有这些距离后,我们将返回其中最接近的 k 个作为我们最接近的匹配项。即进行一次k最近邻 (kNN) 搜索。

维度说明
算法本质对所有向量逐一计算距离,排序后取 TopK(Brute Force)
时间复杂度O(N × D)
N:向量数量
D:向量维度
距离计算L2(欧氏距离)
IP(内积)
COSINE(语义检索最常用)
精度100% 精确
构建成本无需构建索引
参数调优不需要
性能特点❌ 性能差
❌ 无法扩展
❌ N 稍大就不可用
适用规模< 10k:可用
10k ~ 100k:勉强
> 100k:不现实
Redis 支持VECTOR FLAT 768
典型场景调试验证
小规模语义缓存
召回精度基准(Baseline)

二、HNSW:分层可导航小世界图

HNSW 是什么

HNSW(Hierarchical Navigable Small World):

层次化可导航小世界(HNSW)图是向量相似性搜索中表现最佳的索引之一。HNSW 技术以其超级快速的搜索速度和出色的召回率,在近似最近邻(ANN)搜索中表现卓越。

在这里插入图片描述

HNSW的基础

我们可以将ANN算法分为三个不同的类别;树、哈希和图。HNSW属于图类别。更具体地说,它是一个基于接近度的图,其中两个顶点根据它们的接近度(更接近的顶点被连接)连接——通常在欧几里得距离中定义。

从“接近度”图到“层次可导航的小世界”图的复杂度有显著的飞跃,将描述两种对HNSW贡献最大的基本技术:概率跳表和可导航的小世界图。

概率跳表

概率跳表由William Pugh在1990年引入,它结合了排序数组的快速搜索能力和链表的便捷插入操作。

跳表通过构建多个层的链表来工作。在最高层,链接能够跳过许多中间节点。在较低层,链接的“跳跃”数量逐渐减少。

要在跳表中进行搜索,从最高层开始,沿着边缘向右移动。如果发现当前节点的“键”大于目标键,表示已经超出目标,于是向下移动到下一层继续搜索。

在这里插入图片描述

NSW: 可导航的小世界图

NSW: Navigable Small World Graphs

NSW是一种图结构,其中包含通过边连接到最近邻居的顶点。HNSW是对可导航小世界(NSW)图的进一步改进。

在NSW图中,每个节点(或称为顶点)都与若干其他节点相连,这些相连的节点被称为“朋友”。每个节点维护着一个朋友列表,共同构成了整个图的结构。

进行NSW图搜索时,搜索过程遵循以下步骤:

  • 从预定义的起点出发:选择一个起点,该点与多个相邻节点相连。
  • 局部邻近性识别:在这些相邻节点中,识别出与查询向量最为接近的一个节点。
  • 逐步逼近目标:移动到该节点,并重复上述过程,逐步缩小搜索范围,直至找到最接近查询向量的节点。
    在这里插入图片描述
HNSW:分层导航小世界图

分层导航小世界图(Hierarchical Navigable Small World Graphs,简称HNSW)是可导航小世界图(NSW)的高级演变,它引入了概率跳表结构中的概率多层次概念。

HNSW通过向NSW添加层次化结构,创建一个在不同层级间具有不同链接长度的图。这种结构在最高层拥有最长的链接,在最低层则拥有最短的链接。

在这里插入图片描述

数据结构

  • 多层图结构
  • 每个节点连接有限邻居
  • 高层稀疏,低层密集

从“接近度”图到“层次可导航的小世界”图的复杂度有显著的飞跃,将描述两种对HNSW贡献最大的基本技术:概率跳表和可导航的小世界图。

HNSW特点
维度说明
算法全称HNSW(Hierarchical Navigable Small World)
算法类型近似最近邻搜索(ANN)
核心思想基于“小世界网络”,通过多层图结构实现快速导航
图结构分层图(Layered Graph)
高层稀疏、低层密集
节点连接每个节点只连接有限数量的近邻(由 M 控制)
索引结构特点高层用于快速定位区域
底层用于精细搜索
搜索起点最高层的入口节点
搜索策略贪心搜索(Greedy Search)逐层下降
查询流程高层快速收敛 → 逐层下探 → 底层精搜返回 TopK
插入流程新节点随机分配最高层级 → 自顶向下查找邻居 → 逐层建立连接
查询时间复杂度近似 O(log N)
插入时间复杂度近似 O(log N)
扩展能力可支撑百万、千万级向量规模
精度特点非 100% 精确,但可通过参数逼近精确结果
实时性支持在线插入与查询
工程优势查询稳定、延迟可控、适合在线系统
典型应用语义缓存
向量数据库
RAG 粗排召回
Redis 支持RediSearch Vector(HNSW)

结构示意

Level 3:   o────o
            │
Level 2: o──o──o──o
          │     │
Level 1: o─o─o─o─o─o

3️⃣ 搜索流程

从最高层入口节点开始
↓
贪心搜索,快速接近目标区域
↓
逐层下降
↓
底层精搜,返回 TopK
HNSW算法 参数含义

HNSW性能
在深入了解了HNSW(分层导航小世界图)的理论基础和Faiss库的实现细节后,现在转向评估不同参数对HNSW索引性能的具体影响。将重点分析召回率、搜索时间、构建时间以及内存使用情况。

将调整以下三个关键参数:M、efSearch和efConstruction,并在Sift1M数据集上测试它们的影响。

  • M 控制每个节点的最大连接数量,影响图的密度和搜索精度。
  • efSearch 控制查询过程中候选列表的大小,影响查询时间和精度。
  • efConstruction 控制索引构建过程中候选列表的大小,影响索引构建时间和质量。
参数含义作用阶段调大后的影响常见取值
M每个节点的最大邻居数构建精度 ↑,内存 ↑,构建变慢8 / 16 / 32
EF_CONSTRUCTION构建阶段候选集大小构建索引质量 ↑,构建时间 ↑100 ~ 400
EF_RUNTIME(EF_SEARCH)查询阶段候选集大小查询召回率 ↑,查询延迟 ↑20 ~ 100
K返回的最近邻数量查询结果更多,后处理成本 ↑1 ~ 20

五、Redis 语义缓存整体架构

              ┌────────────┐
User Query →  │ Embedding  │
              └─────┬──────┘
                    │
              ┌─────▼──────┐
              │ Redis ANN  │
              │  HNSW     │
              └─────┬──────┘
            hit?     │ miss
              │      ▼
         ┌────▼───┐  LLM
         │ Answer │   ↓
         └────────┘ 写入缓存

七、Go 语言实现语义缓存(工程示例)

7.0 创建索引(Redis Stack)

FT.CREATE semantic_cache
ON HASH
PREFIX 1 "qa:"
SCHEMA
question VECTOR HNSW 768 TYPE FLOAT32 DISTANCE_METRIC COSINE
answer TEXT

7.1 数据结构设计

type CacheItem struct {
ID       string
Vector   []float32
Question string
Answer   string
}

7.2 向量写入 Redis

func Store(ctx context.Context, rdb *redis.Client, item CacheItem) error {
vecBytes := floatsToBytes(item.Vector)
key := "qa:" + item.ID
return rdb.HSet(ctx, key, map[string]interface{}{
"question": item.Question,
"answer":   item.Answer,
"vector":   vecBytes,
}).Err()
}
func floatsToBytes(vec []float32) []byte {
buf := new(bytes.Buffer)
for _, v := range vec {
_ = binary.Write(buf, binary.LittleEndian, v)
}
return buf.Bytes()
}

7.3 向量检索(KNN)

func Search(ctx context.Context, rdb *redis.Client, vector []float32) (string, float64, error) {
vecBytes := floatsToBytes(vector)
cmd := []interface{}{
"FT.SEARCH", "semantic_cache",
"*=>[KNN 1 @question $vec AS score]",
"PARAMS", 2, "vec", vecBytes,
"SORTBY", "score",
"RETURN", 2, "answer", "score",
"DIALECT", 2,
}
res, err := rdb.Do(ctx, cmd...).Result()
if err != nil {
return "", 0, err
}
// 解析结果(略)
return answer, score, nil
}

7.4 命中策略(工程关键)

const HitThreshold = 0.85
if score >= HitThreshold {
return cachedAnswer
}
// fallback to LLM

实践经验:

  • 技术问答:0.85~0.9
  • 闲聊类:0.75~0.8
  • 汽修 / 医疗:宁高勿低

八、语义缓存 vs 向量数据库

维度Redis 语义缓存专用向量库
延迟极低(<5ms)
数据规模百万级千万~亿
事务支持
运维简单偏复杂
适合场景热问题冷知识

结论:

Redis 语义缓存 ≠ 向量数据库
它是 RAG 架构里的第一层“语义 L1 Cache”


九、常见工程坑

向量版本漂移:

embedding 模型升级 → 缓存需失效,或者维护embedding模型版本。

缓存污染:

低质量回答进入缓存

阈值静态:

不同业务应动态调整

并发写入:

建议异步写缓存

命中率忽高忽低/相同问题结果不稳定

原因 90% 是:

EF_RUNTIME 太小

EF_RUNTIME 是 HNSW 算法的一个关键参数,它控制查询时的搜索精度与延迟之间的权衡。 查询阶段 EF 小,等于“搜索半途就停了”

posted @ 2026-01-21 10:02  gccbuaa  阅读(1)  评论(0)    收藏  举报