A document assistant that searches your German legal documents and answers questions in English, with source citations. Built with Elasticsearch, Jina AI models on Elastic Inference Service, and a local LLM via Ollama.
Blog post: Your Private Paperwork Assistant
- Embed —
jina-embeddings-v5-omni-smallon EIS embeds both PDF text and phone photos into the same vector space. A text query in English finds a German document without OCR. - Index — Vectors and extracted text go into Elasticsearch with a German analyzer for BM25.
- Retrieve — Hybrid search combines BM25 keyword matching and kNN semantic search, merged with Reciprocal Rank Fusion (RRF).
- Rerank —
jina-reranker-v3on EIS re-scores candidates using listwise cross-document attention. - Generate — A local LLM (Ollama) answers from the retrieved passages. No document text leaves your machine for this step.
- Elastic Cloud deployment with EIS enabled (or self-managed Elasticsearch 9.x)
- Python 3.11+
- Ollama installed locally
# Clone and install
git clone https://github.com/sofia-vas/paperwork-assistant.git
cd paperwork-assistant
pip install -r requirements.txt
# Configure credentials
cp .env.example .env
# Edit .env with your Elastic Cloud ID and API key
# Pull a local model
ollama pull llama3.2:3b
# Generate the sample corpus (fictional German legal documents)
python generate_corpus.py
# Index documents into Elasticsearch via EIS
python ingest.py --force-recreate
# Start the chat interface
PORT=8000 python app.pyOpen http://localhost:8000 in your browser.
The repository includes a generator (generate_corpus.py) that creates 35 fictional German legal documents and 10 phone-photo versions:
- Employment contracts and payslips
- Health insurance notices and medical invoices
- Rent increase letters and utility bills
- Tax assessments and bank statements
- Telecom contracts and government notices
All documents are fictional (addresses in "Musterstadt", fictional IBANs) and safe to use for demos.
User question (English)
│
▼
┌─────────────────────────┐
│ Elasticsearch │
│ ┌───────────────────┐ │
│ │ BM25 (German) │──┤
│ │ kNN (image embed) │──┤── RRF merge ── Rerank (.jina-reranker-v3)
│ │ kNN (text embed) │──┤
│ └───────────────────┘ │
└─────────────────────────┘
│
▼ top passages
┌─────────────────────────┐
│ Ollama (local LLM) │
│ Answer in English │
│ with source citations │
└─────────────────────────┘
| Variable | Default | Description |
|---|---|---|
ELASTIC_CLOUD_ID |
— | Elastic Cloud deployment ID |
ELASTIC_API_KEY |
— | Elasticsearch API key |
ELASTICSEARCH_URL |
— | Alternative: direct cluster URL |
OLLAMA_MODEL |
llama3.2:3b |
Local LLM model name |
OLLAMA_BASE_URL |
http://localhost:11434 |
Ollama server URL |
PORT |
5000 |
Web server port |
MIT