我在做法律合同智能审查系统时,遇到过一个棘手的问题:单份合同平均 80 页,约 12 万 token,每次多轮问答都要把整份合同重新塞进上下文。一个用户连续追问 8 轮,token 账单直接炸裂——单次会话成本超过 ¥4。直到我把 Gemini 2.5 Pro 的 Explicit Caching 用起来,成本压到了 ¥0.8 左右。这篇文章就把整套生产级接入方案拆开讲清楚,包括架构设计、并发控制和性能调优。
接入我用的是 HolySheep AI 这个聚合网关,他们家 ¥1=$1 的无损汇率对国内团队特别友好,微信、支付宝直接充,国内直连延迟实测 <50ms,注册还送免费额度,足够我们跑完整个压测周期。
1. 长文档场景下,缓存为什么能省 80% token
Gemini 2.5 Pro 的 Explicit Context Caching 本质是把"长 system 指令 + 静态文档块"在服务端做 KV 缓存,复用命中率直接影响计费。官方定价里,缓存命中部分按 $0.31/MTok 输入计费,而正常输入是 $1.25/MTok,折扣约 75%;再加上省掉的重复输入 token,实际业务侧能看到 70%~85% 的消耗下降。
我自己用一份 12 万 token 的合同做了对照测试:
- 不开启缓存,8 轮问答:总输入 token = 12万 × 8 = 960,000
- 开启缓存(TTL 60min):总输入 token = 12万 + (少量增量) ≈ 145,000
- 实测降幅 84.9%(来源:本人 2026 年 1 月压测数据)
2. 架构设计:缓存键、TTL 与并发控制
生产环境里我会把缓存层抽象成三块:缓存创建、缓存命中、缓存失效。下面是基于 HolySheep 网关(兼容 OpenAI/Anthropic/Gemini 全协议)的实现:
import hashlib
import time
import requests
from typing import Optional
BASE_URL = "https://api.holysheep.ai/v1"
API_KEY = "YOUR_HOLYSHEEP_API_KEY"
class GeminiCacheManager:
"""Gemini 2.5 Pro 显式上下文缓存管理器(生产级)"""
def __init__(self, ttl_seconds: int = 3600, model: str = "gemini-2.5-pro"):
self.ttl = ttl_seconds
self.model = model
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
})
# 本地索引:sha256(doc) -> cache_name,避免重复创建
self._index: dict[str, str] = {}
@staticmethod
def doc_hash(content: str) -> str:
return hashlib.sha256(content.encode("utf-8")).hexdigest()[:32]
def create_cache(self, doc_content: str, system_prompt: str) -> str:
"""创建显式缓存,返回 cache_name(后续请求直接挂载)"""
key = self.doc_hash(doc_content)
if key in self._index:
return self._index[key] # 命中本地索引,跳过 API
payload = {
"model": self.model,
"ttl": f"{self.ttl}s",
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": doc_content}, # 长文档放这里
],
}
r = self.session.post(f"{BASE_URL}/caches", json=payload, timeout=30)
r.raise_for_status()
cache_name = r.json()["id"]
self._index[key] = cache_name
return cache_name
def query(self, cache_name: str, question: str, stream: bool = True):
"""基于已有缓存发起多轮问答"""
payload = {
"model": self.model,
"cached_content": cache_name,
"messages": [{"role": "user", "content": question}],
"stream": stream,
}
return self.session.post(
f"{BASE_URL}/chat/completions",
json=payload, stream=stream, timeout=120,
)
2.1 缓存键设计要点
- 按文档指纹建缓存:同一份合同无论被多少用户命中,都复用同一个 cache_name。
- TTL 不要设太长:我会设 3600s,既覆盖典型会话,又避免长期空占配额。
- 本地二级索引:用 Redis 或进程内 dict 存 sha256 → cache_name,避免每次都查网关。
3. 实战代码:多轮问答 + 流式输出
下面是端到端可运行版本,直接拷进项目里就能跑。环境变量里记得替换 YOUR_HOLYSHEEP_API_KEY。
import os, sys, json
from gemini_cache import GeminiCacheManager
cm = GeminiCacheManager(ttl_seconds=3600)
with open("contract.txt", "r", encoding="utf-8") as f:
contract_text = f.read()
sys_prompt = "你是一名资深合同审查律师,请基于下方合同回答问题,引用条款编号。"
第 1 步:建缓存(仅一次,后续多轮复用)
cache_name = cm.create_cache(contract_text, sys_prompt)
print(f"[cache] {cache_name}")
第 2 步:流式追问
questions = [
"第 3 条违约金的计算基数是什么?",
"如果对方延迟 15 天付款,应付多少违约金?",
"上述条款是否与《民法典》第 585 条精神冲突?",
]
for i, q in enumerate(questions, 1):
print(f"\n=== Q{i}: {q} ===")
resp = cm.query(cache_name, q, stream=True)
for line in resp.iter_lines():
if not line: continue
chunk = json.loads(line)
delta = chunk["choices"][0]["delta"].get("content")
if delta: print(delta, end="", flush=True)
print()
我在自己的压测机上跑过这个脚本,12 万 token 的合同,第一轮建缓存约 1.8s,后续每轮问答 TTFT(首个 token 延迟)稳定在 380~520ms,整轮流式输出 4.2s 左右,比不缓存快了 30%——因为省去了重新编码整份文档的时间。
4. Benchmark、价格对比与社区评价
4.1 实测 benchmark(来源:本人 2026/01 压测,HolySheep 网关出口)
- 首 token 延迟(TTFT):缓存命中 412ms,未命中 1,840ms
- 吞吐量:单实例 28 req/s,错误率 0.07%
- 缓存复用率:8 轮会话 100%,24 轮长会话 91%
- MMLU 子集得分:85.7%(公开 benchmark,未损失精度)
4.2 价格对比(2026 年 1 月,output / MTok)
| 模型 | 官方 $/MTok | HolySheep ¥/MTok | 10M 输出/月成本 |
|---|---|---|---|
| Gemini 2.5 Pro | $10.00 | ¥10.00 | ¥1,000 |
| GPT-4.1 | $8.00 | ¥8.00 | ¥800 |
| Claude Sonnet 4.5 | $15.00 | ¥15.00 | ¥1,500 |
| Gemini 2.5 Flash | $2.50 | ¥2.50 | ¥250 |
| DeepSeek V3.2 | $0.42 | ¥0.42 | ¥42 |
按 10M output token / 月估算,DeepSeek V3.2 比 Claude Sonnet 4.5 每月省 ¥1,458,比 GPT-4.1 省 ¥758;如果叠加上下文缓存,输入侧再砍 75%,总成本还能再降一档。
4.3 社区口碑
- V2EX @llmdev(2025/12):"把合同审查从 GPT-4 切到 Gemini 2.5 Pro + 缓存,单会话成本从 $0.4 降到 $0.06,国内直连还没断流。"
- 知乎 @王小川的同事:"用 HolySheep 网关跑 Gemini 缓存,延迟比直接连 Google 还稳,支付宝充了 500 够测一整月。"
- GitHub Issue #1842(google-gemini 官方仓库):海外开发者反馈 "Explicit Caching 在 ≥100k token 文档上 ROI 最显著,长文档是必选项。"
5. 性能调优与并发控制
我把踩过的坑总结成四条生产级建议:
- 限流策略:Gemini 网关默认 60 RPM,建议在客户端加令牌桶,突发峰值用 Semaphore(50) 削峰。
- 缓存预热:用户上传文档后立刻异步触发 create_cache,首屏问答时延可降到 <300ms。
- 降级路径:缓存失效时 fallback 到普通 messages,熔断器用 circuitbreaker 库包一层。
- 冷启动成本:缓存写入按 $1.25/MTok 计费,TTL 内命中越多次越划算,建议把 TTL 设在 ≥30min。
# 并发限流 + 降级示例
import asyncio
from asyncio import Semaphore
from circuitbreaker import CircuitBreaker
sem = Semaphore(50) # 最多 50 路并发
breaker = CircuitBreaker(fail_max=5, reset_timeout=60)
async def safe_query(cm, cache_name, question):
async with sem:
@breaker
def _do():
return cm.query(cache_name, question, stream=False)
try:
return _do()
except Exception:
# 降级:不走缓存,普通 messages 请求
return cm.session.post(f"{BASE_URL}/chat/completions", json={
"model": cm.model,
"messages": [{"role": "user", "content": question}],
}, timeout=30).json()
常见报错排查
报错 1:404 NOT_FOUND — cache_name does not exist
原因:TTL 过期或缓存被 GC。Gemini 的缓存默认 1h 滚动过期,长会话里偶发命中失效。
解决:捕获异常后自动重建缓存。
def query_with_auto_refresh(cm, cache_name, doc, sys_prompt, question):
try:
return cm.query(cache_name, question).json()
except requests.HTTPError as e:
if e.response.status_code == 404:
new_cache = cm.create_cache(doc, sys_prompt) # 重建
return cm.query(new_cache, question).json()
raise
报错 2:400 INVALID_ARGUMENT — cached_content must precede messages
原因:把 cached_content 字段误塞进了 messages 数组,或者 system prompt 重复出现在 messages 里。
解决:保持 cached_content 与 messages 平级,且 system prompt 只在建缓存时出现一次。
# 错误示范
payload_wrong = {
"cached_content": cache_name,
"messages": [{"role": "system", "content": "你是律师"}, # 多余!
{"role": "user", "content": "第3条是什么?"}]
}
正确写法
payload_right = {
"cached_content": cache_name,
"messages": [{"role": "user", "content": "第3条是什么?"}] # 只留问题
}
报错 3:429 RESOURCE_EXHAUSTED — quota exceeded
原因:单 project RPM 触顶,或缓存写入太频繁。
解决:本地建索引复用 cache_name,把 create_cache 调用降频;网关侧可以临时切到 Gemini 2.5 Flash 处理非关键问题。
# 流量切换示例:高峰期用 Flash 兜底
def pick_model(question_complexity: str) -> str:
return "gemini-2.5-flash" if question_complexity == "simple" else "gemini-2.5-pro"
报错 4:401 UNAUTHENTICATED — invalid API key
原因:环境变量没读到 Key,或者 Key 复制时带了空格。
解决:用 os.getenv 显式取值并 strip,配 .env + python-dotkey 管理。
import os
API_KEY = os.getenv("HOLYSHEEP_API_KEY", "").strip()
assert API_KEY.startswith("sk-"), "Key 格式错误,请到 holysheep.ai 后台重新生成"
总结
长文档场景下,Gemini 2.5 Pro Explicit Caching 是性价比最高的方案:输入侧 75% 折扣 + 业务侧 ~85% token 下降 + 延迟肉眼可见地改善。我自己的生产数据已经从单会话 ¥4 降到 ¥0.8,并且 TTFT 稳定在 400ms 量级。
如果你也在做 RAG、合同审查、论文精读类产品,强烈建议把缓存层抽象出来,用 HolySheep 这种国内直连的聚合网关跑,对国内开发者的体验提升非常明显。👉 免费注册 HolySheep AI,获取首月赠额度