Khám phá Spring AI 1.0, cách tích hợp các mô hình ngôn ngữ lớn (LLM) và truy vấn vector với MongoDB Atlas, xây dựng ứng dụng AI thông minh sử dụng RAG trong Java và Spring Boot.
Xin chào các bạn! Hôm nay, chúng ta sẽ cùng khám phá một "vũ khí" mới toanh, cực kỳ lợi hại trong thế giới lập trình AI: **Spring AI 1.0**! Đây là dự án "cây nhà lá vườn" từ đội ngũ Spring đình đám, mà bạn có thể đã biết qua cái tên quen thuộc Josh Long - Anh chàng Spring Developer Advocate cực kỳ nhiệt huyết và cũng là YouTuber tại @coffeesoftware. <br><br> Sau một thời gian phát triển "thần tốc" cùng với sự bùng nổ của AI, Spring AI 1.0 đã chính thức ra mắt. Tưởng tượng xem: bạn có thể kết nối ứng dụng Spring Boot của mình với các mô hình ngôn ngữ lớn (LLM), công cụ tìm kiếm vector, hay thậm chí là tạo ảnh chỉ bằng vài dòng code! Nghe "chanh sả" không? <br><br> Java và Spring đang ở vị trí vàng để "cưỡi" con sóng AI này. Hàng tỉ công ty đang chạy ứng dụng của họ trên Spring Boot, và giờ đây, việc "ghép nối" logic nghiệp vụ với dữ liệu vào các mô hình AI trở nên dễ như ăn kẹo! Không còn đau đầu với mớ bòng bong kết nối nữa đâu nhé. <br><br> Mà khoan, nếu bạn chỉ muốn "xem code cho nóng", thì đây là kho báu dành cho bạn: ghé thăm <a href="https://github.com/mongodb-developer/springairag">kho GitHub của chúng mình</a> ngay! <br><br> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9m9bwlel6jjonrdraxm.png' alt='Tổng quan kiến trúc ứng dụng AI'> <br><br> **Spring AI là gì mà nghe oách vậy?** <br><br> Đơn giản thôi, Spring AI là một "cầu nối" giúp bạn nói chuyện với đủ loại mô hình AI khác nhau. Tưởng tượng như một dàn nhạc giao hưởng, mỗi nhạc cụ có nhiệm vụ riêng, và Spring AI là nhạc trưởng vậy! <br><br> Chúng ta có: <br> * **Mô hình Tạo ảnh (Image models):** Chỉ cần gõ vài chữ, "bùm", bạn có ngay một bức ảnh nghệ thuật! <br> * **Mô hình Chuyển đổi giọng nói thành văn bản (Transcription models):** Nghe cái tên là biết rồi, biến lời bạn nói thành chữ viết trong nháy mắt. <br> * **Mô hình Nhúng (Embedding models):** Cái này hơi "hack não" một chút. Nó biến mọi loại dữ liệu (từ chữ, số, đến hình ảnh) thành một dãy số được gọi là vector. Dãy số này "tóm tắt" ý nghĩa của dữ liệu, giúp chúng ta tìm kiếm những thứ có ý nghĩa tương tự nhau siêu nhanh. <br> * **Mô hình Trò chuyện (Chat models):** Cái này thì chắc ai cũng quen rồi, phải không? Chắc bạn đã từng "buôn dưa lê" với ChatGPT hay Bard rồi nhỉ? Đây chính là "ngôi sao" của làng AI hiện tại đó! Chúng có thể giúp bạn sửa văn bản, làm thơ... nhưng tuyệt đối đừng bảo chúng kể chuyện cười nhé (ít nhất là bây giờ)! Chúng rất "đỉnh", nhưng mà cũng có vài "chuyện khó nói"... <br><br> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzv22i2qg7buz1qer9tht.jpg' alt='Các mô hình và giải pháp AI'> <br><br> **À, cái "chuyện khó nói" của AI là gì?** <br><br> Các mô hình AI mạnh mẽ thật đấy, nhưng chúng cũng có những "điểm yếu chí mạng" của riêng mình. May mắn thay, Spring AI đã có sẵn những "liều thuốc đặc trị" cho từng vấn đề: <br> * **Mô hình Chat và System Prompts:** Mấy em AI chat hay bị "lạc đề" lắm! Để chúng trả lời đúng trọng tâm, bạn cần cung cấp một "system prompt" – như một lời dặn dò cấu trúc, cài đặt giọng điệu và ngữ cảnh cho mọi cuộc trò chuyện. <br> * **Quản lý bộ nhớ:** AI không có "não cá vàng" đâu... à quên, thực ra chúng không có bộ nhớ vốn có. Để chúng nhớ được bạn là ai và bạn đã nói gì, bạn cần phải "nhắc bài" cho chúng thông qua các cơ chế quản lý bộ nhớ. <br> * **Gọi công cụ (Tool calling):** Tưởng tượng AI như một "con người thông thái" nhưng lại bị "cấm cung". Tính năng gọi công cụ cho phép bạn "mở cửa sổ" để AI có thể "thao tác" với các chức năng cụ thể khi cần, ví dụ như tra cứu thông tin hay thực hiện một hành động nào đó. <br> * **Truy cập dữ liệu và Kỹ thuật Prompt (Prompt Engineering):** AI không tự động biết về cơ sở dữ liệu "bí mật" của công ty bạn đâu. Bạn có thể "tiêm" thông tin vào prompt, nhưng mà nhét nhiều quá thì nó cũng "bội thực" và tốn kém lắm! <br> * **Tạo sinh được tăng cường truy xuất (RAG - Retrieval-augmented generation):** Thay vì "nhồi nhét" cả tấn dữ liệu vào prompt, RAG như một "phao cứu sinh" vậy. Nó sẽ dùng một kho vector (kiểu như MongoDB Atlas) để chỉ tìm kiếm những thông tin CẦN THIẾT và LIÊN QUAN NHẤT. Cách này giúp giảm độ phức tạp, giảm kích thước phản hồi, mà lại tăng độ chính xác và tiết kiệm chi phí nữa chứ! <br> * **Xác thực phản hồi:** Đôi khi AI có thể "chém gió" tự tin nhưng lại sai bét nhè (gọi là "ảo giác"). Các mô hình đánh giá sẽ giúp kiểm tra lại, đảm bảo phản hồi của AI luôn đúng chuẩn. <br> * **Tích hợp và quy trình làm việc với MCP:** Không có hệ thống AI nào "một mình một cõi" cả. Với Model Context Protocol (MCP), bạn có thể kết nối ứng dụng Spring AI của mình với các dịch vụ khác hỗ trợ MCP, bất kể ngôn ngữ hay nền tảng, tạo ra các quy trình làm việc có cấu trúc và có mục tiêu rõ ràng. <br><br> Điều tuyệt vời là, Spring AI được thiết kế cực kỳ thân thiện với những ai đã quen dùng Spring Boot! Bạn có thể dễ dàng bắt đầu trên <a href="https://start.spring.io">Spring Initializr</a>. Với các cấu hình tự động được "tinh chỉnh" (autoconfiguration) của Spring AI, bạn sẽ có ngay một môi trường làm việc chuẩn "convention-over-configuration" quen thuộc. Hơn nữa, Spring AI còn hỗ trợ tính năng "quan sát" (observability) với Spring Boot Actuator và Micrometer, giúp bạn theo dõi "sức khỏe" ứng dụng dễ dàng. Đặc biệt, nó còn cực kỳ "hợp cạ" với GraalVM và Virtual Threads, giúp bạn xây dựng các ứng dụng AI siêu nhanh và hiệu quả, có khả năng mở rộng "khủng khiếp"! <br><br> **Khi Spring AI "kết duyên" cùng MongoDB** <br><br> Dù bạn là "tay mơ" hay đã là "lão làng" của MongoDB, thì cơ sở dữ liệu này vẫn là một nền tảng hiện đại, linh hoạt cho việc phát triển AI. MongoDB lưu trữ dữ liệu dưới dạng JSON, cực kỳ phù hợp để xử lý các loại dữ liệu không cấu trúc hoặc bán cấu trúc như tin nhắn chat, tài liệu hay dữ liệu người dùng. <br><br> Và đây mới là điểm nhấn: Với <a href="https://www.mongodb.com/products/platform/atlas-vector-search/?utm_campaign=devrel&utm_source=third-party-content&utm_medium=cta&utm_content=Spring+AI+MongoDB+RAG&utm_term=tim.kelly">Atlas Vector Search</a>, MongoDB còn "nâng cấp" thêm sự linh hoạt này bằng cách cho phép bạn lưu trữ và tìm kiếm các "vector embeddings" (dãy số biểu diễn ý nghĩa dữ liệu) ngay cạnh dữ liệu ứng dụng của bạn – tất cả TRONG MỘT NƠI DUY NHẤT! <br><br> Tưởng tượng một mô tả sản phẩm hay một phiếu hỗ trợ khách hàng được "dịch" thành một vector 512 chiều (một danh sách các con số ghi lại ý nghĩa của nó). Việc này giúp kiến trúc của bạn trở nên siêu đơn giản: không cần database vector riêng biệt, không cần pipeline tùy chỉnh, và không phải đau đầu đồng bộ dữ liệu giữa các hệ thống nữa! Nếu bạn dùng Java, <a href="https://www.mongodb.com/docs/drivers/java/sync/current/?utm_campaign=devrel&utm_source=third-party-content&utm_medium=cta&utm_content=Spring+AI+MongoDB+RAG&utm_term=tim.kelly">MongoDB Java Sync Driver</a> và <a href="https://spring.io/projects/spring-ai">Spring AI</a> sẽ giúp bạn tích hợp tìm kiếm ngữ nghĩa và các tính năng dựa trên LLM trực tiếp vào ứng dụng. <br><br> **RAG là gì mà "thần thánh" vậy? (Retrieval-Augmented Generation)** <br><br> Bạn có thấy mấy em LLM đôi khi "ngơ ngác" vì kiến thức cũ rích, hay "chém gió" bừa bãi không? Đó là lúc RAG "ra tay" giải cứu! RAG (Retrieval-Augmented Generation) là một "chiêu thức" cực kỳ mạnh mẽ để "nâng cấp" chất lượng phản hồi của các mô hình ngôn ngữ lớn (LLM). Nó hoạt động bằng cách "kết hợp" LLM với một hệ thống truy xuất tài liệu – ví dụ như MongoDB Atlas Vector Search – để "định hướng" câu trả lời của LLM dựa trên thông tin THỰC TẾ và MỚI NHẤT. <br><br> **Tại sao cần dùng RAG?** <br> Khi làm việc với LLM "chay" (chỉ một mình nó), bạn sẽ gặp vài "rắc rối" sau: <br> * **Kiến thức lỗi thời:** LLM được huấn luyện dựa trên dữ liệu "chụp ảnh nhanh" của internet tại một thời điểm nào đó, nên chúng không thể biết được những sự kiện mới nhất. <br> * **Thiếu ngữ cảnh:** Chúng không thể truy cập dữ liệu riêng tư hoặc dữ liệu chuyên ngành của bạn trừ khi bạn "đút" cho chúng. <br> * **Ảo giác (Hallucinations):** Thiếu đi ngữ cảnh đáng tin cậy, LLM có thể "tự tin" bịa ra những thông tin nghe có vẻ hợp lý nhưng thực ra sai bét! <br><br> RAG giải quyết những vấn đề này bằng cách "bổ sung" ngữ cảnh từ thế giới thực vào quá trình tạo sinh: <br> * **Bước 1: "Nhồi" dữ liệu (Ingest):** Biến tài liệu nội bộ hoặc dữ liệu chuyên ngành của bạn thành các vector embeddings và lưu trữ chúng trong một kho vector như MongoDB Atlas. <br> * **Bước 2: "Đi tìm" (Retrieve):** Khi người dùng hỏi một câu hỏi, sử dụng MongoDB Atlas Vector Search để tìm kiếm các tài liệu có ý nghĩa liên quan dựa trên "ý nghĩa" của câu hỏi – chứ không chỉ là khớp từ khóa đơn thuần. <br> * **Bước 3: "Sáng tác" (Generate):** Đưa các tài liệu vừa tìm được cho LLM làm ngữ cảnh, để nó có thể tạo ra những phản hồi chính xác và phù hợp. <br><br> Kiến trúc này là "kim chỉ nam" để xây dựng các chatbot thông minh, hệ thống hỏi đáp tài liệu, hoặc trợ lý nội bộ có thể "lý luận" dựa trên kiến thức riêng của tổ chức bạn, chứ không chỉ dựa vào những gì mô hình được huấn luyện ban đầu. <br><br> Kết hợp API của Spring AI với MongoDB Atlas Vector Search, bạn có thể xây dựng các ứng dụng AI thông minh, sẵn sàng sản xuất mà không cần "rời xa" hệ sinh thái Java thân thuộc. Nào, chúng ta hãy bắt tay vào xây dựng ứng dụng AI RAG đầu tiên của mình nhé! <br><br> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feudawofp4t5c39q9y4mi.png' alt='Mô tả luồng hoạt động của ứng dụng RAG'> <br><br> Đây là sơ đồ mô tả cách ứng dụng của chúng ta sẽ hoạt động. Người dùng gửi câu hỏi đến endpoint chat. Câu hỏi sẽ được "nhúng" (embedded) bằng mô hình AI của chúng ta, và vector nhúng này sau đó được sử dụng để truy xuất các tài liệu liên quan trong cơ sở dữ liệu bằng tính năng tìm kiếm vector. Các tài liệu này, cùng với system prompt và bộ nhớ chat tùy chọn, sẽ được áp dụng để duy trì ngữ cảnh. Những tài liệu này được "tiêm" vào prompt, và LLM sẽ tạo ra một phản hồi có ngữ cảnh, sau đó được trả về cho người dùng. <br><br> **Bắt tay xây dựng ứng dụng: Gặp gỡ những "boss" cún cưng!** <br><br> Chúng ta sẽ xây dựng một "trung tâm" nhận nuôi chó ảo có tên **Pooch Palace**! Cứ như một trại cứu trợ chó online vậy đó, nơi bạn có thể tìm và nhận nuôi những "thành viên bốn chân" mới. Và cũng như bất kỳ trung tâm nào, mọi người sẽ muốn "phỏng vấn" mấy em cún đúng không? Vậy nên, chúng ta sẽ xây dựng một "trợ lý" AI để làm công việc đó! <br><br> Mục tiêu của chúng ta là tạo ra một trợ lý giúp tìm "người bạn đời" trong mơ của ai đó – à không, của chó chứ! Cụ thể là chú chó **Prancer**, được mô tả một cách "kinh dị" nhưng cực kỳ hài hước: **“một con chó quỷ ám, thần kinh, ghét đàn ông, ghét động vật, ghét trẻ con và trông như một con quái vật Gremlin.”** Nghe là thấy "chất" rồi phải không? Chú chó này từng "gây bão mạng" vài năm trước khi chủ cũ tìm nhà mới cho nó đó! Quảng cáo của chú ta thì phải nói là "cười ra nước mắt"! Bạn có thể xem bài đăng gốc trên <a href="https://www.facebook.com/tyfanee.fortuna/posts/10219752628710467">Facebook</a>, trên <a href="https://www.buzzfeednews.com/article/amberjamieson/prancer-chihuahua">Buzzfeed News</a>, trên <a href="https://www.usatoday.com/story/news/nation/2021/04/12/chihuahua-hates-men-and-children-goes-viral-facebook-meet-prancer/7186543002/">USA Today</a>, và cả <a href="https://www.nytimes.com/2021/04/28/us/prancer-chihuahua-adopted-connecticut.html">New York Times</a> nữa! <br><br> Chúng ta sẽ xây dựng một endpoint HTTP đơn giản sử dụng tích hợp Spring AI với một LLM (ở đây là OpenAI, nhưng bạn có thể dùng bất cứ mô hình nào bạn thích như Ollama, Amazon Bedrock, Google Gemini, HuggingFace, vân vân và mây mây – tất cả đều được Spring AI hỗ trợ!). Mục đích là để AI phân tích câu hỏi của chúng ta và đưa ra lời khuyên – sau khi đã "ngó nghiêng" qua danh sách chó trong trung tâm (và trong database của chúng ta) – xem chú chó nào sẽ là "một nửa" phù hợp nhất cho chúng ta. <br><br> **Bắt đầu thôi: "Chuẩn bị đồ nghề" cho dự án!** <br><br> Để "làm theo" hướng dẫn này, bạn sẽ cần chuẩn bị vài thứ sau đây: <br> * **Java 24 và Maven:** Đảm bảo đã cài sẵn trên máy nhé. <br> * **MongoDB Atlas Cluster:** Một <a href="https://www.mongodb.com/docs/atlas/getting-started/?utm_campaign=devrel&utm_source=third-party-content&utm_medium=cta&utm_content=Spring+AI+MongoDB+RAG&utm_term=tim.kelly">cluster MongoDB Atlas</a> (gói M0 miễn phí là đủ dùng rồi!). <br> * **Tài khoản OpenAI và API Key:** Một tài khoản OpenAI có sẵn <a href="https://platform.openai.com/api-keys">API key</a>. <br><br> Sau khi có đủ "nguyên liệu", bạn hãy ghé <a href="https://start.spring.io">Spring Initializr</a>, tạo một dự án mới với Artifact ID là `mongodb`, và thêm các dependency sau: <br> * `Web` <br> * `Actuator` <br> * `GraalVM` <br> * `Devtools` <br> * `OpenAI` <br> * `MongoDB Atlas Vector Database` <br><br> Chúng ta đang dùng Java 24 và Apache Maven, nhưng bạn có thể điều chỉnh nếu muốn. Tải file `.zip` về và mở nó trong IDE yêu thích của bạn. <br><br> Tiếp theo, chúng ta cần thêm các thuộc tính sau vào file `application.properties`: <br><br> ```properties spring.application.name=mongodb # ai spring.ai.openai.api-key=<YOUR_OPENAI_CREDENTIAL_HERE> # mongodb spring.data.mongodb.uri=<YOUR_MONGODB_ATLAS_CONNECTION_STRING_HERE> spring.data.mongodb.database=rag # mongodb atlas vector store spring.ai.vectorstore.mongodb.collection-name=vector_store spring.ai.vectorstore.mongodb.initialize-schema=true # scale spring.threads.virtual.enabled=true # health management.endpoints.web.exposure.include=* management.endpoint.health.show-details=always ``` <br><br> Hãy cùng "giải mã" vài giá trị cấu hình nhé: <br> * Mục `ai`: Bạn sẽ điền API key của OpenAI vào đây. **Lưu ý quan trọng:** Bạn NÊN đưa cái này ra môi trường biến (environment variable) như `SPRING_AI_OPENAI_API_KEY` để tránh lỡ tay commit key lên GitHub nhé! <br> * Mục `mongodb`: Đây là các thông tin kết nối cơ bản đến MongoDB của bạn. Bạn cũng nên lấy chuỗi kết nối từ tài khoản MongoDB Atlas và đưa nó ra environment variable để bảo mật. <br> * Mục `mongodb atlas vector store`: Chúng ta "dặn dò" MongoDB Atlas Vector Search tạo cho chúng ta một collection tên là `vector_store`. Việc đặt `initialize-schema` thành `true` sẽ giúp Spring AI tự động tạo <a href="https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-type/?utm_campaign=devrel&utm_source=third-party-content&utm_medium=cta&utm_content=Spring+AI+MongoDB+RAG&utm_term=tim.kelly">index tìm kiếm vector</a> trên collection của chúng ta. Quá tiện lợi! <br> * Mục `scale`: Chúng ta bật tính năng "virtual threads" siêu việt của Java lên. Lát nữa sẽ nói kỹ hơn về cái này nhé. <br> * Mục `health`: Dùng để cấu hình Spring Boot Actuator hiển thị thêm thông tin chi tiết về "sức khỏe" của ứng dụng. Phần này cũng sẽ được nhắc đến sau. <br><br> **Khởi tạo "kho tàng" vector của chúng ta!** <br><br> Giờ thì chúng ta sẽ "viết code" một chút nhé! Trong lớp chính `MongodbApplication.java`, chúng ta sẽ khởi tạo collection bằng cách đọc dữ liệu từ một file `.json` và đưa vào MongoDB Atlas Vector Store. Bạn có thể tìm thấy file `dogs.json` này trong <a href="https://github.com/mongodb-developer/springairag">repo GitHub của chúng mình</a>. Hãy tải nó về và thêm vào thư mục `resources` trong ứng dụng của bạn với tên `dogs.json`. <br><br> Tiếp theo, chúng ta mở lớp `MongodbApplication` và thêm đoạn code sau để "nhắc nhở" JVM đọc tài nguyên mới: <br><br> ```java @ImportRuntimeHints(MongoApplication.Hints.class) @SpringBootApplication public class MongodbApplication { public static void main(String[] args) { SpringApplication.run(MongoApplication.class, args); } static final Resource DOGS_JSON_FILE = new ClassPathResource("/dogs.json"); static class Hints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { hints.resources().registerResource(DOGS_JSON_FILE); } } // TBD } ``` <br><br> Và bây giờ, chúng ta cần "tải" dữ liệu này vào kho vector! Thêm đoạn code dưới đây vào lớp `MongodbApplication`, và chúng ta sẽ cùng xem nó làm gì nhé: <br><br> ```java @Bean ApplicationRunner mongoDbInitialzier(MongoTemplate template, VectorStore vectorStore, @Value("${spring.ai.vectorstore.mongodb.collection-name}") String collectionName, ObjectMapper objectMapper) { return args -> { if (template.collectionExists(collectionName) && template.estimatedCount(collectionName) > 0) return; var documentData = DOGS_JSON_FILE.getContentAsString(Charset.defaultCharset()); var jsonNode = objectMapper.readTree(documentData); jsonNode.spliterator().forEachRemaining(jsonNode1 -> { var id = jsonNode1.get("id").intValue(); var name = jsonNode1.get("name").textValue(); var description = jsonNode1.get("description").textValue(); var dogument = new Document("id: %s, name: %s, description: %s".formatted( id, name, description )); vectorStore.add(List.of(dogument)); }); }; } ``` <br><br> Cái `ApplicationRunner` này sẽ đọc dữ liệu từ file `dogs.json` và ghi vào MongoDB Atlas thông qua giao diện `Spring AI VectorStore`. Mỗi bản ghi được viết dưới dạng một chuỗi đơn giản như `id: %s, name: %s, description: %s`. Định dạng không quá quan trọng, miễn là bạn giữ nó nhất quán. Đây sẽ là dữ liệu chúng ta dùng để tạo embedding, nên hãy đảm bảo nó chứa tất cả thông tin bạn muốn tham chiếu. <br><br> Điều tuyệt vời là `VectorStore` tự động xử lý cả việc tạo embedding VÀ lưu trữ chỉ trong MỘT bước! Nó sử dụng `EmbeddingModel` được hỗ trợ bởi OpenAI (hay bất kỳ mô hình nào bạn cấu hình) để chuyển đổi mỗi chuỗi thành một vector, sau đó lưu trữ vector đó vào MongoDB Atlas. Nếu collection đích chưa tồn tại, Spring AI sẽ tự động tạo nó và định nghĩa một chỉ mục vector trên trường `embedding`. Điều này có nghĩa là collection và index của bạn sẵn sàng để tìm kiếm tương đồng ngay lập tức sau khi nạp dữ liệu, không cần thiết lập thủ công gì cả! <br><br> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/data_ingestion_flow.png' alt='Luồng nạp dữ liệu vào Vector Store'> <br><br> **Xây dựng "trợ lý" AI: Từ A đến Á!** <br><br> Giờ thì đến phần "dễ thở" nhất đây: xây dựng "trợ lý" AI của chúng ta! Chúng ta sẽ định nghĩa `AdoptionController`. Đây là một Spring MVC controller đơn giản, chuyên nhận các yêu cầu và "nhờ" mô hình AI giới thiệu chó dựa trên thông tin người dùng cung cấp. <br><br> ```java @Controller @ResponseBody class AdoptionController { private final ChatClient ai; private final InMemoryChatMemoryRepository memoryRepository; AdoptionController(ChatClient.Builder ai, VectorStore vectorStore) { var system = """ Bạn là một trợ lý AI giúp mọi người nhận nuôi chó từ trung tâm Pooch Palace có các chi nhánh tại Antwerp, Seoul, Tokyo, Singapore, Paris, Mumbai, New Delhi, Barcelona, San Francisco, và London. Thông tin về các chú chó có sẵn sẽ được trình bày bên dưới. Nếu không có thông tin, hãy trả lời lịch sự rằng chúng ta không có chó nào có sẵn. """; this.memoryRepository = new InMemoryChatMemoryRepository(); this.ai = ai .defaultSystem(system) .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore)) .build(); } /// TBD } ``` <br><br> Cho đến giờ, chúng ta chỉ mới định nghĩa một constructor, nơi chúng ta "tiêm" vào đối tượng `Spring AI ChatClient.Builder`. `ChatClient` chính là "trái tim" của API, giúp chúng ta giao tiếp với `ChatModel` (mà đã được cấu hình tự động để kết nối với OpenAI, Ollama, hay bất cứ mô hình nào). <br><br> Chúng ta muốn mọi tương tác với mô hình chat đều có vài "mặc định" hợp lý, trong đó có một `system prompt`. `System prompt` này giống như việc bạn "cài đặt tính cách" cho AI vậy đó! Nó định hình cách mô hình nên trả lời tất cả các câu hỏi sau đó. Trong trường hợp này, chúng ta "huấn luyện" nó trả lời mọi thứ như thể nó là nhân viên của trung tâm nhận nuôi chó Pooch Palace vậy. Nhờ đó, nó sẽ lịch sự từ chối hoặc lái câu chuyện sang chủ đề khác nếu câu hỏi không liên quan đến việc nhận nuôi chó. <br><br> Ngoài ra, chúng ta còn sử dụng `QuestionAnswerAdvisor` được hỗ trợ bởi MongoDB `VectorStore`. Điều này đảm bảo rằng mô hình chỉ cố gắng trả lời các câu hỏi dựa trên các tài liệu liên quan – cụ thể là các mục khớp vector từ cơ sở dữ liệu chó cưng của chúng ta. <br><br> À, "Advisors" là một khái niệm hay ho của Spring AI đấy! Hãy coi chúng như những "bộ lọc", hay "người điều phối" vậy. Chúng sẽ xử lý yêu cầu TRƯỚC và SAU khi gửi đến mô hình ngôn ngữ lớn. `QuestionAnswerAdvisor` này cần thêm một dependency nữa trong `pom.xml`: <br><br> ```xml <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-advisors-vector-store</artifactId> </dependency> ``` <br><br> `Advisor` này sẽ tìm kiếm trong MongoDB Atlas vector store mỗi khi có yêu cầu, thực hiện tìm kiếm tương đồng để xem có dữ liệu nào liên quan đến câu hỏi của người dùng không. Ví dụ, khi ai đó hỏi về một chú chó có đặc điểm cụ thể, nó sẽ tìm kiếm những chú chó phù hợp. Kết quả chúng ta nhận được chỉ là một phần nhỏ trong số tất cả các chú chó có sẵn để nhận nuôi. Chúng ta sẽ chỉ gửi PHẦN DỮ LIỆU LIÊN QUAN đó cho mô hình. Nhớ nhé, mỗi tương tác với mô hình đều tốn kém – cả tiền bạc và độ phức tạp! Vì vậy, gửi càng ít dữ liệu càng tốt. <br><br> Mô hình sau đó sẽ kết hợp `system prompt` của chúng ta, câu hỏi của người dùng và dữ liệu được bao gồm từ kho vector để đưa ra phản hồi. <br><br> **À, mô hình không nhớ gì đâu!** Cứ tưởng tượng chúng như cô cá Dory trong "Đi tìm Nemo" ấy! Vì vậy, chúng ta cấu hình thêm một `advisor` nữa, chuyên giữ lại bản ghi cuộc trò chuyện giữa bạn và AI, rồi "nhắc bài" cho AI những gì đã được nói. Nhờ đó, nó sẽ "nhớ" bạn từ yêu cầu này sang yêu cầu khác. <br><br> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/ai_memory_icon.png' alt='Mô hình AI và trí nhớ'> <br><br> Giờ thì, chúng ta sẽ thêm một endpoint vào `AdoptionController` để giao tiếp với trợ lý AI, có "chút trí nhớ" nhé! Điều này giúp trợ lý của chúng ta duy trì nhật ký trò chuyện cục bộ (10 tin nhắn gần nhất), theo dõi cuộc hội thoại và cung cấp thêm ngữ cảnh cho LLM. <br><br> ```java @GetMapping("/{user}/dogs/assistant") String inquire(@PathVariable String user, @RequestParam String question) { var memory = MessageWindowChatMemory.builder() .chatMemoryRepository(memoryRepository) .maxMessages(10) .build(); var memoryAdvisor = MessageChatMemoryAdvisor.builder(memory).build(); return this.ai .prompt() .user(question) .advisors(a -> a .advisors(memoryAdvisor) .param(ChatMemory.CONVERSATION_ID, user) ) .call() .content(); } ``` <br><br> Với tất cả mọi thứ đã được thiết lập, bạn có thể khởi chạy chương trình và "thử nghiệm" ngay! <br><br> `http :8080/tkelly/dogs/assistant question=="do you have any neurotic dogs?"` <br><br> Khi mọi thứ đã vào đúng vị trí, bạn sẽ thấy nó trả lời rằng có một chú chó tên Prancer mà chúng ta có thể quan tâm. (Ôi, chúng ta sẽ quan tâm lắm chứ!) <br><br> ```text Có, chúng tôi có một chú chó thần kinh sẵn sàng để nhận nuôi. Gặp gỡ Prancer, một chú chó quỷ ám, thần kinh, không ưa người, động vật hay trẻ nhỏ, và trông giống một con Gremlin. Nếu bạn quan tâm đến Prancer hoặc muốn biết thêm thông tin, cứ hỏi nhé! ``` <br><br> Và thế là xong! Chỉ trong tích tắc, chúng ta đã từ "con số 0" trở thành "người hùng AI", giúp kết nối mọi người với chú chó trong mơ... hay cơn ác mộng của họ! <br><br> **Đưa AI ra môi trường sản phẩm: "Chơi lớn" luôn!** <br><br> Spring AI không chỉ dừng lại ở mấy dự án "thú cưng" vui vui đâu nhé! Nó được thiết kế cho các ứng dụng cấp doanh nghiệp, và để "ăn khớp" với các ứng dụng Spring hiện có của bạn. Để đưa ứng dụng AI "ra trận", bạn cần đảm bảo các yếu tố bảo mật, khả năng mở rộng và khả năng quan sát. Cùng "mổ xẻ" từng phần nhé: <br><br> **1. Bảo mật: Chắc chắn như "áo giáp"!** <br> Việc dùng Spring Security để "khóa chặt" ứng dụng web này thì "dễ như ăn kẹo" rồi. Bạn có thể dùng `Principal#getName` của người dùng đã xác thực làm ID cuộc trò chuyện luôn! Còn dữ liệu trong các collection thì sao? MongoDB có cơ chế <a href="https://www.mongodb.com/products/capabilities/security/encryption/?utm_campaign=devrel&utm_source=third-party-content&utm_medium=cta&utm_content=Spring+AI+MongoDB+RAG&utm_term=tim.kelly">mã hóa dữ liệu tại chỗ</a> cực kỳ mạnh mẽ để bảo vệ thông tin của bạn. <br><br> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/security_shield.png' alt='Bảo mật trong ứng dụng AI'> <br><br> **2. Khả năng mở rộng (Scalability): Đón "ngàn người truy cập"!** <br> Mỗi khi bạn gửi một yêu cầu HTTP đến LLM (hoặc nhiều database), bạn đang "chặn" luồng (thread) thực thi của mình. Một luồng đáng lẽ phải hoạt động lại cứ ngồi "chơi xơi nước" chờ đợi thì phí phạm quá! Java 21 đã mang đến cho chúng ta "virtual threads" (luồng ảo) – những "siêu nhân" có thể cải thiện đáng kể khả năng mở rộng cho các dịch vụ bị giới hạn bởi IO (input/output). Chỉ cần bật nó trong `application.properties`: <br><br> ```properties spring.threads.virtual.enabled=true ``` <br><br> Thay vì ngồi chờ IO hoàn thành, virtual threads sẽ "giải phóng" tài nguyên, giúp ứng dụng của bạn mở rộng hiệu quả hơn khi tải trọng tăng cao. <br><br> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/scalability_icon.png' alt='Khả năng mở rộng với Virtual Threads'> <br><br> **3. GraalVM Native Images: Ứng dụng "siêu gọn, siêu nhanh"!** <br> <a href="https://www.graalvm.org/">GraalVM</a> có khả năng biên dịch ứng dụng Java thành các file thực thi gốc (native executables), giúp giảm đáng kể thời gian khởi động và mức tiêu thụ bộ nhớ. Đây là cách để biên dịch ứng dụng Spring AI của bạn: <br><br> Nếu bạn đã cài đặt GraalVM làm SDK, bạn có thể biến ứng dụng Spring AI này thành một native image cụ thể cho từng hệ điều hành và kiến trúc một cách dễ dàng: <br><br> ```bash ./mvnw -DskipTests -Pnative native:compile ``` <br><br> Việc này có thể mất khoảng một phút hoặc hơn trên hầu hết các máy, nhưng khi hoàn thành, bạn có thể chạy file binary đó một cách dễ dàng: <br><br> ```bash ../target/mongodb ``` <br><br> Chương trình này sẽ khởi động nhanh hơn RẤT NHIỀU so với khi chạy trên JVM! Bạn có thể cân nhắc tắt `ApplicationRunner` mà chúng ta đã tạo trước đó, vì nó sẽ thực hiện IO khi khởi động, làm chậm quá trình này. Trên máy của tôi, nó khởi động chưa đến một phần mười giây! Thậm chí còn tuyệt vời hơn, bạn sẽ thấy ứng dụng này chỉ chiếm một phần RẤT NHỎ bộ nhớ RAM so với khi chạy trên JVM. <br><br> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/graalvm_icon.png' alt='GraalVM Native Images'> <br><br> **4. Đóng gói cho Docker: "Chạy mọi nơi"!** <br> Bạn có thể nói: "Nghe hay đấy, nhưng tôi cần ứng dụng này chạy trên MỌI nền tảng đám mây, và điều đó có nghĩa là phải đóng gói nó thành Docker image!" Dễ ợt! <br><br> ```bash ./mvnw -DskipTests -Pnative spring-boot:build-image ``` <br><br> Chờ một chút nhé, có thể mất thêm một phút nữa. Khi hoàn thành, bạn sẽ thấy tên của Docker image đã được tạo ra. Bạn có thể chạy nó, nhớ ghi đè các host và port mà nó tham chiếu trên máy chủ của bạn nhé. Thật ngạc nhiên, trên macOS, ứng dụng này khi chạy trên một máy ảo macOS mô phỏng Linux lại còn chạy nhanh hơn (ngay từ đầu luôn!) so với chạy trực tiếp trên macOS. Thật đáng kinh ngạc! <br><br> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/docker_icon.png' alt='Đóng gói Docker'> <br><br> **5. Khả năng quan sát (Observability): Luôn "để mắt" đến ứng dụng!** <br> Ứng dụng này "ngon" đến mức tôi cá nó sẽ lên báo, y như chú chó Prancer của chúng ta vậy! Và khi điều đó xảy ra, bạn nên theo dõi tài nguyên hệ thống và quan trọng hơn là số lượng token đã dùng. Mọi yêu cầu gửi đến LLM đều tốn chi phí – ít nhất là độ phức tạp, nếu không muốn nói là tiền bạc. Spring Boot Actuator, được hỗ trợ bởi <a href="https://micrometer.io/">Micrometer</a>, cung cấp các chỉ số (metrics) ngay lập tức. <br><br> Truy cập endpoint metrics để theo dõi mức sử dụng token và các chỉ số quan trọng khác: <br><br> `http://localhost:8080/actuator/metrics` <br><br> Tuyệt vời! Bạn có thể sử dụng Micrometer để chuyển tiếp các chỉ số đó đến cơ sở dữ liệu <a href="https://www.mongodb.com/docs/manual/core/timeseries-collections/?utm_campaign=devrel&utm_source=third-party-content&utm_medium=cta&utm_content=Spring+AI+MongoDB+RAG&utm_term=tim.kelly">MongoDB time-series</a> của bạn để có một "bảng điều khiển" tổng quan, một dashboard duy nhất! <br><br> <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/observability_dashboard.png' alt='Dashboard giám sát ứng dụng'> <br><br> **MongoDB Atlas: Đầy đủ tính năng và khả năng mở rộng!** <br> <a href="https://www.mongodb.com/atlas/database/?utm_campaign=devrel&utm_source=third-party-content&utm_medium=cta&utm_content=Spring+AI+MongoDB+RAG&utm_term=tim.kelly">MongoDB Atlas</a> là dịch vụ đám mây được quản lý cho MongoDB, cung cấp khả năng mở rộng và tính sẵn sàng cao trên nhiều nhà cung cấp dịch vụ đám mây. Với Atlas, MongoDB sẽ lo liệu mọi thứ từ sao lưu, giám sát, nâng cấp và vá lỗi – giúp bạn tập trung vào việc xây dựng ứng dụng, chứ không phải "đau đầu" duy trì hạ tầng. Nếu quản lý database không phải là "nghề chính" của bạn, tại sao không giao phó nó cho những người đã tạo ra MongoDB chứ? <br><br> **Lời kết** <br> Vậy là xong! Bạn vừa xây dựng một ứng dụng AI "xịn sò", sẵn sàng cho môi trường sản xuất, được cung cấp bởi Spring AI và MongoDB chỉ trong "nháy mắt"! Chúng ta mới chỉ "gãi nhẹ" bề mặt của tảng băng chìm thôi. Hãy khám phá Spring AI 1.0 ngay hôm nay tại <a href="https://start.spring.io">Spring Initializr</a> và tìm hiểu thêm về MongoDB Atlas cùng <a href="https://docs.spring.io/spring-ai/reference/api/vectordbs/mongodb.html">hỗ trợ vector store</a> nhé. <br><br> Nếu bạn muốn xem thêm những gì bạn có thể làm với MongoDB, Spring và AI, hãy xem hướng dẫn của chúng tôi về <a href="https://dev.to/mongodb/building-a-real-time-ai-fraud-detection-system-with-spring-kafka-and-mongodb-2jbn">xây dựng hệ thống phát hiện gian lận AI thời gian thực với Spring Kafka và MongoDB</a>. Nếu bạn chỉ muốn bắt đầu với Spring và MongoDB, chúng tôi cũng có một <a href="https://www.mongodb.com/developer/products/mongodb/springdata-getting-started-with-java-mongodb/?utm_campaign=devrel&utm_source=third-party-content&utm_medium=cta&utm_content=Spring+AI+MongoDB+RAG&utm_term=tim.kelly">hướng dẫn về điều đó</a>!