diff --git a/HDSD.md b/HDSD.md new file mode 100644 index 0000000..e5a0c8c --- /dev/null +++ b/HDSD.md @@ -0,0 +1,309 @@ +# Hướng Dẫn Sử Dụng — Blog RAG + +## Mục lục + +1. [Yêu cầu hệ thống](#1-yêu-cầu-hệ-thống) +2. [Cài đặt](#2-cài-đặt) +3. [Cấu hình API](#3-cấu-hình-api) +4. [Sử dụng dữ liệu có sẵn](#4-sử-dụng-dữ-liệu-có-sẵn) +5. [Crawl dữ liệu mới](#5-crawl-dữ-liệu-mới) +6. [Build index](#6-build-index) +7. [Chạy giao diện web](#7-chạy-giao-diện-web) +8. [Chế độ CLI](#8-chế-độ-cli) +9. [Cấu hình nâng cao](#9-cấu-hình-nâng-cao) +10. [Xử lý sự cố](#10-xử-lý-sự-cố) + +--- + +## 1. Yêu cầu hệ thống + +- Python 3.10+ +- RAM: tối thiểu 4GB (để load embedding model) +- Dung lượng ổ cứng: ~500MB cho dữ liệu + index + +## 2. Cài đặt + +```bash +# Clone repo +git clone +cd orangepi-rag + +# Tạo virtual environment +python -m venv .venv +# Windows: +.venv\Scripts\activate +# Linux/macOS: +source .venv/bin/activate + +# Cài dependencies +pip install -r requirements.txt + +# Nếu dùng CPU-only (không có GPU), dùng file riêng: +pip install -r requirements-cpu.txt +``` + +## 3. Cấu hình API + +```bash +cp .env.example .env +``` + +Chỉnh sửa file `.env`: + +```env +# BẮT BUỘC: API key cho LLM (OpenAI hoặc tương thích OpenAI) +LLM_API_KEY=sk-... + +# Tuỳ chọn: thay đổi base URL (dùng Together.ai, Groq, v.v.) +# LLM_BASE_URL=https://api.groq.com/openai/v1 + +# Tuỳ chọn: thay đổi model +# LLM_MODEL=llama-3.1-70b-versatile + +# Tuỳ chọn: chỉ cần khi crawl dữ liệu mới +# FIRECRAWL_API_KEY=fc-... +``` + +**Hỗ trợ API:** + +| Nhà cung cấp | Base URL | Ví dụ model | +|---|---|---| +| OpenAI | `https://api.openai.com/v1` | `gpt-4o-mini` | +| Groq | `https://api.groq.com/openai/v1` | `llama-3.1-70b-versatile` | +| Together.ai | `https://api.together.xyz/v1` | `meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo` | +| Ollama (local) | `http://localhost:11434/v1` | `llama3.1` | + +## 4. Sử dụng dữ liệu có sẵn + +Dữ liệu `orangepi_data/` đã được crawl sẵn từ blog orangepi.vn với 199 bài viết, 36 model Orange Pi. + +### Bước 1: Build index lần đầu + +```bash +python rag_app.py --build --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index +``` + +> Quá trình này mất ~2-5 phút tùy máy (load embedding model + tạo vectors cho 901 chunks). + +### Bước 2: Chạy web + +```bash +python web_app.py --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index --port 5000 +``` + +Mở trình duyệt: `http://localhost:5000` + +### Cách dùng trên web + +1. Nhấn **nút +** (góc trên sidebar) để tạo phiên chat mới +2. Nhập câu hỏi vào ô nhập liệu +3. Nhấn **Enter** hoặc nút gửi để nhận câu trả lời +4. Mỗi câu trả lời hiển thị **thời gian xử lý** và **nguồn** (tên bài viết + link) +5. Nhấn **biểu tượng mặt trăng/ngày** ở sidebar để chuyển Dark/Light mode + +### Ví dụ câu hỏi + +- "Orange Pi 5 dùng chip gì?" +- "So sánh Orange Pi Zero 2 và Raspberry Pi Zero 2" +- "Cài Home Assistant trên Orange Pi Zero2 như thế nào?" +- "Orange Pi nào chạy được RISC-V?" +- "Các hệ điều hành nào hỗ trợ Orange Pi 5?" + +## 5. Crawl dữ liệu mới + +> **Lưu ý:** Cần `FIRECRAWL_API_KEY` trong file `.env`. + +### Crawl blog bất kỳ (tổng quát) + +```bash +# Crawl 5 bài đầu tiên (test) +python crawl_blog.py --sitemap https://example.com/post-sitemap.xml --limit 5 + +# Crawl tất cả bài viết +python crawl_blog.py --sitemap https://example.com/post-sitemap.xml --all + +# Dùng file từ khóa tuỳ chỉnh +python crawl_blog.py --sitemap https://example.com/post-sitemap.xml --all --keywords my_keywords.json + +# Xuất ra thư mục tuỳ chỉnh +python crawl_blog.py --sitemap https://example.com/post-sitemap.xml --all --out-dir ./my_blog_data +``` + +### Crawl orangepi.vn (đặc thù) + +```bash +# Crawl 5 bài +python crawl_orangepi_blog.py --limit 5 + +# Crawl tất cả +python crawl_orangepi_blog.py --all +``` + +### File từ khóa (keywords.json) + +Định dạng danh mục: + +```json +[ + { + "category": "hardware", + "keywords": ["Orange Pi 5", "Orange Pi Zero", "RK3588"] + }, + { + "category": "software", + "keywords": ["Docker", "Ubuntu", "Armbian"] + }, + { + "category": "smart_home", + "keywords": ["Home Assistant", "MQTT", "Node-RED"] + } +] +``` + +Xem file mẫu: `keywords_example.json` + +## 6. Build index + +Sau khi crawl xong, build FAISS index: + +```bash +# Build từ dữ liệu mới crawl +python rag_app.py --build --data-dir ./blog_data --index-dir ./blog_data/rag_index + +# Build từ dữ liệu orangepi có sẵn +python rag_app.py --build --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index +``` + +### Kiểm tra retrieval (không cần LLM) + +```bash +python rag_app.py --retrieve-only --query "Orange Pi 5 dùng chip gì" --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index +``` + +## 7. Chạy giao diện web + +```bash +# Dữ liệu orangepi.vn có sẵn +python web_app.py --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index --port 5000 + +# Dữ liệu blog riêng +python web_app.py --data-dir ./blog_data --index-dir ./blog_data/rag_index --port 5000 + +# Tuỳ chọn khác +python web_app.py --host 0.0.0.0 --port 8080 --debug +``` + +### Tuỳ chọn CLI + +| Tuỳ chọn | Mặc định | Mô tả | +|---|---|---| +| `--host` | `0.0.0.0` | Địa chỉ bind | +| `--port` | `5000` | Cổng lắng nghe | +| `--data-dir` | `.` | Thư mục dữ liệu (chứa `chunks.jsonl`) | +| `--index-dir` | `./rag_index` | Thư mục FAISS index | +| `--debug` | `false` | Chế độ debug | + +### Tính năng web + +- **Quản lý phiên chat:** Tạo, xóa, chuyển đổi giữa các phiên +- **Lịch sử hội đồng:** Mỗi phiên lưu riêng, hỗ trợ ngữ cảnh 10 tin nhắn gần nhất +- **Hiển thị markdown:** Headings, code blocks, tables, blockquotes, links +- **Thời gian xử lý:** Hiển thị bên dưới mỗi câu trả lời +- **Nguồn trích dẫn:** Link đến bài viết gốc +- **Dark/Light mode:** Nhấn biểu tượng ở sidebar header +- **Topic guard:** Tự động từ chối câu hỏi ngoài phạm vi dữ liệu + +## 8. Chế độ CLI + +```bash +# Hỏi một câu +python rag_app.py --query "Orange Pi 5 dùng chip gì" --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index + +# Chat interactive +python rag_app.py --interactive --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index + +# Chỉ test retrieval (không gọi LLM) +python rag_app.py --retrieve-only --query "Home Assistant" --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index +``` + +### Tuỳ chọn CLI + +| Tuỳ chọn | Mặc định | Mô tả | +|---|---|---| +| `--data-dir` | `.` | Thư mục dữ liệu | +| `--index-dir` | `./rag_index` | Thư mục FAISS index | +| `--build` | - | Build index từ chunks | +| `--query` | - | Câu hỏi (một lần) | +| `--interactive` | - | Chế độ chat interactive | +| `--retrieve-only` | - | Chỉ test retrieval, không gọi LLM | +| `--top-k` | `5` | Số chunks trả về | +| `--embed-model` | `paraphrase-multilingual-MiniLM-L12-v2` | Embedding model | +| `--llm-model` | `gpt-4o-mini` | Tên model LLM | +| `--llm-base-url` | `https://api.openai.com/v1` | Base URL API | + +## 9. Cấu hình nâng cao + +### Biến môi trường + +| Biến | Mô tả | Mặc định | +|---|---|---| +| `LLM_API_KEY` | API key cho LLM | - | +| `LLM_BASE_URL` | Base URL API | `https://api.openai.com/v1` | +| `LLM_MODEL` | Model name | `gpt-4o-mini` | +| `FIRECRAWL_API_KEY` | API key cho Firecrawl | - | +| `RAG_DATA_DIR` | Thư mục dữ liệu | `.` | +| `RAG_INDEX_DIR` | Thư mục index | `./rag_index` | +| `RAG_TOP_K` | Số chunks retrieval | `5` | +| `RAG_MAX_HISTORY` | Số tin nhắn ngữ cảnh | `10` | +| `RAG_EMBED_MODEL` | Embedding model | `paraphrase-multilingual-MiniLM-L12-v2` | + +### Thay đổi embedding model + +```bash +# Dùng model khác +python rag_app.py --build --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index \ + --embed-model "sentence-transformers/all-MiniLM-L6-v2" +``` + +> **Lưu ý:** Khi thay đổi embedding model cần build lại index. + +## 10. Xử lý sự cố + +### Lỗi `ModuleNotFoundError: No module named 'torch'` + +```bash +pip install torch +# Hoặc dùng bản CPU: +pip install -r requirements-cpu.txt +``` + +### Lỗi `FIRECRAWL_API_KEY is not set` + +Đảm bảo file `.env` đã được tạo và điền đúng key. Chỉ cần thiết khi crawl dữ liệu mới. + +### Lỗi `LLM_API_KEY is not set` + +Đảm bảo file `.env` có `LLM_API_KEY=sk-...`. Nếu chỉ muốn test retrieval mà không cần LLM: + +```bash +python rag_app.py --retrieve-only --query "câu hỏi" --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index +``` + +### Lỗi encoding trên Windows + +Nếu gặp lỗi ký tự tiếng Việt, đảm bảo terminal dùng UTF-8: + +```bash +chcp 65001 +python web_app.py --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index --port 5000 +``` + +### Web không load được + +- Kiểm tra port chưa bị chiếm: `netstat -ano | findstr :5000` +- Thử port khác: `--port 8080` +- Bật debug mode: `--debug` + +--- + +**Copyright © 2026 — Blog RAG Solution — TTAI Solutions Software** diff --git a/README.md b/README.md index c632a45..3e744b9 100644 --- a/README.md +++ b/README.md @@ -1,326 +1,57 @@ -# Blog RAG Toolkit +# Blog RAG -Bộ công cụ RAG (Retrieval-Augmented Generation) hoàn chỉnh: **crawl** blog, **trích xuất** từ khóa, **chia nhỏ** nội dung, **truy vấn** bằng LLM, và **giao diện web** để chat. +Hệ thống Retrieval-Augmented Generation (RAG) cho blog orangepi.vn — trợ lý AI trả lời câu hỏi về sản phẩm Orange Pi dựa trên dữ liệu thực tế. ---- +## Tính năng -## Mục lục +- Truy vấn ngữ nghĩa qua FAISS + sentence-transformers (hỗ trợ tiếng Việt) +- Giao diện web với quản lý phiên chat, lịch sử hội đồng, hiển thị markdown +- Chế độ Light/Dark mode +- Dữ liệu sẵn có: 199 bài viết orangepi.vn, 36 model Orange Pi +- API tương thích OpenAI (OpenAI, Together.ai, Groq, v.v.) -1. [Cài đặt](#1-cài-đặt) -2. [Cấu hình API](#2-cấu-hình-api) -3. [Bước 1 — Crawl dữ liệu blog](#bước-1--crawl-dữ-liệu-blog) -4. [Bước 2 — Xây dựng chỉ mục & truy vấn](#bước-2--xây-dựng-chỉ-mục--truy-vấn) -5. [Bước 3 — Giao diện web](#bước-3--giao-diện-web) -6. [Tham khảo](#tham-khảo) - ---- - -## 1. Cài đặt +## Cài đặt ```bash -git clone -cd orangepi-rag pip install -r requirements.txt +cp .env.example .env +# Chỉnh sửa .env — điền LLM_API_KEY ``` -**Yêu cầu:** Python 3.10+, tài khoản [Firecrawl](https://www.firecrawl.dev) (cho crawl), tài khoản LLM — OpenAI / Together.ai / Groq / Ollama (cho truy vấn). - ---- - -## 2. Cấu hình API - -Tạo file `.env` ở thư mục gốc dự án: +## Sử dụng nhanh ```bash -# ─── BẮT BUỘC cho crawl ─── -FIRECRAWL_API_KEY=fc-... - -# ─── BẮT BUỘC cho RAG query ─── -OPENAI_API_KEY=sk-... - -# ─── TÙY CHỌN ─── -# Thay đổi LLM provider (mặc định: OpenAI) -# LLM_BASE_URL=https://api.together.xyz/v1 -# LLM_MODEL=meta-llama/Llama-3-70b-chat-hf +# Chạy web UI với dữ liệu orangepi.vn có sẵn +python web_app.py --data-dir ./orangepi_data --index-dir ./orangepi_data/rag_index --port 5000 ``` -> Lấy Firecrawl key tại: https://www.firecrawl.dev -> Lấy OpenAI key tại: https://platform.openai.com/api-keys +Mở trình duyệt tại `http://localhost:5000` → tạo phiên mới → đặt câu hỏi. ---- +> **Lưu ý:** Lần đầu cần build index trước khi chạy web. Xem hướng dẫn chi tiết tại [HDSD.md](HDSD.md). -## Bước 1 — Crawl dữ liệu blog - -### 1.1 Tạo file từ khóa - -Tạo file `keywords.json` chứa các từ khóa cần trích xuất từ blog: - -```json -[ - { - "category": "hardware", - "keywords": ["Raspberry Pi", "Orange Pi", "Arduino", "ESP32"] - }, - { - "category": "software", - "keywords": ["Docker", "Ubuntu", "Home Assistant", "MQTT"] - } -] -``` - -Xem file mẫu tại `keywords_example.json`. - -### 1.2 Tìm sitemap URL - -Blog WordPress thường có sitemap tại: -- `https://example.com/post-sitemap.xml` (Yoast SEO) -- `https://example.com/sitemap.xml` (generic) - -### 1.3 Chạy crawl - -```bash -# Test thử 5 bài viết -python crawl_blog.py \ - --sitemap https://example.com/post-sitemap.xml \ - --limit 5 \ - --out-dir ./blog_data - -# Crawl toàn bộ blog -python crawl_blog.py \ - --sitemap https://example.com/post-sitemap.xml \ - --all \ - --keywords keywords.json \ - --out-dir ./blog_data - -# Crawl với tùy chỉnh -python crawl_blog.py \ - --sitemap https://example.com/post-sitemap.xml \ - --all \ - --keywords keywords.json \ - --out-dir ./blog_data \ - --sleep 1.5 \ - --max-words 500 \ - --overlap-words 80 \ - --language vi -``` - -### 1.4 Kết quả - -Sau khi crawl xong, thư mục `blog_data/` sẽ chứa: - -``` -blog_data/ -├── articles.jsonl # Mỗi dòng = 1 bài viết (title, text, keywords, ...) -├── chunks.jsonl # Mỗi dòng = 1 đoạn nhỏ (~650 từ) cho embedding -├── keywords.json # File từ khóa đã dùng -├── urls.json # Danh sách URL tìm được từ sitemap -├── raw/.json # Response gốc từ Firecrawl -├── markdown/.md # Markdown đã làm sạch -├── errors.jsonl # Các URL lỗi -└── summary.json # Tổng kết crawl -``` - -### 1.5 Tham số đầy đủ - -| Tham số | Mặc định | Mô tả | -|---------|----------|-------| -| `--sitemap` | (bắt buộc) | URL sitemap | -| `--out-dir` | `./blog_data` | Thư mục output | -| `--keywords` | `/keywords.json` | File từ khóa JSON | -| `--limit N` | 5 | Crawl N bài đầu tiên | -| `--all` | — | Crawl toàn bộ | -| `--sleep SEC` | 1.0 | Nghỉ giữa mỗi request (giây) | -| `--force` | — | Crawl lại kể cả đã có cache | -| `--max-words N` | 650 | Số từ tối đa mỗi chunk | -| `--overlap-words N` | 100 | Số từ overlap giữa các chunk | -| `--language` | `en` | Mã ngôn ngữ mặc định | - ---- - -## Bước 2 — Xây dựng chỉ mục & truy vấn - -### 2.1 Xây dựng chỉ mục FAISS - -```bash -python rag_app.py \ - --build \ - --data-dir ./blog_data \ - --index-dir ./rag_index -``` - -Kết quả: - -``` -rag_index/ -├── faiss.index # Chỉ mục vector FAISS -└── chunks.jsonl # Bản sao chunks cho retrieval -``` - -### 2.2 Truy vấn đơn lẻ - -```bash -python rag_app.py \ - --query "Cài Docker trên Raspberry Pi như thế nào?" \ - --data-dir ./blog_data \ - --index-dir ./rag_index -``` - -### 2.3 Chat interactive (terminal) - -```bash -python rag_app.py \ - --interactive \ - --data-dir ./blog_data \ - --index-dir ./rag_index -``` - -Gõ câu hỏi, nhận câu trả lời. Nhấn `Ctrl+C` để thoát. - -### 2.4 Kiểm tra retrieval không cần LLM - -```bash -python rag_app.py \ - --query "Home Assistant" \ - --retrieve-only \ - --data-dir ./blog_data \ - --index-dir ./rag_index -``` - -Chỉ hiển thị các chunk liên quan nhất, không gọi LLM. - -### 2.5 Tham số đầy đủ - -| Tham số | Mặc định | Mô tả | -|---------|----------|-------| -| `--data-dir` | `.` | Thư mục chứa chunks.jsonl | -| `--index-dir` | `./rag_index` | Thư mục chỉ mục FAISS | -| `--build` | — | Xây dựng chỉ mục | -| `--query` | — | Câu hỏi cần trả lời | -| `--interactive` | — | Chế độ chat terminal | -| `--retrieve-only` | — | Chỉ test retrieval, không dùng LLM | -| `--top-k` | 5 | Số chunk trả về | -| `--embed-model` | `paraphrase-multilingual-MiniLM-L12-v2` | Mô hình embedding | -| `--llm-model` | `gpt-4o-mini` | Tên mô hình LLM | -| `--llm-base-url` | `https://api.openai.com/v1` | URL API LLM | - ---- - -## Bước 3 — Giao diện web - -### 3.1 Khởi động server - -```bash -python web_app.py \ - --data-dir ./blog_data \ - --index-dir ./rag_index \ - --port 5000 -``` - -Mở trình duyệt: **http://localhost:5000** - -### 3.2 Sử dụng - -1. Nhấn **+** để tạo phiên chat mới -2. Gõ câu hỏi vào ô nhập, nhấn **Enter** để gửi -3. Xem câu trả lời + nguồn bài viết -4. Tạo nhiều phiên để hỏi nhiều chủ đề khác nhau -5. Xóa lịch sử hoặc xóa phiên bằng nút trên header - -### 3.3 Tính năng - -| Tính năng | Mô tả | -|-----------|-------| -| **Quản lý phiên** | Tạo, chuyển đổi, xóa nhiều phiên chat | -| **Lịch sử chat** | Lưu vào SQLite, giữ lại khi reload trang | -| **Nhớ ngữ cảnh** | 10 tin nhắn cuối được đưa vào prompt để giữ context | -| **Tránh lạc đề** | LLM được hướng dẫn chỉ trả lời trong phạm vi dữ liệu | -| **Trích nguồn** | Mỗi câu trả lời có link đến bài viết gốc | -| **Responsive** | Giao diện thích ứng desktop và mobile | - -### 3.4 Tham số - -| Tham số | Mặc định | Mô tả | -|---------|----------|-------| -| `--host` | `0.0.0.0` | Host để bind | -| `--port` | `5000` | Port | -| `--debug` | — | Chế độ debug | -| `--data-dir` | `.` | Thư mục dữ liệu | -| `--index-dir` | `./rag_index` | Thư mục chỉ mục | - -### 3.5 Biến môi trường web - -```bash -# Trong file .env -RAG_DATA_DIR=./blog_data -RAG_INDEX_DIR=./rag_index -RAG_LLM_MODEL=gpt-4o-mini -RAG_LLM_BASE_URL=https://api.openai.com/v1 -RAG_TOP_K=5 -RAG_MAX_HISTORY=10 # Số tin nhắn giữ context -``` - -### 3.6 API endpoints - -| Method | Path | Mô tả | -|--------|------|-------| -| `GET` | `/api/sessions` | Danh sách phiên | -| `POST` | `/api/sessions` | Tạo phiên mới | -| `DELETE` | `/api/sessions/` | Xóa phiên | -| `GET` | `/api/sessions//messages` | Lịch sử tin nhắn | -| `POST` | `/api/sessions//messages` | Gửi tin nhắn, nhận câu trả lời | -| `POST` | `/api/sessions//clear` | Xóa lịch sử phiên | -| `GET` | `/api/stats` | Thống kê hệ thống | - ---- - -## Tham khảo - -### Cấu trúc thư mục hoàn chỉnh +## Cấu trúc thư mục ``` orangepi-rag/ -├── .env # API keys (FIRECRAWL, OPENAI) -├── requirements.txt # Python dependencies -├── crawl_blog.py # Crawler tổng quát -├── crawl_orangepi_blog.py # Crawler orangepi.vn -├── rag_app.py # RAG query (CLI) -├── web_app.py # Giao diện web (Flask) -├── keywords_example.json # Mẫu file từ khóa -├── templates/ -│ └── index.html # HTML template -├── static/ -│ ├── style.css # CSS -│ └── app.js # JavaScript -├── blog_data/ # Dữ liệu crawl được +├── web_app.py # Web server (Flask) +├── rag_app.py # Core RAG pipeline (FAISS + LLM) +├── crawl_blog.py # Crawler tổng quát (Firecrawl + sitemap) +├── crawl_orangepi_blog.py # Crawler đặc thù orangepi.vn +├── keywords_example.json # Ví dụ từ khóa theo danh mục +├── requirements.txt # Dependencies +├── .env.example # Template API key +├── orangepi_data/ # Dữ liệu orangepi.vn đã crawl │ ├── articles.jsonl │ ├── chunks.jsonl -│ ├── keywords.json -│ ├── urls.json -│ ├── raw/ -│ ├── markdown/ -│ ├── errors.jsonl -│ └── summary.json -├── rag_index/ # Chỉ mục FAISS -│ ├── faiss.index -│ └── chunks.jsonl -└── rag_chat.db # SQLite chat history +│ ├── orangepi_models.json +│ └── ... +├── templates/index.html # HTML template +├── static/ +│ ├── app.js # Frontend JavaScript +│ └── style.css # Styles (Dark/Light theme) +└── HDSD.md # Hướng dẫn sử dụng chi tiết ``` -### Lưu ý khi dùng LLM provider khác +## Tài liệu -```bash -# Together.ai -LLM_BASE_URL=https://api.together.xyz/v1 -LLM_MODEL=meta-llama/Llama-3-70b-chat-hf -OPENAI_API_KEY=... - -# Groq -LLM_BASE_URL=https://api.groq.com/openai/v1 -LLM_MODEL=llama-3.1-70b-versatile -OPENAI_API_KEY=... - -# Ollama (chạy local) -LLM_BASE_URL=http://localhost:11434/v1 -LLM_MODEL=llama3 -OPENAI_API_KEY=ollama -``` +- [HDSD.md](HDSD.md) — Hướng dẫn sử dụng chi tiết (cài đặt, crawl dữ liệu, build index, chạy web)