Spring AI 和 Elasticsearch 作为你的向量数据库

作者:来自 Elastic Josh Long, Philipp Krenn 及 Laura Trotta

使用 Spring AI 和 Elasticsearch 构建一个完整的 AI 应用程序。

Elasticsearch 原生集成了业界领先的生成式 AI 工具和服务提供商。查看我们关于超越 RAG 基础或使用 Elastic 向量数据库构建生产级应用的网络研讨会。

为了为你的用例构建最佳搜索解决方案,现在就开始免费云试用,或者在本地机器上尝试 Elastic。


Spring AI 现在已正式发布,第一个稳定版本 1.0 已可在 Maven Central 下载。让我们马上开始使用它,结合你喜欢的 LLM 和我们最喜欢的向量数据库,构建一个完整的 AI 应用程序。

什么是 Spring AI?

Spring AI 1.0 是一个面向 Java 的全面 AI 工程解决方案,经过一段时间的开发并受到 AI 领域快速发展的推动,如今正式发布。这个版本为 AI 工程师带来了许多关键的新功能。

Java 和 Spring 正处于迎接 AI 浪潮的有利位置。很多公司都在使用 Spring Boot,这使得将 AI 集成到现有系统中变得非常简单。你基本上可以毫不费力地将业务逻辑和数据直接连接到那些 AI 模型上。

Spring AI 支持多种 AI 模型和技术,例如:

  • 图像模型:根据文本提示生成图像。
  • 转录模型:将音频源转换为文本。
  • 嵌入模型:将任意数据转换为向量,这是一种针对语义相似性搜索优化的数据类型。
  • 聊天模型:这些你应该很熟悉!你肯定在哪儿已经和其中一个简单聊过几句了。

聊天模型是当前 AI 领域最受关注的部分,而且确实如此,它们非常强大!你可以让它们帮你修改文档或者写一首诗。(只是暂时别让它们讲笑话……)它们很厉害,但也确实存在一些问题。

Spring AI 应对 AI 挑战的解决方案

所示图片已获得 Spring AI 团队负责人 Mark Pollack 博士的许可使用。

让我们来看看这些问题以及 Spring AI 中的解决方案。

问题解决方案
一致性

聊天模型思想开放,容易分心

你可以给它们一个 system prompt 来控制它们整体的行为和结构

内存

AI 模型没有记忆,所以它们无法将同一个用户的多条消息关联起来

你可以给它们一个记忆系统来存储对话中相关的部分

隔离

AI 模型生活在隔离的小沙盒中,但如果你给它们访问工具的权限——当它们认为有必要时可以调用的函数,它们可以做非常惊人的事情。

Spring AI 支持工具调用,这让你可以告诉 AI 模型它环境中的工具,之后它可以请求你调用这些工具。这种多轮交互会为你透明地处理。

私有数据

AI 模型很智能,但它们不是无所不知!它们不知道你专有数据库中的内容 —— 而且我们也认为你不会想让它们知道!

你需要通过填充 prompts 来指导它们的回答 —— 基本上是用强大的字符串拼接操作符,在模型查看问题之前,把文本放进请求里。可以看作是背景信息。你怎么决定什么该发送,什么不该发送?用 vector store 选择只有相关的数据,再发送给模型。这叫做检索增强生成,或者 RAG。

幻觉

AI chat 模型喜欢聊天!有时候它们非常自信,以至于会编造内容。

你需要使用评估 —— 用一个模型验证另一个模型的输出——来确认结果是否合理。

当然,没有哪个 AI 应用是孤立的。现代 AI 系统和服务在与其他系统和服务集成时表现最佳。Model Context Protocol (MCP) 使你能够将 AI 应用连接到其他基于 MCP 的服务,无论它们使用什么语言编写。你可以将这些组合成推动更大目标的 agentic 工作流。

最棒的是?你可以在熟悉的 Spring Boot 习惯用法和抽象基础上完成这一切:Spring Initializr 上几乎所有功能都有方便的 starter 依赖。

Spring AI 提供方便的 Spring Boot 自动配置,让你享受约定优于配置的体验。Spring AI 还支持通过 Spring Boot 的 Actuator 和 Micrometer 项目进行可观察性。它还能很好地兼容 GraalVM 和虚拟线程,帮助你构建超快、高效且可扩展的 AI 应用。

为什么选择 Elasticsearch

Elasticsearch 是一个全文搜索引擎,你可能已经知道了。那么为什么我们用它来做这个项目?因为它也是一个向量存储!而且非常不错,数据和全文本紧密存放。其他显著优点:

  • 超级容易设置

  • 开源

  • 横向可扩展

  • 你们组织的大部分自由格式数据可能已经存在 Elasticsearch 集群中

  • 功能完善的搜索引擎能力

  • 完全集成在 Spring AI 中!

综合考虑,Elasticsearch 满足作为优秀向量存储的所有条件,所以让我们开始设置并构建应用吧!

开始使用 Elasticsearch

我们需要 Elasticsearch 和 Kibana,Kibana 是你用来与数据库中数据交互的 UI 控制台。

多亏了 Docker 镜像和 Elastic.co 首页的便利,你可以在本地机器上试用所有东西。去那里,向下滚动找到 curl 命令,运行它并直接输入你的终端:

 curl -fsSL https://elastic.co/start-local | sh ______ _           _   _      |  ____| |         | | (_)     | |__  | | __ _ ___| |_ _  ___ |  __| | |/ _` / __| __| |/ __|| |____| | (_| \__ \ |_| | (__ |______|_|\__,_|___/\__|_|\___|
-------------------------------------------------
🚀 Run Elasticsearch and Kibana for local testing
-------------------------------------------------
ℹ️  Do not use this script in a production environment
⌛️ Setting up Elasticsearch and Kibana v9.0.0...
- Generated random passwords
- Created the elastic-start-local folder containing the files:- .env, with settings- docker-compose.yml, for Docker services- start/stop/uninstall commands
- Running docker compose up --wait
[+] Running 25/26✔ kibana_settings Pulled                                                 16.7s ✔ kibana Pulled                                                          26.8s ✔ elasticsearch Pulled                                                   17.4s                                                                     
[+] Running 6/6✔ Network elastic-start-local_default             Created                 0.0s ✔ Volume "elastic-start-local_dev-elasticsearch"  Created                 0.0s ✔ Volume "elastic-start-local_dev-kibana"         Created                 0.0s ✔ Container es-local-dev                          Healthy                12.9s ✔ Container kibana_settings                       Exited                 11.9s ✔ Container kibana-local-dev                      Healthy                21.8s 
🎉 Congrats, Elasticsearch and Kibana are installed and running in Docker!
🌐 Open your browser at http://localhost:5601Username: elasticPassword: w1GB15uQ
🔌 Elasticsearch API endpoint: http://localhost:9200
🔑 API key: SERqaGlKWUJLNVJDODc1UGxjLWE6WFdxSTNvMU5SbVc5NDlKMEhpMzJmZw==
Learn more at https://github.com/elastic/start-local
➜  ~ 

这将会拉取并配置 Elasticsearch 和 Kibana 的 Docker 镜像,几分钟后你就可以在本地机器上运行它们,连接凭据也会一起提供。

你还会得到两个不同的 URL 用来与你的 Elasticsearch 实例交互。按照提示操作,打开浏览器访问 http://localhost:5601。

注意:控制台上打印的用户名 elastic 和密码:你登录时需要用到它们(在上面的示例输出中,用户名和密码分别是 elastic 和 w1GB15uQ)。

整合应用

访问 Spring Initializr 页面,生成一个包含以下依赖项的新 Spring AI 项目:

  • Elasticsearch Vector Store

  • Spring Boot Actuator

  • GraalVM

  • OpenAI

  • Web

确保选择最新版本的 Java(理想是 Java 24 —— 写本文时是这个版本 —— 或更高)以及你选择的构建工具。这里的示例使用 Apache Maven。

点击 Generate,下载并解压项目,然后导入到你喜欢的 IDE 中。(我们用的是 IntelliJ IDEA。)

首先,指定 Spring Boot 应用的连接详情。在 application.properties 中写入以下内容:

spring.elasticsearch.uris=http://localhost:9200
spring.elasticsearch.username=elastic
spring.elasticsearch.password=w1GB15uQ

我们还会使用 Spring AI 的 vector store 功能来初始化 Elasticsearch 端所需的数据结构,所以请指定:

spring.ai.vectorstore.elasticsearch.initialize-schema=true

在这个演示中我们将使用 OpenAI,具体是 Embedding ModelChat Model(你可以使用任何 Spring AI 支持的服务)。

Embedding Model 用于在将数据存入 Elasticsearch 之前创建数据的向量。要使用 OpenAI,我们需要指定 API key:

spring.ai.openai.api-key=...

你可以将它定义为环境变量,比如 SPRING_AI_OPENAI_API_KEY,避免将凭证写入源代码。

我们要上传文件,所以一定要自定义允许上传到 servlet 容器的数据大小:

spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=20M

快完成了!在我们开始写代码之前,先预览一下这个流程。

我们在机器上下载了一个文件(一个棋盘游戏规则列表),重命名为 test.pdf,放在 ~/Downloads/test.pdf。

该文件将发送到 /rag/ingest 端点(根据你的本地设置替换路径):

http --form POST http://localhost:8080/rag/ingest path@/Users/jlong/Downloads/test.pdf

这可能需要几秒钟……

在后台,数据被发送到 OpenAI,OpenAI 会为数据创建 embeddings;然后这些数据,包括向量和原始文本,都写入到 Elasticsearch。

这些数据和所有的 embeddings 就是魔法所在。然后我们可以通过 VectorStore 接口查询 Elasticsearch。

完整流程如下:

  • HTTP 客户端将你选择的 PDF 上传到 Spring 应用。
  • Spring AI 负责从 PDF 提取文本,并将每页分成 800 字符的块。
  • OpenAI 为每个块生成向量表示。
  • 分块文本和向量都会存储到 Elasticsearch。

最后,我们将发出一个查询:

http :8080/rag/query question=="where do you place the reward card after obtaining it?" 

然后我们会得到一个相关的答案:

After obtaining a Reward card, you place it facedown under the Hero card of the hero who received it.
Found at page: 28 of the manual

不错!这整个过程是怎么工作的?

  • HTTP 客户端把问题提交给 Spring 应用。
  • Spring AI 从 OpenAI 获取问题的向量表示。
  • 利用这个向量,它在 Elasticsearch 中存储的文本块里搜索相似文档,并检索最相似的文档。
  • 然后 Spring AI 把问题和检索到的上下文发送给 OpenAI,生成大语言模型(LLM)的答案。
  • 最后,它返回生成的答案和检索到的上下文引用。

让我们深入看看 Java 代码,了解它到底是怎么工作的。

首先是 Main 类:它是任何普通 Spring Boot 应用的标准主类。

@SpringBootApplication
public class DemoApplication {public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args);}
}

没什么特别的。继续...

接下来,是一个基础的 HTTP 控制器:

@RestController
class RagController {private final RagService rag;RagController(RagService rag) {this.rag = rag;}@PostMapping("/rag/ingest")ResponseEntity<?> ingestPDF(@RequestBody MultipartFile path) {rag.ingest(path.getResource());return ResponseEntity.ok().body("Done!");}@GetMapping("/rag/query")ResponseEntity<?> query(@RequestParam String question) {String response = rag.directRag(question);return ResponseEntity.ok().body(response);}
}

控制器只是调用了我们构建的一个服务,用来处理文件的摄取并写入 Elasticsearch 向量存储,然后方便地对同一个向量存储进行查询。

我们来看一下这个服务:

@Service
class RagService {private final ElasticsearchVectorStore vectorStore;private final ChatClient ai;RagService(ElasticsearchVectorStore vectorStore, ChatClient.Builder clientBuilder) {this.vectorStore = vectorStore;this.ai = clientBuilder.build();}void ingest(Resource path) {PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(path);List<Document> batch = new TokenTextSplitter().apply(pdfReader.read());vectorStore.add(batch);}// TBD
}

这段代码处理所有的摄取:给定一个 Spring Framework 的 Resource,它是一个字节容器,我们使用 Spring AI 的 PagePdfDocumentReader 读取 PDF 数据(假设是 .PDF 文件——确保在接受任意输入之前尽量验证!),然后用 Spring AI 的 TokenTextSplitter 进行分词,最后将得到的 List 添加到向量存储实现 ElasticsearchVectorStore 中。

你可以通过 Kibana 确认:在发送文件到 /rag/ingest 端点后,打开浏览器访问 localhost:5601,然后在左侧菜单中导航到 Dev Tools。在那里你可以发出查询,和 Elasticsearch 实例中的数据交互。

现在是有趣的部分:我们如何根据用户查询把数据取回来?

下面是在名为 directRag 的方法中对查询的初步实现。

String directRag(String question) {// Query the vector store for documents related to the questionList<Document> vectorStoreResult =vectorStore.doSimilaritySearch(SearchRequest.builder().query(question).topK(5).similarityThreshold(0.7).build());// Merging the documents into a single stringString documents = vectorStoreResult.stream().map(Document::getText).collect(Collectors.joining(System.lineSeparator()));// Exit if the vector search didn't find any resultsif (documents.isEmpty()) {return "No relevant context found. Please change your question.";}// Setting the prompt with the contextString prompt = """You're assisting with providing the rules of the tabletop game Runewars.Use the information from the DOCUMENTS section to provide accurate answers to thequestion in the QUESTION section.If unsure, simply state that you don't know.DOCUMENTS:""" + documents+ """QUESTION:""" + question;// Calling the chat model with the questionString response = ai.prompt().user(prompt).call().content();return response +System.lineSeparator() +"Found at page: " +// Retrieving the first ranked page number from the document metadatavectorStoreResult.getFirst().getMetadata().get(PagePdfDocumentReader.METADATA_START_PAGE_NUMBER) +" of the manual";}

代码很简单,但我们分几步来看:

  1. 用 VectorStore 执行相似度搜索。

  2. 从所有结果中,获取底层的 Spring AI Documents 并提取它们的文本,全部拼接成一个结果。

  3. 把 VectorStore 的结果连同指示模型如何处理它们的提示语和用户的问题一起发给模型。等待响应并返回。

这就是 RAG(检索增强生成)。意思是我们用向量库中的数据来辅助模型的处理和分析。现在你知道怎么做了,希望你永远不需要亲自写!至少不用像这样写:Spring AI 的 Advisors 可以让这个过程更简单。

Advisors 允许你对模型的请求做前处理和后处理,同时为你的应用和向量库之间提供抽象层。在构建文件中添加以下依赖:

<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>

在类里添加另一个方法,叫 advisedRag(String question):

String advisedRag(String question) {return this.ai.prompt().user(question).advisors(new QuestionAnswerAdvisor(vectorStore)).call().content();
}

所有的 RAG 模式逻辑都封装在 QuestionAnswerAdvisor 中。其他的就像对 ChatModel 的任何请求一样!很棒!

结论

在这个演示中,我们使用了 Docker 镜像,并且在本地机器上完成了所有操作,但目标是构建适合生产环境的 AI 系统和服务。你可以做很多事情来实现这个目标。

首先,你可以添加 Spring Boot Actuator 来监控 token 的消耗。token 是衡量模型请求复杂度(有时也是成本)的代理指标。

你已经在类路径上添加了 Spring Boot Actuator,只需指定以下属性来显示所有指标(由出色的 Micrometer.io 项目捕获):

management.endpoints.web.exposure.include=*

重启你的应用。发起一次查询,然后访问:http://localhost:8080/actuator/metrics。搜索 “token”,你会看到应用使用的 token 信息。一定要关注这些信息。当然,你也可以使用 Micrometer 对 Elasticsearch 的集成,将这些指标推送过去,让 Elasticsearch 成为你的时间序列数据库!

你还应该考虑,每次请求像 Elasticsearch、OpenAI 或其他网络服务时,都是 IO 操作,并且这些 IO 通常会阻塞执行的线程。Java 21 及以后版本带来了非阻塞的虚拟线程,大幅提升了可扩展性。用以下方式启用:

spring.threads.virtual.enabled=true

最后,你会想把你的应用和数据托管在一个能让它们茁壮成长和扩展的地方。我们相信你可能已经考虑过在哪里运行你的应用,但你的数据会托管在哪里?我们推荐 Elastic Cloud。它安全、私密、可扩展且功能丰富。我们最喜欢的部分是,如果你愿意,可以选择无服务器版本,由 Elastic 来负责报警,而不是你!

原文:Spring AI and Elasticsearch as your vector database - Elasticsearch Labs

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

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

相关文章

TDengine 高可用——双活方案

概述 部分用户因为部署环境的特殊性只能部署两台服务器&#xff0c;同时希望实现一定的服务高可用和数据高可靠。本文主要描述基于数据复制和客户端 Failover 两项关键技术的 TDengine 双活系统的产品行为&#xff0c;包括双活系统的架构、配置、运维等。TDengine 双活既可以用…

与 JetBrains 官方沟通记录(PyCharm 相关问题反馈)

#工作记录 沟通记录&#xff1a; Subject: Feedback on Terminal and Environment Activation Issues in PyCharm : PY-81233 沟通进度&#xff1a; 【笔记】记一次PyCharm的问题反馈_the polyglot context is using an implementation th-CSDN博客 【笔记】与PyCharm官方沟通…

前端实战:用 JavaScript 模拟文件选择器,同步实现图片预览与 Base64 转换

代码 function 仙盟插件_通用_图片_上传(人间通道id,检验关卡img,仙界指引id){const 人间通道 document.getElementById(人间通道id);const 检验关卡 document.getElementById(检验关卡img);const 仙界指引 document.getElementById(仙界指引id);人间通道.addEventListener(…

数据库设计文档撰写攻略

数据库设计文档撰写攻略 一、数据库设计文档的核心价值二、数据库设计文档的核心框架与内容详解2.1 文档基础信息2.2 需求分析与设计原则2.2.1 业务需求概述2.2.2 设计原则 2.3 数据模型设计2.3.1 概念模型&#xff08;ER 图&#xff09;2.3.2 逻辑模型&#xff08;表结构设计&…

3D个人简历网站 7.联系我

3D个人简历网站 7.联系我 修改Contact.jsx // 从 react 库导入 useRef 和 useState hooks import { useRef, useState } from "react";/*** Contact 组件&#xff0c;用于展示联系表单&#xff0c;处理用户表单输入和提交。* returns {JSX.Element} 包含联系表单的 …

AI大模型从0到1记录学习numpy pandas day25

第 3 章 Pandas 3.1 什么是Pandas Pandas 是一个开源的数据分析和数据处理库&#xff0c;它是基于 Python 编程语言的。 Pandas 提供了易于使用的数据结构和数据分析工具&#xff0c;特别适用于处理结构化数据&#xff0c;如表格型数据&#xff08;类似于Excel表格&#xff09;…

一些可以优化JavaScript性能的工具或库汇总

在 JavaScript 性能优化方面,有许多实用的工具和库可以帮助你分析、监控和提升代码性能。以下是一些常用的工具和库分类整理: 一、性能分析工具 这些工具用于诊断性能瓶颈,定位问题代码。 Chrome DevTools Performance 面板:分析运行时性能,记录函数执行时间、内存使用、…

Linux | tmux | 无法复制粘贴

问题&#xff1a;在Linux中使用tmux时&#xff0c;总是没法使用复制粘贴功能&#xff1b; 解决: 如果希望直接用鼠标选择并复制&#xff08;类似普通终端&#xff09;&#xff0c;可以&#xff1a; 在 ~/.tmux.conf 中添加&#xff1a;sh set -g mouse on;重新加载 tmux 配置…

行贿罪案件(公安侦查阶段)询问笔录发问提纲

一、基本情况核实 与行贿对象&#xff08;受贿人&#xff09;的关系&#xff1f;何时通过何种方式认识&#xff1f;日常交往频率及主要内容&#xff1f; 是否具备国家工作人员身份或其他特定主体资格&#xff1f;是否属于被追诉单位的直接责任人员&#xff1f; 二、行贿动机与…

活到老学到老-Spring参数校验注解Validated /Valid

通过 Validated 和 Valid可以对请求的进行参数校验。 1.核心对比&#xff1a; 特性Valid (JSR-303)Validated (Spring)来源Java标准规范Spring框架扩展分组校验不支持支持&#xff08;通过groups属性&#xff09;嵌套路径自动处理级联校验需配合Valid生效应用范围方法参数、属…

【笔记】JetBrains 数据迁移与符号链接操作

数据迁移与符号链接操作 一、备份原始数据 使用 robocopy 命令备份 C 盘中的源文件夹&#xff0c;确保原始数据完整备份。 robocopy "C:\Users\love\AppData\Local\JetBrains" "E:\Downloads\Other\JetBrains" /E确保备份路径足够存储空间。 二、复制文…

使用 Terraform 创建 Azure Databricks 工作区

使用 Terraform 创建 Azure Databricks Terraform 是一种基础设施即代码(IaC)工具,允许用户通过声明式配置文件来管理和部署云资源。Azure Databricks 是一个基于 Apache Spark 的分析平台,专为数据工程和数据科学设计。通过 Terraform,可以自动化 Azure Databricks 的创…

【zookeeper】--部署3.6.3

文章目录 下载解压创建data和logs配置文件1)创建目录并且编辑 zoo.cfg2)接下来将 node01 的 ZooKeeper 所有文件拷贝至 node02 和 node03。推荐从 node02 和 node03 拷贝4&#xff09;最后 vim /etc/profile 配置环境变量&#xff0c;环境搭建结束。配完环境变量后 source /etc…

RxJS 核心操作符详细用法示例

1. Observable 详细用法 Observable 是 RxJS 的核心概念&#xff0c;代表一个可观察的数据流。 创建和订阅 Observable import { Observable } from "rxjs";// 1. 创建Observable const myObservable new Observable(subscriber > {// 发出三个值subscriber.n…

QGrphicsScen画布网格和QGrphicsItem对齐到网格

#include <QGraphicsScene> #include <QPainter> #include <QWheelEvent> #include <QGraphicsView> class MyGraphicsView : public QGraphicsView { public:MyGraphicsView(QGraphicsScene* scene) : QGraphicsView(scene){}protected:// 重写滚轮事…

深入解析自然语言处理中的语言转换方法

在数字化浪潮席卷全球的今天&#xff0c;自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;作为人工智能领域的核心技术之一&#xff0c;正深刻地改变着我们与机器交互的方式。其中&#xff0c;语言转换方法更是 NLP 的关键组成部分&#xff0c…

VRRP虚拟路由器协议的基本概述

目录 vrrp是什么&#xff1f; VRRP的一些概念与专有名词 VRRP的Master选举规则&#xff1a; 尾声 vrrp是什么&#xff1f; vrrp全名virtual router redundance protocol&#xff0c;虚拟路由器冗余协议 VRRP的一些概念与专有名词 1&#xff09;VRRP设备&#xff1a;运行VRRP…

数据结构 -- 交换排序(冒泡排序和快速排序)

冒泡排序 基于“交换”的排序&#xff1a;根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置 //交换 void swap(int &a,int &b){int temp a;a b;b temp; }//冒泡排序 void BubbleSort(int A[],int n){for(int i0;i<n-1;i){bool flag false; …

多模态AI终极形态?GPT-5与Stable Diffusion 3的融合实验报告

多模态AI终极形态&#xff1f;GPT-5与Stable Diffusion 3的融合实验报告 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 多模态AI终极形态&#xff1f;GPT-5与Stable Diffusion 3的融合实验报告摘要引言技术架构对…

ajax中get和post的区别,datatype返回的数据类型有哪些?

GET 请求 和 POST 请求 是 HTTP 协议中常用的两种请求方法&#xff0c;它们主要的区别在于&#xff1a; GET 请求&#xff1a; 数据传输方式&#xff1a;数据通过 URL 传递&#xff0c;通常是附加在 URL 后面的查询字符串中&#xff0c;例如 https://example.com/page?nameJoh…