Instructor 缓存策略完全指南:从内置 Cache 适配器到 functools / diskcache / Redis 实战

发布时间:2026/9/14 14:42:13
Instructor 缓存策略完全指南:从内置 Cache 适配器到 functools / diskcache / Redis 实战 Instructor 缓存策略完全指南从内置 Cache 适配器到 functools / diskcache / Redis 实战【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructorInstructor 为 LLM 结构化输出提供了两套互补的缓存手段内置缓存cache适配器 cache_namespace隔离 TTL 覆盖和应用层装饰器方案functools.cache、diskcache、Redis。本篇以 docs/concepts/caching.md 为骨架结合 instructor/cache/init.py、instructor/v2/core/patch.py 与 examples/caching/ 源码讲清每种方案的适用场景、完整可运行代码、缓存键设计原理与注意事项帮助你减少重复 API 调用带来的延迟与费用。目录为什么 LLM 应用需要缓存内置缓存cache适配器BaseCache 抽象与两个开箱即用的适配器默认命名空间隔离1.17 起cache_namespace显式共享与安全边界每调用 TTL 覆盖cache_ttl缓存键设计SHA-256 确定性哈希原始响应Raw Response重建内置缓存的能力边界方案一functools.cache进程内内存缓存方案二diskcache持久化缓存方案三Redis 分布式缓存高级实践schema 自动失效、分层缓存与监控仓库内可运行示例总结如何选择缓存策略为什么 LLM 应用需要缓存调用 LLM 接口是有成本的无论按 Token 计费还是按调用计费重复处理同一段输入都意味着重复的延迟和费用。在以下场景中缓存能带来立竿见影的效果开发与测试同一批测试用例反复执行批处理数据集中存在大量重复或相似条目Web 应用多个用户请求相同信息数据管道ETL 流程多次处理同一数据模型实验在相同输入上迭代不同 prompt 或 response_model。Instructor 的缓存理念建立在两个事实之上LLM 调用昂贵、结构化输出的 Pydantic 模型天然适合 JSON 序列化model_dump_json()与model_validate_json()无损往返。这决定了从内置缓存到手工装饰器所有方案的序列化路径都是一致的。内置缓存cache适配器自 v1.9.1 起Instructor 为每个客户端提供了内置缓存。你只需在创建客户端或调用create时传入一个缓存适配器之后的普通调用会自动命中缓存无需改造业务代码from instructor import from_provider from instructor.cache import AutoCache, DiskCache # 任何 provider 都支持 —— cache 通过 **kwargs 自动流向所有 provider 实现 client from_provider(openai/gpt-4.1-mini, cacheAutoCache(maxsize1000)) client from_provider(anthropic/claude-3-haiku, cacheAutoCache(maxsize1000)) client from_provider(google/gemini-2.5-flash, cacheDiskCache(directory.cache)) # 普通调用现在被自动缓存 from pydantic import BaseModel class User(BaseModel): name: str first client.create( messages[{role: user, content: Hi.}], response_modelUser ) second client.create( messages[{role: user, content: Hi.}], response_modelUser ) assert first.name second.name # 第二次调用由缓存直接返回BaseCache 抽象与两个开箱即用的适配器在 instructor/cache/init.py 中缓存后端被抽象为一个极小的接口BaseCache抽象基类要求实现同步的get(key) - Any | None返回None表示缓存未命中与set(key, value, ttlNone)ttl单位为秒实现可以忽略。源码注释明确要求具体子类必须是线程安全的且异步包装器当前直接调用同步方法。AutoCache基于collections.OrderedDict实现的进程内线程安全 LRU 缓存默认maxsize128maxsize 0会抛出ValueError。get时把命中的键移到末尾最近使用set时当容量超出maxsize用popitem(lastFalse)淘汰最久未使用项。该实现忽略ttl。DiskCache对diskcache.Cache的薄封装默认目录为.instructor_cache可通过directory覆盖底层支持expire语义因此支持 TTL。diskcache是可选依赖未安装时会提示pip install instructor[diskcache]。从源码结构看内置缓存被设计成安全的地基当前迭代刻意保持 API 精简无淘汰钩子、无手动失效、LRU 无 TTL后续版本再逐步扩展。默认命名空间隔离1.17 起1.17 引入了一个关键安全特性每个被 patch 的客户端默认拥有独立的缓存命名空间。在 instructor/v2/core/patch.py 中可以看到每次创建 wrapper 时会生成cache_scope uuid4().hex并将其作为默认的cache_namespacecache_scope uuid4().hex ... cache_namespace kwargs.pop(cache_namespace, cache_scope) if not isinstance(cache_namespace, str) or not cache_namespace: raise ValueError(cache_namespace must be a non-empty string)这意味着通过同一个客户端重复调用可以复用缓存条目新建一个客户端即使使用同一个磁盘缓存目录会得到全新的命名空间不会误用旧条目从而避免不同 endpoint、不同账号在共用模型名时发生缓存串用。配套测试 tests/cache/test_cache_key.py 验证了make_cache_key(namespaceaccount-a) ! make_cache_key(namespaceaccount-b)tests/cache/test_cache_namespace.py 则验证了None、空字符串、非字符串如123作为cache_namespace都会抛出ValueError同步与异步路径一致。cache_namespace显式共享与安全边界如果确实需要跨客户端实例或跨进程重启复用缓存就在调用client.create时同时传入稳定的cache与cache_namespace字符串from instructor import from_provider from instructor.cache import DiskCache from pydantic import BaseModel class User(BaseModel): name: str cache DiskCache(directory.cache) client from_provider(openai/gpt-4.1-mini) client.create( messages[{role: user, content: Hi}], response_modelUser, cachecache, cache_namespaceendpoint:prod:account-42, # 稳定且可标识的命名空间 )官方文档对命名空间提出了明确的工程约定选择一个能标识endpoint、账号或租户、应用策略的命名空间绝不要用凭据API Key、Token作为命名空间值不要在多个租户之间共享同一个命名空间切换 endpoint、账号或校验策略时应更换命名空间。每调用 TTL 覆盖cache_ttlcache之外还可以传入cache_ttl秒让结果自动过期。注意cache_ttl必须为int否则会被忽略instructor/v2/core/patch.pyfrom instructor import from_provider from instructor.cache import DiskCache from pydantic import BaseModel class User(BaseModel): name: str cache DiskCache(directory.cache) client from_provider(openai/gpt-4.1-mini) client.create( messages[{role: user, content: Hi}], response_modelUser, cachecache, cache_ttl3600, # 1 小时后过期 )语义要点底层后端支持 TTL如DiskCache时条目会在指定时长后被淘汰对于AutoCache该参数被忽略LRU 只按容量淘汰。缓存键设计SHA-256 确定性哈希内置缓存用instructor.cache.make_request_cache_key计算完整请求的缓存键见 instructor/cache/init.py。它对完整准备好的请求哈希除了下列基础组件外还包含client 身份、namespace、校验上下文context和 strictness组成部分为什么影响缓存键model不同模型名可能产生不同答案messages/contents完整对话历史被哈希modeJSON / TOOLS / RESPONSES 等模式会改变格式化方式response_modelschema完整的model_json_schema()被纳入键字段名、类型甚至description 或 docstring 的任何变化都会自动使缓存失效具体实现要点源码级请求中的BaseModel、模型类、Enum、datetime/date、bytes、布尔值等都会经过_canonical_cache_value规范化如Enum退化为.value、dict 按键排序、True与1区分开保证等价输入生成等价键生成参数temperature、top_p、seed、max_tokens、stop、reasoning_effort等白名单字段会被单独提取进generation部分因此改变采样配置也会改变键无法可靠序列化的请求对象如不可哈希的回调会抛出TypeErrormake_request_cache_key捕获后返回None——此时该调用直接绕过缓存而不是复用一个语义含糊的键见 tests/cache/test_cache_key.py最终键为SHA-256 十六进制摘要长度恒定与输入大小无关。测试 tests/cache/test_cache_key.py 详细验证了这些行为字段 description 变化、字段增减、类 docstring 变化、hoisted system prompt 变化都会使键不同相同 schema 产生相同键无 system prompt 时键保持稳定provider不同键不同。低层辅助函数make_cache_key仍保留给自定义集成使用——调用者需要自行提供所有相关的身份与策略输入from instructor.cache import make_cache_key from pydantic import BaseModel class User(BaseModel): name: str key make_cache_key( messages[{role: user, content: hello}], modelgpt-4.1-mini, response_modelUser, modeTOOLS, ) print(key) # → 2e2a9521bd269d62ee9a8559d7deacba0025c1f6da0ec1fc63d472788be096fe如果需要自定义行为例如忽略某些 prompt 字段可以编写自己的 helper把派生键传给自建的缓存适配器。原始响应Raw Response重建create_with_completion会返回(模型, 原始补全对象)缓存时必须连原始对象一起恢复。实现位于 instructor/v2/core/cache_response.py存储时把model_dump_json()的模型 JSON 与原始响应 JSON 打包为{model: ..., raw: ...}原始响应的序列化采用多级防御策略Pydantic 模型OpenAI、Anthropic 等优先model_dump_json()可完美序列化普通字典json.dumps(raw_resp, defaultstr)兜底完全不可序列化对象退化为str(raw_resp)并发出警告。恢复时如果 raw JSON 是形如{id: ..., object: ..., model: ..., choices: ...}的 completion-like 结构则用SimpleNamespace技巧重建对象从而保留点号访问习惯如completion.usage.total_tokens且无需原始类定义from pydantic import BaseModel class Completion(BaseModel): content: str usage: dict # 示例补全对象 completion Completion(contentHello, usage{tokens: 10}) # 缓存时 raw_json completion.model_dump_json() # 序列化为 JSON # 恢复时 import json from types import SimpleNamespace restored json.loads(raw_json, object_hooklambda d: SimpleNamespace(**d))此外缓存的模型会用当前调用的 context 与 strict 重新校验load_cached_response中调用response_model.model_validate_json(model_json, contextcontext, strictstrict)并且包含_raw_response的恢复逻辑保证create_with_completion在缓存命中时行为完全一致。内置缓存的能力边界从源码与文档可以确认以下边界create(...)与create_with_completion(...)支持缓存后者第二个元组元素也能从缓存恢复create_partial(...)、create_iterable(...)以及任何streamTrue的调用暂不缓存——流式生成器需要专门设计才能重放目前这些调用总是到达 provider只有响应模型非空时才会走缓存路径if cache is not None and response_model is not None。方案一functools.cache进程内内存缓存适用场景参数不可变、在中小型应用中被反复以相同参数调用的函数只需在单次会话内复用数据开发环境、测试。import time import functools import instructor from pydantic import BaseModel client instructor.from_provider(openai/gpt-4.1-mini) class UserDetail(BaseModel): name: str age: int functools.cache def extract(data) - UserDetail: return client.create( response_modelUserDetail, messages[ {role: user, content: data}, ], ) start time.perf_counter() # (1) model extract(Extract jason is 25 years old) print(fTime taken: {time.perf_counter() - start}) # Time taken: 0.43337099999189377 start time.perf_counter() model extract(Extract jason is 25 years old) # (2) print(fTime taken: {time.perf_counter() - start}) # Time taken: 1.166015863418579e-06用time.perf_counter()而非time.time()计时前者精度更高、更不易受系统时钟调整影响第二次调用直接命中缓存函数体不会执行——耗时从百毫秒级降到微秒级。仓库示例 examples/caching/lru.py 中用functools.lru_cache包装client.chat.completions.create(...)实测第一次约0.9268s、第二次约1.2e-06s。警告更换模型不会使缓存失效functools.cache的缓存键基于函数名与参数与模型无关。如果你把client.create里的model换了缓存仍会返回旧结果。生产环境请考虑把模型名等参数也纳入缓存键或改用内置缓存其键天然包含 model。若想限制缓存体积、观察命中率可改用functools.lru_cacheimport functools functools.lru_cache(maxsize1000) # 限制最多缓存 1000 个条目 def extract_with_limit(data: str, model: str gpt-4.1-mini) - UserDetail: return client.create( modelmodel, response_modelUserDetail, messages[ {role: user, content: data}, ], )它额外提供maxsize内存上限、LRU 自动淘汰、cache_info()统计hits/misses/maxsize/currsize。装饰器小科普原文附注装饰器是接收函数、返回扩展后函数的闭包包装。decorator等价于func decorator(func)在函数调用前后注入逻辑def decorator(func): def wrapper(*args, **kwargs): print(Do something before) result func(*args, **kwargs) print(Do something after) return result return wrapper局限进程重启后缓存丢失内存随缓存体积增长默认无容量上限不适合分布式应用。方案二diskcache持久化缓存适用场景需要跨会话持久化的应用、处理大规模数据集、希望避免开发期重复 API 调用。下面的instructor_cache装饰器针对返回 Pydantic 模型的函数做了完整处理用inspect.signature读取返回类型注解非法类型直接ValueError用functools._make_key生成基于函数名与实参的唯一键命中时用model_validate_json反序列化未命中时调用原函数并用model_dump_json序列化落盘import functools import inspect import instructor import diskcache from pydantic import BaseModel client instructor.from_provider(openai/gpt-4.1-mini) cache diskcache.Cache(./my_cache_directory) # (1) def instructor_cache(func): 缓存返回 Pydantic 模型的函数 return_type inspect.signature(func).return_annotation # (4) if not issubclass(return_type, BaseModel): # (2) raise ValueError(The return type must be a Pydantic model) functools.wraps(func) def wrapper(*args, **kwargs): key ( f{func.__name__}-{functools._make_key(args, kwargs, typedFalse)} # (3) ) # 命中缓存则直接反序列化 if (cached : cache.get(key)) is not None: return return_type.model_validate_json(cached) # (5) # 未命中调用函数并缓存结果 result func(*args, **kwargs) serialized_result result.model_dump_json() cache.set(key, serialized_result) return result return wrapper class UserDetail(BaseModel): name: str age: int instructor_cache def extract(data) - UserDetail: return client.create( response_modelUserDetail, messages[ {role: user, content: data}, ], )diskcache.Cache(./my_cache_directory)会在当前工作目录创建缓存目录仅缓存返回 Pydantic 模型的函数以简化序列化/反序列化逻辑可自行扩展支持其他类型functools._make_key基于函数名与参数生成唯一键保证每个调用的结果独立缓存inspect.signature读取返回类型注解用于校验缓存结果Pydantic 的model_validate_json把 JSON 字符串还原为模型实例。要点提示该装饰器不会随模型变更自动失效——建议把Model.model_json_schema()编码进缓存键见下文高级实践这样 schema 一变键就变。仓库中的完整版 examples/caching/example_diskcache.py 还额外支持异步函数inspect.iscoroutinefunction判断后返回awrapper实测磁盘缓存命中时从0.7285s降到9.8e-05s。收益减轻重度数据处理的重复计算磁盘持久化进程重启后缓存仍在支持大小限制与淘汰策略、线程安全。方案三Redis 分布式缓存适用场景多进程/多实例需要共享缓存数据的分布式系统或需要快速读写并处理复杂数据结构的应用。关键点在于与 diskcache 使用同一个instructor_cache装饰器只是换一个后端——实现相同、API 一致便于切换缓存策略import redis import functools import inspect import instructor from pydantic import BaseModel client instructor.from_provider(openai/gpt-4.1-mini) cache redis.Redis(localhost) # (1) def instructor_cache(func): 缓存返回 Pydantic 模型的函数 return_type inspect.signature(func).return_annotation if not issubclass(return_type, BaseModel): # (2) raise ValueError(The return type must be a Pydantic model) functools.wraps(func) def wrapper(*args, **kwargs): key f{func.__name__}-{functools._make_key(args, kwargs, typedFalse)} # (3) # 命中缓存则直接反序列化 if (cached : cache.get(key)) is not None: return return_type.model_validate_json(cached) # 未命中调用函数并缓存结果 result func(*args, **kwargs) serialized_result result.model_dump_json() cache.set(key, serialized_result) return result return wrapper class UserDetail(BaseModel): name: str age: int instructor_cache def extract(data) - UserDetail: return client.create( response_modelUserDetail, messages[ {role: user, content: data}, ], )连接本机 Redis生产环境请配置连接池与鉴权仅缓存返回 Pydantic 模型的函数简化序列化逻辑与 diskcache 完全相同的键生成方式。仓库中的生产级版本 examples/caching/example_redis.py 与 examples/caching/run.py 进一步展示了连接池redis.ConnectionPool(host..., port6379, db0, max_connections20, decode_responsesTrue)TTL 与命名空间前缀cache.setex(key, ttl, serialized_result)键形如instructor:extract:schema_hash:args_hash容错降级Redis 读/写异常时记录警告retry_on_failureFalse时直接调用原函数而非失败schema 版本化键把model_json_schema()的 MD5 摘要前 8 位放进键模型结构变化自动失效。收益可扩展到大规模系统内存级快速读写支持多种数据类型自带过期与淘汰策略适合微服务/多实例共享缓存。高级实践schema 自动失效、分层缓存与监控1. 基于 schema 的智能失效内置缓存已把model_json_schema()纳入键手工装饰器同样可以做到。给模型新增字段如给User加email: Optional[str]后schema 哈希自动变化缓存不会返回旧结构的数据import hashlib import json def smart_cache_key(func_name: str, args: tuple, kwargs: dict, model_class: type) - str: 生成包含模型 schema 哈希的缓存键实现自动失效 schema_hash hashlib.md5( json.dumps(model_class.model_json_schema(), sort_keysTrue).encode() ).hexdigest()[:8] args_hash hashlib.md5(str((args, kwargs)).encode()).hexdigest()[:8] return f{func_name}:{schema_hash}:{args_hash}2. 分层缓存把内存、磁盘、Redis 组合成 L1 → L2 → L3 层级兼顾速度、持久化与共享完整可运行版见 examples/caching/run.pyfunctools.lru_cache(maxsize100) # L1进程内内存最快 def extract_l1(data: str) - UserDetail: return extract_l2(data) create_diskcache_decorator() # L2本地磁盘快、持久 def extract_l2(data: str) - UserDetail: return extract_l3(data) create_redis_decorator() # L3Redis共享、网络 def extract_l3(data: str) - UserDetail: return client.chat.completions.create(...)3. 缓存监控与指标run.py中的CacheMetrics类统计命中/未命中、命中率、节省时间与按函数拆分的指标便于在生产中观察缓存收益并定位优化点。4. 通用最佳实践键设计包含模型 schema自动失效共享缓存中用前缀做命名空间隔离必要时加入应用版本号做受控失效错误处理缓存读失败应降级为直接调用缓存写失败不应影响业务记录 warning 即可安全绝不缓存个人敏感信息多租户应用做好缓存键隔离敏感场景考虑加密缓存内容预热对高频查询预先执行一遍以填充缓存。仓库内可运行示例examples/caching/lru.pyfunctools.lru_cache内存缓存最小示例examples/caching/example_diskcache.pydiskcache 装饰器含异步变体examples/caching/example_redis.pyRedis 装饰器examples/caching/run.py五种策略无缓存基线、lru_cache、diskcache、Redis、分层缓存的基准对比、成本节省计算、schema 失效演示与监控指标tests/cache/test_cache_key.py缓存键确定性、schema 变化失效、生成参数隔离等行为的测试佐证tests/cache/test_cache_namespace.py命名空间校验测试instructor/cache/init.pyBaseCache/AutoCache/DiskCache/make_cache_key/make_request_cache_key实现instructor/v2/core/cache_response.py模型 原始响应的序列化与重建instructor/v2/core/patch.pycache/cache_namespace/cache_ttl的调用入口。相关文档提示词缓存Provider 侧、性能优化建议、批量任务与成本优化、Hooks 监控缓存命中/未命中、详细缓存博客。总结如何选择缓存策略内置缓存cacheAutoCache/cacheDiskCachev1.9.1 官方方案所有 provider 通用自带命名空间隔离、schema 感知的 SHA-256 键、原始响应重建与防御性序列化优先推荐functools.cache/lru_cache单进程、开发测试环境的最简方案零依赖、微秒级命中但重启即失且换模型不失效diskcache需要跨会话持久化、数据量大、希望进程重启后复用缓存时使用Redis多实例/微服务/分布式系统需要共享缓存、高吞吐场景使用。三种手工装饰器共享同一套接口与序列化思路model_dump_json/model_validate_json因此你可以在开发期用functools.cache上线后无缝切换到 Redis而业务代码几乎不变。无论选择哪种都要记得把model_json_schema()编入缓存键、设置合适的 TTL、并加上监控与降级——这正是内置缓存已经替你完成的事情。【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询