
1. 从 RAG 换到 LoopAgent不是赶时髦是被逼的先交代一下背景。我参与的内部知识库项目大概 200 份 PDF 和 Word 文档内容是制度条款、项目方案、验收记录、设备参数表这类东西。早期方案很自然地选了 RAG文档切块、Embedding、向量库、Top-K 召回再让大模型基于召回内容生成回答。这套东西在 Demo 阶段非常顺但真正放到使用环境里跑了两个月之后问题一个接一个冒出来。最大的问题是切片。制度文档里的条款经常跨页表格的表头和数据可能被切到不同的块里。用户问“验收时限是几个工作日”向量召回回来的块里要么只有表头没有值要么只有数字没有上下文模型只能连猜带蒙。第二个问题是召回分数不可信。Top-5 里经常混进两三条“看起来相关但实际没用”的内容真正关键的那一段反而排不进去。第三个问题是维护成本。文档一更新就要重新切、重新 Embedding索引生效有时间差用户总在问“为什么文档刚更新过你们回答的还是旧内容”。这三个问题叠加起来我基本确定传统 RAG 在这个场景里是结构的错不是调参能救的。后来在技术社区里看到有人讨论 agent 循环读取的方案核心思路很直接不要让向量算相似度而是让模型自己决定“读哪段、读到什么程度”。我就在模拟项目X里搭了一套轻量版跑了大概三个月。结论是对我们这种规模的知识库它确实能顶替 RAG而且精确度更好。这篇文章就是把当时的选型思路、实现细节、踩过的坑完整写出来给同样被 RAG 折磨的人一个参考。要提前说清楚的是这里的“替代”不是普适结论。我的使用场景是几百份文档、更新频繁、对精确性要求高、对延迟容忍度还行。如果你的文档量到了几万份或者用户要求秒回那本文的结论不适用后面我会专门说这个边界。2. LoopAgent 到底在循环什么阅读、判断、收敛的完整链路想用 LoopAgent 替代 RAG第一件事是理解它和 RAG 的本质区别。RAG 的核心假设是“语义相似的文本就是相关的”。这个假设在开放域问答里基本成立但落到具体的制度、参数、验收记录这类文档里经常失效。因为用户问的往往是“第几条怎么说”、“这台设备的最大负载是多少”、“哪个版本才生效”这些问题的答案在语义上不一定和问句长得像但位置关系、表格结构、章节编号才是关键线索。LoopAgent 换了一个思路不预测“哪块最像”而是让模型一段一段地读读完之后自己判断“这段对我回答问题有没有用”。整个检索过程变成一次有目的地阅读而不是一次相似度排序。轻量这个词也体现在这里——它不需要向量数据库不需要 Embedding 服务不需要单独的召回服务一支能调用的 LLM 加一个文件解析器就能跑。我实现的流程分成两个阶段。第一阶段是一次性预处理叫结构扫描第二阶段是每次提问时的阅读循环。下面这个伪代码能反映出循环的核心逻辑def answer_with_loop(query, doc_list): # 第一阶段产生的文本目录每篇文档分块后的标题、摘要、关键词 index build_text_index(doc_list) # 第二阶段查询时先缩小阅读范围 plan llm.plan(query, index) cursor, notes, step plan[start_block], [], 0 while step max_steps: block read_block(cursor) # 核心模型读一段决定是提取、跳过、还是继续 decision llm.read_and_decide( blockblock, queryquery, notesnotes ) if decision[action] extract: notes.append(decision[note]) elif decision[action] continue: cursor decision[next_block] elif decision[action] stop: break step 1 return llm.final_answer(query, notes)这个循环里最关键的接口是read_and_decide。它每次接收当前文档块、用户问题、以及已经收集到的笔记让模型输出一个结构化决策。实际 Prompt 里我会强制它输出 JSON包含三个字段actionextract / continue / stop、note提取到的关键信息、next_block下一块的位置。这样循环的过程完全可控每一步都知道模型为什么要读下一段。和 RAG 的单次检索相比这个循环的差异性体现在三个地方。第一信息是累积的。模型看到的不只是孤立的块还有前面已经提取的笔记所以它能做跨文档的比对和推理。第二停止条件由模型自己判断。模型认为证据足够、可以回答时就输出 stop不会无脑读完全部。第三答案天然带引用。循环过程里读过哪些块、提取了哪些内容全部有记录最后可以给用户展示“这段答案出自哪一篇文档的哪一段”。这一点对制度型问答特别重要用户不只看答案还要能溯源。当然这里有一个很现实的问题如果文档有几千页让模型一页页读是不现实的。所以我在预处理阶段做了一个重要的折中——不是全量精读而是先做一遍“文本目录”扫描把每篇文档按标题结构切成块用便宜的小模型给每个块生成两三行摘要和若干关键词。这个目录就是代码里的text_index它完全替代了向量索引。查询的时候模型先看目录确定候选范围再精准地进入相关块进行阅读循环。这样既避免了全文阅读的天文 token 开销又保留了“模型自己判断相关性”的核心优势。3. 落地实作模拟项目X里的一套可复用配置光讲原理不够我把实际的实现步骤拆开方便直接照着搭。我们当时的目标是替换掉整个 RAG 栈所以每一步都要尽量简单任何需要单独部署的组件都不考虑。3.1 文档接入与分块策略这一步比想象中重要。PDF 解析出来的文本经常乱序尤其是带表格、页眉页脚的长文档。我踩过一次很深的坑某份设备手册转出来之后表格的数据行全在前面表头全在后面模型读起来完全不知道这些数字对应什么字段。我的处理顺序是这样的先用解析工具把 PDF 转成纯文本然后按页码和章节标题切块表格单独识别转成 Markdown 形式的文本页眉页脚直接过滤。切块的粒度控制在 500 到 1500 字之间太大会稀释注意力太小会丢失上下文。每个块会记录一个唯一 ID、所属文档、章节路径、页码。这个 ID 是后面引用溯源的关键。3.2 用摘要目录替代向量索引这是整个方案替换 RAG 的关键一步。传统 RAG 会给每个块计算向量我直接把这一步换成了“让模型读块并输出摘要”。调用一次便宜的小模型输入是文档块本身输出是固定格式的 JSON包含两个字段summary和keywords。摘要必须保留实体信息比如设备型号、日期、数字、条款编号不能只写“本节介绍了设备参数”而要写“本节列出 XX 设备在 25℃ 下的最大负载为 8kW”。为什么强调保留实体因为预处理摘要失真的话后面查询时模型从目录里判断候选块就会漏。前面预扫描阶段一共 4000 多个块用便宜模型跑一遍大概消耗 120 万 token 的输入、40 万 token 的输出一次性成本不高。之后文档更新时只需对变更的文档重新生成摘要不用像向量库那样全量重建。3.3 查询循环的关键配置查询侧的配置直接影响效果和成本我最后调稳定的一组参数是这样的。max_steps设为 12意思是单次查询最多读 12 个块防止循环失控。候选范围由模型基于文本目录决定通常从相关文档的相关章节切入。read_and_decide的 Prompt 会明确告诉模型只有当笔记足以回答用户问题时才输出 stop否则优先从当前文档的下一块继续读当前文档读完仍有疑问时再跳到其他候选文档。一个容易忽略的细节是笔记的维护。我要求模型每次 extract 时输出的是经过整理的规范化信息而不是原文照抄。比如原始文本是“按本合同第十二条规定乙方应于验收合格后 10 个工作日内提交结算资料”模型输出的笔记应该是“验收合格后 10 个工作日内提交结算资料依据合同第十二条”。这样做的好处是最终合成答案时模型面对的是精简过的证据链而不是一堆原始句子答案会更紧凑。3.4 成本与延迟的估算方式我习惯在方案切换前把成本算清楚。以 200 份文档、约 4000 个块为例预扫描阶段一次性成本大约几美元级别按现网商用模型的价格估算在 5 美元以内如果用开源小模型部署在本机这笔费用就变成硬件电费基本可以忽略。单次查询的 token 消耗取决于循环步数平均在 8000 到 20000 token 之间比 RAG 单次查询确实贵但换回来的是更少的错误回答和零向量库维护成本。延迟方面LoopAgent 单次查询因多步串行交互通常在 10 到 60 秒之间远高于 RAG 的秒级响应。这个差距在实践里比成本更敏感所以我也准备了降级方案对响应速度要求高的入口仍然保留 RAG 作为快速通道而把 LoopAgent 作为默认的精读通道。这个混合方案在项目里运行了很久后面我会细说。4. 实测对比三类典型问题差距比想象中大为了让自己确信这套方案真的有用我建了一个 100 道题的测试集分成三类精确条款类 30 道问具体制度、数值、时间、语义归纳类 40 道问某篇文档的核心内容、多跳关联类 30 道需要跨文档比对才能回答。每道题都标注了标准答案和答案所在文档位置。评测时分别跑 RAG 方案和 LoopAgent 方案记录正确率、幻觉率、平均耗时。结果对比问题类型RAG 正确率LoopAgent 正确率RAG 平均耗时LoopAgent 平均耗时精确条款类67%92%2.8s21s语义归纳类89%91%3.2s18s多跳关联类55%84%3.5s26s这个结果里有几个值得解读的地方。精确条款类 RAG 只有 67%主要是切片割裂导致的LoopAgent 因为直接读原文条款编号和数字都能对上正确率到了 92%。多跳关联类差距最大RAG 的 55% 意味着近一半问题答错或答不到点子上而 LoopAgent 因为天然支持先读 A 篇再读 B 篇的连续动作能自己完成跨文档比对。语义归纳类两边都维持在 90% 上下这是符合预期的。这类问题本身不太依赖精确定位向量召回个大概就能答。这也说明了 RAG 并非处处不如 LoopAgent它在宽泛的语义问题上效率更高。幻觉率差异同样明显。RAG 方案在精确条款类里的幻觉率接近 20%模型会一本正经地编出“第七条明确规定……”这样的句子而实际上根本没读到那条。LoopAgent 因为笔记机制强制溯源幻觉被压到 5% 以内。原因很简单模型的每一步决策都基于真实读过的块没有块作为依据时它会选择继续读或承认找不到而不是直接编。在自建测试集之外我特意挑了一份带大量表格的参数手册做压力测试连续问了 20 道关于设备规格的问题。RAG 在这组问题里表现很不稳定表格切碎后经常把不同列的数值混在一起LoopAgent 直接读表格的 Markdown 文本数值对齐关系保留完整20 道题全部答对。这让我确认了一件事只要文档里表格是结构化呈现循环阅读的方式比向量召回稳得多。5. 踩坑记录循环失控、摘要失真、和看起来能打的假象任何方案都有一堆坑LoopAgent 也不例外。我把它实际运行中遇到的四个主要问题写出来每个问题都附上了处理方式。5.1 循环失控模型读不完也停不下来第一个版本最大的问题是read_and_decide经常输出 continue即使笔记已经足够回答模型还是会继续读下一个块。结果单次查询的 token 消耗飙升到 10 万以上延迟超过两分钟完全不可用。我一度以为这个方案走不通后来才意识到是 Prompt 里没把停止条件讲清楚。解决办法是在 Prompt 里加入一个显式的“证据充分性判断”先要求模型对照笔记检查“是否有足够信息回答用户的每个子问题”再决定动作。同时把max_steps硬性设为 12超出后直接进入最终合成阶段。这两个约束叠加之后循环平均步数降到了 4 到 6 步大部分问题在顶多两篇文档的范围内就能收敛。5.2 预扫描摘要失真预扫描摘要如果丢失关键实体查询时候选块就定位不准。我第二次实测的时候发现精确条款正确率从 92% 跌到 80%排查后定位到摘要生成 Prompt 的问题。原来摘要模型偷懒把表格里的数值省略成“若干参数”导致目录里没有可检索的实体信息。对策是重写摘要模板强制要求保留所有数字、日期、条款编号和专有名词并且要求摘要必须能独立成句不允许出现“本节内容较多主要包括若干参数”这种废话。重跑预扫描后候选块定位准确率明显回升。这件事也提醒我目录是循环阅读的眼睛摘要质量必须单独做评测不能想当然。5.3 长表格和乱序文本PDF 解析后的表格乱序问题在前面提过这里展开说下处理方式。我用的是一个启发式规则检测到连续数字行时向上寻找最近的非数字表头行把表头和数据重新拼接。这个规则对 90% 的常规表格有效遇到复杂合并单元格仍然会失效。更稳妥的做法是在解析阶段就引入视觉模型识别表格结构但对轻量方案来说成本太高我最终选择了正则规则加人工抽查。5.4 “看起来能打”的假象这是心态上的坑。初版 Demo 我只测了 5 个精心挑选的问题每个问题 LoopAgent 都能给出漂亮答案我当时差点直接宣布方案成功。还好后来建了 100 道题的测试集才暴露出找茬式的缺陷。我的建议是方案评估一定要从一开始就建标准问题集哪怕只有 20 道题也好过反复用那几个 demo 问题自欺欺人。正确率、延迟、token 消耗、幻觉率四项指标要一起记录不能只看手感。6. 我现在的取舍标准什么场景值得换什么场景别硬换项目收尾后我做了一次复盘整理出一套相对稳定的选型标准希望能减少别人重走弯路的时间。文档规模在几十到几百份、更新频繁且内容偏向制度条款、规格参数这类对精确性要求高的场景LoopAgent 的优势很明显。它省掉了向量库运维的成本文档更新后只重建摘要目录当天就能生效。回答附带的引用溯源能力也是制度问答场景的刚需。反过来如果文档规模到了上万份或者用户要求首响在几秒以内LoopAgent 就不合适了。万份文档做预扫描是一次不小的开销查询时模型要读的候选块也会增多延迟和成本都难以接受。这种情况下RAG 仍然是最务实的方案。我个人建议的中间态是混合部署入口用 RAG 快速命中热区命中置信度低的时候再切 LoopAgent 做精读。最后分享一个我在实际运行中形成的习惯。我会在日志里给每次查询打个标记模型输出的 action 序列里如果出现了超过两次“continue 之后又回到同一个文档块”大概率是候选定位有问题应该去检查摘要目录。这个信号比单纯看正确率更早暴露索引的劣化。项目跑了三个月我靠着这个方法及时修正了两次文档更新导致的摘要缺失避免了用户感知到的错误回答。循环阅读这种模式不神秘也不高级它只是把“检索”从数学计算变回了“阅读和判断”。在文档量可控的场景里这一替换带来的是精确度和可解释性的显著提升代价是延迟和 token 消耗换来的。至于值不值取决于你的用户更在意什么。对我参与的项目来说这个交换很划算。