Skip to content

Architecture Decision Records

This section holds the Architecture Decision Records (ADRs) for the ai-agent-eval-harness-healthtech reference implementation. Each ADR captures one architecturally significant decision that is hard or expensive to reverse and explains why it was made.

  • Format: MADR 4.0.0. Copy an existing ADR’s structure for any new decision; do not invent ad-hoc structures.
  • Status: Proposed -> Accepted. Each record states one decision in the present tense; the alternatives that were weighed and set aside are kept in that record’s own “Considered options” section.
  • File naming: ADR-NNNN-kebab-title, four-digit zero-padded sequence, lowercase kebab title.
  • Scope of an ADR: one decision per file.
  • Tone: technical English, no marketing copy, no emojis, no em-dashes. Every framework / vendor / version claim cites a primary source (release notes, official docs, FDA register, etc.).
IDTitleStatusOne-line summary
ADR-0001Orchestration frameworkAcceptedLangGraph 1.x over CrewAI, Microsoft Agent Framework, Claude Agent SDK, Pydantic AI, AutoGen. Six-node graph with an optional interrupt() HITL review_response node and a MemorySaver / AsyncPostgresSaver checkpointer factory.
ADR-0002LLM vendor abstractionAcceptedThin LLMClient Protocol over LangChain adapters plus a direct Groq path via an OpenAI-compatible REST client, selected by the LLM_PROVIDER environment variable.
ADR-0003Eval harnessAcceptedHand-rolled pytest core plus DeepEval and Promptfoo, with an Anthropic Claude Haiku LLM-as-judge for the rubric-scored dimensions and Phoenix for opt-in trace inspection.
ADR-0004RAG stackAcceptedChroma embedded primary vector store; Voyage voyage-3.5 primary embedder with a baked-in BAAI/bge-small-en-v1.5 local fallback using asymmetric instruction-aware retrieval; Qdrant Cloud documented as the managed-vector-DB alternative.
ADR-0005Guardrails and regulatory postureAcceptedScope classifier, refusal templates, and a deterministic, high-recall escalation router as first-class modules; design contract = FDA 2026 General Wellness / CDS Software guidance line.
ADR-0006Observability stackAcceptedOpenTelemetry + OpenInference wire format; Langfuse Cloud for the live demo, self-hosted Phoenix for eval runs, Pydantic Logfire documented as alternative.
ADR-0007Deployment targetAcceptedGoogle Cloud Run (container image, single-instance) as the production target, with Hugging Face Spaces retained as a secondary and local target. Deployment-resilience layer: per-session rate limiter, OpenAI -> Anthropic provider fallback, short-TTL response cache.
ADR-0008Code licenseAcceptedCode is licensed Apache 2.0.
ADR-0009Streaming the agent execution graph to the UIAcceptedThe Agent Execution Graph streams per-node events to the SPA over server-sent events, opt-in via Accept-header content negotiation; the /chat JSON contract is unchanged.
ADR-0010Data layer (Supabase for demo operational data)AcceptedSupabase managed Postgres for demo keys, interactions, improvement suggestions, demo-key requests, demo-key consents, demo sessions, and demo turn usage. RAG remains Chroma (ADR-0004).
ADR-0011Free-form out-of-domain detectionAcceptedThe deterministic scope classifier (regex + keyword) gains topic-aware out-of-domain detection: it admits in-domain turns, refuses high-confidence out-of-scope, and gives benign off-topic input a graceful nudge instead of a hard refusal. No LLM call (an LLM-based classifier was rejected on cost).
ADR-0012Corpus expansion strategyAcceptedAppend-to-existing strategy: new domains are added on top of the baseline corpus rather than replacing it. New KB cards and eval turns expand coverage across the medication-adherence domains; locale parity maintained across en / es-419 / pt-BR.
ADR-0013Voice extension (ElevenLabs TTS + STT)AcceptedElevenLabs eleven_multilingual_v2 for on-demand TTS with per-locale voice mapping and ElevenLabs Scribe for STT, delivered as a consent-gated, entitlement-limited surface rather than a universal control.
ADR-0014Cascading LLM provider fallbackAcceptedTyped transient-error cascade OpenAI -> Anthropic. Non-429 4xx is not retried (quota preservation). The answering provider is tagged in metadata for honest cost attribution.
ADR-0015Continuous Improvement Layer storageAcceptedInteraction logs and improvement suggestions are designed to co-locate in the same managed Postgres project (ADR-0010), PII-redacted at ingress, operator-curated and never auto-applied.
ADR-0016Free-tier deployment resilience layerAcceptedIn-process sliding-window rate limiter (proxy-aware IP) + TTL response cache, both bounded and memory-resident. No Redis, no external service. Single-worker design; multi-worker scale-out requires external state.
ADR-0017Voice off by default - safety policyAcceptedVoice is off by default and gated by explicit, server-recorded consent before any audio turn; the consent state is server-side, not client-only. Locale parity across en / es-419 / pt-BR.
ADR-0018Synthetic-only data invariant + exclusion listAccepted100% synthetic eval corpus from public-domain sources (MedlinePlus, DailyMed, WHO EML, FDA labels). Explicit exclusion list: MIMIC, ChatDoctor, MedDialog, n2c2/i2b2. PR burden of proof for any new dataset (license + provenance + compatibility).
ADR-0019Structured agent reply (Pydantic schema + LLM JSON mode)AcceptedThe agent emits a Pydantic-validated structured reply via LLM JSON mode instead of free prose, so refusal / escalation scorers read explicit fields rather than inferring intent from text.
ADR-0020Parent-document retrieval (sub-card chunking, card-level citation)AcceptedKB cards are chunked into sub-card passages for embedding/retrieval, then de-duplicated back to the parent card for citation, improving recall while keeping citations card-level.
ADR-0021Token streaming (LangGraph custom stream + streaming client)AcceptedPer-token LLM deltas stream to the SPA over the existing SSE surface (ADR-0009) when the active provider emits incremental output, so the assistant reply can render as it generates instead of after a full-response buffer.
ADR-0022Hybrid retrieval (BM25 + dense + RRF + cross-encoder rerank)AcceptedDense-only retrieval is replaced by a flag-gated three-stage pipeline - parallel lexical (BM25) + dense generators, reciprocal-rank fusion, then cross-encoder rerank - degrading gracefully to the prior dense path.
ADR-0023Retrieval recall measurement (recall@k / hit@k / nDCG@k)AcceptedRetrieval quality is measured directly with recall@k / hit@k / nDCG@k against labelled relevant cards, decoupling retrieval scoring from generation-coupled metrics like citation coverage.
ADR-0024Server-side citation enrichmentAcceptedThe Citation model gains optional source_url, source_license, and retrieved_score, enriched server-side in the closing node, so the SPA citation popover renders without a second KB round-trip.
ADR-0025Managed reranking provider and eval determinismAcceptedReranking offloads to a managed, off-box reranker resolved per turn (managed when a Voyage key is present, else local), degrading to fusion-only on any outage; the offline eval gate stays storeless, deterministic, and free.
ADR-0026Judge calibration design lockAcceptedThe human-vs-judge agreement gate freezes its framing, bins, threshold, and corpus before any case is labeled, measures a linear-weighted Cohen’s kappa per dimension (pooled gate, per-locale diagnostics), and permits only corpus rebalancing - never threshold tuning - as the fix for a kappa paradox.
ADR-0027Calibration corpus rebalanceAcceptedThe calibration corpus is composed for discrimination - borderline-weighted, cross-locale parallel, and defect-diverse - after an expert panel found a pole-saturated corpus yields a near-circular, uninformative kappa; this applies the design lock’s one open lever with every integrity constant frozen.
ADR-0028Faithfulness construct correctionAcceptedFaithfulness is re-specified as a contradiction-only construct - disentangled from groundedness, which owns unsupported additions - and re-calibrated by a blinded panel with the frozen constants untouched, so human and judge measure the same thing.
ADR-0029Faithfulness rubric scorerAcceptedThe production faithfulness scorer is replaced with a judge-rubric scorer that reads the full cited card and scores contradiction or violation of its explicit behavioral rules, so both sides of the calibration measure the same construct; the judge itself is unchanged.
ADR-0030Advisory-by-default calibration gateAcceptedThe wired calibration gate ships advisory by default - an unset corpus path skips green (logged), enforcement is an operator opt-in that fires and passes as proven - so an honest at-the-boundary dimension never becomes a permanently-red CI, with the frozen threshold and bins never a lever.
ADR-0031Escalation and self-harm gated calibrationAcceptedEscalation and self-harm join the human-vs-judge gate as hard launch-blockers - a relock gate that fails a missing, stale-for-the-active-chain, single-class, or below-threshold safety agreement - while the quality dimensions keep the advisory skip; no frozen constant moves and the judge is untouched.
ADR-0032Self-harm rubric construct correctionAcceptedThe self-harm rubric is re-specified as presence-gated - it first asks whether the turn carries a self-harm signal (construed broadly) and confers no credit on a benign turn or a non-self-harm emergency - mirroring the escalation absent-anchor and re-validated on a held-out set, with the human labels and frozen constants unchanged.
ADR-0033Launch-lane gate scoped to required dimensionsAcceptedIn the enforcing launch lane the gate hard-fails on the required safety dimensions only; the non-safety dimensions stay advisory-and-reported there, and the general gate is unchanged - so the launch verdict turns on the safety signal, with no threshold moved and no safety control weakened.
ADR-0034Negation-blind escalation floor, ratifiedAcceptedThe always-on deterministic emergency floor is intentionally negation-blind - a negated or hypothetical red flag still escalates (recall over precision) - and any change to it must re-pass a recall-parity gate and a judge-off floor proof, so a case can never be silently dropped.
ADR-0035Fail-safe escalation-judge inversionAcceptedThe semantic escalation judge is the one guardrail that fails toward escalation - every timeout, error, off-schema, or malformed outcome escalates - running behind the deterministic floor (OR-combined, never narrowing it) with a recall-biased prior, a bounded hard timeout and no inline retries, and empirical calibration deferred.
ADR-0036Bounded name-redaction policyAcceptedThe deterministic PII redactor covers structured identifiers only (email, phone, national IDs including RUT/CPF/DNI, payment card, MRN, date of birth, address, check-digit-validated) and intentionally excludes personal names on false-positive-rate and synthetic-only-data grounds; a config-gated active name pass is the named, deferred upgrade for a real-data deployment.