Ask questions against your own documents, answered by Claude using only what's actually in them (retrieval-augmented generation, aka RAG).
chunking.pyreads files fromdocs/and splits them into overlapping chunksembed_store.pyturns each chunk into a vector and does similarity search with plain numpy. Two embedding backends, chosen automatically: local (sentence-transformers, free, no API key - the default for your own machine) or Voyage AI (hosted, used automatically whenVOYAGE_API_KEYis set - what the public deployment uses, since it avoids PyTorch's memory footprint on a small server)app.pyties it together:ingestbuilds the index,askretrieves the most relevant chunks for your question and asks Claude to answer from them
# From this folder:
.\venv\Scripts\Activate.ps1
pip install -r requirements-local.txt
copy .env.example .env
# then edit .env and paste in your Anthropic API key# 1. Drop .txt / .md / .pdf files into docs/
# 2. Build the index
python app.py ingest
# 3a. Ask questions from the terminal
python app.py ask
# 3b. ...or launch the web interface instead
python web.py
# then open http://127.0.0.1:5000qa.py holds the shared retrieval + answer logic that both app.py and
web.py call into - the only difference between the two is how the question
comes in and the answer gets displayed.
web.py is written to be safely exposed to anyone on the internet, not just
run locally:
- Rate limited - 8 questions per visitor per hour (
flask-limiter) - Input validated - oversized questions/history are rejected before they ever reach Claude, so a crafted request can't inflate token usage
- Cheaper public model - defaults to
claude-sonnet-5instead of the CLI'sclaude-opus-5(override with thePUBLIC_MODELenv var) - Hosted embeddings (Voyage AI) - set
VOYAGE_API_KEYin the deploy environment. Localsentence-transformersneeds PyTorch, which reliably runs a small free-tier server (e.g. Render's 512MB) out of memory; Voyage keeps the deployed process lightweight.requirements.txtdeliberately omitssentence-transformers/PyTorch for this reason - local dev installsrequirements-local.txtinstead, which adds it back - Self-building index - builds
index.pklfromdocs/automatically on first request if it's missing (or was built by a different embedding backend than the one currently active), so a fresh deploy needs no manualingeststep
None of that replaces a monthly spend cap - set one in the Anthropic Console (Settings -> Billing) before deploying, regardless of the above. Same idea for Voyage: add a payment method in their dashboard (Billing) even though the first 200M tokens are free - without one, new accounts are throttled to 3 requests/minute, which a public demo will hit almost immediately.
- Push this repo to GitHub (already done if you're reading this from here)
- On render.com, create a New Web Service from this repo
- Build command:
pip install -r requirements.txt - Start command:
gunicorn web:app(or leave it - the includedProcfilesets this) - Add environment variables:
ANTHROPIC_API_KEY= your Anthropic keyVOYAGE_API_KEY= your Voyage key- optionally
PUBLIC_MODELto override the default Claude model
- Deploy - Render gives you a public URL once the build finishes
Remember: only files actually committed to this repo exist on the deployed
server. docs/ is gitignored except the sample file, so the public deploy
only ever answers questions about that sample policy document - not any real
documents you've tested locally.
- Only two things ever call an external API: the final answer-generation step (Claude) and, on the public deployment only, embeddings (Voyage AI).
docs/andindex.pklare for your own files - be mindful before committing real/sensitive documents to a public GitHub repo.