ContextCache 论文解读:智能问数里的上下文感知语义缓存
ContextCache:智能问数缓存不能只看“当前问题”,还要看“上下文”
这篇论文研究 LLM 多轮对话里的语义缓存。它的核心提醒很适合智能问数:两个问题字面很像,不代表可以复用同一个答案;缓存命中必须同时匹配当前问题和历史对话上下文。
核心结论
做了什么
ContextCache 做了一个多轮 LLM 查询缓存系统。它不是简单缓存“问题-答案”,而是缓存“问题、答案、问题向量、上下文向量”,下次用户追问时先判断是否能安全复用历史回答。
为什么重要
智能问数经常是连续追问。同一句“那去年呢?”、“它的明细呢?”在不同对话里含义完全不同。只按当前问题向量查缓存,容易把别的上下文答案错拿过来。
最适合借鉴点
缓存命中分两层:先用当前问题向量粗召回,再用当前问题 + 历史对话的上下文表示精匹配。这样兼顾速度和准确性。
它解决的问题
传统缓存很容易理解:完全一样的查询来了,就复用旧结果。LLM 场景更复杂,因为用户可能换一种说法问同一个问题,也可能用同一句话表达不同上下文里的问题。
正确命中
缓存里有“什么是关系型数据库?”。用户问“解释一下 relational database 的重要特征”。字面不同,但意思接近,可以命中缓存。
错误命中
用户在 A 对话里问“它的关键特征是什么?”,这里的“它”指关系型数据库;在 B 对话里同样问“它的关键特征是什么?”,但“它”可能指云计算。当前问题很像,答案不能复用。
方法:两阶段上下文语义缓存
拦截 Query
用户问题先不直接发给 LLM,而是进入缓存层。缓存层先判断:有没有可复用的历史回答。
收集上下文
拿到当前问题,也拿到历史对话,例如上一轮用户问了什么、系统答了什么、当前指代对象是什么。
两阶段检索
第一阶段只用当前问题向量快速找候选;第二阶段用上下文表示做精匹配,防止相似问题误命中。
命中或更新
如果语义和上下文都匹配,返回缓存回答;否则调用 LLM,并把新问题、新答案、新向量写入缓存。
两个阈值
粗召回阶段先找当前问题相似的缓存项:
精匹配阶段再比较当前上下文表示和候选缓存的上下文表示:
只有当 \(S_c\) 超过第二个阈值 \(\theta_2\),才真正判定 cache hit。
它怎么实现缓存
论文实现上增强了 GPTCache,并使用了双存储结构:关系数据库存文本和元数据,向量库负责快速相似检索。
| 模块 | 保存/处理什么 | 在智能问数里的对应物 |
|---|---|---|
| Query preprocessing | 标准化当前问题,并结合历史对话 | 问题改写、指标补全、指代消解 |
| Embedding generator | 生成当前问题向量,复用历史轮次向量 | 用户问题、SQL 意图、字段意图向量 |
| Coarse retrieval | 用当前问题向量从缓存中找候选 | 先找可能可复用的历史问数请求 |
| Context modeling | 用 self-attention 结合历史对话,生成上下文表示 | 识别“它”“这个指标”“去年”等依赖上一轮的含义 |
| Cache update | LLM 新生成的回答写入关系库和向量库 | 保存 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"
}
}
实验结果
Precision 提升
论文报告 ContextCache 相比 GPTCache 提高了缓存命中精度,减少错误命中。
Recall 提升
它也能找回更多本该命中的缓存,不只是变保守。
延迟降低
缓存返回相比直接调用 LLM 约低一个数量级的延迟。
放到智能问数项目里怎么用
如果你的系统是“用户自然语言问题 → skill 解析 → MCP 查库 → 返回答案”,缓存不应该只缓存最终文本答案。更稳的缓存对象应该覆盖意图、schema linking、SQL、结果摘要和数据版本。
| 缓存层 | 适合缓存什么 | 命中条件 | 失效条件 |
|---|---|---|---|
| 问题理解缓存 | 意图、指标、维度、过滤条件 | 当前问题和上下文语义一致 | 业务词典/指标口径变更 |
| Schema linking 缓存 | 目标库、表、字段、join key | 问题意图一致且数据权限一致 | schema 变更、字段注释变更、权限变更 |
| SQL 缓存 | 生成的 SQL 或查询计划 | 参数、时间范围、数据源版本一致 | 数据源版本更新、SQL 方言变更 |
| 结果缓存 | 查询结果、图表数据、摘要 | SQL、参数、数据版本完全一致 | 底表数据刷新、权限变化、TTL 到期 |
评价
创新点
它把语义缓存从单轮问题扩展到多轮上下文,提出“两阶段检索 + 上下文表示 + 双存储”的工程结构,特别适合连续问答式系统。
局限
论文更偏 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。
