使用 Java 中的 Spring AI 构建应用程序
本教程将介绍如何使用 Spring AI 框架编写 Java 生成式 AI (GenAI) 应用程序,并利用检索增强生成 (RAG) 来优化答案。生成式 AI (GenAI) 是指大语言模型 (LLM) 基于其在训练中或提示词中所见的模式,生成文本、图像或音乐等新内容的过程。检索增强生成 (RAG) 是一种通过将生成式 AI 模型植根于外部知识源,并利用输入中的更多上下文来引导其输出,从而提高模型准确性和可靠性的技术。虽然大多数 GenAI 应用程序及相关内容都围绕 Python 及其生态系统,但如果您想用 Java 编写 GenAI 应用程序该怎么办?
图片来源:Igor Omilaev 来自 Unsplash

什么是 Spring AI?
Spring AI 是一个用于在 Java 中构建生成式 AI 应用程序的框架。它提供了一套用于处理生成式 AI 模型和架构(如大语言模型 (LLM) 和检索增强生成 (RAG))的工具和实用程序。Spring AI 构建在 Spring Framework 之上,这是一个用于构建企业级应用程序的流行 Java 框架,允许那些已经熟悉或参与 Spring 生态系统的开发人员能够将 GenAI 策略整合到他们现有的应用程序和工作流程中。
Java 中还有其他 GenAI 选项,例如 Langchain4j,但本教程重点介绍 Spring AI。
创建项目
要开始使用 Spring AI,您需要创建一个新项目或将相应的依赖项添加到现有项目中。您可以使用 https://start.spring.io/ 上的 Spring Initializr 创建一个新项目,这是一个用于生成 Spring Boot 项目的 Web 工具。
创建新项目时,您需要以下依赖项:
- Spring Web
- OpenAI(或其他 LLM 模型,如 Mistral、Ollama 等)
- Neo4j 向量数据库(也可选择其他向量数据库)
- Spring Data Neo4j
如果您手动将这些依赖项添加到现有项目中,可以在 今天的相关 Github 仓库 中查看依赖项详细信息。
Spring Web 依赖项用于为 GenAI 应用程序创建 REST API。OpenAI 依赖项将访问 OpenAI 模型(一种流行的大语言模型)以获取嵌入和消息。Neo4j 向量数据库依赖项访问 Neo4j 以存储和查询用于相似性搜索的向量。最后,添加 Spring Data Neo4j 依赖项可以支持在 Spring 应用程序中使用 Neo4j 数据库,从而在 Neo4j 中运行 Cypher 查询并将实体映射到 Java 对象。
请继续生成项目,然后在您喜欢的 IDE 中打开它。
一些样板代码
首先需要的是 Neo4j 数据库。Neo4j Aura 免费层是本教程的绝佳选择,但也可以使用 Docker 镜像和其他方法。
根据您选择的 LLM 模型,您可能还需要一个 API 密钥。对于许多公共模型,您可以通过在供应商网站上注册(例如 OpenAI 的 https://platform.openai.com/signup)来获取一个。
拥有 API 密钥后,您可以在 application.properties 文件中进行配置。以下是一个示例:
# LLM model (OpenAI) config
spring.ai.openai.api-key=<YOUR API KEY HERE>Code language: HTML, XML (xml)
注意:最好将 API 密钥和密码等敏感信息保留在环境变量中,或保留在应用程序外部的其他位置。要创建环境变量,可以在终端中使用 export 命令或在您的 IDE 中进行设置。
该文件还包含用于 Neo4j 向量存储的配置属性,以便访问和搜索向量索引进行相似性搜索,以及运行检索查询。
此配置指定 Review 作为存储嵌入的节点标签,因为 Spring 的默认设置会查找 Document 实体。这也指定了嵌入的索引名称(默认为 spring-ai-document-index)。由于此演示服务器包含多个数据库(每个数据库都有单独的数据集),因此有一个属性指向 goodreads 数据库。最后一个属性可防止应用程序在数据库中不存在索引时创建它(这不是生产环境的最佳实践)。
数据集
在此示例中,我们将使用来自 Goodreads 的图书和评论数据集。该数据最初源自 UCSD Book Graph 项目,其中包含有关图书、作者以及相关评论和撰写这些评论的用户的信息。
该数据库通过只读用户公开提供。您可以通过在 https://demo.neo4jlabs.com:7473/browser/ 打开一个新标签页来探索数据。
URI: neo4j+s://demo.neo4jlabs.com
User: goodreads
Password: goodreads
Database: goodreadsCode language: HTTP (http)
应用程序模型
接下来,我们需要在应用程序中创建一个域模型,以映射到下图中显示的数据库模型。

在此示例中,我们将创建一个表示图书节点的 Book 实体。我们还将创建一个表示图书评论的 Review 实体。Review 实体将关联一个嵌入(向量),我们将使用它进行相似性搜索。
这些实体是标准的 Spring Data Neo4j 代码,所以我不会在这里展示代码。但是,每个类的完整代码都可以在 Github 仓库中找到 – Book 类,Review 类。
我们还需要定义一个存储库接口,以便与数据库进行交互。虽然我们需要定义一个自定义查询,但我们稍后会回来添加它。
public interface BookRepository extends Neo4jRepository<Book, String> {
}Code language: Java (java)
接下来,此应用程序的核心(所有魔力发生的地方)是控制器类。该类将包含处理用户提供的搜索短语并调用 Neo4jVectorStore 来计算并返回最相似内容的逻辑。然后,我们可以将这些相似的评论传递到 Neo4j 查询中以检索关联的实体,从而在输入提示词中为 LLM 提供额外的上下文。它将使用提供的所有信息来回答原始搜索短语相关的图书推荐。
控制器
我们的控制器类包含一个通用的注解,将其设置为 rest 控制器。我们还将注入 Neo4jVectorStore 和 BookRepository,以便稍后调用向量搜索和检索查询方法,以及用于嵌入客户端的、与模型无关的 ChatClient。
接下来是为我们的提示词定义一个字符串。这是我们将传递给 LLM 以生成响应的文本。我们将在几分钟后使用用户提供的搜索短语和我们在数据库中找到的相似评论来填充我们的提示词参数。接下来,我们定义控制器类的构造函数,它将注入必要的 bean。
@RestController
public class BookController {
private final ChatClient client;
private final Neo4jVectorStore vectorStore;
private final BookRepository repo;
String prompt = """
You are a book expert providing recommendations from high-quality book information in the CONTEXT section.
Please summarize the books provided in the context section.
CONTEXT:
{context}
PHRASE:
{searchPhrase}
""";
public BookController(ChatClient.Builder builder, Neo4jVectorStore vectorStore, BookRepository repo) {
this.client = builder.build();
this.vectorStore = vectorStore;
this.repo = repo;
}
//Retrieval Augmented Generation with Neo4j - vector search + retrieval query for related context
@GetMapping("/graphrag")
public String generateResponseWithContext(@RequestParam String searchPhrase) {
List<Document> results = vectorStore.similaritySearch(searchPhrase);
//more code shortly!
}
}Code language: Java (java)
最后,我们定义一个方法,当用户向 /graphrag 端点发送 GET 请求时,该方法将被调用。此方法将首先获取搜索短语作为查询参数,并将其传递给向量存储的 similaritySearch() 方法以查找相似的评论。您还可以向查询添加自定义过滤器,例如限制前五个结果 (.withTopK(5)) 并仅提取最相似的结果 (withSimilarityThreshold(0.8))。
然后,我们将相似的 Review 节点映射回 Document 实体,因为 Spring AI 期望通用的文档类型。Neo4jVectorStore 类包含将 Document 转换为自定义记录的方法,反之亦然。
我们现在有了用户搜索短语的相似评论。但是评论(及其附带的文本)在提供图书推荐方面并没有太大帮助。因此,我们需要在 Neo4j 中运行一个查询,为这些评论检索相关的书籍。这就是应用程序的检索增强生成 (RAG) 部分。
让我们在 BookRepository 接口中编写查询,以查找与这些评论关联的图书。
public interface BookRepository extends Neo4jRepository<Book, String> {
@Query("MATCH (b:Book)<-[rel:WRITTEN_FOR]-(r:Review) " +
"WHERE r.id IN $reviewIds " +
"OPTIONAL MATCH (b)<-[rel2:AUTHORED]-(a:Author) " +
"RETURN b, collect(rel), collect(r), collect(rel2), collect(a);")
List<Book> findBooks(List<String> reviewIds);
}Code language: Java (java)
在查询中,我们传入相似性搜索中的评论 ID ($reviewIds),并提取这些评论的 Review -> Book 模式。我们还可以选择性地检索与该书相连的任何作者。然后,我们返回 Book 节点、Review 节点、Author 节点以及所有关系。
现在我们需要在控制器中调用该方法,并将结果传递给提示词模板。我们将把它传递给 LLM,以根据用户的搜索短语生成带有图书推荐列表的响应。
//Retrieval Augmented Generation with Neo4j - vector search + retrieval query for related context
@GetMapping("/graphrag")
public String generateResponseWithContext(@RequestParam String searchPhrase) {
//run vector similarity search in Neo4j
List<Document> results = vectorStore.similaritySearch(searchPhrase);
//execute retrieval query in Neo4j to pull related entities to the similar results
List<Book> bookList = bookRepository.findBooks(results.stream().map(Document::getId).toList());
//create a message to send to the LLM containing original prompt + RAG results
var template = new PromptTemplate(prompt).create(
Map.of("context", bookList.stream().map(Book::toString).collect(Collectors.joining("\n")),
"searchPhrase", searchPhrase)
);
System.out.println("----- PROMPT -----");
System.out.println(template);
//call the LLM with the message and return the answer string (content)
return chatClient.prompt(template).call().content();
}Code language: Java (java)
在相似性搜索之后,我们立即调用 findBooks() 方法,并传入相似性搜索中得到的评论 ID 列表。检索查询返回一个名为 bookList 的图书列表。接下来,我们使用提示词字符串、来自图谱的上下文数据和用户的搜索短语创建一个提示词模板,分别将 context 和 searchPhrase 提示词参数映射到图谱数据(每项占一行的新列表)和用户的搜索短语。我还添加了一个 System.out.println() 将提示词打印到控制台,以便我们可以看到传递给 LLM 的内容。
最后,我们执行 call() 方法以从 LLM 生成响应。返回的 JSON 对象包含一个 contents 键,其中包含带有基于用户搜索短语的图书推荐列表的响应字符串。
让我们来测试一下!
运行应用程序
要运行 Goodreads AI 应用程序,可以在终端中使用 ./mvnw spring-boot:run 命令。应用程序运行后,您可以向 /graphrag 端点发送 GET 请求,并带上搜索短语作为查询参数。接下来包含一些示例。
http ":8080/graphrag?searchPhrase=happy ending"
http ":8080/graphrag?searchPhrase=encouragement"
http ":8080/graphrag?searchPhrase=could you recommend books that are high tech"
http ":8080/graphrag?searchPhrase=please tell me about books that have dragons and magic"Code language: Bash (bash)
示例调用和输出 + 完整提示词
调用和返回的图书推荐
jenniferreif@elf-lord springai-java-tutorial % http ":8080/graphrag?searchPhrase=please tell me about books that have dragons and magic"
Here are summaries of the books featuring dragons and magic from the provided context:
1. **Throne of Jade (Temeraire, #2) by Naomi Novik**: This book is part of the Temeraire series, which blends historical fiction with fantasy, focusing on the relationship between a dragon named Temeraire and his captain, Laurence. The story explores themes of loyalty, friendship, and the complexities of war, all while highlighting the bond between humans and dragons. It has received praise for its engaging narrative, especially from those who enjoy dragon-centric tales.
2. **Dragonmaster (DragonMaster, #1) by Chris Bunch**: This novel centers around a war between two countries where dragons play a pivotal role in aerial combat. The protagonist, Hal, begins as an inexperienced country boy who becomes an apprentice dragon rider and eventually takes on a military command. The book mixes military strategy with fantasy elements, featuring relatable characters and a humorous take on the challenges of war. Some readers appreciated the military aspects, although others wished for more magical elements.
3. **Mechanical Dragons: Fire & Water by Bobbi Schemerhorn**: This story involves a quest to save magic in a world filled with mechanical dragons. The protagonist, Khaly, is portrayed as intelligent and engaging, and the narrative draws inspiration from popular anime and fantasy games. With a plot that combines adventure and darker themes, the book has been well-received for its imaginative world-building and character development, making it a recommended read for fans of magical adventures.
These books should appeal to readers interested in dragons and magical elements within their narratives.Code language: Bash (bash)
应用程序日志输出
----- PROMPT -----
You are a book expert providing recommendations from high-quality book information in the CONTEXT section.
Please summarize the books provided in the context section.
CONTEXT:
Book[book_id=14069, title=Throne of Jade (Temeraire, #2), isbn=0345481291, isbn13=9780345481290, authorList=[Author[author_id=8730, name=Naomi Novik]], reviewList=[Review[id=08a97af8dadb42f047c4124dfcbeddf3, text=If you like dragons, READ THIS BOOK!, rating=5]]]
Book[book_id=1390326, title=Dragonmaster (DragonMaster, #1), isbn=0451460308, isbn13=9780451460301, authorList=[Author[author_id=48308, name=Chris Bunch]], reviewList=[Review[id=d56f87a8cced880de5b56685877cfa9d, text=Long story short: This is a tale about war. And in this war, some people are riding dragons. The end.
It was an okay read, but I wished for more magic and more from the dragons., rating=2], Review[id=7f0df50131b598c55f81771501c50672, text=A really good military book with Dragons! What more can a girl ask for. The premise is simple. Two countries at war, a good old archers and swordsman fighting style until someone decides to use dragons and take the battle airborne.
The main character, Hal starts of as a country bumpkin that cannot stand any harm to come to animals. It follows his journey to apprentice dragon rider to a command in the army fighting on dragon back.
The characters are enjoyable and realistic with a great sense of military humour that had me laughing out loud in a few places. Mariah could be Corporal Nobbs ( from the Discworld) brother, that's all i'm saying.
I would recommend this to anyone that is a fan of battles and the military in their fantasy., rating=3]]]
Book[book_id=23933461, title=Mechanical Dragons: Fire & Water, isbn=null, isbn13=null, authorList=[Author[author_id=7349514, name=Bobbi Schemerhorn]], reviewList=[Review[id=90c877bd35ed31093048f34e87a592a9, text=A tale of a quest to save magic! That's a great premise if I've ever heard one. Paired with reminiscent tones of beloved anime such as Fullmetal Alchemist, a touch of DragonAge, and mechanical dragons, this was a quick and enjoyable read. Khaly is a likable and intelligent protagonist and Bobbi did a great job bringing her (and the dragons!) to life. The story started off in way that had me immediately understanding the world and engaging with the characters before winding into a plot with a dark edge that took me all the way to the end, leaving me ready for book two!
I highly recommend "Mechanical Dragons: Fire & Water" for anyone looking for a magical adventure!, rating=4]]]
PHRASE:
please tell me about books that have dragons and magicCode language: Bash (bash)
我们可以看到 LLM 生成了一个响应,其中包含基于数据库中找到的图书(提示词的 CONTEXT 部分)的图书推荐列表。用户搜索短语的相似性搜索 + 图谱检索查询的结果已包含在提示词中,LLM 的回答使用了该数据进行响应。
总结!
在今天的教程中,您学习了如何使用 Java 中的 Spring AI 构建 GenAI 应用程序。我们使用了 OpenAI 模型来根据用户的搜索短语生成图书推荐。我们使用 Neo4j 向量数据库存储和查询用于相似性搜索的向量。我们还将域模型映射到数据库模型,编写了一个存储库接口来与数据库交互,并创建了一个控制器类来处理用户请求并生成响应。
希望这篇文章能帮助您开始使用 Spring AI 及探索更多内容。编码愉快!
资源
- 代码 (Github 仓库): Spring AI Goodreads
- 文档: Spring AI
- 网页: Spring AI 项目


