一、什么是向量?🧭
在深度学习的世界里,向量是最基础、也是最重要的概念之一。
它不仅用来表示数据,更贯穿了模型的输入、输出以及内部计算的每一个环节。下面我们从几个角度,把它彻底讲明白。
1. 基本定义
向量,本质上是一组有序排列的数字。这些数字被称为向量的「分量」或「元素」。
-
在二维空间里,一个点可以用「横坐标 + 纵坐标」两个数来表示;
-
在三维空间里,则需要「长、宽、高」三个数;
-
更一般地,在 n 维空间中,一个向量就是 n 个实数组成的序列。
2. 向量在机器学习中的作用
向量几乎撑起了机器学习的半边天:
-
特征表示:原始数据要先转换成数值形式,机器才能处理。这些特征值按顺序排好,就成了「特征向量」。
-
模型参数:很多算法内部都在学习「权重向量」。比如线性回归,就是在找一个最优的权重向量 w,让预测值和真实值的误差最小。
-
嵌入层(Embedding):在 NLP 领域,词向量、句向量被用来捕捉文本语义。训练得到的词嵌入,能真实反映词语之间的语义关系。
3. 向量的运算规则
向量支持加法、减法、标量乘法等基础运算,还有内积(点积)、外积(叉积)等特殊运算。
正是这些运算,构建起了复杂神经网络的底层骨架。
4. 向量化编程
为了提升效率,工程师往往会用「向量化」技术------一次性对整个数组做运算,而不是逐个元素循环处理。
这种方式极大加速了计算,也让代码更简洁优雅。
5. 向量空间模型
这是信息检索中的经典方法:把文档表示成由词汇项组成的向量,每个维度对应一个术语,值则反映该术语在文档中的重要程度。
于是,「文档相似度」就变成了「向量相似度」的计算问题。
"
一句话总结:向量是连接理论与实践的桥梁。理解了向量,你就握住了理解现代大模型的钥匙。🔑
二、向量的数学本质 📐
向量是既有大小、又有方向的量。在数学中,我们通常用「有向线段」来表示它------线段的长度代表大小,箭头的指向代表方向。

关于向量,有几条关键性质值得记住:
-
向量只有方向和大小 。等长、平行、同向的向量,因为大小和方向都一致,就是同一个向量,也叫相等向量。
-
两个向量相等可以用「=」连接。但向量之间没有大于或小于关系,不能用「>」「<」连接。
-
方向相同或相反的向量,叫作共线向量 ,也叫平行向量。
-
向量之间可以用「∥」或「⊥」连接。其中「a ∥b 」表示 a 与 b 共线,「a ⊥b」表示 a 与 b 所在直线互相垂直。
-
向量 a 的大小,又称长度、模长 ,简称模 ,记作 |a|。
-
模长是数值而非向量,所以可以用「>」「<」比较大小。
-
长度为 0 的向量叫作零向量。
-
零向量与任意向量平行。
-
长度为 1 个单位的向量叫作单位向量。
三、什么是向量化?🔄
一句话:向量化,就是把文档、字符等内容,转换成一组浮点数组。
"
下图来源于 Qdrant 官方文档(向量检索概述章节)。

四、实战示例:LangChain4j + Qdrant 💻
理论讲够了,接下来上真代码。我们参考 LangChain4j 官方示例(Qdrant Embedding Store Example),来跑通整条链路。
官方案例:环境搭建
新建模块,配置 pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.whc</groupId>
<artifactId>langChain4j-whc</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>langchain4j-whc-embedding</artifactId>
<packaging>jar</packaging>
<name>langchain4j-whc-embedding</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--langChain4j -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
</dependency>
<!-- 阿里百炼平台 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
</dependency>
<!--qdrant相关-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-qdrant</artifactId>
<version>1.5.0-beta11</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-embeddings-all-minilm-l6-v2</artifactId>
<version>1.5.0-beta11</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>qdrant</artifactId>
<version>1.19.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
核心代码(含详细注释)
在官方类的基础上稍作修改,并补充了完整注释,方便理解每一步在做什么:
public class QdrantEmbeddingStoreExample {
/**
* gRPC服务端口配置
* 用于连接向量数据库的gRPC通信端口
*/
private static int grpcPort = 6334;
/**
* 集合名称配置
* 指定向量数据库中使用的集合名称
*/
private static String collectionName = "langchain4j-whc";
/**
* 距离计算方式配置
* 设置向量相似度计算的距离度量方式为余弦距离
*/
private static Collections.Distance distance = Collections.Distance.Cosine;
/**
* 向量维度配置
* 指定向量的维度大小为384维
*/
private static int dimension = 384;
/**
* 数据库主机地址配置
* 向量数据库服务器的IP地址
*/
private static final String HOST = "192.168.100.130";
/**
* 数据库端口配置
* 向量数据库服务的端口号,与gRPC端口相同
*/
private static final int PORT = 6334;
/**
* 主函数,演示了使用Qdrant向量数据库进行文本嵌入存储和相似性搜索的完整流程
*
* @param args 命令行参数数组
* @throws ExecutionException 当异步执行出现异常时抛出
* @throws InterruptedException 当线程被中断时抛出
*/
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建Qdrant嵌入存储实例,用于存储和检索文本嵌入向量
EmbeddingStore<TextSegment> embeddingStore =
QdrantEmbeddingStore.builder()
.host(HOST)
.port(PORT)
.collectionName(collectionName)
.build();
// 创建Qdrant客户端,用于与Qdrant服务器进行通信
QdrantClient client =
new QdrantClient(
QdrantGrpcClient.newBuilder(HOST, PORT, false)
.build());
// 异步创建集合,定义向量的维度和距离计算方式
client
.createCollectionAsync(
collectionName,
Collections.VectorParams.newBuilder().setDistance(distance).setSize(dimension).build())
.get();
// 初始化嵌入模型,用于将文本转换为向量表示
EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel();
// 创建并存储第一个文本段落的嵌入向量
TextSegment segment1 = TextSegment.from("I've been to France twice.");
Embedding embedding1 = embeddingModel.embed(segment1).content();
embeddingStore.add(embedding1, segment1);
// 创建并存储第二个文本段落的嵌入向量
TextSegment segment2 = TextSegment.from("New Delhi is the capital of India.");
Embedding embedding2 = embeddingModel.embed(segment2).content();
embeddingStore.add(embedding2, segment2);
// 执行相似性搜索,查找与查询文本最相似的文本段落
Embedding queryEmbedding = embeddingModel.embed("Did you ever travel abroad?").content();
EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(queryEmbedding)
.maxResults(1)
.build();
List<EmbeddingMatch<TextSegment>> matches = embeddingStore.search(embeddingSearchRequest).matches();
EmbeddingMatch<TextSegment> embeddingMatch = matches.get(0);
// 输出匹配结果的相似度分数和文本内容
System.out.println(embeddingMatch.score());
System.out.println(embeddingMatch.embedded().text());
}
}
这里的嵌入模型用的是 all-MiniLM-L6-v2。
"
💡 all-MiniLM-L6-v2 是 MiniLM 系列的一个小型语言模型,通过「知识蒸馏」技术从更大的模型压缩而来。它的目标是------在保持较高性能的同时,尽可能降低计算资源需求,非常适合本地和轻量场景。
✅ 测试一下
代码里把两条英文句子转成了向量并保存下来:

然后提问:哪句话更适合回答 "Did you ever travel abroad?"

结果如下:
0.7378319074630284
I've been to France twice.
「我去过法国两次」------完美命中,模型准确理解了「出国旅行」的语义。🎯
五、接口方式:接入阿里百炼向量模型 🔌
模型选择
这里我们选用阿里云百炼平台的向量模型 text-embedding-v3。


配置类
@Slf4j
@Getter
@Configuration
public class LlmConfig {
@Value("${ai.dashScope.apiKey}")
private String dashScopeApiKey;
@Value("${ai.dashScope.modelName}")
private String dashScopeModelName;
@Value("${ai.dashScope.baseUrl}")
private String dashScopeBaseUrl;
@Value("${qdrant.host}")
private String qdrantHost;
@Value("${qdrant.port}")
private int qdrantPort;
@Value("${qdrant.collectionName}")
private String qdrantCollectionName;
/**
* 创建EmbeddingModel 向量模型
*
* @return EmbeddingModel
*/
@Bean
public EmbeddingModel embeddingModel() {
return OpenAiEmbeddingModel.builder()
.apiKey(dashScopeApiKey)
.modelName(dashScopeModelName)
.baseUrl(dashScopeBaseUrl)
.build();
}
/**
* 创建Qdrant客户端
*
* @return QdrantClient
*/
@Bean
public QdrantClient qdrantClient() {
QdrantGrpcClient.Builder grpcClientBuilder =
QdrantGrpcClient.newBuilder(qdrantHost, qdrantPort, false);
return new QdrantClient(grpcClientBuilder.build());
}
@Bean
public EmbeddingStore<TextSegment> embeddingStore() {
return QdrantEmbeddingStore.builder()
.host(qdrantHost)
.port(qdrantPort)
.collectionName(qdrantCollectionName)
.build();
}
}
YAML 配置
port: 9008
servlet:
encoding:
enabled: true
force: true
charset: UTF-8
spring:
application:
name: langchain_whc_embedding
ai:
dashScope:
# 配置dashScope
apiKey: ${AI_DASHSCOPE_API_KEY}
modelName: ${AI_DASHSCOPE_MODEL_NAME}
baseUrl: ${AI_DASHSCOPE_BASE_URL}
qdrant:
host: ${QDRANT_HOST}
port: ${QDRANT_PORT}
collectionName: ${QDRANT_COLLECTION_NAME}
Controller
@Slf4j
@RestController
@RequiredArgsConstructor
public class EmbeddingController {
private final LlmConfig llmConfig;
private final EmbeddingModel embeddingModel;
private final QdrantClient qdrantClient;
private final EmbeddingStore<TextSegment> embeddingStore;
@GetMapping(value = "/embedding/createCollection")
public void createCollection() {
var vectorParams = Collections.VectorParams.newBuilder()
.setDistance(Collections.Distance.Cosine)
.setSize(1024)
.build();
qdrantClient.createCollectionAsync(llmConfig.getQdrantCollectionName(), vectorParams);
}
}
✅ 测试新增 Collection
访问接口:http://localhost:9008/embedding/createCollection

测试结果:成功新增了一个 Collection。

✅ 测试新增 Points
在 Controller 中增加接口,这次我们塞一首自创的词进去:
/**
* 处理GET请求,向嵌入存储中添加文本段落的嵌入向量
*
* @return 添加操作的结果标识符字符串
*/
@GetMapping(value = "/embedding/add")
public String add() {
// 定义《永遇乐・师徒取经》词牌的文本内容作为待处理文本
String prompt = """
永遇乐・师徒取经
万里西行,风沙漫卷,云路迢递。玄奘慈眉,凝眸西向,志比金石厉。
悟空火眼,金箍掣处,妖魅现形惊悸。笑猪痴、贪眠恋栈,也随杖履千里。
沙憎谨肃,行囊稳护,默默担当行李。九九磨难,千般险厄,终把真经觅。东归胜果,流传千古,漫说佛门真谛。
到如今、传奇犹在,世人共记。
""";
// 将文本内容转换为文本段落对象,并添加元数据信息
TextSegment segment = TextSegment.from(prompt);
segment.metadata().put("author", "robot");
segment.metadata().put("title", "永遇乐・师徒取经");
// 生成文本段落的嵌入向量
Embedding embedding = embeddingModel.embed(segment).content();
// 将嵌入向量和文本段落添加到嵌入存储中
String result = embeddingStore.add(embedding, segment);
log.info(result);
return result;
}
测试效果:

"
你可以多塞几条 points 进去,让检索结果更有对比性。
✅ 测试查询
/**
* 处理/embedding/query1路径的GET请求,用于查询与"写爱情的诗"相关的嵌入向量匹配结果
* 该方法不接受参数,无返回值
*/
@GetMapping(value = "/embedding/query")
public void query1() {
// 生成查询文本"写爱情的诗"的嵌入向量
Embedding queryEmbedding = embeddingModel.embed("与爱情相关的").content();
// 构建嵌入搜索请求
EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(queryEmbedding)
.build();
// 执行嵌入向量搜索
EmbeddingSearchResult<TextSegment> searchResult = embeddingStore.search(embeddingSearchRequest);
// 输出查询结果
searchResult.matches().forEach(match -> {
log.info("\n匹配度得分---->"+ match.score());
log.info("\n"+match.embedded().text());
});
}
测试结果有点意思------排序跟直觉略有出入:

当然,也可以加上过滤条件,精确锁定某个标题:
EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(queryEmbedding)
.filter(metadataKey("title").isEqualTo("鹧鸪天・别绪"))
.build();

六、写在最后 📝
从「向量是什么」到「用 LangChain4j + Qdrant 跑通一整套向量检索」,我们走完了从理论到工程的完整闭环:
-
理论层:向量是有序数字组,是大模型理解世界的语言;
-
工程层:向量化把文本变成浮点数组,向量数据库负责高效存储与相似度检索;
-
实践层:无论是本地的 all-MiniLM-L6-v2,还是云端的 text-embedding-v3,都能轻松接入。
理解了向量,你就理解了大模型「思考」的底层方式。剩下的,就是动手把它跑起来。💪