0


四种无向量RAG 方案实测:BM25、GraphRAG、Tree Search、Agent

基于向量的 RAG 优化的是语义相似度(semantic similarity):比如"不允许退货的政策"和"允许退货的政策"这两个查询会产生几乎相同的 embedding。模型理解的不是逻辑而是向量空间中的邻近关系。

更麻烦的是分块(chunking):把文档机械切成片段,会把逻辑连贯性一并打碎。一个定义落在一个 chunk 里,约束它的条件却落在另一个 chunk 里。LLM 拿到的是不完整的上下文所以也只能靠猜。

比如说在受监管的系统里(法律、金融、医疗),这就变成了法律责任。

我没有使用合成基准测试,而是准备了一个小语料库:四份企业文档,JSON 格式,预先抽取好实体与关系。

 [  
    {  
        "id": "doc1",  
        "title": "Refund Policy 2026",  
        "content": "Our refund policy allows for refunds within 30 days of purchase. Digital goods are non-refundable. Error code E-4012 indicates a failed refund transaction due to expired card.",  
        "entities": ["Refund", "30 days", "Digital goods", "E-4012"],  
        "relationships": [["Refund", "has condition", "30 days"], ["E-4012", "means", "expired card"]]  
    },  
    {  
        "id": "doc2",  
        "title": "Q3 Revenue Report",  
        "content": "In Q3 2025, our revenue grew by 15% due to the successful launch of Product X. Manufacturer Z provided critical components. However, supply chain issues with Supplier Y caused minor delays.",  
        "entities": ["Q3 2025", "Revenue", "Product X", "Manufacturer Z", "Supplier Y"],  
        "relationships": [["Product X", "drove", "Revenue"], ["Manufacturer Z", "provided", "components"], ["Supplier Y", "caused", "delays"]]  
    }  
 ]

四种方法都查这一份数据集,但每种方法分别使用刻意挑选、能发挥其长处的查询。得到的结果是:没有任何一种方法是通用的。

方法一:BM25词法检索(Lexical retrieval)


查找具体标识符的场景:错误码、条款编号、产品名称。BM25 不理解含义,它只统计某个词在一篇文档中出现多少次,以及该词在整个语料库中有多稀有(TF-IDF)。这和 Google 搜索最底层做的事情是同一件。

代码(Python,约 30 行)

 from rank_bm25 import BM25Okapi  
   
 tokenized_corpus = [doc['content'].lower().split() for doc in data]  
 bm25 = BM25Okapi(tokenized_corpus)  
 query = "error code E-4012"  
 tokenized_query = query.lower().split()  
 doc_scores = bm25.get_scores(tokenized_query)

测试结果

 Query: 'error code E-4012'  

[doc1] Refund Policy 2026  - Score: 2.2774  
[doc2] Q3 Revenue Report   - Score: 0.0000  
[doc3] Cloud Migration      - Score: 0.0000  
[doc4] Financial Stability  - Score: 0.0000  

 Best match: Refund Policy 2026

精准命中,零噪声。基于向量的 RAG 在这种情况下可能会从另一个上下文里返回一份讲"transaction errors"的文档,因为"error"的 embedding 很相似。BM25 字面地搜索

E-4012

,要么找到,要么找不到。

但是,查询"OOM error"匹配不到讲"out of memory crash"的文档。BM25 不懂同义词。

方法二:GraphRAG 知识图谱遍历

需要把多个来源的事实连起来的时候。经典的多跳(multi-hop)问题:是谁为推动了营收增长的那个产品提供了组件?回答它需要遍历多个图节点。

代码(Python,NetworkX)

 import networkx as nx  

G = nx.DiGraph()  
for doc in data:  
    for entity in doc["entities"]:  
        G.add_node(entity, source_doc=doc['id'])  
    for subj, pred, obj in doc["relationships"]:  
         G.add_edge(subj, obj, relation=pred)

测试结果

 Query: What drove Revenue?  
   
 Relationship found: [Product X] --(drove)--> [Revenue]  
 Additional fact:    [Manufacturer Z] --(provided)--> [components]

两跳、两个事实,外加一条完整的推理路径。换成基于向量的 RAG,拿到的只会是关于营收的 chunk,却没有任何信息能说明营收为什么会增长,因为图谱给出的是因果上下文。

而且构建图谱需要干净、结构化的数据。如果文档是没有格式的 PDF 或零散笔记,实体抽取会很糟糕,图谱也就没用了。

方法三:Tree Search(PageIndex)文档树导航

面对一份结构良好、篇幅很长的文档时——年度报告、法律合同、技术规范——希望系统像分析师那样先扫一眼目录,而不是从头读到尾。

工作方式

  1. 把文档解析为树:标题 → 章节 → 子章节,并附短摘要。
  2. AI 模型在顶层只读标题和摘要。
  3. 选出最可能的节点,"进入"它,读取子章节,重复这一过程,直到抵达相关片段。
  4. 此时才取出全文,并生成答案。

测试结果

 Query: 'financial stability risks'  

Scanning node: Documents  
 -> Analyzing: Refund Policy 2026        - skipped  
 -> Analyzing: Q3 Revenue Report         - skipped  
 -> Analyzing: Cloud Migration            - skipped  
 -> Analyzing: Annual Report - Financial Stability - HIT  
Selected node: doc4 - Annual Report - Financial Stability  
 Content: The board has approved a new budget for Q4 to mitigate risks identified in Q3.

模型扫过标题,拒掉不相关的章节,最后定位到正确的文档,全程没有做任何 embedding。在真实系统里树会有多层嵌套,每个节点都包含由 LLM 生成的摘要。

好的地方是,可以清楚看到系统选择了哪个章节、为什么这样选。在受监管的系统里,这不是"锦上添花",而是硬性要求。

但是,如果遇到没有标题的文档(聊天记录、零散笔记)则无法生成树,导航过程中额外的 LLM 调用也意味着时间和金钱成本。

方法四:Agentic Search 自主研究员

问题复杂、多步骤并且事先不知道该去哪里找答案时。Agent 会自己规划策略,调用工具,评估结果,再决定要不要继续搜索。

代码(agent 循环模拟)

 def agent_loop(data, query):  
    print("Agent: First, I'll search titles for 'Refund'.")  
    results = tool_search_title(data, "Refund")  

if not results:  
        print("Agent: Nothing. Searching deeper in content.")  
        results = tool_search_content(data, "Refund")  
    print(f"Agent: Got a result: {results[0]['title']}. Generating answer.")  
     return results[0]['content']

测试结果

 Agent: Received query: What is your refund policy?  
 Agent (Thinking): Searching for refund policy documents by title.  
 [Tool] Searching titles for: 'Refund'  
 Agent: Result: ['Refund Policy 2026']. I have enough information.  
   
 Answer: Our refund policy allows for refunds within 30 days...

Agent 自行决定如何搜索,并在拿到足够结果时停下。在真实系统中(搭配 o3 或 Gemini 2.5 Pro 这类推理模型),Agent 可以把复杂问题拆解成子查询,交叉验证结果,并回溯到先前的步骤。

这是最贵的方案。每一步思考都是一次独立的 LLM 调用,查询量一大,成本会迅速膨胀;能力较弱的模型还可能陷入无限循环。

方法对比以及如何选择


不要根据哪种方法听起来更前沿来挑选,要看当前系统在哪里出问题:

  1. 返回的文档相似但错误?→ 加 BM25 或结构化信号——问题出在精确度。
  2. 完全找不到相关文档?→ 加向量检索——问题出在召回率(recall)。
  3. 复杂查询返回的是垃圾?→ 加 re-ranker 或 Agent 层。

2026 年大多数成熟的系统都是混合式的:向量负责语义召回,BM25 负责精确匹配,re-ranker 负责质量,基于推理的检索负责最难的那部分查询。

原文最好的一条建议是:从自己的系统里挑出 20–30 个真实问题,把两种方案都跑一遍对比结果。一天的评估能省下几个月的架构争论。

本文代码

完整代码已整理成一个开箱即用的仓库,附带共享测试语料库。每种方法都是一个独立的 agentic skill一段轻量级的 Python 脚本,配一个

SKILL.md

描述文件,AI Agent 可以自动发现并执行:

 # Clone and set up  
git clone https://gitlab.com/public-ako74programmer/ai-projects/4-vectorless-rag-approaches.git  
cd 4-vectorless-RAG-approaches  
setup.bat          # creates venv and installs dependencies  

# Activate the environment  
venv\Scripts\activate  
# Test individual methods  
python .agents/skills/bm25-search/bm25.py "error code E-4012"  
python .agents/skills/graphrag/graphrag.py "Revenue"  
python .agents/skills/tree-search/tree_search.py "financial stability risks"  
 python .agents/skills/agentic-search/agentic_search.py "Refund"

依赖只有

rank_bm25

networkx

,没有别的。每个测试都是几十行 Python 代码,可读、可改,可以拿来扩展到自己的数据上。

总结

基于向量的 RAG 不差,只是有局限,这些局限只在生产里才会暴露,不会出现在 demo 里。无向量方案也不是银弹,每种都有自己的弱点。但把它们以合适的方式组合起来,能搭出一套不只是找到信息,而是能找到正确信息的系统。

先从一种方法开始。测出它的失效点。再加上下一种。

by Andrzej K.

“四种无向量RAG 方案实测:BM25、GraphRAG、Tree Search、Agent”的评论:

还没有评论