推荐语
Loop Engineering正成为RAG运维的新范式,让系统自动发现问题、分派任务、检查结果,彻底改变传统脚本维护的繁琐模式。Milvus 3.0为这一变革提供了关键的向量数据库支撑。核心内容: 1. Loop Engineering的核心原理及其在RAG运维中的优势 2. 从Prompt Engineering到Loop Engineering的范式转变 3. Milvus 3.0如何针对性支持Loop Engineering的运行
杨芳贤
53AI创始人/腾讯云(TVP)最具价值专家
最近,在硅谷又多了一个流行新词,Loop Engineering。而伴随Loop Engineering的爆火,Anthropic Claude Code 负责人 Boris Cherny、OpenClaw的创始人Peter在内诸多大神开始公开发声:loop会彻底取代prompt,成为人与LLM交互的新范式。这里可能很多人还不清楚发生了什么。翻译一下,所谓Loop,就是你设计一套系统,让它自己发现问题、分派任务、检查结果、记录状态,并决定下一步的思路。而Prompt Engineering 只能你不断告诉模型下一步做什么。在 coding 场景里,这套模式已经跑起来了。比如每天自动扫描 CI 失败、issue、commit,让 agent 修复代码,再让另一个 agent 审查,通过后自动提 PR。RAG 系统的数据运维,也同样非常适合用 loop 的方式重新组织。因为 RAG 系统最大的问题,往往不是第一次把知识库建起来,而是后面每天都要维护它:灌文档、刷索引、换模型、清过期、故障恢复……很多团队今天还是靠脚本处理这些事,等到下次再出问题,再改脚本。而这些任务,其实非常适合用 loop 循环去做优化,因为无论是coding场景,还是RAG场景,其实都需要持续触发、隔离执行、状态记录、结果验证和失败恢复。但如何从向量数据库层面去支撑Loop Engineering的运行?以下是我们对Loop Engineering的一些理解,以及我们在Milvus 3.0 中的思考与针对性能力建设,希望可以帮你做好RAG建设。01 Loop Engineering 到底是什么,为什么RAG需要它
过去两年,很多人使用 coding agent 的方式是这样的:你写一个 prompt—模型返回结果—你看结果—你再写下一个 prompt。这个过程里,人是循环的一部分。每一轮都需要人在场。agent 能走多远,取决于人的判断力。agent 能跑多快,取决于人的时间。这就是典型的 Prompt Engineering。在Loop Engineering 行业彻底换了一个思路:人类设计一个循环系统,这个循环会自动触发任务,调用 agent,检查输出,记录状态,然后决定下一步。这个系统自动发现需要做的工作,把工作分派给 agent,检查 agent 的输出,记录结果,然后决定下一步做什么。人类从循环中,跳到了循环外,变成设计循环的人。每天早上,系统自动读取昨天失败的 CI、扫描新开的 issue、检查最近的 commit。对每一个值得处理的问题,它都会新开一个隔离工作区,让一个 agent 去修复。修完之后,再让另一个 agent 审查。审查通过,就自动提 PR、关联 issue、通知相关人。审查不过,就进入人工 triage 队列。不难发现, loop engineering 的适用范围不止于写代码。任何需要持续运行、多步骤、有判断、需要记住上下文的 agent 工作流,都可以用 loop 的框架来组织。RAG 系统看起来和 coding agent 不一样,但它们都有一个共同点:工作不是一次性的。在这些系统中,文档会更新、数据源会增加、业务规则会变化、embedding 模型会升级、旧内容会污染结果……系统中的数据运维天然是一个持续循环。手动处理,在数据量小、更新频率低的时候可以接受。但一旦系统持续运行,数据量增加,就会出现:写入和查询同时发生时,很难判断读到的是不是最新数据(这对agent尤其重要)每次改 embedding 模型或索引策略都缺少清晰的版本边界(数据量变大之后,引入embedding是必须的)系统故障后如果没有 checkpoint只能重跑一遍02 Loop 的五个构件,对RAG有什么启发
Addy Osmani 在 Loop Engineering 一文中把 loop engineering 拆成了五个构件加一个记忆层:Automations:按节奏自动触发任务,是 loop 的心跳Worktrees:让多个 agent 在隔离环境里并行工作。Skills:把项目知识写下来,agent 不用每次从零猜Connectors:连接 issue tracker、CI、Slack、代码仓库等外部系统。Sub-agents:把执行和检查拆开,不让同一个 agent 自写自审。Memory:记录跨轮状态,知道什么做完了、什么失败了、下一轮从哪里继续。Automations 对应持续的数据与索引的持续刷新;Memory 对应 checkpoint 和版本状态;Sub-agents 对应写入、验证、线上读取之间的职责拆分。Skills 对应数据处理规则,比如 chunking 策略、metadata 提取规则、字段索引规则、数据源优先级等。Connectors 对应数据源和下游应用(Milvus 3.0 的 External Collection 本身就承担了部分 connector 的角色,下文会展开)。此外,RAG 的数据 loop 还有两个 coding agent 不太操心的问题:第二,向量库一旦故障,恢复成本远高于重跑一个 coding task。03 Heartbeat:数据灌入成为持续任务后,要如何处理索引
在 coding agent 的 loop 里,Automations 是心跳,它会每天早上自动跑一轮 CI triage,读昨天的失败、issue、commit,把发现写进状态文件,触发下游修复。没传统做法是把数据搬进向量库。数据在对象存储或数据库里有一份,在 Milvus 里又有一份。然后你还要维护两边的同步关系。这个过程中,全量重灌的成本随数据量线性增长,不可持续。因此,在Milvus 3.0 中,我们在 External Collection 设计上换了一个思路:数据不搬家,索引去数据那里。External Collection 可以直接引用外部存储里的数据,比如 S3 上的数据,Parquet、Lance、Vortex、Iceberg 格式都可以。这个过程中,Milvus 不需要把数据复制进自己的存储,而是在外部数据上建立映射和索引,然后提供查询能力。schema = MilvusClient.create_schema( external_source="s3://my-bucket/path/to/data/", external_spec='{"format": "parquet"}')schema.add_field( field_name="text", datatype=DataType.VARCHAR, max_length=256, external_field="source_text_column" )schema.add_field( field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=128, external_field="embedding_column")
当源数据发生变化时,loop 可以调用 refresh_external_collection() 触发增量刷新。这个 API 是异步的,返回一个 job_id,你可以追踪刷新状态:job_id = client.refresh_external_collection( collection_name="my_external_collection")progress = client.get_refresh_external_collection_progress(job_id=job_id)
这个动作就是 RAG 数据 loop 的心跳:帮助我们把“哪些数据已经处理过、哪些数据需要更新”这类状态管理交给基础设施,而不是散落在业务脚本里。需要注意的是,External Collection 更适合只读或以外部数据源为主的场景,不支持 insert、delete、upsert。数据修改必须发生在源头(对象存储上的文件),Milvus 通过 refresh 同步变化。这个约束价值在于:源数据只有一个写入点,避免 Milvus 和数据湖之间出现不一致的问题。04 Checkpoint:Loop 跑了一百轮,出问题怎么回滚
Coding agent 里常用 worktree 做隔离。每个 agent 在自己的分支或工作区里改代码。改坏了,不影响主分支。验证通过,再合并。RAG的数据 loop 也需要类似能力,只是对象从代码变成了向量数据。比如你换了一个新的 embedding 模型,重新生成了几百万条向量,也重建了索引。上线后发现,新模型在某类 query 上效果变差了。这时你需要回到上一个好版本。传统做法是维护两套 collection,然后手动切换。更糟糕的情况是没有备份,只能把旧模型重新跑一遍。Milvus 3.0 的 Snapshot 可以被理解为 RAG 数据 loop 的 checkpoint。Snapshot 是 collection 在某个时间点的只读视图。它记录向量数据、元数据、索引和 schema 在某个时间点的完整状态。关键是,create 操作是轻量的,创建通常在毫秒级完成,不复制实际数据:client.flush(collection_name="my_collection")client.create_snapshot( collection_name="my_collection", snapshot_name="loop_round_42", description="embedding model v2, index HNSW M=16")
发现问题需要回滚时,restore 是异步操作,直接复制 segment 文件到新 collection,跳过索引重建:job_id = client.restore_snapshot( snapshot_name="loop_round_42", collection_name="my_collection_rollback" )state = client.get_restore_snapshot_state(job_id=job_id)
这和 coding agent 的 worktree 逻辑是类似的:worktree 让你在不影响主分支的情况下实验,出问题可以丢掉分支回到主分支。Snapshot 让你在不影响线上 collection 的情况下打版本,出问题可以 restore 回到上一个好的状态。Snapshot 还有一个在 loop 场景下特别有用的能力:你可以在同一个 snapshot 上跑验证查询。你的 loop 可以先在新版本上跑一组测试 query,对比旧版本的检索效果。通过了再切到线上。没通过就保留旧版本。这也是数据管理 loop 里的 maker-checker 分离:写数据的环节和验证数据的环节不应该混在一起。05 一致性:写数据的 Agent 和读数据的 Agent 看到的是不是同一份怎么办?
在 coding agent 里,worktree 主要解决文件隔离问题。两个 agent 不要同时改坏同一份代码。在 RAG的数据 loop 里,问题变成了数据可见性。一个 agent 刚写入一批新文档,另一个 agent 立刻发起查询,它能不能查到这批新数据?传统做法是要么不管(灌完就查,查到什么算什么),要么加 sleep 等一下(灌完等5秒再查应该够了),要么轮询确认(反复查直到能查到)。这些做法在手动操作时勉强可以,但在 loop 化场景下,灌入和查询都是自动触发的,频率可能很高,不确定的可见性窗口会导致 loop 的行为不可预测。Milvus 的一致性合约在 loop 场景下可以提供明确的可见性保障。注:一致性级别(Strong / Bounded / Session / Eventually)是 Milvus 自2.x 以来就支持的能力,不是 3.0 新增。但在 loop 化运维场景下,理解和正确使用一致性级别变得尤为重要,因为 loop 的多个环节对数据新鲜度的要求不同。Milvus 3.0 的 Streaming WAL 架构(引入 Streaming Node 和基于 Woodpecker 的 WAL 实现)让一致性保障在高吞吐场景下更高效。
WAL(Write-Ahead Log)是 Milvus 所有数据变更的 single source of truth。每一条写入操作走 Append 路径进入 WAL,每一个 PChannel 上有一个单调递增的TimeTick作为逻辑时间戳。TimeTick 同时是 MVCC 的可见性边界:只有 TimeTick 已经推进到的数据,才对查询可见。这意味着在 loop 里,你可以用一致性级别来控制写完多久后读能看到:Strong:查询等待所有已写入的数据都可见后再返回。写后立刻读,保证能读到。适合 loop 里灌完立刻验证的环节。Bounded:查询允许一个可配置的时间窗口内的延迟。适合 loop 里不需要最新数据但不能太旧的环节。Session:同一个会话内的写入对后续查询可见。适合 loop 的同一轮内我写的数据我自己能看到。不同的 loop 环节用不同的一致性级别,不需要所有查询都付 Strong 的延迟成本。灌入后的验证查询用 Strong,常规的线上查询用 Bounded 或 Session就可以。06 垃圾回收:过期数据不该靠人记着删
RAG 数据 loop 还有一个 coding agent 不太操心的问题:数据会过期。代码仓库有 git history,旧代码被新 commit 覆盖后,不会继续参与运行。向量数据库不一样。旧文档、旧政策、旧产品说明、旧版本 embedding,如果不清理,仍然可能被召回。结果就是用户问一个新问题,系统答了一段旧知识。传统做法是定期跑删除脚本。按时间戳筛选过期数据,然后逐条删除。这个脚本要维护,要重试,要有人记得跑,还要处理失败情况。在agent场景,有时候甚至需要手动读取记忆完成增删改查。Milvus 的 TTL 把这件事变成了声明式配置。注:Collection 级别的 TTL(collection.ttl.seconds)在 Milvus 2.x 就已存在。Milvus 3.0 新增的是Entity-level TTL,支持为每条数据设置独立的过期时间,通过 TIMESTAMPTZ 字段实现。以下分别展示两种用法。Collection 级别 TTL,适合所有数据使用同一个过期时间:client.create_collection( collection_name="rag_knowledge", schema=schema, index_params=index_params, properties={"collection.ttl.seconds": 604800})client.alter_collection_properties( collection_name="rag_knowledge", properties={"collection.ttl.seconds": 604800})
Entity 级别 TTL(3.0 新增,按条粒度过期):schema.add_field( field_name="expire_at", datatype=DataType.TIMESTAMPTZ)client.create_collection( collection_name="rag_knowledge_v2", schema=schema, index_params=index_params, properties={"ttl_field": "expire_at"})client.insert( collection_name="rag_knowledge_v2", data=[ {"text": "...", "vector": [...], "expire_at": "2025-08-01T00:00:00Z"}, {"text": "...", "vector": [...], "expire_at": "2026-12-31T00:00:00Z"}, ])
注意:两种 TTL 模式互斥,不能同时设置 collection.ttl.seconds 和 ttl_field。
Entity-level TTL 在数据管理场景下的实际意义是:你不再需要把不同过期策略的数据拆成不同 collection。同一个知识库里,新闻类内容可以设7 天过期,政策文档可以设 1 年,产品手册可以不设过期,Milvus 自动按条处理。过期数据自动变得不可查询、不可搜索,然后由 GC 机制在后台清理。loop 不需要自己维护哪些数据该删了的逻辑,声明过期策略,基础设施自动执行。TTL 是 loop 的垃圾回收器,它让 loop 可以只管往前跑,不用回头打扫。07 容错:Loop 挂了不应该从零开始
在 coding agent 场景里,如果一个 loop 挂了,通常可以重跑。代码还在 git 里,状态也相对容易恢复。但这会导致整体的agent任务执行时间,被无限拉长,在RAG 中,数据 loop 的恢复成本更高。如果 Milvus 实例故障,里面可能有几百万、几千万条向量数据,还有已经构建好的索引。重新生成 embedding、重新导入、重新建索引,成本都很高。所以,RAG 数据 loop 需要的不只是逻辑回滚,还需要物理层面的容错。可以把这里分成两层来看:Snapshot 解决的是“我想回到某个历史版本”。Milvus 3.0 的 CDC Replication 解决的是“主集群出问题后,数据层怎么继续服务”。注:以下关于 CDC Replication 的拓扑描述基于 Milvus 的设计文档和 GitHub Issue(#47351)中的功能规划。该功能在持续迭代中,具体 API 和行为以官方文档正式发布为准。
在 primary-secondary 架构里,一个 PRIMARY 集群接受写入,一个或多个 SECONDARY 集群通过 WAL 复制持续同步。SECONDARY 记录自己的同步位置。发生故障后,可以通过 switchover (计划内切换,主备角色互换,数据不丢失)或 failover (非计划故障,SECONDARY 强制提升为 PRIMARY,从拓扑中移除故障节点)让备用集群接管。对 loop 来说,这意味着数据层的故障不会让整个循环从零开始。SECONDARY 一直在同步,切换后 loop 从上次的 checkpoint 继续跑。结合前面的 Snapshot,一个健壮的 RAG 数据循环的容错链路是:Snapshot 提供 point-in-time 回滚能力(逻辑层面),CDC Replication 提供持续同步和故障切换能力(物理层面)。前者解决我想回到某个版本,后者解决机器挂了怎么办。08 什么时候值得做 RAG 的数据 loop
其实,Loop engineering 的核心不是自动化本身,而是把人从循环中解放出来。你设计它一次,之后你不再 prompt 那些步骤。但 loop 化是有成本的。你需要设计心跳节奏、checkpoint 策略、一致性级别选择、过期策略、容错拓扑。上面讲的每一个能力都增加了系统复杂度。- 因此,如果你的知识库是一次性构建,内容稳定,几个月都不更新,那手动灌一次就够了。
- 如果你还是单人单 agent 使用,没有并发写入和查询,那一致性设计不是眼前最重要的问题。
- 如果数据量很小,全量重灌只需要几分钟,那 External Collection 和 Snapshot 的收益也不会特别明显。
- 如果检索效果本身还不稳定,第一步应该是把 chunking、embedding、rerank、评估集做好,而不是急着设计 loop。
但如果你已经遇到下面这些情况,就该认真考虑 RAG 的数据 loop:- 团队频繁实验 embedding 模型、索引参数或 chunking 策略。
- 不同类型内容有不同有效期,旧数据开始污染检索结果。
决策可以简化成一个判断:你是不是已经在反复手动做同样的数据运维操作,而且每次都担心会出错?如果是,是时候把这些操作设计成一个 loop 了。如果不是,先把手动流程跑通跑稳,loop 化是下一步的事。注:Milvus 3.0 开源版目前处于 beta 阶段;在 Zilliz Cloud 上以 On-demand Cluster 形式提供 Public Preview 服务。文中 External Collection、Snapshot 为已发布能力,Entity-level TTL 为 3.0 新增能力,一致性级别为 Milvus 长期支持的核心能力,CDC Replication 处于功能迭代中。