我在做法律合同智能审查系统时,遇到过一个棘手的问题:单份合同平均 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 的合同做了对照测试:

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 缓存键设计要点

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 网关出口)

4.2 价格对比(2026 年 1 月,output / MTok)

模型官方 $/MTokHolySheep ¥/MTok10M 输出/月成本
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 社区口碑

5. 性能调优与并发控制

我把踩过的坑总结成四条生产级建议:

# 并发限流 + 降级示例
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,获取首月赠额度