Research Lab is a Rust project that orchestrates a multi‑agent research pipeline using pocketflow_rs. Agents (PI, Reader, Critic, Planner, Statistician, Ethics, Analyst, Postdoc) collaborate via LLM calls and share state in a central Context. The flow can be driven from the CLI or via an HTTP server. Output includes a live transcript and a final report artifact (PDF/HTML).
- Multi‑agent flow with role‑aware LLM prompts
- Tavily web search in Reader node
- CLI: run once or chat‑seeded flow
- REST API: run, stream via SSE, health
- Live transcript broadcast; optional pacing for readability
- Final report artifact generation (wkhtmltopdf or HTML fallback)
- OpenAPI docs (utoipa + Swagger UI)
- Requirements
- Rust (stable)
- wkhtmltopdf (optional, for PDF; otherwise HTML fallback)
- Environment
Create .env at project root:
OPENAI_API_KEY=sk-...
TAVILY_API_KEY=tvly-...
The app loads .env automatically (dotenvy). Without keys, stubbed responses are used for development.
- Build
cargo build
Run with a goal:
cargo run -- run --goal "Survey: foundation models for healthcare"
Interactive chat (prompts for a message if omitted):
cargo run -- chat "Accelerate a review on foundation models for healthcare"
Start HTTP server:
cargo run -- serve --port 8080
Base: http://localhost:8080
-
Health
- GET
/healthz→ok
- GET
-
Run once (blocking)
- POST
/run - Body:
{ "goal": "..." }(optional) - Returns:
{ "transcript": [...], "final_report": { "artifact": "...", "kind": "pdf|html" } }
- POST
-
Start streaming run (async)
- POST
/run-stream - Body:
{ "goal": "..." }(optional) - Returns immediately:
{ "run_id": "<uuid>" }
- POST
-
Stream transcript (SSE)
- GET
/stream?run_id=<uuid>&delay_ms=1000 - Content-Type:
text/event-stream - Each event data is a JSON
TranscriptEvent:
- GET
{
"type": "transcript",
"run_id": "...",
"agent": "Reader",
"field": "claims",
"value": { ... },
"ts_ms": 173109...
}
Minimal JS client:
const run = await fetch('/run-stream', {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({ goal: 'Survey: foundation models for healthcare' })
}).then(r => r.json());
const es = new EventSource(`/stream?run_id=${run.run_id}&delay_ms=1000`);
es.onmessage = (ev) => {
const evt = JSON.parse(ev.data);
console.log(evt.agent, evt.field, evt.value);
};- Swagger UI:
http://localhost:8080/docs - OpenAPI JSON:
http://localhost:8080/openapi.json
Documented schemas: RunRequest, RunResponse, RunStreamResponse, TranscriptEvent.
Note: SSE is documented as text/event-stream with TranscriptEvent payload schema per event.
- Generated by the FinalReportNode near the end of the flow.
- Default output:
data/artifacts/reports/final_<timestamp>.pdf(requires wkhtmltopdf). If wkhtmltopdf is missing, an HTML file is produced instead.
- Keys not detected → ensure
.envexists at project root and containsOPENAI_API_KEY,TAVILY_API_KEY. - No PDF output → install
wkhtmltopdfor use the HTML fallback. - SSE in Swagger UI → UI won’t render the live stream; test via curl or a simple web page using
EventSource.
MIT