
1. 面试官问这道题到底在考察什么面过 Java AI 岗的人大概都有这种体验简历上写了“熟悉 Spring AI”“做过 RAG 知识库”面试官不问你 API 怎么调张口就是一句——“Embedding 和 LLM 输出的向量到底是不是一回事”这个问题杀伤力很大。因为它不像八股文那样背一背就能糊弄过去它直接暴露你对大模型底层数据流的理解深度。我见过不少候选人能熟练写出ChatClient的调用链能讲清楚VectorStore怎么存怎么查但一被追问“那 LLM 输出的那个向量和 Embedding 向量能不能混着用”当场就卡壳了。先把结论摆在最前面方便你建立整体认知Embedding 向量和 LLM 输出向量虽然都是浮点数数组但它们是两个完全不同维度的东西——一个是“语义坐标”一个是“概率分布”。前者用来衡量“两段文本像不像”后者用来回答“下一个词是什么”。把这两个概念混为一谈是很多 Java 后端转 AI 时最容易踩的认知坑。这篇文章我打算从面试考察意图、底层原理、代码实战、生产踩坑四个层面把它彻底讲透。适合三类人看正在准备 Java AI 岗面试的、用 Spring AI 做 RAG 项目但原理没吃透的、以及想搞清楚“向量”这个词在大模型里到底有几种含义的开发者。全文会配可运行的 Spring AI 代码不是纸上谈兵。在展开之前先明确一个贯穿全文的类比后面所有原理都围绕它展开Embedding 像是给每段文本发一张“身份证”上面写满了它在语义空间里的坐标而 LLM 输出的向量像是它在做选择题时填的“答题卡”每一格代表某个候选词的得分。身份证可以拿去比对两个人像不像答题卡只能用来决定下一个字写什么。这个区别就是整道面试题的核心。2. 两种向量的本质差异语义坐标 vs 概率分布2.1 Embedding 向量把文本钉在语义空间的一个点上Embedding 的本质是一次静态映射。你给它一段文本它经过编码器比如 BERT 类模型、或者各家厂商的 embedding 模型吐出一个固定维度的向量比如 768 维、1024 维、1536 维。这个向量一旦生成就固定了同一段文本每次生成的向量几乎一致确定性输出。它的核心特性是语义相似度可度量。两段文本的 embedding 向量做余弦相似度计算值越接近 1说明语义越接近。这就是 RAG 检索的物理基础——用户问“怎么退货”知识库里有一条“商品如何申请退款”虽然字面不一样但 embedding 相似度高就能被召回。你可以把 embedding 空间想象成一张巨大的地图每个词、每句话都被钉在地图上的某个坐标点。语义相近的文本坐标点就挨得近。“猫”和“狗”离得近“猫”和“汽车”离得远。这个空间是训练出来的不是人为设计的所以它捕捉的是统计意义上的语义关联。关键点在于embedding 向量不携带生成能力。它只是坐标你没法从坐标反推出“下一句话该说什么”。它的使命就是“表示”不是“预测”。2.2 LLM 输出向量下一个 token 的概率分布LLM 输出的向量准确说法叫logits 向量。当模型处理完你的输入在最后一层会输出一个维度等于词表大小的向量。比如词表有 15 万个 token那这个向量就是 15 万维每一维对应一个 token 的原始得分logit。这个向量经过 softmax 之后就变成了概率分布——每个 token 被选为下一个词的概率。模型根据这个分布采样选出下一个 token然后把这个 token 拼回输入再跑一遍生成再下一个。这就是自回归生成的全过程。所以 LLM 输出向量的本质是动态的、上下文相关的、用于决策的。同一个问题你问两遍因为采样随机性输出的 logits 分布可能略有不同最终生成的文本也可能不一样。它不是一个固定的“表示”而是一个“决策依据”。这里有个容易被忽略的细节LLM 内部其实也有隐藏状态向量hidden states那是它中间层的表示维度通常是几千。但面试官问“LLM 输出向量”时默认指的是最后一层用于预测 next token 的那个 logits 向量不是中间隐藏态。这个区分要讲清楚否则容易答偏。2.3 一张表看清两者的分野对比维度Embedding 向量LLM 输出向量logits本质语义坐标概率分布维度通常 768~3072等于词表大小常 5万~15万是否确定基本确定受采样影响有随机性用途相似度计算、检索、聚类预测下一个 token能否互相替代不能不能生成方式编码器前向一次解码器每步生成一次典型消费方向量数据库采样器 / 解码策略这张表建议直接背下来面试时能脱口而出基本就赢了一半。但光背没用面试官会追问“为什么维度差这么多”“为什么不能混用”这就得靠下一节的原理来撑。3. 从 Transformer 结构看两种向量是怎么产生的3.1 编码器路线Embedding 是怎么被“压”出来的主流 embedding 模型大多基于 Transformer 的编码器结构Encoder。输入文本先被 tokenize 成一串 token id然后查 embedding 表变成初始向量再经过多层自注意力最后通过池化pooling操作——常见的有 CLS token 池化、均值池化——把整个序列压缩成一个固定维度的向量。这个压缩过程是有损的但保留了语义主干。就像把一本书浓缩成一句话摘要细节丢了主旨还在。正因为有损所以 embedding 向量维度不需要很大768 到 1536 就够表达语义了。维度再高边际收益递减还浪费存储。这里有个工程上的关键点embedding 模型和生成模型是分开训练的甚至是不同厂商的。你用 OpenAI 的 embedding 模型生成的向量和用本地部署的 BGE 模型生成的向量维度可能一样但语义空间完全不同绝对不能混用。这是 RAG 项目里最常见的翻车点之一。3.2 解码器路线logits 是怎么一步步“算”出来的LLM 大多是解码器结构Decoder-only。它同样是 token 进、向量出但最后一层的输出不是池化成一个向量而是保留每个位置的输出。当你只关心最后一个位置时它对应的输出向量经过一个线性层LM Head映射到词表维度就得到了 logits。这个 LM Head 是一个巨大的矩阵形状是[hidden_size, vocab_size]。比如 hidden_size 是 4096vocab_size 是 15 万这个矩阵就有 6 亿多参数。它负责把“内部语义表示”翻译成“每个词的得分”。这一步是 embedding 模型完全没有的。所以从结构上看embedding 模型止步于“表示”LLM 多了一个“决策头”。这个决策头就是两者最本质的结构差异。面试时如果能画出这个结构差异比背十句定义都管用。3.3 为什么维度差异这么大却都叫“向量”这是面试官爱挖的坑。答案是“向量”只是数学形式不代表语义等价。就像“温度”和“身高”都可以用数字表示但你不会拿 25 度和 175 厘米去比较。Embedding 向量的每一维没有独立含义它是分布式表示整体才有意义。logits 向量的每一维有明确含义——对应一个具体 token 的得分。一个是“整体语义”一个是“逐词打分”维度自然天差地别。理解了这一点你就能回答那个经典追问“能不能把 LLM 的隐藏态拿来当 embedding 用”答案是理论上可以实践中也有这种做法叫 LLM-based embedding但效果通常不如专门训练的 embedding 模型而且成本高得多。因为 LLM 的隐藏态是为生成优化的不是为相似度优化的。4. Spring AI 实战两条链路分别怎么落地4.1 环境与依赖准备先搭一个能跑的最小工程。Spring Boot 3.2JDK 17Spring AI 用 1.0 稳定版写这篇文章时 1.0.x 已经 GA比早期的 0.x 稳定太多。dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version1.0.0/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-pgvector-store-spring-boot-starter/artifactId version1.0.0/version /dependency配置文件里配好模型和向量库spring: ai: openai: api-key: ${OPENAI_API_KEY} embedding: options: model: text-embedding-3-small chat: options: model: gpt-4o-mini temperature: 0.7 datasource: url: jdbc:postgresql://localhost:5432/vectordb提示embedding 模型和 chat 模型要分开配。很多人图省事用同一个模型名结果 embedding 调用直接报错。这两个是不同端点、不同用途的模型必须区分。4.2 生成 Embedding 并做相似度检索先看 embedding 这条链路。核心是EmbeddingModel接口它负责把文本转成向量。Service public class EmbeddingService { private final EmbeddingModel embeddingModel; private final VectorStore vectorStore; public EmbeddingService(EmbeddingModel embeddingModel, VectorStore vectorStore) { this.embeddingModel embeddingModel; this.vectorStore vectorStore; } // 单条文本转向量 public float[] embed(String text) { return embeddingModel.embed(text); } // 批量转向量生产环境务必用批量接口 public Listfloat[] embedBatch(ListString texts) { return embeddingModel.embed(texts); } // 写入向量库 public void save(String content, MapString, Object metadata) { Document doc new Document(content, metadata); vectorStore.add(List.of(doc)); } // 相似度检索 public ListDocument search(String query, int topK) { SearchRequest request SearchRequest.builder() .query(query) .topK(topK) .similarityThreshold(0.7) .build(); return vectorStore.similaritySearch(request); } }这段代码有几个生产级的细节值得说。第一embedBatch一定要用单条循环调用 embedding 接口网络往返开销能把性能拖垮批量接口一次能处理几十上百条。第二similarityThreshold别设太低0.7 是个经验起点太低会召回一堆不相关的内容反而干扰 LLM。第三metadata 里建议存上来源、时间、业务标签检索时可以按 metadata 过滤这是纯向量检索做不到的。4.3 观察 LLM 的输出从 logits 到文本再看 LLM 这条链路。日常开发中你拿到的是生成好的文本看不到 logits。但 Spring AI 提供了底层接口可以拿到更多信息。Service public class ChatService { private final ChatModel chatModel; public ChatService(ChatModel chatModel) { this.chatModel chatModel; } // 普通调用拿到文本 public String chat(String prompt) { return chatModel.call(prompt); } // 流式调用观察逐 token 生成过程 public void streamChat(String prompt) { FluxString stream chatModel.stream(prompt); stream.doOnNext(token - System.out.print(token)) .blockLast(); } }流式输出是理解“LLM 逐 token 生成”最直观的方式。你会看到文字一个一个蹦出来每个 token 就是一次 logits 计算 采样的结果。这跟 embedding 一次性输出一个固定向量是完全不同的节奏。如果你想更深入可以看ChatResponse里的 metadata里面包含 token 使用量、finish reason 等信息。虽然 Spring AI 默认不暴露原始 logits这是合理的抽象但理解底层是 logits 采样能帮你调好 temperature、top_p 这些参数。4.4 把两条链路串成 RAGRAG 的本质就是把 embedding 链路和 LLM 链路接起来public String ragAnswer(String question) { // 第一步用 embedding 检索相关文档 ListDocument docs embeddingService.search(question, 5); // 第二步把检索结果拼进 prompt String context docs.stream() .map(Document::getText) .collect(Collectors.joining(\n---\n)); String prompt 请根据以下资料回答问题资料中没有的信息不要编造。 资料 %s 问题%s .formatted(context, question); // 第三步交给 LLM 生成 return chatService.chat(prompt); }这段代码虽然短但把两种向量的分工体现得淋漓尽致embedding 负责“找”LLM 负责“答”。找的时候用相似度答的时候用生成。面试时能把这条链路讲清楚再点出“两种向量各司其职、不可互换”基本就到位了。5. 生产环境里最容易翻车的五个点5.1 混用不同模型的 embedding 向量这是 RAG 项目第一大坑。开发时用 A 模型建库上线换成 B 模型或者中途升级了 embedding 模型版本结果检索全乱套。因为不同模型的语义空间不兼容向量之间算相似度毫无意义。解决方案embedding 模型一旦确定就锁死版本写进配置中心。如果必须换模型整个向量库必须重建没有捷径。重建时记得做灰度新旧库并行一段时间对比召回效果。5.2 把 LLM 隐藏态当 embedding 用有些团队为了省一个模型想直接用 LLM 的中间层输出做检索。理论上可行但实测效果通常差一截而且 LLM 前向一次的成本远高于专用 embedding 模型。检索场景要的是快和准不是生成能力。解决方案检索归检索生成归生成各用各的模型。这是被大量实践验证过的最优解别为了省事走弯路。5.3 忽略 token 长度限制embedding 模型和 LLM 都有最大 token 限制。embedding 模型通常 512 到 8192 tokenLLM 可能 8k 到 128k。超长文本直接截断会导致语义丢失检索召回不准。解决方案入库前做分块chunking按语义边界切比如按段落、按标题层级。块大小控制在 200 到 500 token块之间留一点重叠overlap避免切断上下文。Spring AI 的TokenTextSplitter可以直接用。5.4 相似度阈值拍脑袋定similarityThreshold设 0.7 还是 0.8很多人凭感觉。设高了召回不足设低了噪声太多。不同 embedding 模型的相似度分布不一样没有万能值。解决方案拿一批真实 query 做离线评估画出相似度分布曲线找到召回率和准确率的平衡点。这个工作一次做好后面长期受益。5.5 忘了向量维度和索引的匹配建向量库时表的向量维度必须和 embedding 模型输出维度一致。用 1536 维的模型建了 768 维的表写入直接报错。另外向量索引如 HNSW、IVFFlat的参数也要根据数据量调数据量小的时候暴力检索反而更快。解决方案建表前确认模型维度索引参数参考 pgvector 官方建议。数据量低于 10 万条时可以先不建索引用精确检索简单可靠。6. 面试怎么答才能拿高分6.1 一句话抓住本质如果面试官只给你 30 秒你就说“Embedding 向量是文本的语义坐标用于相似度计算和检索LLM 输出向量是下一个 token 的概率分布用于生成。两者维度、用途、生成方式都不同不能互换。”这句话信息密度高直接命中要害。6.2 主动画出数据流面试时如果能主动画一下数据流加分很多。embedding 链路是文本 → tokenize → 编码器 → 池化 → 固定向量 → 向量库。LLM 链路是文本 → tokenize → 解码器 → 最后一层隐藏态 → LM Head → logits → softmax → 采样 → 下一个 token。两条链路一对比结构差异一目了然。6.3 用 RAG 场景收尾最后用 RAG 把两者串起来说明它们在真实系统里怎么协作。这样回答既有理论深度又有工程落地面试官会觉得你是真做过项目不是背题。6.4 常见追问的应对面试官可能追问“那 LLM 能不能生成 embedding”可以答能但通常不划算专用 embedding 模型更快更准更便宜。追问“向量维度越高越好吗”答不是维度高存储和计算成本都上升边际收益递减要按场景权衡。追问“怎么评估 embedding 质量”答用检索任务的召回率、MRR 等指标在真实业务数据上评估别只看公开榜单。7. 我在实际项目里踩过的坑和几点体会说几个文档里不会写、但实际做项目一定会遇到的细节。第一个坑是批量 embedding 的并发问题。批量接口虽然快但一次塞太多文本请求体可能超限或者触发服务端的速率限制。我的做法是分批每批 50 到 100 条批之间加一点间隔配合重试。别一次性把几千条全怼进去大概率失败。第二个坑是向量库的写入性能。pgvector 在数据量大时逐条写入会非常慢。用批量插入并且把索引创建放到数据导入之后能快好几倍。索引边写边建性能损耗很大。第三个坑是相似度分数的解读。不同模型的余弦相似度分布不一样有的模型普遍偏高0.9 才算相关有的模型 0.7 就已经很相关了。别拿一个模型的阈值套到另一个模型上一定要重新标定。第四个体会是别过度迷信向量检索。纯向量检索对精确匹配比如订单号、专有名词不敏感。生产环境里向量检索 关键词检索的混合方案效果通常比纯向量好。Spring AI 也支持这种组合值得一试。最后一个建议把 embedding 和 LLM 当成两个独立的服务来治理。它们有不同的模型、不同的成本、不同的性能特征、不同的失败模式。分开监控、分开限流、分开降级系统才稳。把它们混在一起管理出问题时你连是检索挂了还是生成挂了都分不清。这道面试题之所以经典是因为它表面问概念实际考的是你对大模型数据流的整体理解。把两种向量的来龙去脉搞清楚RAG、Agent、微调这些上层应用你都能看得更透。