← 返回 2026-09-10

面向普通CPU硬件低延迟LLM网页搜索的三层缓存架构 A Three-Layer Caching Architecture for Low-Latency LLM Web Search on Commodity CPU Hardware

Ayushman Bhattacharya, Nihal Gazi 📅 2026-08-12 👍 8 2026-09-12 18:30
LLM系统 Redis 成本优化 检索增强生成 系统工程 缓存架构

用会话、语义、嵌入三层Redis缓存,在普通CPU上把AI搜索成本压到每次约1.5美分

前置知识

Redis 逻辑数据库与 TTL

Redis 是内存键值存储,一个实例可划分出 DB 0–15 等多个逻辑数据库,每个键可设置 TTL 到期自动删除。本文用三个逻辑 DB 分别存放语义查询缓存、URL 嵌入和会话上下文,各自配不同的 TTL(5 分钟/24 小时)与数据格式,从而获得选择性清空、按库独立监控和命名空间隔离三种运维能力。

论文的三层架构完全建立在对 Redis 逻辑分区与 TTL 语义的理解之上,不理解这一点就无法看懂数据如何分区、过期与隔离。

文本嵌入与余弦相似度

嵌入模型把文本映射为定长向量,本文用 sentence-transformers/all-MiniLM-L6-v2 输出 $q \in \mathbb{R}^{384}$。语义相近的文本向量方向接近,余弦相似度 $\mathrm{sim}(q,e)=\frac{q\cdot e}{\|q\|\|e\|}$ 度量方向一致性,取值约 $[-1,1]$,越接近 1 越相似。

语义查询缓存靠 $\max_i \mathrm{sim}(q,e_i) \ge \tau=0.90$ 判定改写后的查询命中缓存,是全文最核心的机制。

语义缓存与传统 KV 缓存

传统缓存按键精确匹配,查询措辞稍变即失效;语义缓存先为查询计算嵌入,再与缓存中的嵌入逐一比较相似度,超过阈值就直接返回旧答案,能拦截 "weather India" 与 "India weather forecast" 这类改写(论文实测相似度约 0.94)。

本文 Layer 2 的价值完全来自"语义级"而非"键级"匹配,必须分清两者才能理解为什么改写查询可以短路整条搜索流水线。

Canonical 霍夫曼编码

依据符号频率构建最优前缀码:高频符号码短、低频符号码长,实现无损压缩。canonical 变体只存"符号→码长"映射即可让解码端重建完全相同的码表,省去整棵树。英文文本字节频率极度偏斜(空格约 18%、字母 e 约 13%、z 仅约 0.07%),正适合它发挥。

论文自研纯 Python canonical 霍夫曼编解码做会话磁盘档案压缩,"压缩比 vs 零原生依赖"的权衡是关键设计决策之一。

冷热分层存储与 LRU 淘汰

热层是 Redis 内存中的最近 $k=20$ 条消息窗口,读取约 0.1 ms;冷层是磁盘上的 Huffman 压缩档案,读取约 3–107 ms。LRU 守护线程每 60 秒扫描一次,把空闲超过 120 分钟的整个会话从 Redis 迁到磁盘,用户返回时再把最近 k 条透明回灌(re-hydrate)。

这解释了论文如何做到"Redis 内存占用与会话总长度无关",是理解会话生命周期(Fig. 4)的前提。

研究动机

AI 搜索的商业 API 定价陡峭:OpenAI 网络搜索 SearchGPT 按每千次调用 10–30 美元收费,外加约 8,000 token 注入网页上下文按输入价计费,实际单次查询 $0.03–0.10;Google Gemini grounding 对 Pro 收 $35/千次(Flash $14/千次);Perplexity Sonar 基础档 $5/千次、Sonar Pro $18/千次。作者自建的答案引擎 OreoLook(原名 lixSearch)用无头浏览器代理直接搜索、远程 LLM 合成答案,把单查询成本压到约 $0.02,但用户增长后暴露三个生产痛点:(1) 会话失忆——完整对话史放内存无法跨数千并发会话扩展,用户追问“我们刚才聊到哪了”时助手无法回应;(2) 用户频繁改写查询(“weather Tokyo”→“Tokyo weather forecast”),每次改写都重新执行搜索代理+网页抓取+LLM 合成的完整流水线,尽管答案几秒前刚算过;(3) 引入 RAG 后,Wikipedia 等热门 URL 被多个会话重复抓取并各自计算一次 384 维嵌入(每次约 200 ms),浪费算力预算。

本文的目标是本文的目标是让这套自建 AI 搜索在普通 CPU 硬件上可持续运营:不订阅任何按查询计费的搜索 API,本地承担搜索、缓存、会话管理与嵌入计算,仅 LLM 合成走远程推理,同时逐一解决上述三个痛点。可量化的目标包括:会话可在数小时甚至数天后恢复且 Redis 内存占用有界(与对话总长度无关,$O(k)$);改写查询不再触发完整流水线;每个 URL 在 24 小时窗口内至多嵌入一次、跨所有会话共享;缓存读取达到亚毫秒级;单查询综合成本显著低于 SearchGPT($0.03–0.10)、Gemini Pro($0.035)和 Sonar Pro($0.018)。最终部署形态是单台 8 vCPU Intel Cascade Lake 云主机(2 GHz、32 GB 内存、无 GPU),30 个 Hypercorn worker 分 3 个容器副本,外加限 2 GB 的 Redis 7.4 容器和 nginx 负载均衡,基础设施月成本约 $96。

与已有工作不同的是,每个痛点在生态里都有局部方案:LangChain/LlamaIndex 提供进程内对话缓冲,但不跨重启、不跨副本扩展;GPTCache 做语义缓存,但它是全局缓存(多用户搜索助手会跨用户泄露答案)、需要外接 FAISS/Milvus/Qdrant 向量库(增加运维复杂度)、且只管语义缓存一件事;MemGPT 用 LLM 自主决定换页来管理上下文,行为不可预测且难调试。没有任何单一系统能把会话持久化、语义去重、嵌入复用三件事统一成一个轻量包。本文的切入角度是"一个 Redis 实例、三个逻辑数据库":不用键前缀混放,而是按 TTL 曲线(5 分钟/24 小时)、作用域(会话私有 vs 全局共享)和故障域做物理分区,再配自研纯 Python canonical 霍夫曼编解码实现零原生依赖的磁盘溢出层,用确定性 LRU 取代 MemGPT 式 LLM 自主换页换取可预测性。作者也坦诚未与 GPTCache 直接基准对比——后者是包装 LLM 调用的中间件,前者是管理会话、上下文与嵌入的一体化缓存层,强行对比等于在 GPTCache 上重造整个架构。

核心方法

整体思路从直觉出发:AI 搜索真正的成本大头不在搜索和 LLM 调用本身,而在多轮对话不断积累的状态;于是把"重复发生的工作"归为三类——重复发送的上下文、重复改写的查询、重复嵌入的 URL——各配一层缓存,全部托管在同一个 Redis 实例的三个逻辑数据库中,由 Coordinator 协调器统一封装:每个会话一个对象、一个配置、四个动词(add_message_to_context、get_semantic_response、get_url_embedding、get_stats)。查询流水线顺序如 Fig. 1:先查语义缓存(DB 0),命中($\cos \ge 0.90$)直接短路整条流水线返回旧答案;未命中则从 DB 2 加载最近 $k=20$ 条会话上下文,再查全局 URL 嵌入缓存(DB 1)避免重复嵌入,最后才执行浏览器搜索代理+LLM 合成。后台另有一个 LRU 守护线程把空闲会话迁出 Redis 到 Huffman 压缩的磁盘档案,使 Redis 内存占用始终有界。

核心创新是按"作用域 × TTL × 数据格式"把三个关注点正交拆分到 Redis 逻辑数据库,而非单库加键前缀:DB 0 语义缓存按会话私有(防跨用户泄露)、TTL 仅 5 分钟、每键一个 JSON 文档存至多 50 对(嵌入, 响应);DB 1 URL 嵌入全局共享(跨会话摊销嵌入成本)、TTL 24 小时、以 1,536 字节原始 float32 存 384 维向量,比约 3,800 字节的 JSON 数组省 2.5 倍空间;DB 2 会话上下文按会话私有、TTL 24 小时、有序列表限长 $k=20$。溢出策略不是截断丢弃,而是把被弹出的最老消息序列化后追加进 Huffman 压缩的 .huff 磁盘档案,完整历史得以保留用于语义检索、审计回放和淘汰后恢复;每 60 秒扫描的守护线程把空闲超 120 分钟的整个会话迁往磁盘。与 GPTCache 的本质区别有三:缓存按会话隔离、嵌入直接以 JSON/原始字节存 Redis 无需外部向量库、会话管理与嵌入复用是内建层而非外挂中间件。

方法步骤详情

第一步,写入会话:新消息以毫秒时间戳为 turn ID 推入 DB 2 有序列表,同时以带 TTL 的独立键存 JSON;列表超过 $k=20$ 时以流水线事务原子地弹出最老条目、Huffman 编码后追加到 .huff 档案并删键。第二步,语义查询:为查询算 384 维嵌入 $q$,取出当前会话+URL 键下全部 $(e_i,r_i)$ 对,计算 $\mathrm{sim}(q,e_i)=\frac{q\cdot e_i}{\|q\|\|e_i\|}$(加 $\epsilon=10^{-8}$ 防零除),若 $\max_i \mathrm{sim} \ge \tau=0.90$ 则返回缓存响应 $r_i$、跳过搜索与 LLM;未命中则追加新对(超 50 条 FIFO 裁剪)并回写刷新 TTL。第三步,URL 嵌入:全局键 emb:{url_hash} 存 24 小时 TTL 的原始 float32 字节,命中省约 200 ms。第四步,冷热迁移:守护线程每 60 秒检查,空闲超 120 分钟的会话整体迁往磁盘;返回用户且 Redis 为空时从磁盘加载最近 k 条透明回灌;Redis 不可用时退化为纯盘读。第五步,.huff 格式:24 字节应用头(魔数+时间戳+轮数,单次 read(24) 可读)包裹 HCv1 编解码头(原长、符号表、填充)与压缩比特流,30 天清理。

技术新颖性

技术新颖性体现在四处:(1) 三库物理分区的运维设计——不同 TTL 曲线(语义缓存 5 分钟、嵌入与会话 24 小时)、不同作用域(会话私有 vs 全局)、独立故障域(清空语义缓存不影响对话史),并因此获得选择性 flush 与按库监控能力,这是 LangChain/GPTCache/Haystack 等框架都不具备的模式;(2) 零原生依赖的 canonical 霍夫曼编解码:纯 Python 实现(吞吐约 800 KB/s),利用英文文本字节频率极度偏斜(空格约 18%、e 约 13%、z 约 0.07%),符号表开销最多约 512 字节,在 1–100 KB 的典型会话负载上做到 65–69% 压缩比,稳定优于 lz4、距 zlib-1 仅 5–10 个百分点,换来容器化部署零 C 扩展依赖;(3) 用确定性 LRU+固定窗口替代 MemGPT 式 LLM 自主换页,行为可预测、生产可调试;(4) 少见的诚实评估框架:明确区分 Redis 键空间命中率(89.3%)与查询级语义命中率,拒绝用前者推导"避免的推理成本",并声明成本对比仅为测量期快照。

Query pipeline flow. A semantic cache hit (Layer 2) short-circuits the entire pipeline. On a miss, session context is loaded (Layer 1), embeddings are checked (Layer 3), and the full search–synthesis pipeline executes.
Fig. 1: Query pipeline flow. A semantic cache hit (Layer 2) short-circuits the entire pipeline. On a miss, session context is loaded (Layer 1), embeddings are checked (Layer 3), and the full search–synthesis pipeline executes.
Data flow through the three-layer caching architecture. Solid arrows indicate the primary path; dashed arrows indicate overflow and eviction paths.
Fig. 2: Data flow through the three-layer caching architecture. Solid arrows indicate the primary path; dashed arrows indicate overflow and eviction paths.
Redis database layout. Three logical databases within a single Redis instance, each with distinct scope, TTL, and data format.
Fig. 3: Redis database layout. Three logical databases within a single Redis instance, each with distinct scope, TTL, and data format.
Session lifecycle: new messages enter the Redis hot window. When the window exceeds k entries, the oldest overflow to Huffman-compressed disk archives. The LRU daemon migrates entire idle sessions. Returning users trigger re-hydration from disk.
Fig. 4: Session lifecycle: new messages enter the Redis hot window. When the window exceeds k entries, the oldest overflow to Huffman-compressed disk archives. The LRU daemon migrates entire idle sessions. Returning users trigger re-hydration from disk.
Binary layout of a .huff archive file. The 24-byte application header (orange) stores session metadata readable in a single read(24) call—critical for the TTL cleanup daemon. The Huffman codec header (blue) stores the symbol table needed for decompression. The compressed bitstream (green) contains the actual conversation JSON.
Fig. 5: Binary layout of a .huff archive file. The 24-byte application header (orange) stores session metadata readable in a single read(24) call—critical for the TTL cleanup daemon. The Huffman codec header (blue) stores the symbol table needed for decompression. The compressed bitstream (green) contains the actual conversation JSON.
Hybrid conversation cache internals. Left: write path—new messages are pushed to Redis; overflow beyond k is Huffman-compressed and appended to disk. Right: read path—if Redis is empty (post-eviction), the system re-hydrates from the disk archive transparently.
Fig. 6: Hybrid conversation cache internals. Left: write path—new messages are pushed to Redis; overflow beyond k is Huffman-compressed and appended to disk. Right: read path—if Redis is empty (post-eviction), the system re-hydrates from the disk archive transparently.

实验结果

评估为一次历史生产快照(6 天、114,547 条 Redis 命令):单台 8 vCPU Cascade Lake(无 GPU)、30 个 Hypercorn worker、Redis 7.4 限 2 GB。(1) 聚合键空间命中率 89.3%(2,182 命中/262 未命中),读延迟 0.1 ms,总内存 1.38 MB(96.4% 为数据);DB 0/1 均 0 键(TTL 如期过期),DB 2 有 16 键(平均 TTL 约 20 小时)。(2) 延迟(Fig. 8):Redis GET 0.1 ms,磁盘读 6 轮 3.0 ms、133 轮 107 ms,Huffman 编/解码 8.9/8.0 ms——内存快两个数量级,验证热窗口设计。(3) 压缩:5 个生产档案平均 67.2%(73,840 B→65.3%,2,314 B→68.8%),合成数据 >1 KB 约 44–47%;Huffman 全面优于 lz4(65.3% vs 47.2%,Table V),距 zlib-1(38.4%)差 5–10 个百分点。(4) 成本(Table II/Fig. 7):单查询约 $0.015($96/月基础设施+LLM 约 $0.014),低于 SearchGPT($0.03–0.10)与 Gemini Pro($0.035),与 Flash 持平,略高于 Sonar($0.005)。(5) 分层贡献(探索性):Layer 1 占 75–80% 命中;Layer 2 拦截 15–20% 会话内近重复查询、每次省 3–8 秒;Layer 3 热门 URL 见于 10–30% 会话、每次省约 200 ms。

Measurement-period per-query estimates (log scale). OreoLook includes amortized local infrastructure and provider inference; no cached-cost estimate is inferred from the Redis keyspace hit rate.
Fig. 7: Measurement-period per-query estimates (log scale). OreoLook includes amortized local infrastructure and provider inference; no cached-cost estimate is inferred from the Redis keyspace hit rate.
查看结构化数据
任务指标本文基线提升
缓存读取延迟 单键读延迟 (ms) 0.1 ms(Redis 热窗口) 磁盘 .huff 档案读:3.0 ms(6 轮)至 107 ms(133 轮) 比磁盘读快约 2–3 个数量级
单查询综合成本 USD/查询(含基础设施摊销) ≈$0.015(OreoLook) SearchGPT $0.03–0.10;Gemini Pro $0.035;Sonar Pro $0.018;Sonar $0.005 比 SearchGPT 低约 2–7 倍,比 Gemini Pro 低 2.3 倍
缓存有效性 Redis 键空间命中率 (%) 89.3%(2,182 命中 / 262 未命中,6 天窗口) 无缓存对照未测;该指标含内部操作,非查询级命中率 作为工作集驻留内存的指示器,非端到端收益证明
会话存档压缩 压缩比(越小越好,%) 生产档案平均 67.2%;合成数据 >1 KB 时约 44–47% lz4 47.2–72.1%;zlib-1 38.4–63.2% 小负载(<5 KB)优于 lz4、接近 zlib-1,且零原生依赖
Redis 内存占用 实例总内存 1.38 MB(96.4% 为真实数据) Redis 容器上限 2 GB 占用不到上限 0.1%,且随会话数保持 $O(k)$ 有界
URL 嵌入去重 单次 384 维嵌入计算耗时 缓存命中时节省 ≈200 ms/URL 无缓存:每个会话独立重复计算并丢弃 热门 URL(出现在 10–30% 会话)每日至多嵌入一次

局限与改进

作者承认的局限:(1) 评估是单一历史生产快照、单一硬件配置,命中率与延迟对查询分布和并发模式敏感,不能外推;(2) 89.3% 是 Redis 键空间命中率,混入 TTL 刷新、列表读、存在性检查等内部操作,不能换算成“多少比例查询绕过了推理”,请求级测量留作 future work;(3) 语义缓存用 $O(n)$、$n \le 50$ 的暴力余弦扫描,更大规模需向量索引;(4) 纯 Python 霍夫曼吞吐约 800 KB/s,MB 级负载需 C 扩展;(5) 磁盘档案只压缩不加密,敏感场景需叠加文件系统或应用层加密。我的补充观察:论文未报告 p95/p99 长尾延迟,30 个 worker 高并发下均值 0.1 ms 可能掩盖抖动;成本对比混用了不同答案质量档位(Sonar 基础档和 Flash 更便宜但质量可能更低);语义缓存按“会话+URL”作用域,跨会话相同问题无法复用 LLM 答案(仅嵌入层全局共享);15–20% 近重复率与 75–80% 分层贡献是键分布推断的探索性估计而非受控实验;全文没有答案质量评测——5 分钟 TTL 内命中的旧答案可能已过时。

独立分析的弱点

独立分析几个弱点。第一,语义缓存作用域是“会话 × URL”:不同用户问同一个热门问题(如“今天金价多少”)时,即使嵌入完全相同也不能互相复用 LLM 答案,只能共享 URL 嵌入——流量越大浪费越明显,改进方向是增加带脱敏与用户授权的全局语义缓存层。第二,每键一个最多 50 条的 JSON 文档,每次插入都要整体回写并刷新 TTL,热门会话+URL 组合会成为 Redis 热点键,可改用 hash 分片字段级写入。第三,回灌只恢复最近 $k=20$ 条,用户问“三天前你找的那篇文章”时仍需解压整个档案,论文未给出深历史查询的路径与延迟数据,可在冷档案上建嵌入索引支持跨全档案语义检索。第四,TTL 与阈值全部固定($\tau=0.90$、5 分钟):新闻类查询 5 分钟后答案可能已错,而“勾股定理”类本可缓存一周,应按查询类别自适应学习 TTL 与阈值。第五,89.3% 命中率缺乏消融实验(ablation),“Layer 1 占 75–80%”仅由键计数推断,工程决策所需的因果证据不足;霍夫曼追加写入在崩溃场景下的一致性也未讨论。

未来方向

作者提出的方向包括:为 MB 级大型档案做可选的原生 Huffman C 扩展;语义缓存接入 Qdrant 等可插拔向量存储以突破 $O(n)$ 暴力扫描;从观测到的查询模式学习自适应 TTL 策略;以及跨会话知识迁移——让一个对话中的洞见能反哺另一个对话的回答。请求级测量语义缓存命中率与避免的 provider token 是最直接的后续工作。基于本文成果还可延伸:把"作用域 × TTL × 格式"三层模式泛化为可复用的开源缓存中间件(作者已抽出 lix-open-cache 组件,可进一步产品化);在冷档案上建向量索引,实现"会话级长期记忆"让数百轮前的内容可被语义召回;对磁盘档案加每会话密钥的应用层加密(如 AES-GCM);研究多区域部署下 Redis 复制的语义缓存一致性与失效广播;引入 URL 内容变更信号(ETag/Last-Modified)做时效感知的缓存失效,替代纯时间 TTL;最后补充受控消融实验,量化三层各自的因果贡献。

复现评估

复现友好度较高但有缺口。有利条件:所有组件命名明确(答案引擎 OreoLook、可复用缓存实现 lix-open-cache、嵌入模型 sentence-transformers/all-MiniLM-L6-v2、Redis 7.4、Hypercorn、nginx);硬件门槛低,单台 8 vCPU/32 GB 云主机即可,本地 CPU 嵌入约 200 ms/URL,全程无需 GPU;架构、Redis 键格式、.huff 二进制布局、canonical 霍夫曼编解码伪代码(算法 1/2)均在论文中完整给出;关键参数全部披露($k=20$、$\tau=0.90$、50 对上限、5 分钟/24 小时 TTL、120 分钟空闲阈值、60 秒扫描、30 天清理)。缺口:正文未给出代码仓库链接和公开数据集,评估是历史生产快照而非可重跑基准;LLM 计价使用服务商内部货币“pollen”,外部读者难以复算成本;语义命中数、会话轨迹等原始指标未公开。综合判断:按论文重写功能等价系统的难度中等(工程量集中在霍夫曼编解码与混合缓存状态机),但精确复现 89.3% 命中率等数字不可能,因为负载不可复得。