Skip to content

Chrisolande/MedReportAI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

66 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🧬 MedReportAI

AI-powered biomedical research report generator

Parallel LangGraph agents that plan β†’ retrieve β†’ synthesise β†’ compile evidence-based medical reports from PubMed and the web.

Python 3.12+ LangGraph DSPy DeepSeek License: MIT


✨ Features

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

πŸ— Architecture

MedReportAI is a multi-agent LangGraph pipeline with three main phases:

Two-Phase Section Writer

Each research section runs a two-phase loop inside a parallel sub-graph:

  1. 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.
  2. Phase 2 - Synthesis: The agent writes the final section from the scratchpad, using [N] numbered citations that map to the shared citation registry.

Unified Dataset Per Run

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.

Numbered Citation System

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)     β”‚
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Graph Topology

  • Entry: generate_plan
  • Fan-out: initiate_section_writing β†’ parallel build_section_with_tools sub-graphs
  • Gather: gather_completed_sections
  • Fan-out: initiate_final_section_writing β†’ parallel write_final_sections
  • Compile: compile_final_report
  • Validate: validate_report_quality
  • Exit: END
LangGraph pipeline topology

LangGraph Studio view of the pipeline graph


πŸ“ Project Structure

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

πŸš€ Getting Started

Prerequisites

Installation

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]"

Environment Variables

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 search

πŸ–₯ Usage

LangGraph Studio

The 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.

Programmatic API

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"])

βš™ Configuration

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

πŸ›  Tech Stack

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

πŸ§ͺ Development

# 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/

πŸ“„ License

This project is licensed under the MIT License.


Built with ❀ by @Chrisolande

About

A biomedical research agent powered by Retrieval-Augmented Generation (RAG). It leverages PubMed and Tavily for knowledge retrieval, enabling accurate, context-aware medical report synthesis and evaluation.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages