05 · Functionality
Two modes, three depths, one knowledge base.
The Streamlit UI exposes two top-level modes. Each call to the API enriches the query with the user's health profile before it reaches the retrieval pipeline.
Mode 1: Tanya (Ask)
General questions about traditional vs modern medicine. Three answer depths controlled by a segmented control in the sidebar:
Depth 1
Ringkas
2–3 sentences. For quick, accessible answers to general audience questions. No sources listed.
Depth 2
Detail
Full evidence-based answer. Explains the mechanism, notes caveats, suitable for someone who wants to understand, not just a verdict.
Depth 3
Sumber
Answer + explicit citations from BPOM, IDI, WHO, or Kemenkes. For users who need to verify the source themselves.
Mode 2: Cek Fakta (Fact-Check)
Paste any claim — from a WhatsApp forward, a health blog, or a product label — and the system returns a structured JSON verdict. The LLM is forced to produce a schema-constrained response (response_mime_type="application/json"), not free text.
FAKTA
Claim supported by retrieved documents from trusted sources. Summary + evidence-based explanation provided.
MITOS
Claim contradicted by the knowledge base. Common example: "herbal X cures cancer" — flagged against BPOM/IDI context.
PERLU BUKTI LEBIH LANJUT
Retrieved context insufficient to confirm or deny. Also used as the fallback verdict when the LLM quota is exhausted.
Personalization via profile context injection.
The Streamlit sidebar collects a health profile: gender, age, medical conditions (Diabetes, Hipertensi, Gagal Ginjal…), allergies (Jahe, Kunyit, Aspirin/NSAID…), and current medications (Warfarin, Metformin, Simvastatin…). Before each API call, build_profile_context() serializes this into a structured string prepended to the query.
Profile context injected before the question
[Profil pengguna: Demografi: Pria, 58 tahun | Kondisi kesehatan: Diabetes Tipe 2, Hipertensi | Alergi: Jahe | Obat rutin: Warfarin/Pengencer darah]
Apakah bawang putih bisa menggantikan statin untuk kolesterol?
The API is stateless — no profile is stored server-side. The profile context travels with the query, gets embedded alongside the question, and shapes both retrieval (the embedding now represents "a 58-year-old diabetic on warfarin asking about garlic") and generation (Gemini sees the conditions and adjusts accordingly). If no profile is set, the query is sent without context and the response is generic.
# chatbot.py — build_profile_context()
def build_profile_context(profile):
parts = []
demo = [x for x in [profile.get("gender"),
f"{profile['age']} tahun" if profile.get("age") else ""] if x]
if demo:
parts.append("Demografi: " + ", ".join(demo))
if profile.get("conditions"):
parts.append("Kondisi kesehatan: " + ", ".join(profile["conditions"]))
if profile.get("allergies"):
parts.append("Alergi: " + ", ".join(profile["allergies"]))
if profile.get("medications"):
parts.append("Obat rutin: " + ", ".join(profile["medications"]))
if not parts:
return ""
return "[Profil pengguna: " + " | ".join(parts) + "]\n\n"
# The enriched query is prepended before the API call
enriched = build_profile_context(profile) + user_input
resp = requests.post(f"{API_URL}/ask", json={"question": enriched, "mode": mode})
Streamlit
Session state
Profile context injection
Stateless API
Schema-constrained JSON
HealthTruth-AI · Mode: Tanya · Depth: Detail+Sumber · profil kesehatan di sidebar · dokumen referensi ditampilkan per jawaban