楼层: 首页/ 软件技术/ Spring AI/ Embedding:把文字变成向量,算语义相似度
六补

Embedding:把文字变成向量,算语义相似度

Embedding & Semantic Similarity

RAG 的地基是 Embedding——把一段文字变成一串数字(向量),语义相近的文字,向量在空间里也离得近。"退货"和"不想买了"字面上一个字都不像,但向量离得很近,这就是为什么 RAG 能"听懂"你的问题去找相关文档。这一章把 Embedding 单独讲透。

论Embedding 到底干了啥

"我想退货" → EmbeddingModel → [0.12, -0.87, 0.33, ...](比如 1024 维)。

"商品我不想要了" → 同一个模型 → [0.11, -0.85, 0.30, ...]。两个向量夹角很小。

"今天中午吃啥" → [-0.5, 0.2, ...],跟上面那两个夹角很大。

余弦相似度(cosine)就是算两个向量夹角的余弦:越接近 1 越相似,越接近 0 越不相关。RAG 检索就是把问题向量化后,在向量库里找 cosine 最大的几段文档。

注入 EmbeddingModel,给两句话算相似度

@RestController @RequestMapping("/embed") public class EmbeddingController { private final EmbeddingModel embeddingModel; public EmbeddingController(EmbeddingModel embeddingModel) { this.embeddingModel = embeddingModel; } @GetMapping("/sim") public String sim(String a, String b) { // 把两句话各自向量化 float[] va = embeddingModel.embed(a).getOutput().getVector(); float[] vb = embeddingModel.embed(b).getOutput().getVector(); double cos = cosine(va, vb); return String.format("'%s' vs '%s' 相似度 = %.3f", a, b, cos); } // 余弦相似度:点积 / (模长乘积) private double cosine(float[] x, float[] y) { double dot = 0, nx = 0, ny = 0; for (int i = 0; i < x.length; i++) { dot += x[i] * y[i]; nx += x[i]*x[i]; ny += y[i]*y[i]; } return dot / (Math.sqrt(nx) * Math.sqrt(ny)); } }

跑起来看输出

GET /embed/sim?a=我想退货&b=商品我不想要了 # '我想退货' vs '商品我不想要了' 相似度 = 0.892 ← 很像 GET /embed/sim?a=我想退货&b=今天中午吃什么 # '我想退货' vs '今天中午吃什么' 相似度 = 0.213 ← 不相干

Embedding 的三个硬约束

约束为什么
入库和检索必须同一模型不同模型向量空间不同,坐标对不上,再像也搜不到。
换模型必须重建索引真要换 Embedding 模型,所有历史文档重新向量化入库。
维度要和向量库对齐每个模型输出固定维(如 1024),建库时维度写错直接报错。
别把 Embedding 模型当聊天模型用

EmbeddingModel 只能"把文字变向量",不能聊天、不能回答问题。它是 RAG 里的"翻译官",不是"思考者"。聊天用 ChatModel,算相似度/检索用 EmbeddingModel,两个别混。

本章面试题(RAG + Embedding)

面试快答 · RAG 与向量检索

Q1. RAG 七步流水线是什么?

参考答案

文档加载 → 切分(chunk) → Embedding 向量化 → 存向量库 → 用户问题向量化后相似度检索 → 把相关片段拼进 prompt → 大模型基于片段生成。核心是"先找对资料,再让模型说话"。

Q2. 为什么入库和检索必须用同一个 Embedding 模型?

参考答案

不同模型各有各的向量空间,"猫"在模型 A 的坐标和模型 B 的坐标完全不同。用 A 入库、用 B 检索,问题向量跟文档向量不在同一空间,余弦相似度失真,怎么搜都不相关。换模型必须全量重建索引。

Q3. 向量数据库怎么选?

参考答案

学习 demo 用内存 SimpleVectorStore;已有 PG 用 PGVector;已有 Redis 用 Redis Stack;大规模生产选 Milvus。判断:别为向量库单独引入新中间件,优先用你公司已有的。

Q4. chunk 切太大太小分别有什么问题?

参考答案

太大:一段混多个主题,检索时相关片段里掺无关内容,噪声多;太小:一两句话没上下文,语义不完整。一般 500~1000 token,留点 overlap 防止句子被切断。