- Read .md/.txt files from a
data/folder. - Chunk documents into paragraph-style pieces.
- Embed chunks with Ollama and persist embeddings in Chroma.
- At query time embed the user question, retrieve top-k similar chunks from Chroma, build a prompt that forces the model to answer only from the returned context, and include citations.
app_v2.py. The full script is included in the sections below.
Prepare example data
Create adata directory with a couple of small documents:
data/ contains two short documents we will ingest.
app_v2.py — settings and imports
Below are the imports and the demo’s simple hard-coded settings. These values are easy to change for your environment.
You can change
LLM_MODEL, EMBED_MODEL, TOP_K, and the Chroma path as needed.
Embedding & generation helpers (Ollama)
These helpers handle embeddings and text generation. The embedding helper supports bothprompt= and input= parameter styles used by different Ollama client versions.
The
_embed helper attempts both parameter styles to maintain compatibility across Ollama client versions. If you control the client, pick one style and simplify the helper.Chroma collection helper
Create or get a persistent Chroma collection. This example uses the duckdb+parquet implementation and a local persist directory.Simple paragraph-based chunking
The chunking strategy below splits documents into paragraphs and packs them greedily into chunks with optional overlap. Paragraph-based chunks are small, document-like, and work well for many short-doc corpora.max_chars and overlap for your documents. Paragraph packing keeps chunks coherent and readable by the model.
Iterating files to ingest
Only.txt and .md files are considered. This helper walks a directory tree and yields matching files.
Quick environment check (init)
A small command verifies embeddings, text generation, and Chroma connectivity. Runpython app_v2.py init to validate your environment.
Semantic search helper
Embed the user question, query Chroma for the top-k results, and return hits with document text, metadata, and distance scores.Build the prompt with citations
Format the retrieved chunks into a context block and construct a deterministic prompt that instructs the LLM to answer ONLY from that context and to cite the sources.Ingest command
This command ingests all.md/.txt files under a directory:
- Read files from disk
- Chunk documents
- Create deterministic chunk IDs based on file path + chunk content
- Embed chunks and add to Chroma, skipping duplicates
Deterministic chunk IDs allow safe re-ingestion: the script skips chunks already present in Chroma. If your Chroma client supports efficient upserts, you can swap the existence check for an upsert workflow.
Ask command
At query time we run semantic search, build the prompt with the returned chunks, call the LLM, and print the model’s answer along with a list of cited sources.Stats and reset
Utilities to inspect and reset the local Chroma index:CLI wiring (argparse)
The CLI wires the commands described above. This is the entrypoint for the script.Quick demo (example commands and expected outputs)
Use these example commands to verify the workflow.- Initialize (environment check):
- Ingest local files:
- Check stats:
- Ask a question using top-k retrieval:
- Ask another question:
- Reset the index, then ask (shows the behavior when there is no index):
ingest to rebuild the index and queries will work again.
Command reference
What we accomplished
- Replaced the tiny in-memory demo with a simple file-based ingestion pipeline.
- Implemented deterministic chunk IDs so re-ingests skip duplicates.
- Used top-k retrieval from Chroma to build a deterministic context for the LLM.
- Instructed the LLM to answer only from the provided context and to include citations.
- Kept the code intentionally minimal so you can extend it for batching, semantic chunking, richer metadata, or different embeddings/LLM providers.
This demo is for local testing and small datasets. For production, consider secure deployment of Ollama/Chroma, robust error handling, rate limits, batching, and privacy/PII considerations for ingested content.
Next steps
- Improve chunking (semantic splits, sentence boundaries, or models like BERT-based splitters).
- Add update/delete semantics for documents (incremental ingestion).
- Add richer metadata (file paths, timestamps, tags) to improve retrieval filtering.
- Add batching and parallelization for embeddings and adds.
- Expose a small web UI to demo RAG in a browser.
Links and references
- Ollama
- Chroma
- Chroma docs: https://www.trychroma.com/docs