一、背景简介

原AI聊天助手项目中,每次提问(Prompt)都需要调用公有LLM API来获取大模型的回答(Answer),然而,有些时候用户会向聊天助手询问同样的问题,我们将KV存储集成到聊天助手当中,初步实现了一个Prompt对应一个Answer的缓存功能。

但是有时候,用户向聊天助手询问的问题并不是一字不差,同样的语义,由于prompt字面的不同,原先的缓存功能就失效,现在的问题是如何实现语义级别的缓存功能

二、组件依赖

2.1 高性能中间件 - 自研KV存储

Github - KVstore

支持命令

命令 语法 说明
SET SET 写入键值对,key 已存在则覆盖
GET GET 读取键值,不存在返回 $-1
DEL DEL 删除键,返回删除数量
EXISTS EXISTS 检查键是否存在,返回 0 / 1
MOD MOD 仅当键存在时更新值
SAVE SAVE 手动触发 RDB 快照保存
PING PING [message] 连通性探测,返回 PONG 或回显 message

2.2 高效向量搜索 - FAISS

语义级别的识别,首先想到的方案是结合FAISS来做,(Facebook AI Similarity Search),这是由 Meta(原 Facebook)开源的一个高效向量搜索库。

FAISS向量搜索具体的内部实现是一个比较复杂的问题,这里只粗浅介绍一下:

原始的prompt,比如“我饿了”、“我想吃饭”,被模型编码成为高维向量:

文本输入                    高维向量空间
"我饿了"    → 模型编码 → [0.12, -0.34, 0.56, ..., 0.78]  (768维)
"我想吃饭"  → 模型编码 → [0.11, -0.32, 0.55, ..., 0.79]  (768维)
                           ↑ 两个向量方向几乎相同 ↑

内存结构:

隐式ID 向量数据 (768个float32) 对应的文本
0 [0.0123, -0.0456, 0.0789, ...] “我饿了”
1 [0.0115, -0.0412, 0.0765, ...] “我想吃饭”
2 [-0.0891, 0.1234, -0.0056, ...] “今天天气不错”
  • FAISS 只存数字,不知道prompt的文本是什么
  • ID 自动生成(从0开始,每次调用add自增)
  • prompt文本存在KV存储里,用 ID 关联

检索时暴力遍历,通过余弦相似度衡量向量方向的相似性,给定阈值,判断是否命中。当然了,检索算法有内置的优化实现。

我们faiss模块中使用到的核心接口如下:

分类 接口 一句话作用
创建索引 IndexFlatIP 建一个用内积算相似度的索引
写入 add 把向量存进索引
检索 search 找最相似的 Top-K 个向量
保存 write_index 把内存索引存成文件
加载 read_index 从文件恢复索引到内存
取数量 ntotal 看索引里存了多少条
取维度 d 看向量是几维的

2.3 通信底座 - gRPC

gRPC(Google Remote Procedure Call)是由谷歌公司开发的远程调用框架。什么叫远程调用?

如下图所示,这是一个多语言多平台的系统,不同语言和平台之间的对象本不能直接相互调用,gRPC就是为了解决这个问题出现的:

在这里插入图片描述
通过Protobuf协议(一种语言无关、平台无关且便于传输的序列化协议),server端对需要共享的对象进行序列化,接收端进行反序列化,并且client端通过stub封装从而实现直接调用(无需关系底层实现),这篇博客介绍了你所需要的Protobuf相关内容

三、模块架构

Go后端作为gRPC的client,通过stub收发proto消息,由Python实现的faiss语义层为gRPCserver,他们之间的通信消息和服务由Proto文件给定,proto文件中定义三个接口函数:

GetCache(prompt)
PutCache(prompt)
DeleteCache(prompt)

prompt进faiss层被编码为相似度+ID,ID与KV中的answer绑定,faiss层通过GET ID、SET ID Answer等命令与KV存储交互。

整体模块架构总结如下:

┌────────────────────────────────────────────────────────┐
│               业务客户端层 (Go Web 后端)               │
└───────────────────────────┬────────────────────────────┘
                            │ gRPC 通信 (50051)
┌───────────────────────────▼────────────────────────────┐
│ 1. 网关层 (main.py) - 流量拦截与服务生命周期管理        │
└───────────────────────────┬────────────────────────────┘
                            │ 本地方法调度
┌───────────────────────────▼────────────────────────────┐
│ 2. 核心算法层 (engine.py)                              │
│    ├── [模块 A] 语义编码 (SentenceTransformer)          │
│    ├── [模块 B] 几何正规化 ($L_2$ Normalization)       │
│    └── [模块 C] 容错自愈 (维度探测器)                   │
└───────────────────────────┬────────────────────────────┘
                            │ 双向读写
┌───────────────────────────▼────────────────────────────┐
│ 3. 存储与索引层 (持久化)                                │
│    ├── [模块 D] 内存阵列检索 (FAISS) ──> 负责“控门”   │
│    └── [模块 E] KV缓存 (自研KV)  ──> 负责“管账”   │
└────────────────────────────────────────────────────────┘
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐