Embedding 模型选型与配置

0 阅读4分钟

本文面向:需要为语义搜索选择合适 Embedding 模型的开发者。 预计阅读时间:7 分钟


Embedding 模型是干什么的

一句话:把文本变成向量(一组浮点数),让计算机能「理解」文本的语义相似度。

搜「CORS 跨域问题」时,Embedding 模型把这个查询转成向量,然后在知识库里找语义最接近的笔记。这就是为什么搜「跨域报错」也能匹配到标题是「CORS 配置」的笔记——它们的向量距离很近。

选型标准

选 Embedding 模型主要看三个维度:

维度说明重要性
质量语义理解能力,决定搜索精准度★★★★★
速度单次 Embedding 延迟★★★
成本API 费用或本地资源占用★★★

推荐模型

本地模型(Ollama)

模型大小维度质量说明
nomic-embed-text274MB768★★★★首选推荐,专为 Embedding 设计
mxbai-embed-large670MB1024★★★★更大模型,精度略高
# 安装推荐模型
ollama pull nomic-embed-text

本地模型的优势:免费、隐私、离线可用。nomic-embed-text 是目前 Ollama 生态里最成熟的 Embedding 模型,274MB 对任何机器都不是负担。

云端模型

Provider模型维度质量价格
OpenAItext-embedding-3-small1536★★★★$0.02/1M tokens
OpenAItext-embedding-3-large3072★★★★★$0.13/1M tokens
Googletext-embedding-004768★★★★免费额度内

云端模型质量更好,特别是 OpenAI 的 text-embedding-3-large,维度 3072,语义区分能力最强。但需要 API Key 和网络。

配置方法

方式一:设置页面

打开 ChatCrystal 设置页面,在 Embedding 部分填写:

字段
Embedding Providerollama / openai / google
Embedding Modelnomic-embed-text / text-embedding-3-small / ...
Embedding Base URLhttp://localhost:11434(Ollama 需要)
Embedding API Key你的 API Key(云端需要)

方式二:CLI

# 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...

方式三:环境变量

EMBEDDING_PROVIDER=openai
EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_API_KEY=sk-your-key

验证配置

crystal config test

输出:

LLM: connected (response: "OK")
Embedding: connected

如果报错,常见原因:

错误原因解决
404 Not Found模型名拼错检查模型名称
model not foundOllama 没拉取ollama pull 模型名
unauthorizedAPI Key 错误检查 Key
ECONNREFUSEDOllama 没运行ollama serve

ChatCrystal 的 Embedding 流程

了解内部流程有助于理解搜索质量的影响因素:

笔记内容
    ↓ buildNoteEmbeddingText()
拼接:标题 + 摘要 + 结论 + 标签 + 代码描述
    ↓ chunkText()
按 500 字符分段(段落边界优先)
    ↓ embed() × N 段
每段生成一个向量
    ↓ vectra LocalIndex
存入本地向量索引

搜索时:

查询文本
    ↓ embed()
查询向量
    ↓ vectra.queryItems()
与所有笔记向量计算相似度
    ↓ 按 score 排序
返回 Top K 结果

分块策略

ChatCrystal 按 500 字符分块,优先在段落边界切分:

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 模型后,已有的向量索引需要重建。因为不同模型生成的向量维度和分布不同,不能混用。

# 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 来重建向量索引。


项目地址:github.com/ZengLiangYi…