公司动态
RAG检索不准?用Qwen3微调Embedding模型提升召回质量的实战指南
在 RAG 项目里你有没有遇到过这种情况文档已经切好块、向量库也搭起来了、大模型也换成了更强的版本但检索出来的片段总是“差点意思”——要么漏掉关键信息要么召回一堆无关内容最后大模型基于这些片段一本正经地胡说八道。如果你去调整 prompt、换提示词模板、或加大模型温度通常效果都很有限。真正的问题往往出在Embedding文本向量化模型上通用 Embedding 模型没有见过你的领域数据不理解你的业务词汇和问答习惯召回质量自然上不去。这篇文章想给你一个明确判断RAG 系统的检索准确率最大瓶颈通常在 Embedding 质量而不是大模型本身。而这两年火起来的 Qwen3除了作为生成模型之外还有一个非常实用的角色——它可以帮助你低成本生成高质量的训练数据用来微调 Embedding 模型。也就是说你可以让 Qwen3 当“教练”把 Embedding 模型训练成“懂你领域”的检索器。下面的内容会从原理讲到实战如何用 Qwen3 生成微调数据、如何微调 Embedding 模型、如何评估效果、如何接入 RAG 链路。读完你可以照着一套最少可运行流程在本地或服务器上把这件事跑通。1. 这篇文章真正要解决的问题做 RAG 的人都有一个共同痛点文档库刚上线时效果还行一旦换到专业领域比如法律条款、设备故障手册、金融研报、企业内部制度检索结果就开始“飘”。去查相似度分数前排结果看起来和 query 不是一回事去查召回结果明明文档里有的内容就是检索不到。这时候常见的排查方向是调切块大小、换向量数据库、加 rerank但很多人漏掉了最基础的一环——Embedding 模型本身需要针对你的领域做适配。通用 Embedding 模型是在海量通用语料上训练的它理解“苹果是一种水果”但不一定理解“苹果”在你的业务里指的是“苹果公司供应链条款”或“苹果酸工艺参数”。它能把语义相近的句子聚到一起但无法精确区分你领域里那些“只差一个词但意思完全不同”的文本。于是问题变成怎么让 Embedding 模型懂你的领域答案就是微调。而微调需要训练数据高质量的训练数据从哪里来让大模型来生成这就是 Qwen3 发挥作用的地方。对大多数团队来说这条路比想象中更可行不需要重新训练一个 Embedding 模型只需要在通用模型基础上做少量迭代不需要人工标注几万条数据Qwen3 可以帮助生成 query、positive、hard negative不需要很高配置的 GPUEmbedding 模型参数量远小于生成模型少量数据也能见效。这篇文章适合正在做 RAG 落地、并且已经跑通了基础流程的开发者也适合刚接触 Embedding 微调、想搞明白原理和操作路径学习的人。我会把关键步骤拆开讲尽量让文章可以直接照着做。2. RAG 与 Embedding 微调的核心概念在进入实操之前先把几个概念对齐。2.1 RAG 的工作流程RAGRetrieval-Augmented Generation检索增强生成通常是这样一条链路对知识文档做切块用 Embedding 模型把每个文本块转成向量将向量写入向量数据库如 Milvus、Chroma、pgvector用户提问时把 query 也转成向量在向量库中检索最相似的 Top-K 文本块将文本块拼接进 prompt交给大模型生成回答。在这个链路里每个文本块和 query 是否“语义相近”完全取决于 Embedding 模型的编码能力。如果编码出来的向量空间不对后面的相似度计算、排序、生成都会跟着错。2.2 Embedding 模型到底在做什么Embedding 模型做的事情是把一段文本映射成一个固定维度的向量比如 768 维或 1024 维。训练目标通常是让语义相近的文本向量距离更近语义无关的文本向量距离更远。判断一个 Embedding 模型好不好不只是看它在公开 benchmark比如 MTEB上的分数更要看它在你的数据分布上的召回效果。很多通用模型在公开数据集上表现不错但迁移到特定领域后因为没有见过领域词汇和句式向量空间就会出现偏差。2.3 微调 Embedding 模型的本质Embedding 微调的本质是在原有模型基础上通过少量领域数据调整模型参数让向量空间重新排列使得同一问题的不同问法向量更近问题与对应答案片段向量更近容易混淆的相似但不相关文本向量距离被拉开。这里的“少量”是相对的。一般情况下几千条到几万条质量不错的数据就能让效果有明显变化。这也是为什么 Qwen3 生成数据这么有价值——人工标注几千条 query-document 对成本很高但大模型可以批量生成。2.4 Qwen3 在 Embedding 微调中的角色这里需要澄清一个容易误解的地方Qwen3 本身是生成式大语言模型不是专门的 Embedding 模型。所谓“通过 Qwen3 对 Embedding 进行训练微调”并不是把 Qwen3 直接微调成 Embedding 模型这种做法的训练目标和推理成本都不划算而是让 Qwen3 充当数据生成器和质量过滤器生成查询给定一个文档片段生成若干个可能的用户提问生成正例把文档片段改写为更简洁、更口语化的表达作为 query 的正例生成难负例生成一个表面上相似、但与文档内容无关的文本作为 hard negative辅助蒸馏用 Qwen3 对候选文档的相关性打分作为训练信号。这样做的好处是你可以把 Qwen3 的语义理解能力迁移给更轻量的 Embedding 模型最终推理时只运行 Embedding 模型成本很低。从更泛化的角度看Qwen3 在 RAG 体系里还可以担任 rerank 模型即对向量召回的结果做二次精排。不过这篇文章的重点是 Embedding 微调所以 rerank 只会在评估和工程建议中提一下。3. 微调 Embedding 模型的路线选择不是所有 RAG 项目都需要微调 Embedding。在做决定之前先看看你属于哪种情况。3.1 什么情况下优先考虑微调下面几类问题容易跟 Embedding 有关领域术语频繁出现通用模型无法区分比如“甲状腺结节 TI-RADS 分级”和“乳腺结节 BI-RADS 分级”用户提问口语化、碎片化而文档是正式表达比如用户问“这玩意怎么退”文档里写的是“退货流程”同一实体在不同上下文含义不同比如“Python”可以指语言、指爬虫工具、指某项目代号召回 Top-K 中总是混入大量明显不相关的内容而且这些内容在向量距离上就是很近。如果你遇到这些问题微调 Embedding 往往比换更大检索模型更直接。3.2 微调前的先决条件微调不是银弹。在微调之前先确保基础链路已经正确切块大小和重叠是否合理query 是否需要改写、拼写纠错向量检索的 top-k 是否合理是否已经加过 rerank是否排查过文档本身的质量重复、噪音、格式混乱。如果这些都没问题再进入 Embedding 微调。3.3 常见微调路线对比路线核心做法适合场景成本通用 Embedding 直接使用用 bge-m3、text-embedding-v3 等现成模型通用知识问答文档没有强领域性最低Embedding 全参微调在领域数据上继续训练整个 Embedding 模型数据量中等领域差异大中等需要一定显存Embedding LoRA 微调只训练低秩适配层数据量较少、训练资源有限较低Co-condenser / 对比学习使用对比学习损失优化向量表示需要精细排序质量中等结合 rerank 的流水线向量召回 交叉编码器精排对召回精准度要求高推理时增加一个模型对于刚起步的团队我建议先走「通用模型 向量库 rerank」跑通基线再回头微调 Embedding。微调 Embedding 之后你的 rerank 模型效果通常也会更好因为输入到 rerank 的候选质量提升了。3.4 全参训练与 LoRA 怎么选这里结合很多人关心的“全参训练与微调对显存的要求”来聊。Embedding 模型的参数量通常在 0.1B0.6B 之间比如常见的 bge-large 约 0.3B 参数、bge-m3 约 0.57B 参数。即使全参微调所需显存也远小于微调一个 7B 或 14B 的生成模型。如果使用 24GB 显存的 GPUBGE 系列 Embedding 模型全参微调基本够用如果只是单卡 8GB12GB建议使用 LoRALoRA 可以显著降低显存需求但需要把 Embedding 模型视为底层模型在其上插入低秩矩阵。在实际项目中我更推荐先尝试 Embedding 模型的 LoRA不需要改动太多参数也能获得不错的领域适配能力。如果你的数据集足够大、且评测后仍不满意再尝试全参微调。4. 环境准备与前置条件下面开始实操。这篇文章的示例以 Python 为主使用开源生态。具体版本请以当前最新稳定版为准我这里重点演示通用思路。4.1 硬件要求微调 Embedding 模型所需资源可以按下面做一个预估数据量 1 万条以内LoRA 微调单卡 8GB16GB 显存即可数据量 1 万条以上全参微调建议至少 24GB 显存32GB 更舒适数据集较大时可以先用小模型如 0.1B验证流程再用目标模型训练。Qwen3 生成数据这一步对资源要求更高。如果你本机有 24GB 以上显存可以用 vLLM 部署本地 Qwen3-7B 或更小的 1.7B/4B 模型如果没有 GPU也可以用云厂商提供的大模型 API或者企业内网自建服务。注意使用 API 时务必遵守服务商的使用条款敏感数据要在合规前提下处理。4.2 安装依赖主要依赖如下transformersdatasetssentence-transformersFlagEmbedding官方 BGE 微调工具torchvllm可选用于本地部署 Qwen3milvus或chromadb用于向量检索验证可以用下面的命令安装pip install --upgrade pip pip install torch transformers datasets sentence-transformers pip install FlagEmbedding pip install chromadb # 轻量验证时使用如果你要本地跑 Qwen3 生成数据再单独安装 vLLMpip install vllm安装过程中如果遇到依赖冲突建议创建一个独立的 Python 虚拟环境。python -m venv rag-embedding-env source rag-embedding-env/bin/activate # Windows 下为 rag-embedding-env\Scripts\activate4.3 基础目录结构建议把代码按下面结构组织rag-embedding-finetune/ ├── data/ │ ├── raw_docs/ # 原始文档 │ ├── train_data.jsonl # 训练数据 │ └── eval_data.jsonl # 评测数据 ├── scripts/ │ ├── generate_data.py # Qwen3 生成数据 │ ├── finetune_embedding.py │ └── evaluate.py ├── model_output/ └── README.md5. 用 Qwen3 生成高质量训练数据这是整个流程中最关键的环节。数据质量直接决定微调效果而 Qwen3 在这里能帮你批量生成三样东西query、positive、hard negative。5.1 数据格式设计先定义微调所需的训练样本格式。常见的一种格式是{ query: 用户实际可能会怎么提问, positive: 与 query 匹配的文档片段, negative: 与 query 表面相似但内容不相关的文档片段 }也可以扩展为多个 positive / negative{ query: 如何申请退货, positive: [退货申请入口位于个人中心-订单管理..., 用户可以在订单详情页点击申请售后...], negative: [退款金额计算规则..., 物流时效说明...] }5.2 从文档片段生成 query我们可以把原始文档按切块后的片段作为输入让 Qwen3 生成可能的用户问题。这里以 OpenAI 兼容的 API 为例。如果你本地用 vLLM 部署了 Qwen3API 地址可以指向本地服务。# 文件路径scripts/generate_data.py import json from openai import OpenAI client OpenAI( base_urlhttp://localhost:8000/v1, # vLLM 本地服务地址 api_keyEMPTY # 本地服务可不校验 key ) def generate_queries(doc_chunk: str, num_queries: int 5) - list[str]: prompt f你是一名数据标注专家。请根据下面的文档片段生成 {num_queries} 个用户可能会问的问题。 要求 1. 问题要口语化贴近真实用户表达 2. 问题不能直接复制文档原句要换一种说法 3. 问题既要覆盖文档中的关键信息也要包含可能出现的模糊表述 4. 每个问题占一行不要编号。 文档片段 {doc_chunk} resp client.chat.completions.create( modelQwen3-7B, # 根据实际部署的模型名调整 messages[{role: user, content: prompt}], temperature1.0, max_tokens512 ) content resp.choices[0].message.content return [line.strip() for line in content.strip().splitlines() if line.strip()] if __name__ __main__: sample_doc 钻石会员享受七天无理由退货退货时需保持商品吊牌完整。 queries generate_queries(sample_doc) for q in queries: print(q)从材料上看Qwen3 的小参数版本在指令遵循上也能完成类似任务。如果生成结果质量不稳定可以适当调低 temperature或在 prompt 中增加 few-shot 示例。5.3 生成 hard negativehard negative 是微调中最容易提升效果的数据。它的定义是表面上与 query 相关但其实不是正确答案的文本。比如 query 是“退货需要什么条件”hard negative 是“退款金额如何计算”两者都出现在售后相关页面里语义有交集但答案不同。生成 hard negative 有两种常见方式用向量检索先把文档片段都向量化对每个 query 检索出相似但非对的片段作为负例用 LLM 生成让 Qwen3 根据 query 写一个容易混淆的干扰文本。下面是用 Qwen3 生成 hard negative 的示例# 文件路径generate_hard_negative.py片段 def generate_hard_negative(query: str, positive_doc: str) - str: prompt f请生成一段与以下问题相关的知识片段。 要求 1. 这段话表面上和问题相关出现在同一知识库中 2. 但这段话不能回答用户问题与真正的答案内容无关 3. 控制在 100 字以内。 问题{query} 正确答案片段{positive_doc} 干扰片段 resp client.chat.completions.create( modelQwen3-7B, messages[{role: user, content: prompt}], temperature0.8, max_tokens256 ) return resp.choices[0].message.content.strip()需要注意LLM 生成的 hard negative 不一定满足“不能回答用户问题”这个约束。建议在生成后用同一个 Qwen3 模型做一次过滤或打分只保留明确不匹配的样本。5.4 清洗与保存训练集生成完数据后不能直接拿去训练至少要完成以下清洗去空行、去重复样本过滤掉明显包含隐私、敏感信息的文本过滤掉正例和负例完全相同的样本检查 query 长度去掉过短或过长的噪声项如果数据量太大可以按 query 类型分层采样。清洗后的数据可以统一保存为 JSONLmkdir -p data python -c import json samples [ { query: 退货要满足什么条件, positive: 钻石会员享受七天无理由退货退货时需保持商品吊牌完整。, negative: 退款金额根据订单实际支付金额计算。 } ] with open(data/train_data.jsonl, w, encodingutf-8) as f: for s in samples: f.write(json.dumps(s, ensure_asciiFalse) \n) print(saved) 6. 微调 Embedding 模型的完整示例这里我们以sentence-transformers为例因为 API 简单方便二次开发。如果你想使用 BGE 官方工具FlagEmbedding思路也一致只是数据格式和训练参数会稍有差异。6.1 加载预训练模型我们选一个通用 Embedding 模型作为底座例如 BGE 系列。具体选择哪个可以看你的语言和场景这里使用 BGE 中文模型示意。没有实测资料支撑的情况下请以当前官方可用模型为准。# 文件路径finetune_embedding.py from sentence_transformers import SentenceTransformer model_name BAAI/bge-small-zh-v1.5 # 以实际仓库名为准 model SentenceTransformer(model_name) print(f模型加载完成向量维度{model.get_sentence_embedding_dimension()})6.2 构造训练数据集sentence-transformers支持直接读取 JSONL 文件字段名可以是 query、positive、negative。我们只需要按要求写好my_train.jsonl{query: 退货要满足什么条件, positive: 钻石会员享受七天无理由退货..., negative: 退款金额根据订单实际支付金额计算...} {query: Python 怎么读取 CSV, positive: 使用 pandas.read_csv 可以读取 CSV 文件..., negative: CSV 文件可以用 Excel 打开...}使用datasets加载from datasets import load_dataset dataset load_dataset(json, data_filesdata/train_data.jsonl, splittrain) print(dataset[0])6.3 设置损失函数embedding 微调中最常用的损失函数是MultipleNegativesRankingLoss。它的思想是在一个 batch 内每一条 query 的 positive 应该与它最相似而 batch 中其他样本的 positive / negative 都可以看作该 query 的负例。from sentence_transformers.losses import MultipleNegativesRankingLoss loss MultipleNegativesRankingLoss(model)如果数据里显式提供了 hard negative则可以考虑ContrastiveTensionLoss或自定义带 hard negative 的对比损失。不过MultipleNegativesRankingLoss已经能把 batch 内其他样本作为隐式负例效果通常不错先跑通即可。6.4 训练配置与启动from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, SentenceTransformerTrainingArguments model SentenceTransformer(BAAI/bge-small-zh-v1.5) args SentenceTransformerTrainingArguments( output_dirmodel_output, num_train_epochs3, per_device_train_batch_size16, per_device_eval_batch_size16, learning_rate2e-5, warmup_ratio0.1, fp16True, eval_strategysteps, eval_steps100, save_steps100, logging_steps20, save_total_limit2, ) trainer SentenceTransformerTrainer( modelmodel, argsargs, train_datasetdataset, lossloss, ) trainer.train() trainer.save_model(model_output/final_model)如果你只有 8GB 显存可以把per_device_train_batch_size调小到 8或者开启gradient_accumulation_steps。运行命令python scripts/finetune_embedding.py训练完成后model_output/final_model就是微调后的 Embedding 模型。6.5 训练时的显存与速度观察训练过程中可以关注日志里的train_loss。但这个指标只能作为参考不能代表最终检索效果。真正的效果验证要回到检索评测上。7. 效果评估微调前与微调后很多人微调完只看 loss这不是好习惯。Embedding 微调的最终目标是让 RAG 召回更准确所以要用检索指标来验证。7.1 构建评测集评测集应该尽量贴近线上 query 分布。你可以用 Qwen3 再生成一批 query然后人工审核筛选 100200 条。每条数据包含queryground truth 文档 ID 或文本片段可选一组候选文档。也可以从已有训练数据中切出一部分不参与训练作为验证集。7.2 评估指标常用两个指标RecallK前 K 个检索结果中包含正确答案的比例MRRMean Reciprocal Rank正确答案在排序中位置的倒数衡量排序质量。下面是一个简单的评估实现# 文件路径scripts/evaluate.py from sentence_transformers import SentenceTransformer import numpy as np model_path model_output/final_model # 可换成微调前的模型名做对比 model SentenceTransformer(model_path) # 示例评测集 eval_data [ { query: 退货需要什么条件, gold: 钻石会员享受七天无理由退货退货时需保持商品吊牌完整。, docs: [ 钻石会员享受七天无理由退货退货时需保持商品吊牌完整。, 退款金额根据订单实际支付金额计算。, 普通会员购买的商品在签收后7天内可以申请退货。, 如果商品存在质量问题请联系售后客服处理。 ] } ] def recall_at_k(query, docs, gold, k1): query_vec model.encode([query]) doc_vecs model.encode(docs) scores np.dot(query_vec, doc_vecs.T)[0] top_k_idx np.argsort(scores)[::-1][:k] return sum(gold in docs[i] for i in top_k_idx) 0 for item in eval_data: print(recall_at_k(item[query], item[docs], item[gold], k1))上面的代码比较简单实际项目中一般会根据文档 ID 做去重和排名计算同时统计整体 RecallK 和 MRR。7.3 对比策略建议至少做三组对比原始通用 Embedding微调后的 Embedding微调后 Embedding rerank。这样你能清晰看到每一步带来的收益。如果微调后指标没有提升先检查数据质量、训练轮次、学习率再看是否需要增加 hard negative 比例。8. 将微调后的 Embedding 接入 RAG微调模型训练完成后还需要接入 RAG 系统中实际使用。这里以 LlamaIndex Chroma 为例展示怎么替换模型。8.1 使用自定义 Embedding 封装from llama_index.core import Settings, VectorStoreIndex, SimpleDirectoryReader from llama_index.embeddings.huggingface import HuggingFaceEmbedding from llama_index.vector_stores.chroma import ChromaVectorStore import chromadb # 使用微调后的本地模型 embed_model HuggingFaceEmbedding( model_namemodel_output/final_model, devicecuda, ) Settings.embed_model embed_model # 加载文档 documents SimpleDirectoryReader(data/raw_docs).load_data() # 初始化向量库 chroma_client chromadb.Client() chroma_collection chroma_client.create_collection(my_rag_docs) vector_store ChromaVectorStore(chroma_collectionchroma_collection) # 建立索引并插入文档 index VectorStoreIndex.from_documents( documents, embed_modelembed_model, vector_storevector_store, ) query_engine index.as_query_engine( similarity_top_k5, ) resp query_engine.query(退货需要满足什么条件) print(resp)如果你使用的是 Milvus接入方式也类似只需替换 vector store 的初始化部分。8.2 加上 Qwen3 生成回答检索之后把 Top-K 文档片段传给 Qwen3 生成答案。这一步属于 RAG 的 generation 阶段和 Embedding 微调是互补关系。from openai import OpenAI client OpenAI( base_urlhttp://localhost:8000/v1, # Qwen3 服务 api_keyEMPTY ) def generate_answer(query, contexts): context_text \n\n.join(contexts) prompt f请根据以下资料回答问题。 资料 {context_text} 问题{query} 回答 resp client.chat.completions.create( modelQwen3-7B, messages[{role: user, content: prompt}], temperature0.3, max_tokens512 ) return resp.choices[0].message.content # 假设 query_engine 返回的片段 contexts [node.node.text for node in resp.source_nodes] answer generate_answer(退货需要满足什么条件, contexts) print(answer)在这里Qwen3 担任的是生成角色。如果你的资源充足还可以把 Qwen3 作为 rerank 模型对检索出的 Top-K 片段重新打分进一步提高答案正确率。9. 常见问题与排查方法问题现象可能原因排查方式解决方案训练时显存不足batch size 过大、模型参数较大观察显存占用日志调小 batch size、开启梯度累积、使用 LoRAloss 不下降学习率过高或过低、训练数据太少打印每一步 loss 曲线调整学习率到 1e-55e-5 区间增加数据量微调后检索效果反而变差过拟合、训练数据质量差对比验证集指标增加评测集数量减少训练轮次清理训练数据hard negative 生成成了 positiveLLM 对“不相关”理解不够人工抽检生成结果增加过滤规则或使用向量检索自动选择负样本训练集与线上 query 分布不一致生成 query 过于正式抽样检查 Qwen3 生成结果在 prompt 中加入真实用户提问样本作为 few-shot微调后 embedding 维度变化设置了输出维度检查模型配置保持输出维度一致避免重建向量库向量库需要重建替换 embedding 模型后向量空间变了检查向量维度使用新模型重新 embedding 全量文档其中最容易踩的坑是用微调后的模型做检索但向量库里的向量还是旧模型生成的。两者向量维度相同但分布不一致导致检索结果莫名其妙。所以在替换模型时必须重新建立向量索引。10. 最佳实践与工程建议分享一些我在实际项目里的建议不一定都是你自己的路径但值得参考。10.1 数据为王模型是放大器Qwen3 生成数据很方便但它生成的质量需要你严格把关。建议至少保留 20% 的数据由人工审核修正。一条错误的训练数据可能比十条普通数据造成的伤害更大。10.2 先跑小模型验证流程不要一上来就用最大的 Embedding 模型。先用一个小模型跑通生成数据、训练、接入 RAG、评测的完整链路确认有效后再换大模型、增加数据量。这样可以节省大量等待时间。10.3 保留你的原模型和向量库快照Embedding 模型微调后效果可能变好也有可能在个别 query 上变差。上线前建议保留原始模型文件保留旧向量库的导出备份或重建脚本做 A/B 测试至少对比旧模型和新模型在评测集上的 RecallK / MRR。10.4 结合 rerank 形成多级召回Embedding 微调解决的是“召回”环节rerank 解决的是“排序”环节。两者结合往往比单独使用效果更好。你可以让微调后的 Embedding 先召回 Top-50再用交叉编码器或 Qwen3 精排到 Top-5最后交给生成模型。10.5 定期更新 Embedding 模型业务数据会持续变化Embedding 模型也需要周期性更新。建议建立数据回流机制把线上失败的用户 query 和正确答案积累下来作为下一轮微调的数据集。10.6 注意数据安全与合规在把文档发给大模型 API 生成训练数据之前先检查文档中是否包含敏感信息。如果涉及内部数据优先使用本地部署的 Qwen3 服务不要将数据发送到外部 API。在训练和推理过程中遵循最小权限原则妥善管理 API Key 和模型文件。11. 总结与后续学习方向RAG 不是“PDF 向量库 LLM”三步走那么简单检索质量是从数据到模型全链路共同作用的结果。本文想表达的核心判断是在基础链路无误的前提下Embedding 微调是 RAG 优化中最值得投入的方向之一而 Qwen3 最大的助攻不是直接替代 Embedding而是帮你低成本构造训练数据、担任生成与排序角色。从应用角度你可以按照下面顺序实践用 Qwen3 生成一批 query、positive、hard negative用 sentence-transformers 对通用 Embedding 模型做微调在评估集上对比微调前后的 RecallK 和 MRR把微调后的模型接入 LlamaIndex / LangChain / Milvus重建向量索引最终结合 Qwen3 生成答案形成完整的 RAG 链路。后续如果你想继续深入可以关注这几个方向数据增强如何自动构建更大规模的 hard negative模型蒸馏如何用 Qwen3 的语义打分蒸馏出更小的 embedding 模型Agentic RAG如何让大模型在检索过程中动态决定查什么、查几次多模态 Embedding图文混合场景下的向量表示方案。RAG 优化的道路很长但每一次让“检索更准”的改进都会直接反馈到大模型回答的专业度上。建议收藏这篇下一次你的 RAG 召回不准时再回来看看。