Chuyển đến nội dung chính

Spring AI và Oracle Database: Trao cho tác nhân AI bộ nhớ

Đừng để LLM quên ngữ cảnh. Tìm hiểu cách xây dựng tác nhân AI với bộ nhớ theo hồi ức, ngữ nghĩa và thủ tục bằng Spring AI và Oracle Database.
· 10 phút đọc

Khám phá cùng AI

Mọi LLM đều có cùng một vấn đề: nó quên sạch mọi thứ ngay khi cuộc trò chuyện kết thúc, đôi khi còn trong cả những cuộc trò chuyện dài. Bạn dành hai mươi phút để giải thích thiết lập dự án, các ràng buộc và sở thích của mình, thì nó trả lời chuẩn không cần chỉnh. Đóng tab, mở phiên mới, và nó lại chào bạn như người xa lạ. Toàn bộ ngữ cảnh, biến mất.

Nếu bạn muốn xây dựng một tác nhân AI, thứ thực sự ghi nhớ ngữ cảnh và biết những điều về miền của bạn, bạn cần cho nó bộ nhớ. Dạng bộ nhớ thực dụng, nơi nó thực sự nhớ những gì bạn đã nói và có thể tra cứu các sự kiện bạn đã dạy nó.

Đây là một POC tôi xây dựng để làm đúng điều đó. Ba loại bộ nhớ, một cơ sở dữ liệu, không nhiều mã. Mã nguồn đầy đủ có trên GitHub.

Kiến trúc tác nhân AI với Spring AI

Mermaid diagram 1

Hình 1. Kiến trúc bộ nhớ tác nhân end-to-end với Spring AI và Oracle AI Database 26ai

Ngăn xếp:

  • Spring Boot 3.5.11 + Spring AI 1.1.2 cho backend
  • Ollama cho suy luận chat (qwen2.5), chạy cục bộ
  • Oracle AI Database 26ai cho cả ba kho bộ nhớ, với Hybrid Vector Indexes (tìm kiếm vector + từ khóa hợp nhất bằng Reciprocal Rank Fusion) cho truy xuất ngữ nghĩa và một mô hình ONNX (all-MiniLM-L12-v2) nạp trực tiếp để tạo embedding trong cơ sở dữ liệu
  • Streamlit cho web UI nhanh-gọn (~100 dòng Python)
  • Java 21, Gradle 8.14

Ba loại bộ nhớ của tác nhân AI

Mọi người nói về bộ nhớ theo hồi ức (episodic), bộ nhớ ngữ nghĩa (semantic), bộ nhớ thủ tục (procedural) và bộ nhớ làm việc (working) cho tác nhân. 

Bộ nhớ làm việc chỉ là cửa sổ ngữ cảnh của LLM; “sổ nháp” đang hoạt động cho yêu cầu hiện tại. Nó không được lưu, nên không có gì để xây. Tôi đã triển khai ba loại còn lại:

Bộ nhớ theo hồi ức: Lưu lịch sử chat

Bộ nhớ theo hồi ức là lịch sử chat. Tác nhân nhớ những gì bạn đã nói trước đó trong cuộc trò chuyện. "Tên tôi là Victor" ở tin nhắn số 1 nghĩa là đến tin nhắn số 50 nó vẫn biết tên bạn. Điều này được lưu thành các hàng trong một bảng quan hệ.

The agent remembers context from earlier in the conversation (episodic memory).

Hình 2. Bộ nhớ theo hồi ức là lịch sử chat

Bộ nhớ ngữ nghĩa: Tích hợp RAG

Bộ nhớ ngữ nghĩa là kiến thức miền. Bạn nạp cho tác nhân các sự kiện (tài liệu sản phẩm, chính sách công ty, v.v.) và nó truy xuất các mục liên quan khi trả lời câu hỏi. 

Đây là RAG (Retrieval-Augmented Generation): văn bản được chuyển thành vector dày đặc (embedding) ánh xạ ý nghĩa vào không gian hình học, sao cho văn bản tương đồng về ngữ nghĩa nằm gần nhau. 

Nhưng tìm kiếm thuần vector có điểm mù; nó nắm bắt ý nghĩa tốt nhưng gặp khó với các thuật ngữ chính xác như ID đơn hàng hoặc mã sản phẩm. 

Vì vậy POC này dùng Hybrid Vector Indexes của Oracle, chạy tìm kiếm tương đồng vector và tìm kiếm từ khóa (lexical) song song, rồi hợp nhất hai danh sách kết quả bằng Reciprocal Rank Fusion (RRF). 

Khi truy vấn, tìm kiếm lai sẽ tìm các tài liệu khớp theo ý nghĩa hoặc theo thuật ngữ chính xác, và chèn các kết quả hàng đầu vào prompt của LLM. Embedding được tính ngay trong cơ sở dữ liệu bởi một mô hình ONNX (all-MiniLM-L12-v2, 384 chiều) được nạp trực tiếp vào Oracle, không cần gọi API embedding bên ngoài. Cách hoạt động chi tiết ở bên dưới.

The agent answers a policy question using RAG-retrieved documents (semantic memory).

Hình 3. Bộ nhớ ngữ nghĩa như kiến thức miền

Bộ nhớ thủ tục: Gọi công cụ của LLM

Bộ nhớ thủ tục là “cách thức”, các quy trình từng bước mà tác nhân biết cách thực thi. Tra cứu đơn hàng, khởi tạo trả hàng, chuyển cấp cho hỗ trợ. Trong Spring AI, đây là các phương thức có chú thích @Tool mà LLM có thể gọi khi nó quyết định một tác vụ cần hành động chứ không chỉ câu trả lời.

The agent lists orders and looks up order details via tool calls (procedural memory).

Hình 4. Bộ nhớ thủ tục hoạt động, nơi tác nhân dùng lời gọi công cụ để truy vấn dữ liệu đơn hàng trực tiếp và trả về kết quả theo tác vụ.

Mermaid diagram 2

Hình 5. Luồng yêu cầu qua AgentController, nơi bộ nhớ theo hồi ức, ngữ nghĩa và thủ tục được kết hợp từ các bảng Oracle AI Database trước khi tạo phản hồi với Ollama.

Cả hai bảng cùng nằm trong một Oracle Database. Không Pinecone. Không Redis. Không cơ sở dữ liệu thứ hai. Một pool kết nối, một bộ thông tin xác thực, một thứ để theo dõi.

Triển khai Spring AI Tools

Bộ nhớ thủ tục được triển khai dưới dạng các phương thức có chú thích @Tool trong một thành phần Spring truy vấn các bảng cơ sở dữ liệu thực. Dưới đây là hai phương thức tiêu biểu, đã đơn giản hóa cho dễ hiểu; lớp đầy đủ có tổng cộng sáu công cụ (xem AgentTools.java):

@Tool(description = "Look up the status of a customer order by its order ID. " +
        "Returns the current status including shipping information.")
public String lookupOrderStatus(
        @ToolParam(description = "The order ID to look up, e.g. ORD-1001") String orderId) {
    // Fetches order from DB via JPA, returns formatted status string
}

@Tool(description = "Initiate a product return for a given order. " +
        "Validates the order exists, checks that it is in DELIVERED status, " +
        "and verifies the return is within the 30-day return window.")
public String initiateReturn(
        @ToolParam(description = "The order ID to return") String orderId,
        @ToolParam(description = "The reason for the return") String reason) {
    // Validates order exists, checks DELIVERED status and 30-day window, updates status via JPA
}

Phần mô tả @Tool cho LLM biết khi nào dùng mỗi phương thức, và @ToolParam mô tả các tham số. Khi người dùng nói "Tôi muốn trả đơn ORD-1001," LLM đọc mô tả công cụ, quyết định initiateReturn là thủ tục phù hợp, trích xuất tham số từ cuộc trò chuyện, gọi phương thức và đưa kết quả vào phản hồi của nó.

Bốn công cụ còn lại là getCurrentDateTime (lấy ngày/giờ hiện tại từ cơ sở dữ liệu), listOrders, escalateToSupport và listSupportTickets. Chúng theo cùng một mẫu: truy vấn cơ sở dữ liệu qua JPA hoặc JDBC. LLM quyết định khi nào hành động; các phương thức Java định nghĩa cách thực hiện.

Xây dựng Spring Boot Controller

Controller kết nối mọi thứ; hai advisor, sáu công cụ, một ChatClient. Đây là phần lõi, đã lược giản cho rõ ràng (bản đầy đủ bổ sung xác thực đầu vào, xử lý lỗi và các endpoint quản lý hội thoại. Xem AgentController.java):

@RestController
@RequestMapping("/api/v1/agent")
public class AgentController {

    public AgentController(ChatClient.Builder builder,
                           JdbcChatMemoryRepository chatMemoryRepository,
                           JdbcTemplate jdbcTemplate,
                           AgentTools agentTools) {
        // Builds a ChatClient with:
        //   - MessageChatMemoryAdvisor (episodic: last 100 messages per conversation)
        //   - RetrievalAugmentationAdvisor + OracleHybridDocumentRetriever (semantic: hybrid search)
        //   - AgentTools via .defaultTools() (procedural: 6 @Tool methods)
        //   - System prompt defining the agent persona and tool usage rules
    }

    @PostMapping("/chat")
    public ResponseEntity<String> chat(
            @RequestBody String message,
            @RequestHeader("X-Conversation-Id") String conversationId) {
        // Sends message to ChatClient with conversation ID, returns LLM response
    }

    @PostMapping("/knowledge")
    public ResponseEntity<String> addKnowledge(@RequestBody String content) {
        // Inserts text into POLICY_DOCS table via JDBC (hybrid index handles embedding)
    }
}

Hai endpoint, hai advisor, sáu công cụ, một ChatClient. Hãy phân tách ba loại bộ nhớ.

Quản lý bộ nhớ chat với Spring Advisors

Mẫu advisor của Spring AI là nơi phép màu diễn ra. Advisor chặn mọi lời gọi đến LLM và có thể chỉnh sửa prompt trước khi gửi đi cũng như xử lý phản hồi khi trả về.

MessageChatMemoryAdvisor xử lý bộ nhớ theo hồi ức. Trước mỗi lần gọi LLM, nó tải 100 tin nhắn gần nhất cho cuộc trò chuyện hiện tại từ bảng SPRING_AI_CHAT_MEMORY và thêm chúng vào đầu prompt. Sau phản hồi, nó lưu trao đổi mới. Cuộc trò chuyện được xác định bằng header X-Conversation-Id: ID khác, bộ nhớ khác.

Triển khai RAG với trình truy xuất tài liệu

RetrievalAugmentationAdvisor xử lý bộ nhớ ngữ nghĩa. Trước mỗi lần gọi LLM, một OracleHybridDocumentRetriever tùy chỉnh gọi DBMS_HYBRID_VECTOR.SEARCH, chạy tìm kiếm tương đồng vector và Oracle Text theo từ khóa song song và hợp nhất kết quả bằng RRF. 

Top 5 tài liệu khớp được chèn vào prompt như ngữ cảnh thông qua một ContextualQueryAugmenter tùy chỉnh coi tài liệu là bổ trợ; LLM được chỉ dẫn chỉ dùng chúng cho câu hỏi chính sách, không cho yêu cầu hành động hoặc ngữ cảnh hội thoại. Điều này ngăn ngữ cảnh RAG ghi đè lịch sử chat hoặc chặn lời gọi công cụ.

Vì sao dùng lai thay vì thuần vector? Embedding dày đặc nắm bắt ý nghĩa: truy vấn về "chính sách trả hàng" sẽ khớp tài liệu về hoàn tiền và đổi trả dù không xuất hiện đúng các từ đó. 

Nhưng chúng yếu ở thuật ngữ chính xác: truy vấn "ORD-1001" kém đi vì mô hình embedding mã hóa ngữ nghĩa, không phải từ khóa. Tìm kiếm lai bao phủ cả hai: phía vector xử lý ý nghĩa, phía từ khóa xử lý khớp chính xác, và RRF gộp hai danh sách kết quả theo vị trí xếp hạng thay vì cố bình thường hóa các điểm số không tương thích.

Đăng ký công cụ tác nhân

AgentTools xử lý bộ nhớ thủ tục. Lời gọi .defaultTools(agentTools) đăng ký cả sáu phương thức có chú thích @Tool từ component. Ở mỗi yêu cầu, LLM nhận mô tả công cụ cùng với thông điệp người dùng. Nếu tác vụ cần hành động, không chỉ truy xuất kiến thức, LLM gọi công cụ phù hợp, nhận kết quả và dệt nó vào phản hồi. Spring AI tự động xử lý giao thức gọi công cụ.

Cả ba loại bộ nhớ chạy ở mỗi yêu cầu. Tác nhân đồng thời nhớ những gì bạn đã nói, tra cứu kiến thức liên quan và biết cách thực hiện tác vụ.

Mermaid diagram 3

Hình 6  Trình tự yêu cầu end-to-end nơi lịch sử chat, ngữ cảnh RAG lai và lời gọi công cụ tùy chọn được kết hợp để tạo và lưu phản hồi cuối cùng.

Tạo endpoint kiến thức RAG

Endpoint /knowledge đơn giản: POST một đoạn văn bản, và nó được chèn vào bảng POLICY_DOCS qua JDBC. Chỉ mục vector lai xử lý embedding tự động bằng mô hình ONNX trong cơ sở dữ liệu, không cần gọi API embedding bên ngoài. Lần tới khi có người hỏi câu liên quan, tìm kiếm lai sẽ tìm thấy nó.

Nạp dữ liệu ban đầu cho Oracle AI database

Một DataSeeder (Spring CommandLineRunner) điền dữ liệu cho cơ sở dữ liệu khi khởi động với 8 đơn hàng demo và 12 tài liệu chính sách (trả hàng, vận chuyển, hỗ trợ, bảo hành, thanh toán, hủy, đổi hàng, vận chuyển quốc tế, quyền riêng tư, khuyến mãi, bảo đảm sản phẩm và đơn hàng số lượng lớn). Các chính sách được nạp từ tệp tài nguyên policies.json và chèn vào bảng POLICY_DOCS qua JDBC. Đơn hàng dùng ngày tương đối để logic cửa sổ trả hàng 30 ngày luôn hoạt động cho mục đích demo. Seeder kiểm tra số lượng hiện có để tránh trùng khi khởi động lại.

Nâng cấp bộ nhớ ngữ nghĩa: Tìm kiếm lai

Phiên bản đầu tiên của POC này dùng QuestionAnswerAdvisor của Spring AI với OracleVectorStore: tìm kiếm tương đồng vector thuần với ngưỡng cosine. Nó hoạt động với câu hỏi sạch, diễn đạt rõ về chính sách. Nhưng nó sụp đổ trước thuật ngữ chính xác và lỗi chính tả. Truy vấn "order ORD-1001" sẽ cố khớp ngữ nghĩa với tài liệu chính sách, điều này vô nghĩa. "retrun polcy" viết sai sẽ mất điểm tương đồng vì mô hình embedding không biết đó là lỗi chính tả.

Tạo hybrid vector index của Oracle

Oracle 26ai cung cấp DBMS_HYBRID_VECTOR.SEARCH, một lời gọi PL/SQL duy nhất chạy tìm kiếm tương đồng vector và tìm kiếm từ khóa Oracle Text song song, rồi hợp nhất kết quả.

Điểm mấu chốt là Reciprocal Rank Fusion (RRF): thay vì cố bình thường hóa điểm cosine (giới hạn 0-1) so với điểm từ khóa BM25 (không giới hạn), nó xếp hạng tài liệu theo vị trí trong mỗi danh sách kết quả. 

Một tài liệu đứng #1 ở kết quả vector và #3 ở kết quả từ khóa sẽ có thứ hạng kết hợp phản ánh cả hai tín hiệu.

Thiết lập là một script SQL một lần nạp một mô hình ONNX embedding vào Oracle AI Database và tạo một chỉ mục lai:

/SQL
-- Load the ONNX model for in-database embeddings
BEGIN
  DBMS_VECTOR.LOAD_ONNX_MODEL(
    directory  => 'DM_DUMP',
    file_name  => 'all_MiniLM_L12_v2.onnx',
    model_name => 'ALL_MINILM_L12_V2'
  );
END;
/

-- Create a hybrid index: vector similarity + Oracle Text keyword search
CREATE HYBRID VECTOR INDEX POLICY_HYBRID_IDX
ON POLICY_DOCS(content)
PARAMETERS('MODEL ALL_MINILM_L12_V2 VECTOR_IDXTYPE HNSW');

Khi chỉ mục đã tồn tại, embedding được tính tự động khi chèn hàng —không cần gọi API embedding bên ngoài.

Tích hợp truy xuất Spring AI

Spring AI QuestionAnswerAdvisor chỉ bọc VectorStore.similaritySearch(), tìm kiếm thuần vector, không gì khác. Để dùng tìm kiếm lai, tôi chuyển sang RetrievalAugmentationAdvisor, lựa chọn dạng mô-đun: nó nhận một DocumentRetriever tùy chỉnh và một QueryAugmenter tùy chỉnh để kiểm soát cách tài liệu được chèn vào prompt.

OracleHybridDocumentRetriever tùy chỉnh triển khai DocumentRetriever và gọi DBMS_HYBRID_VECTOR.SEARCH qua JDBC, truyền tham số JSON chỉ định tên chỉ mục lai, bộ chấm điểm (RRF) và điều kiện từ khóa:

public List<Document> retrieve(Query query) {
    // Builds a JSON spec with hybrid index name, RRF scorer, vector + text search clauses
    // Calls DBMS_HYBRID_VECTOR.SEARCH via JDBC, parses JSON results into List<Document>
}

Điều này bỏ qua hoàn toàn OracleVectorStore cho việc truy xuất. Mệnh đề text.contains dùng khớp từ khóa thuần OR (từ dài hơn 2 ký tự), cho phép stemming tích hợp của Oracle Text xử lý biến thể.

Vì sao tìm kiếm lai cải thiện tác nhân AI

Tác nhân cần truy xuất chính xác để đưa ra quyết định tốt. Nếu bộ nhớ ngữ nghĩa trả về tài liệu chính sách sai hoặc độ tin cậy thấp, LLM có thể bịa tham số công cụ hoặc bỏ qua việc gọi công cụ lẽ ra phải dùng. 

Tìm kiếm lai mang lại ngữ cảnh tin cậy cao hơn, đồng nghĩa quyết định tự động tốt hơn; tác nhân ít có khả năng trích dẫn sai chính sách hoặc bỏ lỡ tài liệu liên quan khi cả ý nghĩa và thuật ngữ chính xác đều được khớp.

Cấu hình cơ sở dữ liệu cho Spring AI

Cấu hình nằm trong một application.yaml duy nhất. Các quyết định chính: Hibernate tự tạo bảng JPA (ddl-auto: update), Spring AI tự tạo bảng bộ nhớ chat (initialize-schema: always), bảng POLICY_DOCS và chỉ mục vector lai được tạo bằng script SQL một lần (setup-hybrid-search.sql), và Oracle UCP dùng chung một pool kết nối cho cả ba loại bộ nhớ. 

Không Flyway, không lớp @Configuration tùy chỉnh. Tự động cấu hình của Spring AI phát hiện driver JDBC của Oracle và đấu nối mọi thứ. Xem đầy đủ application.yaml trong repo.

Xây dựng Web UI bằng Streamlit

Frontend Streamlit gửi thông điệp đến backend và hiển thị phản hồi. Đây là phần lõi (ứng dụng đầy đủ có các nút khởi động nhanh và tính năng cuộc trò chuyện mới. Xem app.py):

def send_message(prompt, url):
    # POSTs plain text to /api/v1/agent/chat with X-Conversation-Id header
    # Renders user message and assistant response in Streamlit chat UI

if prompt := st.chat_input("Type a message..."):
    send_message(prompt, backend_url)

Mỗi phiên nó tạo một UUID làm conversation ID, gửi plain text đến backend và hiển thị phản hồi. Hết.

Chạy POC tác nhân AI tại chỗ

Phiên bản rút gọn: khởi động container Oracle DB, nạp mô hình ONNX và tạo chỉ mục lai (thiết lập một lần), cài Ollama và kéo mô hình chat (qwen2.5), chạy backend Spring Boot với profile local, và tùy chọn khởi động UI Streamlit. Embedding được xử lý trong cơ sở dữ liệu bởi mô hình ONNX, không cần mô hình embedding của Ollama. Hướng dẫn thiết lập đầy đủ có trong README của repo.

Thử nhanh với curl:

curl -X POST http://localhost:8080/api/v1/agent/chat \
  -H "Content-Type: text/plain" \
  -H "X-Conversation-Id: test-1" \
  -d "What orders do I have?"

Kết luận

Thiết lập này là mặc định mạnh mẽ để xây dựng tác nhân thực thụ với Spring AI và Oracle Database vì nó giữ mọi lớp bộ nhớ trong một ngăn xếp vận hành: ngữ cảnh theo hồi ức, truy xuất ngữ nghĩa và hành động thủ tục. Spring AI cung cấp các trừu tượng gọn gàng (Advisors + @Tool) để hành vi tác nhân dễ đọc và kiểm thử, trong khi Oracle xử lý dữ liệu giao dịch và truy xuất lai trong cùng hệ thống, với embedding trong cơ sở dữ liệu và xếp hạng RRF cho độ chính xác tốt hơn ở cả ý nghĩa và thuật ngữ chính xác.

Kết quả là tính thực tiễn: ít thành phần hơn, ít lỗi tích hợp hơn, độ trễ thấp hơn và vận hành sản xuất dễ dàng hơn. Thay vì chắp vá các hệ thống riêng cho lịch sử chat, tìm kiếm vector, tìm kiếm từ khóa và bảng nghiệp vụ, bạn xây dựng trên một nền tảng mà vẫn có truy xuất chất lượng cao cùng thực thi công cụ đáng tin cậy. Với các đội ngũ triển khai tính năng tác nhân, sự kết hợp giữa đơn giản, chính xác và kiểm soát vận hành này rất khó vượt qua.

Để tìm hiểu thêm về tác nhân AI và cách chúng hoạt động, tôi khuyên bạn xem lộ trình kỹ năng AI Agent Fundamentals của DataCamp.

FAQs

Bộ nhớ tác nhân là gì và vì sao quan trọng?

Bộ nhớ tác nhân cho phép hệ thống dùng LLM lưu giữ ngữ cảnh qua nhiều yêu cầu để có thể nhớ cuộc trò chuyện trước đó, truy xuất kiến thức miền và thực thi quy trình một cách nhất quán.

Tôi có cần cơ sở dữ liệu vector riêng cho bộ nhớ ngữ nghĩa không?

Không. Trong hướng dẫn này, Oracle AI Database lưu trữ dữ liệu quan hệ, lịch sử chat và chỉ mục truy xuất lai trong một hệ thống.

Sự khác nhau giữa bộ nhớ theo hồi ức, bộ nhớ ngữ nghĩa và bộ nhớ thủ tục là gì?

Bộ nhớ theo hồi ức lưu lịch sử hội thoại, bộ nhớ ngữ nghĩa lưu kiến thức có thể truy xuất, và bộ nhớ thủ tục định nghĩa các hành động có thể thực thi qua công cụ.

Vì sao dùng truy xuất lai thay vì tìm kiếm thuần vector?

Truy xuất lai kết hợp ý nghĩa ngữ nghĩa và khớp từ khóa chính xác, cải thiện kết quả cho cả câu hỏi mơ hồ lẫn ID hay thuật ngữ chính xác.

Kiến trúc này đã sẵn sàng cho sản xuất chưa?

Đây là nền tảng vững chắc, nhưng môi trường sản xuất vẫn cần xác thực, phân quyền, quan sát hệ thống và kiểm thử tải.

Victor Martin

Tác giả

Victor Martin

Tôi là một kiến trúc sư backend và trưởng nhóm kỹ thuật với hơn một thập kỷ kinh nghiệm xây dựng các hệ thống có khả năng mở rộng, thuần đám mây. Tôi chuyên về kiến trúc phân tán với Java, Kotlin và các nền tảng đám mây hiện đại, tập trung vào thiết kế hướng miền (domain-driven design), hệ thống hướng sự kiện và thiết kế phần mềm thực tiễn.
 
Công việc của tôi tập trung vào thiết kế các nền tảng backend bền bỉ và cải thiện thực hành kỹ thuật, đồng thời ngày càng khám phá cách AI có thể nâng cao năng suất của nhà phát triển và thiết kế hệ thống.

Chủ đề

Liên quan