模型目录既不能每次启动都依赖网络,也不能永远相信打包时的旧数据。ModelsManager 把内置目录、磁盘缓存、远端响应和用户覆盖组合为一个有 ETag 与 TTL 的可刷新视图。
具体问题与启用条件
本节研究目录来源、刷新策略和覆盖顺序,不讨论某个模型字段如何影响请求;后者回到 5.1 和 5.4。
| 条件来源 | 决定字段或状态 | 对本机制的影响 |
|---|---|---|
| 刷新策略 | Online / Offline / OnlineIfUncached | 决定是否读取网络 |
| 磁盘缓存 | client version、fetched_at、TTL | 决定缓存能否作为远端目录 |
| 响应元数据 | Models ETag | 发现目录变化时触发刷新 |
协议、类型与状态所有权
| 状态或协议 | 所有者 | 生命周期 | 关键不变量 |
|---|---|---|---|
| Bundled models | 程序资源 | 版本固定 | 始终提供离线下限 |
| Remote models + ETag | ModelsManager | 内存刷新周期 | 同一快照内一致 |
models_cache.json | 模型缓存层 | 跨进程,受 TTL/版本限制 | 损坏不可阻断启动 |
| Config overrides | 有效配置 | Session | 最终覆盖匹配模型字段 |
机制调用链如下:
RefreshStrategy
→ 尝试读取并验证磁盘缓存
→ 按策略决定远端请求
→ 用远端条目覆盖/扩展 bundled catalog
→ 应用用户 overrides
→ 排序并暴露 ModelInfo 快照
机制怎样工作
Online 会主动请求远端,Offline 完全不联网,OnlineIfUncached 只在没有可用缓存时请求。缓存是否“可用”同时检查客户端版本和 TTL;旧客户端写出的目录不会被新版本盲用。远端返回成功后,Manager 保存 models、ETag、抓取时间和客户端版本。
最终目录不是简单拼接。内置目录提供保底字段与顺序,远端条目可以补充或替换同 slug 的模型,配置覆盖最后修改明确指定的字段。缓存过半寿命后可续期,避免活跃进程在临界点集中刷新。Responses 流中出现新 ETag 时,Session 调用 refresh_if_new_etag,使目录变化能在不重启的情况下进入后续 Turn。
Python 风格伪代码
这段伪代码保留生产实现中会改变结果的状态、分支和异步边界;认证 SDK、遥测字段和 Rust 所有权样板被折叠为明确的领域对象。
class RefreshStrategy(Enum):
ONLINE = auto()
OFFLINE = auto()
ONLINE_IF_UNCACHED = auto()
async def load_catalog(strategy: RefreshStrategy, cache: Cache) -> Catalog:
cached = cache.read_if_version_and_ttl_match()
should_fetch = strategy is RefreshStrategy.ONLINE or (
strategy is RefreshStrategy.ONLINE_IF_UNCACHED and cached is None
)
remote = await fetch_models(cached.etag if cached and should_fetch else None) if should_fetch else cached
merged = overlay_by_slug(bundled_models(), remote.models if remote else [])
return apply_config_overrides(merged)
async def refresh_if_new_etag(current: Catalog, etag: str) -> None:
if etag != current.etag:
await current.refresh_online()
失败、取消与恢复
| 故障或边界 | 已发生的状态 | 对上层的结果 | 能否直接重试 | 恢复动作 |
|---|---|---|---|---|
| 缓存 JSON 损坏 | 内置目录仍在 | 忽略缓存并记录诊断 | 是 | 按策略联网或离线退回 |
| TTL 过期 | 旧文件保留但不采信 | 需要远端刷新 | 是 | 请求模型目录 |
| 远端不可达 | 可能已有内置/缓存 | 使用可用下限或报刷新错误 | 是 | 后续再刷新 |
| 未知 override slug | 目录未改变 | 覆盖不生效 | 否 | 修正配置目标 |
设计取舍与验证
内置目录保证离线可启动,远端目录允许服务端快速修正能力,配置覆盖给用户最后控制权。代价是同一模型字段可能有三层来源,因此合并顺序和缓存版本必须被测试固定。ETag 负责变化检测,TTL 负责时间新鲜度,二者不是重复机制。
| 可验证契约 | 证据方式 | 预期结果 |
|---|---|---|
| 新鲜缓存避免网络 | 集成测试统计请求次数 | OnlineIfUncached 为零次远端请求 |
| 客户端版本变化使缓存失效 | 缓存测试 | 重新抓取并重写版本 |
| 配置覆盖最后生效 | override 测试 | 目标字段使用用户值 |
Mini Codex 对照
Mini Codex 可以把内置目录写在 Python 模块中,用带版本和时间戳的 JSON 缓存模拟远端;必须把合并函数写成纯函数并测试顺序。可以省略半寿命续期,但需承认并发刷新会更粗糙。
本节边界
本节得到可用的 ModelInfo 快照。下一节说明连接客户端为什么还要再分 Session 与 Turn 两个生命周期。
评论
登录后即可评论