TabibuIO Hackathon Submission
Challenge: Agents That Do Real Work
License: Apache 2.0
Inspiration
Kenya has 47 counties and roughly 47 million people. Roughly 75% of them will visit a Level 1–3 facility first a dispensary or health center before anyone figures out they need a cardiologist, a nephrologist, or a dialysis machine. The problem isn't that specialists don't exist. It's that no one knows who's available, what equipment a facility actually has working that day, or whether a patient's NHIF policy covers the procedure they're being sent to.
A Community Health Volunteer in Homa Bay doing a home visit has no way to check in realtime whether Jaramogi Oginga Odinga Hospital has a working renal ultrasound. A patient in Garissa doesn't know whether the referral letter her CHW wrote will be honoured. A pharmacist in Nakuru doesn't know that the patient coming in with a prescription has NHIF coverage that should bring her copay to zero.
We've been watching this problem for a long time and the core failure isn't resources it's information that exists but isn't connected. Specialists update their availability. Devices go on maintenance. Insurance policies change. Drug stock fluctuates daily. None of these facts reach the people who need them at the moment they need them.
The Datahub hackathon gave us the technical foundation to actually fix this. Not with a dashboard that someone checks once a week. With agents that act on it in real time.
What it does
TabibuIO is a care navigation system for Kenya's public health network. A patient or Community Health Volunteer describes a medical concern in whatever language feels natural, be it Dholuo, Swahili, Kikuyu, Somali, Kalenjin, or plain English and the system takes it the rest of the way.
The full journey looks like this:
A patient sends a message: "Ayany rem e chuny, ok anyal yienyo maber" Dholuo for "I have chest pain, I can't breathe well." Within a few minutes:
- The Translation agent identifies the language and converts the message to English for downstream processing
- The Intake agent (Gemini 2.5 Flash) extracts the clinical picture NSTEMI risk, urgency level, and the kind of specialist needed
- The Referral & Booking agent checks which facilities within range have a working PCI machine, finds the first available cardiologist, and generates a secure referral code the patient can use to retrieve their care instructions at any time
- The Insurance agent pulls the patient's NHIF policy from Datahub and calculates the exact copay
- The Billing agent checks the quoted procedure price against the NHIF benchmark if the facility is charging 40% above the guideline, the patient gets an alert before they walk in
- After the appointment, the Treatment & Prescription agent records the procedure, creates the prescription, and emails a discharge summary to the referring CHW
- The Pharmacy agent finds the nearest pharmacy with the prescribed drug in stock and sends the patient directions
- The Inventory agent decrements the stock record and alerts the pharmacy manager if it drops below 10 units
- The Home Care agent schedules medication reminders and checkin calls, and responds to "maagizo <code>" messages with care instructions in the patient's own language (still under development)
Every one of those steps creates a real entity in Datahub. The patient's full care journey from first complaint to final followup is a lineage graph that any authorized person can inspect.
How we built it
The core architectural decision was to use Datahub not as a metadata catalog sitting alongside the application, but as the operational data store. There is no MongoDB. There is no secondary database. Every entity patientCareRequest, referral, appointment, treatmentRecord, prescription, dispensation, followUpTask, medicationStock, healthSpecialist, healthFacility, medicalDevice, pharmacy, priceBenchmark, insurancePolicy is a Datahub Dataset entity under the platform TabibuIO, with its fields stored as customProperties and its relationships expressed as real UpstreamLineage aspects.
This shapes the architecture in a fundamental way: agents never call each other. Each agent reads from Datahub, does its work, and writes back to Datahub. The Datahub Action Service (datahub_action_service.py) listens for entity change events via webhook and dispatches the appropriate Telegram message or email. This means the pipeline is fully decoupled any agent can fail and recover independently, and the full audit trail is always in Datahub.
The 9 agents run on Google ADK with Gemini 2.5 Flash can be switched with the latest models for better results. The orchestrator is a FastAPI application that manages the state machine lifecycle, the humanintheloop confirmation gate for highurgency referrals, and the Telegram bot relay. Clinical text extraction uses Gemini's multimodal API audio from CHV field visits, typed text from the web portal, or structured form submissions all go through the same intake pipeline.
We also built a full Model Context Protocol server (18 tools, at /mcp) so that any MCPcompatible assistant Claude Desktop, Cursor, Gemini CLI can query and write care data through standard MCP calls. The tools cover the full read/write surface: searching specialists by specialty and county, getting a patient's full journey lineage, booking referrals, recording dispensations, and checking NHIF benchmarks.
The frontend is Angular 20. The patient portal is conversational plain language, no clinical jargon, works on any phone with a browser. The clinician dashboard shows the swarm pipeline, live agent health, the Datahub lineage graph per entity type, and a humanintheloop gate card for approving REDtriage referrals before dispatch.
For data, we seeded 131 realistic Kenyan entities: 8 real hospitals (KNH, AGH, Coast General, JOOTRH, MTRH, Pumwani, Nakuru PGH, Kisii Level 5), 14 specialists across 7 specialties, 9 medical devices including a dialysis machine on maintenance and an MRI with a 6hour queue, 30 medication stock records, 16 NHIF price benchmarks, and 8 complete demo patient journeys spanning 6 languages and all urgency levels.
Challenges we ran into
The Datahub entity model for operational data. Datahub is designed as a metadata catalog where entities evolve slowly datasets are created, schemas are versioned, lineage is added. Using it as an operational store where entities are created and updated hundreds of times per day by autonomous agents required careful design. We store all mutable fields as customProperties (a Dict[str, str]), which means everything gets flattened to strings and nested structures need a naming convention. That meant a lot of extracted_triage_color keys where you'd expect extracted.triage_color. We learned to work with this consistently rather than fight it.
Datahub search latency after writes. After an agent writes an entity, the Elasticsearch index takes 30–60 seconds to catch up. For the seed scripts this is fine. For a live encounter being processed in real time, an agent that writes a referral entity and then immediately searches for it will get zero results. We solved this by routing reads back through the REST client (direct entity fetch by URN) rather than search when we know the URN, and only using fulltext search for queries where exact identifiers aren't available.
The AssertionRunEventClass schema change. When we added data quality assertions in seed_extended.py, they were accepted by the GMS API (HTTP 200) but 0 were persisted. It took some digging to find that AssertionRunEventClass gained a required assertionUrn field in acrylDatahub 1.6.x that wasn't in the older docs we were working from. Once we found it, the fix was one line but finding it cost a couple of hours.
The offline queue for CHV field visits. Community Health Volunteers often work in areas with intermittent connectivity. We built an inmemory offline queue with automatic sync on reconnect, but making the state machine handle a mix of live and replayed encounters without duplication required careful session ID management.
Google ADK subagent loading time. The first import of googleadk and googlegenai takes about 9 seconds per subagent. With 8 subagents loading at startup, the server would appear unresponsive for over a minute. We moved subagent loading to a background thread that runs concurrently with the first requests, and added a circuit breaker in the health check so the frontend doesn't flood the backend with 8 parallel agentstatus requests if the backend isn't up yet.
Accomplishments that we're proud of
The thing we're most proud of is that the Datahub lineage graph actually tells a patient's story. You can open a link, find Akoth's care request (REQDEMO001), and follow the lineage forward: request → referral to KNH cardiology → appointment with Dr. Odhiambo → treatment record for PCI angioplasty → prescription for aspirin + atorvastatin → dispensation at Nairobi Hospital Pharmacy → three followup tasks at days 1, 3, and 7. Every node is a real entity with real properties. The lineage isn't synthetic it was created by agents making real decisions.
Second, the multilingual intake actually works. We tested it with native speakers: a Dholuo message about a child with severe diarrhea produced a correct acute_watery_diarrhea extraction with YELLOW triage and the right followup schedule (days 2, 7, 14). A Somali message about a patient with three weeks of cough triggered correct TBsuspect flagging and MOH notification requirements. The language agent handles codeswitching too a message mixing Swahili and English in the same sentence processes correctly.
Third, the MCP server makes the system genuinely composable. We connected Claude Desktop to the local MCP endpoint during development and asked it to find a specialist in Kisumu with dialysis capability for a patient with CKD stage 4. Claude called search_facilities, found JOOTRH, called search_specialists to find the nephrologist, and returned a coherent care recommendation all from real Datahub data, no hallucination about what facilities or equipment exist.
What we learned
This being our first datahub project, we learned that Datahub's strength as an operational store is specifically its lineage model not the entity storage itself. A flat JSON database could hold the same data. What Datahub gives you that nothing else does is the ability to answer "how did this patient end up here?" with a single API call. Every downstream agent inherits context from everything upstream. When the Treatment agent records a procedure, it knows the referral that led to the appointment, the care request that triggered the referral, and the CHV who filed it. That chain of custody matters for healthcare.
We also learned that the hardest part of building an agentic system for healthcare isn't the AI it's the humanintheloop design. A 60second timeout on a referral confirmation gate is the right call for a rural CHV with a sick patient in front of them. Getting the gate to behave correctly under all the edge cases double submissions, network drops, timeout + confirmation race, session expiry took more iteration than the clinical extraction did.
What's next for TabibuIO
The most immediate thing is replacing the seeded specialist roster with real data from Kenya's Ministry of Health provider registry. The Kenya Health Information System (KHIS) publishes facility and provider data through an API feeding that into Datahub's healthSpecialist and healthFacility entities would make the specialist search actually useful to a real CHV.
After that: realtime device availability. Right now the medicalDevice entities are seeded with static operational status. Integrating with facility management systems to update operational_status in Datahub as equipment goes on maintenance would make the referral routing genuinely intelligent the system would stop sending patients to facilities where the only MRI has been offline for three days.
On the agent side, we want to add a Surveillance agent that watches the stream of patientCareRequest entities coming in and flags geographic clusters of the same syndrome the original problem TabibuIO was built to solve. With Datahub lineage, you can trace an outbreak cluster backwards: every patient in Homa Bay with acute_watery_diarrhea in the last 72 hours, their geographic coordinates, their CHW assignments, whether any of them share a water source. That's the analysis that saves lives in an outbreak.
Built With
- angular.js
- cloud-run
- datahub
- docker
- fastapi
- gemini
- google-adk
- google-cloud
- google-maps
- opentelemetry
- python
- typescript
Log in or sign up for Devpost to join the conversation.