Java大模型开发入门 (9/15):连接外部世界(中) - 向量嵌入与向量数据库

前言

在上一篇文章中,我们成功地将一篇长文档加载并分割成了一系列小的文本片段(TextSegment)。我们现在有了一堆“知识碎片”,但面临一个新问题:计算机如何理解这些碎片的内容,并找出与用户问题最相关的片段呢?

如果用户问“X-Wing的设置要求是什么?”,我们不能只用简单的关键词匹配(比如搜索“setup”或“requirements”),因为用户可能会用不同的词语提问(“我该如何安装X-Wing?”)。我们需要一种能够理解**语义(Semantic Meaning)**的搜索方式。

这就是文本嵌入(Text Embedding)向量数据库(Vector Database) 发挥作用的地方。今天,我们将深入RAG技术的心脏地带,学习如何将文本转化为向量,并将其存储起来以便进行高效的语义搜索。

第一部分:什么是文本嵌入(Embedding)?语义的“指纹”

想象一下图书馆里的书。图书管理员不会记住每本书的每一个字,但他们知道哪些书是关于“历史”的,哪些是关于“科幻”的,哪些是关于“烹饪”的。他们将书的内容“映射”到了一个类别体系中。

文本嵌入模型(Embedding Model)做的是类似但更精细的事情。它是一个专门的AI模型,它的唯一工作就是读取一段文本,然后输出一个由几百上千个数字组成的列表——向量(Vector)

这个向量,就是这段文本在多维语义空间中的“坐标”或“指纹”。

这个语义空间非常神奇。在其中,意思相近的文本,它们的向量在空间中的距离也相近。经典的例子是:
vector("King") - vector("Man") + vector("Woman") ≈ vector("Queen")

通过将所有文档片段都转换成向量,我们就可以通过计算向量之间的“距离”来判断文本之间的语义相似度,从而实现比关键词搜索高级得多的语义搜索。

在LangChain4j中,这个功能由EmbeddingModel接口来抽象。

第二部分:什么是向量数据库(Vector Store)?向量的“家”

现在我们有能力将所有文本片段都转换成向量了,但我们该把这些向量存放在哪里,又如何高效地搜索它们呢?

这就是向量数据库(或称为向量存储,EmbeddingStore)的作用。

如果说嵌入模型是“翻译官”,把文本翻译成向量;那么向量数据库就是专门为这些向量建立的“高维空间索引系统”。

它允许我们:

  1. 存储大量的向量及其关联的原始文本。
  2. 当给定一个新的查询向量时,能以极高的效率找出数据库中与它最相似的N个向量(这个过程通常被称为“最近邻搜索”)。

LangChain4j通过EmbeddingStore接口支持多种向量数据库,从简单的内存存储到专业的分布式数据库:

  • InMemoryEmbeddingStore: 完全在内存中运行,非常适合快速原型开发和测试,无需任何外部依赖。
  • Chroma, Milvus, Pinecone, Weaviate: 生产级的、可独立部署的向量数据库,支持海量数据和高并发查询。

在今天的教程中,我们将从最简单的InMemoryEmbeddingStore开始。

第三部分:实战 - 将文档片段嵌入并存储

我们将继续完善上一篇的DocumentService,为其增加嵌入和存储的功能。

  1. 确认依赖和配置
    好消息是,我们之前添加的langchain4j-open-ai-spring-boot-starter已经包含了嵌入模型的功能。现在还需要修改application.prroperties增加embedding模型配置
langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY:your-api-key-here}
langchain4j.open-ai.chat-model.base-url=https://yibuapi.com/v1/
langchain4j.open-ai.chat-model.model-name=gpt-4o-mini
langchain4j.open-ai.chat-model.temperature=0.7
langchain4j.open-ai.chat-model.max-tokens=1024langchain4j.open-ai.embedding-model.model-name=text-embedding-ada-002
  1. 修改DocumentService
    我们将注入EmbeddingModelEmbeddingStoreIngestor,并创建一个InMemoryEmbeddingStore的Bean。

    第一步:在config/LangChain4jConfig.java中创建EmbeddingModel Bean

    package com.example.aidemoapp.config;
    // ... other imports
    import dev.langchain4j.store.embedding.EmbeddingStore;
    import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;@Configuration
    public class LangChain4jConfig {// ... chatLanguageModel 和 chatMemoryProvider Beans ...@Beanpublic EmbeddingModel embeddingModel() {// 通常嵌入模型也使用相同的api-key和base-url// 注意:OpenAI有专门的嵌入模型名称,// 比如 "text-embedding-ada-002" 。// 如果不指定,LangChain4j可能会使用一个默认值。// 为清晰起见,最好在properties中也定义它。return OpenAiEmbeddingModel.builder().apiKey(apiKey).baseUrl(baseUrl)// 推荐在application.properties中添加:// langchain4j.open-ai.embedding-model.model-name=text-embedding-ada-002.modelName(embeddingModelName).build();}
    }
    

    第二步:改造DocumentService.java

    package com.example.aidemoapp.service;import dev.langchain4j.data.document.Document;
    import dev.langchain4j.data.document.DocumentSplitter;
    import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
    import dev.langchain4j.data.document.parser.TextDocumentParser;
    import dev.langchain4j.data.document.splitter.DocumentSplitters;
    import dev.langchain4j.data.segment.TextSegment;
    import dev.langchain4j.model.embedding.EmbeddingModel;
    import dev.langchain4j.store.embedding.EmbeddingStore;
    import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Service;import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.List;@Service
    @RequiredArgsConstructor
    public class DocumentService {// 注入由Starter自动创建的EmbeddingModelprivate final EmbeddingModel embeddingModel;// 注入我们自己创建的EmbeddingStore Beanprivate final EmbeddingStore embeddingStore;public void loadSplitAndEmbed() {Path documentPath = Paths.get("src/main/resources/documents/product-info.txt");Document document = FileSystemDocumentLoader.loadDocument(documentPath, new TextDocumentParser());// 2. 将文档分割成片段DocumentSplitter splitter = DocumentSplitters.recursive(300, 10);List<TextSegment> segments = splitter.split(document);System.out.println("Document split into " + segments.size() + " segments.");// 3. 将片段嵌入并存储到向量数据库中// LangChain4j提供了一个方便的EmbeddingStoreIngestor来处理这个流程EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder().documentSplitter(splitter) // 可以在这里也指定分割器.embeddingModel(embeddingModel).embeddingStore(embeddingStore).build();// 开始摄入文档ingestor.ingest(document);System.out.println("Document ingested and stored in the embedding store.");}
    }
    

    代码解析

    • 我们注入了EmbeddingModelEmbeddingStore
    • 我们使用了EmbeddingStoreIngestor,这是一个高级工具,它将分割、嵌入和存储这三个步骤打包成了一个简单的.ingest()方法调用,非常方便。
    • 运行loadSplitAndEmbed()方法后,我们的InMemoryEmbeddingStore中就包含了文档所有片段的向量信息。
第四部分:进行第一次语义搜索

光存储还不够,我们需要验证一下检索效果。让我们添加一个搜索方法。

// 在 DocumentService.java 中继续添加
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.store.embedding.EmbeddingMatch;// ...public List<String> search(String query) {System.out.println("\n--- Performing search for query: '" + query + "' ---");// 1. 将用户问题也进行嵌入,得到查询向量Response<Embedding> queryEmbedding = embeddingModel.embed(query);// 2. 在向量存储中查找最相关的N个匹配项// 参数1: 查询向量// 参数2: 返回的最大结果数EmbeddingSearchRequest request = EmbeddingSearchRequest.builder().queryEmbedding(queryEmbedding.content()).build();List<EmbeddingMatch<TextSegment>> relevant = embeddingStore.search(request).matches();// 3. 打印结果System.out.println("Found " + relevant.size() + " relevant segments:");relevant.forEach(match -> {System.out.println("--------------------");System.out.println("Score: " + match.score()); // 相似度得分System.out.println("Text: " + match.embedded().text()); // 原始文本});return relevant.stream().map(match -> match.embedded().text()).collect(Collectors.toList());}

现在,你可以创建一个测试端点来调用loadSplitAndEmbed(),然后再调用search("What are the setup requirements for X-Wing?")。你会看到,即使你的问题中没有“RAM”或“CPU”这些词,返回的最相关的片段也正是包含“16GB RAM and a 4-core CPU”的那一段!这就是语义搜索的威力。

总结

今天,我们深入了RAG技术的核心腹地。我们学习了:

  • **文本嵌入(Embedding)**如何将文字转换成代表其语义的数学向量。
  • **向量数据库(Vector Store)**如何存储这些向量并进行高效的相似度搜索。
  • 如何使用LangChain4j的EmbeddingModelEmbeddingStoreIngestor将文档片段向量化并存入内存向量库。
  • 如何执行一次真正的语义搜索,并找到了与问题最相关的文档片段。

我们已经成功地“开卷”并“找到了答案所在的页面”。现在,我们离终点只差最后一步:将找到的这些“参考资料”连同原始问题一起,交给大语言模型,让它用自然、流畅的语言“总结”出最终的答案。


下一篇预告:
Java大模型开发入门 (10/15):连接外部世界(下) - 端到端构建完整的RAG问答系统》—— 我们将整合所有学过的知识,打通RAG的“最后一公里”。我们将把检索到的文本片段与用户问题组合起来,发送给ChatLanguageModel,最终构建一个可以针对我们私有文档进行智能问答的完整端到端应用!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/87109.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows下MySQL安装全流程图文教程及客户端使用指南(付整合安装包)

本教程是基于5.7版本安装&#xff0c;5.7和8.0的安装过程大差不差 安装包「windows上mysql中安装包资源」 链接&#xff1a;https://pan.quark.cn/s/de275899936d 一、安装前的准备 1.1 获取 MySQL 安装程序 官网 前往 MySQL 官方下载页面&#xff0c;下载适用于 Windows 系…

笔记 软件工程复习

第一章 软件工程学概述 1.1 软件危机&#xff08;Software Crisis&#xff09; 概念 定义&#xff1a;软件危机指在计算机软件开发与维护过程中遇到的一系列严重问题&#xff0c;源于1960年代软件复杂度激增与传统开发方法失效的矛盾。 本质&#xff1a;软件规模扩大 → 开…

GaussDB创建数据库存储

示例一&#xff1a; 下面是一个简单的GaussDB存储过程示例&#xff1a; –创建一个存储过程。 CREATE OR REPLACE PROCEDURE prc_add (param1 IN INTEGER,param2 IN OUT INTEGER ) AS BEGINparam2: param1 param2;dbe_output.print_line(result is: ||to_char(param…

基于51单片机的校园打铃及灯控制系统

目录 具体实现功能 设计介绍 资料内容 全部内容 资料获取 具体实现功能 具体功能&#xff1a; &#xff08;1&#xff09;实时显示当前时间&#xff08;年月日时分秒星期&#xff09;&#xff0c;LED模式指示灯亮。 &#xff08;2&#xff09;按下“打铃”和“打铃-”按键…

PHP+mysql雪里开轻量级报修系统 V1.0Beta

# PHP雪里开轻量级报修系统 V1.0Beta ## 简介 这是一个基于PHP7和MySQL5.6的简易报修系统&#xff0c;适用于学校、企业等机构的设备报修管理。 系统支持学生提交报修、后勤处理报修以及系统管理员管理用户和报修记录。 初代版本V1.0&#xff0c;尚未实际业务验证&#xff0c;…

XCTF-misc-base64÷4

拿到一串字符串 666C61677B45333342374644384133423834314341393639394544444241323442363041417D转换为字符串得到flag

Mini DeepSeek-v3训练脚本学习

Mini DeepSeek-v3 训练脚本详细技术说明(脚本在文章最后) &#x1f4cb; 概述 这是一个实现了Mini DeepSeek-v3大语言模型的训练脚本&#xff0c;集成了多项先进的深度学习技术。该脚本支持自动GPU选择和分布式训练&#xff0c;适合在多GPU环境下训练Transformer模型。 &…

FPGA 的硬件结构

FPGA 的基本结构分为5 部分&#xff1a;可编程逻辑块&#xff08;CLB&#xff09;、输入/输出块&#xff08;IOB&#xff09;、逻辑块之间的布线资源、内嵌RAM 和内嵌的功能单元。 &#xff08;1&#xff09;可编程逻辑块&#xff08;CLB&#xff09; 一个基本的可编程逻辑块由…

算法专题八: 链表

1.两数相加 题目链接&#xff1a;2. 两数相加 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode…

5G+边缘计算推动下的商品详情API低延迟高效率新方案

在电商行业&#xff0c;商品详情API的性能直接关系到用户体验与平台竞争力。传统云计算模式在处理高并发请求时&#xff0c;常面临网络延迟高、带宽成本大等问题。而5G与边缘计算的结合&#xff0c;为商品详情API的低延迟高效率提供了新方案。本文将深入探讨这一新方案&#xf…

【Python教程】CentOS系统下Miniconda3安装与Python项目后台运行全攻略

一、引言 为了在CentOS系统上高效地开发和运行Python项目&#xff0c;我们常常需要借助Miniconda3来管理Python环境。本文将详细介绍如何在CentOS系统上安装Miniconda3&#xff0c;并将Python项目部署到后台运行。 二、Miniconda3和CentOS系统介绍 Miniconda3介绍 Minicond…

【读点论文】A Survey on Open-Set Image Recognition

A Survey on Open-Set Image Recognition Abstract 开集图像识别(Open-set image recognition&#xff0c;OSR)旨在对测试集中已知类别的样本进行分类&#xff0c;并识别未知类别的样本&#xff0c;在许多实际应用中支持鲁棒的分类器&#xff0c;如自动驾驶、医疗诊断、安全监…

使用DuckDB查询DeepSeek历史对话

DeepSeek网页版在左下角个人信息/系统设置的账号管理页签中有个“导出所有历史对话”功能&#xff0c;点击“导出”&#xff0c;片刻就能生成一个deepseek_data-2025-06-14.zip的文件&#xff0c;里面有2个json文件&#xff0c;直接用文本编辑器查看不太方便。 而用DuckDB查询却…

多线程下 到底是事务内部开启锁 还是先加锁再开启事务?

前言 不知大家是否有观察到一个最常见的错误&#xff1a; 先开启事务&#xff0c;然后针对资源加锁&#xff0c;操作资源&#xff0c;然后释放锁&#xff0c;最后提交事务 你是否发现了在这样的场景下会出现并发安全的问题&#xff1f; &#xff08;提示&#xff1a;一个线程A…

Javascript解耦,以及Javascript学习网站推荐

一、学习网站推荐 解构 - JavaScript | MDN 界面如下&#xff0c;既有知识点&#xff0c;也有在线编译器执行代码。初学者可以看看 二、Javascript什么是解构 解构语法是一种 Javascript 语法。可以将数组中的值或对象的属性取出&#xff0c;赋值给其他变量。它可以在接收数…

Java大模型开发入门 (11/15):让AI自主行动 - 初探LangChain4j中的智能体(Agents)

前言 在过去的十篇文章里&#xff0c;我们已经打造出了一个相当强大的AI应用。它有记忆&#xff0c;能进行多轮对话&#xff1b;它有知识&#xff0c;能通过RAG回答关于我们私有文档的问题。它就像一个博学的“学者”&#xff0c;你可以向它请教任何在其知识范围内的问题。 但…

Qt KDReports详解与使用

Qt KDReports详解与使用 一、KD Reports 简介二、安装与配置三、核心功能与使用1、创建基础报表2、添加表格数据3、导出为 PDF4、XML报表定义 四、高级功能1、动态数据绑定2、自定义图表3、模板化设计4、页眉页脚设置 五、常见问题六、总结七、实际应用示例&#xff1a;发票生成…

Spring Cloud 原生中间件

&#x1f4dd; 代码记录 Consul&#xff08;服务注册与发现 分布式配置管理&#xff09; 拥有服务治理功能&#xff0c;实现微服务之间的动态注册与发现 ❌不在使用Eureka&#xff1a;1. 停更进维 2. 注册中心独立且和微服务功能解耦 Consul官网 Spring官方介绍 三个注册中…

CMake实践: 以开源库QSimpleUpdater为例,详细讲解编译、查找依赖等全过程

目录 1.环境和工具 2.CMake编译 3.查找依赖文件 3.1.windeployqt 3.2.dumpbin 4.总结 相关链接 QSimpleUpdater&#xff1a;解锁 Qt 应用自动更新的全新姿势-CSDN博客 1.环境和工具 windows 11, x64 Qt5.12.12或Qt5.15.2 CMake 4.0.2 干净的windows 7&#xff0c;最好是…

WordToCard制作高考志愿填报攻略小卡片【豆包版】

一、什么是WordToCard WordToCard是一个免费的工具&#xff0c;能够将Word文档自动转换为美观的知识卡片或图文海报。以下是它的主要特点&#xff1a; 功能优势 格式支持&#xff1a;支持标题、列表、表格、LaTeX公式等多种格式。模板丰富&#xff1a;提供多种风格的模板&am…