大家好,我是程序員魚皮。
炸裂,炸裂,炸裂!從第一次提交代碼到現在,經過 2 年的沉淀,Spring AI 框架的第一個正式版本 1.0 終于發布了。
![]()
有了這玩意,開發 AI 應用就是灑灑水的事,Java 開發者們是不是又爽了,反正我是很興奮啊,讓 Java 再次偉大!
![]()
但可能很多同學還不知道 Spring AI 能干什么,憑什么這玩意就讓 Java 偉大了?
正好我最近剛帶編程導航的同學做完一套 AI 超級智能體實戰項目,毫不夸張地說,我已經把 Spring AI 玩得 “手拿把掐” 了。
![]()
下面我來給大家快速分享一下 Spring AI 的核心能力和魔法。看完之后,我相信你會點贊收藏三連,并且說一句:“偉的太大了”。
Spring AI 核心特性
1、大模型調用能力
大模型調用能力是 AI 應用開發的基礎,允許應用程序與各種 AI 大模型進行交互,發送提示詞并獲取模型的響應。Spring AI 提供了統一的接口來支持各種主流大模型,包括 OpenAI GPT 系列、Claude、通義千問等。
![]()
Spring AI 通過配置 + 抽象接口簡化了大模型的調用過程,我可以直接在配置中聲明多個大模型:
spring:
ai:
# 阿里大模型
dashscope:
chat:
options:
model:qwen-max
# 本地大模型
ollama:
base-url:http://localhost:11434
chat:
model:gemma3:1b
# 谷歌大模型
vertex:
ai:
gemini:
chat:
options:
model:gemini-1.5-pro-001
然后使用支持鏈式調用的 ChatClient 靈活地調用各種不同的大模型:
// 使用 Spring AI 調用大模型
@Bean
publicChatClientchatClient(ChatModel chatModel){
returnChatClient.builder(chatModel).build();
}
publicStringdoChat(String message){
ChatResponse response = chatClient
.prompt(message)
.call()
.chatResponse();
returnresponse.getResult().getOutput().getText();
}
只用一行代碼,就能支持 Stream 流式響應,實現打字機效果:
chatClient
.prompt(message)
.stream()
如果不使用 Spring AI,則需要為每個模型分別實現 API 調用,要自己編寫請求、解析響應,很麻煩!
// 不使用 Spring AI 調用大模型
publicStringchatWithOpenAI(String message){
// 配置 OpenAI API
OkHttpClient client =newOkHttpClient();
MediaType JSON = MediaType.get("application/json; charset=utf-8");
// 構建請求體
JSONObject requestBody =newJSONObject();
requestBody.put("model","gpt-3.5-turbo");
JSONArray messages =newJSONArray();
JSONObject userMessage =newJSONObject();
userMessage.put("role","user");
userMessage.put("content", message);
messages.put(userMessage);
requestBody.put("messages", messages);
// 發送請求
RequestBody body = RequestBody.create(requestBody.toString(), JSON);
Request request =newRequest.Builder()
.url("https://api.openai.com/v1/chat/completions")
.header("Authorization","Bearer "+ OPENAI_API_KEY)
.post(body)
.build();
try(Response response = client.newCall(request).execute()) {
String responseBody = response.body().string();
JSONObject jsonResponse =newJSONObject(responseBody);
returnjsonResponse.getJSONArray("choices")
.getJSONObject(0)
.getJSONObject("message")
.getString("content");
}catch(Exception e) {
return"Error: "+ e.getMessage();
}
}
Spring AI 不僅提供了統一接口支持多種大模型,讓我們可以輕松切換模型而無需修改業務代碼。它還支持多模態大模型調用,使 AI 能夠同時處理文本、圖像、音頻等多種輸入類型。
我們只需要將圖片等資源添加到消息對象中,一起發送給 AI 就可以了,使用 Spring AI 幾行代碼就能實現:
// 調用多模態模型
String response = ChatClient.create(chatModel).prompt()
.user(u -> u.text("描述這張圖片中的內容")
.media(MimeTypeUtils.IMAGE_PNG,newClassPathResource("/yupi.png")))
.call()
.content();
如果不使用 Spring AI,多模態處理將變得復雜得多:
// 不使用 Spring AI 的多模態實現
publicStringanalyzeImage(String textPrompt, File imageFile){
// 讀取圖像文件并編碼為 Base64
String base64Image ="";
try{
byte[] fileContent = Files.readAllBytes(imageFile.toPath());
base64Image = Base64.getEncoder().encodeToString(fileContent);
}catch(IOException e) {
return"Error reading image file: "+ e.getMessage();
}
// 構建請求體,不同模型的格式差異很大
JSONObject requestBody =newJSONObject();
requestBody.put("model","gpt-4-vision-preview");
JSONArray messages =newJSONArray();
JSONObject userMessage =newJSONObject();
userMessage.put("role","user");
// 構建復雜的內容數組
JSONArray contentArray =newJSONArray();
// 添加文本部分
JSONObject textContent =newJSONObject();
textContent.put("type","text");
textContent.put("text", textPrompt);
contentArray.put(textContent);
// 添加圖像部分
JSONObject imageContent =newJSONObject();
imageContent.put("type","image_url");
JSONObject imageUrl =newJSONObject();
imageUrl.put("url","data:image/png;base64,"+ base64Image);
imageContent.put("image_url", imageUrl);
contentArray.put(imageContent);
userMessage.put("content", contentArray);
messages.put(userMessage);
requestBody.put("messages", messages);
// 發送請求并解析響應...
// 代碼略
}
此外,Spring AI 提供了強大的 Advisors 機制,有點類似面向切面編程,可以在模型調用前后添加額外的邏輯,增強 AI 的能力。
舉個例子,使用 Spring AI 內置的日志 Advisor,一行代碼就能在調用 AI 前后記錄日志:
// 使用 Advisors 增強 ChatClient
publicStringdoChatWithAdvisors(String message, String chatId){
ChatResponse response = chatClient
.prompt()
.user(message)
// 添加日志 Advisor
.advisors(newLoggingAdvisor())
.call()
.chatResponse();
returnresponse.getResult().getOutput().getText();
}
Advisor 的應用場景還有很多,比如調用 AI 前檢查提示詞是否安全、得到 AI 響應后保存到數據庫中等等。
2、提示工程
提示工程(Prompt Engineering)是一門復雜的學問,指通過精心設計提示詞,讓 AI 更準確地理解用戶意圖,生成更符合預期的回答,減少幻覺(生成虛假信息)的概率,同時優化 AI 模型的性能表現并節省成本。
Spring AI 通過 Prompt 和 PromptTemplate 類實現提示工程。
Prompt 類可以統一封裝多種不同類型的提示詞,便于發送給大模型:
// 用戶提示詞
Message userMessage =newUserMessage(userText);
// 系統提示詞
Message systemMessage =newSystemMessage(systemText);
Prompt prompt =newPrompt(List.of(userMessage, systemMessage));
利用 PromptTemplate 可以創建支持替換變量的提示詞模板,便于提示詞的維護和復用:
// 使用 Spring AI 的提示模板
PromptTemplate promptTemplate =newPromptTemplate("你好,我是{name},我擅長{skill}");
Prompt prompt = promptTemplate.create(Map.of(
"name","魚皮",
"skill","編程"
));
ChatResponse response = chatClient.call(prompt);
如果不使用 Spring AI,你就需要手動 / 或者利用工具類來拼接提示詞字符串,會更麻煩:
// 不使用 Spring AI 需要手動字符串拼接
String name ="AI 戀愛顧問";
String skill ="解決戀愛問題";
String promptText ="你好,我是"+ name +",我擅長"+ skill;
// 還需自行實現條件邏輯、變量轉義等
if(hasCondition) {
promptText +=",我注意到你可能遇到了"+ conditionType +"問題";
}
// 調用 API 需自行封裝請求
Response response = apiClient.sendPrompt(promptText);
3、會話記憶
會話記憶(Chat Memory)使 AI 能夠保存多輪對話歷史,理解上下文,實現連貫對話體驗,防止 AI 斷片兒。
利用 Spring AI 的 Advisor 機制,一行代碼就能輕松開啟對話記憶:
// 使用 Spring AI 的會話記憶
publicStringdoChatWithMemory(String message, String chatId){
ChatResponse response = chatClient
.prompt()
.user(message)
.advisors(
// 將對話記憶保存到內存中
newMessageChatMemoryAdvisor(newInMemoryChatMemory())
)
.call()
.chatResponse();
returnresponse.getResult().getOutput().getText();
}
還可以設置會話 id 實現隔離、設置上下文大小限制等參數:
// 使用 Spring AI 的會話記憶
publicStringdoChatWithMemory(String message, String chatId){
ChatResponse response = chatClient
.prompt()
.user(message)
.advisors(
// 將對話記憶保存到內存中
newMessageChatMemoryAdvisor(newInMemoryChatMemory())
)
.advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY,10))
.call()
.chatResponse();
returnresponse.getResult().getOutput().getText();
}
Spring AI 會自動處理上下文窗口大小限制,避免超出模型最大 token 限制。
如果不使用 Spring AI,需要手動管理對話歷史,代碼量一下子就上來了:
// 不使用 Spring AI 的會話記憶實現
MapnewHashMap<>();
publicStringchat(String message, String userId){
// 獲取用戶歷史記錄
ListnewArrayList<>());
// 添加用戶新消息
Message userMessage =newMessage("user", message);
history.add(userMessage);
// 構建完整歷史上下文
StringBuilder contextBuilder =newStringBuilder();
for(Message msg : history) {
contextBuilder.append(msg.getRole()).append(": ").append(msg.getContent()).append("\n");
}
// 調用 AI API
String response = callAiApi(contextBuilder.toString());
// 保存 AI 回復到歷史
Message aiMessage =newMessage("assistant", response);
history.add(aiMessage);
conversationHistory.put(userId, history);
returnresponse;
}
> conversationHistory =
history = conversationHistory.getOrDefault(userId,
Spring AI 的實現非常優秀,將會話存儲和保存機制分離,我們可以自己定義 ChatMemory,將對話歷史保存到數據庫等持久存儲中。
4、RAG 檢索增強生成
RAG(Retrieval-Augmented Generation)是指利用外部知識來增強 AI 生成結果的技術。通過從知識庫檢索相關信息并注入到提示詞中,讓 AI 能夠利用這些信息生成更準確的回答。
比如我帶大家做了一個 AI 戀愛大師應用,給 AI 準備了一套專注于戀愛問答的知識庫文檔:
![]()
利用 RAG 技術,AI 就能從我自己定義的知識庫中獲取到特定領域的、最新的信息,不僅能減少大模型的幻覺(防止瞎編內容),還能趁機推薦一波自己的課程,豈不美哉?
所以 AI 的回復也不能完全相信哦~
![]()
RAG 的完整工作流程包括文檔收集和切割、向量轉換和存儲、文檔過濾和檢索、查詢增強和關聯 4 大步驟。
![]()
Spring AI 給 RAG 全流程的實現都提供了支持:
1)文檔讀取。直接利用 Spring AI 提供的文檔加載器,各種類型的文檔都能輕松讀取:
publicListloadDocuments(){
ListnewArrayList<>();
// 加載 Markdown 文檔
Resource resource = resourceLoader.getResource("classpath:documents/knowledge.md");
MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder()
.withHorizontalRuleCreateDocument(true)
.withIncludeCodeBlock(true)
.withAdditionalMetadata("source","knowledge-base")
.build();
MarkdownDocumentReader reader =newMarkdownDocumentReader(resource, config);
documents.addAll(reader.get());
returndocuments;
}
documents =
2)向量存儲。利用 Spring AI 提供的 VectorStore 輕松將文檔轉換為向量并保存到向量數據庫中:
// 創建簡單向量存儲
SimpleVectorStore vectorStore = SimpleVectorStore.builder(embeddingModel)
.build();
// 加載文檔并存儲
List
vectorStore.add(documents);
documents = documentLoader.loadDocuments();
3)文檔過濾檢索 + 查詢增強關聯。直接使用 QuestionAnswerAdvisor,一行代碼就可以讓 Spring AI 自動從知識庫中檢索文檔,并將檢索到的文檔提供給 AI 來增強輸出結果。
ChatResponse response = chatClient.prompt()
.user(question)
.advisors(newQuestionAnswerAdvisor(vectorStore))
.call()
.chatResponse();
如果不使用 Spring AI,上述過程的實現可就太復雜了,要自己檢索文檔、構建提示詞等等:
// 不使用 Spring AI 的 RAG 實現
publicStringgenerateAnswerWithKnowledge(String query){
// 1. 將查詢轉換為向量
float[] queryVector = embeddingService.embedText(query);
// 2. 在向量數據庫中搜索相似內容
ListnewArrayList<>();
for(Document doc : vectorDatabase.getAllDocuments()) {
floatsimilarity = calculateCosineSimilarity(queryVector, doc.getVector());
if(similarity >0.5) {
relevantDocs.add(doc);
}
}
relevantDocs.sort((a, b) -> Float.compare(
calculateCosineSimilarity(queryVector, b.getVector()),
calculateCosineSimilarity(queryVector, a.getVector())
));
// 3. 截取前三個最相關文檔
relevantDocs = relevantDocs.subList(0, Math.min(3, relevantDocs.size()));
// 4. 構建提示詞,包含檢索到的知識
StringBuilder prompt =newStringBuilder();
prompt.append("使用以下信息回答問題:\n\n");
for(Document doc : relevantDocs) {
prompt.append("---\n").append(doc.getContent()).append("\n---\n\n");
}
prompt.append("問題: ").append(query);
// 5. 調用 AI 生成回答
returnaiService.generateResponse(prompt.toString());
}
relevantDocs =
除了實現基礎的 RAG 能力外,Spring AI 還提供了更多高級能力來優化 RAG 的效果。比如提供了完整的 ETL流程的支持,能夠快速抽取文檔、切分處理文檔、并加載到向量存儲中。
![]()
提供了多查詢擴展器,可以為原始提示詞生成多個查詢變體,提高召回文檔的幾率:
MultiQueryExpander queryExpander = MultiQueryExpander.builder()
.chatClientBuilder(chatClientBuilder)
.numberOfQueries(3)
.build();
ListnewQuery("誰是程序員魚皮?"));
queries = queryExpander.expand(
提供了查詢重寫器,可以把原始提示詞變得更精確和專業:
publicStringdoQueryRewrite(String prompt){
QueryTransformer queryTransformer = RewriteQueryTransformer.builder()
.chatClientBuilder(builder)
.build();
Query query =newQuery(prompt);
// 執行查詢重寫
Query transformedQuery = queryTransformer.transform(query);
// 輸出重寫后的查詢
returntransformedQuery.text();
}
效果如圖:
![]()
還支持自定義文檔檢索器,能夠更靈活地定義查詢規則,比如按照文檔的元信息精確查詢、只查詢相似度最高的 N 條數據等:
DocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.73)
.topK(5)
.filterExpression(newFilterExpressionBuilder()
.eq("name","魚皮")
.build())
.build();
5、工具調用
工具調用(Tool Calling)允許 AI 借助外部工具完成自身無法直接完成的任務,比如網絡搜索、文件操作、數據查詢等。它擴展了 AI 的能力范圍,使 AI 能夠獲取實時信息、執行實際操作。
工具調用實現的本質是拼接提示詞,讓 AI 選擇要調用哪些工具,然后由程序調用工具并將返回結果交給 AI 進行后續輸出。
![]()
利用 Spring AI,只需要通過注解就能快速定義工具:
// 使用 Spring AI 定義工具
publicclassWebSearchTool{
@Tool(description ="Search for information from Baidu Search Engine")
publicStringsearchWeb(
@ToolParam(description ="Search query keyword")String query){
// 網絡搜索實現
return"搜索結果: "+ query +" 的相關信息...";
}
}
然后一行代碼就能使用工具,Spring AI 會控制程序和大模型進行交互并自動調用工具,非常方便:
ChatResponse response = chatClient
.prompt()
.user(message)
.tools(newWebSearchTool())
.call()
.chatResponse();
如果不使用 Spring AI,可就太復雜了!
// 不使用 Spring AI 的工具調用實現
publicStringhandleUserRequest(String userMessage){
// 1. 構建含工具定義的提示詞
String toolDefinition ="""
{
"tools": [
{
"name": "searchWeb",
"description": "Searchforinformation from Baidu Search Engine",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query keyword"
}
},
"required": ["query"]
}
}
]
}
""";
// 2. 調用 AI 判斷是否需要工具
JsonObject aiResponse = callAiWithTools(userMessage, toolDefinition);
// 3. 解析 AI 響應判斷是否需調用工具
if(aiResponse.has("tool_calls")) {
JsonArray toolCalls = aiResponse.getAsJsonArray("tool_calls");
// 4. 依次執行每個工具
ListnewArrayList<>();
for(JsonElement toolCall : toolCalls) {
String toolName = toolCall.getAsJsonObject().get("name").getAsString();
JsonObject args = toolCall.getAsJsonObject().get("arguments").getAsJsonObject();
// 5. 根據工具名執行對應工具
if("searchWeb".equals(toolName)) {
String query = args.get("query").getAsString();
String result = searchWeb(query);// 實際執行搜索
toolResults.add(result);
}
}
// 6. 將工具結果發回給 AI 生成最終回答
returncallAiWithToolResults(userMessage, toolCalls, toolResults);
}
returnaiResponse.get("content").getAsString();
}
toolResults =
此外,Spring AI 提供了工具上下文 ToolContext,可以讓程序給工具傳遞額外參數,實現用戶身份認證等功能。還支持直接返回模式(returnDirect),可以繞過大模型直接返回工具結果。
6、MCP 模型上下文協議
MCP(Model Context Protocol 模型上下文協議)是一種開放標準,目的是增強 AI 與外部系統的交互能力。MCP 為 AI 提供了與外部工具、資源和服務交互的標準化方式,讓 AI 能夠訪問最新數據、執行復雜操作,并與現有系統集成。
可以將 MCP 想象成 AI 應用的 USB 接口,就像 USB 為設備連接各種外設和配件提供了標準化方式一樣,MCP 為 AI 模型連接不同的數據源和工具提供了標準化的方法。從而輕松增強 AI 的能力,有效降低開發者的理解成本,并且打造出 MCP 服務生態。
![]()
利用 Spring AI,我們可以快速接入別人的 MCP 服務,只需要定義 MCP 服務配置,然后直接通過 Bean 注入 MCP 服務提供的工具即可:
// 使用 Spring AI 的 MCP 客戶端
// 1. 在配置文件中定義 MCP 服務
// mcp-servers.json
{
"mcpServers": {
"amap-maps": {
"command":"npx",
"args": ["-y","@amap/amap-maps-mcp-server"],
"env": {"AMAP_MAPS_API_KEY":"你的API密鑰"}
}
}
}
// 2. 在應用程序中使用 MCP 服務
@Resource
privateToolCallbackProvider toolCallbackProvider;
publicStringdoChatWithMcp(String message){
ChatResponse response = chatClient
.prompt()
.user(message)
.tools(toolCallbackProvider)// MCP 服務提供的所有工具
.call()
.chatResponse();
returnresponse.getResult().getOutput().getText();
}
當然,開發 MCP 服務也很簡單。先利用注解定義工具,然后將工具注冊到 MCP 服務中:
// 定義工具
publicclassImageSearchTool{
@Tool(description ="search image from web")
publicStringsearchImage(@ToolParam(description ="Search query keyword")String query){
// 搜索圖片,返回結果
return"https://www.codefather.cn";
}
}
// 注冊 MCP 服務
@Bean
publicToolCallbackProviderimageSearchTools(){
returnMethodToolCallbackProvider.builder()
.toolObjects(newImageSearchTool())
.build();
}
如果不使用 Spring AI,你就需要引入 MCP 官方的 SDK 進行開發,或者自主實現,太麻煩了!
// 不使用 Spring AI 的 MCP 實現
publicStringchatWithExternalTools(String userMessage){
// 1. 啟動外部 MCP 服務進程
Process mcpProcess = startMcpProcess("npx","-y","@amap/amap-maps-mcp-server");
// 2. 建立與 MCP 服務的通信通道
InputStream inputStream = mcpProcess.getInputStream();
OutputStream outputStream = mcpProcess.getOutputStream();
// 3. 發送初始化握手消息
JsonObject initMessage =newJsonObject();
initMessage.addProperty("jsonrpc","2.0");
initMessage.addProperty("method","initialize");
// ... 添加更多初始化參數
sendMessage(outputStream, initMessage);
// 4. 接收并解析服務提供的工具定義
JsonObject response = readResponse(inputStream);
JsonArray toolDefinitions = extractToolDefinitions(response);
// 5. 調用 AI 模型,將工具定義傳遞給模型
JsonObject aiResponse = callAiWithTools(userMessage, toolDefinitions);
// 6. 解析 AI 響應,如果需要調用工具則發送給 MCP 服務
if(aiResponse.has("tool_calls")) {
JsonArray toolCalls = aiResponse.getAsJsonArray("tool_calls");
ListnewArrayList<>();
for(JsonElement toolCall : toolCalls) {
// 7. 將工具調用請求發送給 MCP 服務
JsonObject toolRequest =newJsonObject();
toolRequest.addProperty("jsonrpc","2.0");
toolRequest.addProperty("method","executeFunction");
// ... 添加工具調用參數
sendMessage(outputStream, toolRequest);
// 8. 接收 MCP 服務的執行結果
JsonObject toolResponse = readResponse(inputStream);
toolResults.add(toolResponse.toString());
}
// 9. 將工具結果發回給 AI 生成最終回答
returncallAiWithToolResults(userMessage, toolCalls, toolResults);
}
// 10. 最后關閉 MCP 服務
mcpProcess.destroy();
returnaiResponse.get("content").getAsString();
}
toolResults =
結尾
以上就是 Spring AI 的核心特性解析,相信大家也感受到使用 Spring AI 開發 AI 應用有多爽了吧!
除了前面提到的之外,Spring AI 還提供了大模型評估測試能力,比如評估 AI 回答與用戶輸入和上下文的相關性;還提供了全面的可觀測性功能,幫助開發者監控 AI 應用的運行狀態。
不過目前這些特性還不夠成熟,Spring AI 也還有很長一段路要走,后續應該也會推出智能體工作流編排框架吧~
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.