文字转语音工具
118.24M · 2026-03-29
这两年只要聊 RAG,大家脑子里默认浮现出来的,基本都是同一套流程:
这套东西当然没问题。直到今天,它依然是工业界最主流、最成熟、工程上最好落地的一条路。OpenAI 的 file_search 依然是围绕 vector store 做解析、分块、embedding 和检索;Anthropic 也没有抛弃这条路线,而是在它之上继续补强,提出了 Contextual Retrieval,希望缓解“文本一切就碎、碎了就丢上下文”的问题。Anthropic 官方给出的结果是,这套方法能把 failed retrieval 降低 49%,配合 reranking 后降低 67%。
但如果你做过财报、合同、技术手册、政策文件这类长文档问答,很快就会发现一个更本质的问题:
很多 RAG 的瓶颈,不在生成层,而在检索层一开始就把文档处理错了。
不是所有知识都适合先被切碎,再靠相似度拼回来。
有些文档天生就是按目录、章节、小节、表格、脚注、附录组织的。你把它先打散,再问“哪段最像问题”,在很多高价值场景里,其实已经输了一半。
我最近一直在认真看一条路线:结构化推理检索。
有人叫它 tree-based retrieval,有人叫 reasoning-based retrieval,也有人直接打出 vectorless RAG 的旗号。名字不重要,重要的是它背后的判断很清楚:
这篇文章不再停留在概念层,而是直接往工程落地走,重点回答五个问题:
先说一个特别容易误解的点。
这套方案不是在说:
我不这么看。
更准确的说法应该是:
传统 RAG 的默认假设是:
这个假设在 FAQ、客服知识库、内部 Wiki 搜索这种场景下非常好用。
但在长文档、强结构、低容错场景里,它经常不成立。
微软在 GraphRAG 文档里把这个问题说得很直接:baseline RAG 擅长局部片段匹配,但在两类问题上容易掉链子,一类是 connect the dots,也就是答案分散在多处,需要连点成线;另一类是 holistic understanding,也就是需要理解整体结构和抽象概念的问题。
换句话说,传统 RAG 最大的短板不是“找不到相关句子”,而是:
它对“文档结构本身也是信息”这件事,天然不够敏感。
如果把这套东西说得足够朴素,它其实就是把“人翻目录找答案”这件事程序化。
你想想自己是怎么在一本教材里找内容的:
你不会从第一页看到最后一页。
你也不会把全书切成 500 段,然后逐段比较哪一段和问题最像。
你会先看目录,先判断大概属于哪一章,再定位到哪个小节,最后才认真读原文。
结构化推理检索,本质上就是在模拟这个过程。
一个最小可运行闭环,大概只有五步:
不是按固定 token 长度一刀切,而是先按章节、小节、子节组织成层级树:
叶子节点基于原文生成 summary;
父节点不直接看原文,而是基于所有孩子节点的 summary,再生成更高层的 summary。
最后整棵树上的每个节点,都有一个能代表“这一支主要在讲什么”的摘要。
最简单的方式就是序列化成 JSON。
实际工程里当然不建议只靠 JSON,但对 demo 来说,它足够把闭环跑通。
query 进来后,不再是:
而是:
模型就这样一层一层往下,直到走到足够具体的叶子节点。
最终回答阶段,模型拿到的不是一堆全局检索出来的散块,而是一个通过结构路由筛出来的局部上下文。
如果只看表面,这好像只是“换了一种索引形式”。
但它真正改变的,其实不是索引,而是检索本身的性质。
传统 RAG 问的是:
这类方案问的是:
这不是一个实现细节上的小改动。
这是把 retrieval 从“近邻搜索”改成了“路径决策”。
如果只盯着某个项目名字,比如 PageIndex,很容易误以为这是个突然冒出来的新概念。其实不是。
把时间线拉开看,过去两年,RAG 一直在朝同一个方向缓慢但坚定地移动:
从扁平 chunk 检索,走向结构化、多层级、带路径感的检索。
RAPTOR 的核心思想是:别只在底层 chunk 上检索,而是对文本块递归聚类和摘要,形成一棵多层级树,再在棵多层级树,再在不同抽象层级上一起检索。论文里给出的结果很亮眼:在需要复杂推理的 QuALITY 任务上,RAPTOR 配合 GPT-4,能把当时最佳结果再提升 20 个点search3
RAPTOR 真正重要的地方,不是某个数字,而是它第一次把一个观念立住了:
微软走的不是树,而是图。它先从语料里抽实体、关系和 claim,再构建社区层次结构和自底向上的 summary,最后在 query time 用这些结构增强回答。形式不同,但底层判断一致:
LATTICE 更进一步,它不再满足于“有一棵树”,而是专门解决另一个更现实的问题:
论文的关键结论很直接:LLM 的局部 relevance judgment 是有噪声的,所以不能只做最朴素的 greedy search,而要做路径相关性校准。它在 BRIGHT benchmark 上报告的结果是,Recall@100 提升 9%,nDCG@10search5
你把这几条线放在一起看,会发现一个很清楚的趋势:
所以我现在看这类结构化推理检索,并不会觉得它只是一个新玩具。
它更像是 RAG 走到今天之后,很自然会长出来的一步。
如果只是做 demo,最简单的结构当然是:
但如果你真想上线,我不建议照这个结构直接做。
因为 demo 的问题不是“方向不对”,而是少了太多工程护栏。
我更推荐下面这种生产化架构。
这张图里,最重要的不是“树”本身,而是我故意多加出来的五个模块:
Structure ParserNode EnricherBeam Search TraversalFallback RetrievalEvidence Pack Builder因为真正的生产问题,恰恰都出在这五个地方。
这是整套系统里最容易被低估、但最该先重视的部分。
很多 demo 都默认文档很规整:有标题、有段落、有层级,切起来像教科书一样顺滑。现实不是这样。PDF 可能标题层级丢失,多栏布局可能 OCR 成一条,表格会跨页,页脚会混进正文,图表标题和段落可能交错在一起。
所以生产版系统里,第一原则不是“让 LLM 自己切树”,而是:
这个模块的输出不应该只是原始文本,而应该是一个规范化文档模型,至少包含:
简单说,先把文档还原成“有位置感的内容单元”,再往后做树。
很多人一看到“层级检索”,就会下意识把树做得很深。
我反而建议,第一版别贪深。
对于大多数单文档问答,两层到三层已经够用了:
rootsectionsubsection / leaf原因很简单。树太浅,定位不够细;树太深,query-time traversal 会变慢,而且每多一层就多一次路由出错的机会。
第一版建议加两个规则:
规则一:节点粒度不要只看字数,还要看结构边界。
一个 500 字的表格说明,可能比 1500 字的普通段落更应该单独成节点。
规则二:叶子不要只存 content,要存完整 path。
{
"node_id": "sec_2_3",
"path": ["Risk Factors", "Supply Chain", "Semiconductor Dependence"],
"page_start": 47,
"page_end": 48,
"content": "...",
"parent_id": "sec_2"
}
这样 query-time 不只是拿内容,还能拿位置和层级。
如果节点上只有 title + content + summary,系统会很脆。
因为 query-time 路由几乎全押在 summary 上,而 summary 本质是压缩过的信息,天然会丢细节。
所以节点至少还应该补一批结构化特征:
keyword_setnamed_entities这样做的目的不是让它立刻变成知识图谱,而是给 query-time 更多“低成本、可对齐”的信号。
我通常会把 summary 拆成三种:
routing_summary:给路由模型看,短、概括、强调主题边界answer_summary:给回答模型看,强调事实和限定条件citation_summary:给审计和溯源看,强调页码、章节和原文位置一句话:
这类系统通常会做标准的 bottom-up summary:叶子先总结,父节点再根据子摘要继续总结。这个方向没问题。
问题在于,生产里要防两种坏事:
第一种坏事,越往上越空泛。
到了根节点,最后只剩“这份文档主要讨论收入、风险和战略”,这种东西拿来路由几乎没用。
第二种坏事,关键限制条件在摘要过程中被抹掉。
尤其是财报、合同、政策场景,很多真正决定答案对不对的,恰恰是一些 caveat。
所以 Summary Builder 最好做成两阶段:
先抽关键句、关键字段、表头、实体。
再生成自然语言摘要,但要求模型必须保留限定条件。
这样摘要不会完全漂在空中。
对 demo 来说,JSON 很方便。
但只要你开始考虑下面这些问题:
你就会发现,纯 JSON 很快不够用。
第一版工程系统至少建议拆成两层存储:
存:
存:
最简单的落地方式可以是:
这里不一定要上很重的 infra,关键是把“内容”和“索引”分开。
很多人觉得“这套方法的重点在索引构建”。
我反而觉得,真正决定工程质量的是 query-time。
因为 demo 之所以看起来好用,是因为它把查询阶段做得太理想化了。
用户的原始问题常常很脏:
所以 query-time 第一步,我建议先做轻量 query rewrite,把问题拆成三件事:
lookup / compare / explain / summarize例如:
rewrite 后可以变成:
{
"intent": "explain",
"entities": ["gross margin", "year-over-year"],
"route_hints": ["management discussion", "cost of revenue", "risk factors"]
}
这一步会显著改善 traversal 的稳定性。
这是最想强调的一点。
最小 demo 每层只选一个 child,这在教学上很直观,但工程上太脆。
LATTICE 那篇论文真正有价值的地方,就是它明确指出:LLM 的局部 relevance judgment 是 noisy 的,所以不能把每一步选择search5
所以生产里至少要这样做:
一个非常简单但够用的打分式子可以是:
第一版甚至不用训练,就能跑起来。
任何单一路径策略,最终都得有兜底。
树搜索也一样。
尤其下面这些场景,纯树路由很容易失手:
所以我建议 query-time 至少准备三种 fallback:
按年份、章节、文档类型、页码范围过滤。
专打精确术语、条款号、表头、数值问答。
但别全库上 dense retrieval。
更合理的方式是:
这才是更现实的 hybrid。
很多专业问题天然不是单段可答。
FinanceBench 这类长文档问答基准,本身就是为了测这种能力而建的:它包含 10,231 个问题,每个问题都有对应 evidence string,作者也明确指出,单纯依赖 long-context stuffing 在企业场景里会遇到 latencysearch8
所以 query-time 的理想输出,不应该只是:
而应该是:
{
"primary_leaf": "...",
"parent_summary": "...",
"supporting_siblings": ["...", "..."],
"related_tables": ["..."],
"page_spans": [47, 48],
"path_trace": ["Risk Factors", "Supply Chain", "Semiconductor Dependence"]
}
也就是一个 evidence pack。
最后再由 answer model 去做 grounded synthesis。
这一步的好处很大:
如果今天就起 repo,我会这么拆:
rag-tree/
├── app/
│ ├── api/
│ │ ├── ask.py
│ │ ├── ingest.py
│ │ └── eval.py
│ ├── core/
│ │ ├── config.py
│ │ ├── models.py
│ │ └── logging.py
│ ├── parsing/
│ │ ├── loader.py
│ │ ├── structure_parser.py
│ │ └── canonicalizer.py
│ ├── indexing/
│ │ ├── tree_builder.py
│ │ ├── enrichers.py
│ │ ├── summarizer.py
│ │ └── storage.py
│ ├── retrieval/
│ │ ├── query_rewriter.py
│ │ ├── router.py
│ │ ├── beam_search.py
│ │ ├── fallback.py
│ │ └── evidence_pack.py
│ ├── generation/
│ │ ├── answerer.py
│ │ └── citation_checker.py
│ └── eval/
│ ├── metrics.py
│ ├── datasets.py
│ └── judge.py
├── data/
├── scripts/
│ ├── build_index.py
│ ├── run_demo.py
│ └── run_eval.py
├── tests/
├── index.db
└── README.md
这个结构背后的设计原则很简单:
parsing 负责把文档读对indexing 负责把结构建对retrieval 负责把路径走对generation 负责把答案说对eval 负责告诉你到底哪一层错了这样系统出了问题,才好定位。
如果你想一周内做个 demo,我建议功能先收敛到这些:
markdown / txt / 简单 PDFtitle/content/summary/path/page_rangebeam_size=2这个版本不求“聪明”,求的是能稳定跑完 ingest → index → ask → eval 的闭环。
当第一版能跑后,再补这些能力:
routing_summary / answer_summary 分离到了这一步,系统才开始从“能跑”往“能用”走。
这一步我反而建议别太早做。
等前两版稳定后,再加:
这时候你做出来的,就不是一个纯粹的 vectorless demo 了,而是一套更现实的 structure-first hybrid RAG。
这也是我更认同的方向。
因为今天主流世界并没有抛弃向量检索。OpenAI 的托管文件检索还是 vector store 路线;Anthropic 的优化也是在 retrieval 体系内部演进,而search1
RAG 项目最常见的问题之一,就是最后只看 answer accuracy。
但这对树式检索尤其不够。
因为一个答案错了,可能错在四个不同层次:
所以评测必须拆层。
这是树式检索独有、也最应该重点看的指标。
Path Exact Match:模型走的路径是否和标注路径一致Path Prefix Accuracy:前 n 层是否走对Branch Recall@k:gold node 是否出现在 beam 中Traversal Steps:平均走了几层Fallback Rate:多少请求触发 fallback这层告诉你:
系统到底是“走歪了”,还是“最后回答没组织好”。
Leaf Hit Rate:是否召回 gold leafEvidence Recall@k:gold evidence 是否出现在 evidence packCitation Coverage:答案里的 claim 有多少能指回节点 / 页码Table Hit Rate:表格问题是否召回表格节点这层特别关键,因为很多看起来“答对”的系统,实际证据召回是错的,只是模型猜对了。
RAGVUE 这类新评估框架的价值,就在于它把 RAG 不再只看成一个 answer score,而是拆成 retrieval quality、answer relevance & completeness、claim-level faithfulness、judge calibsearch9
你完全可以照这个思路,为树式检索做一版更细的内部评测。
这一层很多文章不写,但线上最要命。
长上下文模型今天已经能支持 1M token 级别,Google 官方文档也在强调 long-context 的能力;Anthropic 则把“context engineering”提到了更高优先级,强调真正重要的是如何组织上下文,而不是search7
所以你在评测里一定要把一个问题问清楚:
如果这个问题答不清,方案就还没站住。
如果你今天就想做,我建议别上来挑战“全功能 PageIndex”。
先做一个足够小,但工程闭环完整的 demo。
做一个单文档问答系统,支持:
markdown / txt / 简单 PDF第一版别上太复杂的企业文档,先拿 3 类样本:
每类自己写 10–20 个问答,手工标 gold node 和 gold evidence。
总共 30–50 条,就够你看到系统的真问题了。
python scripts/ingest.py --input data/annual_report.md
python scripts/build_index.py --doc-id annual_report
python scripts/run_demo.py --doc-id annual_report --query "What drove gross margin decline?"
python scripts/inspect_path.py --trace-id xxx
python scripts/run_eval.py --dataset data/eval/finance_eval.jsonl
python scripts/report.py --run-id latest
POST /ingest
POST /build_index
POST /ask
POST /eval
每次查询,至少保留这些字段:
{
"query": "...",
"rewritten_query": "...",
"candidate_paths": [
{"path": ["root", "Risk Factors"], "score": 0.82},
{"path": ["root", "MD&A"], "score": 0.76}
],
"selected_path": ["root", "MD&A", "Margins"],
"fallback_used": true,
"evidence_nodes": ["sec_2_3", "table_7", "sec_2_2"],
"final_answer": "...",
"citations": [{"node_id": "sec_2_3", "page": 47}]
}
为什么 trace 这么重要?
因为树式检索最大的优势之一就是路径可解释。
如果你不把路径存下来,那这个优势就白白浪费了。
如果只站在“概念新不新”的角度,这条路当然很吸引人。
但如果站在工程视角,我更愿意把它总结成一句更冷静的话:
这是两回事。
向量检索不会消失。
OpenAI 的 file search 还在走 vector store 路线。
Anthropic 也在继续优化 retrieval,而不是宣布 retriesearch1
但与此同时,另一条趋势也已经很清楚了:
所以我现在真正认可的,不是“纯血 vectorless RAG”这个标签,而是这条更现实的路线:
也就是:
如果你问我,这条路值不值得做?
我的答案是:非常值得。
但前提是,别把它当一个“更酷的 demo”,而是把它当一套真正的检索系统来设计。
过去很多 RAG 系统,其实是在把知识处理成一种最方便机器算相似度的形式。这个方向没有错,它也确实带来了巨大的工程红利:简单、成熟、扩展性强。
但现在越来越多高价值场景在逼着我们承认另一件事:
不是所有知识都适合先碎片化,再向量化。
有些知识,本来就应该先被组织,再被检索。
有些问题,本来就不是“找最像的句子”,而是“顺着结构找到真正该看的那一节”。
有些检索,本来就不是数学近邻,而是带路径感的判断过程。
从这个意义上说,我并不觉得结构化推理检索在“颠覆 RAG”。
它更像是在纠偏。
它提醒大家,RAG 如果继续往前走,检索层迟早会从“扁平 chunk 搜索”走向“结构化导航 + 推理路由 + 多级证据组织”。
这件事,值得继续看。
如果你愿意,我下一步可以直接继续帮你做一版更适合掘金发布的标题、摘要、封面文案、结尾引导语。