Embedding 基础使用:用 Ollama 和 LangChain.js 生成文本向量

上一篇介绍了常见的 Embedding 模型、模型选型方法和应用场景。本文将从最小示例出发,使用 Ollama、nomic-embed-text 和 LangChain.js,把文本转换成向量。

前言

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

text 复制代码
文本
  ↓ Embedding 模型
向量

本文会完成以下内容:

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

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


一、Embedding 的输入和输出

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

例如:

text 复制代码
"如何重置密码?"
        ↓ Embedding 模型
[0.12, -0.35, 0.81, ..., 0.27]

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

ts 复制代码
const vector: number[] = [0.12, -0.35, 0.81, 0.27];

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

ts 复制代码
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。

这里需要区分两个概念:

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

Ollama 本身不是 Embedding 模型。

2. 下载模型

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

bash 复制代码
ollama pull nomic-embed-text

查看已经下载的模型:

bash 复制代码
ollama list

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

3. 确认 Ollama 服务地址

Ollama 默认提供本地服务:

text 复制代码
http://localhost:11434

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

bash 复制代码
ollama serve

三、通过 Ollama HTTP API 生成向量

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

执行:

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

返回结果的结构类似:

json 复制代码
{
  "model": "nomic-embed-text",
  "embeddings": [
    [0.012, -0.035, 0.081, 0.027]
  ]
}

其中:

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

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

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

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

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

text 复制代码
文本 → Ollama API → nomic-embed-text → 向量

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

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

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

调用关系如下:

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

1. 安装依赖

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

bash 复制代码
npm install @langchain/ollama

2. 创建 Embedding 客户端

ts 复制代码
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() 接收一条文本,返回一个向量:

ts 复制代码
const vector = await embeddings.embedQuery("如何重置密码?");

console.log(vector.length);
console.log(vector.slice(0, 5));

返回值类型可以理解为:

ts 复制代码
number[]

完整示例:

ts 复制代码
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() 接收多条文本,返回多个向量:

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

返回值类型可以理解为:

ts 复制代码
number[][]

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

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

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

text 复制代码
texts[0] → vectors[0]
texts[1] → vectors[1]
texts[2] → vectors[2]

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

两个方法可以这样记忆:

text 复制代码
embedQuery(text)      → 一条文本 → 一个向量
embedDocuments(texts) → 多条文本 → 多个向量

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


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

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

正确方式:

text 复制代码
文档 → nomic-embed-text → 文档向量
问题 → nomic-embed-text → 查询向量

错误方式:

text 复制代码
文档 → 模型 A → 文档向量
问题 → 模型 B → 查询向量

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

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

2. 向量维度必须一致

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

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

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

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

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

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

不能简单认为:

text 复制代码
相似度大于 0.8 就一定相关

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

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

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

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

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

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

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

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

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


八、常见问题

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

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

text 复制代码
http://localhost:11434

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

bash 复制代码
ollama serve

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

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

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

bash 复制代码
ollama pull nomic-embed-text

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

3. OllamaEmbeddings 会保存向量吗?

不会。

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

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

不能。

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

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

可能原因包括:

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

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

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

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

更有意义的检查方式是:

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

九、从基础示例到真实检索系统

本文的示例完成了从文本到向量的转换:

text 复制代码
文本 → Embedding 模型 → 向量

在真实知识库中,这一步通常会扩展为:

text 复制代码
原始文档
   ↓ 加载与清洗
文档分块
   ↓ embedDocuments()
文档向量
   ↓
写入向量数据库

用户问题
   ↓ embedQuery()
查询向量
   ↓
向量数据库相似度搜索
   ↓
返回 Top-K 个相关片段

如果继续构建 RAG,还会把检索到的片段与用户问题一起交给大语言模型:

text 复制代码
相关片段 + 用户问题 → 大语言模型 → 最终回答

因此,本文完成的是 RAG 检索链路中最基础的一层:把文本转换成向量,为后续的向量存储和语义检索提供数据。


总结

Embedding 的基础使用可以归纳为两步:

  1. 准备并运行 Embedding 模型;
  2. 把查询和文档转换成向量。

使用 Ollama 和 LangChain.js 时,可以记住下面的关系:

text 复制代码
Ollama:负责运行模型
nomic-embed-text:负责把文本转换成向量
OllamaEmbeddings:负责让 LangChain.js 调用 Ollama

还需要牢记三条规则:

  • 索引和查询使用同一个 Embedding 模型;
  • 相似度表示语义接近程度,不代表事实正确;
  • 长文档通常需要分块后再生成向量。

掌握文本向量的生成方式后,就可以继续引入文档加载、文本分块和向量数据库,逐步搭建完整的语义搜索或 RAG 知识库。

相关推荐
颜进强1 小时前
Vector Store 入门:什么是向量数据库,主流产品如何选择
前端·后端·ai编程
AI编程实验室1 小时前
Agent Skills 实战第三课:从规格到工单,别再按前后端拆任务
ai编程
AI大模型-小雄1 小时前
ChatGPT Plus 够不够用?从5种开发场景判断是否需要 Pro
chatgpt·ai编程·开发工具·codex·chatgpt plus·chatgpt pro
PH = 71 小时前
SpringBoot使用自动装配编写Start包
java·spring boot·后端
方方洛2 小时前
AI 教程系列-TUI 应用开发教程14-技能系统与可扩展性
前端·javascript·typescript
方方洛2 小时前
AI 教程系列-TUI 应用开发教程05-大模型对话界面设计
前端·javascript·typescript
方方洛2 小时前
AI 教程系列-TUI 应用开发教程08-流式响应与动画
前端·javascript·typescript
canonical_entropy2 小时前
Mission Driver:Loop Engineering 的一种通用参考实现
后端·aigc·ai编程
方方洛2 小时前
AI 教程系列-TUI 应用开发教程04-第一个 TUI 应用
前端·javascript·typescript