LLM 网关与缓存
在 InterX 项目中,LLM 调用是系统的核心算力来源,也是成本和延迟的主要瓶颈。为了解决这一问题,我们设计了一套完整的 LLM 网关层,基于 LiteLLM 实现统一的多上游路由,并引入双层语义缓存机制,大幅提升系统响应速度。
为什么需要 LLM 网关
InterX 的 RAG 流水线中,一次完整的问答可能涉及多次 LLM 调用:查询改写、路由判断、三层并行回答、ensemble 融合。如果每次都直接请求上游 LLM 服务,会面临三个问题:
- 上游服务不稳定 — 单一 LLM 服务故障时整个系统不可用
- 成本高昂 — 高频重复问题反复调用 LLM 是浪费
- 延迟过高 — 多层 LLM 调用叠加,端到端延迟可达数十秒
因此,我们需要一个统一的 LLM 网关来解决这些问题。
LiteLLM 作为统一网关
我们选择 LiteLLM Proxy 作为网关层,运行在 :4000 端口。它提供了统一的 OpenAI 兼容接口,屏蔽了不同上游 LLM 服务的差异。
多上游路由
系统配置了三个上游 LLM 服务:
- MiMo — 主力推理模型
- DashScope — 通义千问系列,承担部分生成和 embedding 任务
- GPT-5.5 — 作为备用和特定任务的模型
网关采用 simple-shuffle 轮转策略,自动在多个上游之间分配请求。每个上游配置独立的 RPM/TPM 限流,避免某个上游过载。
故障转移机制
当某个上游服务出问题时,网关会自动处理:
- 失败 1-3 次后标记该端点为不健康状态,冷却 15-30 秒
- 冷却期间自动将请求路由到其他健康端点
drop_params: true自动丢弃上游不支持的参数,避免兼容性错误
上游评分与探测
网关会对每个上游进行评分:成功 100 分 + 延迟奖励(0-20 分)+ HTTP 状态分。通过 12 轮探测验证路由均匀性,冒烟测试不仅检查 HTTP 状态码,还会验证模型是否实际生成了有意义的响应。
双层语义缓存
这是网关层最核心的设计。在产品客服场景中,用户经常会用不同措辞问同一个问题,比如"怎么开机"和"如何启动"本质上是同一个问题。如果我们每次都调用 LLM,就太浪费了。
第一层:SHA-256 精确匹配(~1ms)
当请求到达网关时,首先进行精确匹配:
json.dumps(payload, sort_keys=True) → SHA-256 → Redis GET将请求的 payload 序列化为 JSON 字符串,计算 SHA-256 哈希值,然后到 Redis 中查找。如果命中,直接返回缓存结果,整个过程大约只需要 1ms。
这一层解决的是完全相同请求的重复调用问题,比如用户刷新页面、重试请求等场景。
第二层:LLM 语义等价判断(~500ms)
如果精确匹配没有命中,就进入第二层语义匹配:
- 提取最近 6 条消息的摘要(最多 4000 字符)
- 从 Redis 的候选列表中取出最多 5 个历史请求
- 使用轻量级 LLM(gpt-4o-mini)判断当前请求与候选是否语义等价
- 命中条件:
hit=true且confidence ≥ 0.92
这一层用一个小型 LLM 来判断"怎么开机"和"如何启动"是否等价,虽然耗时约 500ms,但相比重新调用主力 LLM 生成回答(通常需要数秒),仍然快很多。
缓存策略
- TTL:精确缓存 1 小时过期
- 候选列表:保留最近 50 条历史请求
- 写入策略:未命中时,结果同时写入精确缓存和候选列表
缓存命中率
在验证测试中,缓存表现如下:
| 缓存层级 | 测试数 | 准确率 |
|---|---|---|
| 精确匹配(SHA-256) | 6 | 100% |
| 语义匹配(LLM 判断) | 6 | 67% |
精确匹配零误判;语义匹配对同义改写有效(confidence 达到 0.98),但在跨语言场景下存在一些局限。总体来说,在产品客服这种高频重复问题的场景下,缓存机制能显著降低 API 调用成本和延迟。
Prometheus 监控集成
为了实时掌握网关的运行状态,我们集成了 Prometheus 监控,通过 6 个指标精确区分两种缓存的命中情况。这些指标让我们能够清楚地看到:
- 精确匹配的命中率和延迟
- 语义匹配的命中率和延迟
- 上游各服务的响应时间和错误率
- 缓存未命中时的回源调用情况
故障容忍与优雅降级
网关层作为所有 LLM 调用的入口,自身的健壮性至关重要。我们设计了多级容错机制:
- 上游故障 — 自动轮转到其他健康端点
- 网关层故障 — 上层调用直接回退到默认 LLM 端点
- 缓存故障 — Redis 不可用时自动降级为无缓存模式,所有请求直接回源
结合整个系统的容错体系:
Dense 检索故障 → 静默降级到纯 BM25
Rerank 故障 → 静默保留融合排序
KG 扩展故障 → 跳过,不影响主流程
路由失败 → 默认走 RAG(保守策略)
回答重试 → 指数退避(1s → 2s),json_object 降级系统在最极端的故障场景(Dense + Rerank 同时失败)下仍能返回 10 个有效检索结果,12/12 降级测试场景全部通过。
加速路径优先级
整个系统的加速策略按优先级排列:
| 优先级 | 路径 | 延迟 | 机制 |
|---|---|---|---|
| 1 | 语义缓存精确匹配 | ~1ms | SHA-256 → Redis |
| 2 | 语义缓存语义匹配 | ~500ms-2s | 轻量 LLM 等价判断 |
| 3 | 三级并行回答 | ~3-5x 提速 | ThreadPoolExecutor(3) |
| 4 | 模块级缓存 | 首次加载后零开销 | BM25 索引 + 语料驻留内存 |
优先级 1 和 2 直接在网关层拦截,避免了下游整个 RAG 流水线的执行。这种分层加速的设计,让高频问题的响应时间从数十秒降到毫秒级。
小结
LLM 网关层的设计思路其实很简单:能不调用 LLM 就不调用(缓存),必须调用时选择最优路径(路由),出了问题自动切换(容错)。这三层机制叠加在一起,让 InterX 在保持高质量回答的同时,将延迟和成本控制在可接受的范围内。