Skip to main content

User Experience

The ZOL Intelligent Search presents two distinct user-experience surfaces. The patient-facing chatbot at /chat (and its admin-search variant /search) is the primary surface that 100 K monthly visitors interact with. The administrative surface — the Knowledge Base wizard at /knowledge-base, the Operations and Feedback dashboards, and the Value Dashboard — is what hospital staff use to keep that chatbot running. This page documents both, mapped one-to-one against the running frontend code so an examiner who opens the system sees the same labels and routes documented here.

Patient-Facing Chat (/chat)

The patient-facing entry point is PublicChatPage, deliberately named "public" because it requires no authentication: a hospital visitor lands on it directly from the website. The interface is a single-column conversation view designed for the diverse user base — from tech-savyy young adults to elderly patients who may be less comfortable with technology.

The page composes six interactive React components:

ComponentPurpose
MessageList + StreamingResponseRenders the conversation, including streamed-token rendering during generation.
SkeletonLoaderShows the five-stage Dutch progress labels while the backend works (no debug badges — see "Why no debug badges on /chat" below).
SourcesListRenders citations as clickable cards under each assistant turn.
FollowUpChipsThree context-aware follow-up suggestions appended after each answer.
ClarificationCardsDepartment selection cards when the query is classified as AMBIGUOUS_SYMPTOM_DESCRIPTION and ≥3 departments handle it (see Clarifying Questions).
AIDisclaimer + SessionFeedbackPrompt + NegativeFeedbackChipsMandatory medical-advice disclaimer plus a session-level "Was this useful?" prompt with negative-feedback follow-up.

WebSocket transport is handled by the usePublicWebSocket hook, which generates a UUID session ID and conversation ID, stores both in sessionStorage, and includes them on every message. The session ID maps to a deterministic anonymous user ID so the same browser tab always sees its own history; closing the tab discards both IDs (no PII is persisted client-side beyond the tab lifetime).

Why no debug badges on /chat

/chat and /search use SkeletonLoader, not the legacy PipelineProgress component. They render the five Dutch stage labels listed below and nothing else — no intent badges, no "Tier 2 model" annotations, no retrieval candidate counts. The badged debug surface only exists in the legacy QueryPage route (no sidebar entry) where showDebugInfo={true} is hard-coded. This avoids leaking system internals to public visitors and keeps the patient-facing UI free of jargon.

Multi-turn conversation example

Conversation memory for follow-up queries

Follow-up queries like "En op welke campus?" are meaningless without conversation history. The system maintains conversation memory across turns through several mechanisms:

LayerStorageScopePurpose
Session IDBrowser sessionStorageTab lifetimeAnonymous user identifier
Conversation IDBrowser sessionStorage + PostgreSQLTab lifetimeGroups messages into a thread
Message historyPostgreSQL (messages table)PersistentQ&A pairs with citations

When a follow-up arrives, the backend uses several mechanisms in cascade — primary LLM rewrite (Tier 2), nano fallback for ≤6-word follow-ups, citation-based topic enrichment as a final fallback, +25 % document-context boost on previously-cited sources, category-filter bypass, short-query heuristic for unknown-intent classifier outputs, and the full last-5-turn history is passed to the response LLM as user/assistant pairs. The full mechanism list is documented in Query Pipeline. Conversation-IR foundations follow @sacks1974turntaking for turn-taking and @gao2024ragsurvey for the conversational RAG pattern.

Real-time progress indicator

End-to-end response time has a measured median of 7,829 ms (thesis Chapter 4, Table 4.3 — full distribution P90 12,182 ms, P99 20,925 ms). Without feedback, users would face a blank screen for that duration — far above @nielsen1993responsetimes's 1-second flow threshold. The WebSocket-based progress indicator solves this by reporting pipeline stages in real time:

Pipeline Stages Shown to User

The labels below are the exact Dutch strings rendered by SkeletonLoader (sourced from frontend/src/i18n/nl.json:1477-1481). An examiner running the system in Dutch will see this exact text:

StageDutch LabelApprox. DurationWhat Happens
1"Uw vraag analyseren..."~700 msIntent classification + query rewriting
2"Relevante documenten zoeken..."~800 msVector + BM25 + taxonomy retrieval
3"Context verwerken..."~100 msMetadata boosting + ranking + Value Framework
4"Antwoord genereren..."~3,500 msLLM response generation (streamed)
5"Kwaliteit controleren..."~600 msFast quality gate

ADR-0009 documents the deliberate UX decision behind this five-stage indicator: user testing showed that progress stages transformed perceived wait time from "the system is broken" to "the system is working hard for me."

Source citations

Every response includes source citations — clickable links to the original hospital web pages or brochure PDFs. Citations serve four purposes:

  1. Verifiability: Users can confirm the information at the source.
  2. Trust: Visible sources increase confidence in the response.
  3. Navigation: Sources often contain additional relevant information.
  4. Transparency: Users understand that the system retrieves rather than generates knowledge (@gao2024ragsurvey).

Feedback widget

Each response includes a feedback widget with three interaction options:

  1. Thumbs up: Signals a helpful response. Logged for quality analytics.
  2. Thumbs down: Signals an unhelpful response. Triggers the "Think Harder" option.
  3. Think Harder: Initiates an escalated search with more candidates and reranking.

Admin Surface — route map

The administrative surface is structured around a sidebar with four sections (frontend/src/components/AppShell/navigation.ts). All routes below HospitalContextGuard require an authenticated user with a selected hospital tenant; platform routes additionally require OwnerRoute. Several routes were rebadged in May 2026 (the old /analytics is now /value; the deep operations page now sits at /analytics/system with the sidebar label "Operations"), so this map is the canonical name-to-route reference:

Sidebar sectionSidebar labelRouteComponentGuards
OverviewValue/valueValueDashboardPageHospitalContextGuard
OverviewDashboard/dashboardDashboardPageHospitalContextGuard
OverviewSearch/searchSearchPageHospitalContextGuard
ContentDocuments/documentsDocumentsPageHospitalContextGuard
ContentKnowledge Base/knowledge-baseTaxonomyPipelinePageHospitalContextGuard
AnalyticsOperations/analytics/systemAnalyticsHospitalContextGuard; Costs tab gated by isOwner
AnalyticsFeedback/feedbackFeedbackDashboardPageHospitalContextGuard
AdminPipeline/pipelinePipelineDebugPageHospitalContextGuard
AdminIngestion History/ingestion-historyIngestionHistoryPageHospitalContextGuard
AdminDiagnostics/diagnosticsDatabaseDiagnosticsPageHospitalContextGuard
AdminSettings/settingsSettingsPageHospitalContextGuard
Platform (owner-only)Platform Dashboard/platformPlatformDashboardPageOwnerRoute
Platform (owner-only)Platform Value/platform/valuePlatformValueDashboardPageOwnerRoute

Backwards-compatibility redirects: /analytics → /value, /taxonomy → /knowledge-base, /pipeline-debug → /pipeline (frontend/src/App.tsx:140-165).

The multi-tenant separation (HospitalContextGuard, tenant-scoped routes) follows the multi-tenant SaaS patterns surveyed in @bezemer2010multitenant.

Admin Experience: Knowledge Base Wizard (/knowledge-base)

The Knowledge Base wizard at /knowledge-base is the operator surface for ingesting a hospital's website, extracting taxonomy entities, reviewing relationships, and publishing the result to production. The component tree is TaxonomyPipelinePage → PipelineWizard, and the canonical stage list is the STAGE_ORDER constant in PipelineWizard.tsx:17:

const STAGE_ORDER: Stage[] = [
'setup', 'crawl', 'process', 'hub-pages',
'entity-table', 'relationships', 'merge-suggestions', 'graph', 'publish'
];

The sidebar (PipelineSidebar.tsx) groups these into six top-level numbered steps, with the four taxonomy-review sub-stages nested under step 5:

StepStage IDSidebar labelComponentWhat the operator does
1setup"1. Hospital Setup"SetupChecklistConfirm hospital metadata (name, hospital_id, timezone), entry sitemap, language hint. Pass-through if pre-seeded.
2crawl"2. Crawl"CrawlDashboardTrigger / monitor sitemap-driven URL discovery and HTTP fetch. Surfaces fail classes (timeout / 404 / empty content / boilerplate-only) per nightly-ingest spec.
3process"3. Process"ProcessDashboardDrive document → markdown → chunk → embed pipeline, applying the seven dedup layers (Content Deduplication).
4hub-pages"4. Hub Pages"HubCandidateListReview LLM-flagged hub pages (department index, doctor list) and accept / reject for entity extraction.
5entity-table"5. Taxonomy Review"EntityTableBrowse extracted entities (doctors, departments, conditions, treatments, campuses) with bulk-action controls.
5arelationships" Relationships"RelationshipBrowserReview WORKS_IN / TREATS / OFFERS / LOCATED_AT edges and patch low-confidence ones.
5bmerge-suggestions" Merge Suggestions"MergeSuggestionsApprove / reject fuzzy-dedup merge candidates surfaced by the SP-7 pipeline.
5cgraph" Graph View"GraphViewVisualise the taxonomy as a force-directed graph for sanity-checking the topology.
6publish"6. Publish"PublishPageCompare draft vs current production, review diff, atomically publish. Logs an immutable version row (VersionHistory).

The wizard's "navigation rail" at the top of every stage exposes Previous / Next buttons that walk STAGE_ORDER linearly; the sidebar allows skipping ahead once a stage's prerequisite has shipped. Stage status badges (○ not started, ● in progress, ✓ complete) are pulled from the getPipelineStatus(hospitalId) endpoint and refresh every 30 s (PipelineSidebar.tsx:50).

This nine-stage structure replaces a deleted six-step ingestion wizard that lived inside DocumentsPage until commit d6a5e0db. The new wizard expanded the post-ingest lifecycle (hub-page review, taxonomy review, merge suggestions, graph visualisation, atomic publish) that the older wizard collapsed into a single "Results & Quality" step. The route is gated by HospitalContextGuard only — any authenticated user with a selected hospital can drive it; backend endpoints enforce the admin-only checks (the frontend relies on backend 403s rather than role-gating the route).

Admin Experience: Value Dashboard (/value)

The Value Dashboard is the customer-ROI surface — it converts query traffic, voice deflection, and cost-model attribution into the executive answer to "is the system paying for itself?" The page composes:

ComponentPurpose
HeadlineTilesCalls handled (total / 24 h), minutes deflected, after-hours coverage, daily query volume — the four executive KPIs.
DailyVolumeTrendTime-series of daily queries pulled from /api/v1/admin/value/trend.
CostModelBannerPer-tenant cost attribution; opens CostModelEditModal for owner role.
Period / channel filtersSlice by 7 d / 30 d / 90 d, all-channel / chat-only / voice-only.

Data source is the nightly daily_tenant_metrics aggregation populated by an APScheduler job at 02:30 UTC. The /platform/value variant rolls the same metrics up across all tenants for the platform-owner view.

Admin Experience: Operations (/analytics/system)

The Operations page (sidebar label "Operations" — historically "Analytics") aggregates retrieval-health and system-health visualisations:

TabVisible toWhat it shows
OverviewAll adminsTop-level system health, recent activity, error rates.
CostsisOwner onlyLLM spend rollup, CategoryMismatchTrend, DiagnosticAccuracyTrend.
Feedback metricsAll adminsP95 latency comparison, Think Harder funnel, flag-for-review actions.

The Costs tab is conditionally rendered (Analytics.tsx:333) — non-owner admins do not see the LLM-spend or category-mismatch panels even though the page route itself is open to any admin under HospitalContextGuard. This matches the principle of least-privilege expectations of @iso27001_2022 for tenant administrators.

Admin Experience: Feedback Dashboard (/feedback)

The Feedback Dashboard is the closed-loop quality surface. It shows feedback telemetry (positive / negative / flagged ratios over time, with P95 latency comparisons by feedback class), surfaces individual flagged conversations, and triggers the v2 Diagnostic / Investigation flow:

Admin clicks "Investigate" →
FeedbackDashboardPage triggers analysis →
InvestigationViewV2 renders dimensional cards
(correctness / safety / memory / tool_use / latency) →
Operator clicks 👍 / ⚠ / 👎 →
InvestigationFeedback POSTs to
/api/v1/admin/voice-calls/{convId}/investigation/feedback →
DiagnosticAccuracyTrend picks up the vote in nightly aggregation

The dimensional rubric and 👍/⚠/👎 operator-rating loop align with the LLM-as-judge methodology of @zheng2023llmjudge and the RAG-evaluation framing of @es2023ragas. Audit-trail fields (original value, new value, admin user, timestamp, optional justification) on every override satisfy @iso27001_2022 audit-logging requirements.

Language and tone

The patient-facing interface operates in Dutch (Flemish). Following @nielsen1993responsetimes's usability heuristics, the system's language style is calibrated to be:

  • Professional: Appropriate for a hospital context.
  • Accessible: Avoiding excessive medical jargon in navigation responses.
  • Warm: Polite forms and empathetic phrasing.
  • Clear: Short sentences, structured responses with bullet points where appropriate.
  • Safe: Always including disclaimers and referrals to medical professionals.

The voice channel adds three additional output languages (English, French, Italian) with parallel safety-prompt coverage; see Voice Stack Compendium for the multi-language voice architecture.

User journey

Accessibility considerations

The chat interface follows web accessibility best practices:

  • Keyboard navigation: All interactive elements (input, send, feedback buttons, follow-up chips, clarification cards) are keyboard-accessible.
  • Screen reader compatibility: ARIA labels on dynamic content updates (streaming tokens, stage progression).
  • Color contrast: All text meets WCAG 2.1 AA contrast requirements (4.5:1 minimum).
  • Focus management: Focus moves to new responses as they appear.
  • Reduced motion: Respects prefers-reduced-motion for stage-progression animations.

References