AI-powered biomedical research report generator
Parallel LangGraph agents that plan β retrieve β synthesise β compile evidence-based medical reports from PubMed and the web.
| Feature | Description |
|---|---|
| π¬ Automated Report Planning | DSPy-driven planner generates structured section outlines from a single research query |
| π‘ Triple-Source Retrieval | Live PubMed search via BioPython Entrez, local PubMed FAISS index, and Tavily web search |
| π§ Parallel Agent Architecture | LangGraph orchestrates multiple section-writing agents concurrently with tool access |
| π Scratchpad Protocol | Agents follow a disciplined extract β note β synthesise workflow for traceable research |
| π Hybrid Retrieval (BM25 + Dense) | Ensemble retriever with cross-encoder reranking for high-precision document retrieval |
| π Live PubMed Search | On-demand querying of PubMed with automatic CSV persistence and active-source switching |
| π Unified Dataset Per Run | Single CSV and FAISS index per run, all section branches share one deduplicated dataset |
| π’ Numbered Citation System | Academic-style [N] inline citations with a clean References section at the end |
| β Pre-synthesis Verification | Evidence quality gate checks source count, quantitative depth, and scratchpad length before synthesis |
MedReportAI is a multi-agent LangGraph pipeline with three main phases:
Each research section runs a two-phase loop inside a parallel sub-graph:
- Phase 1 - Tool-Based Research: The section agent uses PubMed search, FAISS retrieval, and web search tools to gather evidence into a scratchpad. A verification gate checks the scratchpad before advancing.
- Phase 2 - Synthesis: The agent writes the final section from the scratchpad, using
[N]numbered citations that map to the shared citation registry.
Every pipeline run receives a unique run_id. All pubmed_scraper_tool calls within a run write to one shared CSV
file, deduplicated by PMID. A single FAISS index reflects this unified dataset and is queried by retriever_tool across
all section branches.
A citation registry (dict[str, int]) maps each unique source URL to a stable number. Section agents emit [N] inline
citations during synthesis. The final report includes a References section containing only sources whose [N] appears
in the body text.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LangGraph Pipeline β
β β
β ββββββββββββββββ βββββββββββββββββββββββββββββββ β
β β Plan Report βββββΆβ Build Sections (parallel) β β
β β (DSPy) β β βββββββββββ βββββββββββ β β
β ββββββββββββββββ β βSection 1β βSection Nβ β β
β β βPhase 1: β βPhase 1: β β β
β β β researchβ β researchβ β β
β β βPhase 2: β βPhase 2: β β β
β β β synth. β β synth. β β β
β β βββββββββββ βββββββββββ β β
β ββββββββββββββ¬βββββββββββββββββ β
β βΌ β
β ββββββββββββββββ βββββββββββββββββββββββββββββββ β
β β Compile ββββββ Write Final Sections β β
β β Final Report β β (intro, conclusion) β β
β β + References β βββββββββββββββββββββββββββββββ β
β ββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β
ββββββββββββββ ββββββββββββββ ββββββββββββββ
β PubMed RAG β β Live β β Tavily β
β (FAISS + β β PubMed β β Web β
β BM25) β β Search β β Search β
ββββββββββββββ ββββββββββββββ ββββββββββββββ
β β β
βββββββββββββββββΌββββββββββββββββ
βΌ
ββββββββββββββββββββββββ
β Unified CSV + FAISS β
β (one per run_id) β
ββββββββββββββββββββββββ
- Entry:
generate_plan - Fan-out:
initiate_section_writingβ parallelbuild_section_with_toolssub-graphs - Gather:
gather_completed_sections - Fan-out:
initiate_final_section_writingβ parallelwrite_final_sections - Compile:
compile_final_report - Validate:
validate_report_quality - Exit:
END
MedReportAI/
βββ app.py # LangGraph pipeline definition & entry point
βββ config.py # Model, retriever, and path configuration
βββ langgraph.json # LangGraph Studio deployment config
βββ pyproject.toml # Project metadata & dependencies
β
βββ agents/
β βββ planner.py # Report plan generation & final section writing (DSPy)
β
βββ core/
β βββ nodes.py # Graph node functions (fan-out, synthesis, compile)
β βββ quality.py # Citation validation, reference building, truncation detection
β βββ schemas.py # Pydantic schemas (scratchpad ops, Section model)
β βββ signatures.py # DSPy signatures (ReportPlanner, FinalInstructions)
β βββ states.py # LangGraph state definitions with reducer annotations
β βββ tool_node.py # Tool execution node with routing + citation registry
β βββ verification.py # Pre-synthesis verification gate
β
βββ rag/
β βββ chain.py # RAG chain construction
β βββ embeddings.py # FastEmbed wrapper for LangChain
β βββ retrieval_builder.py # Ensemble retriever + cross-encoder reranker
β βββ retrieval_formatter.py # Structured report from retriever results
β βββ source_formatter.py # Web search result formatting & deduplication
β
βββ tools/
β βββ pubmed_search.py # Live PubMed search with unified CSV persistence
β βββ retrieval.py # PubMed FAISS retriever tool
β βββ web_search.py # Tavily web search tool
β βββ scratchpad.py # Read/write/clear scratchpad operations
β βββ query_generator.py # DSPy multi-query generator
β
βββ prompts/
β βββ planner.py # Context persona & report structure prompts
β βββ section_writer.py # Two-phase section writing protocol
β βββ scraper.py # PubMed query parsing prompt
β
βββ scripts/
β βββ pubmed_scraper.py # PubMed article scraper (BioPython Entrez + DeepSeek)
β
βββ utils/
β βββ data_processing.py # CSV loading, semantic chunking, FAISS indexing
β βββ formatting.py # Rich console formatters
β βββ helpers.py # Environment setup, logging, file helpers
β βββ scratchpad_helpers.py # Scratchpad read/write/clear handlers
β
βββ tests/ # Detailed test suite
- Python 3.12+
- A DeepSeek API key
- A Tavily API key (for web search)
- (Optional) An email for NCBI Entrez (for live PubMed search)
git clone https://github.com/Chrisolande/MedReportAI.git
cd MedReportAI
# Install with uv (recommended)
uv sync --extra dev
# Or with pip
pip install -e ".[dev]"Create a .env file in the project root:
DEEPSEEK_API_KEY=your_deepseek_api_key_here
TAVILY_API_KEY=your_tavily_api_key_here
ENTREZ_EMAIL=your_email@example.com # Optional: for live PubMed searchThe sole interface is LangGraph Studio. Open the project directory in LangGraph Studio and invoke the graph with a topic:
{
"topic": "Long-term pediatric health outcomes in conflict settings"
}The pipeline runs fully autonomously from topic input to final report.
from app import graph
result = await graph.ainvoke(
{"topic": "Impact of malnutrition on child neurodevelopment"},
config={
"configurable": {
"context": "You are a pediatric nutrition researcher...",
"report_organization": "Structure the report with..."
}
}
)
print(result["final_report"])All settings are centralized in config.py:
| Setting | Default | Description |
|---|---|---|
deepseek_model |
deepseek-chat |
LLM model |
deepseek_temperature |
1.3 |
Generation temperature |
max_tokens |
512 |
Maximum tokens per LLM call |
embedding_model |
sentence-transformers/all-MiniLM-L6-v2 |
Embedding model (FastEmbed) |
k |
15 |
Documents to retrieve |
sparse_weight |
0.65 |
BM25 weight in ensemble |
dense_weight |
0.35 |
Dense retrieval weight |
top_n |
5 |
Documents after reranking |
| Layer | Technology |
|---|---|
| Orchestration | LangGraph - stateful multi-agent pipeline |
| Prompt Framework | DSPy - structured signatures |
| LLM | DeepSeek |
| Retrieval | FAISS + BM25 ensemble with FastEmbed reranking |
| Live Search | BioPython Entrez - real-time PubMed |
| Web Search | Tavily - web search with raw content |
| Tracing | LangSmith - observability |
# Run tests
uv run pytest tests/ -v
# Lint
uvx ruff check .
uvx ruff format .
# Complexity check
uv run radon cc -s -a core/ tools/ scripts/This project is licensed under the MIT License.
Built with β€ by @Chrisolande
