前言

Embedding 的基本使用流程并不复杂:


文本 ↓ Embedding 模型 向量

本文会完成以下内容:

  1. 使用 Ollama 在本地运行 Embedding 模型;
  2. 通过 Ollama HTTP API 生成向量;
  3. 使用 LangChain.js 调用 Embedding 模型;
  4. 理解 embedQuery() 和 embedDocuments()

本文只关注最基础的向量生成过程,暂时不展示相似度计算和语义搜索的具体实现,也不引入向量数据库和完整 RAG 项目。


一、Embedding 的输入和输出

Embedding 模型接收文本,返回一个固定长度的数字数组。

例如:


“如何重置密码?” ↓ Embedding 模型 [0.12, -0.35, 0.81, ..., 0.27]

在 TypeScript 中,一条文本生成的向量可以表示为:


const vector: number[] = [0.12, -0.35, 0.81, 0.27];

多条文本则会得到多个向量:


const vectors: number[][] = [ [0.12, -0.35, 0.81, 0.27], [0.09, -0.31, 0.77, 0.30], ];

真实模型输出的向量通常包含数百或数千个数字。为了便于阅读,示例中只展示前几个值。

向量中的单个数字通常不适合被单独解释。真正有用的是不同向量之间的整体关系:语义相近的文本,其向量通常也更接近。


二、准备 Ollama 和 Embedding 模型

1. Ollama 是什么?

Ollama 是一个本地模型运行工具。它负责下载、管理和运行模型,并向应用程序提供 HTTP API。

这里需要区分两个概念:


Ollama = 运行模型的服务 nomic-embed-text = 实际把文本转换成向量的模型

Ollama 本身不是 Embedding 模型。

2. 下载模型

安装并启动 Ollama 后,下载本文使用的模型:


ollama pull nomic-embed-text

查看已经下载的模型:


ollama list

正常情况下,可以在列表中看到 nomic-embed-text

3. 确认 Ollama 服务地址

Ollama 默认提供本地服务:


http://localhost:11434

如果 Ollama 桌面程序没有自动启动服务,可以执行:


ollama serve


三、通过 Ollama HTTP API 生成向量

在使用 LangChain.js 之前,可以先直接调用 Ollama API,观察 Embedding 模型的原始返回结果。

执行:


curl http://localhost:11434/api/embed \ -H "Content-Type: application/json" \ -d '{ "model": "nomic-embed-text", "input": "如何重置密码?" }'

返回结果的结构类似:


{ "model": "nomic-embed-text", "embeddings": [ [0.012, -0.035, 0.081, 0.027] ] }

其中:

  • model 表示使用的模型;
  • input 是需要转换的文本;
  • embeddings 是模型生成的向量列表。

即使只传入一条文本,embeddings 仍然是二维数组,因为接口也支持批量输入:


curl http://localhost:11434/api/embed \ -H "Content-Type: application/json" \ -d '{ "model": "nomic-embed-text", "input": [ "如何重置密码?", "忘记密码后怎样重新设置?", "今天天气怎么样?" ] }'

三条文本会按输入顺序得到三个向量。

至此,我们已经完成了最基础的调用:


文本 → Ollama API → nomic-embed-text → 向量


四、使用 LangChain.js 调用 Embedding 模型

直接调用 HTTP API 没有问题,但在 LangChain.js 应用中,通常会使用 OllamaEmbeddings

它是 LangChain 对 Ollama Embedding API 的客户端封装。

调用关系如下:


TypeScript 程序 ↓ OllamaEmbeddings ↓ HTTP Ollama 服务 ↓ nomic-embed-text ↓ 数值向量

1. 安装依赖

在 TypeScript 或 Node.js 项目中安装:


npm install @langchain/ollama

2. 创建 Embedding 客户端


import { OllamaEmbeddings } from "@langchain/ollama"; const embeddings = new OllamaEmbeddings({ model: "nomic-embed-text", baseUrl: "http://localhost:11434", });

两个主要参数分别是:

参数 作用
model 指定 Ollama 中使用的 Embedding 模型
baseUrl 指定 Ollama 服务地址

创建 OllamaEmbeddings 对象时,主要是在保存客户端配置。真正调用向量生成方法时,才会向 Ollama 发出请求。


五、使用 embedQuery() 生成单个向量

embedQuery() 接收一条文本,返回一个向量:


const vector = await embeddings.embedQuery("如何重置密码?"); console.log(vector.length); console.log(vector.slice(0, 5));

返回值类型可以理解为:


number[]

完整示例:


import { OllamaEmbeddings } from "@langchain/ollama"; const embeddings = new OllamaEmbeddings({ model: "nomic-embed-text", baseUrl: "http://localhost:11434", }); const vector = await embeddings.embedQuery("如何重置密码?"); console.log("向量维度:", vector.length); console.log("前 5 个值:", vector.slice(0, 5));

embedQuery() 通常用于查询阶段,例如把用户输入的问题转换成查询向量。

需要注意,它只负责生成向量,不会自动搜索文档,也不会生成自然语言回答。


六、使用 embedDocuments() 批量生成向量

embedDocuments() 接收多条文本,返回多个向量:


const vectors = await embeddings.embedDocuments([ "如何重置密码?", "忘记密码后怎样重新设置?", "今天天气怎么样?", ]);

返回值类型可以理解为:


number[][]

可以检查返回数量和向量维度:


console.log("向量数量:", vectors.length); console.log("每个向量的维度:", vectors[0].length);

输入文本与输出向量按顺序对应:


texts[0] → vectors[0] texts[1] → vectors[1] texts[2] → vectors[2]

embedDocuments() 通常用于索引阶段,为多个文档片段批量生成向量。

两个方法可以这样记忆:


embedQuery(text) → 一条文本 → 一个向量 embedDocuments(texts) → 多条文本 → 多个向量

它们的区别主要体现在调用语义和输入数量上。部分模型还会针对“查询”和“文档”使用不同的编码方式,因此在实际项目中应按照用途选择正确的方法。


七、基础使用中的重要规则

1. 文档和查询必须使用同一个模型

正确方式:


文档 → nomic-embed-text → 文档向量 问题 → nomic-embed-text → 查询向量

错误方式:


文档 → 模型 A → 文档向量 问题 → 模型 B → 查询向量

不同模型生成的向量通常不在同一个语义空间中。即使两个模型输出的维度相同,也不代表它们可以直接比较。

因此,更换 Embedding 模型后,通常需要重新生成所有文档向量。

2. 向量维度必须一致

计算余弦相似度时,两个向量必须具有相同维度。

如果一个向量是 768 维,另一个是 1024 维,就不能直接进行对应位置的计算。

3. 相似度高不等于事实正确

Embedding 判断的是“语义是否接近”,而不是“内容是否真实”。

两段内容可能表达相似,但同时包含错误信息。因此,语义相似度不能代替事实校验。

4. 不要直接照搬固定阈值

不能简单认为:


相似度大于 0.8 就一定相关

不同模型、文本长度、语言和业务数据的分数分布都可能不同。阈值应该使用真实样本进行评估后再确定。

5. 长文档通常需要先分块

虽然部分模型支持较长输入,但不建议把包含多个主题的整篇文档直接生成一个向量。

例如,一篇文档同时包含:

  • 用户注册;
  • 密码重置;
  • 权限管理;
  • 订单支付。

如果整篇文档只生成一个向量,具体主题可能被稀释。实际检索系统通常先把文档切成多个语义相对完整的片段,再分别生成向量。

6. 索引时尽量批量生成向量

为大量文档建立索引时,应优先使用 embedDocuments() 批量处理,而不是对每条文本单独调用一次 embedQuery()

批量调用通常可以减少请求次数,提高索引效率。具体批次大小需要根据模型服务的输入限制和机器资源进行调整。


八、常见问题

1. 为什么提示连接不到 Ollama?

先确认 Ollama 服务正在运行,并检查地址是否正确:


http://localhost:11434

可以使用以下命令启动服务:


ollama serve

如果应用运行在 Docker 容器或另一台机器中,localhost 指向的是应用自身所在的环境,需要改成应用能够访问的 Ollama 地址。

2. 为什么提示模型不存在?

通常是因为模型尚未下载。执行:


ollama pull nomic-embed-text

然后通过 ollama list 确认模型名称与代码中的 model 配置一致。

3. OllamaEmbeddings 会保存向量吗?

不会。

它只负责请求模型并返回向量。向量需要由应用程序自行保存,或者交给向量数据库管理。

4. OllamaEmbeddings 能生成自然语言回答吗?

不能。

它调用的是 Embedding 模型,输出是数值向量。生成自然语言回答需要聊天模型或其他大语言模型客户端。

5. 为什么相似文本的分数没有想象中高?

可能原因包括:

  • 模型对当前语言支持有限;
  • 文本过短,缺少足够上下文;
  • 两段文本属于特定专业领域;
  • 使用了不适合当前任务的模型;
  • 固定阈值并不适合当前模型的分数分布。

判断效果时,不要只检查一组文本。更可靠的方式是准备一批相关和不相关样本,观察整体排序表现。

6. 可以把向量打印出来直接分析吗?

可以打印,但通常很难从单个数字中直接理解模型学到了什么。

更有意义的检查方式是:

  • 比较相似度;
  • 查看检索排序;
  • 检查正确结果是否出现在 Top-K 中;
  • 使用真实问题构建评估集。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐