缓存、流控、迁移与治理
缓存身份包含模型与权限
上下文缓存保存可复用的输入材料,后续请求引用缓存名称;它与应用对最终答案做 memoization 不同。创建缓存时模型、system instruction、内容和 TTL 都参与契约,查询缓存对象确认过期时间,不能认为文件上传后就自动永久缓存。
沿用第一章环境,以下 cache.py 是真实 SDK 路径。将有权处理且满足所选模型缓存长度要求的材料放在 document.txt。短文件可能被平台拒绝,这属于能力前提,不应吞掉错误。
import os
from pathlib import Path
from google import genai
from google.genai import types
with genai.Client(api_key=os.environ['GEMINI_API_KEY']) as c:
cache=c.caches.create(model=os.environ['GEMINI_MODEL'],
config=types.CreateCachedContentConfig(
contents=[Path('document.txt').read_text()],ttl='300s'))
try:
r=c.models.generate_content(model=os.environ['GEMINI_MODEL'],
contents='归纳材料的三个可核对事实',
config=types.GenerateContentConfig(cached_content=cache.name))
print(r.text,r.usage_metadata)
finally:
c.caches.delete(name=cache.name)缓存成本既可能含写入/存储,又包含后续读取。是否省钱取决于重复次数、材料长度和有效期;通过 usage metadata 观察缓存 token,不能只靠延迟判断。权限改变时不能继续把旧租户的缓存名称交给新请求。
配额与请求预算
并发限制约束在途请求数,每分钟配额约束时间窗口内的吞吐量,二者不同。一个请求处理快时,低并发仍可能超每分钟配额。429 按平台反馈退避并加抖动,错误请求 400、凭证 401/403 应直接修正。取消本地等待可能不取消远端生成或文件处理。
离线先观察并发上限。保存 queue.py,使用 Python 标准库运行:
import asyncio
async def main():
gate=asyncio.Semaphore(2)
active=peak=0
async def one():
nonlocal active,peak
async with gate:
active+=1;peak=max(peak,active)
try:await asyncio.sleep(0.01)
finally:active-=1
await asyncio.gather(*(one() for _ in range(5)))
assert peak==2 and active==0
print('peak',peak)
asyncio.run(main())本实验只证明并发门,未证明真实配额控制。生产再加速率限制与总 deadline,并记录输入、输出、缓存用量和请求 ID。多模态日志只保存必要引用与哈希,不自动复制所有原文件。
旧客户端与新入口
旧 google.generativeai 的模型对象持有配置,新 google.genai.Client 把模型名传给 models.generate_content。排错时先检查实际 import 和已安装包,不能把旧包升级版本号后假定导入路径改变。迁移保持三类测试:固定文本抽取、工具往返、多模态安全拦截。Developer API 迁 Vertex AI 还涉及区域、服务账户与权限,不能只换一个 base URL。
评估要分文本忠实度、图片定位、工具正确率与拒绝误伤;返回空文本不总是基础设施故障。逐步发布时固定 SDK 锁文件、模型配置、schema 和系统指令,回滚对应整组配置。本章云端缓存未运行,队列实验为本地行为验证。
旧生成配置怎样变成显式请求合同
旧GenerativeModel常把model_name与generation_config保存在对象;新Client将它们放到每次models.generate_content请求。迁移时先适配真实存档数据,再接新SDK。下面legacy_config.py使用真实GenerateContentConfig验证类型与生成字段,离线运行:
from google.genai import types
def adapt(old):
if set(old)!={'model_name','generation_config','prompt'}:raise ValueError('unsupported old fields')
config=old['generation_config']
if set(config)-{'temperature','max_output_tokens'}:raise ValueError('unknown generation setting')
if type(config.get('max_output_tokens')) is not int or config['max_output_tokens']<=0:
raise ValueError('invalid output budget')
return {'model':old['model_name'],'contents':old['prompt'],
'config':types.GenerateContentConfig(**config)}
old={'model_name':'account-model','prompt':'hello',
'generation_config':{'temperature':0.2,'max_output_tokens':100}}
new=adapt(old)
assert new['config'].max_output_tokens==100 and new['contents']=='hello'
try:adapt({**old,'generation_config':{'max_output_tokens':100,'unknown_safety':True}})
except ValueError:pass
else:raise AssertionError('setting silently discarded')
print('configuration contract retained')在线时把new交给 client.models.generate_content(**new)。这不是旧google.generativeai客户端运行证明,而是已归档输入的兼容入口;多轮history需要逐项保留Content角色及函数往返,不能只迁最后一条prompt。旧文件对象带认证入口身份,迁到Vertex时重新获取有权访问的云资源引用,不能复用Developer文件名。
练习
将并发上限改为 1,仍提交 5 个任务。应观察 peak=1、active=0;解释为什么这不能证明每分钟调用量小于 60。
先记录自己的输出与解释,再核对本章答案。