MediCortex: AI-Powered Medical Research Assistant
Built for LuminHacks 2025 | Website: http://medicortex.appwrite.network/ | Demo: https://youtu.be/fvXn2LLjlIQ
Inspiration
Okay, real talk - have you ever Googled a symptom and ended up convinced you had three days to live? Yeah, me too.
The problem isn't that medical information doesn't exist online. It's that:
- WebMD and similar sites are designed to scare you (more clicks = more ad revenue)
- Actual research papers are locked behind $40 paywalls or written in language only doctors understand
- AI chatbots confidently lie about medical facts without citing any sources
- You have no way to verify what's real and what's made up
I'm not a medical professional, but I am a developer who got tired of this mess. So I thought: what if we could build an AI that's forced to show its sources? Like a good teacher who makes you cite your work.
That's how MediCortex was born - an AI research assistant that can only answer using real PubMed papers, and must cite every claim. No hallucinations, no guessing, just real research with receipts.
What it does
MediCortex is basically a medical librarian with superhuman speed who's read 50,000+ research papers and can find answers to your questions in under a second - with citations for everything.
Here's the magic:
You: "What are the latest treatments for type 2 diabetes?"
MediCortex:
- 🔍 Searches through 50,000+ PubMed articles using hybrid search (combines semantic understanding with keyword matching)
- 🤖 Feeds the most relevant papers to Google Gemini
- 📝 Gemini writes an answer - but here's the catch: it can ONLY use information from those specific papers
- ✅ Every single claim gets a citation [PMID: xxx] that you can click to verify on PubMed
Result: You get plain-English answers backed by real science. No "Dr. Google" scaring you unnecessarily, no unsourced claims.
Cool features I'm proud of:
Smart Search 🔍
- Combines vector embeddings (768 dimensions of semantic meaning) with traditional keyword search
- Finds papers even when your wording is different from medical terminology
- Example: searching "sugar disease" finds diabetes papers
AI That Can't Lie 🤖
- Custom RAG (Retrieval-Augmented Generation) pipeline using Google Gemini
- Strict system prompts that force citations
- If the papers don't have an answer, it says so instead of making something up
One-Click Research Tools 📚
- Synthesize: Select multiple papers, click a button, AI identifies where researchers agree vs. disagree
- Compare: Side-by-side analysis of different studies
- Extract Insights: Get the key clinical takeaways without reading 50 abstracts
Works Everywhere 💻
- Web app (no installation needed)
- Desktop apps (macOS, Windows, Linux)
- Built with Flutter - one codebase, works everywhere
How I built it
The Stack (aka what I learned in 2 weeks)
AI Models:
- Google Gemini (gemini-flash-latest) - For reading papers and generating answers
- Vertex AI (text-embedding-004) - For turning text into 768-dimensional vectors that capture meaning
- Built my own RAG pipeline to connect them
Search Engine:
- Elasticsearch 8.x with hybrid search
- Implemented Reciprocal Rank Fusion (RRF) to merge vector and keyword results
- Indexed 50K+ PubMed articles with full abstracts + embeddings
Frontend:
- Flutter for cross-platform UI (seriously, Flutter is amazing)
- Material Design 3 for that clean, modern look
- Markdown rendering so AI responses look good
- Riverpod for state management
Backend:
- Appwrite Functions for the web deployment (handles CORS issues)
- PubMed API for fetching articles
- Python scripts for data ingestion and embedding generation
The Build Process (Day by Day)
Days 1-2: Search Infrastructure
- Set up Elasticsearch Cloud (free tier)
- Designed index mapping for 768-dim vectors
- Built the hybrid search query (kNN + BM25 + RRF fusion)
- Got my first successful search working - that "it actually works!" moment was incredible
Days 3-4: Data Ingestion
- Wrote Python scripts to fetch 50K articles from PubMed API
- Used Vertex AI to generate embeddings for all abstracts
- Batch processed 1000 documents at a time (learned about rate limits the hard way)
- Took about 90 minutes total to process everything
Days 5-6: RAG Pipeline
- Integrated Google Gemini API
- Spent way too long on prompt engineering to force citations
- Implemented streaming responses (way better UX than waiting)
- Added function calling so Gemini could search autonomously
Days 7-8: UI/UX
- Built the chat interface
- Added quick action buttons (Synthesize, Compare, Insights)
- Implemented search history and analytics
- Made it look pretty (design matters!)
Days 9-10: Web Deployment
- Hit CORS errors trying to call Elasticsearch from browser (classic web dev problem)
- Solution: Created Appwrite serverless function to proxy requests
- Implemented platform-specific code (web uses proxy, desktop goes direct)
- Learned about double-encoding bugs (my JSON had backslashes in its backslashes...)
Days 11: Polish
- Recorded demo video
- Fixed loading states
- Added error handling
- Wrote documentation
The Tech Details (for the curious)
How Hybrid Search Works:
// 1. User query comes in
final query = "diabetes treatment options";
// 2. Generate embedding (captures semantic meaning)
final embedding = await vertexAI.generateEmbedding(query);
// Result: [0.023, -0.145, 0.891, ...] (768 numbers)
// 3. Search Elasticsearch with BOTH methods
final results = await elastic.hybridSearch(
query: query, // Keyword search (BM25)
queryEmbedding: embedding, // Vector search (kNN)
size: 10
);
// 4. Elasticsearch uses RRF to merge results
// RRF = Reciprocal Rank Fusion
// Score = 1/(k + rank_in_vector) + 1/(k + rank_in_keyword)
Why This Works Better:
- Keyword search finds exact medical terms
- Vector search finds conceptually similar papers
- RRF combines both without needing to normalize scores
- Result: More relevant papers than either method alone
The RAG Pipeline:
// After getting papers from search...
// 1. Build structured context
final context = papers.map((p) => '''
[Paper ${p.index}]
Title: ${p.title}
PMID: ${p.id}
Authors: ${p.authors.join(", ")}
Year: ${p.year}
Abstract: ${p.abstract}
''').join('\n\n');
// 2. Create prompt with strict instructions
final prompt = '''
You are a medical research assistant.
RULES:
1. Answer ONLY using the papers below
2. Cite EVERY claim with [PMID: xxx]
3. If papers don't answer the question, say so
4. Note contradictions between papers
5. Remind user to consult healthcare professionals
Papers:
$context
Question: $userQuestion
''';
// 3. Ask Gemini
final answer = await gemini.generate(prompt);
// Result: Grounded, cited answer
Challenges I ran into
Challenge 1: The Double-Encoding Nightmare
The Bug: After setting up the web deployment, search worked but returned this cryptic error:
Invalid argument (string): Contains invalid characters: \\\\\\\"
What was happening: My Appwrite function returned a decoded JSON object. My Flutter code encoded it once. Then somewhere in the pipeline it got encoded AGAIN. So I had backslashes escaping backslashes escaping backslashes...
The Fix: Track the state of your data. Is it a string? A Map? Encoded? Decoded? In async pipelines, it's easy to lose track. I added checks:
final data = result['data'];
if (data is! Map<String, dynamic>) {
throw Exception('Expected Map, got ${data.runtimeType}');
}
// Only encode ONCE here
final jsonString = jsonEncode(data);
Lesson: JSON encoding/decoding in async flows is tricky. Always know whether you're working with strings or objects.
Challenge 2: Context Window Limits
The Problem: Medical abstracts are long. I naively tried stuffing 50 papers into Gemini's context. It said "nope" and returned an error.
What I learned: 50 papers × 300 words = 15,000+ words. That's way over the context limit.
The Solution:
- Rank papers by relevance score
- Take only top 5
- Truncate abstracts to 500 characters
- Still include enough info for good answers
Surprising result: Quality actually improved. Less noise = clearer answers.
Challenge 3: Embeddings at Scale
The Challenge: I needed to generate embeddings for 50,000 article abstracts. Vertex AI has rate limits.
Naive approach: Loop through all 50K and call the API. Result: Rate limited after 100 requests. 😅
Solution: Batch processing
// Process 1000 at a time
for (var i = 0; i < texts.length; i += 1000) {
final batch = texts.sublist(i, min(i + 1000, texts.length));
final embeddings = await vertexAI.generateBatch(batch);
// Exponential backoff on rate limit
if (rateLimited) {
await Future.delayed(Duration(seconds: pow(2, retryCount)));
}
}
Result: 50,000 embeddings in ~90 minutes, zero failures.
Challenge 4: Making RAG Actually Work
The Problem: My first RAG attempt just dumped papers into the prompt. Gemini would ignore them and answer from its training data. Classic hallucination.
What I tried:
- ❌ "Here are some papers..." - Gemini ignored them
- ❌ "Based on these papers..." - Better, but still hallucinated
- ✅ "You MUST ONLY use these papers. Cite EVERY claim. If papers don't answer, say so." - Finally worked!
Key insight: Prompt engineering is EVERYTHING in RAG. Be very explicit about what the AI can and cannot do.
Challenge 5: CORS (The Classic Web Problem)
What happened: Worked perfectly on desktop. Deployed to web. Instant errors:
CORS policy: No 'Access-Control-Allow-Origin' header
Why: Browsers block cross-origin requests for security. Can't call Elasticsearch directly from client.
Solution: Serverless proxy
- Created Appwrite Function that runs server-side
- Flutter web calls proxy, proxy calls Elasticsearch
- Desktop apps still call Elasticsearch directly
- One codebase, platform-specific behavior
Accomplishments that I'm proud of
Technical Stuff:
50K+ Articles Indexed 🎉
- Comprehensive medical coverage
- Each with 768-dim embeddings
- Sub-second search performance
Zero Hallucinations ✅
- Every answer is grounded in papers
- Every claim has a clickable citation
- Users can verify everything
Actually Fast ⚡
- ~800ms from question to answer
- Streaming makes it feel instant
- Searches 50K papers in <1 second
Works Everywhere 💻
- One Flutter codebase
- Web + macOS + Windows + Linux
- Responsive design
Clean Code 📝
- Following Clean Architecture
- Well documented
- Easy to test and maintain
Personal Wins:
Learned RAG From Scratch 🧠
- Built my own implementation
- Understood the theory AND practice
- Can explain it to others now
Mastered Hybrid Search 🔍
- Vector embeddings finally "clicked" for me
- Understood when to use semantic vs. keyword
- Can implement this for other domains
Shipped Something Real 🚀
- Not just a tutorial project
- Solves an actual problem
- People can actually use it
Portfolio Material 📂
- Impressive demo video
- Well-documented code
- Deployed and accessible
What I learned
About AI:
RAG is both simple and complex
- Concept: Retrieve relevant docs, generate answer from them
- Simple in theory, tricky in practice
- Prompt engineering is 80% of the work
Embeddings are incredible
- 768 numbers that capture meaning
- "diabetes treatment" matches "insulin therapy"
- Even though words are completely different
Function calling is powerful
- Let AI decide when to use tools
- More natural interaction
- Better than rigid command parsing
AI needs guardrails
- Without strict prompts, it hallucinates
- Citations force accountability
- Transparency builds trust
About Search:
Hybrid > Pure Vector
- Medical terms need exact matching (BM25)
- Concepts need semantic understanding (vectors)
- Combined: Best of both worlds
Context is king
- Less context can be better (top 5 vs all 50)
- Structure matters (clear sections, explicit format)
- Quality > quantity
Relevance scoring matters
- Take top N by score
- Don't include low-relevance results
- Noise dilutes signal
About Building Software:
Clean Architecture pays off
- Easy to add web support later
- Platform-specific code in one place
- Testing is straightforward
User experience matters
- Streaming > waiting for full response
- Clear loading states
- Good error messages
Privacy is a feature
- Run with your own API keys
- No data collection
- Users appreciate transparency
Demos matter
- Show, don't just tell
- Real examples resonate
- Video > screenshots > text
What's next for MediCortex
If this does well, I want to:
Short term:
- Add more medical specialties (pediatrics, dermatology, etc.)
- Implement paper caching for faster repeat queries
- Create comparison tables (visualize study differences)
- Mobile-responsive design improvements
Medium term:
- Auto-index new PubMed papers daily
- Multi-language support (translate papers)
- Clinical trials database integration
- Collaboration features (share research threads)
Long term:
- Image search for medical imaging papers
- Graph relationships between papers (citation networks)
- Fine-tune embeddings on medical text
- API for other apps to integrate
Dream features:
- Real-time literature monitoring
- Personalized research feeds
- Multi-modal search (text + images)
- Drug interaction checker
For LuminHacks Judges
Why MediCortex fits LuminHacks perfectly:
✅ Showcases AI Integration
- Google Gemini for generation
- Vertex AI for embeddings
- Custom RAG pipeline
- All working together seamlessly
✅ Solves Real Problems
- Medical misinformation is huge
- Research is inaccessible to most people
- AI hallucination is dangerous in healthcare
- Citations build trust
✅ Accessible to Everyone
- Plain language explanations
- Intuitive chat interface
- Works on any device
- Can run with your own API keys
✅ Privacy-First
- No data collection
- Your medical questions stay private
- Run locally with your credentials
- Open source (can audit the code)
✅ Technical Excellence
- Well-architected (Clean Architecture)
- Documented code
- Error handling
- Performance optimized
✅ Innovation
- Hybrid search is uncommon in this domain
- RAG with forced citations is unique
- Cross-platform Flutter implementation
- Scales to millions of documents
Try It Yourself
🎥 Demo Video (3 min): https://youtu.be/fvXn2LLjlIQ
💻 GitHub: https://github.com/mj-963/medicortex
🚀 Quick Start:
git clone https://github.com/mj-963/medicortex.git
cd medicortex
flutter pub get
# Add your API keys to env.json
flutter run -d chrome
🔑 Get Free API Keys:
- Gemini API (Free tier)
- Elasticsearch (14-day trial)
- Vertex AI ($300 credits)
Final Thoughts
Building MediCortex taught me that AI is only as trustworthy as its sources.
GPT can write beautiful prose, but if it's making up facts, what's the point? That's why I'm obsessed with citations. Every single claim in MediCortex links to a real research paper you can verify.
The future of AI isn't about replacing doctors or experts - it's about making expert knowledge accessible to everyone. MediCortex is a small step in that direction.
Thanks for checking this out! Whether I win or not, I learned a ton and built something I'm genuinely proud of.
Now if you'll excuse me, I need to go outside. I haven't seen the sun in two weeks. 😅
Built for LuminHacks 2025
Tech: Google Gemini | Vertex AI | Elasticsearch | Flutter | Dart
By: A developer who got tired of being scared by WebMD
Disclaimer: I'm not a doctor. This is a research tool, not medical advice. Always consult healthcare professionals for medical decisions.
P.S. If you're a judge reading this, thanks for making it to the end! Hope you enjoyed the demo. Feel free to test it with your own medical questions - the AI might surprise you with how well it works. And yes, every citation is real and clickable. Go ahead, verify one! 😊
Built With
- appwrite
- dart
- docker
- elasticsearch
- flutter
- google-gemini
- markdown
- material-design-3
- pubmed-api
- python
- rest-apis
- riverpod
- vertex-ai
Log in or sign up for Devpost to join the conversation.