A local-first Retrieval-Augmented Generation (RAG) application for querying personal notes and documents. The system indexes notes with SentenceTransformer embeddings and FAISS, retrieves relevant chunks for each query, and sends grounded context to a configurable LLM backend.
The application can run from source or as a containerized CLI. It supports one-time queries, an interactive REPL, source citations, smart re-indexing, multiple LLM providers, PDF ingestion, and RAGAS-based evaluation.
- Why RAG for Notes
- Features
- Architecture
- How It Works
- Evaluation
- Tech Stack
- Project Structure
- Local Development
- Usage
- Docker Usage
- Configuration
- Testing
- CI/CD
- Limitations
- Future Work
- License
A note assistant should answer from the current knowledge base without requiring model fine-tuning every time the notes change.
Fine-tuning is usually a poor fit for personal notes because:
- knowledge updates require additional training runs
- model behavior becomes harder to debug and reproduce
- private notes may be mixed into model weights
- hallucination sources are harder to inspect
- infrastructure cost increases for a problem that can be solved with retrieval
This project keeps knowledge external and inspectable. Notes are chunked, embedded, indexed, retrieved at query time, and passed to the LLM as explicit context.
- Ingests
.txt,.md,.pdf, and.pyfiles fromdata/. - Splits documents into overlapping chunks with configurable chunk size and overlap.
- Generates local embeddings with SentenceTransformer.
- Stores vectors in FAISS for similarity search.
- Stores chunk metadata separately in JSONL with an offset index for efficient lookup.
- Supports smart indexing so unchanged files do not need to be reprocessed.
- Supports multiple LLM backends:
- Hugging Face Inference API
- Google Gemini
- OpenAI
- Ollama for local inference
- Grounds answers in retrieved context.
- Supports optional citation output for source visibility.
- Supports streaming responses in REPL mode.
- One-time query mode for shell usage and scripting.
- Interactive REPL mode for repeated queries after the index is loaded.
- Commands for re-indexing, smart updates, configuration inspection, source listing, citation toggling, stream toggling, and evaluation.
- Runtime configuration validation with Pydantic Settings.
- Docker image published through GitHub Container Registry.
- GitHub Actions workflows for tests and container builds.
- Unit tests for ingestion, loading, chunking, indexing, retrieval, metadata storage, and answer generation.
- Latency and runtime logs under
logs/.
Documents in data/
|
v
Loaders (.txt / .md / .pdf / .py)
|
v
Streaming chunking
|
v
SentenceTransformer embeddings
|
+--------------------+
| |
v v
FAISS vector index Metadata store
| |
+---------+----------+
|
v
User query -> query embedding -> top-k retrieval
|
v
Prompt with retrieved context
|
v
LLM backend -> grounded answer + optional citations
-
Configuration validation
- The CLI reads
.envthrough Pydantic Settings. - Required provider/model/key fields are validated before runtime.
- The CLI reads
-
Document loading
- Files in
data/are scanned for supported extensions. - Loaders normalize source content into text streams.
- Files in
-
Chunking
- Documents are split into overlapping chunks.
- Chunk size and overlap are controlled by
CHUNK_SIZEandCHUNK_OVERLAP.
-
Indexing
- Chunks are embedded with
sentence-transformers/all-MiniLM-L6-v2by default. - Embeddings are normalized and stored in a FAISS index.
- Chunk text and source metadata are stored outside the vector index.
- Chunks are embedded with
-
Retrieval
- The user query is embedded with the same embedding model.
- FAISS retrieves the top-k nearest chunks.
- Low-scoring matches are filtered with
MIN_RETRIEVAL_SCORE.
-
Answer generation
- Retrieved chunks are inserted into the LLM prompt as context.
- The answer can include source citations.
- REPL mode keeps the loaded index available for faster repeated queries.
The CLI includes a RAGAS evaluation mode:
rag-app --evalEvaluation reports are generated locally under src/rag_notes_helper/eval/reports/ and are ignored by git by default because they are runtime artifacts.
A recent local evaluation run used:
| Setting / Metric | Value |
|---|---|
TOP_K |
5 |
LLM_TEMPERATURE |
0.1 |
MIN_RETRIEVAL_SCORE |
0.3 |
CHUNK_SIZE |
800 |
CHUNK_OVERLAP |
200 |
| Faithfulness | 1.000 |
| Answer relevancy | 0.750 |
| Context precision | 0.797 |
| Context recall | 1.000 |
| Evaluation provider | Gemini |
These metrics are produced by the evaluation pipeline under src/rag_notes_helper/eval/ and provide a repeatable way to compare retrieval and generation settings.
| Layer | Tools |
|---|---|
| CLI / runtime | Python, argparse, Pydantic Settings |
| Retrieval | FAISS, SentenceTransformer, NumPy |
| LLM backends | Hugging Face, Gemini, OpenAI, Ollama |
| Evaluation | RAGAS, LangChain integrations |
| Document parsing | PyMuPDF for PDF support, text/markdown/python loaders |
| Packaging | uv, hatchling, pyproject.toml |
| Testing | pytest |
| Containerization | Docker, Docker Compose, GitHub Container Registry |
| CI/CD | GitHub Actions |
RAG-based_notes_helper/
├── README.md
├── pyproject.toml
├── uv.lock
├── .env.example
├── Dockerfile
├── docker-compose.yml
├── LICENSE
├── .github/workflows/
│ ├── ci.yml
│ └── cd.yml
├── data/
│ └── notes_helper.md # example/base note file tracked by git
├── docs/
│ ├── demo.gif
│ ├── demo.mp4
│ └── test.png
├── src/rag_notes_helper/
│ ├── cli.py # rag-app entry point
│ ├── core/config.py # runtime settings and validation
│ ├── eval/ # RAGAS evaluation workflow
│ ├── rag/
│ │ ├── answer.py
│ │ ├── chunking.py
│ │ ├── index.py
│ │ ├── ingest.py
│ │ ├── loaders.py
│ │ ├── meta_store.py
│ │ ├── retrieval.py
│ │ └── llm/ # provider implementations
│ └── utils/
└── tests/ # pytest unit tests
Runtime directories such as storage/, logs/, and hf_cache/ are created locally and ignored by git.
- Python 3.11+
uvrecommended- Docker optional for containerized usage
- API key for the selected remote LLM provider, unless using Ollama locally
git clone https://github.com/StevenHuang41/RAG-based_notes_helper.git
cd RAG-based_notes_helper
uv syncCreate a local environment file:
cp .env.example .envEdit .env and choose one LLM provider.
The repository includes data/notes_helper.md as an example document. Add your own notes under data/.
uv run rag-app "What does my note say about RAG?"Useful options:
uv run rag-app "What is RAG?" --update # smart re-index before answering
uv run rag-app "What is RAG?" --reindex # rebuild the full index before answering
uv run rag-app "What is RAG?" --citations # include source citations
uv run rag-app --sources # list indexed source files
uv run rag-app --config # show validated configuration
uv run rag-app --eval # run RAGAS evaluationuv run rag-app --replREPL commands:
:quit or :q exit
:help or :h show commands
:reindex or :ri rebuild the index from scratch
:update or :ud smart update changed files
:citations or :ci toggle citation display
:sources or :so show indexed files
:config or :co show configuration
:stream or :s toggle stream mode
:evaluate or :ev run evaluation
Create a working directory with .env, data/, and docker-compose.yml:
mkdir rag-application
cd rag-application
curl -L -o docker-compose.yml \
https://raw.githubusercontent.com/StevenHuang41/RAG-based_notes_helper/main/docker-compose.yml
curl -L -o .env.example \
https://raw.githubusercontent.com/StevenHuang41/RAG-based_notes_helper/main/.env.example
cp .env.example .env
mkdir -p data storage logs hf_cacheAdd notes under data/, then run:
docker compose run --rm rag-app "What do my notes say about RAG?"For Ollama from Docker, set:
LLM_PROVIDER=ollama
LLM_MODEL=llama3.1
OLLAMA_BASE_URL=http://host.docker.internal:11434
Do not use docker compose up for the interactive CLI workflow; use docker compose run --rm rag-app ... instead.
Equivalent docker run example:
docker run --rm -it \
--env-file .env \
-v "$(pwd)/data:/app/data" \
-v "$(pwd)/storage:/app/storage" \
-v "$(pwd)/hf_cache:/root/.cache/huggingface" \
-v "$(pwd)/logs:/app/logs" \
-e TZ=Asia/Taipei \
ghcr.io/stevenhuang41/rag-based-notes-helper:latest \
"What do my notes say about RAG?"The application reads configuration from .env.
Minimal remote-provider configuration:
LLM_PROVIDER=gemini
LLM_MODEL=gemini-2.0-flash
LLM_API_KEY=your-api-key
Minimal Ollama configuration:
LLM_PROVIDER=ollama
LLM_MODEL=llama3.1
LLM_API_KEY=not-used
OLLAMA_BASE_URL=http://localhost:11434
Common settings:
| Variable | Purpose | Default / example |
|---|---|---|
LLM_PROVIDER |
LLM backend | hf, gemini, openai, ollama |
LLM_MODEL |
Model name for selected provider | gemini-2.0-flash |
LLM_API_KEY |
API key for remote providers | required except Ollama |
LLM_MAX_CHUNKS |
Maximum retrieved chunks sent to LLM | 5 |
LLM_MAX_TOKENS |
Generation token budget | 1024 |
LLM_TEMPERATURE |
Generation temperature | 0.1 |
CHUNK_SIZE |
Chunk size for ingestion | 800 |
CHUNK_OVERLAP |
Chunk overlap | 200 |
TOP_K |
Number of retrieved chunks | 5 |
MIN_RETRIEVAL_SCORE |
Retrieval score threshold | 0.3 |
STREAM |
Stream model output where supported | true |
Run unit tests from the project root:
uv run pytestTest evidence:
| Workflow | Purpose |
|---|---|
.github/workflows/ci.yml |
Install dependencies and run pytest |
.github/workflows/cd.yml |
Build and push the Docker image to GitHub Container Registry |
The published image name is:
ghcr.io/stevenhuang41/rag-based-notes-helper:latest
- Single-turn query flow; conversation memory is not yet implemented.
- Retrieval quality depends on note quality, chunk size, and embedding model choice.
- Runtime artifacts such as FAISS indexes, logs, and evaluation reports are local by default.
- API-based LLM providers require valid credentials in
.env.
- Make the RAG workflow more agentic, allowing the assistant to plan multi-step note exploration, decide when to retrieve again, and synthesize answers across multiple retrieval passes.
- Add conversation memory so follow-up questions can reuse prior context while still grounding final answers in retrieved notes.
- Add an optional web UI for browsing sources, retrieved chunks, answers, and citations.
- Improve deployment examples for local Ollama and remote LLM providers.
This project is licensed under the MIT License.
