热闻岛
Embedding:文本怎么变成向量?语义检索为什么能工作?
AI新闻

Embedding:文本怎么变成向量?语义检索为什么能工作?

2026年6月14日 07:3714 阅读
Embedding 不是可有可无的组件,它是 RAG 的地基。地基歪了,后面 Retriever、Rerank、Prompt 再漂亮也救不回来。 一、Embedding 到底是什么? Embedding,就是把一段文字变成一串数字。 这串数

Embedding 不是可有可无的组件,它是 RAG 的地基。地基歪了,后面 Retriever、Rerank、Prompt 再漂亮也救不回来。

一、Embedding 到底是什么?

Embedding,就是把一段文字变成一串数字。

这串数字不是随机数,而是语义坐标。意思相近的文本,向量距离更近;意思差得远的文本,距离更远。

LangChain 官方文档对 Embedding 的解释也很直接:Embedding 模型会把原始文本转换成固定长度的数字向量,这些向量能表达语义含义,让机器按“意思”比较文本,而不是只按关键词匹配。

这就是语义检索能工作的原因。

用户问“提前还款怎么操作”,知识库里写的是“如何结清贷款”,关键词不一样,但意思接近。Embedding 能把它们拉到同一个语义区域,向量库就能召回。

如果没有 Embedding,RAG 很容易退化成普通搜索。只能找关键词,找不到意思。

二、Embedding 在 RAG 里处在哪一层?

前面几章已经讲过:Loader 把资料读进来,Splitter 把长文档切成 Chunk。接下来,就轮到 Embedding。

它做一件事:把每个 Chunk Document 的 page_content 转成向量,然后交给 VectorStore 入库。

离线建库和在线查询两条链路,都要经过 Embedding

离线建库时,Embedding 面向的是一批文档。在线查询时,Embedding 面向的是用户的一句话。

所以 LangChain 在接口上故意拆成两个方法:

embed_documents(texts):文档批量向量化。

embed_query(text):问题单条向量化。

这两个方法看起来简单,但这是整个语义检索的分水岭。文档侧是“建索引”,问题侧是“查索引”。

三、Embeddings 抽象接口

打开 LangChain Core 的源码,可以看到 Embeddings 是一个抽象基类。它不是 OpenAI,也不是 HuggingFace。它只是规定:一个 Embedding 模型必须能把文档和查询变成向量。

源码的设计非常克制。核心就是四个方法:

class Embeddings(ABC):

def embed_documents(self, texts: list[str]) -> list[list[float]]: ...

def embed_query(self, text: str) -> list[float]: ...

async def aembed_documents(self, texts: list[str]) -> list[list[float]]: ...

async def aembed_query(self, text: str) -> list[float]: ...

这里最关键的是返回类型。

文档是 list[str],所以返回 list[list[float]]。一个文本,对应一个向量。

查询是 str,所以返回 list[float]。一个问题,对应一个向量。

这就是 LangChain 抽象层的好处:上层 VectorStore 和 Retriever 不需要知道你底下接的是哪个模型。只要实现这个接口,就能被接入 RAG 流水线。

四、为什么要区分 embed_documents 和 embed_query?

很多人第一次看源码会疑惑:文档是文本,问题也是文本,为什么不统一叫 embed_text?

不同 Embedding 模型可能会对“文档”和“查询”使用不同提示方式。比如某些检索模型会要求:

文档侧:把这段内容编码成可检索资料。

查询侧:把这个问题编码成检索意图。

有些模型内部路径一样,有些模型内部路径不一样。LangChain 先把接口拆开,给不同模型留下扩展空间。

这就是好的框架设计:看起来多写了一个方法,实际是在给未来兼容性留口子。

五、OpenAIEmbeddings 怎么实现?

以 OpenAIEmbeddings 为例,源码链路很清晰。

它的 embed_documents 大致分两种情况:

如果不检查上下文长度:直接按 chunk_size 分批调用 embedding API。

如果检查上下文长度:先 tokenize,再按 embedding_ctx_length 切分,再批量请求。

源码里有一个细节很重要:长文本不会直接硬塞给 embedding API。它会被分成 token chunks,再请求向量,最后把多段结果合并。

embed_query 更直接。源码逻辑可以压缩成一句话:

def embed_query(self, text: str) -> list[float]:

return self.embed_documents([text])[0]

也就是说:问题向量化,本质上还是复用文档向量化能力。先把单个问题包装成列表,再取第一个结果。

这不是偷懒。恰恰是工程复用。

六、从源码看 VectorStore 为什么能自动调用 Embedding?

很多人写代码时会发现,自己明明只写了 from_texts、add_documents 或 retriever.invoke,为什么 Embedding 就自动被调用了?

因为 VectorStore 和 Retriever 在内部已经接上了 Embeddings 接口。

文档入库时:

VectorStore.add_documents(documents)

-> 取出 page_content

-> embeddings.embed_documents(texts)

-> 向量 + metadata + id 入库

查询检索时:

retriever.invoke(query)

-> vectorstore.similarity_search(query)

-> embeddings.embed_query(query)

-> 用 query_vector 去向量库查近邻

-> 返回 List[Document]

所以,Embedding 是隐藏在 RAG 后面的发动机。你看不到它转,但它一直在转。

七、向量怎么比较?三个常见相似度指标

文本变成向量后,下一步就是比较。LangChain 官方文档列出了常见的三类距离或相似度指标:余弦相似度、欧氏距离、点积。

余弦相似度:看两个向量方向是否接近。常用于语义相似度。

欧氏距离:看两个点在空间里的直线距离。

点积:看一个向量在另一个方向上的投影强度。

实际项目里,你不一定要自己手写这些公式。向量库会帮你算。但你必须知道:不同指标会影响召回结果,向量是否归一化也会影响结果。

八、Embedding 模型怎么选?这张图比参数更重要

选 Embedding 模型,不要只看榜单。

真正上线时,最关键的是业务评测。你要拿自己的问题、自己的知识库、自己的标准答案去测。

比如智能客服要测:用户口语化提问能不能召回制度文档。股票研报助手要测:同一公司不同公告、行业新闻、研报摘要之间能不能建立语义关联。

如果只拿通用 benchmark 选模型,很容易线上翻车。

九、缓存为什么重要?Embedding 不是一次性成本

很多人以为 Embedding 只在建库时花钱。错。

查询也要 embed_query。每个用户问题都可能触发一次向量化。高并发下,延迟和成本都会上来。

所以生产系统至少要做三类缓存:

文档向量缓存:同一 Chunk 内容不重复向量化。

查询向量缓存:高频问题不重复调用 embedding 模型。

索引版本缓存:模型版本、切分版本、向量库版本要绑定。

只要文档内容、切分策略、Embedding 模型版本任何一个变了,历史向量就可能需要重建。

十、企业级落地:Embedding 不该只是一个函数

如果只是 Demo,Embedding 可以写成一行代码。

如果是企业系统,Embedding 必须进入数据流水线。

Java 主服务负责文档上传、任务调度、权限审计、索引版本。

Python AI 服务负责 Loader、Splitter、Embedding、Retriever。

Milvus / ES 负责向量和关键词索引。

Redis 做缓存,MySQL 记录元数据和版本。

日志系统记录每次向量化耗时、模型名称、维度、token、失败原因。

Embedding 是基础设施,不是工具函数。

十一、Embedding 最容易踩的坑

RAG 效果差,很多时候不是大模型的问题,而是 Embedding 链路的问题。

最常见的就是:文档切得不好、模型版本乱换、向量没有重建、metadata 没带、只做向量检索不做关键词融合。

尤其是模型版本。

同一个向量库里,不能混着放不同 Embedding 模型生成的向量。维度可能不同,语义空间也可能不同。哪怕维度一样,距离也不一定有可比性。

十二、总结

Embedding 把文本变成向量,VectorStore 按距离找资料,Retriever 把相关 Document 交给 Prompt,Model 再基于资料生成答案。

所以,RAG 的第一性原理不是“把资料塞给大模型”,而是:先把资料变成可计算的语义坐标,再按问题找到最相关的上下文。

学 LangChain,Embedding 这一章必须吃透。因为从这里开始,RAG 才真正从“文本处理”进入“语义计算”。

相关推荐

LangChain Tool Calling 原理:模型是怎么决定调用哪个工具的?
AI新闻

LangChain Tool Calling 原理:模型是怎么决定调用哪个工具的?

一、模型不会真的调用工具 很多人第一次看 Tool Calling,会以为模型自己会查数据库、调接口、执行函数。错。模型没有直接执行你代码的权限。它能做的,是看懂你给它的工具说明,然后输出一段结构化数据。 这段结构化数据就是 tool_ca

1027 分钟前
LangChain 系列之Tools:让大模型真正连接业务系统
AI新闻

LangChain 系列之Tools:让大模型真正连接业务系统

前面几章,我们把 RAG 的底层链路讲完了:文档进来,切分,向量化,入库,检索,重排,最后把上下文交给模型。 但这还不够。 RAG 让模型会“查资料”。Tools 让模型能“办事情”。 没有 Tools,大模型只是一个会聊天的脑子。它能分析

141 小时前
LangChain 系列:从 0 搭一个企业知识库问答系统
AI新闻

LangChain 系列:从 0 搭一个企业知识库问答系统

这一章不再单讲一个组件,而是把前面所有 RAG 组件合起来:从文件上传,到向量入库,再到用户提问、检索证据、模型回答、日志追踪。 01 先搞清楚:企业知识库问答不是 Demo Demo 只要能回答。企业系统要能上传、能解析、能检索、能引用、

112 小时前
Tensor:PyTorch 世界里的一切都是张量
AI新闻

Tensor:PyTorch 世界里的一切都是张量

1. Tensor 是 PyTorch 的基本单位 PyTorch 里,模型吃进去的是 Tensor,参数保存的是 Tensor,梯度也是 Tensor,GPU 上跑的还是 Tensor。 你可以把 Tensor 理解成“加强版数组”。但这

83 小时前
Rerank 与上下文压缩:为什么召回 TopK 后还要重排?
AI新闻

Rerank 与上下文压缩:为什么召回 TopK 后还要重排?

RAG 最容易踩的坑,不是“没有召回”。 而是召回了一堆看起来相关、实际会干扰模型的资料。 所以,成熟的 RAG 链路不会把 TopK 直接塞进 Prompt。 它会先召回,再重排,再压缩。 1. 为什么召回 TopK 后还要重排? 向量召

103 小时前
环境安装与开发姿势:CPU、CUDA、ROCm、MPS 怎么选
AI新闻

环境安装与开发姿势:CPU、CUDA、ROCm、MPS 怎么选

学 PyTorch,第一个门槛不是模型。是环境。环境装错,代码还没开始跑,报错已经堆满屏。 这一章只讲一件事:把 PyTorch 环境一次性讲清楚。不是背安装命令,而是看懂安装背后的逻辑。 因为命令会随着版本变化。逻辑不会。你只要能判断 C

64 小时前

阅读补充

一句话看懂

Embedding 不是可有可无的组件,它是 RAG 的地基。地基歪了,后面 Retriever、Rerank、Prom

事件背景

这篇内容围绕“Embedding”展开,热闻岛基于公开信息整理事件背景、主要进展与可继续关注的方向。

事件时间线

发布

相关信息进入公开传播

更新

热闻岛对内容进行整理与补充。

看点

  • · Embedding的最新进展是什么
  • · 相关信息对用户或行业会带来哪些影响
  • · 后续是否会有新的回应或处理结果

后续关注

  • · 后续官方回应或权威通报
  • · 相关主体的进一步说明
  • · 事件对普通用户和平台传播的持续影响

免责声明:本文仅代表作者观点,不构成投资建议、法律建议、医疗建议。财经类内容尤其需要注意风险;爆料类信息请以权威通报为准。

评论 (0)

登录后即可发表评论

去登录
暂无评论,快来抢沙发