A local-first RAG knowledge base for personal documents. Upload PDF/Markdown files and ask questions in natural language — every answer is streamed (SSE) and grounded in citations that link back to the exact source page and excerpt.
Works entirely on your machine with local models (Ollama / vLLM), or plug in any OpenAI-compatible cloud API (DeepSeek, SiliconFlow, etc.). Also exposes an OpenAI-compatible API and MCP server so AI agents (opencode, cherrystudio, codex, …) can query your knowledge base.
- Document management — upload PDF / Markdown, automatic chunking & vectorization, delete documents, track ingestion status
- Citation-grounded Q&A — retrieval-augmented streaming answers (SSE) with multi-turn conversation and adjustable Top-K
- Source tracing — every answer comes with source cards (document, page, verbatim excerpt) that jump to the cited location
- Local-first & private — runs fully offline with Ollama / vLLM; no data leaves your machine unless you opt into a cloud API
- Provider-agnostic — LLM and Embedding both go through OpenAI-compatible interfaces; switch between local and cloud via config
- Agent-ready — OpenAI-compatible API (
/v1/chat/completions) + MCP server (retrieve_documents,rag_answer) - Single-port deployment — the FastAPI backend serves the Vue 3 frontend build (
web/dist); no extra static server needed - Settings panel — inspect Qdrant health and the currently active model configuration
Browser ── HTTP/SSE ──> FastAPI (app/) ──> Qdrant (vector search)
│
├──> SQLite (conversations / document metadata)
└──> LLM / Embedding provider (OpenAI-compatible, configurable)
- Backend: FastAPI + SQLAlchemy (SQLite) + Qdrant vector store
- Frontend: Vue 3 + Element Plus + Vite (build artifacts served by the backend)
- Providers: LLM & Embedding via OpenAI-compatible endpoints — local (Ollama / vLLM) or cloud (DeepSeek, SiliconFlow, …)
Prerequisites: Python 3.11+, Node.js 18+, Docker (or an existing Qdrant service).
Qdrant via Docker:
docker compose up -dOr run Qdrant natively (download the qdrant binary), default port localhost:6333.
Backend:
conda create -n llm python=3.11 -y
conda activate llm
pip install -e ".[dev]"Frontend build:
cd web
npm install
npm run build
cd ..
web/distmust exist for the backend to serve the UI; during development runnpm run devin another terminal for hot reload.
Copy-Item .env.example .envEdit .env:
KB_CONFIG=config/local.yaml— local Ollama (pull the models first:ollama pull qwen2.5:7b-instruct,ollama pull bge-m3)KB_CONFIG=config/cloud.yaml— cloud APIs (fill inKB_LLM_API_KEY,KB_EMBEDDING_API_KEY)
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000Open http://localhost:8000.
Configuration has two layers: config/*.yaml presets (local.yaml for Ollama, cloud.yaml for cloud APIs), and KB_* environment variables in .env which override the presets:
| Variable | Meaning |
|---|---|
KB_CONFIG |
Config preset: config/local.yaml or config/cloud.yaml |
KB_LLM_BASE_URL / KB_LLM_API_KEY / KB_LLM_MODEL |
LLM endpoint, key, model |
KB_EMBEDDING_BASE_URL / KB_EMBEDDING_API_KEY / KB_EMBEDDING_MODEL |
Embedding endpoint, key, model |
KB_QDRANT_URL |
Qdrant address (default http://localhost:6333) |
Security: put API keys only in
.env(git-ignored) — never commit them toconfig/*.yamlor the repository.
Every model is accessed through an OpenAI-compatible endpoint, so local weights only need to be exposed as one:
- vLLM:
vllm serve Qwen3-8B --served-model-name Qwen3-8B(defaulthttp://localhost:8000/v1), then in.env:KB_CONFIG=config/local.yaml KB_LLM_BASE_URL=http://localhost:8000/v1 KB_LLM_MODEL=Qwen3-8B - Ollama:
ollama run qwen2.5:7b-instruct(defaulthttp://localhost:11434/v1); for embeddings runollama pull bge-m3and setKB_EMBEDDING_MODEL=bge-m3.
Embedding works the same way — point it at the OpenAI-compatible endpoint of your local embedding service.
The service exposes an OpenAI-compatible API and an MCP server for tools like opencode / codex / cherrystudio (loopback only, no auth).
Note:
/v1/*has no authentication. Bound to127.0.0.1for local-only use; do not expose to the network without adding auth.
- Base URL:
http://127.0.0.1:8000/v1 - Models:
rag-chat— retrieves from the knowledge base and answers with citationsqwen3-8b— raw local model passthrough (no retrieval)
- Command:
python -m app.mcp_server(run in the repo root) - Tools:
retrieve_documents(query, top_k),rag_answer(question)
python -m pytest -vCovers chunking, ingestion, vector retrieval, citation generation, SSE streaming, and the full API chain (integration tests use a mock provider and Qdrant :memory:, no real models or services required).
- Page won't open after start? Make sure
web/distwas built (npm run build) — otherwise the backend only serves/api/*. - Documents stuck on "processing"? Check the Embedding endpoint is reachable (
KB_EMBEDDING_BASE_URL). - Answers missing citations? The document must be ingested successfully and the question must relate to its content; the model will say so explicitly when it cannot answer from the knowledge base.