想象你是一家餐厅的总厨。后厨有一个巨大的档案柜,里面放着每一道招牌菜的菜谱:食材从哪取、装盘是什么造型、用哪座灶台。无论主厨换了几任、客人换了几批,只要照菜谱操作,就能稳定复刻同一道菜。更妙的是,想改良某道菜时不必扔掉旧菜谱——v1、v2、v3 并存,老客人点“经典版”,新客人尝“改良版”,互不干扰。这就是 NVIDIA Triton 推理服务器的“模型仓库”(Model Repository)哲学。 Triton 是 AI 推理界的“总厨”。它服务的每个模型是一道菜,config.pbtxt 是这道菜的“制作卡片”,模型目录下的版本号子目录就是菜谱的迭代版本。部署模型不再是一次性烧录,而是像管理菜单一样:有序、可升级、可回滚、可热切换。本文用这套“厨房哲学”带你拆解 Triton 的目录结构、配置编写与多版本实战,让你在 RAG 客服系统等生产级场景中,真正做到“不停服换菜”。

岗位关联

对 AI 从业者:模型仓库是连接训练与生产的桥梁,掌握 config.pbtxt 与多版本控制,才能让模型升级像发布软件一样可预期、可回滚、零停机。

目录
  1. 可视化演示
  2. 仓库与目录:档案柜里的存放规则
  3. config 配置:决定出品的制作卡片
  4. 多版本管理:老味道与改良版的共存之道
  5. 热加载控制:换菜单不必关店
  6. Ensemble 编排:从单点到套餐的流水线
  7. 生产实践:后厨的落地与巡检
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

Triton模型仓库以目录管理多版本模型,config.pbtxt定义输入输出。自动播放或手动步进,观察版本热加载与不停服平滑切换流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

模型仓库 服务中 加载中 停用/待载 config.pbtxt 定义输入输出

仓库与目录:档案柜里的存放规则

老餐厅的后厨都有一个档案柜,规矩简单:每道菜一个抽屉,抽屉里按版本号分小格,柜门贴最新的制作卡片。v1 旧菜谱和 v2 改良版各归其位,没人会为一道菜单独买一个柜子,也没人会把两道菜的菜谱混在一起。

这套“按菜名分抽屉、按版本分小格”的规矩,就是 Triton 模型仓库的设计哲学。启动时用 --model-repository 指向仓库根目录,根目录下每个子目录代表一个模型,目录名就是模型名,例如 chat_llm、reranker。服务端对外暴露的模型名,直接对应到这个目录名。

模型目录内是 config.pbtxt 和若干版本号子目录。版本号必须是正整数(1、2、3……),权重文件放在这些版本目录里;非数字目录会被 Triton 当成另一个模型或直接忽略。于是整个仓库形成一条清晰的树状链路:仓库 → 模型目录 → 版本目录 → 模型文件,每一层都决定了模型如何被发现、加载和调用。

目录即接口。只要把模型文件放进正确的格子,总厨(Triton)扫一眼柜子就知道今天能上哪些菜;格子放错,再好的菜谱也端不上桌。

repo={Md:d∈D},Md={Vv:v∈N+},Vv={config.pbtxt, model files}\displaystyle \text{repo} = \left\{ M_{d} : d \in \mathcal{D} \right\}, \quad M_{d} = \left\{ V_{v} : v \in \mathbb{N}^{+} \right\}, \quad V_{v} = \left\{ \text{config.pbtxt},~\text{model files} \right\}
这个公式描述的是“仓库 → 模型 → 版本 → 文件”的四层嵌套结构:仓库是模型的集合,每个模型内部是版本的集合,每个版本内部才是真正的模型文件与配置。
  1. 仓库到模型repo 表示整个仓库根目录,M_d 是其中一个模型目录,下标 d 是模型名,\mathcal{D} 是所有模型名的集合——就像档案柜里按菜名分抽屉。
    Md:d∈DM_{d} : d \in \mathcal{D}
  2. 模型到版本每个模型目录内再按版本号分小格,v 必须为正整数,\mathbb{N}^{+} 代表正整数集合——格子号就是版本号。
    Vv:v∈N+V_{v} : v \in \mathbb{N}^{+}
  3. 版本到文件真正存放权重文件的层是版本目录;而 config.pbtxt 放在模型目录层,对模型所有版本统一生效。
    Vv={config.pbtxt, model files}V_{v} = \left\{ \text{config.pbtxt},~\text{model files} \right\}

config 配置:决定出品的制作卡片

新菜能不能稳定复刻,靠的是制作卡片:食材从哪个库房取(输入)、装盘是什么造型(输出)、用哪座灶台(资源)。老师傅拿到一张空白卡片就知道该怎么填,因为格式是行业通用的。

config.pbtxt 就是 Triton 世界里的制作卡片。它用 protobuf 文本格式书写,声明平台(platform 或 backend,例如 onnxruntime_onnx、pytorch_libtorch)、输入输出张量(name、data_type、dims)、最大批处理(max_batch_size)与实例组(instance_group)。服务端解析这张卡,才知道怎么校验请求、分配资源、调度推理。

细节决定成败:dims 里的 -1 表示可变维度,适合文本长度不固定的场景;max_batch_size 决定一次推理最多合并多少个请求,批处理越大吞吐越高;instance_group 的 count 决定启动几个实例副本,kind 选择 GPU 还是 CPU;dynamic_batching 还能自动攒批。若目录里没有 config.pbtxt,Triton 会尝试从模型元数据自动生成,但生产环境务必显式编写并纳入评审。

卡片越清晰,出品越稳定。config.pbtxt 把“这道菜怎么做”固化成机器可读的合同,杜绝了“换个主厨味道就变”的不确定性。

C=⟨name, input, output, max_batch_size, instance_group⟩\displaystyle C = \left\langle \text{name},~\text{input},~\text{output},~\text{max\_batch\_size},~\text{instance\_group} \right\rangle
一张制作卡片就是一组有序字段的集合:告诉 Triton 这个模型叫什么、吃什么(输入)、吐什么(输出)、用多少资源。
  1. 配置元组C 代表一张“制作卡片”,尖括号把若干有序字段组合在一起,声明顺序即解析顺序。
    ⟨⋯ ⟩\left\langle \cdots \right\rangle
  2. 输入输出声明input 与 output 定义张量名字、类型 data_type 与维度 dims,服务端据此校验请求与响应。
    input=[name, data_type, dims]\text{input} = \left[ \text{name},~\text{data\_type},~\text{dims} \right]
  3. 资源声明instance_group 由实例类型与数量组成,决定用几座灶台同时出菜。
    instance_group=(kind, count)\text{instance\_group} = \left( \text{kind},~\text{count} \right)

多版本管理:老味道与改良版的共存之道

餐厅改良招牌菜时,绝不会烧掉老菜谱:老客人认旧味,新客人想尝鲜,于是 v1、v2、v3 在同一档案柜里共存。总厨按客人的要求,端出对应版本。

Triton 的版本就是模型目录下的数字子目录。默认情况下,每个模型只加载一个最新版本;但通过 config.pbtxt 里的 version_policy,可以控制加载哪些版本:latest 只加载最近的 N 个,specific 只加载白名单版本,all 则全部加载。多个版本可同时常驻内存,客户端在 HTTP/gRPC 请求里用 version 参数点名要哪一个。

生产中最常用的是“latest + 显式请求”的组合:先让新版本随目录就绪,验证通过后,把线上请求的版本号从 v2 改为 v3,流量自然迁移;一旦发现异常,把版本号指回 v2 即完成回滚——全程不碰文件、不重启进程。注意:旧版本目录不会被自动清理,归档和删除要写进发布流程,避免仓库无限膨胀。

老味道与改良版共存,靠的是编号归档而非覆盖烧毁。多版本管理让升级从一场豪赌变成一次可逆操作。

V∗={{max⁡V}latest 模式V∩{vs}specific 模式Vall 模式\mathcal{V}^{\ast} = \begin{cases} \left\{ \max \mathcal{V} \right\} & \text{latest 模式} \\ \mathcal{V} \cap \left\{ v_{s} \right\} & \text{specific 模式} \\ \mathcal{V} & \text{all 模式} \end{cases}
版本策略就是“加载哪些菜谱版本”的选择规则:只要最新、只要白名单,还是全部都要。
  1. latest 模式只加载版本号最大的那个(或最大的 N 个),\max \mathcal{V} 表示取集合最大值。
    {max⁡V}\left\{ \max \mathcal{V} \right\}
  2. specific 模式只加载白名单指定的版本,\mathcal{V} \cap \{ v_s \} 是版本集合与白名单的交集。
    V∩{vs}\mathcal{V} \cap \left\{ v_{s} \right\}
  3. all 模式加载目录下全部版本,\mathcal{V} 即完整版本集合,适合需要同时服务多种结果对照的场景。
    V\mathcal{V}

热加载控制:换菜单不必关店

菜单要上新菜,不需要关门营业,更不需要把正在用餐的客人请出去。有经验的后厨在营业间隙就能挂上新菜谱、撤下旧菜谱,客人毫无感知。

Triton 通过 --model-control-mode 参数决定模型何时加载:none 模式只在启动时加载一次,之后不能再新增;poll 模式定时扫描仓库目录,发现新目录就自动加载;explicit 模式完全由外部控制接口决定加载和卸载。默认行为接近全局 poll,生产环境推荐显式控制,因为发布节奏要掌握在运维手里,而不是文件系统手里。

控制接口有 HTTP 和 gRPC 两种,例如 POST /v2/repository/models/{model_name}/load 与 /unload。模型状态在 LOADING → LOADED(就绪)→ UNLOADING → UNLOADED 之间迁移;只有进入就绪态才会接流量,加载中的模型不影响线上。热加载的本质是“先占资源、后切流量”,因此要关注显存水位,防止多版本同时驻留导致 OOM。

换菜单不用关店。模型升级变成挂新菜谱一样轻量的动作,可用性由状态机和流量迁移共同兜底。

R(v2,t)=min⁡(1,t−t0T),R(v1,t)=1−R(v2,t)\displaystyle R(v_{2}, t) = \min \left( 1, \frac{t - t_{0}}{T} \right), \qquad R(v_{1}, t) = 1 - R(v_{2}, t)
平滑切换就是在一段时间窗口内,把流量从旧版本逐步倾斜到新版本,两者比例之和始终为 1。
  1. 流量占比R(v_2, t) 表示 t 时刻新版本 v2 承接的流量比例,t_0 是开始切流时刻,T 是切换窗口长度。
    t−t0T\frac{t - t_{0}}{T}
  2. 限幅函数min(1, ·) 将比例钳制在 0 到 1 之间,防止流量超过 100%。
    min⁡(1,x)\min \left( 1, x \right)
  3. 互补分配新旧版本比例之和恒为 1,保证任意时刻所有请求都有明确去处。
    R(v1,t)+R(v2,t)=1R(v_{1}, t) + R(v_{2}, t) = 1

Ensemble 编排:从单点到套餐的流水线

RAG 客服系统不是一道菜,而是一桌套餐:冷盘负责筛选食材(召回),主菜负责精挑细选(重排),甜点负责端出最终回答(生成)。客人只需点一次“套餐”,后厨自动按顺序出菜。

Ensemble 是 Triton 的一种虚拟模型:它自己没有权重,只负责把仓库里的多个普通模型编排成流水线。配置里 platform 写 ensemble,ensemble_scheduling 中的每个 step 声明要调用的模型名、版本以及输入输出映射(input_map / output_map)。调度器按照拓扑顺序执行,上一个模型的输出自动变成下一个模型的输入。

对客户端来说,Ensemble 就是一个普通模型:一次 HTTP 请求即可跑完整条推理链。在 RAG 客服中,向量检索通常在应用层完成,Ensemble 负责“重排 → 提示词拼接 → 大模型生成”这一段;升级流水线时只需替换其中某个模型,并让 Ensemble 的映射指向新版本。

套餐的价值不在单一食材,而在编排。Ensemble 把多模型协作封装成一份固定菜单,调用方像点单一样使用整条推理流水线。

y0=x,yk=fk(yk−1),k=1,2,…,n\displaystyle \mathbf{y}_{0} = \mathbf{x}, \qquad \mathbf{y}_{k} = f_{k}\left( \mathbf{y}_{k-1} \right), \qquad k = 1, 2, \ldots, n
Ensemble 的本质是函数复合:上一个模型的输出恰好是下一个模型的输入,数据在模型之间自动搬运。
  1. 初始输入y_0 = x 把用户的原始请求作为流水线的第一棒。
    y0=x\mathbf{y}_{0} = \mathbf{x}
  2. 逐级传递第 k 个模型的输出 y_k 恰好变成第 k+1 个模型的输入,数据在模型间自动流转。
    yk=fk(yk−1)\mathbf{y}_{k} = f_{k}\left( \mathbf{y}_{k-1} \right)
  3. 整体复合整个 Ensemble 等价于函数的嵌套复合 f_n ∘ … ∘ f_1,对外只暴露一个模型接口。
    E=fn∘⋯∘f1\mathcal{E} = f_{n} \circ \cdots \circ f_{1}

生产实践:后厨的落地与巡检

好餐厅靠流程而非运气:柜子定置管理、开市前检查、高峰期盯着火候。Triton 上生产,同样需要一套可重复的仪式。

目录规划阶段:仓库与模型名采用语义化命名,版本号只增不减(1→2→3),废弃版本确认无流量后再清理;config.pbtxt 进入代码仓库接受评审。容器化部署时用 volume 把模型仓库挂载进容器,做到镜像不动、模型可换。

上线校验阶段:用 tritonserver --model-repository /models 启动并观察加载日志,轮询 /v2/health/ready 等待全部就绪,再用真实请求做冒烟测试。监控阶段:通过 /metrics 暴露模型状态、推理延迟与 GPU 利用率,结合告警自动回滚——当新版本 p95 延迟或错误率超过阈值,控制脚本立即加载旧版本并把流量切回。

后厨的稳定来自重复的流程和及时的巡检。把目录规划、健康校验、自动回滚固化成发布手册,AI 服务才算从“能跑”走向“可靠”。

G(v2)=1[health(v2)≥θ]={1切换至 v20回滚至 v1\displaystyle G(v_{2}) = \mathbf{1}\left[ \text{health}(v_{2}) \ge \theta \right] = \begin{cases} 1 & \text{切换至 } v_{2} \\ 0 & \text{回滚至 } v_{1} \end{cases}
上线决策就是一个“体检后拍板”的过程:新版本健康分达标就切换,不达标就自动回滚。
  1. 健康体检health(v_2) 是试运行期评估得分,如 p95 延迟、错误率、拒答率等,θ 是预设阈值。
    health(v2)≥θ\text{health}(v_{2}) \ge \theta
  2. 示性函数1[·] 是布尔判断——条件成立为 1,否则为 0,把体检结果变成二选一决策。
    1[⋅]\mathbf{1}\left[ \cdot \right]
  3. 回滚动作决策为 0 时立即切回旧版本 v_1,实现异常时的零人工快速恢复。
    G(v2)=0⇒v1G(v_{2}) = 0 \Rightarrow v_{1}

AI 应用场景

场景 1:RAG 客服系统三模型共库部署

阶段:模型部署与推理服务

解决的问题:客服系统同时依赖文本嵌入、重排、大模型生成三个模型。若各自启动独立服务,端口、版本、资源散落多处,升级一个模型就要改动三套流程。统一放进一个模型仓库后,Triton 集中加载、集中暴露、集中管理版本。

具体案例:某电商客服将 text_embedding(v1)、reranker(v1/v2) 与 chat_llm(v1/v2) 放入 /models 目录,应用层只配置一个 Triton 地址。reranker 升级时,先在仓库中加载 v2,用线上样本评估精排效果,达标后把请求版本号切到 v2,异常则指回 v1。

场景 2:大模型灰度发布与自动回滚

阶段:发布运维(上线 / 回滚)

解决的问题:停服换模型会产生分钟级空窗,且失败后难以恢复。借助 explicit 控制模式与健康检查,实现“先加载、后切流、异常即回滚”的零停机发布。

具体案例:发布 chat_llm v3 时,控制脚本先调用 load 接口加载 v3,轮询 /v2/health/ready 等待就绪;随后按 10% → 50% → 100% 切流,期间监控 p95 延迟与拒答率;一旦超阈值,脚本立即 load v2 并把流量指回 v2,整个过程无需人工改文件。

Python 演示

python78 行
import osfrom pathlib import Path  class TritonRepo:    def __init__(self, root):        self.root = Path(root)     def build(self, models):        # models: {模型名: [版本号列表]}        for name, versions in models.items():            model_dir = self.root / name            for v in versions:                (model_dir / str(v)).mkdir(parents=True, exist_ok=True)            self._write_config(model_dir, versions)     def _write_config(self, model_dir, versions):        # config.pbtxt 写在模型目录层, 不在版本目录内        policy = ", ".join(str(v) for v in versions)        cfg = (            'name: "{name}"\n'            'platform: "onnxruntime_onnx"\n'            'max_batch_size: 8\n'            'input {{\n'            '  name: "INPUT"\n'            '  data_type: TYPE_FP32\n'            '  dims: [ -1 ]\n'            '}}\n'            'output {{\n'            '  name: "OUTPUT"\n'            '  data_type: TYPE_FP32\n'            '  dims: [ -1 ]\n'            '}}\n'            'version_policy {{\n'            '  specific {{\n'            '    versions: [ {policy} ]\n'            '  }}\n'            '}}\n'        ).format(name=model_dir.name, policy=policy)        (model_dir / "config.pbtxt").write_text(cfg)     def list_versions(self, model_name):        model_dir = self.root / model_name        return sorted(            int(p.name) for p in model_dir.iterdir() if p.name.isdigit()        )     def select_versions(self, model_name, policy="latest", k=1, specific=()):        versions = self.list_versions(model_name)        if policy == "all":            return versions        if policy == "specific":            return [v for v in versions if v in specific]        return versions[-k:]     def canary_switch(self, old_version, new_version, health_score, threshold=0.8):        # 健康分 >= 阈值则切到新版本, 否则回滚旧版本        if health_score >= threshold:            return new_version        return old_version  def main():    repo = TritonRepo("/tmp/rag_model_repo")    repo.build({        "text_embedding": [1],        "reranker": [1, 2],        "chat_llm": [1, 2, 3],    })    print("chat_llm 全部版本:", repo.select_versions("chat_llm", "all"))    target = repo.select_versions("chat_llm", "latest", 1)[-1]    print("latest 策略加载:", target)    final = repo.canary_switch(2, target, health_score=0.93)    print("灰度发布结果: 最终服务版本 =", final)  if __name__ == "__main__":    main()
目的:用 Python 构建一个 Triton 风格的模型仓库目录,生成 config.pbtxt,并模拟 version_policy 版本选择与灰度切换/回滚逻辑。
  1. (model_dir / "config.pbtxt").write_text(cfg)config 写在模型目录层而非版本目录内,与 Triton 的解析规则一致。
  2. for v in versions:每个版本号生成一个数字子目录,目录名即版本号。
  3. return versions[-k:]latest 策略取版本号最大的 k 个。
  4. if policy == "specific":白名单策略通过列表交集实现。
  5. def canary_switch(self, old_version, new_version, health_score, threshold=0.8):灰度切换与回滚的统一入口。
  6. if health_score >= threshold:健康分达标才切换,否则回滚。
  7. print("灰度发布结果: 最终服务版本 =", final)输出最终服务版本。

常见误区

误区想换模型版本就直接覆盖权重文件,目录里只保留最新一份,这样“最干净”。

正解Triton 明确支持多版本共存,模型目录下按版本号分子目录即可。覆盖文件会破坏正在服务的旧版本,导致回滚无路可退。保留旧版本不是浪费,而是给线上服务的“后悔药”。

为何常见:传统部署方式往往是一份文件对应一个服务,习惯了“覆盖式更新”,没意识到版本目录就是为共存设计的。

误区配置改了,Triton 不会知道,必须重启进程才能让新配置生效。

正解在 explicit 控制模式下,调用 load 接口即可按新配置热加载模型;如果模型加载失败,错误会直接在响应中返回,无需重启整个服务。重启反而扩大了故障面。

为何常见:把“改配置 = 重启”的运维习惯从单体服务迁移到了推理服务,忽视了 Triton 的控制协议。

误区版本目录叫 `latest` 或 `v2` 都行,只要自己能看懂。

正解Triton 只识别纯整数目录名作为版本号,latest、v2 这类名字会被当作非法版本而忽略,甚至被当成新模型目录。版本号必须是正整数,latest 策略取数值最大者。

为何常见:人类习惯用语义化命名,但机器协议要求严格的编号规则,这也是“目录即接口”的体现。

误区多模型流水线就是在业务代码里先调 embedding,再调 rerank,最后调 LLM,Ensemble 只是营销概念。

正解Ensemble 在 config.pbtxt 里用 ensemble_scheduling 声明编排图和输入输出映射,由 Triton 调度器统一执行,业务侧只需一次请求。相比业务代码串行,Ensemble 省去了多次网络往返和手工拼接。

为何常见:习惯了“代码即编排”,没有意识到推理框架本身提供了声明式编排能力。

误区仓库里放了 v1、v2、v3,那三个版本必然同时占着显存。

正解是否加载由 version_policy 与控制模式共同决定。latest 策略只加载最新版本,specific 只加载白名单,只有 all 才全部加载;显存占用可以通过 /metrics 实测确认。

为何常见:把“磁盘上存在”等同于“内存中加载”,混淆了文件系统与运行时状态。

面试题

Q1. Triton 模型仓库中,一个模型的多个版本应如何存放?

单选题 · easy

  • A.用整数版本号建独立子目录← 正确答案
  • B.全部文件放在同一目录里覆盖
  • C.按时间戳命名不同子目录
  • D.只能保留一个版本文件

答案:A。Triton 约定模型目录下用整数版本号作为子目录名来管理多版本,目录名即版本号,latest 策略取最大值,所以 A 正确。覆盖文件会破坏旧版本;时间戳不是约定格式;只保留一个版本与多版本共存能力矛盾。

Q2. config.pbtxt 的核心作用是?

单选题 · easy

  • A.记录模型训练的日志内容
  • B.声明模型输入输出与资源← 正确答案
  • C.保存分词器的词表文件
  • D.存储模型的权重参数

答案:B。config.pbtxt 声明模型名称、输入输出张量、批处理与实例组等,是服务端与模型之间的“合同”。训练日志、词表、权重分别由日志系统、tokenizer 文件和模型文件负责,不写进 config。

Q3. version_policy 为 latest 时,Triton 会选择?

单选题 · mid

  • A.目录中排在第一的版本
  • B.最近修改时间最新的
  • C.版本号数值最大的版本← 正确答案
  • D.随机挑选一个可用版本

答案:C。latest 策略加载版本号数值最大的版本,与目录顺序、修改时间无关。这正是“编号归档”的意义——唯一权威的排序依据是版本号本身。

Q4. 生产环境推荐使用哪种模型控制模式实现热加载?

单选题 · mid

  • A.none 模式全部自动加载
  • B.explicit 模式按需加载卸载← 正确答案
  • C.poll 模式定时扫描目录
  • D.latest 模式自动更新

答案:B。explicit 模式通过控制接口精确决定加载与卸载,最适合生产发布节奏;none 模式启动后不能新增模型;poll 模式依赖文件系统扫描,实时性差且不可控;latest 是版本策略而非控制模式。

Q5. 关于热加载与平滑切换,正确的说法有?

多选题 · mid

  • A.加载新版本不必重启服务← 正确答案
  • B.就绪后才把流量切过去← 正确答案
  • C.健康检查失败可回滚旧版← 正确答案
  • D.旧版本必须马上删除

答案:ABC。热加载先加载新版本再切流,全程不重启;模型进入就绪态后才接流量;健康检查失败可立即回滚。D 说必须马上删除旧版本是错误的——保留旧版本正是回滚的前提。

Q6. Ensemble 模型中 ensemble_scheduling 的作用是?

单选题 · mid

  • A.声明模型编排流程与数据映射← 正确答案
  • B.分配 GPU 显存与线程资源
  • C.设置最大批处理的大小
  • D.控制日志输出详细级别

答案:A。ensemble_scheduling 声明编排图与输入输出映射,调度器据此顺序执行各模型。显存分配、批处理、日志分别由 instance_group、max_batch_size 和日志配置负责。

Q7. max_batch_size: 8 表示?

单选题 · hard

  • A.输入文本最长八个字符
  • B.一次最多合并八个请求← 正确答案
  • C.模型输出最多八个向量
  • D.最多占用八块 GPU

答案:B。max_batch_size 表示一次推理最多合并的请求样本数,即动态批处理的上限。它与文本长度、输出维度、GPU 数量无关,更像灶台一次最多能同时炒几份菜。

Q8. 灰度发布新版本 v3 的合理流程包括?

多选题 · hard

  • A.先加载 v3 并做健康检查← 正确答案
  • B.指标达标后逐步切流量← 正确答案
  • C.异常时回滚到原版本← 正确答案
  • D.删除旧版本再上新版

答案:ABC。先加载 v3 并体检、达标后逐步切流、异常时回滚是标准三步。直接删除旧版本相当于烧掉老菜谱,回滚没有退路,是发布流程的大忌。

Q9. 多版本共存时,客户端如何指定使用某个版本?

单选题 · hard

  • A.修改 config 中的模型名字
  • B.只能使用默认的 latest
  • C.请求中带版本号参数即可← 正确答案
  • D.重启服务端并等待片刻

答案:C。客户端在 HTTP/gRPC 请求中携带版本号参数即可点名版本,无需改配置或重启;不带版本号时服务端默认使用 latest 策略选出的版本。

Q10. 关于模型仓库目录结构,下列正确的是?

多选题 · easy

  • A.仓库根目录下每模型一目录← 正确答案
  • B.模型目录下按版本号分目录← 正确答案
  • C.config 放在模型目录层← 正确答案
  • D.每个版本目录各放一份 config

答案:ABC。仓库根目录下每个模型一个目录,模型目录下按整数版本号分子目录,config.pbtxt 放在模型目录层对整个模型统一生效。D 说每个版本目录各放一份 config 是错误理解,会造成配置碎片化。