您的第一個 Spring AI 1.0 應用程式

工程 | Josh Long | 2025年5月20日 | ...

您的第一個 Spring AI 1.0 應用程式

作者:Mark Pollack 博士、Christian Tsolov 和 Josh Long

嗨,Spring 粉絲們!Spring AI 已在 Spring Initializr 和其他任何可以找到優質位元組的地方上線。諮詢您的醫生,看看 AI 是否適合您!作為 Java 和 Spring 開發者,現在是一個激動人心的時刻。從來沒有比現在更好的時機成為 Java 和 Spring 開發者,在這個獨特的 AI 時刻更是如此。您看,人們談論 AI 工程時,90% 的內容都只是與模型的整合,其中大多數模型都有 HTTP API。而這些模型接收的大多數內容都只是人類語言的 String。這是整合程式碼,還有什麼地方比您的基於 Spring 的工作負載的側面更適合這些整合存在呢?這些工作負載的業務邏輯驅動著您的組織,並守護著為您的組織提供資料的資料。

AI 工程的痛點與模式

AI 很棒,但它並不完美。和所有技術一樣,它也有問題!有一些事情需要注意,當您探索 Spring AI 時,要知道您正在根據支援您走向生產的模式進行探索。讓我們看看其中的一些。

the pains and patterns of AI

聊天模型幾乎可以處理任何事情,並且會跟隨您進入任何死衚衕。如果您希望它專注於某個特定任務,請給它一個系統提示

模型是無狀態的。如果您曾經透過 ChatGPT 或 Claude Desktop 處理過模型,您可能會覺得這很奇怪,因為它們會在每個後續請求中提交所有已說過內容的副本。這份副本會提醒模型已經說過什麼。這份副本就是聊天記憶

模型生活在一個隔離的沙盒中。這很合理!我們都看過名為《終結者》的紀錄片,知道失控的 AI 會出什麼問題。但是,如果您透過工具呼叫給它們一點控制權,它們可以做很多令人驚奇的事情。

模型相當聰明,但並非無所不知!您可以在請求正文中提供資料,以幫助它們更好地回應。這稱為提示填充

但是不要傳送太多資料!相反,只發送與當前查詢相關的資料。您可以透過將資料放入向量儲存來支援查詢彼此相似的記錄。然後,進行檢索增強生成 (RAG),即您將從向量儲存中篩選出的結果子集傳送給模型進行最終分析。

聊天模型喜歡聊天,即使它們是錯的。這有時會產生有趣且不正確的結果,稱為幻覺。使用評估器來驗證響應基本上符合您的預期。

Spring 開發者的一小步,AI 的一大步

Spring AI 是一個巨大的飛躍,但對於 Spring 開發者來說,它會感覺像是一個自然的下一步。它的工作方式與任何 Spring 專案一樣。它具有可移植的服務抽象,讓您可以一致且方便地使用多種模型。它提供 Spring Boot Starter、配置屬性和自動配置。而且,Spring AI 傳承了 Spring Boot 以生產為導向的理念,支援虛擬執行緒、GraalVM 原生映象以及透過 Micrometer 實現的可觀察性。它還提供出色的開發者體驗,集成了 Spring Boot 的 DevTools,併為 Docker Compose 和 Testcontainers 提供了豐富的支援。與所有 Spring 專案一樣,您可以從 Spring Initializr 開始。

認識這些狗

我們就是要這麼做!我們要構建一個支援領養狗的應用程式!我受到 2021 年一隻走紅的狗的啟發。那隻名叫 Prancer 的狗聽起來真是個麻煩!這是我最喜歡的廣告節選:

“好吧,我試過了。過去幾個月我一直在努力釋出這隻狗的領養資訊,並試圖讓它聽起來……好一點。問題是,它就是不好。像小妖精一樣神經質、厭男、厭動物、厭孩子的狗的市場並不大。但我必須相信 Prancer 也有它的歸宿,因為我已經累了,我的家人也累了。我們每天都生活在它在我們家中創造的惡魔般的吉娃娃地獄中。”

先決條件

聽起來真是個麻煩!但即使是脾氣暴躁的狗也值得擁有充滿愛的家園。所以讓我們構建一個服務,讓人們與他們夢想(或噩夢?)中的狗團聚。訪問 Spring Initializr 並將以下依賴項新增到您的專案中:PgVector, GraalVM Native Support, Actuator, Data JDBC, JDBC Chat Memory, PostgresML, DevtoolsWeb。選擇 Java 24(或更高版本)和 Apache Maven 作為構建工具。(嚴格來說,這裡沒有理由不能使用 Gradle,但示例將以 Apache Maven 為準)確保工件名為 adoptions

請確保您的 pom.xml 中也包含:org.springframework.ai:spring-ai-advisors-vector-store

其中一些東西是熟悉的。Data JDBC 只是引入了 Spring Data JDBC,它只是一個 ORM 對映器,允許您與 SQL 資料庫進行通訊。Web 引入了 Spring MVC。Actuator 引入了 Spring Boot 的可觀察性堆疊,部分由 Micrometer 提供支援。Devtools 是一個開發時功能,允許您在更改時進行即時過載。每次您在 Visual Studio Code 或 Eclipse 中執行“儲存”操作時,它都會自動重新載入程式碼,並且每次您從 IntelliJ IDEA 切換出去時,它都會自動啟動。GraalVM Native Support 引入了對 OpenJDK 分支 GraalVM 的支援,該分支除其他功能外,還提供了一個預編譯 (AOT) 編譯器,可生成輕量級、快速的二進位制檔案。

我們說過 Spring Data JDBC 將使連線到 SQL 資料庫變得容易,但是哪一個呢?在我們的應用程式中,我們將使用 PostgreSQL,但不僅僅是普通的 PostgresSQL!我們將載入兩個非常重要的擴充套件:vectorpostgresmlvector 外掛允許 PostgresSQL 充當向量儲存。您需要將任意(文字、影像、音訊)資料轉換為嵌入,然後才能持久化。為此,您需要一個嵌入模型。PostgresML 在這裡提供了該功能。這些關注點通常是正交的——PostgreSQL 能夠同時完成這兩個任務非常方便。構建 Spring AI 應用程式的一個重要部分是決定您將使用哪個向量儲存、嵌入模型和聊天模型。

當然,我們今天要使用的是 Claude 聊天模型。要連線到它,您需要一個 API 金鑰。您可以從 Anthropic 開發者門戶獲取一個。Claude 非常適合大多數企業工作負載。在不確定或敏感的環境中,它通常更禮貌、穩定和保守。這使其成為企業應用程式的絕佳選擇。Claude 在文件理解和遵循多步驟指令方面也表現出色。

資料庫

正如我之前所說,我們將使用 PostgreSQL。獲取一個支援 vectorpostgresml 的 Docker 映象並不太難。我提供了一個檔案 adoptions/db/run.sh。執行它。它將啟動一個 Docker 映象。然後您需要使用一個應用程式使用者對其進行初始化。執行 adoptions/db/init.sh

現在您已萬事俱備。

application.properties 中指定所有與資料庫連線相關的資訊。

spring.sql.init.mode=always
#
spring.datasource.url=jdbc:postgresql://:5433/postgresml
spring.datasource.username=myappuser
spring.datasource.password=mypassword
#
spring.ai.postgresml.embedding.create-extension=true
spring.ai.postgresml.embedding.options.vector-type=pg_vector
#
spring.ai.vectorstore.pgvector.dimensions=768
spring.ai.vectorstore.pgvector.initialize-schema=true
#
spring.ai.chat.memory.repository.jdbc.initialize-schema=always

這裡我們指定了我們想要的向量型別,是否希望 Spring AI 初始化 PostgresML 擴充套件。我們指定了儲存在 PostgreSQL 中的向量所需的維度,以及是否希望 Spring AI 初始化將其用作向量儲存所需的模式。

我們還希望將一些資料(狗!)安裝到資料庫中,因此我們將告訴 Spring Boot 執行 schema.sqldata.sql,它們分別在資料庫中建立表並安裝資料。

我們需要與剛剛建立的 dog 表進行通訊,所以我們有一個 Spring Data JDBC 實體和倉庫。將以下型別新增到 AdoptionsApplication.java 的底部,在最後一個 } 之後。


interface DogRepository extends ListCrudRepository<Dog, Integer> {
}

record Dog(@Id int id, String name, String owner, String description ){
}

助手

我們將透過 HTTP 控制器處理使用者的提問。這是骨架定義:



@Controller
@ResponseBody
class AdoptionsController {

    private final ChatClient ai;

    AdoptionsController (ChatClient.Builder ai  ) {
        this.ai = ai.build();
    }

    @GetMapping("/{user}/assistant")
    String inquire(@PathVariable String user, @RequestParam String question) {
        return ai
                .prompt()
                .user(question)
                .call()
                .content();
    }
}

所以,基本上,我們可以透過向 :8080/youruser/assistant 傳送 HTTP 請求來提問。試一試。

http :8080/jlong/assistant question=="my name is Josh" 

您應該會得到一個熱情洋溢的回覆。聽起來我們是朋友!

聊天記憶

讓我們來考驗一下這份友誼。

http :8080/jlong/assistant question=="what's my name?" 

在我的測試中,我失望地發現 Claude 已經把我忘了。它完全不記得我了!讓我們給我們的模型一些記憶。我們將使用一個名為 PromptChatMemoryAdvisor 的顧問來完成這項工作,它會在請求模型之前和之後進行預處理和後處理。將其定義新增到 AdoptionsApplication 中。

    @Bean
    PromptChatMemoryAdvisor promptChatMemoryAdvisor(DataSource dataSource) {
        var jdbc = JdbcChatMemoryRepository
                .builder()
                .dataSource(dataSource)
                .build();

        var chatMessageWindow = MessageWindowChatMemory
                .builder()
                .chatMemoryRepository(jdbc)
                .build();

        return PromptChatMemoryAdvisor
                .builder(chatMessageWindow)
                .build();
    }

Advisors 就像過濾器或攔截器。它們是向請求正文新增內容或以通用、橫切的方式處理響應的好方法。有點像 Spring 的面向切面程式設計支援。

此顧問將為您保留訊息。在這種情況下,它將使用我們已經告訴 Spring AI 初始化的模式 (spring.ai.chat.memory.repository.jdbc.initialize-schema=always) 將其持久化到我們的 PostgreSQL 資料庫中。

更改 ChatClient 的配置

    // ..
    AdoptionsController (PromptChatMemoryAdvisor promptChatMemoryAdvisor,
                         ChatClient.Builder ai  ) {
        this.ai = ai
                .defaultAdvisors(promptChatMemoryAdvisor)
                .build();
    }
    // ..

為了讓 PromptChatMemoryAdvisor 工作,它需要某種方式將您的請求與給定的對話關聯起來。您可以透過在請求中分配一個對話 ID 來實現這一點。修改 inquire 方法。


    @GetMapping("/{user}/assistant")
    String inquire(@PathVariable String user, @RequestParam String question) {
        return ai
                .prompt()
                .user(question)
                .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, user)) // new
                .call()
                .content();

    }

在此示例中,我們只是使用 URL 中的路徑變數來建立不同的對話。當然,更合適的方法可能是使用 Spring Security 認證的 Principal#getName() 呼叫。如果安裝了 Spring Security,您可以將認證的主體作為控制器方法的引數注入。

重新啟動程式,然後重新執行相同的 HTTP 互動,這次您應該會發現模型記住了您。注意:您可以透過刪除該特定表中的資料來隨時重置記憶。

系統提示

太好了!如果你只是構建了一個快速 UI,那麼你實際上就擁有了自己的 Claude Desktop。但這並不是我們真正想要的。記住,我們正在努力幫助人們從我們虛構的狗狗領養機構 Pooch Palace 領養狗。我們不希望人們從我們的助手那裡做作業或獲得編碼幫助。讓我們透過配置一個系統提示來為我們的模型設定一個任務宣告。再次更改配置。

    // ..
    AdoptionsController (PromptChatMemoryAdvisor promptChatMemoryAdvisor,
                         ChatClient.Builder ai  ) {
        var system = """
                You are an AI powered assistant to help people adopt a dog from the adoption agency named Pooch Palace with locations in Rio de Janeiro, Mexico City, Seoul, Tokyo, Singapore, New York City, Amsterdam, Paris, Mumbai, New Delhi, Barcelona, London, and San Francisco. Information about the dogs available will be presented below. If there is no information, then return a polite response suggesting we don't have any dogs available.
                """;
        this.ai = ai
                .defaultSystem(system)
                // ..
                .build();
    }
    // ..

讓我們嘗試提一個更相關的問題

http :8080/jlong/assistant question=="do you have any neurotic dogs?"

我們希望模型能知道我們的朋友 Prancer。但它會回答說不知道。

透過良好的可觀察性避免令牌耗盡

我們尚未將對 SQL 資料庫的訪問許可權擴充套件到模型(目前還沒有)。我們可以讀取所有資料庫,然後將它們全部連線到請求的正文中。從概念上講,假設我們有足夠小的資料集和足夠大的令牌計數,這會奏效。但這是原則問題!請記住,所有與模型的互動都會產生令牌成本。這種成本可能以美元和美分的形式承擔,例如在使用 Claude 等託管多租戶 LLM 時,或者至少是以複雜性(CPU 和 GPU 資源消耗)成本的形式承擔。無論哪種方式:我們都希望儘可能降低這些成本。

藉助 Spring AI 與 Actuator 模組的整合,您可以並且應該密切關注令牌消耗。在您的 application.properties 中新增以下內容:

management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

重啟應用程式,然後向模型傳送一些請求。然後導航到瀏覽器中的 localhost:8080/actuator/metrics,您應該會看到以 gen_ai 開頭的指標,例如:gen_ai.client.token.usage。在此處獲取有關該指標的詳細資訊:localhost:8080/actuator/metrics/gen_ai.client.token.usage。指標整合由出色的 Micrometer 專案提供支援,該專案集成了幾乎所有時間序列資料庫,包括 Prometheus、Graphite、Netflix Atlas、DataDog、Dynatrace 等。因此,您也可以將這些指標釋出到這些 TSDB,以幫助為操作構建最重要的單一檢視體驗。

使用向量儲存的檢索增強生成 (R.A.G.)

使用新建立的 DogRepository 從 SQL 資料庫中讀取所有資料,然後在建構函式中將 Spring AI Document 寫入 VectorStore

    //...
    AdoptionsController(JdbcClient db,
                        PromptChatMemoryAdvisor promptChatMemoryAdvisor,
                        ChatClient.Builder ai,
                        DogRepository repository,
                        VectorStore vectorStore) {

        var count = db
                .sql("select count(*) from vector_store")
                .query(Integer.class)
                .single();
        if (count == 0) {
            repository.findAll().forEach(dog -> {
                var dogument = new Document("id: %s, name: %s, description: %s".formatted(
                        dog.id(), dog.name(), dog.description()
                ));
                vectorStore.add(List.of(dogument));
            });
        }

        // ... same as before 
    }

這裡沒有什麼特別的方案。我們正在編寫一個包含一些字串資料的 Document。字串中的內容無關緊要。

這將使用 PostgresML 在後臺完成工作。我們必須配置一個 QuestionAnswerAdvisor,以便 ChatClient 知道在將請求傳送到模型進行最終分析之前,會諮詢向量儲存以獲取支援文件(“狗文件”?)。相應地修改建構函式中稍後的 ChatClient 定義。


        this.ai = ai
                // ...
                .defaultAdvisors(promptChatMemoryAdvisor, 
                        new QuestionAnswerAdvisor(vectorStore))
                // ... 
                .build();

重新執行關於神經質狗的請求,您應該會發現,確實有一隻神經質的狗,它可能正是醫生所需要的,它的名字叫 Prancer!太棒了!

結構化輸出

注意:我們一直以字串形式獲取響應,但這並不是構建抽象的基礎!我們需要一個強型別物件,可以在我們的程式碼庫中傳遞。如果您願意,可以讓模型將返回資料對映到這樣一個強型別物件。假設您有以下記錄:

record DogAdoptionSuggestion(int id,String name, String description) {}

我們可以使用 entity(Class<?>) 方法而不是 content() 方法。

//
@GetMapping("/{user}/assistant")
DogAdoptionSuggestion inquire (...) {
    return ai
            .prompt()
            .user(..) // 
            .entity(DogAdoptionSuggestion.class);
}

你會得到一個強型別物件。怎麼做到的?魔法!(我想。)總之,你可以這樣做,但我們將繼續使用 content(),畢竟我們正在構建一個聊天機器人。

本地工具呼叫

所以,我們與夢想中的狗狗重逢了!現在怎麼辦?好吧,對於任何熱血人類來說,下一步自然是想領養那隻狗狗,對吧!但還有日程安排要做。讓我們允許模型透過訪問工具來與我們正在申請專利的、行業領先的排程演算法進行整合。

將以下型別新增到內碼表底部



@Component
class DogAdoptionScheduler {

    @Tool(description = "schedule an appointment to pickup or adopt a " +
            "dog from a Pooch Palace location")
    String schedule(int dogId, String dogName) {
        System.out.println("Scheduling adoption for dog " + dogName);
        return Instant
                .now()
                .plus(3, ChronoUnit.DAYS)
                .toString();
    }
}

這只是一個普通的 Spring bean。我們宣佈此方法已被呼叫,然後返回一個硬編碼為未來三天的日期。重要的是,我們有一個方法可供模型使用,並用 @Tools 註解。重要的是,這些工具的描述使用人類語言散文,儘可能具有描述性。還記得你媽媽說“用你的話!”嗎?她就是這個意思!它將幫助你成為一名更好的 AI 工程師(更不用說一個更好的隊友了,但那個討論是另一回事了……)。

請務必透過將其指向工具來更新 ChatClient 配置。



    AdoptionsController(
            JdbcClient db,
            DogAdoptionScheduler scheduler,
            ChatClient.Builder ai,
            DogRepository repository,
            VectorStore vectorStore) {
        // ... 
        this.ai = ai
                .defaultTools(scheduler)
                // .. 
                .build();
    }

現在我們來嘗試兩步互動

http :8080/jlong/assistant question=="do you have any neurotic dogs?"

一旦確認有一隻名叫 Prancer 的狗,就詢問安排從紐約市地點接狗的時間。

http :8080/jlong/assistant question=="fantastic. when can i schedule an appointment to pickup Prancer from the New York City location?"

您應該會在控制檯上看到方法被呼叫的確認,並且應該看到日期是三天以後。太好了。

模型上下文協議

我們已經開闢了大量的可能性!Spring AI 是一個簡潔而強大的元件模型,Claude 是一個非常出色的聊天模型,我們已經集成了我們的資料和工具。然而,理想情況下,我們應該能夠以統一的方式使用工具,而無需過度耦合到特定的程式設計模型。2025 年 11 月,Anthropic 釋出了 Claude Desktop 的更新,其中包含一種名為模型上下文協議 (MCP) 的新網路協議。MCP 提供了一種便捷的方式,使模型能夠從工具中獲益,無論這些工具是用何種語言編寫的。MCP 有兩種風格:STDIO 和透過伺服器傳送事件 (SSE) 進行的 HTTP 流傳輸。

結果非常積極!自推出以來,我們目睹了新的 MCP 服務的寒武紀大爆發。有無數的 MCP 服務。有無數的 MCP 服務目錄。現在,我們開始看到越來越多的新 MCP 服務目錄!所有這些都對我們有利;每個 MCP 服務都是您可以教給模型的新技巧。有適用於 Spring Batch、Spring Cloud Config Server、Cloud Foundry、Heroku、AWS、Google Cloud、Azure、Microsoft Office、Github、Adobe 等的 MCP 服務。有 MCP 服務可以讓您在 Blender3D 中渲染 3D 場景。有 MCP 服務可以依次連線任何數量的其他整合和服務,包括 Zapier 中的服務。

現在,我們要再新增一個。讓我們將排程演算法提取為 MCP 服務並重新使用它。訪問 Spring Initializr 並選擇 GraalVM Native SupportWebModel Context Protocol Server。選擇 Java 24(或更高版本)和 Apache Maven。將專案命名為 scheduler。點選 Generate,然後在使用您喜歡的 IDE 開啟生成的 .zip 檔案中的專案。

DogAdoptionScheduler 剪下並貼上到新專案的底部。將以下定義新增到主類 (SchedulerApplication.java) 中。


    @Bean
    MethodToolCallbackProvider methodToolCallbackProvider(DogAdoptionScheduler scheduler) {
        return MethodToolCallbackProvider
                .builder()
                .toolObjects(scheduler)
                .build();
    }

更改 application.properties 以確保新服務在不同的埠上啟動。

# ... 
server.port=8081

啟動排程程式。返回 adoptions 模組,讓我們重新配置它,使其指向這個新的基於遠端 HTTP 的 MCP 服務。

刪除程式碼中所有對 DogAdoptionScheduler 的引用。在配置中定義一個 McpSyncClient 型別的 bean。

    @Bean
    McpSyncClient mcpSyncClient() {
        var mcp = McpClient
                .sync(HttpClientSseClientTransport.builder("https://:8081").build()).build();
        mcp.initialize();
        return mcp;
    }

現在,在建構函式中,指定一個工具回撥,指向這個。


    AdoptionsController(JdbcClient db,
                        McpSyncClient mcpSyncClient,
                        PromptChatMemoryAdvisor promptChatMemoryAdvisor,
                        ChatClient.Builder ai,
                        DogRepository repository,
                        VectorStore vectorStore) {
        // ... 
        this.ai = ai
                // .. 
                .defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClient))
                // ..
                .build();
    }

重新啟動程序,然後再次詢問相同的問題,這次您應該會看到 MCP 服務最終會打印出日程安排的確認資訊,並且建議的日期確實仍然是未來三天。

Claude Desktop 從一開始就支援使用 .json 配置檔案配置 MCP 服務,為了方便互操作性,Spring AI 支援這種配置格式。例如,這是 GitHub MCP 服務的 .json 配置:

{
  "mcpServers": {
    "github": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "GITHUB_PERSONAL_ACCESS_TOKEN",
        "ghcr.io/github/github-mcp-server"
      ],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "...."
      }
    }
  }
}

注意:您需要在此處指定您自己的 GitHub 個人訪問令牌。告訴 Spring AI 將該 MCP 定義包含為工具非常簡單。例如,您只需將以下內容新增到 application.properties 檔案中即可:

spring.ai.mcp.client.stdio.servers-configuration=classpath:/github-mcp.json

現在,您可以請求助手幫助您安排日程,然後讓它將建議的日期寫入 GitHub 中的檔案,所有這些都無需任何干預。太棒了!

聊天框是新的使用者體驗

MCP 在 Claude Desktop 中首次亮相,在其推出時僅支援同一主機上的基於 STDIO 的服務。最近,情況發生了變化。Claude Desktop 剛剛增加了對 HTTP 遠端 MCP 服務的支援,例如我們的 scheduler。您需要升級您的 Anthropic 賬戶,因為至少在撰寫本文時,它僅在 Max 計劃中可用。(我迫不及待地想要付費!)您可以將我們的排程服務重新用作您可以直接從 Claude Desktop 本身使用的工具。假設您已安裝 Claude Desktop(撰寫本文時它可在 macOS 和 Windows 上執行),您將按照以下步驟配置遠端整合。

首先,您需要開啟 Claude 的 Settings 螢幕。

first you'll need to open up Claude's Settings screen

然後,開啟 Settings 螢幕的 Integrations 部分。

then open up the Integrations section of the Settings screen

為了測試此服務,它需要有一個可公開訪問的 URL。顯然,您可以在很多地方執行您的應用程式(CloudFoundry、AWS、Google Cloud、Azure 等),但為了使開發更容易,我們推薦 ngrok?它將使您的本地服務在動態公共 URL 上可用。我們不得不付費升級才能讓它停止顯示插頁式頁面。如果我沒記錯的話,大約是 8 美元,這還不錯。執行:

ngrok http 8081

這將建立一個通往在埠 8081 上執行的任何服務的隧道,允許您透過列印到控制檯的動態 URL 訪問它。

the ngrok dynamic URL

現在我們需要在 Settings 頁面的 Integrations 部分中告訴 Claude Desktop 有關服務的資訊。

then open up the Integrations section of the Settings screen

在主螢幕上點選 Add,然後點選 Connect。您應該會在 ngrok 控制檯中看到 Claude Desktop 和 scheduler 服務在埠 8081 之間連線的確認資訊。

you should see confirmation of the connection between Claude Desktop and the scheduler service on port 8081 in the ngrok console

向 Claude Desktop 提出與上面相同的問題:“我什麼時候可以預約從紐約市地點接 Prancer?”在我們的執行中,它還要求我們指定狗的 ID,我們指定了:45。它最終會提示您允許它呼叫您剛剛提供的工具。

confirm permissions for Claude Desktop

服從它,然後它就開始工作了!

confirm permissions for Claude Desktop

它應該會給你一個三天後的日期。太棒了!

生產級 AI

現在,是時候將我們的目光轉向生產了。

安全

使用 Spring Security 鎖定此 Web 應用程式非常簡單。您還可以使用經過身份驗證的 Principal#getName 作為會話 ID。資料庫中儲存的資料(如對話)怎麼辦?嗯,您有幾個選擇。許多資料庫都支援作為被動功能的靜態加密。

可擴充套件性

我們希望這段程式碼具有可擴充套件性。請記住,每次您向模型(或許多關係資料庫)發出 HTTP 請求時,您都在執行阻塞 IO。IO 位於一個執行緒上,並且在該 IO 完成之前,該執行緒對系統中的任何其他需求都不可用。這是對一個完美執行緒的浪費!執行緒不應該只是空閒等待。Java 21 提供了虛擬執行緒,對於 IO 密集型服務來說,它可以顯著提高可擴充套件性。這就是為什麼您應該(幾乎?)始終在 application.properties 檔案中設定 spring.threads.virtual.enabled=true

GraalVM 原生映象

GraalVM 是一個由 Oracle 主導的 AOT 編譯器,您可以透過 GraalVM 社群版開源專案或功能強大的(免費)Oracle GraalVM 發行版使用它。如果您正在使用 SDKMAN,安裝其中任何一個都非常簡單:sdk install java 24-graalcesdk install java 24-graal。然後,確保使用這些 JDK 發行版之一,例如:sdk use java 24-graal,甚至將其設定為您的系統預設值 sdk default java 24-graalce

請記住,我們用 GraalVM Native Support 配置了我們的兩個 Spring AI 服務,這增加了一個構建外掛,它將允許我們將此應用程式轉換為特定於作業系統和架構的原生二進位制檔案。

./mvnw -DskipTests -Pnative native:compile

退後。你甚至可能有時間去喝杯咖啡……這在大多數機器上需要一分鐘左右,但一旦完成,你就可以輕鬆執行二進位制檔案了。

./target/scheduler

該程式的啟動時間將比在 JVM 上啟動快得多。在我的機器上,它在不到十分之一秒的時間內啟動。更好的是,您應該會發現應用程式佔用的 RAM 比在 JVM 上佔用的 RAM 少得多。當應用程式啟動時,它會在日誌頂部列印其程序識別符號 (PID)。請注意,然後執行:

ps -o rss <PID>

該數字以千位元組為單位,除以 1,000 即可得到兆位元組數。無論您得到什麼數字,我確信您會發現它遠低於您在 JVM 上可能獲得的數字。

Docker

你可能會說,這都很好,但我需要讓它在我的雲平臺上執行,這意味著要把它變成一個 Docker 映象。簡單!

./mvnw -DskipTests -Pnative spring-boot:build-image

退後。這可能還需要一分鐘。完成後,您會看到它打印出了已生成的 Docker 映象的名稱。您可以執行它,記得覆蓋它在您的主機上引用的主機和埠。有趣的是:我們正在使用 macOS,令人驚訝的是,這個應用程式在模擬 Linux 的 macOS 虛擬機器中執行時,啟動速度甚至比直接在 macOS 上執行還要快——而且從一開始就如此!太棒了!

可觀察性

這個應用程式真是太棒了;我敢打賭它會像 Prancer 一樣很快成為頭條新聞。當這種情況發生時,您最好密切關注您的系統資源,以及最重要的——令牌計數。所有對 LLM 的請求都有成本——至少是複雜性成本,如果不是美元和美分的話。正如我們之前探討的,Spring Boot 的可觀察性在這裡是理想的選擇。

下一步

您剛剛構建了一個由 Claude、PostgresSQL 和 Spring AI 提供支援的生產級 AI 應用程式。我們希望您今天就能在 Spring Initializr 上開始使用。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有