LLMOps从资产版本到生产发布恢复完整原理
LLMOps 不是“给 Prompt 加一个版本号”,也不是只监控 Token。它是一套让 AI 应用的代码、Prompt、模型、RAG、工具、策略和评测结果能够一起被构建、验证、审批、发布、观测、回滚和审计的工程体系。
传统接口通常对同一输入给出确定结果;AI 应用不仅可能随机,而且答案同时受模型服务、知识索引和上下文影响。若没有 LLMOps,线上出现“今天为什么答错”时,团队甚至无法确定用户实际命中了哪套资产。
学习目标
学完后应能:
- 解释 LLMOps 与 DevOps、MLOps 的关系和边界。
- 画出控制面与运行数据面的完整职责。
- 区分源资产、构建产物、发布包、部署和运行实例。
- 用不可变 Revision 和 Hash 组装可重放的 Release Bundle。
- 解释为什么 Prompt、模型、RAG、Tool 和策略不能各自随意上线。
- 设计 Draft、Built、Evaluated、Approved、Canary、Stable、Retired 状态机。
- 完成开发、测试、预发、生产的同包晋级,而不是生产重新构建。
- 设计质量、安全、性能、成本和兼容性门禁。
- 讲清 Alias 原子切换、乐观锁和并发发布问题。
- 处理长会话、缓存、流式请求和异步任务造成的版本残留。
- 建立 Trace、指标、日志、反馈和数据安全体系。
- 执行 Prompt、模型、索引、工具、Provider 漂移等事故 Runbook。
一、LLMOps、DevOps和MLOps是什么关系
| 体系 | 主要对象 | 重点问题 |
|---|---|---|
| DevOps | 代码、配置、镜像、基础设施 | 能否稳定构建、测试、部署和恢复 |
| MLOps | 数据集、特征、训练代码、模型权重 | 能否重现训练、评估、部署和监控漂移 |
| LLMOps | Prompt、基础/微调模型、RAG、Tool、策略、Judge | 非确定性质量、引用、权限、Token和多资产协同发布 |
三者是叠加关系,不是互相替代。Spring Boot AI 网关仍需要普通 CI/CD;若训练或微调模型,还需要 MLOps;当模型被组装成 RAG、Agent 或 Tool Calling 应用时,再增加 LLMOps。
不做LLMOps会怎样
- 运营直接在线改 Prompt,失败后不知道旧文本。
model-latest被 Provider 改指向,代码没发布但答案变化。- 新 Embedding 查询旧索引,维度相同却语义空间不兼容。
- Tool Schema 升级后旧 Prompt 仍生成已删除参数。
- 新索引原地覆盖,错误资料无法快速切回。
- 总平均分上升,但跨租户引用或写工具越权。
- 回滚路由后,旧会话、缓存和异步任务仍使用候选版本。
- 日志只有问题和答案,没有版本矩阵,事故只能猜。
二、控制面和运行数据面
flowchart TD
A["Git、文档源、模型目录和策略源"] --> B["LLMOps控制面"]
B --> C["资产注册与不可变Revision"]
C --> D["构建、兼容性验证与评测"]
D --> E["审批、签名与环境晋级"]
E --> F["版本路由和发布控制"]
F --> G["AI运行数据面"]
G --> H["Prompt编译、RAG、模型与Tool执行"]
H --> I["Trace、指标、反馈和安全事件"]
I --> B2.1 控制面负责什么
- 资产登记、版本、依赖和所有者。
- 构建知识索引、Prompt 包、策略包和容器镜像。
- 运行离线评测和安全门禁。
- 审批、环境晋级、灰度比例和回滚。
- 保存发布历史、证据和审计记录。
2.2 运行数据面负责什么
- 接收真实请求并鉴权、限流。
- 根据场景、租户、会话和灰度规则解析发布版本。
- 执行 Prompt、RAG、模型、Tool 和输出校验。
- 记录请求实际命中的 Revision、性能、成本和安全结果。
- 在 Deadline 内降级、拒绝或进入异步状态。
控制面故障不应让正在运行的稳定版本立即不可用。运行面应缓存最后一份签名有效配置;但控制面恢复后要对账,避免节点长期使用陈旧路由。
三、先区分五种容易混淆的对象
| 对象 | 示例 | 是否可变 |
|---|---|---|
| Source | Git中的Prompt模板、Markdown文档 | 开发中可变 |
| Revision | prompt@sha256:...、index@build-42 | 发布后不可变 |
| Release Bundle | 一组兼容Revision及门禁证据 | 不可变 |
| Deployment | Bundle在prod环境的Canary配置 | 可改变流量状态 |
| Runtime Instance | 某个Pod、Worker或会话实际执行 | 短生命周期 |
Source 更新不等于线上生效。构建过程把 Source 固化为不可变 Revision;Release Bundle 再把多个 Revision 绑定成一个整体。Deployment 只控制这个整体在哪个环境、接多少流量。
如果直接让生产读取 Git 分支、数据库中可编辑 Prompt 或动态 Alias,就无法证明一次请求对应哪份不可变内容。
四、需要管理的资产和依赖关系
flowchart TD
A["Application Revision"] --> B["Release Bundle"]
C["Prompt Revision"] --> B
D["Model Contract Revision"] --> B
E["RAG Index Revision"] --> B
F["Tool Schema Revision"] --> B
G["Policy Revision"] --> B
H["Parser Revision"] --> B
I["Evaluation Report"] --> B
J["Rollback Target"] --> B| 资产 | 至少记录 | 典型兼容约束 |
|---|---|---|
| 应用 | Git commit、镜像digest、数据库迁移版本 | API和解析器兼容 |
| Prompt | 内容Hash、变量Schema、角色、Token预算 | 变量和模型能力兼容 |
| 模型契约 | Provider、精确Revision、区域、能力 | Tool/JSON/上下文能力 |
| RAG索引 | 文档快照、Embedding、维度、Chunk策略、ACL Schema | 查询Embedding必须匹配 |
| Reranker | 模型、输入长度、分数口径 | 与候选字段兼容 |
| Tool | 名称、参数Schema、风险级别、后端版本 | Prompt和执行器兼容 |
| 策略 | ACL、路由、脱敏、限额版本 | 与身份字段兼容 |
| 评测 | 数据集、评分器、Judge、报告Hash | 必须评测同一Bundle |
模型“API兼容”不代表能力等价。例如两个 Provider 都接受 messages,但对 JSON Schema、并行 Tool Call、流式 Usage、上下文长度的支持可能不同。Bundle 构建要做能力契约检查。
五、Release Bundle怎样设计
一个最小发布包:
{
"bundleId": "medical-assistant@20260716.3",
"applicationRevision": "sha256:app-8f21",
"promptRevision": "sha256:prompt-a71c",
"modelContractRevision": "provider-a/model-x@2026-06-30",
"ragIndexRevision": "medical-kb@20260715-02",
"embeddingRevision": "embed-x@3+mean+l2",
"rerankerRevision": "rerank-y@2",
"toolSchemaRevision": "collection-tools@4.2",
"policyRevision": "ai-policy@12",
"parserRevision": "output-parser@5",
"evaluationReport": "sha256:report-991d",
"rollbackTarget": "medical-assistant@20260701.8",
"createdBy": "release-pipeline",
"contentHash": "sha256:bundle-66e0"
}为什么Bundle必须不可变
若 bundleId 不变但里面的 Prompt 或索引被替换:
- 评测报告验证的不是当前生产内容。
- 回滚到同一 ID 也无法恢复旧行为。
- 不同节点可能缓存不同内容。
- 审计记录失去证据价值。
正确方式是内容变化就产生新 Revision 和新 Bundle。可变的 stable、canary 只是路由别名,最终必须解析到不可变 Bundle ID。
六、可运行Demo一:构建、校验和签名发布包
下面只使用 Python 标准库,演示稳定序列化、Hash、依赖兼容性和防篡改验证。
from __future__ import annotations
from dataclasses import dataclass, asdict
import hashlib
import json
@dataclass(frozen=True)
class ReleaseBundle:
bundle_id: str
app_revision: str
prompt_revision: str
model_revision: str
embedding_revision: str
index_embedding_revision: str
tool_schema_revision: str
prompt_tool_schema_revision: str
policy_revision: str
evaluation_report_hash: str
rollback_target: str
def canonical_bytes(bundle: ReleaseBundle) -> bytes:
payload = json.dumps(
asdict(bundle),
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
)
return payload.encode("utf-8")
def content_hash(bundle: ReleaseBundle) -> str:
return "sha256:" + hashlib.sha256(canonical_bytes(bundle)).hexdigest()
def validate_compatibility(bundle: ReleaseBundle) -> list[str]:
errors = []
if bundle.embedding_revision != bundle.index_embedding_revision:
errors.append("查询Embedding与索引空间不兼容")
if bundle.tool_schema_revision != bundle.prompt_tool_schema_revision:
errors.append("Prompt工具Schema与后端执行器不兼容")
if not bundle.evaluation_report_hash.startswith("sha256:"):
errors.append("缺少不可变评测报告Hash")
if bundle.bundle_id == bundle.rollback_target:
errors.append("回滚目标不能是当前Bundle")
return errors
candidate = ReleaseBundle(
bundle_id="medical-assistant@20260716.3",
app_revision="sha256:app-8f21",
prompt_revision="sha256:prompt-a71c",
model_revision="provider-a/model-x@2026-06-30",
embedding_revision="embed-x@3+mean+l2",
index_embedding_revision="embed-x@3+mean+l2",
tool_schema_revision="collection-tools@4.2",
prompt_tool_schema_revision="collection-tools@4.2",
policy_revision="ai-policy@12",
evaluation_report_hash="sha256:report-991d",
rollback_target="medical-assistant@20260701.8",
)
errors = validate_compatibility(candidate)
assert not errors, errors
stored_hash = content_hash(candidate)
print("validated:", candidate.bundle_id)
print("content hash:", stored_hash)
# 模拟有人绕过流水线修改了Prompt Revision。
tampered = ReleaseBundle(**{
**asdict(candidate),
"prompt_revision": "sha256:unreviewed-prompt",
})
print("tamper detected:", content_hash(tampered) != stored_hash)
# 模拟新Embedding查询旧索引,即使维度相同也不允许发布。
incompatible = ReleaseBundle(**{
**asdict(candidate),
"embedding_revision": "embed-new@1+mean+l2",
})
print("compatibility errors:", validate_compatibility(incompatible))Hash 证明内容是否变化,但它本身不证明发布者可信。真实系统还要用受控 CI 身份、制品库权限、签名和审批链保证“谁有权产生这个 Hash”。
七、发布状态机为什么不能只用一个status字段随便改
flowchart TD
A["DRAFT"] --> B["BUILT"]
B --> C["EVALUATED"]
C --> D{"门禁是否通过"}
D -- "否" --> X["REJECTED"]
D -- "是" --> E["APPROVED"]
E --> F["SHADOW"]
F --> G["CANARY"]
G --> H["STABLE"]
G --> I["ROLLED_BACK"]
H --> J["RETIRED"]每个状态的进入条件
| 状态 | 证据 |
|---|---|
| BUILT | 所有Revision存在、Hash正确、依赖兼容 |
| EVALUATED | 完整Run Manifest、逐Case结果、报告Hash |
| APPROVED | 硬门禁通过、风险所有者审批 |
| SHADOW | 写工具禁用/模拟、容量和数据策略通过 |
| CANARY | Shadow无阻断问题、Canary停止条件已配置 |
| STABLE | 达到请求量和观察时间,线上指标通过 |
| RETIRED | 无新流量、保留期和依赖检查完成 |
状态迁移必须使用乐观锁:
UPDATE ai_release
SET status = 'CANARY', version = version + 1
WHERE bundle_id = :bundleId
AND status = 'SHADOW'
AND version = :expectedVersion;受影响行数为 0 表示状态已被别人改变,发布器必须重新读取,不能覆盖。这防止两个操作员同时把不同候选提升为 Stable。
八、环境晋级为什么必须同包Promote
推荐流程:
flowchart TD
A["一次构建不可变Bundle"] --> B["开发环境验证"]
B --> C["测试环境集成与安全评测"]
C --> D["预发环境容量与真实依赖验证"]
D --> E["生产Shadow"]
E --> F["生产Canary"]
F --> G["生产Stable"]不能在每个环境重新执行“从 main 分支取最新内容再构建”,否则测试通过的包和生产包可能不是同一个。环境差异应通过受控引用注入,例如数据库地址、Provider 凭据、容量配额,而不是改 Bundle 内的逻辑资产。
Secret为什么不放进Bundle
Bundle 保存 Secret 的引用和所需权限,不保存明文。部署时运行身份从 Secret Manager/KMS 获取;日志只记录 Secret Revision 标识,不能记录值。轮换 Secret 不应改变模型行为,但也要记录生效时间并验证连接。
九、评测和发布门禁
发布不是“所有分数取平均后大于80”:
- Manifest、样本和评分基础设施必须完整。
- Secret、跨租户、未授权写 Tool 等 Critical 样本零失败。
- JSON、正确拒答和引用合法率满足场景阈值。
- 候选与基线按同 Case 配对比较并按风险分层。
- P95/P99 延迟、Token、金额和并发容量达标。
- 新模型、Tool 或索引通过兼容性验证。
- 明确 Canary 停止条件和已验证的回滚目标。
完整评测方法见 Prompt评测从样本契约到灰度门禁。评测报告必须引用精确 Bundle,不能先评测 Prompt A,审批后把索引 B 临时换进去。
十、Prompt发布流程
flowchart TD
A["修改Prompt Source"] --> B["变量Schema和角色单元测试"]
B --> C["生成不可变Prompt Revision"]
C --> D["组装候选Bundle"]
D --> E["配对离线评测与安全门禁"]
E --> F{"是否通过"}
F -- "否" --> G["拒绝并保留报告"]
F -- "是" --> H["审批和Shadow"]
H --> I["Canary稳定路由"]
I --> J{"线上门禁"}
J -- "通过" --> K["提升Stable"]
J -- "失败" --> L["切回稳定Bundle"]Prompt 变更要记录动机、影响场景、变量兼容性、Token 差值、评测结果和回滚目标。在线编辑器可以用于草稿,但保存不能直接等于生产生效。
十一、知识库发布和回滚
知识库不是一堆随时原地更新的向量:
flowchart TD
A["冻结文档快照"] --> B["解析、脱敏、切分和稳定ID"]
B --> C["用固定Embedding批量向量化"]
C --> D["写候选索引"]
D --> E["数量、维度、ACL和抽样对账"]
E --> F["检索与端到端评测"]
F --> G["发布不可变Index Revision"]
G --> H["Bundle引用并灰度"]
H --> I["原子切换Alias或版本路由"]为什么新Embedding通常必须新索引
Embedding Revision 由模型、Tokenizer、Query/Document 指令、Pooling、归一化、维度和预处理共同定义。维度相同不代表坐标空间相同。查询侧和索引侧 Revision 不一致时,距离没有预期语义。
Alias切换怎样避免半发布
先完整构建候选索引,验证通过后使用向量库提供的原子 Alias 操作,或在应用配置存储中用 compare-and-set 更新:
期望当前 stable = index-v41
原子修改 stable = index-v42如果当前值已不是 v41,说明有并发发布,操作失败并重新评估。不要先删除旧索引再创建新索引;旧索引应保留到观察期结束和引用清零。
十二、模型和Provider发布
模型路由表至少包含:
- 业务场景和风险等级。
- 主模型、备用模型和精确 Revision。
- Region、数据级别和批准状态。
- JSON Schema、Tool、流式、上下文等能力。
- Deadline、并发、Token和金额限额。
- 降级允许条件。
“主模型超时就切任意小模型”并不安全。备用模型必须对目标场景单独评测;若不支持 Tool Schema 或高风险质量不达标,应拒答、排队或转人工。
Provider漂移
Provider 可能更新别名背后的模型、Tokenizer、内容过滤或限流策略。若无法固定 Revision,应:
- 保存响应版本、Request ID 和 Usage。
- 持续运行小型控制集。
- 监控输出长度、拒答、格式、Tool、TTFT 和 TPOT 分布。
- 检测异常后冻结晋级并切换经验证路由。
- 不把未知 Provider 自动作为敏感数据故障降级目标。
十三、Tool Schema发布为什么要前后兼容
工具调用涉及三个版本:模型看到的 Schema、应用校验器、业务执行器。典型安全升级:
- 执行器先支持旧参数和新参数。
- 发布能解析两版的应用。
- 更新 Tool Schema 和 Prompt,引导生成新参数。
- 观察旧调用清零。
- 再移除旧参数支持。
如果先删除后端字段,再更新模型 Schema,仍在运行的旧会话或异步任务会失败。退款、删除、改权限等高风险工具还必须使用计划、确认令牌、幂等键和审计,模型不能成为授权者。
十四、Shadow、Canary和稳定路由
| 阶段 | 用户看到候选吗 | 写Tool | 目标 |
|---|---|---|---|
| Offline | 否 | Fixture | 固定集回归 |
| Shadow | 否 | 禁用或模拟 | 真实输入与容量观察 |
| Canary | 少量用户看到 | 按风险逐步开放 | 验证真实体验和反馈 |
| Stable | 大多数用户看到 | 正常受控执行 | 正式服务 |
Canary 应按 tenantId/userId/conversationId 做稳定 Hash 路由,而不是每次请求随机,否则同一会话前后命中不同 Prompt 和模型。高风险租户应由允许名单控制,不应与普通流量一起随机进入。
停止条件要在发布前定义,例如:
任一跨租户或秘密事件:立即回滚
未授权写Tool:立即回滚并禁用该工具
P95延迟比基线上升30%持续10分钟:暂停晋级
JSON失败率超过1%:回滚
单位请求成本上升20%且质量无显著改善:停止晋级十五、回滚为什么不只是把流量拨回去
flowchart TD
A["触发回滚"] --> B["原子切回Stable Bundle"]
B --> C["停止新候选任务和会话"]
C --> D["处理在途流式请求"]
D --> E["隔离候选缓存"]
E --> F["扫描异步任务和Tool副作用"]
F --> G["补偿或人工对账"]
G --> H["验证指标并保留证据"]15.1 长会话
会话应保存 bundleId。普通回滚可以让新会话使用稳定版,旧候选会话按策略结束或迁移;安全事故则应强制失效候选会话。迁移时要重新校验历史长度、Prompt变量和工具状态,不能只换模型名。
15.2 流式请求
路由切换只影响新请求。已开始的 SSE 可能继续输出候选内容。安全事件需要向应用、Provider传播取消,并由输出网关停止转发;仍要记录已经发给用户的部分内容。
15.3 缓存
缓存 Key 至少包含租户、ACL Hash、场景、Prompt、模型和索引 Revision。这样回滚自然命中新版本空间,不需危险地全库清缓存。没有版本的缓存可能在回滚后继续返回候选答案。
15.4 异步任务和写Tool
已创建工单、已发消息不会随模型路由回滚。任务必须保存 Bundle、Tool Call ID、幂等键和业务单号;结果为 UNKNOWN 时先查权威系统再决定重试。副作用按业务补偿或人工处理,而不是重新调用模型猜测。
十六、可运行Demo二:稳定路由与乐观并发发布
from dataclasses import dataclass
import hashlib
@dataclass
class RouteState:
stable: str
canary: str | None
canary_percent: int
version: int
def bucket(identity: str) -> int:
digest = hashlib.sha256(identity.encode("utf-8")).digest()
return int.from_bytes(digest[:4], "big") % 100
def resolve_bundle(state: RouteState, conversation_id: str) -> str:
if state.canary and bucket(conversation_id) < state.canary_percent:
return state.canary
return state.stable
def compare_and_set(
state: RouteState,
expected_version: int,
new_stable: str,
) -> bool:
if state.version != expected_version:
return False
state.stable = new_stable
state.canary = None
state.canary_percent = 0
state.version += 1
return True
state = RouteState(
stable="bundle-v41",
canary="bundle-v42",
canary_percent=10,
version=7,
)
conversation = "tenant-a:conversation-1001"
first = resolve_bundle(state, conversation)
second = resolve_bundle(state, conversation)
assert first == second
print("sticky route:", first)
# 发布器A使用version=7成功提升;发布器B的旧快照必须失败。
print("publisher A:", compare_and_set(state, 7, "bundle-v42"))
print("publisher B stale update:", compare_and_set(state, 7, "bundle-v43"))
print("current:", state)真实路由状态要存入支持事务或 compare-and-set 的一致性存储,并推送到各节点。节点读取失败时使用最后一份签名配置和短 TTL;恢复后核对版本。单纯在每个 Pod 内改内存变量会导致不同节点路由不一致。
十七、可观测性:一次请求要留下什么
flowchart TD
A["requestId和bundleId"] --> B["路由与鉴权Span"]
B --> C["RAG检索与Rerank Span"]
C --> D["Prompt编译与Token Span"]
D --> E["模型Prefill和Decode Span"]
E --> F["Tool与业务校验Span"]
F --> G["解析、脱敏和响应Span"]17.1 指标
| 类别 | 指标 |
|---|---|
| 系统 | QPS、在途、错误率、超时、队列、P95/P99 |
| 模型 | TTFT、TPOT、输入/输出Token、Finish Reason |
| RAG | Recall代理、无结果率、TopK、Rerank耗时、引用合法率 |
| Tool | 调用率、权限拒绝、业务失败、UNKNOWN、幂等冲突 |
| 质量 | 正确拒答、格式成功、用户纠错、人工转接 |
| 安全 | 注入、跨租户、秘密、策略命中 |
| 成本 | 场景/租户/Bundle的Token、模型费、检索费 |
平均延迟会掩盖长尾,至少看 P50/P95/P99。拒答率上涨可能是资料缺失,也可能是新 Prompt 过度拒答,必须按场景、风险和 Bundle 分组。
17.2 日志和Trace的数据安全
完整 Prompt 聚合用户文本、历史、RAG 和 Tool Result,通常是高敏副本。默认记录 ID、Revision、Token、Hash、分类和错误码;内容调试需审批、采样、脱敏、加密和短保留。不能为了可观测性把密码、病历、系统 Prompt 全量写日志。
Metrics 适合聚合趋势,Trace 适合单请求链路,Log 适合离散事件和审计;三者用 Request ID 关联,不能互相替代。
十八、反馈闭环为什么不能只看点赞率
点赞/点踩存在选择偏差:多数用户不反馈;复杂问题更容易点踩;按钮位置也影响行为。应组合:
- 明确原因标签:事实错误、资料旧、引用错、太慢、格式错。
- 人工转接和用户改写行为。
- 任务是否真正完成,例如工单草案是否被采用。
- 抽样人工复核,而不是只看主动反馈。
- 按 Bundle、场景和风险分层。
反馈进入评测集前要脱敏、去重、确认用途并重新标注。用户点踩不自动等于黄金答案,恶意输入也不能直接成为训练数据。
十九、成本和容量治理
AI 不能只限 QPS。一个 100 Token 请求和一个 100K Token 请求占用完全不同。需要同时控制:
- 请求速率。
- 在途并发和模型槽位。
- 输入、输出和每日 Token。
- 租户/用户/场景金额预算。
- Tool 调用次数和RAG候选数量。
调用前按最大预算预留额度,完成后按真实 Usage 结算并释放剩余额度;进程崩溃要有过期回收。模型路由、Prompt压缩、TopK调整和缓存优化都必须重新做质量评测,不能只为了便宜破坏安全规则。
二十、数据模型不能只有三张流水表
最小逻辑实体包括:
ai_asset_revision 不可变资产及Hash
ai_release_bundle Bundle头和状态
ai_release_dependency Bundle引用的各Revision
ai_evaluation_run Run Manifest和报告
ai_deployment 环境、路由比例、状态和version
ai_request_trace 实际命中Bundle和性能摘要
ai_feedback 受控反馈及标注状态
ai_incident 事故、影响、处置和证据ai_request_trace 必须记录实际解析后的 Bundle,而不是只记录当时的 stable Alias;Alias 后续会变化。发布表用唯一约束防止同环境同场景出现两个 Stable,状态迁移用事务和乐观锁。
二十一、生产Runbook
21.1 代码没发布但答案突然变化
- 按首次异常时间和场景定位 Request ID。
- 对比实际 Bundle、模型响应版本、索引Alias和策略Revision。
- 检查 Provider 漂移、控制面错误推送、知识源自动更新。
- 用保存的固定Case分别运行旧/新Provider和旧/新Judge。
- 切到最近验证的不可变Bundle并冻结自动晋级。
21.2 新Bundle只有部分Pod异常
检查节点缓存的路由版本、配置签名、更新时间和控制面订阅偏移;比较异常Pod的镜像digest和环境变量。将陈旧节点摘流并重新同步,恢复后对全节点做版本对账。不能只重启到“看起来好了”。
21.3 索引切换后拒答率暴增
确认查询Embedding与索引Revision、Alias实际指向、文档数量、ACL分布、Chunk数量和Rerank输入。用固定Query比较旧/新 Recall@K;若门禁失败,原子切回旧索引并保留候选证据。
21.4 Tool失败率在发布后上升
按错误分为Schema解析、权限拒绝、业务校验、下游超时和UNKNOWN。检查模型看到的Schema、应用校验器和执行器版本是否一致。写操作先按幂等键查询结果,不能盲目重试。
21.5 回滚后仍出现候选答案
检查在途流、长会话固定Bundle、异步任务、缓存Key和各Pod路由版本。安全事故应取消在途、失效候选会话、隔离缓存并暂停任务;业务副作用另行对账补偿。
21.6 Token成本突然上涨
按Bundle、场景、租户拆输入/输出Token,查看会话历史、RAG TopK、工具循环、重试和输出长度。先用限额和路由止损,再在固定评测集验证压缩方案,不直接删除安全或引用规则。
21.7 控制面不可用
运行面继续使用最后一份签名有效的Stable配置,停止新发布和晋级;若本地配置过期超过安全期限,按场景选择只读降级或拒绝。恢复后对账路由版本、漏发事件和审批状态。
21.8 评测通过但线上质量下降
检查评测数据是否代表线上长度、语言、租户、权限和附件;比较线上请求分布与数据集标签。将事故请求脱敏标注后加入Regression,并保留新的Blind集合,不能只针对原句修改Prompt。
二十二、灾备和恢复
LLMOps 的恢复对象不仅是数据库:
- 资产制品库和签名元数据。
- Release Bundle、审批和路由历史。
- Prompt、策略和 Tool Schema Revision。
- 文档快照、索引构建清单和可重建脚本。
- 评测集、Run Manifest 和报告。
- 删除清单和数据保留策略。
向量索引可选择备份或从文档快照重建,但必须验证恢复时间是否满足 RTO。恢复后先在隔离环境核对Hash、数量、ACL和检索质量,再恢复流量;不能只看到索引“能打开”就认为恢复成功。
二十三、面试标准回答
LLMOps是什么
LLMOps是大模型应用的工程治理体系。它把代码、Prompt、模型、RAG索引、Embedding、Tool Schema、策略和评测报告组装成不可变Release Bundle,通过兼容性校验、离线评测、审批、Shadow、Canary和Stable状态机发布;运行时记录实际Bundle、质量、延迟、成本和安全证据,并处理缓存、会话、异步任务和业务副作用,使系统可复现、可灰度、可回滚、可审计。
LLMOps和传统DevOps的区别
DevOps仍负责代码、镜像、基础设施和稳定性;LLMOps额外治理多资产共同决定的非确定性质量,包括Prompt、模型、知识索引、Judge、引用、拒答、Tool权限和Token成本。LLMOps建立在DevOps之上,不能替代普通测试、事务、监控和灾备。
Prompt为什么要版本管理
Prompt改变业务行为且与变量Schema、模型能力、RAG和Tool耦合。修改后应生成不可变Revision,组装候选Bundle,做配对评测和硬门禁,再Shadow/Canary;运行日志记录实际Revision,异常时切回经过验证的整个Bundle,而不是只复制一段旧文本。
RAG知识库为什么要发布和回滚
文档、切分、Embedding、ACL和Reranker都会改变召回。生产应从冻结快照构建独立候选索引,校验数量、维度和权限,运行检索与端到端评测,通过后原子切换Alias;旧索引在观察期保留,异常时切回。新Embedding通常不能直接查询旧空间。
AI回滚为什么比普通配置回滚复杂
路由切回只影响新请求;在途流、长会话、缓存和异步任务可能仍引用候选,Tool已经产生的工单或消息也不会自动撤销。回滚要同时处理流量、会话、缓存、任务、Provider取消和业务补偿,并保存审计证据。
二十四、关联知识点
| 知识点 | 作用 |
|---|---|
| Prompt评测、门禁与灰度 | 设计样本、Judge、统计和硬门禁 |
| RAG建库与索引发布 | 理解候选索引、Alias和恢复 |
| Tool Calling安全执行 | 理解权限、确认、幂等和UNKNOWN |
| AI数据安全 | 管理日志、评测集、Provider和删除传播 |
| AI推理优化 | TTFT、TPOT、容量和成本优化 |
| AI应用架构 | Deadline、异步任务、Outbox和多Region |
| AI面试题 | 使用标准回答并返回原理页 |
二十五、学习验收清单
- [ ] 能区分Source、Revision、Bundle、Deployment和Instance。
- [ ] 能画出控制面与运行数据面。
- [ ] 能列出一个Bundle中的全部依赖和兼容约束。
- [ ] 能解释不可变Hash能证明什么、不能证明什么。
- [ ] 能设计发布状态机和乐观锁迁移。
- [ ] 能解释为什么环境间必须Promote同一Bundle。
- [ ] 能设计Prompt、索引、模型和Tool Schema发布流程。
- [ ] 能说明Shadow和Canary的写工具边界。
- [ ] 能处理回滚后的会话、流、缓存、任务和业务副作用。
- [ ] 能设计按Bundle分组的指标、Trace和安全日志。
- [ ] 能执行Provider漂移、索引错误和控制面故障Runbook。
- [ ] 能定义资产、发布、部署、评测和事故的数据模型。
本章小结
LLMOps的核心不是多一个平台名词,而是把会影响AI行为的所有资产固化为一个可验证的发布单元,并让它沿可审计状态机晋级。不可变Bundle解决“到底发布了什么”,评测门禁解决“是否允许发布”,稳定路由和原子切换解决“谁正在使用”,可观测性解决“实际发生了什么”,完整回滚和Runbook解决“出错后怎样恢复”。只有这些过程形成闭环,AI系统才从能演示走向可长期运营。
