news 2026/2/3 10:57:39

【干货收藏】告别RAG黑盒:深入解析文档分块、索引增强、编码、检索与重排的全链路优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【干货收藏】告别RAG黑盒:深入解析文档分块、索引增强、编码、检索与重排的全链路优化

本文深入探讨了RAG(Retrieval Augmented Generation)技术的实现细节与优化策略,指出在AI应用开发中,RAG常被视为黑盒导致问题定位困难。文章从文档分块(Chunking)、索引增强(语义增强与反向HyDE)、编码(Embedding)、混合检索(Hybrid Search)到重排序(Re-Ranking)等关键环节进行了详细解析,强调需结合具体场景对各模块进行调优,以提升召回率与精确率的平衡,并倡导从快速使用走向深度优化的实践路径。

写在前面

随着AI应用开发的普及,RAG成了一个家喻户晓的词,非常朴实且出镜率极高,不过在平时也会经常听到一些声音,“RAG效果不好,可能需要微调模型”,“xxx上的RAG产品不好用,召回不精准”,“我需要更强大的RAG工具,不然这个效果很难提升”等等,首先可以肯定,光从上述一些话术中不能说明大家对于RAG的实践和结论是有问题的,但是当进一步沟通的时候,比较多的case中会发现比较那回答出“为什么你觉得RAG不好用?”,“有实际case吗?比如什么样的query召回了什么样的知识?”,“我们的文档是怎么组织的?如何编写的?有做一些结构和分块上的处理吗?”。

日常我们会比较多的把RAG当成一个黑盒,输入是我们沉淀的文档,输出可能是整个AI应用反馈的最终结果(如下图所示),这样的方式下,我们可能可以收获一定的初期收益,但是当要持续优化或者扩展使用场景的时候,可能会缺乏评估和应对的方式,比较难去定位问题,因此也不太能说清楚当下链路的诉求,最后所对应的action也可能会偏离比较大。

Fig1.RAG链路图-粗粒度版,有缺失

下面想稍微深入探索下RAG链路和涉及的一些技术细节,希望可以给到实践中的小伙伴一些参考,从而可以更好的诊断问题、找到可优化的节点,做出更合理的迭代设计。

重新介绍下RAG

RAG核心的功能就是针对用户Query,补充和Query相关的且模型没有的信息,同时要在两个维度上做要求:1.召回率:能够找到最相关的信息;2.精确率:不相关的信息不要;RAG技术以及我们针对实践的设计,主要就是锚定这两个维度指标的提升去的,同时这两个指标在现实实践中是个权衡,需要找到一个相对适合的值,追求两者都很高,不太现实,或者说可能需要付出的代价不足以让我们这么去做。

RAG是RetrievalAugmentationGeneration三个词的缩写,代表了三个核心的行为Retrieve-检索、Augment-增强以及Generate-生成,同时在三个核心动作之外,还有一个embedding-编码,具体见下图:

Fig2.RAG链路图-细粒度版

下面分别针对笔者觉得可能影响RAG实践中效果的一些技术点(图中标有编号的部分),做进一步的描述。

1. 文档分块-Chunking

所谓兵马未动粮草先行,要有一个好的检索结果,首先要从我们的知识文档的优化开始,我们实践中比较重视知识文档的内容沉淀,但是在一些文档结构组织,段落划分,以及一些知识点的内聚性和正交性上会涉及少一点。我们来看下一份文档在语义chunking(基础的按照token、字符、语句、段落切分大部分情况下效果都比较局限)下是被如何处理的。

# 设置本地数据保存目录local_data_dir = pathlib.Path("/Users/jiangdanyang/workspaces/python/MarioPython/src/RAG/dataset/ai-arxiv2")# 加载数据集(如果本地存在则从本地加载,否则下载)dataset = load_dataset("jamescalam/ai-arxiv2", split="train", cache_dir=str(local_data_dir)) # 初始化编码器encoder = OpenAIEncoder( name="text-embedding-3-small", openai_api_key=os.getenv("AI_API_KEY"), openai_base_url=os.getenv("AI_API_BASE_URL")) chunker = StatisticalChunker( encoder=encoder, min_split_tokens=100, max_split_tokens=500, plot_chunks=True, enable_statistics=True,) chunks_0 = chunker(docs=[dataset["content"][0]], batch_size=500)

例子中是针对一篇论文做chunking,chunking会设置min_split_tokens(最小chunk的tokens数)和max_split_tokens(最大chunk的tokens数),chunking完之后的统计结果可见下面的图和表:

Chunking Statistics:`` - Total Documents: 474`` - Total Chunks: 46`` - Chunks by Threshold: 41`` - Chunks by Max Chunk Size: 4`` - Last Chunk: 1`` - Minimum Token Size of Chunk: 54`` - Maximum Token Size of Chunk: 495`` - Similarity Chunk Ratio: 0.89

可以先看下统计的结果文字描述,简单做下解释:

  • 整体Documents(可以简单理解为句子数,本部分设计的document都是该含义):474;
  • 整体切分的文件块chunk:46个,其中41chunk的切分是基于相似度的阈值(可以理解为是按照语义正常划分出来的),有4个是因为达到了500tokens数量被切分的,还有最后1个chunk是到文章结尾了;
  • 最大的chunk的token数495个,最小的chunk的token数54个,因为是lastchunk,所以会出现小于min_split_tokens的情况;
  • 最后SimilarityChunkRatio是统计这次切分的chunk,89%的chunk是按照语义切分出来的(41/46);

SimilarityChunkRatio可以比较好的说明当前外部文档的chunking的结果,因为试想都是被max_split_tokens卡主划分的chunk,后续在语义检索的时候,结果也不会太好。实践中需要针对你的文档情况,调整split的token大小,在chunk的数量和相关性比例上达到一个平衡;除了chunk的大小,还有两个值需要关注:

  • Threshold,就是所谓的相似度的下限,上面例子中threshold是0.31,该值越大,chunk内的相关性越好;
  • WindowSize,是被用于计算的document的数量大小,默认是5,即每次是选择连续的5个document计算相似度,windowsize设置越大,上下文切分的相关性越好,但是同时chunking过程的计算量和耗时也更高,chunk大小相对要大;

这篇论文《Meta-Chunking: Learning Text Segmentation and Semantic Completion via Logical Perception》也给出了一个基于逻辑和语义的chunking的方法,有完整的效果评测,可参考;除了semantic Chunking之外,还有面向多模态数据文档类型的Modality-Specific Chunking(可以比较好的区分不同内容类型的文档块,并面向文本、表格、代码、图使用不同的chunking策略)和Agentic Chunking(让能力强的LLM阅读全文,判断给出切分策略),上述都是工具箱里面的工具,实践中需要结合自身的场景、知识现状、成本综合去权衡选择,并且面向效果进行调优或者切换更适合的方式。

2. 索引增强-Indexing

索引增强,这里介绍两种类型:1.语义增强;2.反向HyDE。

语义增强

语义增强就是将chunk和该chunk所在的文档内容(这里是整片论文)传给LLM,让LLM结合整个文档对这段chunk作个概述,然后把这个概述的信息append到chunk的内容中,从而增强在后续进行语义检索时的精确性。

DOCUMENT_CONTEXT_PROMPT = """<document>{doc_content}</document>""" CHUNK_CONTEXT_PROMPT = """Here is the chunk we want to situate within the whole document<chunk>{chunk_content}</chunk> Please give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk.Answer only with the succinct context and nothing else."""

这里的LLM选择需要能力比较强的大模型,最好可以有promptcache功能,这样可以大大节省这一部的模型调用开销;同时也有一些做法是可以增加前后两个chunk的内容,对于整体文档比较长且前后文本关联度比较大的场景会有一些增强的效果。

反向HyDE

HyDE,Hypothetical Document Embeddings,是正向query检索增强的一种方式,即可以针对用户的query,生成一些假设的答案或者做query扩写,然后通过这些中间内容去做检索召回;反向HyDE的意思是针对chunk(可以视为answer),生成这块chunk可能的question,然后针对这些quetion进行索引构建,关联到具体的chunk内容;反向HyDE相比HyDE的优势是可以离线处理,不影响实时调用的rt。

Given the following text chunk, generate {n} different questions that this chunk would be a good answer to: Chunk: {chunk} Questions (enumarate the questions with 1.2., etc.):

该方法比较适合这类问答型的知识,比如一些答疑内容,有明确的A和Q的,或者可以作为后面hybridsearch中的关键词扩写,提升后续混合检索的效果。

3. 编码-Embedding

Embedding大家应该都很熟悉,就是将输入的文本(多模态内容)转换成向量,主要过程包含文字到token的切分,然后每个token在词汇表中有对应的id,每个tokenid都会对应同等维度(不同embedding模型维度不同)的向量,可以看个简单的例子。

first_sentence = "直播分享会AI Living第一场"second_sentence = "直播鲁班小组分享第77期" model = SentenceTransformer("/Users/jiangdanyang/workspaces/python/Model/all-MiniLM-L6-v2")tokenized_first_sentence = model.tokenize([first_sentence])tokenized_second_sentence = model.tokenize([second_sentence])

编码之后是当前文本对应的tokens的tokenid列表,这里影响编码的原因有这些:

  1. 编码模型的语言问题,不同语言会有不同的分词和词汇表,比如例子中使用的这个编码模型all-MiniLM-L6-v2在处理中文的文本时候,就比较差,可以看到返回的id有好些100(不可识别的token),中文的处理可以找相应的中文embedding模型,但是不是所有语言都有对应的编码模型,因为语种太多,同时如果一些语种对应的数据语料太少,不足以训练这样的一个模型。
  2. 编码模型的词汇表大小,例子中的all-MiniLM-L6-v2的词汇表大小是30522,有些主流模型的词汇表大小基本都在5w以上,有些10w以上,词汇表小会导致一些词无法表示,只能用一个兜底tokenid来代替,会影响后续处理的效果;词汇表大能精准标识文本的输入,但是间接也会增加文本编码完之后的token大小。
  3. 编码模型的语义空间,不同的编码模型有自己的词汇表,以及自己对应的向量语义空间,向量语义空间的效果决定于该模型训练基于的数据集,目前用于文本编码的模型,基本都是现有世界知识的通用语义空间,偏日常、大众化的关联,如果我们需要在一个特定领域下,有一个特殊的语义空间,可能就需要找一个使用该领域下的数据训练的embedding模型,或者需要自己SFT一个,不然预期想要的效果和实际效果可能会有比较大的gap。(顺便说下图知识的问题,直接拿图片当知识,处理过程可能就是OCR的文本提取,或者是LLM对于图片的理解描述,但是这里的干扰会很大,比如中间过程的文本,是不是你期望的样子和描述的维度,这些都需要把握下,不然后续的检索召回肯定也是一团浆糊)

4. 检索-HybridSearch

HyBridSearch,混合搜索,本质上是结合了Term-based和Semantic-based两种模式的检索特性,通过融合两种形式的算法,来提升检索的准确性和相关性;HybridSearch结合了SparseVector(稀疏向量)相似度计算----关键词匹配和DenseVector稠密向量相似度计算----语义匹配,从而提升检索的效果,可见下图:

Sparse向量主要是通过BM25为代表的算法生成,BM25核心就是TF-IDF算法(词频-反向文档频率),返回是某个query相对每个文档编号的分数值(具体算法如下)。

# Load the chunkscorpus_json = json.load(open('/Users/jiangdanyang/workspaces/python/MarioPython/src/RAG/dataset/corpus.json'))corpus_text = [doc["text"] for doc in corpus_json] # optional: create a stemmerenglish_stemmer = snowballstemmer.stemmer("english") # Initialize the Tokenizer with the stemmersparse_tokenizer = Tokenizer( stemmer=english_stemmer, lower=True, # lowercase the tokens stopwords="english",# or pass a list of stopwords splitter=r"\w+",# by default r"(?u)\b\w\w+\b", can also be a function) # Tokenize the corpuscorpus_sparse_tokens = ( sparse_tokenizer .tokenize( corpus_text, update_vocab=True, # update the vocab as we tokenize return_as="ids" )) # Create the BM25 retriever and attach your corpus_json to itsparse_index = bm25s.BM25(corpus=corpus_json)# Now, index the corpus_tokens (the corpus_json is not used yet)sparse_index.index(corpus_sparse_tokens) # Return 10 the most relevant docs according to the querysparse_results, sparse_scores = sparse_index.retrieve(query_tokens, k=10)

Dense向量主要是通过基于Transformer架构的embedding模型来进行编码生成,同时针对查询query,使用同样的embedding模型进行编码,然后再进行向量的相似度比对,找出最相似的n个结果。

#Dense Index# create the vector database clientqdrant = QdrantClient(path="/Users/jiangdanyang/workspaces/python/MarioPython/src/RAG/dataset/qdrant_data")# Create the embedding encoderdense_encoder = SentenceTransformer('/Users/jiangdanyang/workspaces/python/Model/all-MiniLM-L6-v2') collection_name = "hybrid_search"qdrant.recreate_collection( collection_name=collection_name, vectors_config=models.VectorParams( size=dense_encoder.get_sentence_embedding_dimension(), distance=models.Distance.COSINE ))# vectorize!qdrant.upload_points( collection_name=collection_name, points=[ models.PointStruct( id=idx, vector=dense_encoder.encode(doc["text"]).tolist(), payload=doc ) for idx, doc in enumerate(corpus_json) # data is the variable holding all the enriched texts ]) query_vector = dense_encoder.encode(query).tolist()dense_results = qdrant.search( collection_name=collection_name, query_vector=query_vector, limit=10)``` ![](https://i-blog.csdnimg.cn/img_convert/98cd6ceb71cf05c5c7edd9fc9fbd4d9a.jpeg) 最后针对上述两种方式找出的chunk做综合筛选,这里可以有多种方式,比如比较常用的就是先分别对Sparse向量和Dense向量计算出来的topn个结果的分值做归一化,然后针对统一个Chunk,按照一定的权重(比如Sparse向量计算结果权重0.2,Dense向量计算结果权重0.8)计算一个最终分值,最后返回topn个chunk列表给到下个节点: ```code-snippet__js # Normalize the two types of scores

如果当前场景的检索需要兼顾关键词和语义的时候,可以考虑混合搜索(需要结合文档内容、chunking和关键字词构建等环节);相对于关键字词匹配检索,混合搜索可以降低查询编写的规范性(不一定要有特定的关键词出现)以及提升查询的容错性(可能会有拼写错误或者不恰当的描述);相对于语义相似检索,混合搜索可以增加一些领域专有信息的更精准匹配,提升检索结果的准确性。

5. 重排-ReRanking

检索的优点是可以在海量的知识里面快速找到和用户query相关的内容块docs,但是检索所返回出来的docs,实际上可能部分和用户query关联度并不大,这个时候就需要通过re-rank这一步,对于检索返回出来的docs做关联度排序,最终选取最相关的topk个doc,做后续的上下文补充。

在RAG链路中,ReRanking的常用技术是Cross-Encoder(交叉编码器),本质一个Bert模型(Encode-only的transformer架构),计算query和每一个doc相关性,返回0~1之间的结果(1代表最相关),示意图和代码示例如下:

from sentence_transformers import CrossEncoder cross_encoder = CrossEncoder("/Users/jiangdanyang/workspaces/python/Model/jina-reranker-v1-tiny-en")hybrid_search_results = {}with open('/Users/jiangdanyang/workspaces/python/MarioPython/src/RAG/dataset/dense_results.json') as f: dense_results = json.load(f) for doc in dense_results: hybrid_search_results[doc['id']] = doc with open('/Users/jiangdanyang/workspaces/python/MarioPython/src/RAG/dataset/sparse_results.json') as f: sparse_results = json.load(f) for doc in sparse_results: hybrid_search_results[doc['id']] = doc console.print(hybrid_search_results) # This is the query that we used for the retrieval of the above documentsquery = "What is context size of Mixtral?"pairs = [[query, doc['text']] for doc in hybrid_search_results.values()] scores = cross_encoder.predict(pairs)

最后进行排序,选择topk个结果补充到context中,然后调用模型拿最后的结果:

client = OpenAI( api_key=os.getenv("AI_API_KEY"), base_url=os.getenv("AI_API_BASE_URL"))completion = client.chat.completions.create( model="qwen_max", messages=[ {"role": "system", "content": "You are chatbot, an research expert. Your top priority is to help guide users to understand reserach papers."}, {"role": "user", "content": query}, {"role": "assistant", "content": str(search_results)} ])

结语

AI应用的开发实践进行得非常火热,现阶段可能更多的是对已有的一些基建平台、开发编排工具、现成的横向基础产品做整合使用,结合使用场景做链路设计。但是随着时间推移,还是需要慢慢深入到部分细节,往深水区慢慢前行,本文讲述的RAG只是AI架构中的一块,其他相关的技术,在对待方式上也雷同,都需要经历快速使用、技术细节了解、使用产品实现了解、应用中的设计实现迭代、面向效果的循环优化,快速上手有捷径,得益于比较好的基础设施建设,成本比较低,但是深入追寻效果,切实提升效率或幸福感,需要更深入的探寻,希望对读到这里的小伙伴有帮助。

普通人如何抓住AI大模型的风口?

领取方式在文末

为什么要学习大模型?

目前AI大模型的技术岗位与能力培养随着人工智能技术的迅速发展和应用 , 大模型作为其中的重要组成部分 , 正逐渐成为推动人工智能发展的重要引擎 。大模型以其强大的数据处理和模式识别能力, 广泛应用于自然语言处理 、计算机视觉 、 智能推荐等领域 ,为各行各业带来了革命性的改变和机遇 。

目前,开源人工智能大模型已应用于医疗、政务、法律、汽车、娱乐、金融、互联网、教育、制造业、企业服务等多个场景,其中,应用于金融、企业服务、制造业和法律领域的大模型在本次调研中占比超过30%。

随着AI大模型技术的迅速发展,相关岗位的需求也日益增加。大模型产业链催生了一批高薪新职业:

人工智能大潮已来,不加入就可能被淘汰。如果你是技术人,尤其是互联网从业者,现在就开始学习AI大模型技术,真的是给你的人生一个重要建议!

最后

只要你真心想学习AI大模型技术,这份精心整理的学习资料我愿意无偿分享给你,但是想学技术去乱搞的人别来找我!

在当前这个人工智能高速发展的时代,AI大模型正在深刻改变各行各业。我国对高水平AI人才的需求也日益增长,真正懂技术、能落地的人才依旧紧缺。我也希望通过这份资料,能够帮助更多有志于AI领域的朋友入门并深入学习。

真诚无偿分享!!!
vx扫描下方二维码即可
加上后会一个个给大家发

大模型全套学习资料展示

自我们与MoPaaS魔泊云合作以来,我们不断打磨课程体系与技术内容,在细节上精益求精,同时在技术层面也新增了许多前沿且实用的内容,力求为大家带来更系统、更实战、更落地的大模型学习体验。

希望这份系统、实用的大模型学习路径,能够帮助你从零入门,进阶到实战,真正掌握AI时代的核心技能!

01教学内容

  • 从零到精通完整闭环:【基础理论 →RAG开发 → Agent设计 → 模型微调与私有化部署调→热门技术】5大模块,内容比传统教材更贴近企业实战!

  • 大量真实项目案例:带你亲自上手搞数据清洗、模型调优这些硬核操作,把课本知识变成真本事‌!

02适学人群

应届毕业生‌:无工作经验但想要系统学习AI大模型技术,期待通过实战项目掌握核心技术。

零基础转型‌:非技术背景但关注AI应用场景,计划通过低代码工具实现“AI+行业”跨界‌。

业务赋能突破瓶颈:传统开发者(Java/前端等)学习Transformer架构与LangChain框架,向AI全栈工程师转型‌。

vx扫描下方二维码即可

本教程比较珍贵,仅限大家自行学习,不要传播!更严禁商用!

03入门到进阶学习路线图

大模型学习路线图,整体分为5个大的阶段:

04视频和书籍PDF合集

从0到掌握主流大模型技术视频教程(涵盖模型训练、微调、RAG、LangChain、Agent开发等实战方向)

新手必备的大模型学习PDF书单来了!全是硬核知识,帮你少走弯路(不吹牛,真有用)

05行业报告+白皮书合集

收集70+报告与白皮书,了解行业最新动态!

0690+份面试题/经验

AI大模型岗位面试经验总结(谁学技术不是为了赚$呢,找个好的岗位很重要)

07 deepseek部署包+技巧大全

由于篇幅有限

只展示部分资料

并且还在持续更新中…

真诚无偿分享!!!
vx扫描下方二维码即可
加上后会一个个给大家发

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

《游戏场景下伪造内容的识别与处置技术指南》

当AI生成技术精准复刻游戏官方的行文肌理、名人的神态声线,甚至捏造裹挟情绪的诽谤言论,这些伪造内容不仅在排版、术语、语气上与真实信息高度趋同,更能精准捕捉玩家的关注痛点—从版本更新的核心权益到名人代言的情感共鸣,再到针对性的人格诋毁,其迷惑性让传统人工核验陷…

作者头像 李华
网站建设 2026/2/2 2:22:48

计算机Java毕设实战springboot基于顾客偏好的唯品会推荐系统设计与实现电商平台个性化推荐中的精准度与转化效率-【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/2/3 8:16:24

day42(12.23)——leetcode面试经典150

86. 分隔链表 86. 分隔链表 咱也是成功发现leetcode的bug了哈哈哈 题目&#xff1a; 题解&#xff1a; /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val;…

作者头像 李华
网站建设 2026/2/3 1:26:51

html转盘抽奖程序

网页代码如下&#xff1a; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>幸运转盘抽奖</tit…

作者头像 李华
网站建设 2026/2/2 0:34:26

Excel表格大全:模板+教程合集(每日更新)

本期介绍&#xff08;预览图在下方&#xff09;&#xff1a; Excel 表格模板包、视频教程、图文教程及配套练习素材&#xff0c;核心覆盖个人工作计划、企业多部门办公的全场景表格模板&#xff0c;以及从基础到进阶的 Excel 技能教程。适用人群包括职场办公族、财务人员、企业…

作者头像 李华