Triển khai RAG ở quy mô lớn: Bài học từ việc xử lý hơn 10.000 tin tuyển dụng mỗi ngày
Tôi đã xây dựng một pipeline RAG cho một trang web tuyển dụng. Nó hoạt động tốt trong môi trường staging nhưng gặp khó khăn khi chịu tải thực tế. Việc xử lý hàng nghìn tin tuyển dụng mỗi ngày đòi hỏi nhiều hơn là chỉ một vector store tốt. Bạn phải hiểu được điểm yếu khiến hệ thống bị lỗi.
Dưới đây là những bài học của tôi về chunking, embeddings, chi phí và khả năng quan sát (observability).
- Đừng đoán mò chiến lược chunking của bạn
Hầu hết các hướng dẫn đều coi chunking chỉ là một thiết lập đơn giản. Trong môi trường production, chiến lược của bạn sẽ quyết định độ chính xác và chi phí.
Tôi đã thử nghiệm ba phương pháp cho các tin tuyển dụng:
- Fixed-size chunks (chia nhỏ theo kích thước cố định): Phương pháp này đã thất bại. Nó chia cắt các phần như "yêu cầu" (requirements) và "quyền lợi" (benefits) tại các điểm ngẫu nhiên. Điều này tạo ra kết quả truy xuất nhiễu.
- Semantic chunking (chia nhỏ theo ngữ nghĩa): Phương pháp này tốt hơn nhưng không nhất quán. Một số chunk quá dài và số khác lại quá ngắn.
- Recursive character splitting with overlap (chia nhỏ theo ký tự đệ quy có độ chồng lấp): Đây là phương pháp hiệu quả nhất. Tôi chia nhỏ dựa trên các dòng mới và câu. Tôi sử dụng kích thước 400 token với độ chồng lấp (overlap) là 50 token. Điều này đảm bảo các câu trải dài qua hai chunk vẫn được kết nối với nhau.
Mẹo chuyên nghiệp: Hãy chuẩn hóa dữ liệu trước khi chunking. Các nguồn khác nhau như Greenhouse hay Lever trả về các định dạng khác nhau. Hãy làm sạch văn bản trước để bộ chia (chunker) của bạn thấy được một cấu trúc nhất quán.
- Embeddings: Chi phí so với Độ chính xác
Tôi đã thử nghiệm Llama 3.1 qua Ollama so với OpenAI text-embedding-3-small. Mô hình chạy cục bộ (local model) thì miễn phí nhưng gặp khó khăn với các thuật ngữ chuyên ngành như "equity compensation" (đền bù bằng cổ phần). Nó tạo ra các kết quả nhiễu. OpenAI tốn kém hơn nhưng cung cấp các kết quả khớp chính xác. Tôi đã chọn OpenAI vì việc truy xuất kém sẽ làm tốn kém hơn cho các lượt gọi LLM sau đó.
Để tiết kiệm thời gian, tôi gửi yêu cầu theo lô (batch). Tôi gửi tối đa 100 chunk trong một lần gọi. Điều này giúp giảm độ trễ và giữ cho pipeline hoạt động nhanh chóng.
- Sự đánh đổi của Vector Store
Tôi đã sử dụng Pinecone để làm prototype vì nó thiết lập rất nhanh. Tuy nhiên, khi mở rộng quy mô, chi phí tăng lên quá cao.
Tôi đã chuyển sang dùng pgvector bên trong PostgreSQL.
- Nó đòi hỏi nhiều công sức thiết lập hơn.
- Nó giúp tiết kiệm một lượng tiền khổng lồ.
- Nó cung cấp tính nhất quán giao dịch (transactional consistency). Vì các embeddings nằm cùng một cơ sở dữ liệu với dữ liệu công việc, bạn sẽ có một nguồn sự thật duy nhất (one source of truth). Bạn không cần phải đồng bộ hóa hai hệ thống khác nhau.
- Kiểm soát chi phí LLM
Việc chấm điểm (scoring) mọi tin tuyển dụng bằng GPT-4o rất tốn kém. Tôi đã sử dụng ba chiến thuật để giảm chi phí:
- OpenAI Batch API: Tôi xử lý các tác vụ chấm điểm qua đêm. Điều này giúp nhận được mức chiết khấu lớn.
- Caching: Tôi lưu bộ nhớ đệm (cache) kết quả cho các hồ sơ ứng viên lặp lại.
- Phân tầng mô hình (Model tiering): Tôi sử dụng GPT-4o-mini cho các vị trí phổ biến như "Sales Representative". Tôi chỉ sử dụng GPT-4o cho các vị trí ngách (niche roles) nơi mà độ chính xác là tối quan trọng.
- Xây dựng khả năng quan sát (Observability) trước tiên
Pipeline của tôi đã từng gặp lỗi mà không có cảnh báo (failed silently). Dữ liệu sai định dạng đã tạo ra các chunk rỗng, và hệ thống đã bỏ qua chúng mà không báo lỗi.
Tôi đã khắc phục điều này bằng cách thêm logging có cấu trúc với một correlation ID. Điều này cho phép tôi truy vết một tin tuyển dụng từ khâu nạp dữ liệu (ingestion) đến khâu chấm điểm. Cuối cùng, tôi đã có thể thấy nguồn dữ liệu nào đang gây ra lỗi.
Bài học lớn nhất: Hầu hết các vấn đề đến từ dữ liệu lộn xộn, chứ không phải từ AI. Hãy sửa chữa hệ thống đường ống dữ liệu (data plumbing) của bạn trước.
Optional learning community: https://t.me/GyaanSetuAi
