Production-grade multi-agent research system. Enter any topic — autonomous agents plan subtasks, search the web, read articles, synthesize findings, and generate a structured report with citations and cost tracking.
Stack: LangGraph • Groq • Tavily • ChromaDB • Redis • Celery • FastAPI • Docker • LangSmith
User enters topic
│
▼
Planner Agent
(LangGraph node)
Breaks topic into 4 focused subtasks
│
▼
Researcher Agent ──► Tavily Web Search (3 results/subtask)
(runs 4× in loop) ──► Article Content Extraction
──► ChromaDB Memory (check past findings)
──► Groq LLM summarizes key points
──► ChromaDB Memory (save new findings)
│
▼
Writer Agent
(LangGraph node)
Groq LLM writes structured report:
Executive Summary → Key Findings → Analysis → Conclusion → Sources
│
▼
Output: Markdown + PDF report
Cost logged to outputs/costs.jsonl
LangSmith traces every LLM call
API path (async):
POST /research → Celery task dispatched → job stored in Redis
GET /research/{job_id} → poll until status = "complete"
┌─────────────────────────────────────────────────┐
│ Streamlit UI │
│ Sidebar: Status + Cost Monitor │
│ Main: Topic input → Run → Report + Download │
└───────────────────┬─────────────────────────────┘
│ direct call
▼
┌─────────────────────────────────────────────────┐
│ LangGraph Workflow │
│ │
│ planner_node → researcher_node → writer_node │
│ │ │
│ [4 subtasks in loop] │
│ Tavily → Reader → ChromaDB → Groq │
└───────────────────┬─────────────────────────────┘
│
▼
Markdown + PDF saved to outputs/reports/
Cost logged to outputs/costs.jsonl
┌──────────┐ ┌──────────┐ ┌──────────────────┐
│ FastAPI │───►│ Redis │◄───│ Celery Worker │
│ :8000 │ │ :6379 │ │ 4 concurrent │
│ async │ │ job store│ │ run_research() │
└──────────┘ └──────────┘ └──────────────────┘
| Layer | Tool | Why |
|---|---|---|
| Agent framework | LangGraph | Production standard for multi-agent systems |
| LLM | Groq (llama-3.3-70b-versatile) | Fast inference, low cost |
| Web search | Tavily | Structured search results with content |
| Vector memory | ChromaDB | Past findings stored + retrieved per run |
| Task queue | Celery + Redis | Async jobs, 4 concurrent workers |
| Backend API | FastAPI | Async, auto docs, production standard |
| UI | Streamlit | Rapid demo with live cost monitoring |
| Containers | Docker + Compose | 4 services: redis, api, worker, streamlit |
| Monitoring | LangSmith | Traces every LLM call, token costs |
| Evaluation | LLM-as-judge | Scores reports on 4 dimensions (1–5) |
research-agent/
├── app.py # Streamlit UI
├── api.py # FastAPI (async + auth + rate limit)
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── .env
├── src/
│ ├── config.py # Settings, paths
│ ├── llm.py # Groq model + cost tracker
│ ├── costs.py # Token tracking, JSONL logging
│ ├── job_store.py # Redis job persistence
│ ├── worker.py # Celery app
│ ├── tasks.py # Celery research task
│ ├── agents/
│ │ ├── planner.py # Breaks topic into 4 subtasks
│ │ ├── researcher.py # Searches, reads, summarizes
│ │ └── writer.py # Generates structured report
│ ├── graph/
│ │ └── workflow.py # LangGraph state machine
│ ├── tools/
│ │ ├── search.py # Tavily web search
│ │ └── reader.py # Article content extractor
│ ├── memory/
│ │ └── vector_store.py # ChromaDB save/search
│ ├── report/
│ │ └── generator.py # Markdown + PDF export
│ └── eval/
│ └── evaluator.py # LLM-as-judge scoring
├── scripts/
│ └── run_eval.py # Evaluation runner
├── evals/
│ └── topics.json # Test topics + min scores
├── outputs/
│ ├── reports/ # Generated .md and .pdf files
│ ├── memory/ # ChromaDB persistent store
│ ├── costs.jsonl # Per-run token + cost log
│ └── eval_results.jsonl # Eval scores
└── tests/
1. Get API keys:
- Groq: console.groq.com → free tier
- Tavily: tavily.com → free tier
- LangSmith (optional): smith.langchain.com → free tier
2. Install and run:
pip install -r requirements.txt
copy .env.example .env
# Edit .env and add GROQ_API_KEY and TAVILY_API_KEY
streamlit run app.pyOpen: http://localhost:8501
docker compose up --build| Service | URL |
|---|---|
| Streamlit UI | http://localhost:8501 |
| FastAPI docs | http://localhost:8000/docs |
| Redis | localhost:6380 |
| Variable | Required | Default | Description |
|---|---|---|---|
GROQ_API_KEY |
Yes | — | Get from console.groq.com |
TAVILY_API_KEY |
Yes | — | Get from tavily.com |
REDIS_URL |
No | redis://localhost:6379/0 |
Redis connection URL |
API_KEY |
No | dev-key |
Auth key for FastAPI endpoints |
GROQ_MODEL |
No | llama-3.3-70b-versatile |
LLM model name |
LANGCHAIN_API_KEY |
No | — | LangSmith tracing key |
LANGCHAIN_TRACING_V2 |
No | false |
Set true to enable tracing |
LANGCHAIN_PROJECT |
No | research-agent |
LangSmith project name |
All endpoints require X-API-Key header. Rate limit: 10 requests/minute on POST.
Start async research job:
curl -X POST http://localhost:8000/research \
-H "X-API-Key: dev-key" \
-H "Content-Type: application/json" \
-d '{"topic": "AI in healthcare 2026"}'
# → {"job_id": "abc-123", "status": "queued"}Poll for result:
curl http://localhost:8000/research/abc-123 \
-H "X-API-Key: dev-key"
# → {"status": "complete", "result": {"report": "...", "sources": [...]}}List all jobs:
curl http://localhost:8000/jobs -H "X-API-Key: dev-key"Cost summary:
curl http://localhost:8000/costs -H "X-API-Key: dev-key"Health check (no auth):
curl http://localhost:8000/healthAuto-generated API docs: http://localhost:8000/docs
Every research run logs token usage and cost to outputs/costs.jsonl:
{"timestamp": "2026-06-07T15:14:45", "topic": "AI in india 2026",
"prompt_tokens": 5978, "completion_tokens": 2293,
"total_tokens": 8271, "cost_usd": 0.005338}Live totals shown in the Streamlit sidebar. Full history via GET /costs.
Pricing: Groq llama-3.3-70b-versatile — $0.59/M input, $0.79/M output tokens.
LLM-as-judge scores reports on 4 dimensions (1–5):
python scripts/run_eval.pyScores: completeness, structure, citations, insight, overall
Results saved to outputs/eval_results.jsonl:
{"topic": "AI in healthcare 2026", "completeness": 4, "structure": 5,
"citations": 4, "insight": 3, "overall": 4, "passed": true}Add custom topics to evals/topics.json.
# Terminal 1 — start Celery worker (needs Redis running locally)
celery -A src.worker.celery_app worker --loglevel=info --concurrency=4
# Terminal 2 — start API
uvicorn api:app --port 8000 --reload
# Terminal 3 — start UI
streamlit run app.pypytest

