Eight specialist agents over a 16-book verified WA law corpus (RAG with citations), per-user document vault, WA court-form PDF auto-fill, comms missions with DV safety guard, no-KYC auth, TTS. Self-hosted: Flask + SQLite + Ollama, stdlib-only RAG. Includes README, LICENSE (MIT + not-legal-advice notice), DEPLOY runbook, .gitignore.
11 KiB
⚖️ Astraea — Self-Hosted AI Family-Law Assistant for Washington State
Eight specialist AI agents. Grounded in verified Washington law. Your documents never leave your network.
Astraea is a self-hosted, multi-agent legal information assistant focused on Washington State family law: divorce, custody, child support, maintenance, property division, protection orders, and mediation. It combines retrieval-augmented generation (RAG) over a curated library of state-specific legal references with tool-calling LLM agents, PDF form auto-fill, per-user document vaults, and optional Twilio/SMTP communications — all running on your own hardware with your own models.
Live instance: https://astraea.thetempleofdoom.com
Why Astraea
People going through divorce or custody battles face three problems: lawyers are expensive, court forms are intimidating, and generic chatbots hallucinate the law. Astraea attacks all three:
- Grounded answers, not vibes. Every agent answers from a curated book library (185KB of verified WA-law reference material citing RCW chapters, controlling case law like In re Marriage of Littlefield, and WashingtonLawHelp.org). Responses carry citations back to the source chunks.
- Specialists, not one generic bot. Eight agents, each with its own system prompt, book subset, and focus — from the intake Navigator to the Protection Orders specialist with built-in DV-safety guardrails.
- Self-hosted and private. Runs on a Proxmox LXC container, talks to a LAN Ollama server, and stores everything in a local SQLite database. No cloud APIs, no data leaving your network.
⚠️ Not legal advice. Astraea is an educational and organizational tool. It does not replace a licensed Washington attorney. This notice is embedded in the product, the agents' system prompts, and every source book.
The Eight Specialists
| Agent | Focus |
|---|---|
| 🧭 Navigator | Intake — where to start, which specialist next, the overall WA process |
| ⚖️ Divorce & Dissolution | No-fault dissolution, legal separation, the 90-day wait, decrees |
| 🧒 Child Custody & Parenting | Parenting plans, best-interest factors, relocation, nonparental custody |
| 💰 Child Support | Washington's economic table, imputation, deviations |
| 🤝 Spousal Maintenance | Alimony factors, duration, modification |
| 🏠 Property & Debt | Characterization, fair-and-equitable division, QDROs |
| 🛡️ Protection | Protection orders, no-contact orders, DV safety planning |
| 🕊️ Mediation & ADR | Mediation, arbitration, collaborative law, settlement strategy |
Feature Highlights
- Multi-agent RAG chat — stdlib-only vector engine (embeddings via Ollama, cosine retrieval, cached disk index, incremental rebuild when a book changes)
- Per-user document vault — upload filings/correspondence/evidence; text extracted (PDF/DOCX/TXT), chunked, embedded, and retrieved as
[USER DOC]context in chats - PDF form auto-fill — 10+ Washington court form packets (FL All-Cases series) with field detection (
detect_fields.py+pymupdfoverlay) and LLM-assisted mapping from your case facts - Document generation — declarations, letters, timelines generated from your case context
- Comms missions with DV safety guard — Twilio SMS/voice + SMTP email with a hard block on contacting anyone protected by a no-contact/protection order, plus formal message templates
- No-KYC auth — username/password only, per-user profiles with an "About Me" context system, 12 professional avatars
- Text-to-speech — every answer speakable via edge-tts
- Conversation memory — per-agent persisted history in SQLite
- Zero cloud dependencies — Flask + SQLite + Ollama; the RAG engine is pure stdlib (urllib + json + math)
Architecture
┌─────────────────────────────────────────┐
Browser ──HTTPS──▶│ nginx (TLS termination, fleet tunnel) │
└───────────────┬─────────────────────────┘
│ :5000
┌───────────────▼───────────────┐
│ Astraea (Flask + gunicorn) │
│ ├─ agents.py 8 specialists │
│ ├─ rag.py vector engine │
│ ├─ store.py SQLite DAL │
│ ├─ userdocs.py doc vault │
│ ├─ forms.py PDF autofill │
│ ├─ comms.py Twilio/SMTP │
│ └─ tts.py edge-tts │
└───────────────┬───────────────┘
│ LAN
┌───────────────▼───────────────┐
│ Ollama host (GPU) │
│ ├─ qwen3.8fast orchestrator │
│ ├─ ornith-1.5:9b fallback │
│ └─ nomic-embed embeddings │
└───────────────────────────────┘
Repo layout
├── app.py # Flask app: routes, chat orchestration, uploads (757 lines)
├── agents.py # The 8 specialist definitions (system prompts, book subsets)
├── rag.py # Stdlib-only RAG: chunk → embed → cosine retrieve → cite
├── store.py # SQLite: users, sessions, profiles, settings, messages, files
├── documents.py # Generated-document engine
├── detect_fields.py # PDF form field detection (pymupdf)
├── forms.py # Court-form fill + overlay endpoints
├── comms.py # Twilio SMS/voice + SMTP, no-contact safety guard
├── tts.py # edge-tts speech synthesis
├── userdocs.py # Per-user document vault ingestion
├── books/ # 16 curated WA family-law reference books (the RAG corpus)
├── templates/ # Landing page + app SPA (vanilla JS, no build step)
├── static/ # Assets
├── astraea.service # systemd unit (gunicorn, 2 workers × 4 threads)
├── astraea-nginx.conf # nginx site config
└── benchmark_models.py # Model-selection benchmark harness
Quick Start
Requirements
- Python 3.11+ (3.9 works with minor tweaks — stdlib-only core)
- An Ollama server reachable on your LAN with:
qwen3.8fast(or any tool-calling model) — orchestratorornith-1.5:9b(or any reliable instruct model) — fallbacknomic-embed-text-v2-moe— embeddings
- Optional:
pymupdffor PDF form filling,edge-ttsfor speech
Install
git clone http://10.30.20.149:3000/drjones/astraea.git
cd astraea
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
pip install pymupdf edge-tts # optional features
Configure
Point the agents at your Ollama host in agents.py:
OLLAMA_URL = "http://your-ollama-host:11434"
RAG_MODEL = "qwen3.8fast" # tool-calling orchestrator
GENERAL_MODEL = "ornith-1.5:9b" # fallback
EMBED_MODEL = "nomic-embed-text-v2-moe"
The vector index builds on first boot (background thread) and caches to index.json.
Run
python app.py # dev, port 5000
# or production:
gunicorn --workers 2 --threads 4 --bind 0.0.0.0:5000 --timeout 240 app:app
Register a user, answer the About-Me prompts (this becomes silent context for every agent), and start with the Navigator.
Deploy on Proxmox LXC (how we run it)
pct create 150 local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst \
--hostname astraea --memory 2048 --cores 2 \
--net0 name=eth0,bridge=vmbr0,ip=10.30.20.160/24,gw=10.30.20.1 \
--rootfs poolmaster:8 --unprivileged 1
pct start 150
# then: install python3-venv, clone, pip install, cp astraea.service /etc/systemd/system/
systemctl enable --now astraea
nginx TLS termination + Cloudflare Tunnel gives the public URL. See astraea-nginx.conf.
The RAG Corpus
books/ contains 16 reference books (~185KB) written as structured Markdown, each with inline citations to RCW statutes, Washington case law, and WashingtonLawHelp.org:
divorce-dissolution.md+ 3 case-law companions (characterization, division, maintenance)custody-parenting.md,parentage.md,guardianship.mdchild-support.md,spousal-maintenance.md,property-debt.mdprotection-orders.md,mediation-adr.md,modification-enforcement.mdoverview-intake.md,committed-relationships.md,adoption.md
The index is chunked at ~1200 chars, embedded once, and rebuilt incrementally per-book on change. Retrieval returns top-k chunks as [SOURCE n] blocks the agent must cite.
Safety Design
- Not-legal-advice notice in the product UI, agent system prompts, and every book header
- No-contact safety guard (
comms.py): if a user's profile indicates a protection/no-contact order is in effect, outbound Twilio/SMTP actions toward the protected party are hard-blocked with a safety message - No-KYC by design: no emails, no phone numbers required at signup; users stay anonymous
- Local-only data: uploads, chats, and profiles live in one SQLite file on your container
Known Limitations & Roadmap
- Password hashing is unsalted SHA-256 — needs bcrypt/argon2 with a transparent re-hash-on-login migration (next security release)
- Session tokens don't expire — add TTL + refresh
- No rate limiting on auth/chat endpoints
- Single-node SQLite — Postgres adapter for multi-user scale
- Books cover WA only — contributions for other states welcome (keep the citation format!)
- Streaming responses (currently full-answer)
Operations
systemctl status astraea # service health
curl localhost:5000/api/health # app health + index status
curl -X POST localhost:5000/api/reindex # force full RAG rebuild
Database backup: cp astraea.db astraea.db.bak-$(date +%F) — everything (users, chats, vault metadata) is in that one file. Uploads live in uploads/.
Credits
Built by drjones on a Proxmox homelab with local Ollama models — no cloud APIs were harmed.
Licensed under MIT — see LICENSE. Legal reference material is educational, cites public sources, and is not legal advice.