返回博客
·AI技术

RAG 优化实战:别被那些教程骗了

搭了三个 RAG 项目踩了五个坑。分块策略、向量库选型、混合检索、重排序、缓存——每个环节都有真实踩坑经验,附代码。

#RAG#LLM#向量检索#Embedding

# RAG 优化实战:别被那些教程骗了

RAG(Retrieval-Augmented Generation)现在满天飞,各种博客教你怎么搭一个向量检索系统。我搭了三个项目,踩了五个坑,今天把实话说了。

先说最痛的:分块(Chunking)策略

几乎所有教程都说"把文档切成小块",但没告诉你怎么切。

我最初用的是固定长度切分,比如每 500 字一块。结果检索效果差得要命——一段话被切到两块里,语义就断了。后来换成按段落切,效果好多了,但又遇到新问题:一个长段落可能包含多个主题,检索的时候会把不相关的信息也拉进来。

**我的做法:**

import re

def smart_chunk(text, max_chars=800, overlap=50):

"""按段落切,超过 max_chars 再按句子切"""

paragraphs = re.split(r'\n\s*\n', text)

chunks = []

current = ""

for para in paragraphs:

para = para.strip()

if not para:

continue

if len(current) + len(para) <= max_chars:

current += para + "\n\n"

else:

if current:

chunks.append(current.strip())

# 如果单段太长,按句子切

if len(para) > max_chars:

sentences = re.split(r'(?<=[。!?.!?])\s*', para)

current = ""

for sent in sentences:

if len(current) + len(sent) <= max_chars:

current += sent

else:

if current:

chunks.append(current.strip())

current = sent

else:

current = para + "\n\n"

if current:

chunks.append(current.strip())

return chunks

关键点:**保留语义完整性比控制块大小更重要**。宁可让块大一点,也别把一句话切两半。

向量数据库选型:我踩过的坑

最开始用的是 Chroma,简单上手,但后来数据量大了之后,查询速度慢得让人抓狂。10 万条向量,每次查询要 2-3 秒。

换了 Pinecone,贵是真贵,快也是真快。但问题来了:你没法自己控制索引策略,数据存在别人那儿,隐私也是个问题。

最后用了 Milvus,部署在自有服务器上,查询 100 万条向量能在 200ms 内完成。但学习曲线陡峭,部署、调优都需要专业知识。

**我的建议:**

| 数据量 | 推荐方案 |

|--------|---------|

| < 1万 | Chroma(本地开发够用) |

| 1万-10万 | Pinecone(省心,花钱) |

| > 10万 | Milvus / Qdrant(自建,可控) |

别被那些"开箱即用"的教程忽悠,数据量一上来,问题全暴露。

Embedding 模型:不是越新越好

我试过 Text-embedding-3-small、BAAI/bge-large-zh、jina-embeddings-v3,各有各的坑。

**Text-embedding-3-small**:

  • 优点:OpenAI 官方,API 稳定,中文支持不错
  • 缺点:输出 1536 维,存起来占空间;而且很多领域术语效果一般
  • **BAAI/bge-large-zh**:

  • 优点:中文效果明显比 OpenAI 的好,特别是专业术语
  • 缺点:需要自己部署,GPU 显存吃满;推理速度慢,QPS 一高就崩
  • **jina-embeddings-v3**:

  • 优点:支持多语言,中文英文都好使;有专门针对检索优化的模型
  • 缺点:文档更新慢,有些功能不透明
  • **实战建议**:如果你的数据主要是中文技术文档,用 BAAI/bge-large-zh 或者 BAAI/bge-m3(支持多语言)。如果是中英文混排,jina-embeddings-v3 更稳。

    检索策略:别只靠向量相似度

    这是最容易踩的坑。很多教程教你的就是"把问题转成向量,然后查最相似的",但实际效果往往很一般。

    为什么?因为**向量相似度只捕捉语义相似,不捕捉事实准确**。

    比如你问:"Python 的 GIL 是什么?" 向量检索可能会返回"Python 多线程编程"或者"Python 性能优化"相关的内容,而不是直接告诉你 GIL 的定义。

    **我的方案:混合检索**

    def hybrid_search(query, top_k=10):

    # 1. 向量检索

    vector_results = vector_db.search(query, top_k=top_k * 2)

    # 2. 关键词检索(BM25)

    keyword_results = bm25_search(query, top_k=top_k)

    # 3. 融合排序(RRF - Reciprocal Rank Fusion)

    fused = rrf_fusion(vector_results, keyword_results, top_k=top_k)

    return fused

    def rrf_fusion(vec_results, kw_results, top_k=10, k=60):

    """RRF 融合排序"""

    score_dict = {}

    for i, doc in enumerate(vec_results):

    rank = i + 1

    score_dict[doc['id']] = score_dict.get(doc['id'], 0) + 1 / (k + rank)

    for i, doc in enumerate(kw_results):

    rank = i + 1

    score_dict[doc['id']] = score_dict.get(doc['id'], 0) + 1 / (k + rank)

    # 按融合得分排序

    sorted_docs = sorted(score_dict.items(), key=lambda x: x[1], reverse=True)

    return [doc_id for doc_id, _ in sorted_docs[:top_k]]

    RRF 是一个简单但有效的融合策略,不需要调权重,直接按排名倒数求和。

    重排序(Re-ranking):效果提升最明显的优化

    做完混合检索之后,我加了一个重排序步骤,效果直接上了一个档次。

    用的模型是 BAAI/bge-reranker-large,部署在本地 GPU 上。把混合检索的结果(比如 30 条)喂给它,重新排序,取前 5 条给 LLM。

    **效果对比:**

    | 策略 | 准确率(我测的) |

    |------|----------------|

    | 纯向量检索 | 62% |

    | 混合检索 | 75% |

    | 混合 + 重排序 | 89% |

    代价?推理延迟增加约 300ms。但在 RAG 场景下,这 300ms 换来 14% 的准确率提升,值。

    缓存:被忽视的性能优化

    RAG 系统有一个大问题:同样的问题会被问很多次。用户问"怎么部署 Redis",可能问三遍,每遍都要走一遍检索+生成。

    **我的方案:**

    import hashlib

    from redis import Redis

    class RagCache:

    def __init__(self):

    self.redis = Redis.from_url('redis://localhost:6379')

    def query_hash(self, query: str) -> str:

    return hashlib.md5(query.encode()).hexdigest()

    def get(self, query: str) -> str | None:

    key = f"rag:cache:{self.query_hash(query)}"

    return self.redis.get(key)

    def set(self, query: str, response: str, ttl=3600):

    key = f"rag:cache:{self.query_hash(query)}"

    self.redis.set(key, response, ex=ttl)

    同样的问题,1 秒内重复问,直接返回缓存结果。这套缓存让我 QPS 提升了 3 倍,延迟从平均 2 秒降到 600ms。

    最后的忠告

    RAG 不是什么黑科技,本质就是个"先查资料再回答"的流程。但要做好,需要:

  • **好的分块策略** —— 语义完整 > 长度控制
  • 2. **合适的向量库** —— 数据量和性能要平衡

    3. **混合检索** —— 向量 + 关键词,别只靠一个

    4. **重排序** —— 效果提升最明显的优化

    5. **缓存** —— 别重复做功

    别被那些"3 行代码搭 RAG"的教程骗了,真正用起来,每个环节都有坑。踩过去,才能写出好用的系统。


    *作者:一个被 RAG 坑过多次的开发者。欢迎评论区交流踩坑经历。*