📋 Project Overview
Hệ thống này là một nền tảng phân tích và truy vấn hồ sơ ứng viên (CV) tự động bằng cách ứng dụng
kiến trúc RAG (Retrieval-Augmented Generation). Hệ thống cho phép người dùng tìm kiếm
thông tin ứng viên trong kho dữ liệu (HR/IT) hoặc tải lên một CV hoàn toàn mới để AI phân tích và
trả lời câu hỏi trực tiếp.
🎯 Key Features:
- Dual extraction: Text-based + OCR (Tesseract) for maximum coverage
- LLM-powered structuring using Qwen2.5:7b
- Semantic chunking with embeddings (nomic-embed-text)
- ChromaDB vector storage for fast retrieval
- Tiered evaluation system optimized for limited hardware
- Hybrid deployment: Local Backend + Cloud Frontend
🏗 System Architecture (Workflow Pipeline)
Hệ thống được thiết kế thành các giai đoạn (Phases) xử lý tuần tự kèm với các tệp dữ liệu đầu ra:
1️⃣
Raw Data Input
Kho dữ liệu CV thô bao gồm 110 CV (HR) + 120 CV (IT) dưới dạng PDF files.
2️⃣
Ingestion (Phase 1)
Trích xuất văn bản sử dụng kép: Text-based extraction + OCR (Tesseract).
3️⃣
LLM Extraction (Phase 2)
Cấu trúc hóa dữ liệu với Qwen2.5:7b theo schema JSON định nghĩa.
4️⃣
Semantic Chunking (Phase 3)
Chia nhỏ data, tạo embeddings, lưu vào ChromaDB.
📝 Chi tiết từng Phase
Phase 1: Ingestion (Trích xuất văn bản)
Đọc và trích xuất nội dung từ các file PDF. Áp dụng cơ chế kép: Text-based (kéo chữ trực tiếp từ file digital) và OCR (Tesseract) (xử lý CV scan).
💡 Chi tiết xây dựng - Phase 1:
Khu vực xử lý chính nằm ở src/ingestion bao gồm 2 engine:
ocr_engine (pytesseract) và pdf_parser. Luồng xử lý thực hiện cả 2 phương pháp
trên cùng file để tránh kết quả None. Kết quả lưu tại data/temp:
ingested_hr.json và ingested_information-technology.json
Phase 2: LLM Extraction (Cấu trúc hóa)
Văn bản hỗn độn từ Phase 1 được đưa qua Qwen2.5:7b chạy cục bộ via Ollama. LLM phân tích và chuẩn hóa theo JSON schema (Profile, Experience, Education, Projects).
💡 Chi tiết xây dựng - Phase 2:
Mục đích: Chuẩn hóa cấu trúc CV do mỗi người viết khác nhau. Đây là phase tốn thời gian nhất (giới hạn phần cứng).
Schema tối giản để inference nhanh. Ghi nhận: 2 file HR + 3 file IT bị hallucination (tổng 230 file, sai số ~2%).
Xử lý tại chunker.py. Kết quả lưu:
backend/processed/hr_extracted.json + it_extracted.json
Phase 3: Semantic Chunking & Indexing
JSON được chia nhỏ theo cụm ngữ nghĩa (Profile, Experience, Education, Project). Chuyển thành Vector dùng nomic-embed-text. Lưu vào ChromaDB.
💡 Chi tiết xây dựng - Phase 3:
Embedding nhanh chóng. Output: data/chunks (chứa chunks để embedding) + data/vector_db (ChromaDB storage).
Mỗi file tạo 5-9 chunks. Phát hiện lỗi Phase 2 tại đây nhưng chi phí re-run Phase 2 quá cao,
nên xử lý ngay trong chunker.py
🧪 Model Evaluation (Tiered Strategy)
Chiến lược Đánh giá Phân tầng tối ưu cho MacBook 16GB. Chia làm 3 Mode qua run_evaluation.py:
🎯
Mode 1: Retrieval
58%
Hit Rate (50 queries)
⏱️
Mode 1: Latency
0.18s
Avg retrieval time
📝
Mode 2: E2E Latency
22.3s
With LLM inference
☁️
Mode 3: Relevance
3.67/5.0
Cloud Judge score
📊 Evaluation Methods
✅ Mode 1 - Retrieval Evaluation:
Script tự động đặt câu hỏi → ChromaDB trả about source_file/chunk_id.
So sánh: if retrieved_id == expected_id: score += 1.
Chi phí phần cứng: ~0%. Test 50 queries.
✅ Mode 2 - Generation Evaluation:
Chọn 15 test chính xác từ Mode 1, inference end-to-end với Qwen2.5:7b local.
Đo độ trễ và chất lượng response.
✅ Mode 3 - Cloud Judge Evaluation:
Call Google Gemini 2.5 Flash API. Chấm điểm 1-5 dựa trên Answer Relevance.
Async + aiohttp optimization.
🚀 Deployment & API Guide
Mô hình Hybrid Deployment tách Backend (Docker/Local) và Frontend (Streamlit Cloud):
FastAPI + ChromaDB được đóng gói Docker, chạy local trên máy tính.
Cung cấp REST API cho upload, truy vấn. Xử lý rác dữ liệu via cleanup_orphan_collections.
# Xây dựng và chạy backend
docker compose up -d --build
# Tắt backend
docker compose down
2
Frontend (Streamlit Cloud)
Web UI hosted trên Streamlit Community Cloud:
https://windycv.streamlit.app
pip install -r frontend/requirements.txt
streamlit run frontend/app.py
3
Tunnel Connection (Ngrok)
Ngrok tạo đường hầm an toàn từ Streamlit Cloud → Backend Local.
Copy URL HTTPS và cập nhật BACKEND_URL trong frontend/app.py.
ngrok http 8000
⚠️ Limitations & Trade-offs
Ngrok URL Dependency: Link thay đổi mỗi lần khởi động, cần update thủ công mỗi ngày
(trừ khi dùng bản trả phí).
Hardware Constraints: Ingestion + Qwen2.5:7b inference tốn nhiều RAM.
Multiple cloud sessions có thể gây bottleneck hoặc treo máy local.
Orphan Sessions: Streamlit làm mới session_state khi reload (F5), mất dấu session_id cũ.
Nếu user không bấm "Xóa CV", collections tạm lưu ổ cứng. Cần giải pháp TTL/cleanup tốt hơn.
🛠 Technology Stack
🔧 Backend
- ✓ FastAPI (REST API)
- ✓ ChromaDB (Vector DB)
- ✓ Qwen2.5:7b (LLM)
- ✓ Ollama (Local LLM runtime)
- ✓ Docker (Container)
🎨 Frontend
- ✓ Streamlit (Web UI)
- ✓ Streamlit Cloud (Hosting)
- ✓ Ngrok (Tunneling)
- ✓ Session Management
📊 Processing
- ✓ Tesseract (OCR)
- ✓ PyPDF (Text extraction)
- ✓ nomic-embed-text (Embeddings)
- ✓ Google Gemini 2.5 (Judge)
📁 Project Structure
IMTSolutions/
├── backend/
│ ├── src/
│ │ ├── ingestion/ # Phase 1: PDF parsing + OCR
│ │ ├── extraction/ # Phase 2: LLM structuring
│ │ ├── rag/ # Phase 3: Chunking + Indexing
│ │ ├── evaluation/ # Metrics & assessment
│ │ └── api/ # FastAPI endpoints
│ ├── scripts/ # Automation scripts
│ └── Dockerfile
├── frontend/
│ └── app.py # Streamlit Web UI
├── notebooks/ # EDA & analysis
├── data/
│ ├── raw/ # Raw PDF files
│ ├── processed/ # Extracted JSON
│ ├── chunks/ # Semantic chunks
│ ├── vector_db/ # ChromaDB storage
│ └── evaluation/ # Test results
└── README.md
Ready to Get Started?
Explore the project and see the RAG pipeline in action.