HR LLM Wiki - Your Second Brain
A personal knowledge base powered by a local LLM (Ollama). Inspired by Andrej Karpathy's philosophy: simple systems, raw text, let the LLM do the work.
No vector databases. No embeddings. No cloud. No numpy.
📁 Folder Structure
hr-llm-wiki-ak/
├── data/ ← Your .md files live here (auto-created on ingest)
├── logs/ ← Rotating log files (auto-created)
├── cache/ ← File read cache (auto-managed)
│
├── config.py ← ⭐ Central config — change model, ports, limits here
├── logger.py ← Console + rotating file logger shared by all modules
│
├── ingest.py ← Convert PDF / DOCX / TXT → Markdown
├── file_loader.py ← Load + mtime-cache .md files
├── chunker.py ← Split large docs by heading + fixed-size overlap chunks
├── search.py ← BM25 retrieval (no embeddings)
├── context_builder.py ← Trim + assemble LLM context within token budget
├── llm.py ← Ollama (llama3 / mistral / phi3) interface
├── prompt_templates.py ← Wikipedia JSON, search, and comparison prompts
│
├── api.py ← FastAPI: /ask /search /compare /reindex /health
├── ui.py ← Streamlit: Ask · Search · Compare tabs
├── cli.py ← ⭐ Full CLI: ask / search / ingest / list / stats
│
├── requirements.txt
└── README.md
⚙️ Setup
1. Install Python dependencies
pip install -r requirements.txt
2. Install & start Ollama
# Install from https://ollama.com
ollama pull llama3.2:1b # or: ollama pull mistral / ollama pull phi3
ollama serve # starts on http://localhost:11434
3. Ingest your documents
python ingest.py path/to/your/file.pdf
# Supported: .pdf .docx .txt .md
🚀 Run
API + UI (full stack)
# Terminal 1
python api.py # FastAPI at http://localhost:8000
# Terminal 2
streamlit run ui.py # UI at http://localhost:8501
🔌 API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Ollama status + file count |
| GET | /files |
List indexed .md files |
| POST | /reindex |
Reload all files from /data |
| GET | /search?q=... |
BM25 keyword search (no LLM) |
| GET | /ask?q=... |
Full wiki answer via LLM |
| GET | /compare?q=... |
Cross-document comparison via LLM |
# Examples
curl "http://localhost:8000/ask?q=What+is+the+sick+leave+policy"
curl "http://localhost:8000/compare?q=How+do+the+cyber+and+conduct+policies+differ+on+email"
curl -X POST "http://localhost:8000/reindex"
🧠 Architecture
[PDF / DOCX / TXT]
│
▼ ingest.py
[.md files in /data]
│
▼ file_loader.py (mtime cache)
[doc dicts: filename, path, content]
│
▼ chunker.py (split by heading → fixed-size overlap chunks)
[chunk dicts: heading, content, search_text]
│
▼ search.py (BM25 scoring + filename/heading bonuses)
[top-k docs with matched sections]
│
▼ context_builder.py (trim to token budget, label sources)
[context string ≤ 14,000 chars]
│
▼ llm.py → Ollama
[structured JSON: title, summary, sections]
│
▼ api.py / cli.py / ui.py
[Wikipedia-style output]
🔧 Configuration (config.py)
| Setting | Default | Description |
|---|---|---|
DEFAULT_MODEL |
llama3.2:1b |
Ollama model to use |
MAX_CONTEXT_CHARS |
14000 |
~3500 tokens |
DEFAULT_TOP_K |
3 |
Docs retrieved per query |
CHUNK_SIZE |
1200 |
Chars per chunk |
CHUNK_OVERLAP |
150 |
Overlap between chunks |
BM25_K1 |
1.5 |
Term freq saturation |
BM25_B |
0.75 |
Length normalization |
API_PORT |
8000 |
FastAPI port |
LOG_LEVEL |
INFO |
DEBUG / INFO / WARNING |
💡 Design Decisions
| Problem | Solution | Why |
|---|---|---|
| Retrieval | BM25 in search.py |
~60 lines, zero deps, beats most vector RAG on exact-match |
| Large docs | Heading + overlap chunker | Only relevant paragraphs reach the LLM |
| Context management | Hard char trim in context_builder.py |
Never overflow the LLM context window |
| Caching | mtime dict in file_loader.py |
Re-reads only changed files |
| LLM output | Strict JSON prompt | Structured, parseable, Wikipedia-style |
| Multi-doc queries | /compare endpoint + compare_prompt |
Same retrieval, different prompt |
| Logging | Rotating file + console in logger.py |
Audit trail for every query |
This system is featured in Signature Projects on the portfolio.