An AI-powered radiology training platform that helps medical students and radiologists improve their diagnostic skills through intelligent medical document comparison and personalized feedback.
- MedDaaS - Medical Data-as-a-Service
MedDaaS enables radiology students to practice reading chest X-rays by writing their own diagnostic reports, which are then compared against expert ground truth reports using advanced AI. The system provides detailed, finding-by-finding analysis with scores, educational feedback, and actionable suggestions for improvement.
- AI-Powered Report Comparison: Uses SpoonAI SDK with graph-based workflows to orchestrate multi-step LLM analysis
- Real-Time Streaming: Server-Sent Events (SSE) deliver findings progressively as they're analyzed
- Multi-Dimensional Scoring: Evaluates reports on similarity, completeness, accuracy, and medical terminology
- Educational Feedback: AI-generated strengths, improvement areas, and specific recommendations
- Learning History: Track progress over time with detailed comparison history and statistics
- Interactive Image Viewer: Fullscreen mode with zoom, brightness, and contrast controls
- User Authentication: Secure session-based auth with bcrypt password hashing and automatic salting
- Semantic Image Search: RAG-based retrieval using FAISS vector store with cosine similarity
- FastAPI - Modern async Python web framework
- SpoonAI SDK - LLM orchestration with graph-based workflows
- PostgreSQL - Persistent data storage
- SQLAlchemy - Async ORM with Alembic migrations
- FAISS - Vector similarity search for RAG capabilities
- Jinja2 - Server-side HTML templating
- Bootstrap CSS - Responsive UI framework
- Vanilla JavaScript - EventSource API for SSE streaming
- Google Gemini (default)
- Anthropic Claude
- OpenAI GPT
- DeepSeek
- Docker Compose - Container orchestration
- Ollama - Local embeddings (optional GPU support)
- uv - Fast Python package management
The comparison workflow uses SpoonAI's StateGraph for structured AI orchestration:
START
β
βΌ
βββββββββββββββββββββββββββ
β retrieve_ground_truth β βββ Fetch expert report from database
βββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββ
β compare_reports β βββ LLM analyzes student vs ground truth
βββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββ
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββββββ
β score_report β β generate_summary β β Parallel execution
βββββββββββββββββββ βββββββββββββββββββββββ
β β
ββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββ
β finalize β βββ Assemble final results
βββββββββββββββββββββββββββ
β
βΌ
END
MedDaaS implements four custom tools using SpoonAI's BaseTool class, each designed for a specific step in the radiology report analysis workflow:
| Tool | Type | Description |
|---|---|---|
RetrieveGroundTruthTool |
Data Access | Fetches expert radiologist reports from the ground truth repository by image ID |
CompareReportsTool |
LLM-Powered | Performs semantic comparison between student and expert reports, extracting findings with streaming support |
ScoreReportTool |
Rule-Based | Calculates multi-dimensional scores using finding analysis and medical terminology matching |
GenerateSummaryTool |
LLM-Powered | Generates educational feedback with strengths, improvements, and suggestions via streaming |
All tools extend BaseTool and implement the async execute() method:
from spoon_ai.tools import BaseTool
from spoon_ai.tools.base import ToolResult
class CompareReportsTool(BaseTool):
name: str = "compare_reports"
description: str = "Compares student report against ground truth..."
parameters: dict = {
"type": "object",
"properties": {
"student_report": {"type": "string", ...},
"ground_truth_report": {"type": "string", ...},
},
"required": ["student_report", "ground_truth_report"],
}
async def execute(self, *, student_report: str, ground_truth_report: str, **kwargs) -> ToolResult:
# LLM-based semantic comparison
response = await llm.ask(messages=[...], system_msg=COMPARISON_SYSTEM_MSG)
return ToolResult(output=comparison_data)The CompareReportsTool and GenerateSummaryTool support streaming via execute_stream() with callbacks:
async def execute_stream(
self,
*,
student_report: str,
ground_truth_report: str,
on_finding: Callable[[Dict[str, Any]], None], # Called for each finding
**kwargs,
) -> ToolResult:
async for chunk in llm.astream(messages=[...]):
# Extract complete JSON objects from stream
new_findings = extractor.feed(chunk.delta)
for finding in new_findings:
on_finding(finding) # Emit to SSE immediately
return ToolResult(output={"findings": all_findings})The ScoreReportTool includes a curated list of 30+ radiology terms for terminology assessment:
MEDICAL_TERMS = [
"consolidation", "opacity", "infiltrate", "effusion", "pneumothorax",
"cardiomegaly", "mediastinum", "hilar", "pulmonary", "bilateral",
"costophrenic", "diaphragm", "atelectasis", "pleural", "cardiac", ...
]Reports are evaluated across four dimensions:
| Score | Description |
|---|---|
| Similarity | Overall semantic match to ground truth |
| Completeness | Coverage of key findings from expert report |
| Accuracy | Correctness of stated observations |
| Terminology | Proper use of medical/radiological terms |
Each finding is classified as:
- Correct - Accurately identified and matches ground truth
- Partial - Partially correct observation
- Missed - Present in ground truth but not in student report
- Extra - Student mentioned but not in ground truth
MedDaaS includes a RAG (Retrieval-Augmented Generation) system for intelligent image retrieval based on natural language queries.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RAG Pipeline β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β INGESTION (Offline) β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Medical β β Embedding β β FAISS β β
β β Reports βββββΆβ Model βββββΆβ Vector β β
β β (JSON) β β (Ollama/ β β Store β β
β β β β Gemini) β β β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β β β
β βΌ βΌ β
β ββββββββββββββββ ββββββββββββββββ β
β β Findings + β β Vector β β
β β Impression + β β Embeddings + β β
β β MeSH Terms β β Metadata β β
β ββββββββββββββββ ββββββββββββββββ β
β β
β RETRIEVAL (Runtime) β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β User Query β β Query β β Cosine β β
β β "chest xray βββββΆβ Embedding βββββΆβ Similarity β β
β β with pleuralβ β β β Search β β
β β effusion" β β β β β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββ β
β β Top-1 Match β β
β β (Image + β β
β β Metadata) β β
β ββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Medical reports are embedded using a structured format:
Findings: {radiological findings text}
Impression: {clinical impression}
Conditions: {parsed MeSH terms}
Each vector in the FAISS store includes:
| Field | Description |
|---|---|
image_id |
Unique X-ray identifier |
image_path |
Path to PNG image file |
findings |
Expert radiological findings |
impression |
Clinical impression summary |
mesh_terms |
Parsed MeSH medical terms |
difficulty |
Computed difficulty (easy/medium/hard) |
Cases are automatically classified by complexity:
- Easy: Normal findings or single condition
- Medium: 2-3 conditions present
- Hard: 4+ conditions or complex pathology (mass, effusion, pneumothorax, etc.)
| Provider | Model | Use Case |
|---|---|---|
| Ollama | nomic-embed-text | Local, no rate limits, GPU optional |
| Gemini | gemini-embedding-001 | Cloud, fast, good accuracy |
| OpenAI | text-embedding-3-small | Cloud, highest accuracy |
-
Clone the repository:
git clone https://github.com/CodeSeoul/MedDaaS.git cd MedDaaS -
Create environment file:
cp .env.example .env
-
Configure
.envwith your settings:# Required: At least one LLM API key GEMINI_API_KEY=your_gemini_key # ANTHROPIC_API_KEY=your_anthropic_key # OPENAI_API_KEY=your_openai_key # Database DATABASE_PASSWORD=your_secure_password # Session security SESSION_SECRET_KEY=your_secret_key_here
-
Install dependencies:
uv sync
-
Start infrastructure:
docker compose up -d
-
(Optional) Pull embedding model for RAG:
docker exec ollama ollama pull nomic-embed-text
uv run main.pyAccess the application:
- Web UI: http://localhost:8000/
- API Docs: http://localhost:8000/docs
MedDaaS uses data from the NIH Open-i:images and NIH Open-i:reports chest X-ray dataset.
Download the PNG images and XML reports from the NIH website.
Convert XML reports to JSON format:
uv run python report_xml_parsing.py \
--xml_dir /path/to/NLMCXR_reports/ecgen-radiology \
--out_json ./data/reports/all.json \
--images_root ./data/images/For semantic search capabilities:
uv run python ingest_reports.py --limit 100 --provider ollama --input ./data/reports/all.json| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/auth/signup |
Create new account |
| POST | /api/v1/auth/login |
Authenticate user |
| GET | /api/v1/auth/me |
Current user info |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/reports/compare |
Compare student report (sync) |
| GET | /api/v1/reports/compare/stream |
Compare with SSE streaming |
| GET | /api/v1/reports/images |
List available images |
| GET | /api/v1/reports/images/random |
Random image for practice |
| GET | /api/v1/reports/images/{id} |
Get image metadata |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/history/me |
User's comparison history |
| GET | /api/v1/history/{id} |
Specific comparison details |
| GET | /api/v1/stats/me |
User performance statistics |
MedDaaS/
βββ app/
β βββ agents/ # SpoonAI graph workflows
β βββ api/routes/ # FastAPI endpoints
β βββ core/ # Config, auth, exceptions
β βββ db/ # Database session management
β βββ models/ # SQLAlchemy models
β βββ prompts/ # LLM prompt templates
β βββ repositories/ # Data access layer
β βββ schemas/ # Pydantic models
β βββ services/ # Business logic
β βββ templates/ # Jinja2 HTML templates
β βββ tools/ # SpoonAI tool implementations
β βββ utils/ # Helpers (JSON streaming, etc.)
βββ data/
β βββ images/ # X-ray PNG files
β βββ reports/ # Ground truth JSON
βββ docker-compose.yml # PostgreSQL + Ollama
βββ main.py # Application entry point
βββ pyproject.toml # Dependencies
This project was built for the Scoop AI Hackathon: Seoul Bowl (December 20-21, 2025), organized by Neo and SpoonOS.
MedDaaS directly addresses the AI4Science track by accelerating medical education through AI-powered diagnostic training.
| Criteria | How MedDaaS Satisfies It |
|---|---|
| Innovation & Originality | Novel application of graph-based LLM workflows to medical education; finding-by-finding streaming analysis provides real-time learning feedback unlike traditional batch processing |
| Technical Execution | Production-ready architecture with async FastAPI, SpoonAI graph orchestration, PostgreSQL persistence, SSE streaming, and multi-provider LLM support |
| Real-World Impact | Addresses critical shortage of radiology training resources; enables scalable, personalized education without requiring expert radiologist time |
| User Experience & Design | Intuitive UI with progressive result streaming, collapsible report cards, fullscreen image viewer with zoom/contrast controls, and responsive design |
| Presentation & Teamwork | Built collaboratively by CodeSeoul members; clean codebase with separation of concerns and comprehensive documentation |
-
SpoonAI SDK Integration
- Graph-based workflow orchestration for complex multi-step AI tasks
- Parallel node execution for scoring and summary generation
- Streaming tool execution with callbacks
-
Agentic AI Capabilities
- Autonomous report analysis without human intervention
- Multi-dimensional evaluation with educational feedback
- Context-aware suggestions based on specific findings
-
Production Architecture
- Async/await throughout for high concurrency
- Database fallback for offline operation
- Configurable LLM providers (Gemini, Claude, GPT, DeepSeek)
-
RAG-Based Semantic Search
- FAISS vector store for efficient similarity search
- Medical text embeddings with MeSH term enrichment
- Cosine similarity retrieval for top-k matching
- Multi-provider embedding support (Ollama, Gemini, OpenAI)
- SpoonAI SDK - Core orchestration framework for LLM workflows
- Google Cloud - Gemini API for language model inference
This project was developed by MedDaaS team for the Scoop AI Hackathon.
MIT License - See LICENSE file for details.
Built with SpoonAI for the Scoop AI Hackathon: Seoul Bowl 2025