File size: 14,149 Bytes
1704b48
 
0828c2c
a4c67f0
 
1704b48
0d6e301
a4c67f0
0828c2c
a4c67f0
0828c2c
a4c67f0
 
 
1704b48
 
0828c2c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492077d
0828c2c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492077d
0828c2c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0d6e301
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
---
title: ProfillyBot
emoji: πŸ€–
colorFrom: gray
colorTo: indigo
sdk: gradio
sdk_version: 6.0.2
python_version: '3.11'
app_file: gradio_app.py
pinned: true
license: mit
short_description: RAG chatbot for professional profile Q&A
thumbnail: >-
  https://cdn-uploads.huggingface.co/production/uploads/656f08be4893a2a26d8b17c9/1jY92qjVxCyZ0_BVHVAOF.png
---

# πŸ€– ProfillyBot

<p align="center">
  <img src="docs/banner_image.png" alt="ProfillyBot Banner" width="100%" />
</p>

<p align="center">
  <img src="https://github.com/Tin-Hoang/slm-profile-rag/actions/workflows/test-python310.yml/badge.svg" alt="Python 3.10"/>
  <img src="https://github.com/Tin-Hoang/slm-profile-rag/actions/workflows/test-python311.yml/badge.svg" alt="Python 3.11"/>
  <img src="https://github.com/Tin-Hoang/slm-profile-rag/actions/workflows/test-python312.yml/badge.svg" alt="Python 3.12"/>
  <img src="https://github.com/Tin-Hoang/slm-profile-rag/actions/workflows/test-docker.yml/badge.svg" alt="Docker"/>
  <img src="https://github.com/Tin-Hoang/slm-profile-rag/actions/workflows/lint.yml/badge.svg" alt="Lint"/>
  <img src="https://github.com/Tin-Hoang/slm-profile-rag/actions/workflows/code-quality.yml/badge.svg" alt="Code Quality"/>
  <img src="https://github.com/Tin-Hoang/slm-profile-rag/actions/workflows/security-scan.yml/badge.svg" alt="Security Scan"/>
  <a href="https://codecov.io/gh/Tin-Hoang/slm-profile-rag">
    <img src="https://codecov.io/gh/Tin-Hoang/slm-profile-rag/graph/badge.svg?token=MW4RD9R66J" alt="Codecov"/>
  </a>
</p>

**ProfillyBot** is a RAG (Retrieval Augmented Generation) chatbot that answers questions about your professional profile using your resume, project reports, and other documents.

## ✨ Features

- πŸ“„ **Multi-format Support**: Process PDF, Word, HTML, and text documents
- πŸ” **Hybrid Search**: Combines BM25 (keyword) + Vector (semantic) search with Reciprocal Rank Fusion
- 🧠 **Extensible Retrieval**: Pluggable strategy system - easily add new retrieval methods (PageIndex, GraphRAG, etc.)
- πŸ“Œ **Main Document Support**: Guaranteed context - critical information always available, auto-format detection
- πŸ¦™ **Ollama Integration**: Run small language models locally
- 🎨 **Clean UI**: Streamlit-based interface
- βš™οΈ **Highly Configurable**: YAML-based settings for easy customization
- πŸš€ **HuggingFace Spaces Ready**: Deploy with one click
- ✨ **Smart Response Enhancement**: Automatically removes negative language and adds professional, recruiter-friendly tone

## πŸ—οΈ Architecture

<p align="center">
  <img src="docs/arch_system_design.png" alt="System Architecture Diagram" width="800" />
</p>
<p align="center"><em>Overall system design showing the RAG pipeline and component interactions</em></p>

Detailed architecture can be found in [ARCHITECTURE.md](ARCHITECTURE.md).

## πŸ› οΈ Tech Stack

- **Python 3.10+** with UV package manager
- **LangChain** for RAG pipeline
- **ChromaDB** for vector storage
- **rank-bm25** for lexical search
- **Ollama** for local LLM serving
- **Streamlit** for web interface
- **sentence-transformers** for embeddings
- **tiktoken** for token counting
- **Ruff** for linting/formatting

## πŸ“¦ Installation

### Prerequisites

1. **Python 3.10+**
2. **UV Package Manager**: Install via:
   ```bash
   curl -LsSf https://astral.sh/uv/install.sh | sh
   ```
3. **Ollama**: Install from [ollama.ai](https://ollama.ai)

### Setup

1. **Clone the repository**:
   ```bash
   git clone <your-repo-url>
   cd profillybot
   ```

2. **Install dependencies with UV**:
   ```bash
   uv venv
   source .venv/bin/activate  # On Windows: .venv\Scripts\activate
   uv pip install -r requirements.txt
   ```

3. **Set up environment variables**:
   ```bash
   cp .env.template .env
   # Edit .env with your settings
   ```

4. **Configure the chatbot**:
   - Edit `config.yaml` to set your name, title, and preferences
   - Adjust model settings, chunking parameters, and system prompt

5. **Add your documents**:
   ```bash
   # Place your PDFs, Word docs, HTML files in:
   mkdir -p data/documents
   # Copy your resume, project reports, LinkedIn profile, etc.
   ```

6. **Create your Main Profile Document** (πŸ“Œ **Recommended**):

   The Main Document feature ensures critical information is **always** included in responses, never missed by vector search.

   ```bash
   # Create main profile document (any format: .md, .txt, .pdf, .docx, .html)
   nano data/documents/main_profile.md
   ```

   **Include essential information:**
   - Full name, title, contact info
   - Current role and key responsibilities
   - Core skills and expertise
   - Major projects with metrics
   - Education and certifications

   **Example structure:**
   ```markdown
   # Your Name
   **Title**: Your Professional Title
   **Email**: your.email@example.com

   ## Professional Summary
   Brief summary of your experience...

   ## Core Skills
   - Skill 1, Skill 2, Skill 3

   ## Current Role
   ### Company - Role (Dates)
   - Achievement 1 with metrics
   - Achievement 2 with metrics

   ## Education
   - Degree, University, Year
   ```

   **Why use this?**
   - βœ… **Guaranteed Context**: Critical info never missed by vector similarity search
   - βœ… **Priority Positioning**: Appears BEFORE retrieved chunks (higher LLM attention)
   - βœ… **Auto-Format Detection**: Supports MD, TXT, PDF, DOCX, HTML automatically
   - βœ… **Smart Token Management**: LLM-based summarization if content exceeds 10k tokens

   The feature is enabled by default in `config.yaml`. See [docs/MAIN_DOCUMENT_GUIDE.md](docs/MAIN_DOCUMENT_GUIDE.md) for advanced configuration.

7. **Pull Ollama model**:
   ```bash
   ollama pull llama3.2:3b
   # Or other small models: phi3:mini, gemma2:2b
   ```

## πŸš€ Usage

### Local Development

1. **Build the retrieval index** (default: hybrid BM25 + Vector):
   ```bash
   # Build with default strategy from config (bm25_vector)
   python -m src.build_vectorstore

   # Or specify a strategy explicitly
   python -m src.build_vectorstore --strategy bm25_vector  # Hybrid (recommended)
   python -m src.build_vectorstore --strategy vector       # Vector-only
   python -m src.build_vectorstore --strategy bm25         # BM25-only
   ```

2. **Run the Streamlit app**:
   ```bash
   streamlit run app.py
   ```

3. **Open browser**: Navigate to `http://localhost:8501`

**Screenshot Example:**

<p align="center">
  <img src="docs/screenshot-example.png" alt="ProfillyBot UI Example" width="800" />
</p>
<p align="center"><em>Streamlit chatbot interface in action</em></p>

### 🐳 Docker Compose

Run the complete stack with Docker Compose (includes Ollama):

```bash
# Copy environment template
cp .env.template .env

# Start all services (app + Ollama)
docker compose up -d

# View logs
docker compose logs -f

# Stop services
docker compose down
```

Access the app at `http://localhost:7860`

### πŸ“‹ Makefile Commands

Use the Makefile for common development tasks:

```bash
make help          # Show all available commands
```

| Category | Command | Description |
|----------|---------|-------------|
| **Setup** | `make setup` | Initial project setup (copy env, install deps) |
| | `make install` | Install dependencies |
| | `make install-dev` | Install with dev dependencies |
| **Development** | `make run` | Run Streamlit app locally |
| | `make index-build` | Build vector and BM25 indexes |
| | `make index-rebuild` | Rebuild indexes from scratch |
| **Quality** | `make test` | Run tests |
| | `make test-cov` | Run tests with coverage |
| | `make lint` | Run linter (ruff) |
| | `make format` | Format code |
| | `make check` | Run all checks |
| **Docker** | `make docker-up` | Start all services |
| | `make docker-down` | Stop all services |
| | `make docker-rebuild` | Rebuild and restart |
| | `make docker-logs` | Show logs (follow mode) |
| | `make docker-clean` | Stop and remove volumes |
| **Ollama** | `make ollama-pull` | Pull configured model |
| | `make ollama-list` | List available models |
| **Cleanup** | `make clean` | Remove build artifacts |
| | `make clean-all` | Clean everything + indexes |

## 🌐 Deployment to Hugging Face Spaces (ZeroGPU)

ProfillyBot on Spaces uses **Gradio + ZeroGPU** with `Qwen/Qwen3.5-4B`
(`gradio_app.py`). Streamlit (`app.py`) remains available for local/Ollama/Groq.

### Live Space

- [huggingface.co/spaces/MinhDS/ProfillyBot](https://huggingface.co/spaces/MinhDS/ProfillyBot)

### Deploy / update

1. Create a Gradio Space with **ZeroGPU** hardware (Pro account required to *create* ZeroGPU Spaces).
2. Ensure this repo has:
   - `data/documents/MY_CV_2_0.pdf` (or your profile docs)
   - Pre-built `chroma_db/` + `bm25_index/` (recommended) β€” or let the app build on first start
3. Push to the Space:
   ```bash
   git remote add hf https://huggingface.co/spaces/MinhDS/ProfillyBot
   git push hf main
   ```
4. Confirm Space Settings: SDK **Gradio**, hardware **ZeroGPU**, app file `gradio_app.py`.

### Important Notes for HF Spaces

- **ZeroGPU is Gradio-only** β€” do not use the Docker/Ollama `Dockerfile` on ZeroGPU
- **SLM**: `Qwen/Qwen3.5-4B` via the `transformers` provider
- **Embeddings**: `all-MiniLM-L6-v2` on CPU (does not consume GPU quota)
- **Vector DB**: Pre-build indexes locally when possible for faster cold start
- **Secrets**: optional `HF_TOKEN` only if you switch to a gated model

## βš™οΈ Configuration

### `config.yaml` - Main Settings

```yaml
profile:
  name: "Your Name"  # ← Change this!
  title: "Your Title"

llm:
  model: "llama3.2:3b"  # Choose your model
  temperature: 0.7

# Retrieval Strategy Configuration
retrieval:
  strategy: "bm25_vector"  # Options: vector, bm25, bm25_vector
  final_k: 4               # Number of documents to return

  vector:
    search_type: "similarity"
    k: 10                  # Docs to retrieve before fusion

  bm25:
    k: 10                  # Docs to retrieve before fusion
    tokenizer: "simple"

  fusion:
    algorithm: "rrf"       # Reciprocal Rank Fusion
    weights:
      vector: 0.7          # 70% weight on semantic search
      bm25: 0.3            # 30% weight on keyword search

document_processing:
  chunk_size: 1000
  chunk_overlap: 200
```

### `.env` - Environment Variables

```bash
OLLAMA_BASE_URL=http://localhost:11434
CHROMA_PERSIST_DIR=./chroma_db
```

## πŸ“š Project Structure

```
profillybot/
β”œβ”€β”€ app.py                          # Streamlit app entry point
β”œβ”€β”€ pyproject.toml                  # UV/pip dependencies & config
β”œβ”€β”€ config.yaml                     # RAG & LLM settings
β”œβ”€β”€ .env.template                   # Environment variables template
β”œβ”€β”€ Makefile                        # Development commands
β”œβ”€β”€ docker-compose.yml              # Docker Compose configuration
β”œβ”€β”€ Dockerfile                      # Docker image (standalone with Ollama)
β”œβ”€β”€ Dockerfile.app                  # Docker image (for Compose)
β”œβ”€β”€ README.md
β”œβ”€β”€ data/
β”‚   └── documents/                  # Your profile documents
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ document_processor.py       # Load & chunk documents
β”‚   β”œβ”€β”€ vectorstore.py              # ChromaDB operations
β”‚   β”œβ”€β”€ llm_handler.py              # Ollama/LLM interface
β”‚   β”œβ”€β”€ rag_pipeline.py             # RAG chain logic
β”‚   β”œβ”€β”€ main_document_loader.py     # Main document management
β”‚   β”œβ”€β”€ response_enhancer.py        # Response post-processing
β”‚   β”œβ”€β”€ config_loader.py            # Load config.yaml & .env
β”‚   β”œβ”€β”€ build_vectorstore.py        # CLI to build indexes
β”‚   └── retrieval/                  # Extensible retrieval system
β”‚       β”œβ”€β”€ __init__.py
β”‚       β”œβ”€β”€ base.py                 # BaseRetrieverStrategy ABC
β”‚       β”œβ”€β”€ factory.py              # RetrieverFactory
β”‚       β”œβ”€β”€ fusion.py               # RRF and fusion algorithms
β”‚       β”œβ”€β”€ stores/
β”‚       β”‚   └── bm25_store.py       # BM25 index storage
β”‚       └── strategies/
β”‚           β”œβ”€β”€ vector.py           # Vector-only strategy
β”‚           β”œβ”€β”€ bm25.py             # BM25-only strategy
β”‚           └── bm25_vector.py      # Hybrid BM25+Vector strategy
β”œβ”€β”€ chroma_db/                      # Vector database (auto-generated)
β”œβ”€β”€ bm25_index/                     # BM25 index (auto-generated)
└── tests/                          # Unit tests
```

## 🎯 Recommended Models for HF Spaces

| Model | Size | Speed | Quality | HF Spaces Tier |
|-------|------|-------|---------|----------------|
| `llama3.2:3b` | 3B | Fast | Good | Free βœ… |
| `phi3:mini` | 3.8B | Fast | Good | Free βœ… |
| `gemma2:2b` | 2B | Very Fast | Decent | Free βœ… |
| `llama3.1:8b` | 8B | Medium | Great | Upgraded GPU |

## πŸ” LLM Tracing with LangSmith

Monitor prompts, responses, and latency using [LangSmith](https://smith.langchain.com).

### Setup

1. **Get API Key**: Sign up at [smith.langchain.com](https://smith.langchain.com) (free tier: 5,000 traces/month)

2. **Configure `.env`**:
   ```bash
   LANGCHAIN_TRACING_V2=true
   LANGCHAIN_API_KEY=your_api_key_here
   LANGCHAIN_PROJECT=profillybot
   ```

3. **Restart the app**:
   ```bash
   streamlit run app.py
   ```

### What You Can See

- Full prompts sent to LLM (system prompt + context + question)
- Complete LLM responses
- Latency breakdown per step
- Token usage (when available)

## πŸ”§ Troubleshooting

### Ollama Connection Issues
```bash
# Check Ollama is running
ollama list

# Start Ollama service
ollama serve
```

### ChromaDB / Index Errors
```bash
# Rebuild all indexes from scratch
rm -rf chroma_db/ bm25_index/
python -m src.build_vectorstore --force-rebuild
```

### HuggingFace Spaces Issues
- Check logs in the Space's "Logs" tab
- Ensure `requirements.txt` is generated: `uv pip compile pyproject.toml -o requirements.txt`
- Verify GPU/CPU settings match your model size

## πŸ“„ License

MIT License - see LICENSE file

---

**Note**: Remember to update `config.yaml` with your personal information before deploying!