Steer-05: Matching Pipeline — Qdrant → Neo4j → PostgreSQL → LLM¶
Read first:
specs/sdd-mvp/06-matching-engine.md,specs/sdd-mvp/04-agent-pipeline.md,specs/sdd-mvp/03-data-architecture.md
Context & Goal¶
Implement the four-stage matching pipeline with pluggable strategy pattern, shadow mode, and A/B testing. Integrates with EHR Builder for patient data and event system for real-time progress updates.
Backend (curaway-ai/curaway-backend)¶
Step 1: Stage 0 — Qdrant Semantic Discovery¶
Create app/services/matching/qdrant_stage.py:
- Build patient clinical embedding: concatenate conditions + procedures + preferences as text
- Call Voyage AI to generate embedding
- Query Qdrant provider_profiles collection for top-K similar providers (K=30)
- Return candidate provider IDs with similarity scores
Step 2: Stage 1 — Neo4j Hard Constraint Filtering¶
Create app/services/matching/neo4j_stage.py:
- Execute Cypher query: find providers that have required procedure, relevant accreditation, active visa corridor for patient nationality
- Hard pass/fail — no partial scoring
- Return filtered candidate list
Step 3: Stage 2 — PostgreSQL Weighted Scoring¶
Update app/services/matching/weighted_scoring.py:
- Input: filtered candidates from Stage 1
- Load patient FHIR resources via EHR Builder
- Score each provider across 5 domains with configurable weights (from Flagsmith)
- Calculate confidence score based on data completeness
- Return ranked list with per-domain score breakdown
Step 4: Stage 3 — LLM Re-Ranking (Feature Flagged)¶
Update app/agents/match/agent.py:
- When agent_enhanced_matching flag enabled:
1. analyze_clinical_picture (Claude Sonnet): review all FHIR data, note comorbidity interactions
2. rerank_edge_cases (Claude Sonnet): review top 5, check contraindications
3. generate_explanations (Claude Haiku): natural language per provider in patient locale
- When disabled: skip to template explanations
Step 5: Pipeline Orchestration¶
Update app/services/matching_engine.py:
- execute() runs all 4 stages in sequence
- Emits match_progress events at each stage transition (for SSE)
- Stores results in match_results table
- Emits match_completed event
Step 6: Shadow Mode¶
Add shadow mode support:
- When matching_shadow_strategy flag is set, run shadow strategy in parallel
- Log shadow results to events table with shadow: true flag
- Only serve primary strategy results to patient
- Metabase dashboard compares primary vs shadow scores
Step 7: Tests¶
- Test each stage independently with mock data
- Test full pipeline end-to-end with Maria's profile (clean per-persona
account — see
docs/runbook/test-data-hygiene.md) - Test feature flag: disabled → stages 0+3 skipped
- Test shadow mode logs but doesn't serve
Backend Verification¶
python -m pytest tests/test_matching_pipeline.py -v
# Manual: trigger match for a clean persona, verify 4 stages execute.
# (#1194 E4): pass case_id so the matcher only sees this case's FHIR.
curl -X POST "http://localhost:8000/api/v1/patients/demo-patient-maria-001/match?case_id=<case-uuid>" \
-H "Authorization: Bearer $TOKEN" -H "X-Tenant-ID: tenant-curaway-patients"
Frontend (curaway-ai/curaway-frontend)¶
Step 1: Match Progress Indicator¶
Create src/components/match/MatchProgress.tsx:
- Shows in conversation thread when matching is active
- Stage indicators: Discovery → Filtering → Scoring → AI Analysis
- Each stage lights up as SSE match_progress events arrive
- Animated transitions between stages
Step 2: Provider Result Cards¶
Create src/components/match/ProviderMatchCard.tsx:
- Provider name, location, accreditations
- Total score with confidence indicator (opacity based on data completeness)
- Domain score breakdown (horizontal stacked bar or radar)
- Cost estimate with currency conversion
- Key differentiators (2–3 bullet points)
- AI-generated reasoning paragraph (from Explanation Agent)
- "Learn More" → navigates to provider detail page
Step 3: Match Results in Conversation¶
When match_completed SSE event arrives:
- Replace progress indicator with result cards
- Show top 3–5 providers as scrollable cards in conversation
- "See all results" expands to full list
- Follow-up prompt: "Would you like to know more about any of these providers?"
Frontend Verification¶
npm run build
npx tsc --noEmit
# Manual: trigger match, verify progress indicator → result cards transition
Checklist¶
- [ ] Alembic migration:
match_resultstable if not exists - [ ] Seed data: ensure 14+ TKR-capable providers with varied data completeness
- [ ] Error codes: MATCH_QDRANT_001, MATCH_NEO4J_001, MATCH_SCORING_001, MATCH_LLM_001
- [ ] Feature flags:
agent_enhanced_matching,matching_shadow_strategy - [ ] Env vars: QDRANT_URL, VOYAGE_AI_API_KEY, NEO4J_URI
- [ ] Swagger: match endpoint response schema documented
- [ ] Unit tests: per-stage tests + full pipeline
- [ ] PostHog:
match_triggered,match_result_viewed,provider_card_clicked - [ ] Loading states: match progress indicator
- [ ] Mobile: provider cards stack vertically, scrollable
- [ ] CLAUDE.md: update matching pipeline description
- [ ] Rollback: disable
agent_enhanced_matching→ pure deterministic scoring