AI Research Agent (template-first v1) — a full-stack playground for orchestrated web research: LangGraph (planner → parallel retrieve per subtask → merge → synthesize), template registry, SSE streaming, SQLite-versioned artifacts, and a Next.js “run room” UI (live observatory, subtask topology, markdown report, exports). Chat/planner calls go through OpenRouter; optional LangSmith tracing and LangGraph Studio are supported.
Repository: github.com/argahv/research-agent
- Backend: FastAPI, LangGraph
Sendfan-out over planner subtasks, bounded retrieval concurrency, DuckDuckGo-style search + page fetch, heuristic synthesis, Markdown/JSON exports, admin run listing. - Frontend: Clay Lab–style UI,
/starter →/reports/{run_id}for the full session (streaming + report + inspector), history/datasets/templates routes, React Flow topology on the run page. - Observability: SSE phases, correlation with LangSmith via
LANGSMITH_*→LANGCHAIN_*bridge,langgraph devfor local graph inspection.
- Python 3.11+ recommended
- Node.js 20+ (for Next.js 15)
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # fill OPENROUTER_API_KEY (never commit secrets)
export STUB_LLM=true # optional: skip remote LLM entirely
uvicorn app.main:app --reload --port 8000npm install
NEXT_PUBLIC_API_URL=http://127.0.0.1:8000 npm run devDefault dev URL is http://localhost:3005 (per app config).
See backend/.env.example: OpenRouter keys/models, embeddings, LangSmith tracing, SQLite URL.
Never paste API keys into chat or commits — rotate any key that was exposed.
Likely causes:
.envnot applied toLANGSMITH_*— tracing callspython-dotenvload_dotenv()on startup (override=False). Put LangSmith vars inbackend/.envand run commands frombackend/, or export them before starting Python.LANGSMITH_API_KEYempty — logs showLANGCHAIN_API_KEY=unset; add a LangSmith API key matching the LangSmith workspace you opened.LANGSMITH_PROJECTmismatch — open Projects → the exact project named in.env(default sample:research-agent), not Personal / Tracing demos.STUB_LLM=trueor noOPENROUTER_API_KEY— the planner skipsChatOpenAI; traces still appear as LangGraph runs (planner,retrieve_*, …) but not as planner “Chat Model” spans. Use a real key andSTUB_LLM=falseto verify LLM child runs.- No traffic yet — run
POST /v1/researchorPOST /v1/research/streamonce, then refresh the LangSmith Runs / Tracing view.
Suggested order:
cp backend/.env.example backend/.envand setLANGSMITH_API_KEY+ (OPENROUTER_API_KEYunless you intentionally use stub).- Start API from
backend/so.envis found:uvicorn app.main:app --reload --port 8000. - Watch startup log:
LangSmith tracing: onwithLANGCHAIN_API_KEY=set. - In LangSmith, select the
LANGSMITH_PROJECTworkspace and inspect recent runs after one API request.
backend/langgraph.json registers compiled graph research from backend/app/graph/studio_entry.py.
From backend/:
pip install -r requirements.txt # installs langgraph-cli
langgraph devPick the research graph in Studio. Use cwd = backend/ so .env aligns with Studio.
| Method | Path | Description |
|---|---|---|
| POST | /v1/research |
Run pipeline, persist artifact, return JSON + Markdown |
| POST | /v1/research/stream |
SSE stages + final complete |
| GET | /v1/research/{run_id} |
Latest report version |
| GET | /v1/admin/runs |
Recent runs + metrics JSON |
| GET | /v1/templates |
Template definitions |
Health: GET /health.
/— query + sync/stream run; navigates to/reports/{run_id}oncerun_idis known (first SSE eventrun_started)./reports/{run_id}— run room: observatory (timeline, trace, eval, topology), report canvas, source inspector, exports./history,/datasets,/templates— history list and stubs./observatory— redirects to/#observatory(scroll target on home); full observatory UI is on/reports/{run_id}when a run exists./metrics— admin-style metrics view.
cd backend && source .venv/bin/activate
STUB_LLM=true pytest| Path | Role |
|---|---|
backend/app/graph/pipeline.py |
LangGraph DAG |
backend/app/graph/streaming.py |
SSE + astream |
backend/app/core/tracing.py |
LangSmith env bridge |
backend/app/services/llm_client.py |
OpenRouter ChatOpenAI |
backend/app/storage/ |
SQLAlchemy + SQLite (data/research.db) |
frontend/app/ |
Next.js App Router pages |
frontend/contexts/ResearchRunContext.tsx |
Run state + SSE |
frontend/components/observatory/ |
Timeline, trace, topology |
Stitch can be wired in Cursor MCP. Do not commit API keys — .cursor/ is gitignored locally.
- Copy
docs/stitch-mcp.example.jsonto.cursor/mcp.jsonin this repo (or merge into your user-level MCP config). - Replace
YOUR_GOOGLE_OR_STITCH_API_KEYwith a key from Google Cloud / Stitch, then reload Cursor.
If a key was ever pushed to GitHub, revoke it in GCP and create a new one before reusing MCP.
Specify your license in a LICENSE file (this repo does not ship one by default).