AI绿皮书学习之路(五)-RAG知识接入技术

一、为什么需要RAG

1、大模型的两大知识局限

大模型在没有外部技术辅助时,存在两个核心的知识局限性。第一是训练数据的时间截止问题,大模型的知识被“冻结”在训练完成的那一刻,无法凭借自身知道之后发生的新闻、实时动态或新政策。第二是无法访问私有数据,大模型的语料来自公开互联网,无法直接获取企业内部的产品手册、会议纪要等私密资料。

2、传统解决方案的困境

为了解决上述局限,过去常尝试微调和长上下文方案,但它们各有弊端。微调方案就像送厨师去学校学做新菜,不仅需要昂贵的算力和专业团队,而且数据一旦更新就得重新训练,成本高且缺乏灵活性。长上下文方案则是直接把所有文档塞进提示词,这会导致 Token 费用飙升,同时模型在海量文本中查找信息的准确率会下降,且物理上无法容纳超大规模的文档。

3、检索增强生成的核心思想与优势

检索增强生成(简称 RAG)提供了一种更优雅的解决办法,它的核心思想不是让大模型死记硬背所有知识,而是教会它“查资料”的能力。当用户提问时,系统先去知识库中检索出最相关的几段内容,然后把这些真实资料和问题一起拼接好交给大模型,最后由大模型整合理解并生成回答。这种方式具有低成本、灵活可控且可无限扩展的优势。

4、传统搜索与检索增强生成的根本区别

传统搜索的本质是“找资料”,它只能根据关键词进行精确匹配,并返回一堆原始的文档片段,需要用户自己去阅读、理解和整合信息,用户体验比较生硬。而 RAG 的本质是“给答案”,它能够理解人类的自然语言意图,进行语义理解匹配,并跨文档整合信息、自动进行逻辑推理,最终生成针对性强、人性化的具体解决方案。

5、传统数据查询与检索增强生成的特性对比

在传统的资料查询中,人们需要设计结构化的数据库表,并使用复杂的查询语句来获取信息。

SQL

SELECT * FROM knowledge 
WHERE category = 'MySQL' 
AND tags LIKE '%索引%' 
AND tags LIKE '%优化%';

这种方式门槛高,且容易漏掉使用英文或其他近义词记录的故障。而 RAG 允许用户直接使用“线上接口超时怎么办”这样的自然语言进行提问,系统通过将原始文档向量化并在向量数据库中进行语义搜索,最终让大模型整合输出流畅的经验总结,大大降低了数据的使用门槛。

6、总结

本文深入探讨了为什么需要检索增强生成(RAG)技术。由于大模型存在训练时间截止和无法访问私有数据的局限,而传统的微调和长上下文方案又面临成本与效率的困境,RAG 凭借“先检索再生成”的机制脱颖而出。它将大模型的自然语言理解能力与企业的个人知识库完美结合,把传统的“找资料”升级为智能的“给答案”,成为了低成本、高效率构建智能助手和企业知识库的核心方案。

二、RAG核心原理(一)-工作流程与语义搜索

1、RAG的工作流程

RAG系统在处理用户提问时,通常需要经历五个核心步骤。首先是问题向量化,将用户输入的文本转换成一串数字向量;接着是检索相关文档,在向量数据库中寻找与该向量最相似的内容;第三步是提取相关片段,从检索到的文档中筛选出真正有用的段落;第四步是拼接上下文,将提取的片段与用户的原始问题组合成一个新的提示词(Prompt);最后由大模型(AI)基于这些上下文生成个性化的自然语言回答。

2、Embedding的向量化原理

由于传统的结构化数据库查询(如SQL)无法直接应用于小说、网页等非结构化文本,RAG采用了“向量化”的方案,也就是把文字变成数字。这就像给每本书或每个词分配一个“特征码”,让语义相近的词在多维数学空间里彼此靠近(例如“猫”和“狗”的距离很近,而“猫”和“苹果”的距离很远)。在实际应用中,这通常通过调用现成的Embedding API来完成,无论是一句话还是一个段落,都会被整体编码为固定维度的数字列表。

Python

# 文本:"年假怎么申请"
embedding_vector = [
    0.0234, -0.0123, 0.0456, 0.0789, -0.0321, 0.0654,
    -0.0198, 0.0432, ... # 共1536个数字(维度由模型决定)
]

3、相似度计算方法

判断两个向量是否相关的最常用方法是余弦相似度。它通过衡量两个向量在多维空间中的夹角来计算相关性。如果两个向量的方向几乎一致,夹角接近0度,余弦相似度就接近1,代表语义高度相关;如果方向垂直,相似度为0,代表不相关。系统会计算用户问题向量与知识库中各文档向量的相似度,优先选择分值高的文档作为参考资料。

4、向量数据库与Top-K检索

面对海量文档时,逐一计算相似度太慢,因此需要向量数据库。在离线阶段,数据库会将所有文档转为向量并建立特殊的索引结构(如HNSW图);在查询阶段,利用索引可以在毫秒级内快速定位最相似的结果,速度比暴力搜索快上千倍。系统通常会返回相似度最高的前K个结果(即Top-K),经验上简单问题取Top-3,复杂问题取Top-5,以平衡信息的完整性与AI的判断干扰。

5、总结

本文主要阐述了RAG技术(检索增强生成)在语义搜索阶段的核心机制。它通过Embedding技术将复杂的自然语言转化为数学向量,利用余弦相似度度量语义相关性,并借助向量数据库的索引及Top-K机制实现海量文本的毫秒级精准检索。这一系列流程共同构成了大模型获取外部实时或私有知识的基础。

三、RAG核心原理(二)-检索生成与关键机制

1、上下文拼接:喂给AI的“参考资料”

RAG系统需要将检索到的文档片段与用户的问题整合成一个结构化的提示词(Prompt)投喂给大模型。好的提示词模板能够明确AI的角色定位、限定信息边界并给出明确的约束。一个糟糕的模板如果只是将资料和问题胡乱堆砌,会导致AI无法区分参考资料与问题,从而忽略关键信息或导致回答格式混乱。通过合理的结构化呈现和明确的回答规范,可以大幅提升AI的回答质量。

2、生成阶段的逻辑推理

当AI收到完整的结构化提示词后,它会经历理解用户意图、从参考资料中定位答案、逻辑推理和生成回答四个步骤。例如,AI能从用户问题中提取出入职时长、需要请假的天数和时间等关键变量,然后比对参考资料中的年假天数规定和提前申请的时间限制,通过逻辑推理得出“来得及”的结论,并最终为用户定制化生成一份自然流畅且包含具体操作指引的步骤。

3、RAG的关键优势

RAG系统相比于传统检索或单纯的大模型生成具有多项核心优势。首先是语义理解,它不依赖精确的关键词,能识别不同表述下的相同真实意图;其次是跨文档整合,能提炼多份文档的信息综合回答复杂问题;再次是个性化生成,能基于用户的具体情况生成定制化答案;此外还具备可追溯性,通过标注答案来源来有效缓解AI的“幻觉”问题;最后是灵活扩展,添加新知识只需重新向量化存入数据库,无需重新训练模型。

4、文档切分技术细节

在实际实现中,系统检索的并不是整份长篇大论的文档,而是被切分过的小文本片段(Chunk)。如果把几万字完整的《员工手册》直接塞给Prompt,会导致Token超限、无关噪音太多干扰判断以及检索不精准等问题。正确的做法是在离线时将文档切分成数百字的小块,转换为向量存入数据库,同时保留原始文本以便向AI提交和人类核查。

为了避免重要信息在切分点被强行切断,相邻的文本片段之间通常会保留50到100个字符的交叉重叠。常见的切分策略包括按字符数切分、按段落切分、按语义切分以及滑动窗口切分。在检索时,系统会匹配出最相关的几个片段原始文本,将其拼接到提示词中。

Python

# 用户提问
question = "我怎么申请年假?"

# 检索相似的Chunk(不是完整文档)
top_chunks = vector_db.search(question, top_k=3)

# 直接把这些Chunk的原始文本拼接到Prompt
prompt = f"""
【参考资料】
{top_chunks[0].text}
{top_chunks[1].text}
{top_chunks[2].text}

【用户问题】
{question}
"""

5、元数据的作用

元数据(Metadata)是指“关于数据的数据”,在RAG系统中,每个文本片段在保存文本内容的同时,还会额外保存其对应的元数据。这些元数据通常包括文件来源、所在页码、所属章节、最后更新时间等。元数据的主要作用是帮助系统更高效地组织、查找、过滤和管理数据本身。

Python

chunk = {
    "text": "年假申请流程:登录OA系统...",
    "metadata": {
        "source": "员工手册.pdf",
        "page": 12,
        "section": "请假制度",
        "last_updated": "2024-01-15"
    }
}

6、多路召回与重排序机制

“召回”是指从海量数据中把相关结果找回来的过程,而“多路召回”则是由于单一检索方法存在局限性,因此同时使用多种不同的检索方法来获取结果并进行合并。

纯向量检索擅长语义理解,对同义表述敏感,但对网址、电话、专有名词等精确信息容易找不准;纯关键词检索(如基于词频的经典BM25算法)擅长精确匹配,但完全无法理解同义词。

多路召回的解决方案是让不同的检索方法各显神通。系统会同时并行向量检索和关键词检索,将各自返回的候选结果合并在一起并进行去重,接着使用更精细的重排序(Rerank)模型对这些合并后的文档重新打分,筛选出最终最相关的前K个结果提供给大模型,从而实现综合决策。

用户问:"公司OA系统的网址是多少?"

# 路径1:向量检索(Top-5)
vector_results = [
    "OA系统使用指南:登录地址为...",
    "如何登录OA系统...",
    "OA系统常见问题..."
]

# 路径2:关键词检索(Top-5)
keyword_results = [
    "OA系统网址:https://oa.company.com",  ← 精确命中!
    "内部系统网址汇总:OA系统...",
    "网络资源:OA系统地址为..."
]

# 合并去重
all_results = vector_results + keyword_results
# 结果:共8个候选文档(去重后)

# 重新排序(Rerank)
# 使用更精细的模型对这8个文档重新打分
final_results = rerank(all_results, question)
# 最终Top-3:
# 1. "OA系统网址:https://oa.company.com"  (0.95)
# 2. "OA系统使用指南:登录地址为..."      (0.88)
# 3. "内部系统网址汇总:OA系统..."        (0.82)

7、总结

RAG的核心原理是在大模型生成回答前,通过离线文档切分、元数据管理以及融合了向量与关键词的多路召回与重排序技术,精准、低成本地提取出最相关的文本片段,并将其通过结构化的上下文模板拼接到提示词中输入给AI,从而赋予AI具备语义理解、跨文档整合、高可信度及灵活扩展的个性化知识问答能力。

四、Embedding:把文字变成向量

1、什么是Embedding

在传统的计算机处理中,文字如“猫”、“狗”会被转换成无法包含语义信息的字符编码(如Unicode),导致计算机无法理解词语之间的关联。而Embedding技术的作用,就是把文字的“语义特征”用一组固定长度的数字向量(如768维、1536维或3072维)来表示。

这些数字是通过大规模文本数据训练出来的,基于“分布式假设”原理,即相似上下文的词语会拥有相似的向量。通过计算这些向量之间的相似度,计算机就能量化文字在语义上的接近程度,从而真正“理解”文字的含义。现代Embedding模型已不仅限于单字,还能对整个句子、段落甚至文档进行上下文感知的向量化。

2、Embedding模型的选择与维度含义

目前市面上有多种主流的Embedding模型。如果预算充足且追求最高质量,可选择OpenAI的text-embedding-3-large;如果以中文为主且对成本敏感,通义千问的text-embedding-v2是较好的选择;对数据隐私要求极高的场景则推荐本地部署bge-large-zh,资源有限时可选择轻量的m3e-base。

向量的维度代表了其表达的“语义特征”数量。维度越多(如3072维),模型对细微语义差异的区分能力越强,但同时也会带来更大的存储空间、更慢的相似度计算以及更高的API调用成本。因此,常规的问答场景使用768到1536维即可,而法律文档或学术论文等复杂场景则更适合使用3072维。

3、Embedding的三大应用场景

第一是语义搜索,它超越了传统的字面关键词匹配,即使引入“休假流程”和“年假申请”这样用词不同但意思接近的文本,系统也能通过向量相似度将其检索出来。

第二是相似度计算,广泛应用于推荐系统(如根据阅读历史推荐主题相似的文章)、内容去重检测以及学术论文的AI查重。

第三是内容聚类和分类,无需人工标注,利用聚类算法即可自动将海量文本按主题分归到不同的类别中,常用于新闻自动分类、海量客户反馈分析以及社交媒体热点话题的自动发现。

4、实战调用Embedding API

在实际开发中,可以通过安装通义千问SDK来调用其Embedding API。以下是使用Python批量单个文本向量化的核心代码示例:

Python

import dashscope
import os

# 设置API Key
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY") or "sk-xxxxxxxxxxxx"

# 单个文本
response = dashscope.TextEmbedding.call(
    model='text-embedding-v2',
    input='如何申请年假?'
)

if response.status_code == 200:
    embedding = response.output['embeddings'][0]['embedding']
    print(f"向量维度: {len(embedding)}")  # 1536
    print(f"向量前5个值: {embedding[:5]}")
    # 输出示例: [-0.0234, 0.0456, -0.0123, 0.0789, 0.0321]
else:
    print(f"调用失败: {response.message}")

5、Embedding的关键特性与最佳实践

Embedding具有三大关键特性:一是语义相似性,同义表达会产生高度相似的向量;二是跨语言能力,支持多语言的模型能让不同语言的相同语义文本向量保持接近;三是上下文感知,它能根据语境区分多义词(如“苹果手机”与“水果苹果”的相似度,会低于“苹果手机”与“华为手机”的相似度)。

在实际使用时,应注意文本长度的影响,过短会导致语义不足,过长则会导致语义稀释,因此需要合理分块。

同时,由于调用API存在成本且相同文本的向量是固定的,必须引入缓存策略(如内存缓存或持久化JSON文件)来降低费用和提高响应速度。此外,计算相似度时通常需要将向量归一化,不过主流API返回的向量通常默认已完成归一化。

6、总结

本文全面介绍了大模型开发中不可或缺的Embedding(词嵌入)技术。从其将文字转化为语义数字向量的本质原理出发,对比了主流模型及维度的选型策略,并详细阐述了其在语义搜索、相似度计算、文本聚类等领域的广泛应用。通过通义千问API的Python实战示例,展示了如何在开发中落地该技术,并给出了处理文本分块、设计缓存策略等生产环境下的最佳实践指导。

五、向量数据库(一)-核心算法

1、为什么需要向量数据库

在处理大规模企业知识库问答系统时,传统的“暴力搜索”方法面临着巨大的效率瓶颈。假设一个知识库包含10万个1536维的文档片段向量,当用户提出问题时,系统需要将问题转化为向量,并与这10万个向量逐一计算余弦相似度并排序。

这种全量遍历的朴素方法,在10万数据量下可能需要2到3秒的单次查询时间,如果数据量增长到百万或千万级别,耗时将延长至数十秒甚至数分钟,高并发情况下系统极易崩溃。

Python

# 朴素的相似度搜索伪代码
question_vec = get_embedding("年假怎么申请?")

similarities = []
for i, doc_vec in enumerate(all_100k_vectors):
    # 计算余弦相似度,需要遍历10万次高维点积
    sim = cosine_similarity(question_vec, doc_vec)
    similarities.append((sim, i))

# 排序,取前5个
top_5 = sorted(similarities, reverse=True)[:5]

2、向量数据库的核心价值与原理

向量数据库为了解决暴力搜索的痛点,采用了“近似最近邻搜索”(ANN)技术。它的核心思想是“空间分区”,即提前把高维空间中的向量组织成特殊的索引结构,查询时不需要傻傻地遍历所有数据,而是可以“跳着走”,只在最相关的区域内进行搜索。

这种设计牺牲了极少量的精度(准确率通常仍保持在99%以上),但换取了毫秒级的响应速度,使得系统能够支持高并发、具备良好的工程化扩展性,并能轻松处理千万或亿级的数据规模。

3、IVF(倒排文件索引)算法

IVF算法的设计思路可以类比为“在体育场中分区找人”。它通过 K-Means 算法提前把空间中所有语义相近的向量聚类成 N 个簇(例如 1000 个逻辑分组),并计算出每个簇的中心点。

当用户发起查询时,系统首先计算查询向量到这 1000 个簇中心的距离,快速筛选出距离最近的 K 个簇(例如 10 个簇),接着只在这 10 个簇所包含的少量向量中进行精细化搜索,从而将搜索范围缩减到原本的百分之一甚至更小。

4、HNSW(层次化可导航小世界图)算法

HNSW算法将向量组织成一个多层的图结构,类似于金字塔。它通过随机分层机制构建索引,顶层节点最稀疏,连接跨度大;越往下层节点越密集,底层的第 0 层包含所有的向量节点。

查询流程从顶层开始,在每一层利用贪婪搜索找到离查询向量最近的节点,然后逐层下降。在每一层中都能快速定位到大致区域并逐渐逼近目标,最终在底层精确锁定最相似的向量。如果需要找多个相似结果(Top-K),搜索过程中会维护一个固定大小的候选集,最后从中返回最接近的几条数据。

5、总结

向量数据库通过改变传统暴力检索的模式,利用IVF的“聚类分区”思想和HNSW的“多层图结构贪婪搜索”机制,在极短的时间内实现了高维向量的近似最近邻检索,解决了海量文本向量化后的工程化落地与高并发查询难题。

六、向量数据库(二)-进阶算法与Chroma实战

1、LSH(局部敏感哈希)的核心思想与工作原理

传统哈希函数(如MD5)追求微小差异导致完全不同的哈希值,以避免碰撞。而LSH反其道而行之,它的核心思想是让相似的向量有更高的概率被映射到同一个哈希桶中。

LSH使用高维空间中的“超平面”(分界线)来切分空间。因为距离相近的相似向量大概率会落在超平面的同一侧,所以通过判断向量在分界线的哪一侧,就可以为它们赋予相同的哈希码并分到同一个桶中。检索时,只需在对应的哈希桶中搜索,从而大幅提升查询效率。它主要在超高维(大于5000维)或特殊需求时才考虑使用。

2、主流向量数据库的特点与对比

Chroma最适合入门,它开源免费,支持内存模式和持久化,适合小于100万条的中小规模数据。Chroma默认使用HNSW索引算法,且不支持切换其他算法。

Milvus则是企业级首选,作为CNCF孵化项目,它支持亿级数据且具备高可用分布式架构。其核心优势在于支持多种索引算法(如HNSW、IVF、DiskANN),用户可以根据小数据量(选HNSW快)、大数据量(选IVF省内存)、超大数据量(选DiskANN用磁盘)等不同场景灵活选择。

3、Chroma的架构与核心参数配置

Chroma采用简洁的两层结构:客户端(Client)和集合(Collection)。客户端是数据库入口,负责管理数据并提供持久化模式(数据存盘)或内存模式(重启丢失);集合则是实际存储向量的容器,类似传统数据库的表,不同业务场景应使用不同集合。

在向集合添加数据时,有4个核心参数:ids(唯一标识,必须有)、embeddings(向量数据,用于相似度检索,必须有)、documents(原始文本,用于调试,可选)和metadatas(业务数据,返回给用户展示,可选)。存储原始文本和业务数据可以避免检索后去其他数据库二次查询。

Python

# Chroma存储参数标准调用示例
collection.add(
    ids=ids,           # 唯一标识符,如 "faq_001"
    embeddings=embeddings,   # 向量数据(用于检索)
    documents=documents,     # 原始文本(用于调试)
    metadatas=metadatas      # 业务数据(用于展示)
)

4、相似度算法选择与距离转换

文本语义检索必须在创建集合时显式指定使用余弦相似度("hnsw:space": "cosine")。如果使用Chroma默认的欧氏距离(l2),会导致文本检索结果偏低、出现负数甚至完全不相关。余弦相似度关注向量方向(适合文本),而欧氏距离关注空间位置(适合图像、坐标)。

需要特别注意的是,Chroma在配置余弦相似度后,查询返回的指标是“余弦距离(distance)”而不是“相似度”。根据数学定义,余弦距离等于1减去余弦相似度。因此在获取结果后,必须通过“相似度 = 1 - 距离”的公式进行转换,才能还原出直观的相似度分数。

Python

# 查询并进行相似度转换的逻辑示例
results = collection.query(query_embeddings=[query_embedding], n_results=3)

# 从Chroma返回的distance中计算出相似度
for distance in results['distances'][0]:
    similarity = 1 - distance
    print(f"相似度: {similarity:.4f}")

5、总结

内容从LSH算法主动制造“相似碰撞”的核心原理出发,对比了轻量级Chroma与企业级Milvus的适用场景。重点阐述了Chroma的两层架构、文本检索必须绑定余弦相似度的核心装配逻辑,以及如何通过参数合理存储业务数据,并给出了通过“1 - 距离”公式还原相似度分数的计算方法,为构建RAG应用中的向量检索环节提供了完整的技术指导。

七、文档分块:Chunking的艺术

1、分块在RAG系统中的重要性

在真实世界的RAG(检索增强生成)应用中,直接将超长文档输入给AI会导致信息被淹没,增加Token消耗且降低回答准确度。好的文本分块(Chunk)需要同时满足三个条件:精准(不含无关信息)、完整(可独立理解、有足够上下文)以及保持语义单元(不从句子或段落逻辑中间切断)。

2、分块面临的四个核心挑战

分块过程中会遇到四大难题:一是语义边界模糊,在精准度和完整性之间难以取舍;二是上下文依赖无处不在,切得太细会导致代词指代不明或丢失前置背景信息;三是不同用户视角的需求不同,单一的分块方案很难同时满足所有人;四是利用大模型自动识别语义边界会带来极高的成本、时间和稳定性问题,无法在多文档场景下大规模实时处理。

3、现实中的分块优化策略

在实际工程中不追求完美方案,而是追求在当前场景下够用。通常会根据文档类型选择策略(如法律条文按结构切,小说按递归切),并通过准备典型问题集进行测试驱动,在Chunk大小和信息完整度之间寻找平衡,并在上线后根据真实反馈持续迭代。

4、四种常见分块策略详解

第一种是固定大小分块,直接按固定字符或Token数切分,配合一定比例的重叠(Overlap),优点是简单快速但会破坏语义。

第二种是递归分块,优先用大分隔符(如段落、换行)切,切不下再用小分隔符(如句号、逗号)层层递归,能较好地平衡语义和大小,适合90%的通用场景。

第三种是语义分块,通过计算相邻句子的Embedding向量相似度,在话题转换(相似度低于阈值)的地方进行切分,最智能但成本高、速度慢。

以下是语义分块的核心逻辑示例:

Python

def semantic_chunking(text, threshold=0.65):
    # 1. 按句号分句
    sentences = text.split('。')
    
    # 2. 获取每个句子的向量(此处为示意)
    vectors = call_embedding_api(sentences)
    
    # 3. 计算相邻句子的相似度,决定是否切分
    chunks = []
    current_chunk = [sentences[0]]
    
    for i in range(1, len(sentences)):
        similarity = cosine_similarity(vectors[i - 1], vectors[i])
        
        if similarity < threshold:
            # 相似度低 → 话题转换 → 切分
            chunks.append(''.join(current_chunk))
            current_chunk = [sentences[i]]
        else:
            # 相似度高 → 话题连贯 → 合并
            current_chunk.append(sentences[i])
            
    chunks.append(''.join(current_chunk))
    return chunks

第四种是文档结构分块,直接利用Markdown标题、法律条文编号等原生结构进行切分,边界清晰且语义最完整,是结构化文档的首选方案。

5、分块参数调优(Chunk Size 与 Overlap)

Chunk Size(块大小)的选择可以根据文档是否有明确的最小独立单元来决定。强依赖上下文的文档块要大一点(1000-2000),弱依赖的可以小一点(500-1000)。

Overlap(重叠大小)是在两个相邻块之间保留共同文本,避免关键信息被切断。通常推荐将Overlap设置为Chunk Size的10%到20%。

6、总结

文档分块(Chunking)是RAG系统预处理中决定检索精度和语义完整性的核心技术。面对语义边界模糊和上下文丢失等挑战,开发者需要根据文档是否有明确结构,在固定大小、递归、语义和文档结构四种策略中进行权衡选择,并通过合理调整块大小与重叠度参数来优化AI的检索和回答效果。

评论