Build · 3h 20m · ₹0
A forensic analytics platform that detects patterns of systemic neglect in civic complaints, scoring silence metrics (0–100) and pairing Qdrant semantic search with Gemini 2.5 Flash conversational chart generation.
What it does
The mechanics, data flow, and user interaction model behind SENTINEL.
Every complaint gets scored on a 0–100 'Silence Score' derived from days pending, ignored follow-ups, dismissed escalation attempts, and category-level historical neglect rates. Scores above 70 flag likely systematic silencing. On top of that, a conversational AI agent (Gemini 2.5 Flash, with semantic search over 10,000+ records via Qdrant and BAAI/bge-small-en-v1.5 embeddings) lets investigators ask natural-language questions ('which wards have the highest silence rates?') and get back both an evidence-grounded answer and an interactive Chart.js visualization, with session memory persisted directly in the vector store.
Technical Highlights
- Multi-dimensional bias scoring: demographic, ward-level geographic, category, and temporal decay combined into an interpretable 0–100 Silence Score
- Semantic search across 10,000+ complaint records using Qdrant vector database + BAAI/bge-small-en-v1.5 embeddings
- Conversational forensic agent powered by Gemini 2.5 Flash that synthesizes answers and generates dynamic Chart.js configurations on the fly
- Persistent multi-turn chat memory stored directly within vector collections without needing an external session cache
- 13-endpoint Flask REST API cleanly decoupling high-throughput analytical queries from conversational inference
Why it matters
The architectural judgment, practical engineering decisions, and core problems solved.
Institutional bias in complaint-handling is usually invisible precisely because it's diffuse — no single decision looks discriminatory, but aggregate patterns reveal severe demographic, geographic, and temporal disparities. A forensic tool that quantifies 'silence' as an objective, measurable score and lets non-technical oversight teams interrogate datasets conversationally rather than writing complex SQL transforms accountability workflows. Validated against synthetic ground truth (10,000+ records with injected bias distributions) before real-world deployment, ensuring the methodology is provably sound.
Civic tech & municipal ombudsman oversight for tracking equitable public service delivery across city wards
Consumer protection bureaus auditing complaint resolution delays by vendor category and socioeconomic tier
University & institutional grievance committees spotting systemic reporting bottlenecks and unaddressed escalations
Methodological baseline for auditing public algorithmic decision-making and administrative intake fairness
System architecture
End-to-end execution pipeline running across Gemini 2.5, Qdrant, Flask, Sentence-Transformers, Chart.js.
Calculates 0–100 Silence Score from days pending, missed escalations, and category neglect priors
Transforms complaint narratives and resolution logs into 384-dim dense semantic vectors
Performs cosine similarity search + demographic metadata payload filtering across 10,000+ records
Conversational forensic reasoning, evidence cross-referencing, and dynamic Chart.js JSON schema generation
13-endpoint analytical backend serving interactive forensic dashboards and visualization canvases
The path
Step-by-step implementation guide. Verbatim code snippets, configurations, and prompts.
Mathematical Formulation of the Silence Score Metric
Implement the core scoring algorithm weighting unresolved duration, escalation count, ignored messages, and category-level decay rates into a normalized 0–100 score.
Verbatim Code / Config
def calculate_silence_score(row: dict) -> float:
# Formula: w1*days_pending_norm + w2*escalations_norm + w3*ignored_replies_norm + w4*category_penalty
# Normalize to [0, 100]. Flag score >= 70.0 as 'HIGH_RISK_SYSTEMIC_SILENCE'Vectorizing Narratives with Qdrant and BGE Embeddings
Embed 10,000+ complaint records using fast Sentence-Transformers bge-small-en-v1.5 and index them into Qdrant collections with rich payload schemas for ward and demographic filters.
Verbatim Code / Config
qdrant.create_collection(
collection_name='civic_complaints',
vectors_config=VectorParams(size=384, distance=Distance.COSINE)
)
# Upload points with payload: {ward: int, category: str, silence_score: float, text: str}Gemini 2.5 Flash Chart Generation Tool Schema
Equip Gemini 2.5 Flash with structured output schemas to generate both natural-language forensic analysis and structured Chart.js configuration objects (type, labels, datasets, options).
Verbatim Code / Config
class ForensicResponse(BaseModel):
analysis: str
evidence_ids: list[str]
chart_type: Literal['bar', 'line', 'pie', 'none']
chart_config: Optional[dict] = None
# Force Gemini to return valid JSON conforming to ForensicResponseFlask Analytical API & Vector Session Memory
Build the 13-endpoint Flask backend handling faceted analytical filtering (demographic, geographic, temporal) and writing user conversation histories into Qdrant for semantic recall.
Verbatim Code / Config
@app.route('/api/chat', methods=['POST'])
def chat():
query = request.json.get('query')
history = qdrant.get_chat_history(session_id)
context = qdrant.similarity_search(query, filter={'silence_score': {'gte': 70}})
return jsonify(gemini_agent.query(query, context, history))Where it broke
The failure mode, root-cause breakdown, and resolution discovered during development.
The Tell
“The conversational agent frequently hallucinated ward-level totals by summing raw search samples rather than querying complete database aggregations.”
Why it failed
Semantic search retrieves top-k relevant complaints (e.g. k=20), which the LLM mistook for the global count across the entire ward, claiming 'Ward 4 has only 12 complaints total'.
The Fix
Separated macro statistical aggregation from semantic narrative retrieval. The agent now calls a deterministic 'get_ward_statistics()' tool for exact counts and percentages, using semantic retrieval strictly for qualitative context and evidence quotes.
What it cost
₹0 to build and run permanently within verified free tiers.
| Service / Tool | Cost | Free Tier Limits |
|---|---|---|
| Google Gemini 2.5 Flash | ₹0 | Free tier (15 RPM / 1M TPM) covers all conversational queries |
| Qdrant Cloud / Local | ₹0 | Free 1GB cluster / open-source local Docker container |
| BAAI/bge-small-en-v1.5 | ₹0 | Open-source Hugging Face embedding model runs locally on CPU |
| Flask & Chart.js | ₹0 | Open-source Python web server and JavaScript visualization library |
| Synthetic Data Generator | ₹0 | Custom Python script producing 10,000+ benchmark records |
Make it yours
Three concrete variations you can build and ship using this exact foundation.
- 01
Tenant Rights & Housing Violation Auditor: Analyzes municipal code enforcement records to detect systemic delays in low-income rental inspection requests.
- 02
Public Transit Reliability Investigator: Correlates bus cancellation rates and service alerts with neighborhood income and density metrics.
- 03
Hospital Patient Feedback Disparity Tracker: Analyzes healthcare service reviews and clinical grievance resolutions across language and insurance barriers.
Where next
Ready to ship SENTINEL?
Review the architecture, clone the prompt and implementation steps, and deploy your live URL for ₹0.