A recursive, self-hosted deep research agent in a single Python file. It plans searches, reads the web, audits its own evidence, and streams a fully-cited Markdown report to your browser — powered entirely by your models on your hardware.
In 2025 the big labs started selling "deep research" as a premium feature — $200/month for the privilege of having your questions, your sources, and your thinking habits logged on someone else's server.
That's a rental. This is ownership.
| 🔬 DeepResearch Agent | OpenAI Deep Research | Perplexity Pro | |
|---|---|---|---|
| Monthly cost | $0 (your hardware) | $200 (ChatGPT Pro) | $20 |
| Query privacy | Topic & report never leave your network* | Full platform telemetry | Full platform telemetry |
| Model freedom | Any GGUF via Ollama — llama3.1, qwen2.5, mistral, your fine-tune | Fixed frontier models only | Fixed models only |
| Rate limits | None. Research at 3 AM if you want. | Usage caps apply | Usage caps apply |
| Citations | Verbatim inline [Title](URL) from real scraped pages |
Yes, opaque pipeline | Mixed quality |
| Customization | The entire agent is one readable Python file | Closed | Closed |
| Works during outages | ✅ Your stack, your rules | ❌ | ❌ |
* Honest disclosure: search queries are sent to DuckDuckGo or your SearXNG instance — that's how web search works for everyone, including the paid tools. Page fetching happens directly from your machine. Everything else — planning, reading, auditing, writing — touches your LLM on your hardware.
The pitch: a standard 8B model running on an 8 GB laptop can now do what used to require an enterprise subscription.
flowchart LR
A["🧑💻 User Query"] --> B["🧠 Query Planner<br/>(LLM → 3-5 sub-queries)"]
B --> C["🔎 Search Engine<br/>DDG / SearXNG · parallel"]
C --> D["🕸 Async Scraper<br/>httpx + BeautifulSoup4"]
D --> E["⚖️ Evidence Auditor<br/>sufficient? else refine"]
E -- "gaps found · depth ≤ 2" --> B
E -- "evidence solid" --> F["✍️ Ollama Summarizer<br/>streaming synthesis"]
F --> G["📄 Streamed Report<br/>live SSE markdown + citations"]
G --> H["⬇ Export .md / .pdf"]
One pass looks like this:
- 🧠 Plan — your local LLM decomposes the topic into 3–5 diverse, high-signal search queries.
- 🔎 Search — queries fan out in parallel through DuckDuckGo (or your SearXNG instance).
- 🕸 Read — top 3 results per query are fetched concurrently with
httpx, stripped of boilerplate by BeautifulSoup4, and distilled into targeted excerpts using rarity-weighted term matching. - ⚖️ Audit — the LLM judges whether the evidence is sufficient. Not convinced? It writes 2 sharper follow-up queries targeting the exact gaps and the loop recurses (depth ≤ 2).
- ✍️ Synthesize — a structured report (Executive Summary → Key Findings → Analysis → Gaps → Conclusion → References) streams token-by-token over Server-Sent Events, with every claim cited
[Title](URL)verbatim from scraped sources. - ⬇ Export — one-click
.mddownload, or.pdfvia a PDF writer built from raw bytes — no wkhtmltopdf, no headless Chrome, no extra dependencies.
Everything above streams live into the UI's agent trace: you watch it think, search, read, and change its mind in real time.
Prereq: Ollama running somewhere reachable (ollama pull llama3.1).
git clone https://github.com/artinamr/DeepLocal.git
cd DeepLocal
docker compose up -dOpen http://localhost:8000 — first boot installs deps inside the container (~30 s), then it's permanent. Reports persist to ./reports on your host.
# 1. get it
git clone https://github.com/artinamr/DeepLocal.git
cd DeepLocal
python -m venv .venv && source .venv/bin/activate # optional but polite
# 2. install 5 packages — that's the whole dependency tree
pip install fastapi uvicorn httpx beautifulsoup4 duckduckgo-search
# 3. point it at your brain
ollama pull llama3.1
# 4. launch
python app.pyOpen http://127.0.0.1:8000, type a question like "How will solid-state batteries reshape EV economics by 2030?", hit Launch, and watch the recursion happen.
Already running LM Studio / vLLM / LiteLLM / any OpenAI-compatible server?
export OPENAI_BASE_URL=http://localhost:1234/v1 # LM Studio example
export OPENAI_MODEL=qwen2.5-14b-instruct
python app.pyNo key needed for local servers — just leave OPENAI_API_KEY unset and it won't be sent.
Deliberately modest. If it can run a chat model, it can run this agent.
| Setup | Total RAM | Suggested Model | Experience |
|---|---|---|---|
| Minimum | 8 GB | llama3.1:8b (Q4_K_M ≈ 4.9 GB) |
Fully working; CPU inference is slower but coherent |
| Comfortable | 16 GB | llama3.1:8b Q8, qwen2.5:14b Q4 |
Fast planning + crisp citations |
| Luxury | 32 GB+ / 12 GB VRAM GPU | qwen2.5:32b, llama3.1:70b split |
Analyst-grade prose |
The agent itself: ~150 MB of Python process memory. No database, no Redis, no browser binaries, no message queue. The web UI is embedded in the single file and served straight from FastAPI.
| Component | Requirement |
|---|---|
| OS | Linux, macOS, Windows (WSL2) |
| Python | 3.11+ (raw install only) |
| Docker | Any recent engine (compose route only) |
| LLM backend | Ollama or anything speaking OpenAI's /chat/completions |
| Outbound net | HTTPS to search engine + pages you ask it to read |
Every knob is an environment variable with a sane default. Zero config = working out of the box with Ollama.
| Variable | Default | What it does |
|---|---|---|
LLM_PROVIDER |
auto (ollama, falls back per below) |
Force ollama or openai |
OLLAMA_BASE_URL |
http://localhost:11434 |
Ollama endpoint |
OLLAMA_MODEL |
llama3.1 |
Any Ollama model tag |
OLLAMA_NUM_CTX |
8192 |
Context window handed to Ollama — raise for long reports |
OPENAI_BASE_URL |
https://api.openai.com/v1 |
Point at LM Studio / vLLM / LiteLLM / Groq… |
OPENAI_API_KEY |
(unset) | Setting this (or base URL) switches provider to openai |
OPENAI_MODEL |
gpt-4o-mini |
Model name for the compatible endpoint |
SEARXNG_URL |
(unset) | e.g. http://localhost:8888 — used before DDG, great for meta-search self-hosters |
REPORTS_DIR |
(unset) | When set, every finished report is archived here as .md |
HOST / PORT |
127.0.0.1 / 8000 |
Bind address |
Mount a directory and set REPORTS_DIR (the bundled docker-compose.yml does this automatically):
reports/
├── 20250112-093014-solid-state-batteries-ev-economics-a1b2c3d4.md
├── 20250112-104438-compare-vector-databases-rag-e5f6a7b8.md
└── ...
Every mission also stays downloadable in-browser until restart via /api/report/{id}.md and /api/report/{id}.pdf.
SSE needs buffering disabled:
location / {
proxy_pass http://127.0.0.1:8000;
proxy_buffering off; # critical for live streaming
proxy_cache off;
proxy_set_header Connection '';
proxy_http_version 1.1;
}The app already sends X-Accel-Buffering: no, which Caddy Traefik and friends respect out of the box.
- Multi-model crews (planner ≠ writer)
- Per-mission model picker in the UI
- SearXNG JSON instances bundled as a second compose service
- EPUB export
- Optional auth layer for exposed instances
The whole project is one file on purpose — readable top-to-bottom in one sitting. PRs that keep it that way are merged fast.
MIT — do whatever you want, build companies on it, just keep the notice.
If a $200/mo subscription was your only option for deep research, it wasn't — star this repo and run your own. ⭐
Built with FastAPI · AsyncIO · httpx · BeautifulSoup4 · duckduckgo-search · and whatever GGUF you love