1. 先搞清楚:Embedding和向量数据库到底解决什么问题
这两年大模型火得一塌糊涂,但真正落地的项目里,跑得通、用得稳的,十有八九都离不开Embedding和向量数据库这两个东西。我见过不少人一上来就问“怎么把我的文档喂给大模型”“怎么让大模型回答我私有的知识”,结果折腾半天发现大模型压根记不住那么多内容,或者答非所问。说白了,问题就出在他不知道大模型在大多数场景下是“临时调用知识”的,而不是“把知识背下来”。
Embedding的核心作用,是把文字、图片、音视频这类非结构化数据,变成一串固定维度的数字向量。这串数字不是随便给的,它能编码语义信息——意思相近的句子,转换出来的向量在空间里也靠得近。比如“今天天气不错”和“气象条件挺好”,字面上完全不同,但向量距离很近;而“今天天气不错”和“今天股价大涨”,哪怕都有“今天”,语义离得十万八千里。
向量数据库则是专门用来存这些向量,并支持高效检索的存储系统。它的核心能力是“相似度检索”:你把一条查询也转成向量,数据库能在几亿条向量里快速找出最接近的那一批。这个能力和大模型一结合,就构成了当下最主流的应用架构之一——RAG(检索增强生成)。
这套组合到底能干什么?举个典型场景:一个企业内部知识库有几千份PDF和Word文档,员工想用大模型直接提问“报销流程是什么”。如果只靠大模型本身,它没见过你们公司的报销流程,答不出来或者只能瞎编。把文档全部走一遍Embedding存入向量数据库,每次提问先把问题转成向量、从库里捞出最相关的几个片段,再把这些片段作为上下文交给大模型,它就能引用真实资料回答了。
这套方案适合谁去研究?我认为三类人最需要。第一类是已经能调用大模型API、但做的还是玩具级Demo的开发者,他们缺的正是怎么把私有数据接进来的工程能力;第二类是后端工程师,迟早会碰上语义检索、推荐系统这类需求;第三类是AI产品经理和技术管理者,理解了这套架构,才能判断哪些需求能做、成本多少、瓶颈在哪。
这篇文章我会按自己的实践路径来写:先是Embedding的原理和选型,再是向量数据库的选型与核心参数,然后是完整的落地流程,最后是真实项目里容易踩的坑。全程用大白话,不堆公式,但关键概念我会尽量讲透。
2. Embedding的底层逻辑:语义是怎么被“翻译”成向量的
2.1 从词到向量的演进:为什么不能再用“词频打架”那套
要理解Embedding,最好先回头看它前面的那几代做法。早期的文本表示叫One-Hot(独热编码),做法极其朴素:先把语料里出现的所有词收集成一个词表,每个词分配一个编号,然后每个词对应一个超长向量,只有自己编号那个位置是1,其余全是0。听起来没什么问题,但它有两个致命缺陷。
第一个缺陷是向量维度等于词表大小。词表如果有10万个词,每条文本向量就是10万维,存储和计算开销都很离谱。第二个缺陷是完全没有语义关系。“苹果”和“香蕉”这两个词在One-Hot向量空间里是正交的,距离和“苹果”与“冰箱”没有任何区别;更别说“番茄”和“西红柿”明明是同一个东西,机器根本看不出来。
后来有了TF-IDF、BM25这类统计模型,靠词频和逆文档频率给词加权,能解决一部分关键词匹配的问题,但本质还是“字面匹配”。用户搜“车险理赔流程”,文档里写的是“机动车保险索赔指南”,用BM25这类方法就匹配不上。这种“词汇鸿沟”问题一直到Embedding出现才算从根本上有了转机。
2.2 嵌入向量到底是怎么练出来的
Embedding的训练思路可以这样理解:让机器读海量文本,每碰到一个词或一句话,就去预测它旁边的上下文。比如在一句话里遮住一个词让模型猜,如果模型能猜对,说明它已经学会了这个词出现的语境,而这个语境信息就会被压缩到词的向量表示里。
这里最经典的原理是分布假说——一个词的含义由它经常出现的上下文决定。把这句话反过来理解就是:如果两个词经常出现在类似的上下文里,它们的含义大概率接近。模型在预测上下文的过程中,会把语义相近的词在向量空间里聚到差不多的位置上。训练完成后,每个词都有一组数字表示,比如300维或768维,这就是词的Embedding。
到了句子层面,做法类似但不是简单把词向量拼起来。如今主流方案是用基于Transformer架构的编码器模型,把整个句子一次性编码成一个向量,这个过程会同时考虑词的顺序、词与词之间的注意力关系。所以得到的句子向量不仅包含词汇信息,还能捕捉句子的整体语义、指代关系甚至部分逻辑关系。
这里有一个很重要的概念叫“维度的诅咒”。维度太高,算力和存储都会吃紧,而且高维空间里向量之间的差异会变得越来越不显著;维度太低,又装不下足够多的语义信息。768维是目前比较常见的选择,既够用又不会太浪费资源。当然也有更大的,比如1024维或1536维,具体选哪个要看场景、数据量和硬件条件。
2.3 稀疏向量和稠密向量的区别
实际工程里你会听到两个词:稀疏向量和稠密向量。长文本里拉出关键词,命中词的位置置1,或者用BM25算出加权分数,得到的是一种高维但大多数位置为0的稀疏向量。而神经网络生成的Embedding,每一维都有非零值,属于稠密向量。
稀疏向量的优点是可解释性强、不依赖训练语料;缺点是只能做词面匹配,对同义词和语义改写基本无能为力。稠密向量正好相反,能处理同义改写和语义相关,但训练语料本身的质量会直接影响效果,训练语料有偏差那结果就容易跑偏。
现在很多生产级方案会把两者混着用,叫“混合检索”。先用稠密向量做语义召回,把候选范围缩到比较小的数量级,再用BM25基于关键词补一批召回结果,最后两步结果合并去重后一起送给重排阶段。我在实践中试过,对于FAQ和文档问答这类场景,混合检索的准确率通常比纯稠密向量高出不少,代价是链路多了一层,复杂度也上来了,后面到实操章节我再细说。
Embedding模型选型时,我的个人经验是四个字:场景优先。如果做的是中文语义搜索,优先看中文语料上表现好的模型;如果做的是跨语言检索(比如中文文档英文提问),就要选专门的跨语言模型;如果本身是英文为主的技术文档场景,很多国际开源模型直接就能用。不需要一味追大模型,Embedding模型“小而精”往往是更好的选择。
3. 向量数据库选型:不是所有“支持向量的库”都叫向量数据库
3.1 数据库解决的核心问题是“找得快”,不是“存得下”
很多人以为向量数据库就是把向量存起来,普通数据库加个字段也不是不能存。这话对了一半。存确实能存,但真正拉开差距的是“检索”。一个上百万、上千万规模的数据集,如果用暴力线性扫描的方式逐一计算每条向量和查询向量的距离,单次查询的耗时是毫秒级到秒级的天壤之别。
向量数据库的核心竞争力在于索引结构和检索算法。目前最主流的是HNSW(分层可导航小世界图),本质是构建多层图结构,从高层粗粒度图快速定位到目标区域,再下沉到底层细粒度图做精确查找。理解起来有点像先看整座城市地图,定位到哪个城区,再进到街道级别找具体商铺,避免每查一次都要把全城每条街走一遍。
另一种常见索引是IVF(倒排文件索引),思路是先对所有向量做聚类,比如聚成一万个簇,查找时只和距离最近的几个簇的向量去比对,相当于先分桶再查找。IVF的优点是可以控制召回精度和速度的权衡,缺点是聚类需要先训练,数据量变化后可能需要重新训练。HNSW不用训练,插入即建图,但内存占用通常比IVF高。
有个容易被忽略的点:精度和速度的取舍。任何索引都有召回率的概念,召回率意味着“理论上前100个最相似的结果,索引返回了多少个”。90%召回率和99%召回率,在用户体感上差距并不大,但索引参数直接影响的是内存占用和查询延迟。生产环境里我一般先追求99%以上召回率再逐步调参降内存,而不是一上来就为了省资源牺牲准确度。
3.2 主流向量数据库横向对比
现在市面上的选择很多,但各自的适用场景不太一样。我按自己的经验把它们分成几类。
第一类是功能完整的独立向量数据库,代表是Milvus和Qdrant。这类产品就是专门为向量检索设计的,支持分布式部署、数据持久化、灵活的过滤条件,数据量上到千万甚至亿级都比较从容。Milvus胜在生态成熟,但部署组件较多、集群运维有一定门槛;Qdrant用Rust开发,单机性能很好,部署也相对轻量,社区活跃度这几年涨得很快。
第二类是作为插件或扩展的向量能力,典型代表是pgvector(PostgreSQL的扩展)和Elasticsearch的向量检索能力。它们的好处是团队不需要额外维护一套新数据库,直接在原有的数据库上加上向量字段。数据量在百万量级以内、团队运维能力一般时,这套方案足够用。
第三类是托管式的纯云服务,比如Pinecone这类。不用自己运维集群,API直接调用,起步最快。缺点是用到一定规模后成本曲线比较陡,而且因为数据都在人家的平台上,很多公司会有数据合规方面的顾虑。
做选择时别只看功能和性能,还要算团队的成本。核心判断标准就一句话:你的数据量预计会到哪个量级?如果只是几个Demo或中小型项目,上K8s维护一个分布式数据库集群,运维成本就比数据库本身成本还高;如果预期数据量过亿、查询量很高,从一开始选一个真正分布式架构的库,省得后面迁移换库。
3.3 核心参数常人容易忽视:维度、距离度量、索引配置
无论选哪个数据库,有几个参数是绕不开的。
第一个是向量维度。这个由Embedding模型决定,模型输出多少个维度,数据库里的字段就是多少维。这里有个常见的坑:如果中途切换了Embedding模型,新模型的输出维度跟旧的不一致,老数据要么重新生成向量,要么就得用别的方案做映射,两种方案都不省事。所以项目初期定好Embedding模型非常重要,一旦数据大规模写入,更换模型的成本远不是重新跑一遍那么简单。
第二个是距离度量,通常有内积(点积)、余弦相似度、欧氏距离三种选择。选择跟Embedding模型的训练方式有关。如果你的Embedding模型产出的向量都做了归一化(模长为1),那么三个度量方式在排序结果上几乎等价;如果没归一化,一般来说语义相似度场景用余弦相似度最稳妥,推荐系统里有些场景更适合内积。拿不准的时候无脑选余弦相似度不太会出大错。
第三个是索引配置。以HNSW为例,你需要设置的是每层图允许的最大连接数M和搜索时的探查候选数efSearch。M越大,图越稠密,召回率越高但内存也越大;efSearch越大,查询时看的候选越多,召回越好但延迟上升。这是一个需要根据你的数据规模和硬件情况去调平衡的参数,没有一劳永逸的标准答案。
4. 从零到一完成一套RAG落地:实操流程与踩坑实录
4.1 环境准备与总体链路设计
我先交代一下我这里用的技术栈,省得后面看步骤时有疑惑。Embedding模型用的是国产开源的中文语义向量模型,输出维度是768维;向量数据库用的是单机部署的Qdrant,原因是部署最简单,一个Docker容器就能跑起来,后续如果数据规模上来了再平滑换到集群方案。
整体的流程可以串成一条链:先从原始文档开始,做清洗和切分;每条文档片段送进Embedding模型生成向量;把向量和对应的原始文本及元数据一起写入向量数据库;查询时把问题转成向量,去数据库里做相似度检索;返回的TopK个结果拼成上下文,连同原始问题一起发给大模型,生成最终答案。
这套链路看着不复杂,但每一环都有细节。切分切不对,后面检索精度一定上不来;向量生成环节并发控制不好,几千份文档能跑到天荒地老;上下文拼多了,大模型答非所问甚至幻觉严重。下面我把每个环节都拆开讲。
4.2 文档切分:这一步的优先级比选数据库还高
文本切分是整个RAG项目里我踩过最多坑的地方。刚开始做的时候我以为就是简单按字符长度切开,比如每500个字符一段,结果检索出来的片段经常答非所问。原因是硬切会把一句话从中间劈开,语义断裂,检索再准也没用。
切分这件事没有银弹,核心原则是“优先保持语义完整性,再控制片段长度”。最基础的做法是按段落、标题、列表项等结构切分。对PDF和Word这类带结构信息的文档,先提取结构再做切割,每个一级标题下的内容作为一个候选块,块太长再按二级标题切。
切分粒度和Embedding模型的输入长度限制也有关。绝大多数Embedding模型有最大输入长度,比如512个token(大约几百字),超过上限的文本会被截断,截断部分的信息就浪费了。所以切分的最大长度不要超过模型输入上限的80%,留出一些余量。
这里分享一个我用得比较顺手的方案。以Markdown格式的知识文档为例,先按一级标题分成大块,每块内部再按句子边界和段落边界切成不超过400个字符的片段,相邻片段之间保留约50个字符的重叠。重叠的目的是防止切分把某个完整的知识点切到前一个片段的尾巴或后面一个片段的脑袋里,保留重叠能在检索时让两边的语义都有机会被捞到。
代码层面差不多是这种结构:
def split_documents(text, max_len=400, overlap=50): paragraphs = [p.strip() for p in text.split("\n\n") if p.strip()] chunks = [] for para in paragraphs: if len(para) <= max_len: chunks.append(para) else: start = 0 while start < len(para): end = start + max_len chunk = para[start:end] chunks.append(chunk) if end >= len(para): break start = end - overlap return chunks这只是最朴素的版本,生产环境一定会做得更精细,比如用正则识别标题层级、处理表格转成Markdown格式、列表项保持完整性等。但思路是一致的:宁可切小块多几个片段,也不要为了“少存几条”把语义切碎了。
4.3 向量生成:并发策略和数据处理细节
文档切完之后就进入向量生成环节。这个环节最容易出的问题是性能。一个几万片的文档集,如果单线程逐条去调用Embedding接口生成向量,按单条约50毫秒计算,几万片就是几十分钟起步,数据量再大些直接跑到小时级。
我的做法是写一个带有并发控制的脚本:用线程池控制并发数,比如同时10到20个请求并发跑;每条向量的文本、生成的向量、原始文本一起写入本地缓冲,每攒够一批就批量写入向量数据库。批量写入比逐条写入效率高一个数量级,绝大多数向量数据库都支持批量写入接口,写入时注意控制batch size,一般几百条一批比较合适。
再一个细节是数据清洗。现实中很多文档的文本质量并不理想,比如PDF提取后夹杂着页码、页眉页脚、乱码符号,这些噪声如果不处理,Embedding模型会把这些符号也编码进向量里,相当于污染了语义信息。文本清洗至少要处理:去除多余空白和不可见字符、去掉页码和页眉页脚、统一换行格式、转换编码。这一步看起来不起眼,但对检索质量的影响非常大。
向量化之后直接写入数据库前,记得给每条记录带上元数据,也就是信息来源的标识。最基础的是文档ID、片段序号、原始文本、文档标题、来源路径,方便检索到结果后回溯到原文是哪一份文档的哪一段。如果文档有分类、权限、日期这些属性,也一并带上,后面过滤查询会非常有用。
4.4 检索与重排:TopK只是起点,命中率才是关键
看到检索结果的那一刻,很多新手会困惑:明明问题问的是“报销标准是什么”,Top3返回的片段里有一半是差旅报销细则,另一半是采购流程。这个结果不算错,但不够聚焦。原因在于向量检索是“语义相似”,并不是每一层语义相似都能精确匹配到用户真正想问的那个点。
提高命中率最直接的手段是提高TopK个数,从Top3改成Top10或Top20,把候选范围放大。然后加一个重排环节,把粗召回的结果用一个轻量级重排序模型或基于规则的打分逻辑重新排序,只把最相关的三五条拼进上下文。
重排环节的做法可以分为几层。轻量方案是计算每一条候选结果和原始问题的关键词重叠度,作为加权分数调整排序;进阶方案是直接用一个交叉编码器模型来给问题-片段对打分,这种模型能把问题和片段拼在一起做深度语义匹配,比双塔式的向量检索精度高,但速度慢不少,所以只能用于重排小规模候选。
上下文拼装这里也有讲究。不是所有TopK片段都要塞给大模型,塞太多会稀释注意力,让大模型抓不住重点。我通常只选重排后的Top3到Top5,每条片段控制在模型上下文窗口的合理比例内。如果用户问的问题比较简单,Top2就够了;如果问题涉及多处知识融合,再适当增加。
还有一点值得提:检索时机。有些场景适合实时检索,也就是每次提问都查一次向量库;有些场景可以提前预检索,比如先对一系列高频问题做好检索缓存,命中缓存时直接跳过向量检索。生产系统里加一层缓存能显著降低数据库压力和大模型调用成本。
4.5 大模型接入:提示词模板里的门道
向量检索做完,剩下就是把检索结果和问题交给大模型。这个环节看起来没什么可说的,无非是把上下文塞进提示词,但我在项目里踩过不少提示词相关的坑。
第一是格式问题。大模型对上下文的格式是很敏感的,我倾向于用一个结构化的提示词模板,明确区分“参考材料”和“用户问题”。比如用明确的标记把参考材料括起来,让模型知道哪些内容是它回答的依据,哪些是要回答的问题。即使用引号写清楚,也经常会有模型无视内容把参考材料复述一遍的问题。
第二是“不知道就直说”。大模型尤其爱在材料不充分的时候自圆其说。提示词里要强约束:仅依据提供的参考材料回答,如果材料中没有相关信息,明确回答“资料中未找到相关内容”,不要自行发挥。这条约束比什么参数调优都管用,是控制幻觉最有效的手段。
第三是输出格式的约定。对于知识库问答场景,我一般要求模型先给出结论,再标注信息来源编号,最后补充相关细节。这样既能追踪答案的依据,也方便后续做用户反馈和答案评估。
提示词示意大概是这样:
你是一个企业知识助手。请严格根据以下参考材料回答问题。 参考材料: [材料1] 报销流程的第一步是填写报销单... [材料2] 差旅报销标准为每日500元... 用户问题:报销流程是什么? 要求: 1. 仅依据参考材料回答,不要引入材料之外的信息。 2. 如果材料不足以回答问题,明确回复“资料中未找到相关内容”。 3. 回答时给出结论,并在句末标注材料编号,如(来源1)。4.6 一个完整的最小可用代码示例
前面的理论如果觉得散,这里给一个我自己验证过的最小完整流程,从文本片段入库到检索到生成答案都跑通,方便你对照着搭骨架。
from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams, PointStruct from sentence_transformers import SentenceTransformer # 1. 初始化Embedding模型和向量数据库客户端 model = SentenceTransformer("path/to/your-model") client = QdrantClient(host="localhost", port=6333) # 2. 创建集合,维度要和模型输出对齐 client.recreate_collection( collection_name="knowledge_base", vectors_config=VectorParams(size=768, distance=Distance.COSINE) ) # 3. 文档切片并入库 chunks = split_documents(content) # 使用前面定义的切分函数 points = [] for idx, chunk in enumerate(chunks): vector = model.encode(chunk).tolist() points.append(PointStruct( id=idx, vector=vector, payload={"text": chunk, "source": "document_a"} )) client.upsert(collection_name="knowledge_base", points=points) # 4. 查询 query = "报销流程是什么?" query_vector = model.encode(query).tolist() results = client.search( collection_name="knowledge_base", query_vector=query_vector, limit=5, with_payload=True ) # 5. 组装上下文并调用大模型 context = "\n".join([f"[材料{i+1}] {r.payload['text']}" for i, r in enumerate(results)]) prompt = f"参考材料:\n{context}\n\n用户问题:{query}\n\n请仅依据参考材料回答。" answer = llm_client.chat(prompt) # 替换成你实际用的大模型调用方式 print(answer)这段代码把前面讲的整个链路串起来了,实际项目里要在错误处理、日志记录、并发控制、缓存这些方面做增强,但骨架就这么简单。
5. 实战中的常见问题、排查思路与避坑技巧
5.1 检索结果不相关——先别急着换模型,按顺序体检
“为什么我搜出来的结果驴唇不对马嘴”是我被问得最多的问题。遇到这种情况很多人第一反应是换一个更强的Embedding模型,但模型往往不是最大的瓶颈,甚至排在很后面。我自己的排查顺序是这样的。
第一步检查切分质量。随机抽几条检索结果看看原始片段是否语义完整,如果一个片段里夹杂了三四个主题,或者一句话被拦腰截断,那问题多半出在切分上。切分是RAG精度最基础的环节,这一环出了错,后面任何优化都白搭。
第二步检查Embedding模型是否适合你的语言和场景。中文技术文档和一个英文法律文书场景选模型的方向是不同的。如果你的语料是中文为主,却用一个纯英文训练的模型,90%的概率效果好不了。
第三步检查查询和文档的领域差异。有些项目文档术语太多、缩写密集,Embedding模型没见过这类词,语义表示就会很弱。这种情况可以通过在文档片段里补充同义词、别名来缓解,比如给专利文档补上行业常用称谓,向量检索的表现会立刻好一截。
第四步才是检查索引参数。把召回率调到接近暴力扫描的水平,如果此时精度提升明显,说明之前是索引精度的取舍太激进,适当放宽召回率限制就好。
5.2 向量化性能太差——你以为要加机器,其实问题在批次和缓存
几千个文件向量化跑到天荒地老,很多人第一反应是加大并发。我试过把并发从10调到50,结果服务器CPU被打满、接口开始报限流错误,速度反而没提升多少。问题不在并发上限,而在请求节奏和管理策略。
更好的做法是控制一个合理的并发数,比如10到20,同时给每个批次做好失败重试和退避策略。遇到过某一批向量化中间出错的,自动重试三次,都失败就单独落盘写日志,最后统一处理,这比整个脚本从头跑一遍省太多时间。
另一个容易被忽视的技巧是缓存。如果同一个文件反复在调试中被切分和向量化,每次都重新调用模型接口非常浪费。可以对文本内容做哈希,如果哈希值之前已经向量化过,直接从缓存里读取向量,跳过调用。
5.3 召回率很高但答案还是不对——链路接口处出了问题
这类问题最隐蔽。比如检索结果明明很相关,但大模型答错了;或者检索结果相关,但答案里编造了材料里根本没有的内容。这时候问题多半出在“上下文拼装”和“提示词约束”两个环节,而不是向量检索本身。
上下文拼装的常见问题是顺序和长度。向量数据库返回的TopK结果自带相似度得分,但直接把得分最高的放最前面不一定是好事,有时两条高度重复的片段霸占满上下文,核心信息反而没位置。我习惯在拼装前做一个去重(按文本相似度去重),然后按重排分数排序,最后再截断到适合的长度。
提示词层面的问题更常见。如果材料只有一条是真正相关的,但提示词里塞了五条材料,大模型可能被五条材料里的噪声带偏。这种情况要回到4.5节说的约束方法,明确告诉模型“没有相关内容时直说”,而不是硬凑答案。
5.4 数据量增长后查询变慢——索引参数和分片策略调整
数据量从几十万涨到几百万时,查询延迟常常出现肉眼可见的上升。这里有个经验值可以分享:HNSW图每层最大连接M值从16调到32,召回率通常有明显提升,但内存和插入耗时也会涨;efSearch这个查询时参数则直接影响查询速度和召回率的平衡,查得慢时先降低efSearch试试。
如果单机方案的数据量实在撑不住了,优先考虑分片。Qdrant这类数据库支持多分片配置,把数据分到多台机器上并行检索,之后再做合并。但分片会带来跨节点一致性和并发控制的复杂度,不是百利无一害的操作。我的建议是:数据量预估在单机可承受范围内(视具体库和机型而定,大体百万到千万量级)时,优先把单机性能调优做扎实;超过这个量级再考虑集群,不要一开始就奔着分布式去。
5.5 一套亲测有效的调优工作流
最后分享一套我每次新项目都会走的调优工作流。第一步,先拿20-30条有代表性的真实查询样本,建一个手工标注的评估集,标注每个问题对应的正确答案来自哪几个文档片段。第二步,用默认参数全流程跑通,量出基线准确率。第三步,逐个环节做A/B测试:换切分策略、换Embedding模型、换索引参数、加重排环节,每次只改一个变量,对比准确率变化。第四步,准确定位瓶颈后再投入资源重点优化,而不是凭感觉全面推翻重来。
这套工作流看起来多花了一点前期时间,但它能帮你从“感觉哪里不对”变成“明确知道哪里不对”。我见过太多团队凭直觉乱调参数,最后哪里都没调明白。先有评估集,再有调优,这才是做工程的态度。
6. 写在最后:这套技术栈我的一些真实体会
做了几年大模型应用相关项目,一个比较深的感触是:Embedding和向量数据库本身不难,难的是它们是整个RAG链路里看不见的地基。很多人把注意力放在大模型选型、提示词技巧这些显性的东西上,但项目一上线,真正决定体验上限的往往是最不起眼的切分策略、向量质量、索引参数。
我踩过最大的一个坑是中途换了Embedding模型。当时觉得旧模型效果不够好,换成新模型后从维度到语义分布全都变了,之前几百万条向量全都白算,重新向量化的成本花了一整周。所以每接触一个新项目,我第一件事不是看用哪个大模型,而是先定Embedding模型和向量数据库,这两个一旦定了,轻易不换。
另外一个体会是“效果不佳先怀疑链路,而不是怀疑模型”。搜索不准、答案不对,先把切分、召回、重排、上下文拼装这一条链路的每一个环节都验证一遍,再考虑要不要升级模型。大部分情况下,问题出在工程细节而不是模型的“智商”。
这套组合拳往后还有很大的扩展空间:比如多模态内容的向量化、自动评估体系、增量更新的数据管道、针对长文档的分层检索。每一步单独拿出来都能深耕很久。现阶段先把Embedding和向量数据库的底子打扎实,后续不管怎么演进都不慌。