Skip to content

LLMOps从资产版本到生产发布恢复完整原理

LLMOps 不是“给 Prompt 加一个版本号”,也不是只监控 Token。它是一套让 AI 应用的代码、Prompt、模型、RAG、工具、策略和评测结果能够一起被构建、验证、审批、发布、观测、回滚和审计的工程体系。

传统接口通常对同一输入给出确定结果;AI 应用不仅可能随机,而且答案同时受模型服务、知识索引和上下文影响。若没有 LLMOps,线上出现“今天为什么答错”时,团队甚至无法确定用户实际命中了哪套资产。

学习目标

学完后应能:

  1. 解释 LLMOps 与 DevOps、MLOps 的关系和边界。
  2. 画出控制面与运行数据面的完整职责。
  3. 区分源资产、构建产物、发布包、部署和运行实例。
  4. 用不可变 Revision 和 Hash 组装可重放的 Release Bundle。
  5. 解释为什么 Prompt、模型、RAG、Tool 和策略不能各自随意上线。
  6. 设计 Draft、Built、Evaluated、Approved、Canary、Stable、Retired 状态机。
  7. 完成开发、测试、预发、生产的同包晋级,而不是生产重新构建。
  8. 设计质量、安全、性能、成本和兼容性门禁。
  9. 讲清 Alias 原子切换、乐观锁和并发发布问题。
  10. 处理长会话、缓存、流式请求和异步任务造成的版本残留。
  11. 建立 Trace、指标、日志、反馈和数据安全体系。
  12. 执行 Prompt、模型、索引、工具、Provider 漂移等事故 Runbook。

一、LLMOps、DevOps和MLOps是什么关系

体系主要对象重点问题
DevOps代码、配置、镜像、基础设施能否稳定构建、测试、部署和恢复
MLOps数据集、特征、训练代码、模型权重能否重现训练、评估、部署和监控漂移
LLMOpsPrompt、基础/微调模型、RAG、Tool、策略、Judge非确定性质量、引用、权限、Token和多资产协同发布

三者是叠加关系,不是互相替代。Spring Boot AI 网关仍需要普通 CI/CD;若训练或微调模型,还需要 MLOps;当模型被组装成 RAG、Agent 或 Tool Calling 应用时,再增加 LLMOps。

不做LLMOps会怎样

  • 运营直接在线改 Prompt,失败后不知道旧文本。
  • model-latest 被 Provider 改指向,代码没发布但答案变化。
  • 新 Embedding 查询旧索引,维度相同却语义空间不兼容。
  • Tool Schema 升级后旧 Prompt 仍生成已删除参数。
  • 新索引原地覆盖,错误资料无法快速切回。
  • 总平均分上升,但跨租户引用或写工具越权。
  • 回滚路由后,旧会话、缓存和异步任务仍使用候选版本。
  • 日志只有问题和答案,没有版本矩阵,事故只能猜。

二、控制面和运行数据面

mermaid
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 --> B

2.1 控制面负责什么

  • 资产登记、版本、依赖和所有者。
  • 构建知识索引、Prompt 包、策略包和容器镜像。
  • 运行离线评测和安全门禁。
  • 审批、环境晋级、灰度比例和回滚。
  • 保存发布历史、证据和审计记录。

2.2 运行数据面负责什么

  • 接收真实请求并鉴权、限流。
  • 根据场景、租户、会话和灰度规则解析发布版本。
  • 执行 Prompt、RAG、模型、Tool 和输出校验。
  • 记录请求实际命中的 Revision、性能、成本和安全结果。
  • 在 Deadline 内降级、拒绝或进入异步状态。

控制面故障不应让正在运行的稳定版本立即不可用。运行面应缓存最后一份签名有效配置;但控制面恢复后要对账,避免节点长期使用陈旧路由。

三、先区分五种容易混淆的对象

对象示例是否可变
SourceGit中的Prompt模板、Markdown文档开发中可变
Revisionprompt@sha256:...index@build-42发布后不可变
Release Bundle一组兼容Revision及门禁证据不可变
DeploymentBundle在prod环境的Canary配置可改变流量状态
Runtime Instance某个Pod、Worker或会话实际执行短生命周期

Source 更新不等于线上生效。构建过程把 Source 固化为不可变 Revision;Release Bundle 再把多个 Revision 绑定成一个整体。Deployment 只控制这个整体在哪个环境、接多少流量。

如果直接让生产读取 Git 分支、数据库中可编辑 Prompt 或动态 Alias,就无法证明一次请求对应哪份不可变内容。

四、需要管理的资产和依赖关系

mermaid
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怎样设计

一个最小发布包:

json
{
  "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 或索引被替换:

  1. 评测报告验证的不是当前生产内容。
  2. 回滚到同一 ID 也无法恢复旧行为。
  3. 不同节点可能缓存不同内容。
  4. 审计记录失去证据价值。

正确方式是内容变化就产生新 Revision 和新 Bundle。可变的 stablecanary 只是路由别名,最终必须解析到不可变 Bundle ID。

六、可运行Demo一:构建、校验和签名发布包

下面只使用 Python 标准库,演示稳定序列化、Hash、依赖兼容性和防篡改验证。

python
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字段随便改

mermaid
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写工具禁用/模拟、容量和数据策略通过
CANARYShadow无阻断问题、Canary停止条件已配置
STABLE达到请求量和观察时间,线上指标通过
RETIRED无新流量、保留期和依赖检查完成

状态迁移必须使用乐观锁:

sql
UPDATE ai_release
SET status = 'CANARY', version = version + 1
WHERE bundle_id = :bundleId
  AND status = 'SHADOW'
  AND version = :expectedVersion;

受影响行数为 0 表示状态已被别人改变,发布器必须重新读取,不能覆盖。这防止两个操作员同时把不同候选提升为 Stable。

八、环境晋级为什么必须同包Promote

推荐流程:

mermaid
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”:

  1. Manifest、样本和评分基础设施必须完整。
  2. Secret、跨租户、未授权写 Tool 等 Critical 样本零失败。
  3. JSON、正确拒答和引用合法率满足场景阈值。
  4. 候选与基线按同 Case 配对比较并按风险分层。
  5. P95/P99 延迟、Token、金额和并发容量达标。
  6. 新模型、Tool 或索引通过兼容性验证。
  7. 明确 Canary 停止条件和已验证的回滚目标。

完整评测方法见 Prompt评测从样本契约到灰度门禁。评测报告必须引用精确 Bundle,不能先评测 Prompt A,审批后把索引 B 临时换进去。

十、Prompt发布流程

mermaid
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 差值、评测结果和回滚目标。在线编辑器可以用于草稿,但保存不能直接等于生产生效。

十一、知识库发布和回滚

知识库不是一堆随时原地更新的向量:

mermaid
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 更新:

text
期望当前 stable = index-v41
原子修改 stable = index-v42

如果当前值已不是 v41,说明有并发发布,操作失败并重新评估。不要先删除旧索引再创建新索引;旧索引应保留到观察期结束和引用清零。

十二、模型和Provider发布

模型路由表至少包含:

  • 业务场景和风险等级。
  • 主模型、备用模型和精确 Revision。
  • Region、数据级别和批准状态。
  • JSON Schema、Tool、流式、上下文等能力。
  • Deadline、并发、Token和金额限额。
  • 降级允许条件。

“主模型超时就切任意小模型”并不安全。备用模型必须对目标场景单独评测;若不支持 Tool Schema 或高风险质量不达标,应拒答、排队或转人工。

Provider漂移

Provider 可能更新别名背后的模型、Tokenizer、内容过滤或限流策略。若无法固定 Revision,应:

  1. 保存响应版本、Request ID 和 Usage。
  2. 持续运行小型控制集。
  3. 监控输出长度、拒答、格式、Tool、TTFT 和 TPOT 分布。
  4. 检测异常后冻结晋级并切换经验证路由。
  5. 不把未知 Provider 自动作为敏感数据故障降级目标。

十三、Tool Schema发布为什么要前后兼容

工具调用涉及三个版本:模型看到的 Schema、应用校验器、业务执行器。典型安全升级:

  1. 执行器先支持旧参数和新参数。
  2. 发布能解析两版的应用。
  3. 更新 Tool Schema 和 Prompt,引导生成新参数。
  4. 观察旧调用清零。
  5. 再移除旧参数支持。

如果先删除后端字段,再更新模型 Schema,仍在运行的旧会话或异步任务会失败。退款、删除、改权限等高风险工具还必须使用计划、确认令牌、幂等键和审计,模型不能成为授权者。

十四、Shadow、Canary和稳定路由

阶段用户看到候选吗写Tool目标
OfflineFixture固定集回归
Shadow禁用或模拟真实输入与容量观察
Canary少量用户看到按风险逐步开放验证真实体验和反馈
Stable大多数用户看到正常受控执行正式服务

Canary 应按 tenantId/userId/conversationId 做稳定 Hash 路由,而不是每次请求随机,否则同一会话前后命中不同 Prompt 和模型。高风险租户应由允许名单控制,不应与普通流量一起随机进入。

停止条件要在发布前定义,例如:

text
任一跨租户或秘密事件:立即回滚
未授权写Tool:立即回滚并禁用该工具
P95延迟比基线上升30%持续10分钟:暂停晋级
JSON失败率超过1%:回滚
单位请求成本上升20%且质量无显著改善:停止晋级

十五、回滚为什么不只是把流量拨回去

mermaid
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二:稳定路由与乐观并发发布

python
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 内改内存变量会导致不同节点路由不一致。

十七、可观测性:一次请求要留下什么

mermaid
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
RAGRecall代理、无结果率、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调整和缓存优化都必须重新做质量评测,不能只为了便宜破坏安全规则。

二十、数据模型不能只有三张流水表

最小逻辑实体包括:

text
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 代码没发布但答案突然变化

  1. 按首次异常时间和场景定位 Request ID。
  2. 对比实际 Bundle、模型响应版本、索引Alias和策略Revision。
  3. 检查 Provider 漂移、控制面错误推送、知识源自动更新。
  4. 用保存的固定Case分别运行旧/新Provider和旧/新Judge。
  5. 切到最近验证的不可变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系统才从能演示走向可长期运营。