Overview
Chat with Nietzsche is a conversational AI that channels Friedrich Nietzsche's philosophical voice using Retrieval-Augmented Generation over his complete written works. You can ask it about the Will to Power, Eternal Recurrence, master-slave morality, or why it thinks modern social media would have disgusted him — and get answers grounded in Nietzsche's actual writings, with source citations to the passages used.
The goal was never to make a philosophical trivia bot. It was to build a system where responses are demonstrably faithful to the source material. Every answer the system produces can be traced back to real text from the 19 works in the corpus.
It began as a single-file Streamlit app. It has since been rebuilt as a full-stack application — a Next.js frontend talking to a FastAPI backend that owns the retrieval pipeline and streams tokens back to the browser.
The RAG Pipeline
The retrieval pipeline is multi-stage, designed to maximize the quality of context passed to the LLM rather than relying on the model alone.
- Question condensing: When there's conversation history, one LLM call rewrites the follow-up into a standalone question. "What did he mean by that?" becomes something a search index can actually answer. The rewrite is used for retrieval only — the original message is what the model responds to.
- Hybrid search: The condensed query hits both a FAISS semantic index (70% weight) and a BM25 keyword index (30% weight) over roughly 7,300 chunks. Semantic search handles conceptual overlap; BM25 ensures exact philosophical terms like "Ressentiment" or "Zarathustra" are matched precisely.
- Fragment filtering: Candidate passages shorter than two sentences are dropped. Chunking at paragraph boundaries occasionally yields a stray heading or one-line aphorism fragment, and those crowd out passages that carry an actual argument.
- Cross-encoder re-ranking: The survivors are re-ranked using
ms-marco-MiniLM-L-6-v2, a cross-encoder that scores each passage against the question jointly rather than independently. This separates genuinely relevant passages from those that merely share vocabulary. The top 6 make it through. - Grounded generation: Those passages, the persona prompt, and the last 10 turns of history go to Groq. Temperature is set to 0.3 — low enough to suppress creative invention, high enough to avoid robotic phrasing — and tokens stream straight to the browser as they're produced.
Technical Implementation
Corpus and chunking: All 19 works were sourced from Project Gutenberg public domain translations. Text is split at paragraph boundaries with a 1200-character target and 150-character overlap. Paragraph-based chunking preserves argumentative flow — Nietzsche's writing often builds meaning across a full paragraph, and mid-sentence splits would corrupt the context.
Embeddings: sentence-transformers/all-mpnet-base-v2 generates 768-dimensional dense vectors for each chunk. The same model embeds user queries at inference time, ensuring semantic alignment between document and query spaces.
Stack: FastAPI serves the retrieval pipeline and the streaming chat endpoint. FAISS and BM25 (rank-bm25) are driven directly rather than through an orchestration framework — the pipeline is specific enough that a framework's abstractions cost more than they save. Indexes are built offline and committed to the repo, so a cold start never re-embeds the corpus. The Next.js frontend never talks to the backend directly: a server-side route validates each request and proxies it, piping the token stream through unchanged.
Source citations: The UI surfaces the specific Nietzsche passages used to generate each response — title, translator, and a link to the Gutenberg source — alongside the answer. This transparency serves two purposes: it lets users verify authenticity, and it demonstrates that the retrieval pipeline found genuinely relevant context rather than hallucinating.
Deployment: The frontend runs on Vercel; the backend runs on Railway from a Docker image with the embedding and cross-encoder models baked in, so cold starts never wait on a model download. It's the same split used by the AI music generator, and the reasoning is the same: the CPU-and-memory-hungry half gets a container it can size independently of the UI.
Skills Demonstrated
This project required implementing every component of an advanced RAG pipeline from first principles — no managed vector database, no off-the-shelf retrieval API. The multi-stage retrieve-filter-rerank design reflects real production RAG patterns used in enterprise retrieval systems. Rebuilding it from a Streamlit prototype into a streaming full-stack app forced the harder questions a demo never asks: what the API contract between the halves should be, where conversation state lives, and how to keep a cold start from timing out a user's first question.
