Sulci 发布语义缓存并公开尾部指标
Sulci 1.0 是采用 Apache 2.0 许可的上下文感知语义缓存,可复用语义相近请求的回答。作者公开可复现基准:冷缓存命中率为 33.4%,同会话热缓存为 99.7%,并披露尾部会话的模型调用量。
AI 深度解读
Sulci 1.0 想解决的是 Agent 反复为相近问题调用模型的浪费:把语义相同的请求识别出来,直接返回已有答案。作者测了 50 个会话,每个会话 200 次 LLM 调用分发,发现其中存在大量重复。
但语义缓存最难的不是多命中几次,而是别把「看起来相似」误判成「可以共用答案」。这篇发布说明值得读的地方,是同时谈到了缓存收益、上下文隔离和测试边界。不过,标题强调的错误命中问题,在提供的正文里仍缺少关键结果。
缓存能省多少,先看请求到底有多重复
普通缓存通常要求键完全一致;语义缓存先把问题转成向量,再判断它是否接近已有问题,因此不同措辞也可能命中。
Agent 尤其容易产生这类请求。作者观察到,循环会重复推导中间事实,规划器每一步重新概括上下文,重试则把失败的问题稍作改写。模型调用次数因此未必对应实际完成的工作量。
在作者的 Agent 工作负载里:
- 冷启动、缓存为空时,33.4%[30.00–35.00%]的请求可以由缓存服务。
- 同一会话内缓存预热后,这个比例达到 99.7%[99.69–99.81%]。
- 中位数会话的 200 次请求中,仅 2 次发给模型;p95 会话仍有 53 次[46–64],主要来自冷请求。
这些数字说明这套负载很重复,但不能直接换算成你的账单节省比例。原文也没有明确解释冷、热指标的统计口径如何衔接,不宜把它们拼成一条收益曲线。
作者没有给单次查询成本或加速倍数,因为它们还取决于模型、Token 数量和业务流量。这一克制是合理的。
命中率越高,为什么还可能越危险
语义缓存可能把两个相似却答案不同的问题合并,随后快速、稳定地返回错误答案。只追求命中率,甚至可以通过「所有请求都返回缓存」做到 100%。
缓存的收益必须和错误复用的代价一起评估。
作者为此让测试集中 39% 的查询在缓存里没有正确答案,再用默认 MiniLM 嵌入模型、0.85 阈值进行四次固定种子的抽样测试。
问题是:提供的正文没有列出这部分测试的错误命中率、正确拒绝率等结果。它强调会公开这些数字,却没有给读者足够的数据来判断实际表现。正文还出现了 87ms,称耗时几乎都在生成嵌入向量,而非检索,但没有明确标注这是哪个延迟统计口径。
作者给了复现入口。下面这组命令用于运行仓库中的一次基准测试,基准脚本不在 PyPI 包里:
git clone https://github.com/sulci-io/sulci-oss && cd sulci-oss
pip install -e ".[sqlite]"
python benchmark/run.py --use-sulci --fresh --no-sweep --agent --seed 1 --out /tmp/sulci-bench
要注意 --seed 1,作者要求再分别用 --seed 2、--seed 3 和 --seed 42 复现。固定种子方便比较,换到自己的硬件和请求样本,才能回答实际部署的问题。
理解上下文,不等于可以跨会话共享答案
Sulci 接收 session_id 后,会把当前问题与最近的对话混合成检索向量:当前问题占 70%,衰减后的历史占 30%。因此,同一句「怎么修复它」,放在 Docker 崩溃和账单错误之后,会搜索不同的位置。
但改变搜索方向,并不能保证检索出的答案适合当前会话。尤其是依赖前文才能理解的追问,存成共享答案后仍可能污染其他会话。
托管版因此增加两道限制:混合上下文的检索阈值为 0.80,普通检索为 0.65;只在当前对话中成立的追问不存为共享答案,API 返回 stored: false, reason: "context_dependent"。这两个阈值属于托管版规则,不能与前面基准测试的 0.85 混为一谈。
托管版默认启用上下文能力;库里需要通过 context_window 和 session_id 主动开启,再调整 query_weight、context_decay、context_threshold。
更关键的是,作者承认:在他们的语料和默认 MiniLM 上,上下文混合没有带来可测量的答案解析准确率提升。此前较大的提升来自对 TF-IDF 基线的比较,相关数字已经撤回。功能存在,不代表收益已经被证明。
接入成本不高,数据流向要分清
安装命令是 pip install "sulci[sqlite]"。下面这段代码展示了最基本的接法:先查缓存,未命中才调用模型并写回。
from sulci import Cache
cache = Cache(backend="sqlite", threshold=0.85)
response, similarity, context_depth = cache.get("How do I rotate an API key?")
if response is None:
response = call_your_model(...)
cache.set("How do I rotate an API key?", response)
注意 if response is None:只有缓存没有返回答案时才调用模型。threshold= 还支持逐次调用覆盖,让不同严格程度的路径共用一个缓存。
存储从 SQLite 起步,也支持 Chroma、Qdrant、FAISS、Redis 和 Milvus,其中 Redis 不需要额外模块。session_id 可以贯穿库、LlamaIndex、LiteLLM、代理、MCP server、TypeScript client 和 HTTP API;代理使用 x-sulci-session-id 请求头。从 1.0.0 起,docs/API-SURFACE.md 定义的公共 API 遵循 Semantic Versioning,承诺 1.x 内不做破坏性变更。
托管服务有两种数据边界。自托管后端接免费仪表盘,只上传固定白名单中的八项运维字段,查询、响应和嵌入留在原处。使用 Cache(backend="sulci", api_key="sk-sulci-...") 托管缓存,则会上传查询文本和缓存响应。
原文列出的 OSS-Connect 免费额度为每月 10,000 次托管请求,无需绑卡;Pro 为 $29/月,Business 为 $149/月,试用限制为 14 天或 50k 次请求。项目采用 Apache 2.0,同时披露了待审美国专利申请 No. 64/018,452,当前归属 Kathiravan Sengodan,并说明 LICENSE 中的 Apache-2.0 专利授权适用于相应开源贡献。
我的判断
我认可作者把尾部会话开销、错误复用风险和撤回的对照结果摆出来。尤其是 p95 仍需 53 次模型调用,比单独展示 99.7% 更有参考价值。
但这仍是一组作者自建负载上的实验。它没有证明一般团队的 Agent 都有同样高的重复率,也没有在提供的正文中交代错误命中的具体表现。
与精确键缓存相比,语义缓存能覆盖改写请求,同时引入相似度误判;上下文混合又增加了调参和共享边界问题。若重复请求本来就能通过稳定参数识别,精确缓存更容易验证。Sulci 更值得尝试的地方,是确实存在大量自然语言近重复请求的链路。
能拿来做什么
- 先记录日常 AI 编程或 Agent 的请求,检查规划、摘要和重试是否反复问同一件事,再决定是否接入语义缓存。
- 给评测集加入「措辞相近但答案不同」以及「缓存里没有正确答案」的请求,同时统计错误复用和正确拒绝,别只看命中率。
- 接入多轮对话时保留
session_id,重点检查依赖前文的追问是否会被共享;不要把向量混合当成会话隔离。 - 在自己的硬件上测量嵌入耗时,并按真实模型和 Token 用量计算收益,再选择自托管或托管缓存。