本文面向:需要为语义搜索选择合适 Embedding 模型的开发者。 预计阅读时间:7 分钟
Embedding 模型是干什么的
一句话:把文本变成向量(一组浮点数),让计算机能「理解」文本的语义相似度。
搜「CORS 跨域问题」时,Embedding 模型把这个查询转成向量,然后在知识库里找语义最接近的笔记。这就是为什么搜「跨域报错」也能匹配到标题是「CORS 配置」的笔记------它们的向量距离很近。
选型标准
选 Embedding 模型主要看三个维度:
| 维度 | 说明 | 重要性 |
|---|---|---|
| 质量 | 语义理解能力,决定搜索精准度 | ★★★★★ |
| 速度 | 单次 Embedding 延迟 | ★★★ |
| 成本 | API 费用或本地资源占用 | ★★★ |
推荐模型
本地模型(Ollama)
| 模型 | 大小 | 维度 | 质量 | 说明 |
|---|---|---|---|---|
nomic-embed-text |
274MB | 768 | ★★★★ | 首选推荐,专为 Embedding 设计 |
mxbai-embed-large |
670MB | 1024 | ★★★★ | 更大模型,精度略高 |
bash
# 安装推荐模型
ollama pull nomic-embed-text
本地模型的优势:免费、隐私、离线可用。nomic-embed-text 是目前 Ollama 生态里最成熟的 Embedding 模型,274MB 对任何机器都不是负担。
云端模型
| Provider | 模型 | 维度 | 质量 | 价格 |
|---|---|---|---|---|
| OpenAI | text-embedding-3-small |
1536 | ★★★★ | $0.02/1M tokens |
| OpenAI | text-embedding-3-large |
3072 | ★★★★★ | $0.13/1M tokens |
text-embedding-004 |
768 | ★★★★ | 免费额度内 |
云端模型质量更好,特别是 OpenAI 的 text-embedding-3-large,维度 3072,语义区分能力最强。但需要 API Key 和网络。
配置方法
方式一:设置页面
打开 ChatCrystal 设置页面,在 Embedding 部分填写:
| 字段 | 值 |
|---|---|
| Embedding Provider | ollama / openai / google |
| Embedding Model | nomic-embed-text / text-embedding-3-small / ... |
| Embedding Base URL | http://localhost:11434(Ollama 需要) |
| Embedding API Key | 你的 API Key(云端需要) |
方式二:CLI
bash
# Ollama
crystal config set embedding.provider ollama
crystal config set embedding.model nomic-embed-text
# OpenAI
crystal config set embedding.provider openai
crystal config set embedding.model text-embedding-3-small
crystal config set embedding.apiKey sk-...
# Google
crystal config set embedding.provider google
crystal config set embedding.model text-embedding-004
crystal config set embedding.apiKey AIza...
方式三:环境变量
env
EMBEDDING_PROVIDER=openai
EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_API_KEY=sk-your-key
验证配置
bash
crystal config test
输出:
vbnet
LLM: connected (response: "OK")
Embedding: connected
如果报错,常见原因:
| 错误 | 原因 | 解决 |
|---|---|---|
404 Not Found |
模型名拼错 | 检查模型名称 |
model not found |
Ollama 没拉取 | ollama pull 模型名 |
unauthorized |
API Key 错误 | 检查 Key |
ECONNREFUSED |
Ollama 没运行 | ollama serve |
ChatCrystal 的 Embedding 流程
了解内部流程有助于理解搜索质量的影响因素:
scss
笔记内容
↓ buildNoteEmbeddingText()
拼接:标题 + 摘要 + 结论 + 标签 + 代码描述
↓ chunkText()
按 500 字符分段(段落边界优先)
↓ embed() × N 段
每段生成一个向量
↓ vectra LocalIndex
存入本地向量索引
搜索时:
scss
查询文本
↓ embed()
查询向量
↓ vectra.queryItems()
与所有笔记向量计算相似度
↓ 按 score 排序
返回 Top K 结果
分块策略
ChatCrystal 按 500 字符分块,优先在段落边界切分:
typescript
const CHUNK_SIZE = 500;
function chunkText(text: string): string[] {
if (text.length <= CHUNK_SIZE) return [text];
const chunks: string[] = [];
const paragraphs = text.split(/\n\n+/);
let current = '';
for (const para of paragraphs) {
if (current.length + para.length + 2 > CHUNK_SIZE && current.length > 0) {
chunks.push(current.trim());
current = para;
} else {
current += (current ? '\n\n' : '') + para;
}
}
if (current.trim()) chunks.push(current.trim());
return chunks;
}
为什么要分块?因为 Embedding 模型对输入长度有限制,而且太长的文本 Embedding 质量会下降。500 字符是一个经验性的平衡点。
Embedding 文本构建
ChatCrystal 不是直接把笔记原文做 Embedding,而是提取关键信息拼接:
- 标题
- 摘要
- 关键结论(逐条)
- 标签
- 代码片段的描述(不含代码本身)
这样做的好处是向量更聚焦于语义信息,不会被代码细节稀释。
维度和质量的关系
维度越高,向量能表达的语义信息越丰富:
| 维度 | 能力 | 模型 |
|---|---|---|
| 768 | 够用,大部分场景没问题 | nomic-embed-text, text-embedding-004 |
| 1024 | 更好,细微语义区分更强 | mxbai-embed-large |
| 1536 | 很好,适合大量专业内容 | text-embedding-3-small |
| 3072 | 最强,但资源消耗也最大 | text-embedding-3-large |
对 ChatCrystal 的使用场景(搜索 AI 对话笔记),768 维已经够用。除非你的笔记数量上千条且内容高度专业,否则不需要追求 3072 维。
混合搭配建议
Embedding 可以和 LLM 用不同的 Provider:
| LLM | 推荐 Embedding | 理由 |
|---|---|---|
| Ollama (qwen2.5:7b) | Ollama (nomic-embed-text) | 全免费,全本地 |
| OpenAI (gpt-4o) | OpenAI (text-embedding-3-small) | 同一 Provider,统一计费 |
| Anthropic (claude) | OpenAI (text-embedding-3-small) | Anthropic 无 Embedding 模型 |
| Google (gemini) | Google (text-embedding-004) | 免费额度慷慨 |
| DeepSeek (custom) | Ollama (nomic-embed-text) | LLM 便宜 + Embedding 免费 |
切换模型后需要重新生成
更换 Embedding 模型后,已有的向量索引需要重建。因为不同模型生成的向量维度和分布不同,不能混用。
bash
# 1. 删除旧的向量索引
rm -rf <dataDir>/vectra-index
# 2. 用新模型批量重建所有笔记的 Embedding
curl -X POST http://localhost:3721/api/embeddings/batch
笔记数量多的话需要一些时间。
注意 :
crystal summarize --all只会为状态为 'imported'、'error' 或 'summarizing' 的对话排队摘要生成,不会重建 Embedding。如果只是切换了 Embedding 模型而笔记内容不需要重新生成,应该删除 vectra-index 目录后调用POST /api/embeddings/batch来重建向量索引。