Contents
1 前言
在上一篇文章中,我梳理了 RAG 的核心概念和流程(参见文章:家庭数据中心系列 从零理解 RAG(一):原理与完整流程解析),但那毕竟还停留在理论层面,纸上谈兵。要让 RAG 真正跑起来,必须迈出实践的第一步,问题是:从哪里开始最合适?
我觉得突破口应该是 “嵌入模型”,因为在 RAG 流程里,检索效果高度依赖于文本是如何被转换成向量的,这一步决定了后续能不能把相关内容召回来。
说到嵌入模型,很多人第一反应可能是直接调用云服务的 API,比如:OpenAI 的 text-embedding-3-small 和 text-embedding-3-large,这两个模型在社区里用得最多(微软 Azure 其实也是托管了同样的系列);Anthropic、Cohere、甚至一些国内厂商(如百度的 bge 系列、智谱的 embedding-2)也都提供类似的嵌入 API。确实,这样做省心,不用配置环境,直接拿来就能用,但问题也很明显:
- 成本受限:随着数据规模增加,API 调用费用会持续累积。
- 隐私顾虑:语料要传到第三方服务器,不一定适合内部或敏感数据。
- 依赖外部:一旦服务调整价格、限流甚至下线,你的系统就会受到影响。
- 难以比较和替换:不同供应商之间切换并不轻松,实验成本高。
正因为这些原因,我更倾向于自建嵌入模型,这样不仅能完全掌握数据和环境,还能灵活切换模型、自由对比效果,更适合学习与探索。
在自建方案里,Ollama(对Ollama不熟悉的朋友可以参看我另一篇文章:家庭数据中心系列 打造私有AI:基于Ollama在本地搭建开源大语言模型的详细教程) 是一个很好的起点:它专门为本地部署设计,把“下载—加载—调用”这一整套流程封装得很简洁,几条命令就能在你的电脑上跑起一个嵌入模型,不用折腾底层推理框架,也不用担心兼容问题,非常适合想要快速上手的读者。
不过,在实操”Ollama自建嵌入模型”之前,需要先了解什么是嵌入模型。
2 从文本到向量:理解嵌入模型
2.1 什么是嵌入模型?
如果要用一句很简单的话来解释:嵌入模型,就是把文本转化成一串数字向量的模型,听上去是不是有点抽象?
来举个例子,假设你有一句话——“苹果是一种水果”,当你把它交给嵌入模型,它会返回一组数字,这组数字可能有几百个,甚至上千个维度。比如:
[0.12, -0.08, 0.95, ... , 0.34]
关于”维度”,可以这样理解:如果只有 2 个数字,就好像在一个二维平面上标记一个点(X、Y 坐标);如果有 3 个数字,就是三维空间里的一个点(X、Y、Z);而当数字变成几百上千时,意味着这句话被放在一个“高维语义空间”里。我们人类很难想象 1000 维的样子,但对计算机来说,这只是多了很多坐标轴,同时,维度越多,表达就越细腻。
就像描述一个人,如果只说“身高、体重”,那是二维特征;如果再加上“年龄、职业、爱好”,维度就更高,描述就更准确。嵌入模型也是同理,用上百上千个维度来刻画文本,让语义细节被更全面地捕捉。
它们本身没有直观意义,但它们决定了“这句话在语义空间里处于哪个位置”。换句话说,嵌入模型就像一个翻译器,把我们能读懂的自然语言,翻译成计算机能操作的“语义坐标”。
有了这种表示方式,计算机就可以通过比较坐标之间的距离,来判断两段文本的相似程度。比如:
- “苹果是一种水果” 和 “香蕉是一种水果”,在嵌入空间里的距离很近;
- “苹果是一种水果” 和 “苹果公司发布了新手机”,虽然都有“苹果”,但位置会相对远一些。
这就是嵌入模型的魔力:它不再依赖单纯的关键词匹配,而是能捕捉“语义层面”的接近程度。
在 RAG 里,嵌入模型起到的作用就是:把你的问题和知识库里的文档,翻译到同一个语义空间里,然后靠“谁离得更近”来完成检索。
当然,为什么我们需要这种方式,而不是直接用传统搜索?这就涉及到关键词搜索的局限性,以及语义向量的优势。
2.2 为什么需要嵌入模型?
2.2.1 关键词的局限
在上一节提到了嵌入模型能把文本转换成数字向量,也知道向量的维度越高,语义表达越细腻。那么问题来了,为什么我们需要这种向量表示,而不直接用传统的关键词搜索?这是因为,关键词搜索有其局限,简单来说,关键词搜索只能看字面,而不理解“意思”,它有几个明显问题:
- 误匹配:比如你搜索“苹果的营养价值”,文档里写了“苹果公司发布新产品”,关键词“苹果”被匹配上,但语义完全不相关。
- 召回不足:你搜索“香蕉的好处”,文档里写的是“香蕉富含钾”,关键词匹配可能不够精准,排名靠后,甚至没被检索到。
2.2.2 语义向量的优势
嵌入模型把文本映射到高维空间,用“语义距离”衡量相似度。换句话说,它不看字面,而是看“意思靠得近不近”,举例来说明:
- 文本 A:“苹果是一种水果”
- 文本 B:“香蕉是一种水果”
- 文本 C:“苹果公司发布了新手机”
在向量空间里,A 和 B 的向量距离很近,因为它们都是描述水果的语义;A 和 C 的距离较远,即使都有“苹果”这个关键词。
这样一来,搜索“苹果的营养价值”时,系统能优先召回真正相关的水果文档,而不是苹果公司的新闻。
注:向量概念是AI世界的通用语言,非常重要,感兴趣的朋友可以参见我另一篇文章:家庭数据中心系列 向量:AI 世界里的通用语言。
2.2.3 嵌入模型在RAG流程中的价值
在 RAG(检索增强生成)的流程中,嵌入模型的作用非常关键:它不仅仅是把文本转换成向量,更决定了模型在检索阶段能“看到”哪些信息,直接影响生成结果的质量:想象一下,如果你问模型“苹果的营养价值是什么?”,知识库里既有“苹果富含维生素C”的文档,也有“苹果公司发布新手机”的新闻。如果嵌入模型准确,那么模型检索到的文档主要是前者,生成的答案自然正确;如果嵌入模型不够好,模型可能会把不相关的新闻也当作参考,生成的回答就容易跑偏。
同时,嵌入模型还提升了 RAG 的可控性:它把语义空间划分清楚,让生成模型只参考相关内容,减少无关信息干扰。对初学者来说,可以把它理解为“帮模型筛掉噪声”,让答案更可靠、更可预测。
另外,嵌入向量还有很强的可解释性:你可以量化问题和文档的相关度,查看 top-5 最相关的文档和它们的相似度分数,这样你就能清楚地知道模型到底参考了哪些内容。对于实验和调优非常有帮助:换一个嵌入模型,最相关的文档可能完全不同,生成的答案自然也会不一样,这让性能差异变得可观察和可分析。
最后,嵌入模型的价值还在于它的扩展性:向量可以存入数据库,用于问答、推荐或者相似度搜索等多种场景。一旦建立了可靠的嵌入体系,后续增加新文档或切换生成模型,检索和生成的闭环都可以平滑延伸。
总的来说,对初学者来说,先把嵌入模型搞定,就等于打好了 RAG 的地基。只要地基稳固,无论后续怎么调整分块策略、检索参数或者生成模型,都能得到更可控、直观的效果。这也正是我之所以把嵌入模型作为 RAG 实践第一步的原因。
3 Ollama 部署嵌入模型实操
3.1 准备 Ollama 环境
这部分操作根据大家各自使用的系统平台不同而不同,比如我就是使用的m4pro Macmini,具体的下载、部署Ollama及使用大语言模型的详细步骤可以参见我之前的文章:家庭数据中心系列 打造私有AI:基于Ollama在本地搭建开源大语言模型的详细教程,这里我不再重复。
不过,现在macos版Ollama在GUI设置里可以直接设置模型的下载位置以及可以直接启用”跨域访问”功能了,方便了很多:

至于其他版本Ollama的跨域设置,可以参考我另一篇文章:在 家庭数据中心系列 Mac mini(M4 Pro)上部署 Llama 3.2:通过 Ollama 实现高效运行与跨域访问优化全攻略。
3.2 根据自己需求选择一个嵌入模型
在 Ollama 支持的模型里(参见:https://ollama.com/library),有几款主流的嵌入模型可供选择,每款模型在精度、规模和资源消耗上都有差别。为了更容易理解,我把它们分为小型、中型和大型三类:
| 规模类型 | 模型名称 | 模型大小 | 适合场景 |
|---|---|---|---|
| 小型 (Small) | nomic-embed-text | 36.4M | 博客文章、个人笔记、小型知识库,快速部署、低资源消耗 |
| 小型 (Small) | snowflake-arctic-embed 22M/33M | 22M / 33M | 小型文本库,快速试验,低硬件要求 |
| 小型 (Small) | granite-embedding 30M | 30M | 单机小规模文本向量化,CPU 即可运行 |
| 中型 (Medium) | snowflake-arctic-embed 110M/137M/335M | 110M / 137M / 335M | 企业文档、跨部门知识库、文档量几十万条,需较高语义精度 |
| 中型 (Medium) | granite-embedding 278M | 278M | 中等规模企业文档,多文档语义检索,需 GPU 或较高 CPU 内存 |
| 大型 (Large) | mxbai-embed-large | 335M | 大规模企业知识库、复杂问答系统、多语言场景,高显存 GPU 推荐 |
| 大型 (Large) | snowflake-arctic-embed2 | 568M | 多语言知识库、大规模向量库、精度要求高,需高性能服务器 |
1、小型模型
通常体量较小,比如 nomic-embed-text(36M)、snowflake-arctic-embed 的 22M 或 33M,以及 granite-embedding 的 30M 版本。它们的优点是轻量、快速,几乎在任何一台现代个人电脑上都能跑起来。对于博客文章、个人笔记或小规模知识库,这类模型已经完全够用。部署成本低,CPU 就能运行,如果有 GPU,速度会更快。内存占用也很低,一般 8–16GB 就足够。对于你想对博客 .md 文件做向量化,这类小型模型是最省力又最实用的选择。
2、中型模型
体量大一些,例如 snowflake-arctic-embed 的 110M、137M 和 335M 版本,或者 granite-embedding 的 278M 版本。它们适合处理企业级文档或者跨部门的知识库,文档量达到几十万条时也能保持较高的语义精度。这类模型对硬件要求略高:最好有一块 4–12GB 显存的 GPU,CPU 可以跑,但速度会慢一些,内存需求大约 16–32GB。中型模型在精度和资源消耗之间有不错的平衡,是正式业务场景的首选。
3、大型模型
面向大规模企业应用,比如 mxbai-embed-large(335M)或者 snowflake-arctic-embed2(568M,支持多语言)。它们能够处理百万级以上的文档库,语义理解能力极强,适合复杂问答系统或多语言检索场景。不过,硬件要求也高得多:16GB 以上显存的高性能 GPU,内存至少 32GB,再加上向量存储空间也需要相应扩大。对于一般个人博客文章而言,使用大型模型不仅浪费资源,而且部署成本高、运行速度慢。
对于大多数个人或小规模文本库,尤其是对博客文章 .md 文件做向量化,小型模型就已经足够了。它轻量、快速、成本低,而且可以在本地完成完整的 RAG 流程体验。只有在处理大规模企业文档或对语义精度有极高要求的场景下,才需要考虑中型或大型模型。
基于以上的分析,我的需求是针对本地的”.md”格式的博客文章做向量化处理,最适合的嵌入模型就是”nomic-embed-text”了。
3.3 在Ollama里拉取适合的嵌入模型
根据上一节的分析,在我的场景下最合适的嵌入模型是”nomic-embed-text”,所以接下来我需要在ollama里将该模型拉取下来:
ollama pull nomic-embed-text
成功的话结果如下:

可以使用以下命令查看模型的信息:
ollama show nomic-embed-text

3.4 测试嵌入模型可用性
使用以下命令以API的方式测试”nomic-embed-text”嵌入模型能否正常将”The sky is blue because of Rayleigh scattering”这句话进行向量化:
curl http://localhost:11434/api/embeddings \
-d '{"model": "nomic-embed-text", "prompt": "The sky is blue because of Rayleigh scattering"}'
成功的话会得到一组向量输出:

注:如果大家使用其他嵌入模型,需要将上面命令中的向量模型按照自己实际的模型名称进行替换。
3.5 小贴士:Ollama 的运行机制
在 Docker 里,大家熟悉的流程通常是:先用 docker pull 拉取某个镜像,然后再用 docker run 启动一个容器。如果没有执行 run 这一步,镜像只是静静地躺在本地磁盘上,完全无法使用。这种逻辑很容易让人形成一种思维定式:只有 run 才是真正让东西“活起来”的关键动作。
而 Ollama 虽然命令名字看起来很相似,却有着完全不同的运行机制。它虽然同样提供了 “ollama pull” 命令来下载模型,也有 “ollama run”命令,表面上似乎和 Docker 的 run 很像,但本质却完全不同:Ollama 的核心其实是一个常驻服务,在安装完成后就会在本机后台运行(默认监听在 http://127.0.0.1:11434)。只要这个服务在运行,本地的模型就随时可以通过 API 被调用,无需通过命令行手动启动,所以,”ollama run” 命令更多是一种体验式的交互方式。
比如,当你在终端里执行 “ollama run llama3” 时,本质上只是帮你向后台服务发送一个请求,然后把结果打印在命令行里,方便人直接试玩,对于生成模型来说,这的确是一种直观的体验方式。而对于嵌入模型,它根本不支持生成文本,因此用 “ollama run nomic-embed-text”命令就会报错:

在实际应用中,无论是生成模型还是嵌入模型,正式环境里都是通过 API 来调用的。生成模型通过 POST /api/generate 输出文本,嵌入模型通过 POST /api/embeddings 返回向量。这些 API 才是 Ollama 的交付接口,也是生产环境中应该依赖的方式。因此,和 Docker 不同,Ollama 的 run 并不是必须的开关,它只是一个让用户在命令行中体验模型的小工具。
按照这种机制,可能很多朋友会担心,如果 Ollama 不需要 run 就能被调用,那是不是意味着所有模型在后台都时时刻刻运行着?其实并不是这样。
Ollama 的模型在本地只是一份存放在磁盘上的文件,只有在 API 调用时才会被真正加载进内存或 GPU。当调用结束后,模型会暂时保留在内存中作为缓存,以便下次快速响应;但如果资源紧张或服务重启,这些模型也会被卸载,释放系统资源。换句话说,模型并不会无休止地占用算力,而是按需启动、按需释放。这种机制既保证了调用的即时性,也避免了资源浪费。
4 Chatbox创建知识库
4.1 在Chatbox 中添加 Ollama 的嵌入模型
在完成本地 Ollama 嵌入模型的部署并测试可用性之后,下一步就是把它接入到 Chatbox(对Chatbox不熟悉的朋友可以参考我另外一篇文章:家庭数据中心系列 最便捷的 AI App 前端:Chatbox 全面介绍 + 使用指南),作为一个可调用的模型提供方。
首先,打开 Chatbox 的模型管理界面,找到“添加模型提供方”或类似的入口,在这里选择 Ollama 作为提供方,并填写本地服务的地址,通常是 http://localhost:11434,接下来,点击”获取”按钮刷新,并在可用模型列表中选择你之前下载的嵌入模型,例如 “nomic-embed-text”添加,如下图:

然后,选择设置模型类型为嵌入,点击”nomic-embed-text”右边的设置按钮:

“模型类型”的下拉菜单选择”嵌入”并保存:

成功:

4.2 创建知识库并导入markdown文档


知识库创建完成:



成功导入所有.md格式文章后:

注:上图中红框内的分块,就是把一篇长文章切成一小段一小段的内容,就像把一本书拆成一页一页。这样做的原因是,嵌入模型一次只能处理有限长度的文本,如果整篇文章不切开,模型既装不下,也容易遗漏信息。分块后,每一小段都会被转成向量存进知识库,后面用户提问时,系统就能快速找到和问题最相关的那几段,而不是整篇文章乱翻。
5 在Chatbox中使用知识库



然后进行合理的问题构造,提示AI要参考知识库中的内容进行判断,比如:”参考知识库里我的博客文章,对比我在不同技术领域的掌握程度,指出强项和待提高的部分。”,结果如下:

效果非常好,之前我最头痛的需求就是让ChatGPT分析我某些文章的优缺点,一篇文章还好,如果要分析一个系列文章的时候很折腾,现在有了知识库并导入所有博客文章之后,这个需求就变得非常简单了。
另外,我这次提问总共token才花费4345个:

相比一次性把所有文章直接输入模型可能需要的几十万 token,这种基于嵌入向量的检索增强方式节省了上百倍的 token,不仅大幅降低成本,也保证了模型回答只关注最相关的内容,使评估既高效又精准。
为什么有这个效果呢?这是因为在 RAG 的流程里,模型并不会把整个知识库都塞进上下文,而是先通过嵌入向量检索,快速锁定与你问题最相关的若干文档片段,再把这些片段拼接到提示词里交给模型处理。这样一来,无关的内容完全不会占用 token,模型“看到”的信息更干净,推理空间更集中,结果自然既省钱又靠谱。
在之前的文章中,我提到 RAG的流程可以分为五个核心步骤:切分文本 → 向量化 → 向量存储 → 检索 → 生成答案。对于 Chatbox 提供的知识库功能来说,它实际上帮我们解决了其中的文本切分、向量存储和检索这三步,让文档可以被整齐地存放并快速找到最相关的内容。而第二步“向量化”则仍然需要我们自己提供一个嵌入模型来完成。
注:LobeChat 的知识库和 Chatbox 的知识库在核心功能上类似,都是把文档切分、向量化存储,并在用户提问时检索最相关内容。它们在嵌入模型处理方式上略有差异:LobeChat 默认内置了嵌入模型,上传文档后会自动生成向量,无需用户干预;而 Chatbox 可以使用用户提供的嵌入模型(例如 Ollama 的本地模型),理论上可以掌控向量化的模型版本和质量,但在实际操作中,除了选择模型和上传文档,块大小、重叠参数和向量存储方式等可调选项仍然有限。换句话说,LobeChat 偏向开箱即用,而 Chatbox 提供了模型选择的可能,但在定制化和可控性上并不比自建 LobeChat 服务端强很多。
需要注意的是,LobeChat 的知识库在部署上有一定门槛:要完整使用功能必须启用服务端数据库,并额外配置一个 S3 兼容的对象存储(如 MinIO、COS、OSS),同时还需要处理域名和跨域访问设置,这使得 LobeChat 的知识库在个人或轻量场景下显得较为繁琐。而 Chatbox 则更轻量化,尤其适合只需偶尔更新的个人知识库,唯一麻烦一般的是需要自行提供嵌入模型。
6 后话
经过这一系列操作,我终于把一个完整的最小可用 RAG 系统跑通了:从 Ollama 部署嵌入模型,到 Chatbox 创建知识库,再到上传博客文章并进行检索增强式对话。整个流程看似分步骤,其实贯穿的核心逻辑非常清晰:先让嵌入模型稳定、可控,再把知识库搭起来,最后通过大语言模型进行生成。这是理论到实践的第一道桥,也是 RAG 流程中最“看得见”的突破点。
在实践中,我也发现了一些有趣的经验,比如,很多读者和初学者可能以为必须先搭建向量数据库才能用知识库,其实像 Chatbox 这样自带 SQLite 的方案,对于个人博客级别的数据量已经完全够用。只要嵌入模型工作正常,上传文档后系统就能立刻生成向量,并且能够被检索到。这一点对于快速验证和迭代非常友好,也让整个 RAG 实践门槛大大降低。
另一个体会是,很多人(包括我)受“docker run”思维影响,容易误以为嵌入模型必须在命令行中启动才能被调用。实际上,Ollama 的嵌入模型本质上是一个服务,通过 API 就可以跨域被前端调用,”ollama run” 只是提供了一个本地交互体验,并非必需。这种设计让实际部署更加灵活,也便于在多终端、多前端场景下使用相同的嵌入模型。
最后,我想强调一点:这篇文章虽然跑通了最小可用的流程,但 RAG 的潜力远不止如此。后续你可以尝试优化分块策略、引入更大规模的向量数据库、调整检索参数、甚至结合多模型增强检索能力。今天展示的,是让理论落地的第一步,是一个“最小可用闭环”,而接下来的迭代和优化,则完全掌握在你手中。
注:不知从 Chatbox 的哪个版本开始,其内置的文档解析器在处理较长的中文文章时,可能会生成不符合嵌入模型输入预期的文本结构,进而导致在新增知识库文章时,调用本地部署的 Ollama 嵌入模型(如 nomic-embed-text)频繁触发 the input length exceeds the context length 错误。
需要特别说明的是,在直接通过 Ollama API 调用同一嵌入模型、并传入等价甚至更长的文本内容时,嵌入请求是可以正常完成的。这表明问题并非出在嵌入模型本身,也不完全由运行环境(macOS / Linux)或硬件算力所导致,而更可能源于 Chatbox 内置解析器在文档解析、文本拼接或分块阶段引入的中间结果异常。
由于该解析器的具体解析逻辑、分段策略以及中间文本均不可观测、不可配置,在使用内置解析器的前提下,这一问题目前难以通过更换嵌入模型或运行平台来规避。因此,至少在当前条件下,Chatbox 知识库功能与本地 Ollama 嵌入模型在处理长中文文档时,存在明确的兼容性边界。
说人话就是:在 Chatbox 官方修复或重构内置文档解析器之前,其知识库功能并不适合直接配合本地 Ollama 的嵌入模型来处理较长的中文文章。在这一组合下,问题并非“能不能再调一调”就可以解决,而是受限于解析链路本身的实现方式。