llm与RAG的学习与优化

本文首发个人博客:llm与RAG的学习与优化 - 黑白の世界

欢迎点击,评论

前言

这是一篇拖延了半年的文章。

年初有计划做一下基于LLM大模型的应用,正好公司有业务需求,于是学习了一下RAG的相关知识,一边看字节开源的 eino 框架学习开发,一边补充这 agent,mcp,rag相关的知识。

当时就计划写一篇总结的文章,不过之后事情太多有些搁置。今天终于是打算拿出来,总结一番。

笔者也是探索中学,如果读者有更好的建议,可以直接在评论区留言。

AI基础基础知识

大模型的区别 LLM,嵌入式与多模态

如果是一位没有接触过AI开发,只局限于使用AI工具的朋友。

可能会对提及的这些名词有些疑惑,可能经常看到一些新闻携带这些名词。

  • LLM模型
  • 也就是大语言模型。是一种利用机器学习技术来理解和生成人类语言的人工智能模型。大家常说的deepseek,gpt-4.1,claude sonnet 4 等,都算是LLM模型
  • 嵌入式模型
  • 用一种自问自答的方式,嵌入式模型就是在嵌入过程中使用的模型
  • 嵌入: 神经网络的一个概念,指将文本,图片等转化为可学习的向量
  • 所以这里可以简单地将嵌入式模型理解为将文本,图片转化为向量的AI模型
  • 不同的嵌入式模型有不同专精的领域,他们对所属领域的向量化表现要比其他模型好
  • 多模态
  • 模型的一种标签。可以简单的理解为可以理解图片,视频,文本等数据的大模型,而不是只能做文字对话

调用方法 OpenAI 标准调用

对于 AI应用开发。对于模型的调用方法多种多样,不同家有不同的标准

  • OpenAI
  • anthropic
  • Gemini
  • Ollama(本地调用)

而我们最常用,也就是各种大模型平台最经常提供的接入方式就是OpenAI标准。

下面是一个使用curl调用OpenAI chat/completions接口的示例:

vbnet 复制代码
curl https://api.openai.com/v1/chat/completions \

  -H "Content-Type: application/json" \

 -H "Authorization: Bearer $OPENAI_API_KEY" \

  -d '{

 "model": "gpt-4",

 "messages": [

      {

 "role": "system",

 "content": "你是一个乐于助人的助手。"

      },

      {

 "role": "user",

 "content": "你好!"

      }

    ],

 "stream": false,

 "temperature": 0.7

  }'

复制

关键参数说明:

  • model: 指定使用的模型,例如 gpt-4, gpt-3.5-turbo。
  • messages: 一个消息列表,用于驱动对话。
  • role: 角色,可以是 system (设定助手行为), user (用户提问), 或 assistant (模型之前的回复)。
  • content: 消息的具体内容。
  • stream: 是否使用流式传输。如果为 true,服务器会分块发送数据,实现打字机效果。
  • temperature: 控制输出的随机性。值越高(如 0.8),输出越随机;值越低(如 0.2),输出越确定。
  • Authorization: 你的API密钥,通常从环境变量 $OPENAI_API_KEY 中获取。

RAG/向量数据库

基础知识

RAG 检索增强生成(Retrieval Augmented Generation)

RAG的提出是因为大模型存在幻觉问题,大预言模型虽然看上去万能,但其实很多时候会一本正经的胡说八道,如果是编程开发中出现这种问题,问题不大,大不了跑一下发现编译不通过重新编码

但是在医生,律师等行业如果出现这种现象,那么就会导致严重的问题。为了解决这个问题,业界提出了RAG方法 Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks

最初的版本就是,在与AI交互的过程中,在prompt中,尤其是 role 中的 system中,写入相关知识内容,防止AI胡编乱造,

向量

简单来说,向量就是一串数字,它像一个'语义坐标',代表了文本在多维空间中的位置。意思相近的文本,它们的'坐标'在空间中也会靠得很近。

但是当相关知识内容变得庞大后,单纯的在prompt中内置全部资料的方法就失效了。

一是模型存在上下文token限制,过多的资料反而会导致它出现幻觉

二是出于成本考虑,如果塞入过多的资料,但是实际只用到其中的一点,那就会导致token浪费,开销过高

然后就有人提出,将不同的资料都存入向量数据库。在AI需要的时候,根据我们的描述内容的向量结果去匹配数据库中符合的向量。

将符合的top k 都视为匹配的数据给到 llm模型 就好。这样节省了token开销,而且还能保证资料可靠

匹配流派:语义与关键词

在向量搜索领域,主流的匹配方式分为两大流派,它们分别解决了不同类型的问题:

  • 稠密向量 (Dense Vector) - 语义搜索
  • 描述:这是我们通常谈论的向量搜索。它通过神经网络嵌入模型(Embedding Model),将文本的深层语义压缩成一个几百到几千维的连续向量。
  • 优势 :能够捕捉文本的真实意图,实现"意思相似"而非"字面匹配"的搜索。例如,搜索"AI编程"可以找到包含"人工智能程序设计"的文档。
  • 劣势:对于需要精确匹配的关键词、术语或代码片段,可能会因为"理解过头"而召回不那么精确的结果。
  • 稀疏向量 (Sparse Vector) - 关键词搜索
  • 描述:这是传统搜索引擎(如Elasticsearch中的BM25算法)思想的现代化。它将文本表示为一个维度极高(如词汇表大小)但绝大部分为零的向量。只有文本中出现的词,在对应维度上才有值。
  • 优势 :能够实现关键词的精确匹配,对于专有名词、产品型号、代码变量等场景效果极佳。
  • 劣势:无法理解同义词或近义词,存在语义鸿沟。搜索"AI编程"无法找到"人工智能程序设计"。
  • 混合搜索 (Hybrid Search)
  • 现代先进的RAG系统通常采用混合搜索策略,即同时执行稠密和稀疏向量搜索,然后将两者的结果智能地融合(通常通过重排序步骤),从而兼具语义理解和关键词精确匹配的优点,达到最佳的召回效果。

向量索引

本来想写一节谈论向量匹配的方法,比如余弦相似度,欧氏距离,点积之类的,不过本文更多是面向开发者,所以就不多谈了,还是聊一聊开发技术选型强关联的内容

向量索引,也可是一种数据库索引。不过它有很多类型

  • 基于图的方法(如HNSW)
  • 把向量组织成一个复杂的网络图,从一个点出发,快速跳到目标附近
  • 目前最流行、性能最好的索引之一。
  • 基于类的方法(IVF)
  • 先把所有向量分成几千个"堆"(聚类),搜索时先判断查询向量属于哪个"堆",然后只在那个"堆"里进行精确搜索。
  • 基于哈希的方法(LSH)
  • 用一种特殊的方式给向量降维和编码,让相似的向量有很大概率得到相同的编码。

常见的数据库

  1. Elasticsearch
  • 一个开源的、分布式的、RESTful的分析引擎,可以处理文本、数值、地理、结构化和非结构化数据。
  • 优点: 支持大数据场景,可以作为大数据(如日志)的存储和检索服务。非常适合需要混合搜索的场景。
  • 缺点: 相对较重,配置和维护有一定复杂度。
  1. Redis
  • 从 2022 年(Redis 7)起,通过模块正式支持向量搜索。
  • 优点: 可以轻松集成到已有的 Redis 架构中,无需过多调整。
  • 缺点: 基于内存运行,不适合处理海量数据,且方案较新,社区验证时间相对较短。
  1. PostgreSQL (通过 pgvector 插件)
  • 通过 pgvector 插件可以为 PG 提供向量检索能力。
  • 优点: 对于已在使用PG的团队,集成成本极低。
  • 缺点: 在作者的场景下,引入会与已有的 MySQL 架构定位冲突,故未考虑。
  1. Milvus
  • 一个开源的、专门为向量搜索设计的数据库。
  • 优点: 支持云原生,可动态拓展,官方宣称可以支持十亿级数据。
  • 缺点: 需要依赖 etcd 等组件,架构略重。

基础 RAG 流程

基础的RAG流程很简单:

  1. 用户提问
  2. 将用户提问通过嵌入式模型转化为向量a
  3. 通过向量a在向量数据库中做匹配,匹配出top k 个数据结果
  4. 将结果放入 system 中,提问放入正常的user中,发送给llm大模型
  5. 将结果返回
rust 复制代码
sequenceDiagram

    participant 用户

    participant RAG程序

    participant 嵌入式模型

    participant 向量数据库

    participant LLM模型

    用户->>RAG程序: 1. 提问

    RAG程序->>嵌入式模型: 2. 将问题文本向量化

    嵌入式模型-->>RAG程序: 返回问题向量

    RAG程序->>向量数据库: 3. 使用问题向量进行搜索

    向量数据库-->>RAG程序: 返回Top-K相似文档

    RAG程序->>LLM模型: 4. 构造Prompt(相似文档 + 原始问题)

    LLM模型-->>RAG程序: 生成最终答案

    RAG程序-->>用户: 5. 返回答案

复制

向量搜索是匹配,不是提问

这是一个需要注意的点。刚开始接触RAG系统的人(就是本人),往往会因为一开始小数据量的成功匹配,将向量匹配当成问答系统了。

明明向量是由数据向量出来的,却用问题的向量去匹配

如:

向量内容: 岗位名称:go开发工程师

提问: 我这里有一个简历,它的内容是 1.预期岗位go .....

这样或许也可以匹配到,但是基本匹配系数比较低,当数据量增大后,容易匹配出一些奇怪的数据

RAG调优

基础的RAG流程虽然简单,但在实际应用中,召回的上下文质量直接决定了LLM生成答案的优劣。为了从"能用"到"好用",我们需要一系列的调优手段来优化召回效果。RAG的调优可以分为三个核心阶段:预处理(Pre-processing)检索(Retrieval)后处理(Post-processing)

1. 预处理:优化数据质量

分块 (Chunking)

将文档切分成合适的、独立的语义单元是RAG中最关键的第一步。分块的质量直接影响向量的质量和检索的精度。

  • 为什么需要分块?
  • 上下文完整性:太小的块可能丢失关键信息,太大的块可能引入过多噪声。
  • Token限制:最终送入LLM的上下文有长度限制。
  • 检索精度:合适的块大小能让向量更好地代表其核心语义。
  • 常见分块策略
  • 固定大小分块 (Fixed-size Chunking):最简单的方法,按固定字符数(如1000个字符)和一定的重叠(Overlap,如200个字符)来切分。优点是简单快速,缺点是容易切断完整的语义单元(如一个句子或段落)。
  • 递归字符分割 (Recursive Character Text Splitting):一种更智能的策略,它会尝试按一组分隔符(如 \n\n, \n, )递归地分割文本,尽可能保持段落和句子的完整性。这是 LangChain 等框架中默认且推荐的方式。
  • 语义分块 (Semantic Chunking):笔者最喜欢用的方法, 最开始看到实在 eino 框架的社区扩展eino-ext中,这是一种看上去就最很好用的方法(除了token开销更大)。而通过源码查看,它的开头处理也是递归分割,只是将分割后的每个片段来做余弦相似度对比,获得最符合语义的片段。

2. 检索:优化查询与召回

元数据过滤 (Metadata Filtering)

通常,我们存储的不仅仅是文本块的向量,还会附带元数据(Metadata),比如文档来源,这段记录对应的实际意义。不但可以用于知识库,还可以用于各种场景类向量数据库的优化。

  • Pre-filtering(预过滤) :在进行向量搜索之前,先根据元数据筛选出一个文档子集,然后仅在这个子集中执行向量搜索。
  • 优点:搜索范围大大缩小,速度快。
  • 缺点:可能会因为过滤掉了部分相关文档而影响召回质量。
  • 例子:先筛选出所有"2024年"发布的关于"AI"的文档,再进行向量搜索。
  • Post-filtering(后过滤) :先执行向量搜索,得到Top-K个结果,然后再对这K个结果应用元数据过滤。
  • 优点:保证了向量搜索的完整性,不会漏掉任何可能相关的结果。
  • 缺点:对于需要返回大量结果的场景,性能可能较低。
  • 例子:向量搜索"黑色的",得到最相似的100张图片记录,然后从这100张中筛选出元数据中type为帽子的结果。
查询转换 (Query Transformation)

用户的原始问题可能与文档中的表述存在语义鸿沟。查询转换的目的就是通过改写或扩展用户的查询,来提升召回率。

  • 假设性问题 (Hypothetical Questions) :这是一种在数据入库时 使用的技巧。针对每一个文档块,让LLM生成几个"这个文档块可以回答什么问题?"。然后将这些问题向量化,与原始文档块关联。在查询时,用户的问题 会去匹配这些假设性问题,实现了"问题-问题"的匹配,通常比"问题-文档"的匹配效果更好。
  • 多查询检索 (Multi-Query Retrieval) :在查询时,让LLM根据用户的原始问题,生成多个不同角度的相似问题,然后用这些问题并行地去检索,最后合并结果。这能有效扩大召回的广度。

还有一种最简单的优化方法。用户提问都过一遍LLM大模型,让他标准化一些。

3. 后处理:优化最终上下文

重排序 (Reranking)

向量搜索(召回/Retrieval)追求的是"快"和"广",可能会包含一些不那么相关的结果。重排序则是在召回之后、生成之前,引入一个更"精"的模型,对初步召回的Top-K个结果进行二次排序。

  • 工作原理 :重排序模型(如 Cohere Rerank 或一些跨编码器模型)会同时评估用户的原始问题每个召回的文档块,给出一个更精确的相关性分数。
  • 优势:能够将最相关、最重要的信息排在最前面,极大地提升了送入LLM的上下文质量,从而改善最终答案的准确性和相关性。这是提升RAG效果最有效的手段之一。

结语

以上内容大概就是笔者最近在大模型学习,RAG开发与特定领域向量数据库构建业务中的一些总结与优化心得。

同时不禁感叹,大模型从22年到如今的发展迅速。在去年的时候,更多还只是用于提示词和问答,而现在以及可以真的用于实际开发之中了。

从GitHub copilot 到 cursor 到claude code。各种工具越来越可靠,越来越现代化。

但是也有些养懒了大脑

同时在写文档中也有了很多帮助。 可以看到本文在谈到RAG调优的格式突然有些变化。因为到这里笔者有些懒了,手写了思路与大纲后,直接让AI优化,然后再手改一番。

本文还只是单纯的 RAG知识库,或者说向量数据库的相关技术点。下一篇就谈一谈 ai agent 与 mcp 吧

相关推荐
心月狐的流火号5 小时前
详解Java内存模型(JMM)
java·后端
就叫飞六吧5 小时前
企业级主流日志系统架构对比ELKK Stack -Grafana Stack
后端·ubuntu·系统架构
CryptoRzz5 小时前
使用Spring Boot对接印度股票市场API开发实践
后端
River4165 小时前
Javer 学 c++(九):结构体篇
c++·后端
日月卿_宇5 小时前
分布式事务
java·后端
齐 飞5 小时前
XXL-JOB快速入门
spring boot·后端·spring cloud
MacroZheng5 小时前
别再用 BeanUtils 了,这款 PO VO DTO 转换神器不香么?
java·spring boot·后端
AAA修煤气灶刘哥5 小时前
从 “库存飞了” 到 “事务稳了”:后端 er 必通的分布式事务 & Seata 闯关指南
java·后端·spring cloud
一刻缱绻5 小时前
iptables MASQUERADE规则对本地回环地址的影响分析
后端·tcp/ip