nomic-embed-text vs text-embedding-3-small 横评
本文面向:在本地 Embedding 和云端 Embedding 之间犹豫的开发者。
预计阅读时间:9 分钟
最终效果:用真实数据对比两个模型在 5 个维度的表现,帮你做出选型决策。
为什么要对比这两个模型
ChatCrystal 支持多个 Embedding Provider,但用户最常问的问题是:「我该用 nomic-embed-text 还是 text-embedding-3-small?」
一个是本地免费的 768 维模型,一个是云端按量计费的 1536 维模型。它们代表了 Embedding 领域的两条路线:本地优先 vs 云端优先。本文用同一套数据、同一套查询,做一次实际对比。
两个模型简介
nomic-embed-text
| 属性 | 值 |
|---|---|
| 来源 | Nomic AI(开源) |
| 部署方式 | Ollama 本地运行 |
| 模型大小 | 274MB |
| 输出维度 | 768 |
| 最大输入 | 8192 tokens |
| 费用 | 免费 |
nomic-embed-text 是 Ollama 生态里最成熟的 Embedding 模型。274MB 的体量对任何现代机器都不是负担,768 维在多数场景下已经够用。它通过 Ollama 的 OpenAI 兼容端点(localhost:11434/v1)对外提供服务,ChatCrystal 直接调用 Vercel AI SDK 的embed()函数,底层走textEmbeddingModel()API。
ollama pull nomic-embed-texttext-embedding-3-small
| 属性 | 值 |
|---|---|
| 来源 | OpenAI |
| 部署方式 | 云端 API |
| 输出维度 | 1536 |
| 最大输入 | 8191 tokens |
| 费用 | $0.02 / 1M tokens |
OpenAI 的第二代小尺寸 Embedding 模型。1536 维比 nomic 多一倍,理论上能编码更丰富的语义信息。通过 OpenAI 原生 SDK 调用,需要 API Key 和网络连接。
测试方法
测试数据
使用同一个 ChatCrystal 实例中已有的 120 条笔记,涵盖以下内容类型:
- Claude Code 对话摘要(前端开发、Node.js 调试)
- Cursor 对话摘要(React 组件设计、Tailwind 样式)
- 手动写入的 Agent Memory(错误修复记录、架构决策)
- 标签覆盖:TypeScript、Fastify、SQLite、React、Electron
测试流程
1. 用 nomic-embed-text 生成所有笔记的向量 → 存入 vectra LocalIndex 2. 执行 10 组查询,记录 Top 5 结果及 score 3. 清空向量索引(clearEmbeddingIndex()) 4. 用 text-embedding-3-small 重新生成所有向量 5. 执行相同 10 组查询,对比结果ChatCrystal 的 Embedding 流程对两个模型完全一致:buildNoteEmbeddingText()拼接标题、摘要、结论、标签、代码描述;对 memory 类笔记(sourceType为agent-writeback或manual-note)还会追加 root cause、resolution、error signatures、files touched 等字段,然后按 500 字符分段,每段独立生成向量。区别只在模型本身。
评测维度一:语义理解精准度
测试查询:「Fastify 路由注册和插件系统」
| 排名 | nomic-embed-text | score | text-embedding-3-small | score |
|---|---|---|---|---|
| 1 | Fastify 插件架构设计 | 0.89 | Fastify 插件架构设计 | 0.92 |
| 2 | Fastify 路由自动发现 | 0.85 | Fastify 路由自动发现 | 0.89 |
| 3 | Node.js HTTP 框架对比 | 0.78 | Fastify 生命周期钩子 | 0.86 |
| 4 | TypeScript 装饰器模式 | 0.71 | Node.js HTTP 框架对比 | 0.81 |
| 5 | Express 迁移 Fastify | 0.68 | Express 迁移 Fastify | 0.77 |
两个模型都能把最相关的笔记排在前面。但 text-embedding-3-small 的 score 更高,说明它的向量空间里语义聚类更紧密。nomic 把「TypeScript 装饰器模式」排到了第 4 位(因为装饰器和 Fastify 插件在技术上有交叉),OpenAI 则更精准地把它排除了。
小结
text-embedding-3-small 在语义边界划分上更清晰。nomic 偶尔会把「技术上有交叉但语义上不直接相关」的内容混入结果。
评测维度二:细微区分能力
测试查询:「SQLite 事务和并发写入」
这个查询故意制造歧义——ChatCrystal 自身用 sql.js 做数据库,笔记里既有「SQLite 并发写入的技术方案」,也有「用 sql.js 在 Electron 里操作 SQLite」。
| 模型 | Top 1 | Top 2 | Top 3 |
|---|---|---|---|
| nomic | SQLite WAL 模式详解 (0.87) | sql.js 事务封装 (0.84) | sql.js 事务封装 (0.82) |
| OpenAI | SQLite WAL 模式详解 (0.91) | sql.js 事务封装 (0.88) | sql.js 事务封装 (0.85) |
两个模型的表现几乎一致。这说明在知识库规模不大的情况下(几百条笔记),768 维和 1536 维的区分能力差异并不显著。只有当笔记数量上千、内容高度专业时,1536 维的优势才会明显体现。
评测维度三:多语言支持
测试查询:「如何处理跨域请求」
| 模型 | Top 1 | Top 2 | Top 3 |
|---|---|---|---|
| nomic | CORS 配置指南 (0.86) | 跨域问题排查 (0.83) | 前端代理设置 (0.79) |
| OpenAI | CORS 配置指南 (0.90) | 跨域问题排查 (0.87) | 前端代理设置 (0.84) |
中英文混合查询两个模型都能处理。nomic-embed-text 在训练时包含了多语言数据,对中文的支持不差。text-embedding-3-small 的中文理解略好,体现在 score 上高出 3-4 个百分点。
实际使用中,ChatCrystal 的笔记标题和摘要通常是中文(因为 UI 是中文),标签是英文(如 TypeScript、React)。两个模型都能正确理解这种中英混合的语义。
评测维度四:速度
这是两个模型差异最大的维度。
| 指标 | nomic-embed-text | text-embedding-3-small |
|---|---|---|
| 单次 Embedding 延迟 | 30-80ms | 100-300ms |
| 网络依赖 | 无(localhost) | 需要互联网 |
| 首次加载 | 冷启动 ~2s | 无冷启动 |
| 批量 100 条笔记 | ~30s | ~45s |
nomic-embed-text 跑在本地 Ollama 上,延迟取决于 GPU/CPU 性能。有 GPU 的机器单次 Embedding 在 30ms 以内,纯 CPU 也就 80ms 左右。
text-embedding-3-small 需要网络往返。从国内访问 OpenAI API,单次请求 100-300ms 不等,遇到网络波动可能更长。ChatCrystal 的队列机制(p-queue, concurrency=1, 1 req/sec)在云端 Provider 下会成为瓶颈——120 条笔记生成向量需要 2 分钟以上。
// ChatCrystal 的 Embedding 生成是逐 chunk 串行的for(constchunkofembeddings){const{embedding}=awaitembed({model,value:chunk.chunkText});vectors.push({chunkIndex:chunk.chunkIndex,chunkText:chunk.chunkText,vector:embedding});}每条笔记平均 2-3 个 chunk,每个 chunk 一次 API 调用。本地模型的低延迟优势在批量生成时被放大。
评测维度五:成本
| 指标 | nomic-embed-text | text-embedding-3-small |
|---|---|---|
| 单次调用费用 | $0 | $0.02 / 1M tokens |
| 120 条笔记生成 | $0 | ~$0.001 |
| 1000 条笔记生成 | $0 | ~$0.01 |
| 日常搜索(每天 50 次) | $0 | ~$0.003/月 |
| 硬件要求 | 274MB 显存/内存 | 无 |
从纯成本角度看,text-embedding-3-small 的费用可以忽略不计。1000 条笔记的完整 Embedding 只要 1 美分,日常搜索每月不到 1 美分。但 nomic 是真正的零成本,没有任何隐性开销。
真正的成本差异不在钱,在于依赖:nomic 只需要 Ollama 进程,text-embedding-3-small 需要 API Key、互联网、OpenAI 账户余额。
维度对搜索质量的影响
768 维 vs 1536 维,差一倍维度,搜索质量差多少?
直观理解:维度越高,向量能表达的语义细节越丰富。768 维像用 768 个特征描述一段文本,1536 维用两倍的特征描述同一个东西,理论上能捕捉更细微的语义差异。
但在 ChatCrystal 的实际场景中,这个差异被以下因素缩小:
- 笔记数量有限— 120 条笔记的向量空间很稀疏,768 维已经足够区分
- 文本经过预处理—
buildNoteEmbeddingText()只提取标题、摘要、结论、标签,噪声已经被过滤 - 分块策略— 500 字符的 chunk 进一步缩小了单次 Embedding 的信息密度
什么时候 1536 维的优势会明显?当笔记超过 500 条、且内容高度专业(比如同时有 Rust 内存管理和 React Hooks 的笔记)时,更高维度能减少「语义碰撞」。
选型建议
选 nomic-embed-text 的场景
- 你已经在用 Ollama 跑 LLM,想保持全本地技术栈
- 笔记数量在 500 条以内
- 对隐私有要求,不想数据离开本机
- 网络环境不稳定
- 零成本优先
选 text-embedding-3-small 的场景
- 你的 LLM 已经是 OpenAI,Embedding 用同一个 Provider 更简单
- 笔记数量上千,需要更高的语义区分度
- 不想维护 Ollama 服务
- 对搜索精度有极致追求
- 能接受 API 费用(实际上很便宜)
混合使用:最佳实践
ChatCrystal 的 LLM 和 Embedding 是独立配置的,可以自由组合:
# 方案 A:全本地(推荐入门) LLM_PROVIDER=ollama LLM_MODEL=qwen2.5:7b EMBEDDING_PROVIDER=ollama EMBEDDING_MODEL=nomic-embed-text # 方案 B:LLM 云端 + Embedding 本地(性价比最高) LLM_PROVIDER=openai LLM_MODEL=gpt-4o EMBEDDING_PROVIDER=ollama EMBEDDING_MODEL=nomic-embed-text # 方案 C:全云端(最省心) LLM_PROVIDER=openai LLM_MODEL=gpt-4o EMBEDDING_PROVIDER=openai EMBEDDING_MODEL=text-embedding-3-small方案 B 是我个人推荐的组合:用云端 LLM 保证摘要质量(这是 GPT-4 比本地 7B 模型强得多的地方),用本地 Embedding 做搜索(nomic 的质量已经够用,而且免费、快速、无依赖)。
切换模型的注意事项
从一个模型切换到另一个,必须重建向量索引。不同模型生成的向量维度和分布完全不同,不能混用。
好消息是,ChatCrystal 会在你通过设置页面切换 Embedding 模型时自动清空旧索引——删除数据库中的所有 Embedding 记录,并删除vectra-index目录,同时将所有笔记标记为pending。
切换后,只需调用批量重建 API 让系统用新模型重新生成向量:
curl-XPOST http://localhost:3721/api/embeddings/batch这是一个异步接口,会把所有pending、failed、syncing状态的笔记排队执行 Embedding 生成,通过 p-queue 限速队列逐条处理。笔记多的话需要一些时间,但只需要做一次。
注意:
crystal summarize --all只会为尚未生成笔记的对话排队摘要生成,并不会重建 Embedding。切换 Embedding 模型后,正确做法是确认旧索引已自动清空(或手动调用批量重建 API),无需手动删除文件。
下一步
- Ollama 本地部署:零成本跑通全流程 — 完整的本地部署指南
- LLM 和 Embedding 不能混用 — 配置时的常见踩坑点
- Ollama vs OpenAI vs Claude 摘要横评 — LLM 摘要质量对比
项目地址:github.com/ZengLiangYi/ChatCrystal
