PVLDB 2025 · Semantic Cache · Multi-Turn LLM Queries

ContextCache:智能问数缓存不能只看“当前问题”,还要看“上下文”

这篇论文研究 LLM 多轮对话里的语义缓存。它的核心提醒很适合智能问数:两个问题字面很像,不代表可以复用同一个答案;缓存命中必须同时匹配当前问题和历史对话上下文。

论文:ContextCache · PVLDB 18(12), 2025 主题:上下文感知语义缓存

核心结论

做了什么

ContextCache 做了一个多轮 LLM 查询缓存系统。它不是简单缓存“问题-答案”,而是缓存“问题、答案、问题向量、上下文向量”,下次用户追问时先判断是否能安全复用历史回答。

为什么重要

智能问数经常是连续追问。同一句“那去年呢?”、“它的明细呢?”在不同对话里含义完全不同。只按当前问题向量查缓存,容易把别的上下文答案错拿过来。

最适合借鉴点

缓存命中分两层:先用当前问题向量粗召回,再用当前问题 + 历史对话的上下文表示精匹配。这样兼顾速度和准确性。

一句话版:智能问数缓存不要只问“这个问题以前问过吗?”,还要问“这个问题在当前对话语境下,和以前那次是不是同一个意思?”。

它解决的问题

传统缓存很容易理解:完全一样的查询来了,就复用旧结果。LLM 场景更复杂,因为用户可能换一种说法问同一个问题,也可能用同一句话表达不同上下文里的问题。

semantic cache idea
图 1:语义缓存的基本想法。没有缓存时,每次都调用 LLM;有缓存时,语义相似的问题可以直接复用历史回答,节省时间和成本。

正确命中

缓存里有“什么是关系型数据库?”。用户问“解释一下 relational database 的重要特征”。字面不同,但意思接近,可以命中缓存。

错误命中

用户在 A 对话里问“它的关键特征是什么?”,这里的“它”指关系型数据库;在 B 对话里同样问“它的关键特征是什么?”,但“它”可能指云计算。当前问题很像,答案不能复用。

对智能问数的翻译:“按学院统计一下”如果上一轮说的是科研经费,下一轮说的是学生人数,缓存命中逻辑必须知道“按学院”前面指的是什么指标。

方法:两阶段上下文语义缓存

ContextCache architecture
图 2:ContextCache 架构。先拦截用户请求,构造当前问题和历史上下文,再进行两阶段检索:粗召回候选缓存,精判断是否真的命中。
1

拦截 Query

用户问题先不直接发给 LLM,而是进入缓存层。缓存层先判断:有没有可复用的历史回答。

2

收集上下文

拿到当前问题,也拿到历史对话,例如上一轮用户问了什么、系统答了什么、当前指代对象是什么。

3

两阶段检索

第一阶段只用当前问题向量快速找候选;第二阶段用上下文表示做精匹配,防止相似问题误命中。

4

命中或更新

如果语义和上下文都匹配,返回缓存回答;否则调用 LLM,并把新问题、新答案、新向量写入缓存。

两个阈值

粗召回阶段先找当前问题相似的缓存项:

$$C_1 = \{c \mid cos(v_Q, v_c) > \theta_1\}$$

精匹配阶段再比较当前上下文表示和候选缓存的上下文表示:

$$S_c = cos(g_{current}, g_c)$$

只有当 \(S_c\) 超过第二个阈值 \(\theta_2\),才真正判定 cache hit。

它怎么实现缓存

论文实现上增强了 GPTCache,并使用了双存储结构:关系数据库存文本和元数据,向量库负责快速相似检索。

模块保存/处理什么在智能问数里的对应物
Query preprocessing标准化当前问题,并结合历史对话问题改写、指标补全、指代消解
Embedding generator生成当前问题向量,复用历史轮次向量用户问题、SQL 意图、字段意图向量
Coarse retrieval用当前问题向量从缓存中找候选先找可能可复用的历史问数请求
Context modeling用 self-attention 结合历史对话,生成上下文表示识别“它”“这个指标”“去年”等依赖上一轮的含义
Cache updateLLM 新生成的回答写入关系库和向量库保存 NL 问题、SQL、结果摘要、字段、权限、数据版本
{
  "cache_key": "semantic_context_key",
  "current_question": "那去年呢?",
  "resolved_question": "查询上一轮指标在 2025 年的结果",
  "history_context": [
    "上一轮用户问:统计各学院 2026 年科研经费",
    "系统使用字段:department_name, fund_amount, year"
  ],
  "query_vector": "...",
  "context_vector": "...",
  "cached_payload": {
    "sql": "SELECT department_name, SUM(fund_amount) ... WHERE year = 2025",
    "result_summary": "各学院 2025 年科研经费汇总",
    "data_version": "research_dw@2026-07-09",
    "permission_scope": "user_role:research_admin"
  }
}

实验结果

+10.9%

Precision 提升

论文报告 ContextCache 相比 GPTCache 提高了缓存命中精度,减少错误命中。

+14.8%

Recall 提升

它也能找回更多本该命中的缓存,不只是变保守。

约 10x

延迟降低

缓存返回相比直接调用 LLM 约低一个数量级的延迟。

performance metrics
图 3:ContextCache 与 GPTCache 的指标对比。ContextCache 同时提升 Precision 和 Recall,并在平均 cache hit 时间上略优。
user interface
图 4:系统演示界面。左侧是缓存数据库,右侧是聊天界面,可以看到 Cache Hit / Cache Miss、tokens 和耗时。
dashboard
图 5:演示仪表盘。展示 True Hit、False Hit、LLM API Calls、总查询量、缓存大小和最近请求响应时间。

放到智能问数项目里怎么用

如果你的系统是“用户自然语言问题 → skill 解析 → MCP 查库 → 返回答案”,缓存不应该只缓存最终文本答案。更稳的缓存对象应该覆盖意图、schema linking、SQL、结果摘要和数据版本。

缓存层适合缓存什么命中条件失效条件
问题理解缓存意图、指标、维度、过滤条件当前问题和上下文语义一致业务词典/指标口径变更
Schema linking 缓存目标库、表、字段、join key问题意图一致且数据权限一致schema 变更、字段注释变更、权限变更
SQL 缓存生成的 SQL 或查询计划参数、时间范围、数据源版本一致数据源版本更新、SQL 方言变更
结果缓存查询结果、图表数据、摘要SQL、参数、数据版本完全一致底表数据刷新、权限变化、TTL 到期
建议实现:先做语义候选召回,再做上下文和安全条件校验。智能问数里第二阶段最好额外检查:用户权限、数据版本、SQL 参数、指标口径、时间范围。
不要只按问题文本缓存:“按学院汇总一下”这种问题必须结合上一轮指标。否则科研经费、学生人数、资产金额都可能错误复用同一条缓存。

评价

创新点

它把语义缓存从单轮问题扩展到多轮上下文,提出“两阶段检索 + 上下文表示 + 双存储”的工程结构,特别适合连续问答式系统。

局限

论文更偏 LLM 对话缓存演示,尚未完整覆盖数据库问数里的数据新鲜度、权限隔离、SQL 结果一致性和指标口径治理。

我的判断:ContextCache 不是智能问数缓存的全部答案,但它非常适合作为“语义缓存命中判断”的参考架构。实际项目里还需要把它和数据版本、权限、schema 变更、指标口径一起组合。

资料来源

官方 PDF:https://www.vldb.org/pvldb/vol18/p5391-yan.pdf

DOI 页面:10.14778/3750601.3750679

代码仓库:https://github.com/uYanJX/ContextCache

本页面根据 VLDB 官方 PDF 本地抽取正文和图表生成。公式使用 MathJax,首次打开需要能访问 MathJax CDN。