news 2026/10/7 5:09:44

DocResearch 实战:Agent Loop 与 Agentic RAG 构建带引用的调研报告

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DocResearch 实战:Agent Loop 与 Agentic RAG 构建带引用的调研报告

1. 从一条命令说起:DocResearch 到底在解决什么问题

第一次看到 DocResearch 这个项目名,我脑子里蹦出来的画面特别具体:深夜十一点,你对着一个空白的 Markdown 文件,手边开着十几个浏览器标签页,PDF、网页、内部文档混在一起,你要在两个小时里憋出一份带引用的调研报告。这个过程里最耗神的从来不是"写",而是"找"和"对"——找到对的资料,把结论和出处对上号。

DocResearch 想干的事情,就是把这个过程压缩成一条命令。你给它一个研究主题,它自己规划要查什么、去哪查、查完怎么读、读完怎么组织,最后吐出一份每个结论后面都挂着来源的报告。这里的关键词是"有出处",不是那种看起来很像那么回事、但经不起追问的生成内容。

它背后站着三个当下很热的概念:Agent Loop、Agentic RAG和pgvector。Agent Loop 是它的骨架,决定了它怎么"想一步、做一步、看结果、再想下一步";Agentic RAG 是它的检索策略,区别于传统"一次检索、一次生成"的 RAG,它允许模型在多轮里主动决定要不要再查、查什么;pgvector 则是它的记忆底座,把文档切片和向量存进 PostgreSQL,让检索这件事变得可持久化、可复用、可审计。

这篇文章适合谁看?如果你写过基础的 Python,装过依赖、跑过脚本,对"检索增强生成"这个词有模糊印象但没亲手搭过完整链路,那这篇就是给你准备的。我会把 DocResearch 从一条命令到一份报告的完整链路拆开,讲清楚每个环节为什么这么设计、参数怎么定、坑在哪里。如果你已经是老手,也可以直接跳到第 4 节的排查技巧,那里有几个我在实际调试中踩出来的经验。

需要先说明一点:DocResearch 的具体实现细节,公开资料里并没有完整披露,下面涉及架构和参数的部分,是我基于"一个合格从业者在做同类 Agentic RAG 系统时最可能采用的方案"做的合理补全,并会明确标注哪些是常见实践、哪些是推断。这样你拿去复现的时候,心里有数。

2. 整体架构拆解:一条命令背后的四层结构

2.1 为什么是 Agent Loop,而不是一条直线流程

传统 RAG 的流程是一条直线:用户提问 → 向量检索 Top-K → 拼进 Prompt → 生成答案。这条线的问题在于,它假设"第一次检索就能拿到足够的信息"。现实里往往不是这样,你问一个稍微复杂点的问题,第一次检索回来的片段可能只覆盖了一半,剩下那一半需要根据已有信息再追问。

Agent Loop 的核心思想就是把这个直线掰成环。DocResearch 的循环大致长这样:

  1. 规划(Plan):拿到研究主题,先拆成若干子问题,比如"这个技术的核心机制是什么""它和同类方案比有什么取舍""实际落地有哪些限制"。
  2. 行动(Act):针对当前子问题,决定调用哪个工具——是去向量库检索,还是去抓一个具体网页,还是直接读本地文档。
  3. 观察(Observe):拿到工具返回的结果,判断信息够不够、准不准、有没有冲突。
  4. 反思(Reflect):如果不够,调整下一个子问题或换检索词;如果够了,进入下一个子问题。
  5. 收敛(Synthesize):所有子问题都有足够材料后,组织成报告,并把每个结论映射回具体来源。

这个环的价值在于"自适应"。简单问题可能两轮就收敛,复杂问题可能跑十几轮。代价是 token 消耗和延迟都会上去,所以实际项目里通常要设一个最大轮数上限,防止它在一个死胡同里绕不出来。

提示:Agent Loop 最容易失控的地方是"反思"环节。如果模型判断"信息还不够"的标准太宽松,它会无限检索下去。常见做法是给每轮反思加一个明确的收敛条件,比如"连续两轮没有新增有效信息就强制进入综合阶段"。

2.2 Agentic RAG 和普通 RAG 的分水岭在哪

普通 RAG 里,检索是"被动"的——它不知道自己要找什么,只是把问题向量化然后做相似度匹配。Agentic RAG 里,检索是"主动"的——模型会先想"我需要什么类型的信息",再决定用什么查询词、查哪个数据源、要几条结果。

这个差别在 DocResearch 里体现得很明显。它不会拿用户原始主题直接去检索,而是先把它改写成更适合检索的查询。举个例子,主题是"pgvector 在 Windows 上的部署体验",直接检索可能召回一堆泛泛的向量数据库介绍。Agentic 的做法是先拆成"pgvector Windows 编译依赖""pgvector 预编译包获取方式""pgvector 在 Windows 下的性能表现"几个具体查询,分别检索再合并。

这种改写带来的召回质量提升是实打实的,但代价是每轮改写都要过一次模型,延迟和成本都会增加。所以实际项目里通常会在"改写轮数"和"检索质量"之间做权衡,常见配置是首轮改写一次,后续根据观察结果决定要不要再改写。

2.3 pgvector 为什么被选来做记忆底座

向量存储的选择很多,FAISS、Chroma、Milvus、Qdrant 都能干这活。DocResearch 选 pgvector,我理解核心原因是"省事且够用"。

pgvector 是 PostgreSQL 的一个扩展,意味着你不需要额外维护一套向量数据库,直接用现有的 PG 实例就能存向量。对于 DocResearch 这种"文档量不算特别大、但需要和元数据强关联"的场景,这个选择很务实——文档切片、来源 URL、抓取时间、切片序号这些元数据,和向量存在同一张表里,查询的时候一个 SQL 就能把"相似度 + 元数据过滤"一起搞定。

代价是 pgvector 在超大规模(千万级以上向量)和高并发场景下,性能不如专用向量库。但对于个人研究、小团队内部使用,这个量级完全够。而且 PG 的运维生态成熟,备份、迁移、监控都有现成方案,不用为向量单独学一套。

2.4 四层结构总览

把上面几块拼起来,DocResearch 的整体结构可以分成四层:

层级职责关键技术
交互层接收命令、展示进度、输出报告CLI 框架、Markdown 渲染
编排层Agent Loop、子问题规划、收敛判断LLM 调用、状态机
检索层查询改写、向量检索、结果重排Agentic RAG、pgvector
存储层文档切片、向量持久化、元数据管理PostgreSQL、pgvector 扩展

这四层里,编排层是最难调的,因为它涉及"模型判断"这种不确定的东西;存储层是最稳的,一旦表结构定下来基本不用动;检索层是效果提升最明显的,查询改写做得好,召回质量能上一个台阶。

3. 核心细节与实操要点:从环境到检索的完整链路

3.1 环境准备:Python 版本和依赖的取舍

DocResearch 是 Python 项目,环境准备这一步看着简单,但坑不少。我建议直接用 Python 3.10 或 3.11,别用 3.8。原因很实际:现在主流的 LLM SDK 和向量库对 3.8 的支持在逐步收缩,而且 3.10 之后引入的match语法、更完善的类型提示,在写 Agent 状态机的时候会舒服很多。

安装依赖的时候,我习惯先建虚拟环境再装,避免污染全局:

python -m venv .venv source .venv/bin/activate # Windows 下是 .venv\Scripts\activate pip install -r requirements.txt

requirements.txt 里通常会有这几类依赖:LLM 客户端(比如 openai 或 anthropic 的 SDK)、PostgreSQL 驱动(psycopg2 或 psycopg)、向量处理库(numpy、可能还有 sentence-transformers)、文档解析库(pypdf、beautifulsoup4)、CLI 框架(click 或 typer)。

注意:psycopg2 在 Windows 上直接 pip 安装经常编译失败,因为缺 PostgreSQL 的开发头文件。稳妥的做法是装psycopg2-binary,它是预编译版本,省去编译环节。这个坑我在三台不同的 Windows 机器上都遇到过,不是偶发。

3.2 pgvector 的安装:Windows 用户要特别留意

pgvector 的安装分两步:装 PostgreSQL 扩展,再在数据库里启用。

Linux 和 macOS 下相对简单,包管理器一般能直接装。Windows 下就麻烦一些,因为 pgvector 需要编译,而 Windows 上编译 C 扩展对很多人来说是道坎。常见做法是找预编译的二进制包,把vector.dll放到 PostgreSQL 的 lib 目录,把vector.control和 SQL 文件放到 share/extension 目录,然后重启服务。

启用扩展的 SQL 很简单:

CREATE EXTENSION IF NOT EXISTS vector;

建表的时候,向量列的定义大概长这样:

CREATE TABLE doc_chunks ( id BIGSERIAL PRIMARY KEY, doc_id TEXT NOT NULL, chunk_index INT NOT NULL, content TEXT NOT NULL, embedding vector(1536), source_url TEXT, created_at TIMESTAMPTZ DEFAULT NOW() );

这里的vector(1536)里的 1536 是向量维度,取决于你用的 embedding 模型。OpenAI 的 text-embedding-3-small 是 1536 维,text-embedding-3-large 是 3072 维。维度定错了,插入的时候会直接报错,所以建表前一定要确认清楚。

3.3 文档切片:切多大、怎么切,直接决定检索质量

切片是 RAG 里最容易被低估的环节。切太大,一个切片里混了好几个主题,检索出来噪声大;切太小,上下文不完整,模型读不懂。常见实践是 500 到 1000 个 token 一个切片,切片之间留 10% 到 20% 的重叠。

重叠的意义在于防止"关键信息正好被切在边界上"。比如一句话被切成两半,前半句在切片 A 末尾,后半句在切片 B 开头,检索的时候可能只召回 A,模型看到半句话就懵了。留重叠能让这种边界情况下的信息至少在一个切片里是完整的。

DocResearch 这种研究型场景,我倾向于切得稍微大一点,700 到 900 token,因为研究报告需要的是相对完整的论述段落,而不是零散的事实点。同时按语义边界切(比如按段落、按标题层级),比机械地按字符数切效果好很多。

3.4 查询改写:Agentic RAG 的第一道价值

前面提过,Agentic RAG 和普通 RAG 的分水岭在查询改写。具体怎么改?常见做法是让模型扮演"检索策略师",输入是用户的研究主题和当前已收集的信息,输出是接下来要执行的检索查询列表。

一个典型的改写 Prompt 大概是这样组织的:告诉模型当前研究主题是什么、已经查到了哪些信息、还缺什么,然后要求它输出 2 到 3 个具体的检索查询,每个查询要足够具体,能直接拿去向量检索。

这里有个经验:改写出来的查询最好带上"领域限定词"。比如研究"向量数据库选型",改写时加上"性能对比""运维成本""扩展性"这类限定词,比单纯问"向量数据库哪个好"召回质量高得多。因为向量检索本质是语义相似度匹配,查询越具体,匹配到的片段越聚焦。

3.5 检索结果重排:Top-K 之后的第二道筛子

向量检索返回 Top-K 之后,直接喂给模型往往不够。因为向量相似度高不代表内容真的相关,有时候只是用词接近。所以 DocResearch 这类系统通常会在检索后加一道重排(Rerank)。

重排的常见做法有两种:一种是用专门的 rerank 模型(比如 cross-encoder),把查询和每个候选片段拼在一起打分,精度高但慢;另一种是用 LLM 直接判断每个片段和查询的相关性,灵活但贵。实际项目里,如果候选片段不多(比如 20 条以内),用 LLM 重排是可以接受的;如果候选很多,还是上专门的 rerank 模型更划算。

重排之后通常只保留 Top 3 到 Top 5 喂给生成环节,既控制 token 消耗,又保证信息密度。

3.6 来源追踪:让每个结论都有出处

"有出处"是 DocResearch 的核心卖点,实现上靠的是在生成阶段强制模型标注引用。具体做法是在 Prompt 里给每个检索到的片段编号,要求模型在输出结论时用[1]、[2]这样的标记指向具体片段,最后再把编号映射回真实的来源 URL。

这个机制听起来简单,但实际做的时候有两个坑。一是模型可能"编造引用",标了一个不存在的编号,或者标了编号但内容和来源对不上。二是多个片段支持同一个结论时,模型可能只标一个,导致引用不完整。前者需要在后处理阶段做校验,把无效引用剔除或标记;后者可以在 Prompt 里明确要求"如果一个结论有多个来源支持,全部标出"。

4. 实操过程:从零跑通一条研究命令

4.1 数据库初始化与连接配置

第一步是把 PostgreSQL 和 pgvector 准备好,然后建库建表。连接配置通常放在环境变量或配置文件里,我习惯用.env文件管理:

DB_HOST=localhost DB_PORT=5432 DB_NAME=docresearch DB_USER=postgres DB_PASSWORD=your_password

连接的时候用连接池,别每次查询都新建连接。psycopg2 的pool模块或者 SQLAlchemy 的 engine 都能干这活。连接池大小根据并发量定,个人使用 5 到 10 就够,团队使用可以到 20。

建完表之后,记得给向量列建索引。pgvector 支持两种索引:IVFFlat 和 HNSW。IVFFlat 建得快、占空间小,但召回率略低;HNSW 建得慢、占空间大,但召回率和查询速度都更好。研究型场景我推荐 HNSW:

CREATE INDEX ON doc_chunks USING hnsw (embedding vector_cosine_ops);

vector_cosine_ops表示用余弦距离,这是文本 embedding 最常用的距离度量。如果你的 embedding 是归一化的,用内积vector_ip_ops也可以,速度更快。

4.2 文档入库:抓取、解析、切片、向量化

入库流程是一条流水线:抓取原始文档 → 解析成纯文本 → 切片 → 每片算 embedding → 连同元数据写入数据库。

抓取环节,网页用 requests + beautifulsoup,PDF 用 pypdf 或 pdfplumber。解析的时候要注意去掉页眉页脚、导航栏这些噪声,它们会污染切片内容。

切片环节,我前面说了按语义边界切。一个实用的做法是先用正则或简单的规则把文本按段落切开,再把相邻的小段落合并到目标 token 数。这样切出来的片段语义完整性比机械按字符切好很多。

向量化环节,如果文档量大,建议批量调用 embedding API,一次传几十条,比一条条调快得多。同时要注意 API 的速率限制,加个简单的退避重试。

4.3 Agent Loop 的状态管理

Agent Loop 跑起来之后,状态管理是关键。每一轮循环需要记录:当前研究主题、已拆解的子问题列表、每个子问题的状态(待处理/进行中/已完成)、已收集的信息片段、当前轮数。

这些状态我建议用一个 dataclass 或 Pydantic 模型来管理,别用裸字典。原因是状态字段多了之后,裸字典很容易出现"某个字段忘了初始化"或者"字段名拼错"的问题,用模型能在运行前就发现。

轮数上限我一般设 10 到 15。低于 10 可能复杂问题跑不完,高于 15 收益递减明显,而且 token 消耗会失控。同时加一个"无进展检测":如果连续两轮没有新增有效信息片段,直接跳出循环进入综合阶段。

4.4 报告生成与引用映射

综合阶段是把所有收集到的信息组织成报告。Prompt 里要明确报告结构,比如"先给一段概述,然后分点论述,每个论点后面标注来源编号"。同时把检索到的所有片段按编号列在 Prompt 里,让模型有据可依。

生成完之后,做一次引用校验:扫描报告里的所有[n]标记,检查 n 是否在有效范围内,以及标记位置的内容是否和对应片段语义一致。不一致的标记要么修正要么删除。这一步能显著提升报告的可信度。

最后把编号映射回真实来源,输出一份带完整引用的 Markdown 报告。到这里,从一条命令到一份有出处的报告,整条链路就跑通了。

5. 常见问题与排查技巧实录

5.1 检索召回质量差,怎么定位

召回质量差是最常见的问题,表现是"检索回来的片段和问题不相关"。排查思路是从后往前:先看查询改写的结果合不合理,再看向量化有没有问题,最后看切片质量。

如果查询改写出来的查询本身就很泛,那问题在改写 Prompt,需要加更多约束。如果查询没问题但召回的还是不相关,可能是 embedding 模型不适合你的领域,考虑换一个或者做微调。如果前两步都没问题,那就是切片切得不好,检查一下是不是把不相关内容切到了一起。

5.2 Agent Loop 跑飞了怎么办

"跑飞"的表现是循环停不下来,或者反复检索同样的内容。前者通常是收敛条件太宽松,加一个硬性轮数上限就能兜住。后者是查询改写没有利用历史信息,每轮都在问同样的问题。

解决办法是在改写 Prompt 里明确告诉模型"以下查询已经执行过,请生成不同的查询",把历史查询列表传进去。这个改动很小,但效果立竿见影。

5.3 引用对不上号

引用对不上号有两种情况:一种是编号越界,模型标了一个不存在的编号;另一种是编号存在但内容和来源不符。前者在后处理阶段直接过滤掉越界编号即可。后者需要在 Prompt 里强调"引用必须严格对应片段内容",并在校验阶段做语义比对。

如果这种情况频繁出现,说明检索到的片段本身质量不高,模型找不到合适的引用只能硬凑。这时候要回头优化检索环节,而不是在生成环节打补丁。

5.4 常见问题速查表

问题现象可能原因排查方向
检索结果不相关查询改写太泛 / embedding 不匹配检查改写 Prompt、换 embedding 模型
循环停不下来收敛条件太宽松加轮数上限、加无进展检测
反复检索同样内容改写未利用历史把历史查询传入改写 Prompt
引用编号越界模型编造引用后处理过滤无效编号
报告内容空洞检索片段信息量不足优化切片、增加检索轮数
入库速度慢逐条调用 embedding API改批量调用、加并发
Windows 装 pgvector 失败缺编译环境用预编译二进制包

5.5 几个我踩过的坑

第一个坑是 embedding 维度不一致。我一开始用了一个模型建表,后来换了模型但忘了改表结构,插入的时候直接报错。教训是:embedding 模型和表结构要绑定管理,换模型必须重建表。

第二个坑是切片重叠设得太大。我一度把重叠设到 50%,结果同一个片段在库里出现好几次,检索的时候 Top-K 里全是重复内容,浪费了大量 token。重叠 10% 到 20% 就够了,别贪多。

第三个坑是没做速率限制。批量向量化的时候一口气发了几百个请求,直接被 API 限流,整个流程中断。后来加了简单的令牌桶限流和指数退避重试,才稳定下来。

第四个坑是报告生成时没控制上下文长度。收集的片段太多,全塞进 Prompt 直接超了模型上下文窗口。解决办法是在综合阶段做一次筛选,只保留和当前论点最相关的片段,而不是把所有片段都塞进去。

5.6 性能优化的几个方向

如果觉得跑得慢,可以从这几个方向优化。检索环节,给向量列建 HNSW 索引,查询速度能提升一个数量级。向量化环节,批量调用加并发,吞吐量能翻好几倍。Agent Loop 环节,把不依赖前序结果的子问题并行处理,能显著缩短总时长。生成环节,用流式输出,用户能更早看到内容,体感上快很多。

不过优化要有优先级。我的经验是,先保证正确性,再优化速度。一个跑得慢但结果靠谱的系统,比一个跑得快但结果乱七八糟的系统有价值得多。等正确性稳定了,再逐个环节做性能优化,每次只改一个变量,方便定位问题。

6. 这套东西还能怎么扩展

跑通基础链路之后,DocResearch 这类项目还有不少可扩展的方向。比如加一个"多源交叉验证"环节,同一个结论如果有多个来源支持,可信度标记就高一些;如果来源之间有冲突,就单独标出来让用户判断。再比如加一个"增量更新"机制,已经入库的文档定期重新抓取,内容变了就更新向量,保证检索到的是最新信息。

还有一个我觉得很有价值的方向是"研究过程可回放"。把 Agent Loop 每一轮的规划、检索、观察都记录下来,用户不仅能看最终报告,还能看它是怎么一步步得出结论的。这对于需要审计的研究场景特别有用,也方便调试和优化。

pgvector 这边,如果文档量涨到百万级,可以考虑做分区表,按时间或来源分区,查询的时候只扫相关分区。再往上,如果单机 PG 扛不住,可以上 PG 的读写分离或者分片方案。不过到这个量级,可能就该考虑专用向量库了,pgvector 的定位还是"够用就好"。

我自己在实际操作中的体会是,这类 Agentic RAG 项目,最难的不是把链路搭起来,而是把每个环节的"判断标准"调准。什么时候算检索够了、什么时候算信息冲突、什么时候该停止循环,这些判断标准没有标准答案,只能根据你的具体场景反复试。我的建议是先把标准设得保守一点,宁可多查几轮也别漏信息,等跑顺了再逐步收紧,在质量和成本之间找平衡。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 5:09:16

AI日报生产全流程:从信息筛选到自动化工具链的工程实践

1. 一份AI日报背后到底在解决什么问题每天早上打开手机,AI圈的信息像洪水一样涌过来:某实验室放出新模型、某大厂开源了工具链、某篇论文在圈内刷屏、某个产品悄悄更新了定价。信息本身不缺,缺的是"今天到底哪几条跟我有关"。我做A…

作者头像 李华
网站建设 2026/10/7 5:09:16

具身智能嵌入式开发必学:C语言动态数组原理与工程实现

如果问一个打算入行具身智能的人,学习路线应该从哪里开始,网上十有八九会告诉你先学Python和PyTorch。这个答案不能算错,但你真跑到实验室里,面对一台机械臂、一块主控板、一堆传感器,就会发现另一套逻辑:底…

作者头像 李华
网站建设 2026/10/7 5:08:51

Java面试官拆解简历优化:技术栈分级、项目量化、基础自测

做了多年Java面试官,每年经手筛掉的Java简历少说也有上千份。最近又在集中筛选简历,发现一个老问题依然普遍:技术栈堆了十几行,项目经历却只有三五行,一问关键技术点就支支吾吾。说实话,很多候选人不是技术…

作者头像 李华
网站建设 2026/10/7 5:06:47

用scrcpy与ADB搭建免费多手机群控投屏工作台

做设备批量管理或者电脑端同时盯多台手机的时候,很多人第一反应是买一堆昂贵的硬件投屏盒子,或者去用那些收费的云控平台。其实还有一条更轻量、更隐蔽、完全免费的路子,就是直接撸起袖子用开源工具自己搭一套。这篇文章就是专门讲这个的&…

作者头像 李华
网站建设 2026/10/7 5:05:58

Agent-Reach:用触达增强层解构长任务失败,完成率提升20+个百分点

Agent-Reach这个项目,最初诞生于一次让人头疼的内部评测。我们让Agent执行一组长流程任务——围绕某个主题做多轮资料收集、交叉验证、最后输出结构化报告——结果十几轮跑下来,失败率高达四成。注意,失败的原因根本不是模型“能力不够”&…

作者头像 李华