▶ Live demo · React + Vite frontend on Vercel, FastAPI backend on Render — running free on Groq + Tavily.
ℹ️ The free backend sleeps after ~15 min idle, so the first run after a nap takes ~50s to wake. Subsequent runs are fast.
Five specialized AI agents collaborate — coordinated by a LangGraph state machine — to turn a research goal into a well-structured, source-cited deliverable. Enter a goal like "Compare the leading vector databases for a RAG product" and watch the agents plan, research, summarize, write, and self-edit — with a live trace of every handoff in the browser.
💸 Runs on free APIs — $0 to operate. Uses Groq's free hosted LLMs and Tavily's free search tier (1,000 searches/month). Neither requires a credit card. The provider is swappable in one file (
core/llm.py).
Portfolio project. Prioritizes clean architecture, visible agent collaboration, and a demoable web interface over feature breadth.
┌──────────────┐
goal ──▶│ Orchestrator │ plans 3–5 sub-questions, routes the loop
└──────┬───────┘
▼
┌──────────────┐
│ Researcher │ Tavily web search → structured findings
└──────┬───────┘ (claim, detail, source_url, source_title)
▼
┌──────────────┐
│ Summarizer │ dedupe + condense into a knowledge brief,
└──────┬───────┘ flags gaps & weak evidence
▼
┌──────────────┐
│ Writer │ brief → report | comparison | brief | article
└──────┬───────┘ with inline [1] citations + Sources list
▼
┌──────────────┐
│ Editor/Critic│ verdict: approve | revise_writing | needs_more_research
└──────┬───────┘
▼
┌──────────────────┐
│ route on verdict │
└──┬────────┬───────┘
approve │ │ revise_writing ─────▶ back to Writer
(or cap) │ │ needs_more_research ▶ back to Researcher
▼ (capped at MAX_REVISIONS, default 2)
END → final_output
The feedback loop is the point. When the Editor doesn't approve, control returns through
the Orchestrator's router to either the Writer or the Researcher. The loop is capped by
MAX_REVISIONS so it can never run away and burn tokens.
| Agent | Role | Model (default, Groq) |
|---|---|---|
| Orchestrator | Plans sub-questions, routes the loop | llama-3.3-70b-versatile |
| Researcher | Tavily search → structured findings | llama-3.1-8b-instant |
| Summarizer | Dedupe + condense into a knowledge brief | llama-3.1-8b-instant |
| Writer | Brief → cited deliverable in the chosen format | llama-3.3-70b-versatile |
| Editor | Reviews accuracy/structure/tone, gives verdict | llama-3.3-70b-versatile |
Models are per-agent configurable via .env — larger model for the reasoning/writing
roles, fast small model for the high-volume roles — and you can swap any role to a different
free Groq model without touching code. The whole LLM provider is isolated to core/llm.py,
so switching back to Anthropic/OpenAI/etc. is a one-file change.
- Orchestration: LangGraph state machine (
MemorySavercheckpointer, conditional routing) - LLM: Groq (free hosted Llama models) via
langchain-groq - Search: Tavily free tier (
tavily-python) - Backend: FastAPI + Uvicorn, streaming via Server-Sent Events
- Frontend: React + Vite,
react-markdownfor rendering - Config:
pydantic-settings+.env - Logging: structured JSON (
python-json-logger) with per-node timing; optional LangSmith toggle
cd backend
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env # then fill in GROQ_API_KEY and TAVILY_API_KEY (both free, no card)
uvicorn app.main:app --reload --port 8000cd frontend
npm install
npm run dev # http://localhost:5173The Vite dev server proxies /api and /healthz to the backend on port 8000, so the
API key stays server-side and never reaches the browser.
- Open
http://localhost:5173. - Goal: "Compare the leading vector databases for a RAG product", format comparison, audience "a technical engineering team". Click Run research.
- The Agent collaboration trace lights up agent-by-agent (waiting → running → done).
- Sources fill in as the Researcher cites them — clickable links.
- If the Editor returns
revise_writing/needs_more_research, a 🔁 Feedback loop note shows the work routing back (capped at 2 revisions). - The Deliverable renders as Markdown with inline
[1]citations — Copy or Download .md.
| Endpoint | Method | Description |
|---|---|---|
/api/research |
POST | {goal, output_format, audience} → SSE event stream |
/healthz |
GET | Health check |
SSE events: agent_start → agent_output (per node) → final (the deliverable), plus
error on failure.
curl -N -X POST http://localhost:8000/api/research \
-H 'Content-Type: application/json' \
-d '{"goal":"Compare leading vector databases for RAG","output_format":"comparison","audience":"engineers"}'Every node is wrapped by a timing decorator that emits one structured JSON log line:
{"ts":"...","level":"INFO","logger":"agents","message":"node_done","agent":"researcher","output_keys":["findings","log"],"latency_ms":842.1}Set LANGSMITH_TRACING=true (plus LANGSMITH_API_KEY) in .env to also export traces to
LangSmith.
cd backend && source .venv/bin/activate
pytest -qtests/test_agents.py— each agent against fixed fake inputs (LLM/search mocked).tests/test_graph.py— end-to-end graph runs incl. the capped revision loop.
- Revisions capped at
MAX_REVISIONS(default 2). - Researcher limited to 3–5 results per sub-question.
- All model JSON parsed defensively, retried once, then degraded gracefully.
- API keys are server-side only; the frontend never sees them.
- All agent prompts live in one file (
backend/app/agents/prompts.py) for easy tuning.
Hosted the same way as a typical free-tier full-stack app: backend on Render, frontend on Vercel.
Backend → Render
- New → Blueprint, point it at this repo. Render reads
render.yaml. - Set the two secrets in the dashboard:
GROQ_API_KEY,TAVILY_API_KEY. - Deploy. Copy the service URL, e.g.
https://multi-agent-research-api.onrender.com.
Render's free web service sleeps after ~15 min idle, so the first request after a nap takes ~50s to wake. Subsequent requests are fast.
Frontend → Vercel
- Import the repo, set Root Directory to
frontend/(config infrontend/vercel.json). - Add an env var
VITE_API_BASE= your Render backend URL. - Deploy → you get a real
https://<project>.vercel.appURL.
The backend already allows any *.vercel.app origin via CORS, so no extra config is needed
to connect the two. (Optionally pin CORS_ORIGINS on Render to your exact Vercel URL.)
backend/
app/
agents/ # orchestrator, researcher, summarizer, writer, editor + prompts.py
graph/ # state.py (GraphState), build_graph.py (nodes, edges, router)
tools/ # search.py (Tavily wrapper)
core/ # config.py, llm.py (per-agent model factory), logging.py
api/ # routes.py (FastAPI + SSE)
main.py
tests/ # test_agents.py, test_graph.py
frontend/
src/
components/ # GoalForm, AgentTimeline, SourcesPanel, OutputPanel
lib/api.js # SSE-over-POST client
App.jsx