An Autonomous Multi-Agent Research Assistant.
Research, Reinvented. A multi-agent AI system that plans, investigates, reasons, and synthesizes knowledge autonomously. Give it a goal — "Find recent methods for hallucination mitigation in RAG" — and it plans the research, searches arXiv and the web, reads what it finds, critiques its own evidence, and writes a structured, evidence-backed report.
This is not a chatbot. It's an autonomous research pipeline.
- Why ResearchOS
- Architecture
- The Agent Pipeline
- Features
- Tech Stack
- Folder Structure
- Getting Started
- Environment Variables
- Deployment
- Testing
- Roadmap
- Contributing
- License
A single LLM call answers from parametric memory, one shot, with no way to notice its own gaps. Research doesn't work that way — it's iterative: plan, search, read, revise. ResearchOS keeps that iteration instead of collapsing it into one prompt:
| Single-prompt LLM | ResearchOS |
|---|---|
| Answers from training data | Plans, searches, and reads before answering |
| One shot, no self-check | A Critic agent actively looks for gaps and can send the pipeline back for another pass |
| Black box | Every step streams live — you watch the reasoning happen |
| Confidently wrong looks like correct | Every claim traces back to a real, citable source |
Built as a demonstration of production-grade agentic AI systems design: clean provider abstractions, independently testable tools, typed contracts shared across the stack, and an orchestration layer that critiques its own output.
flowchart TB
subgraph Client["apps/web — Next.js 15"]
UI[Dashboard & Landing Page]
end
subgraph API["apps/api — FastAPI"]
Stream["/api/research/stream (SSE)"]
History["/api/history"]
Memory["/api/memory/query"]
end
subgraph Agents["packages/agents — LangGraph"]
Supervisor --> Planner --> Research --> Paper --> Web --> Summarizer --> Critic
Critic -- "evidence too thin, revise" --> Paper
Critic -- approved --> Reporter
end
subgraph Foundation["Foundation"]
LLM["LLM Provider — Groq (via LiteLLM)"]
Mem["packages/memory — ChromaDB + SQLite"]
Tools["Tools — arXiv, Tavily, DuckDuckGo, PDF parser"]
end
UI -- "goal" --> Stream
UI --> History
UI --> Memory
Stream -- "live agent events (SSE)" --> UI
Stream --> Agents
Agents --> LLM
Agents --> Tools
Agents --> Mem
Memory --> Mem
Four layers, one responsibility each:
- Client never talks to an LLM directly — only to the API.
- API is a thin layer: it starts a graph run, streams its events over SSE, and persists the result. No agent logic lives here.
- Orchestration (
packages/agents) is the LangGraph pipeline. Every node is its own file with its own prompt, tools, and structured output. - Foundation — the LLM provider, memory, and search tools — is what the agents are built on, and none of it is agent-aware. The memory store doesn't know what a "Critic" is; the LLM provider doesn't know what a "Report" is.
User goal
│
▼
Supervisor — triages: does this need papers, the web, or both?
│
▼
Planner — decomposes the goal into 3–6 concrete research tasks
│
▼
Research — turns the plan into precise search queries
│
▼
Paper Agent — searches arXiv, extracts grounded findings from abstracts
│
▼
Web Agent — searches Tavily/DuckDuckGo, reads pages, extracts findings
│
▼
Summarizer — merges & deduplicates findings, builds a comparison table
│
▼
Critic ───────► if evidence is thin & revision budget remains,
│ loop back to Paper Agent with sharper queries
▼ (approved)
Reporter — writes the final report: summary, findings, comparison
table, open questions, future research, references
Each agent is its own module under packages/agents/agents/nodes/, with its
own prompt in packages/prompts/. Nothing is one giant prompt — the Planner
doesn't know how the Critic phrases feedback, and the Reporter doesn't know
how the Web Agent selects sources.
- Multi-agent orchestration — eight LangGraph nodes, each independently promptable, testable, and swappable.
- Live execution visibility — the dashboard streams every agent's status, tool calls, and reasoning over server-sent events as they happen.
- Evidence-backed reports — executive summary, key findings with confidence scores, a comparison table, open questions, future research directions, and full references — exportable as Markdown, copyable, or printable to PDF.
- Three-tier memory — working memory (current run), episodic memory (past sessions), and semantic memory (accumulated findings), all over ChromaDB. Every session queries what's already known before searching from scratch, and every completed report writes back into memory.
- Provider-agnostic LLM core — one
LLMProviderinterface; Groq is implemented, OpenAI and Ollama are architected. Switching is an environment variable, not a rewrite. - Resilient by design — every external tool call (arXiv, web search) is timeout-bounded and runs concurrently across queries, so one flaky provider can't stall the whole pipeline; structured LLM calls retry with backoff on rate limits.
| Layer | Technology |
|---|---|
| Frontend | Next.js 15, React 19, TypeScript, Tailwind CSS v4, shadcn/ui, Framer Motion, Lucide |
| Backend | FastAPI, Python 3.12 |
| Orchestration | LangGraph, LangChain |
| Inference | Groq, via LiteLLM (provider-agnostic) + Instructor (structured outputs) |
| Memory | ChromaDB (working / episodic / semantic) |
| Storage | SQLite (Postgres-ready via DATABASE_URL) |
| Search | arXiv, Tavily, DuckDuckGo |
| Observability | LangSmith |
| Deployment | Vercel (frontend), Render / Railway / Docker (backend) |
autonomous-research-agent/
├── apps/
│ ├── web/ # Next.js 15 frontend (landing page + dashboard)
│ └── api/ # FastAPI backend (routers, services, SSE streaming)
├── packages/
│ ├── agents/ # LangGraph graph, nodes, LLM providers, tools
│ ├── memory/ # Working/episodic/semantic memory + SQL repository
│ ├── prompts/ # One markdown file per agent — never hardcoded
│ └── types/ # Shared TypeScript types, mirroring shared/schemas
├── shared/
│ └── schemas/ # Shared Pydantic models (Report, AgentEvent, etc.)
├── docker/ # Dockerfiles for api and web
├── docs/ # Architecture notes
├── docker-compose.yml
├── render.yaml
├── railway.json
└── .env.example
Every package is independently installable and testable — packages/agents
doesn't import from apps/api, and packages/memory doesn't know LangGraph
exists.
- Python 3.11+ (3.12 recommended)
- Node.js 20+
- A Groq API key (required)
- A Tavily API key (optional — falls back to DuckDuckGo)
git clone <this-repo> researchos && cd researchos
cp .env.example .env
# edit .env — at minimum set GROQ_API_KEYpython -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
cd apps/api
pip install -r requirements-dev.txt # run from inside apps/api — see the file's comment
python -m uvicorn app.main:app --reloadThe API is now running at http://localhost:8000 (/health should return
{"status": "ok"}).
cd apps/web
npm install
echo "NEXT_PUBLIC_API_BASE_URL=http://localhost:8000" > .env.local
npm run devOpen http://localhost:3000 for the landing page, or
http://localhost:3000/dashboard to launch a research session directly.
docker compose up --buildThis builds and runs both services — API on :8000, web on :3000 — with a
persistent volume for SQLite + ChromaDB data.
See .env.example for the full, commented list. The
important ones:
| Variable | Required | Description |
|---|---|---|
LLM_PROVIDER |
No (default groq) |
groq | openai | ollama — the only thing that changes to switch model backends |
GROQ_API_KEY |
Yes | Groq API key |
TAVILY_API_KEY |
No | Enables Tavily web search; falls back to DuckDuckGo without it |
LANGSMITH_API_KEY |
No | Enables LangSmith tracing |
CORS_ORIGINS |
No | Comma-separated allowed origins for the API |
DATABASE_URL |
No | SQLite by default; point at a postgresql+psycopg:// URL for production |
NEXT_PUBLIC_API_BASE_URL |
Yes (frontend) | Where the frontend finds the API — never assume localhost in production |
- Import the repo in Vercel, set Root Directory to
apps/web. - Set
NEXT_PUBLIC_API_BASE_URLto your deployed API URL. - Deploy —
apps/web/vercel.jsonand the Next.js defaults handle the rest. No code changes required; the frontend never assumeslocalhost.
- Push this repo to GitHub.
- In Render, "New Blueprint Instance" pointed at this repo — it reads
render.yamland buildsdocker/api.Dockerfile. - Set
GROQ_API_KEY(andTAVILY_API_KEYif you have one) in the Render dashboard — they're intentionally not in the blueprint. - Update
CORS_ORIGINSto your Vercel domain once it's known.
railway.json points Railway at the same docker/api.Dockerfile. Set the
same environment variables as above in the Railway dashboard.
docker build -f docker/api.Dockerfile -t researchos-api .
docker run -p 8000:8000 --env-file .env researchos-apiEvery package tests independently:
# Agents + tools (arXiv, DuckDuckGo, citation extraction, LLM provider factory)
python -m pytest packages/agents/tests
# Memory (SQL repository, ChromaDB collections)
python -m pytest packages/memory/tests
# API (run from inside apps/api)
cd apps/api && python -m pytest tests
# Frontend type-check
cd apps/web && npx tsc --noEmit# Python — from the repo root
ruff check shared packages apps/api
black shared packages apps/api
# TypeScript — from apps/web
npm run lint
npm run formatArchitected for, intentionally not implemented in this MVP:
- Knowledge graph — Neo4j-backed entity/relationship extraction across accumulated findings
- Multi-user auth & projects — session/report ownership, shared workspaces
- Agent marketplace — user-defined agent nodes and prompts
- Long-term memory consolidation — periodic summarization of episodic memory into denser semantic knowledge
- Experiment execution — agents that can run code, not just read about it
- GitHub research — a dedicated tool for reading repos, issues, and PRs
Issues and PRs are welcome. Keep changes scoped and modular — a bug fix shouldn't need to touch the frontend, and a new tool shouldn't need to touch the graph. Run the relevant package's tests before opening a PR.