Skip to content

4.3.6 如何做版本管理、回滚和失效控制? ​

先给结论:只要知识会更新、规则会变化、内容可能出错,你就不能只设计“怎么写入”,还必须设计“怎么失效、怎么回滚、怎么区分版本”。

否则系统一旦写入错误内容,往往会出现两种尴尬局面:

  • 错的内容已经被检索出来,但你不能立刻让它退出
  • 改回去以后,旧内容和新内容又混在一起

这就是版本管理、回滚和失效控制存在的意义。

为什么“更新成功”不等于“系统可控” ​

很多系统的第一版更新逻辑只有一件事:

  • 新数据写进索引

但真实世界里还会发生这些事:

  • 规则刚更新,后来发现写错了
  • 新版本发布后,用户反馈回答异常,需要退回旧版本
  • 某批数据本不该上线,需要立即失效
  • 某些内容需要定时生效或定时失效

这时候如果系统没有版本和失效机制,就只能临时手工删数据,既慢也不稳。

一、版本管理到底在管理什么 ​

版本管理不只是给文档挂一个 v1、v2 标签。

更实际地说,它至少要帮助系统回答:

  • 当前哪个版本是有效版本
  • 旧版本是否仍要保留
  • 多个版本能不能同时存在但只允许一个参与检索
  • 回滚时该切回哪一个版本

所以一个更常见的最小字段集合通常包括:

  • doc_id
  • version
  • status
  • updated_at
  • valid_from
  • valid_to

其中:

  • version 负责区分版本身份
  • status 负责控制是否可用
  • valid_from / valid_to 负责时间边界

二、失效控制为什么不能只靠物理删除 ​

有些场景下,物理删除当然可以直接解决问题。

但很多生产系统里,失效不一定等于立刻删除。因为你可能还需要:

  • 审计历史
  • 回查旧版本
  • 快速回滚
  • 灰度切换

这时候更稳的方式通常是先让对象“退出检索范围”,再决定要不要物理删除。

常见做法包括:

  • status = inactive
  • is_deleted = true
  • valid_to = 当前时间

然后在检索阶段把这些对象过滤掉。

这样做的好处是:

  • 生效快
  • 可回滚
  • 便于审计

但前提是你的过滤链路必须稳定,不能让失效对象漏进召回。

三、回滚机制更像什么 ​

回滚的本质不是“再改一次数据”,而是让系统有能力快速切回上一个稳定状态。

比较稳的思路通常有两类。

1. 对象级回滚 ​

适合:

  • 局部文档出错
  • 小范围修复

做法通常是:

  • 保留旧版本对象
  • 把当前版本设为 inactive
  • 把上一版本重新设为 active

优点是精确、成本低,缺点是版本状态管理要足够清楚。

2. 索引级回滚 ​

适合:

  • 大范围变更
  • 切块策略整体变化
  • embedding 模型切换
  • 一整个索引版本需要切换

做法通常是:

  • 新旧索引并行存在
  • 通过别名、路由或配置切换查询目标

这种方式更重,但在大版本迁移时更稳。

一个更实际的实现框架 ​

如果只讲最小可用思路,你可以先把版本和失效控制理解成:

  1. 每个索引对象都带 doc_id、version、status
  2. 查询时只允许 status = active 的对象参与召回
  3. 新版本上线时,先写新对象,再切状态
  4. 回滚时,重新切回上一版本状态
  5. 旧版本是否物理删除,由单独清理任务决定

一个最小示意可以写成这样:

python
def activate_new_version(doc_id, new_version):
    mark_inactive(doc_id=doc_id, status="active")
    mark_active(doc_id=doc_id, version=new_version)

def rollback_version(doc_id, rollback_to):
    mark_inactive(doc_id=doc_id, status="active")
    mark_active(doc_id=doc_id, version=rollback_to)

这段代码很简化,但表达了一个关键原则:

  • 上线和回滚,本质上都是状态切换

四、什么时候应该“立即失效” ​

下面这些场景,通常要优先考虑快速失效机制,而不是等下一次大同步:

  1. 权限配置错误,内容不该再被看到
  2. 政策内容错误,继续回答会误导用户
  3. 某个租户文档误入了其他租户范围
  4. 某批数据质量异常,继续召回会污染回答

这种场景下,系统越能快一点让对象退出检索,风险越低。

所以很多生产系统都会把:

  • 立即失效
  • 延后物理清理

拆成两步处理。

五、上线前最值得检查什么 ​

如果你想确认版本、回滚和失效机制是否真的可用,至少检查这几件事:

  1. 新旧版本会不会同时进入一次回答
  2. 某个版本设为失效后,检索里还能不能召回到它
  3. 回滚到上一版本后,结果是否能快速稳定恢复
  4. 删除和失效是否混淆,导致该保留的历史被直接清空
  5. 查询侧过滤条件是否和写入侧状态字段一致

这些检查的目标只有一个:确认系统不是“写得进去”,而是真的“控得住”。

一个常见误区 ​

很多人会把版本管理理解成:

  • 数据库里有个版本号字段就够了

这通常不够。

因为真正决定系统稳不稳的,不是“有没有版本号”,而是:

  • 查询时是否只取当前有效版本
  • 旧版本是否能退出检索
  • 回滚时是否能快速恢复
  • 状态切换是否和索引对象一致

如果这些链路没打通,版本号只是一个摆设。

一句话总结 ​

版本管理、回滚和失效控制的核心,不只是记录历史,而是让系统能明确知道谁当前有效、谁应该退出、出错时怎么快速切回稳定状态。只设计写入、不设计退出和回滚,索引就很难长期可控。