尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

向量数据库与FAISS索引:RAG系统高效检索的核心原理与实战选型指南

向量数据库与FAISS索引:RAG系统高效检索的核心原理与实战选型指南 1. 项目概述为什么向量数据库是RAG的基石如果你最近在折腾大模型应用尤其是想让它“有据可查”地回答问题那“RAG”这个词你肯定不陌生。RAG检索增强生成说白了就是让大模型在回答前先去你的知识库里翻翻资料避免它一本正经地胡说八道。而这个过程里最核心、也最容易让人困惑的一环就是如何让模型快速、准确地从海量资料里找到最相关的那几段。答案就是向量数据库和向量索引技术。这就像你有一个巨大的图书馆你的知识库里面堆满了各种文档。当用户问“如何更换汽车轮胎”时你不可能让大模型一页一页去翻所有维修手册。你需要一个超级高效的图书管理员能瞬间理解问题的“意思”然后从书海中精准抽出讲“轮胎拆卸步骤”和“千斤顶使用”的那几页。这个“理解意思”并“快速查找”的能力就依赖于将文本转化为数学上的向量一组数字并通过向量索引进行相似度计算。我见过不少团队在搭建RAG系统时把大部分精力花在了大模型选型、Prompt工程上却在向量检索这一步草草了事随便选个数据库或者索引就上了。结果就是系统召回的内容要么不相关要么慢得让人无法忍受整个RAG的价值大打折扣。今天我们就来彻底拆解这个“图书管理员”的核心装备——向量数据库与FAISS索引。我会从它们最底层的原理讲起一直聊到在不同实战场景下你该如何做出最合适的技术选型。无论你是刚开始接触RAG的新手还是正在为现有系统检索性能发愁的开发者这篇指南都能给你带来实实在在的参考。2. 核心原理拆解向量、索引与相似度搜索在深入工具之前我们必须把几个核心概念掰扯清楚。这是后续所有选型和优化的基础。2.1 万物皆可向量Embedding的本质文本、图片、音频在计算机眼里最初都是一堆无意义的字节。要让机器理解它们的“语义”就需要通过Embedding模型将其映射到一个高维的向量空间。这个向量空间的神奇之处在于语义相近的内容其对应的向量在空间中的距离通常是余弦相似度或欧氏距离也会很近。举个例子我们用某个Embedding模型将三个句子变成向量“我喜欢吃苹果” - 向量 A“苹果是一种水果” - 向量 B“我正在编写代码” - 向量 C在向量空间里A和B的距离会非常近因为它们都关于“苹果”水果而C则会离它们很远。RAG的检索过程就是将用户问题也转化为向量Q然后在知识库的所有文本向量中找出与Q距离最近的K个向量它们对应的原始文本就是我们要召回的“相关片段”。注意Embedding模型的质量直接决定了检索的上限。一个糟糕的模型即使索引再高效找出来的东西也是牛头不对马嘴。通常我们会选择像text-embedding-ada-002、bge-large-zh这类经过海量数据训练、在通用语义匹配任务上表现良好的模型。2.2 索引从暴力扫描到智能检索假设你的知识库有100万个文档片段每个片段都是一个768维的向量。当一个新的查询向量到来时最笨的办法就是“暴力扫描”Brute-force计算查询向量与这100万个向量每一个的距离然后排序。这保证能找到最精确的Top-K结果但计算量是O(N)当N很大时延迟完全无法接受。索引技术的核心目标就是用精度换速度在可接受的误差范围内极大地加速搜索过程。其主流思想可以分为两大类量化Quantization降低向量表示的精度来节省存储和计算。比如将原始的32位浮点数向量通过聚类等方法映射到由少数几个“原型向量”构成的码本上。搜索时只需计算查询向量与这些原型向量的距离大大减少了计算量。乘积量化Product Quantization, PQ是其中最著名的方法。空间分割Partitioning将高维向量空间划分成多个区域搜索时只查询少数几个可能包含近邻的区域。这就像在地图上用经纬度划分网格找附近的餐馆时你只需要搜索自己所在及相邻的几个网格即可。倒排索引IVF、分层可导航小世界图HNSW都属于这类方法。2.3 FAISSMeta开源的索引库“瑞士军刀”FAISSFacebook AI Similarity Search并不是一个完整的数据库而是一个专注于高效相似度搜索和稠密向量聚类的C库提供Python接口。它本身不负责数据的持久化、分布式、或者多用户并发它的核心价值在于提供了一整套先进、高效的向量索引算法实现。你可以把FAISS理解为一个功能强大的“索引算法工具箱”。它把前面提到的量化、空间分割等方法以及它们的各种组合封装成了一个个可配置的索引类型。比如IndexFlatL2: 这就是那个“暴力扫描”的索引精度100%速度慢。IndexIVFFlat: 先用聚类方法如K-means把向量空间划分成nlist个单元倒排列表搜索时只查询距离最近的nprobe个单元。速度显著提升精度略有牺牲。IndexIVFPQ: 在IVF的基础上再对向量进行乘积量化进一步压缩内存占用并加速计算。IndexHNSW: 基于图结构的索引性能优异尤其适合高召回率场景但构建索引较慢且内存占用大。FAISS的强大之处在于它的灵活性和性能。你可以根据数据规模、内存限制、精度要求和延迟敏感度像搭积木一样组合不同的组件来构建最适合你的索引。2.4 向量数据库一站式的向量数据管理平台如果说FAISS是专注算法的“发动机”那么向量数据库如Milvus, Pinecone, Weaviate, Qdrant等就是配备了发动机、底盘、车身和空调的“整车”。一个成熟的向量数据库通常会提供以下核心功能数据持久化与存储管理将向量及其关联的元数据如原始文本ID、来源、时间戳等可靠地存储在磁盘上。分布式与可扩展性支持数据分片、多副本能够横向扩展以处理十亿甚至百亿级别的向量。完整的CRUD操作不仅支持插入和搜索还支持按ID或条件更新、删除向量数据。元数据过滤在向量相似度搜索的同时可以结合结构化过滤条件如“创建时间在2023年后”、“文档类型为PDF”。这是很多复杂业务场景的刚需。多租户与访问控制支持不同用户或应用的数据隔离与权限管理。运维工具监控、备份、升级等企业级功能。核心区别你需要FAISS当你已经有一套存储系统如MySQL、Redis只想找一个最快、最灵活的索引库来加速内存中的向量搜索。你需要向量数据库当你需要从头构建一个生产级的、需要处理海量向量数据、并提供完整数据生命周期管理的系统。3. FAISS索引全解析从入门到调优理解了原理我们动手用FAISS来解决实际问题。这里我假设你已经有了一批文本的Embedding向量例如通过sentence-transformers库生成。3.1 基础索引类型与实战代码首先安装FAISSpip install faiss-cpu或faiss-gpu如果你有CUDA环境。场景一小规模数据集追求极致精度如果你的知识库只有几千到几万条数据并且对精度要求极高例如法律、医疗领域的严谨问答那么IndexFlatL2欧氏距离或IndexFlatIP内积需归一化后等同于余弦相似度是最简单直接的选择。import numpy as np import faiss # 假设我们有10000个768维的向量作为知识库 d 768 # 向量维度 nb 10000 # 数据库大小 np.random.seed(1234) xb np.random.random((nb, d)).astype(float32) # 知识库向量 xb[:, 0] np.arange(nb) / 1000. # 让数据有一点规律便于观察 # 构建Flat索引 index_flat faiss.IndexFlatL2(d) # 使用L2距离欧氏距离 print(f索引是否已训练: {index_flat.is_trained}) # Flat索引不需要训练 # 添加向量到索引 index_flat.add(xb) print(f索引中的向量数: {index_flat.ntotal}) # 进行搜索 nq 5 # 有5个查询向量 xq np.random.random((nq, d)).astype(float32) xq[:, 0] np.arange(nq) / 1000. k 4 # 返回每个查询的最近4个邻居 D, I index_flat.search(xq, k) # D是距离I是索引 print(f最近邻的索引:\n{I}) print(f最近邻的距离:\n{D})实操心得IndexFlatL2和IndexFlatIP虽然慢但它们是验证其他索引精度的“黄金标准”。在开发初期可以用它来确保你的Embedding模型和检索流程是正确无误的。场景二中等规模数据集需平衡速度与精度当数据量达到几十万、上百万时IndexIVFFlat就该登场了。# 继续使用上面的 xb nlist 100 # 将向量空间划分为100个单元聚类中心数 quantizer faiss.IndexFlatL2(d) # 用于对每个单元进行精细比较的量化器 index_ivf faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2) # IVF索引需要先“训练”即通过聚类找到nlist个单元中心 assert not index_ivf.is_trained index_ivf.train(xb) # 训练需要数据通常用全部或部分数据 assert index_ivf.is_trained index_ivf.add(xb) print(fIVF索引中的向量数: {index_ivf.ntotal}) # 搜索时指定要探查的单元数 nprobe。nprobe越大精度越高速度越慢。 index_ivf.nprobe 10 # 探查10个最近的单元 D_ivf, I_ivf index_ivf.search(xq, k) print(fIVF最近邻索引:\n{I_ivf}) # 可以对比一下 I 和 I_ivf 的重合度评估精度损失关键参数解析nlist聚类中心数量。值越大每个单元内的向量越少搜索越快但训练耗时和内存占用也增加。经验值是sqrt(N)到4*sqrt(N)之间N为总向量数。nprobe搜索时探查的单元数。这是平衡速度与精度的核心旋钮。线上服务时可以通过动态调整nprobe来应对不同的延迟要求。3.2 高级索引与内存优化当数据量进一步增大或者内存成为瓶颈时我们需要引入量化技术。IndexIVFPQ速度与内存的权衡大师PQ将高维向量切分成多个子向量分别进行量化能极大压缩存储。m 8 # 子向量的个数必须能被维度d整除 bits 8 # 每个子量化器的比特数通常为8即每个子向量用256个原型表示 # 创建使用PQ的量化器 quantizer_pq faiss.IndexFlatL2(d) index_ivfpq faiss.IndexIVFPQ(quantizer_pq, d, nlist, m, bits) # 同样需要训练 index_ivfpq.train(xb) index_ivfpq.add(xb) index_ivfpq.nprobe 10 D_pq, I_pq index_ivfpq.search(xq, k)注意事项PQ会引入额外的量化误差精度损失比IVFFlat更大。务必在测试集上评估召回率RecallK是否满足业务要求。通常m取d的约数bits8是常用值。IndexHNSW基于图的强力选手HNSWHierarchical Navigable Small World是近年来非常流行的基于图的索引在多个基准测试中表现优异尤其擅长高召回率场景。# 构建HNSW索引 M 32 # 每个节点在图中连接的邻居数越大则图越稠密精度越高内存消耗和构建时间也越长 index_hnsw faiss.IndexHNSWFlat(d, M) index_hnsw.add(xb) # HNSW搜索时的主要参数是 efSearch它控制搜索时探索的候选节点数量 efSearch 64 # 值越大搜索越精确也越慢 index_hnsw.hnsw.efSearch efSearch D_hnsw, I_hnsw index_hnsw.search(xq, k)实操心得HNSW构建索引很慢且一旦构建完成就无法增量添加数据需要重建。但它搜索速度快、精度高。适合读多写少、数据相对静态的场景。M和efSearch是关键调优参数需要在你的数据集上进行网格搜索。3.3 索引的序列化与加载FAISS索引可以保存到磁盘供后续加载使用。# 保存索引 faiss.write_index(index_ivf, “my_index_ivf.index”) # 加载索引 loaded_index faiss.read_index(“my_index_ivf.index”) # 注意加载的索引是只读的如果需要添加新数据必须从原始数据重建或使用支持增量添加的索引类型如IVF。4. 向量数据库选型实战指南当你需要超越FAISS单机库的能力迈向生产系统时向量数据库就是必经之路。选型没有银弹关键看你的场景。4.1 核心选型维度对比下表从几个关键维度对比了几款主流开源向量数据库特性维度MilvusQdrantWeaviateChromaPGVector (PostgreSQL扩展)核心架构云原生存储计算分离单机/集群Rust编写单机/集群Go编写内置GraphQL轻量嵌入Python为主PostgreSQL插件SQL生态部署复杂度较高组件多中等中等极低低如果你已有PG性能与规模最优专为十亿级向量设计优秀注重过滤性能优秀支持多模态轻量适合中小规模依赖PG百万级尚可亿级吃力元数据过滤强大支持复杂布尔表达式非常强大支持多种字段类型和地理空间强大GraphQL语法灵活基础强大即SQL WHERE子句多租户支持支持通过集合支持通过类支持通过集合依赖PG schema/role语言/生态Python/Go/Java SDK 生态最丰富Rust/Python等API简洁Go/Python GraphQL原生Python优先简单任何PG客户端SQL学习成本较高中等中等需学GraphQL极低低对DBA/后端典型场景大规模、高并发生产系统需要极致扩展性对过滤和性能有高要求的业务应用需要灵活图查询、多模态检索快速原型、中小项目、LangChain集成已用PG向量需求简单强事务一致4.2 场景化选型决策树你可以通过回答下面几个问题来缩小选择范围你的数据量级和增长预期是多少 1000万条几乎所有数据库都能胜任。可以优先考虑部署简单的Chroma, Qdrant单机或与你现有技术栈契合的PGVector。1000万 ~ 数亿条需要认真考虑扩展性。Milvus、Qdrant集群、Weaviate集群是主要候选。 数亿条Milvus是为这种规模设计的是首选。需要专业的运维团队。你的团队技术栈和运维能力如何强Python追求快速上线Chroma是绝佳起点与LangChain等框架集成无缝。有成熟K8s和运维团队可以驾驭Milvus的复杂部署以换取未来的扩展性。后端以Rust/Go为主QdrantRust或WeaviateGo可能在性能和集成上更舒服。公司重度使用PostgreSQL且DBA团队强大PGVector是风险最低、一致性最好的选择可以复用现有备份、监控、权限体系。你的业务查询模式有多复杂纯向量相似度搜索所有数据库都支持。需要复杂的元数据过滤如“状态为已发布且作者是张三且创建于上周的文档”Qdrant、Milvus、Weaviate提供了最强大的过滤能力。需要结合标量过滤和向量搜索进行重排序这是高级RAG的常见需求需要数据库在底层支持Milvus和Qdrant做得较好。读写模式与一致性要求读远大于写允许最终一致性大多数向量数据库为此优化性能更好。需要强一致性频繁增删改PGVector依托PG的ACID事务能提供最强的一致性保证。其他数据库在集群模式下的一致性模型需要仔细考察。4.3 以Milvus为例的快速上手假设我们经过评估选择Milvus来处理一个千万级向量的项目。步骤1部署对于测试使用Docker Compose是最快的方式。# 下载docker-compose.yml wget https://github.com/milvus-io/milvus/releases/download/v2.4.0/milvus-standalone-docker-compose.yml -O docker-compose.yml # 启动 sudo docker-compose up -d步骤2连接与集合Collection定义集合类似于关系数据库的表。from pymilvus import connections, CollectionSchema, FieldSchema, DataType, Collection, utility # 连接到Milvus服务 connections.connect(host‘localhost’, port‘19530’) # 1. 定义字段 # 主键字段 id_field FieldSchema(name“id”, dtypeDataType.INT64, is_primaryTrue, auto_idTrue) # 向量字段 (假设维度为768) embedding_field FieldSchema(name“embedding”, dtypeDataType.FLOAT_VECTOR, dim768) # 元数据字段 title_field FieldSchema(name“title”, dtypeDataType.VARCHAR, max_length512) content_field FieldSchema(name“content”, dtypeDataType.VARCHAR, max_length65535) # 2. 构建Schema schema CollectionSchema(fields[id_field, embedding_field, title_field, content_field], description“RAG知识库集合”) # 3. 创建集合 collection_name “rag_knowledge_base” if utility.has_collection(collection_name): utility.drop_collection(collection_name) # 测试时清理旧数据 collection Collection(namecollection_name, schemaschema) # 4. 创建索引这里使用IVF_FLAT索引 index_params { “index_type”: “IVF_FLAT”, “metric_type”: “L2”, # 或“IP” “params”: {“nlist”: 1024} # 聚类单元数 } collection.create_index(field_name“embedding”, index_paramsindex_params) # 加载集合到内存以进行搜索 collection.load()步骤3插入与搜索数据# 插入数据 data [ [i for i in range(5)], # 模拟5个768维向量实际应从Embedding模型获取 [“文档标题1”, “文档标题2”, …], [“文档内容1”, “文档内容2”, …] ] # 注意实际插入时data应是一个list其中每个元素是对应字段的所有值列表 # 例如: data [vec_list, title_list, content_list] mr collection.insert(data) # 返回MutationResult print(f”插入的ID: {mr.primary_keys}”) # 搜索 search_params {“metric_type”: “L2”, “params”: {“nprobe”: 10}} results collection.search( data[[0.1]*768], # 单个查询向量实际应为问题Embedding anns_field“embedding”, paramsearch_params, limit5, # 返回Top-5 exprNone, # 元数据过滤表达式如 “title like ‘%故障%‘” output_fields[“title”, “content”] # 指定返回的元数据字段 ) for hits in results: for hit in hits: print(f”ID: {hit.id}, 距离: {hit.distance}, 标题: {hit.entity.get(‘title’)}, 内容片段: {hit.entity.get(‘content’)[:100]}…”)5. RAG系统中的检索优化与工程化实践选好了数据库或索引并不代表RAG检索环节就高枕无忧了。在实际工程中我们还需要解决一系列问题来提升最终效果。5.1 多路召回与混合检索单纯依靠向量相似度语义召回有时会漏掉关键词完全匹配的重要文档。因此工业级RAG系统通常会采用“多路召回”策略语义召回路使用向量数据库如上文所述。关键词召回路使用传统全文检索引擎如Elasticsearch, BM25算法。 将两路召回的结果合并再去重、排序能有效提升召回率。这就是“混合检索”。实现思路对同一批文档既构建向量索引也构建倒排索引存储文本。收到查询后并行执行向量搜索和关键词搜索。对两路结果进行融合。融合策略可以是加权求和最终分数 α * 向量相似度分数 (1-α) * BM25分数。需要统一分数尺度如归一化到[0,1]。RRFReciprocal Rank Fusion不依赖分数绝对值只依赖排名更鲁棒。RRF分数 Σ (1 / (k rank_i))对每个文档在不同召回列表中的排名进行加权求和。按最终分数重排序取Top-K。5.2 重排序Re-ranking召回得到Top-K比如100个文档后直接全部塞给大模型会浪费上下文窗口且可能引入噪声。重排序器Re-ranker的作用是使用一个更精细但更耗时的模型对这100个文档与问题的相关性进行精排只选出最相关的5-10个送给大模型。常用工具交叉编码器Cross-Encoder如BGE-reranker、bge-reranker-v2-m3。它将问题和文档拼接起来输入模型直接输出一个相关度分数比双塔式的Embedding模型更准但无法预先计算只能在线运行。ColBERT等后期交互模型在精度和速度间取得较好平衡。集成示例伪代码# 1. 多路召回 vector_results vector_db.search(query, top_k100) keyword_results es.search(query, top_k100) # 2. 融合 (以RRF为例) fused_results rrf_fusion([vector_results, keyword_results]) # 3. 重排序 reranker CrossEncoder(‘BAAI/bge-reranker-large’) pairs [[query, doc.text] for doc in fused_results] scores reranker.predict(pairs) # 4. 按重排序分数排序取Top-5 reranked_docs [doc for _, doc in sorted(zip(scores, fused_results), reverseTrue)][:5]5.3 索引维护与数据更新知识库不是静态的。如何处理新增、修改和删除FAISS大部分索引如IVF, HNSW不支持高效的增量更新。标准做法是定期如每天全量重建索引。对于小规模、更新不频繁的场景可以缓存新增数据搜索时同时查询索引和缓存最后合并结果。向量数据库通常都支持单条数据的增删改。但需要注意修改或删除后索引本身可能需要后台异步更新或合并段才能立即生效。务必查阅所选数据库的文档了解其数据更新的一致性和延迟。5.4 性能监控与评测上线后必须建立监控体系。核心指标召回率RecallK人工标注一批问题-标准答案对检查系统召回的Top-K文档中包含标准答案的比率。这是衡量检索效果的核心。延迟P99 Latency检索阶段的耗时特别是高百分位数如P99的延迟。吞吐量QPS系统每秒能处理的查询数。A/B测试任何索引参数调整、Embedding模型更换、引入重排序等操作都应通过A/B测试来评估其对线上业务指标如用户满意度、问题解决率的影响。6. 常见问题与排查技巧实录在实际开发和运维中我踩过不少坑这里总结几个最常见的问题和解决思路。6.1 检索效果差召回不相关这是最头疼的问题。请按以下顺序排查Embedding模型问题可能性最大症状即使用IndexFlatL2暴力搜索结果也不相关。排查手动计算几个你知道应该相似的句子对的余弦相似度看分数是否合理。尝试更换一个更强大的Embedding模型如从text-embedding-3-small升级到text-embedding-3-large或使用针对中文优化的bge-large-zh-v1.5。技巧对于专业领域如医学、法律考虑使用在该领域数据上继续训练微调过的Embedding模型或使用像M3E这类针对中文检索优化的模型。文本预处理问题症状检索结果总是包含一些无关的“高频词”文档。排查检查你的文本切片Chunking策略。过小的切片丢失上下文过大的切片包含过多噪声。可以尝试不同的切片大小和重叠窗口。对于中文确保分词准确。技巧尝试基于语义的智能切片如使用langchain的SemanticChunker而不是简单的固定长度滑动窗口。索引参数问题症状使用IndexIVFFlat或IndexIVFPQ时召回率显著低于IndexFlatL2。排查逐步增大nprobe参数比如从10调到50甚至100。如果召回率提升明显说明最初nprobe设得太小。同时检查nlist是否合理通常不小于sqrt(N)。技巧在测试集上绘制nprobe与召回率/查询时间的曲线找到业务可接受的平衡点。6.2 搜索速度慢硬件与配置问题CPU模式确保FAISS或向量数据库使用了多线程。FAISS的许多索引操作默认是单线程的可以通过faiss.omp_set_num_threads()设置线程数。GPU模式如果数据量巨大且延迟要求严苛考虑使用faiss-gpu。但要注意GPU内存限制以及数据在CPU和GPU间传输的开销。向量数据库配置检查向量数据库的资源配置CPU、内存、索引类型是否匹配场景。对于QPS很高的场景可能需要增加副本数。索引类型选择不当场景数据量百万级却用了IndexFlatL2。解决切换到IndexIVFFlat或IndexHNSW。对于十亿级数据必须使用IndexIVFPQ或分布式向量数据库。查询负载问题症状并发查询时延迟飙升。排查监控系统资源。可能是达到了CPU或IO瓶颈。对于向量数据库查看慢查询日志优化查询语句如减少返回字段优化过滤条件。6.3 内存或磁盘占用过高向量维度爆炸问题使用1024维甚至更高维的Embedding模型导致存储成本激增。解决评估是否可以降维而不显著损失精度。一些Embedding模型提供不同尺寸的版本如text-embedding-3-large是3072维而-small是1536维。也可以使用PCA等降维技术但需谨慎评估效果。索引本身的内存开销IndexHNSWM参数对内存影响巨大。M16和M64的内存占用可能差4倍。在满足召回率的前提下尽量使用较小的M。IndexIVFPQm和bits参数影响量化后的存储大小。m越大、bits越大精度越高占用也越大。解决使用index.ntotal * index.d * 4Flat等公式估算内存或直接使用faiss.get_mem_usage()监控。考虑将索引切换到磁盘如OnDiskInvertedLists但会牺牲速度。6.4 与现有系统集成困难已有数据在PostgreSQL/MySQL中方案一推荐使用PGVector。几乎无集成成本利用现有备份、监控和SQL能力。性能满足百万级数据需求。方案二使用双写。应用层同时向业务数据库和向量数据库写数据通过消息队列保证最终一致性。复杂度高但可以选择性能更强的专用向量库。需要复杂的业务过滤问题简单的向量数据库过滤语法无法满足业务逻辑。解决优先选择过滤功能强大的数据库如Qdrant, Milvus。如果不行可以采用后过滤先进行向量粗筛top-K放大如取200个然后在应用层用业务逻辑对这200个结果进行精细过滤和排序。但这会损失一些效率。最后我的个人体会是构建RAG的检索系统是一个典型的“没有最好只有最合适”的工程问题。初期可以选用Chroma或PGVector快速验证想法一旦在效果和规模上遇到瓶颈就要深入理解向量索引的原理并基于清晰的业务指标数据量、QPS、延迟、召回率、过滤复杂度来做出理性的选型与调优决策。记住检索是RAG的根基这个根基打不牢上面再华丽的大模型应用也只是空中楼阁。
返回列表