← All examples
// Use-case Recipe

Rare Disease Information Triage (Source-Isolated Evidence Audit + Safety Arbitration)

Keep five evidence types separate, expose conflicts and overclaims, and produce a safety-constrained information triage result.

Useful forTeams evaluating safety patterns for health-information systems

Enterprise support
01 The scenario

Patient reports, educational material, guidelines, genetic evidence, and commercial claims have very different reliability. Blending them too early can turn weak overlap into false certainty or a commercial recommendation.

Five clearly marked mock evidence sources

Fictional patient-reported symptoms, nonprofit education, guideline-style content, gene–phenotype evidence, and web or commercial claims.

02 How it works

A team, with a clear handoff.

Each specialist sees the evidence it needs. Independent work runs together; dependent work waits for the right inputs.

  1. Audit each evidence class in isolation

    Five reviewers work independently so provenance and source quality stay visible.

    runs in parallel
    Patient-report reviewer

    Structures the reported symptoms without diagnosing.

    Education reviewer

    Checks nonprofit educational claims.

    Guideline reviewer

    Preserves the broader differential in official-style guidance.

    Genetic-evidence reviewer

    Records weak or uncertain gene–phenotype evidence.

    Web-claims reviewer

    Flags certainty inflation and paid-test promotion.

  2. Arbitrate with safety constraints

    The downstream arbiter receives structured reviews, not the raw blended sources.

    Safety arbiter

    Detects conflicts and forbids diagnosis, treatment, dosing, or commercial recommendation.

03 The result

A structured, safety-bounded evidence triage

The output communicates uncertainty and source disagreement without converting the material into medical advice.

  1. 01Five attributable evidence audits
  2. 02Conflict and overclaim flags
  3. 03Schema-validated safety-constrained result
  • Source isolation reduces evidence laundering.
  • Parallel audits keep the workflow practical.
  • A separate arbiter applies cross-source and safety rules consistently.
Scope and limits
All fixtures are mock and contain no real patient data. The example must not be used for diagnosis, treatment, dosing, or commercial recommendations.
04 For builders

Run it, then inspect the implementation.

The business view above is editorial. The commands, prerequisites, APIs, and source below stay synchronized with the real repository.

Implementation facts AgentToolExecutorToolRegistry
Use-case Recipe 509 lines

Run it

From a clone of the repo — this exact file:

terminal
npx tsx packages/core/examples/cookbook/rare-disease-information-triage.ts
Prerequisites
  • ANTHROPIC_API_KEY env var must be set.
  • Requires Node.js >= 18.

OMA is provider-agnostic — this example is written for the key above, but you can run it on OpenAI, Gemini, Groq and others. All providers →

Open the complete, synchronized source · 509 lines

The complete example, synchronized from the pinned Framework commit.

cookbook/rare-disease-information-triage.ts
/**
* Rare Disease Information Triage (Source-Isolated Evidence Audit + Safety Arbitration)
*
* Demonstrates:
* - Five source-isolated agents reading different MOCK fixtures
* - Source-isolated evidence audits over patient-reported symptoms, nonprofit education,
* official guideline/expert-consensus style content, gene-phenotype evidence,
* and web/forum/commercial claims
* - A downstream arbiter that receives only structured audit outputs
* - Runtime conflict detection:
* - nonprofit content may make symptom overlap look specific
* - official guidance keeps the differential broad
* - gene-phenotype evidence is weak/uncertain for the claimed disease
* - web/commercial content overstates certainty and promotes a paid test
* - safety policy forbids diagnosis, treatment, dosing, or commercial recommendation
* - Zod-validated structured output and simple runtime assertions
*
* Run:
* npx tsx packages/core/examples/cookbook/rare-disease-information-triage.ts
*
* Prerequisites:
* ANTHROPIC_API_KEY env var must be set.
* Requires Node.js >= 18.
*
* Fixtures:
* All fixtures under examples/fixtures/rare-disease-information-triage/ are MOCK.
* They are shaped like realistic source types but do not contain real patient data
* and do not provide medical diagnosis or treatment advice.
*/
 
import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
import { z } from 'zod'
import { Agent, ToolExecutor, ToolRegistry } from '../../src/index.js'
import type { AgentConfig, AgentRunResult } from '../../src/types.js'
 
// ---------------------------------------------------------------------------
// Fixture loading
// ---------------------------------------------------------------------------
 
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const fixtureRoot = path.join(__dirname, '../fixtures/rare-disease-information-triage')
 
function readFixture(name: string): string {
return readFileSync(path.join(fixtureRoot, name), 'utf-8')
}
 
const patientSymptomSummary = readFixture('patient-symptom-summary.json')
const nonprofitPatientEducation = readFixture('nonprofit-patient-education.md')
const officialGuidelineExcerpt = readFixture('official-guideline-excerpt.md')
const genePhenotypeEvidence = readFixture('gene-phenotype-evidence.json')
const webClaimsSnippets = readFixture('web-claims-snippets.json')
const medicalSafetyPolicy = readFixture('medical-safety-policy.json')
const MODEL = process.env['MODEL'] ?? 'claude-sonnet-4-6'
const ANTHROPIC_API_KEY = process.env['ANTHROPIC_API_KEY']
const ANTHROPIC_BASE_URL = process.env['ANTHROPIC_BASE_URL']
 
const providerConfig = {
provider: 'anthropic' as const,
model: MODEL,
apiKey: ANTHROPIC_API_KEY,
baseURL: ANTHROPIC_BASE_URL,
tools: [] as const,
}
 
// ---------------------------------------------------------------------------
// Zod schemas
// ---------------------------------------------------------------------------
 
const SymptomAudit = z.object({
source_is_mock: z.boolean(),
symptom_clusters: z.array(z.string()),
red_flags: z.array(z.string()),
claimed_condition_from_patient_context: z.string(),
missing_clinical_information: z.array(z.string()),
diagnosis_attempted: z.boolean(),
})
type SymptomAudit = z.infer<typeof SymptomAudit>
 
const NonprofitEducationAudit = z.object({
source_is_mock: z.boolean(),
educational_claims: z.array(z.string()),
simplification_risks: z.array(z.string()),
patient_friendly_but_incomplete_points: z.array(z.string()),
claims_that_may_overfocus_on_one_condition: z.array(z.string()),
})
type NonprofitEducationAudit = z.infer<typeof NonprofitEducationAudit>
 
const GuidelineAudit = z.object({
source_is_mock: z.boolean(),
candidate_disease_families: z.array(z.string()),
specialist_evaluation_required: z.boolean(),
reasons_symptoms_are_not_specific: z.array(z.string()),
recommended_non_diagnostic_next_steps: z.array(z.string()),
})
type GuidelineAudit = z.infer<typeof GuidelineAudit>
 
const GeneticsAudit = z.object({
source_is_mock: z.boolean(),
claimed_gene_or_marker: z.string(),
evidence_strength_for_claimed_disease: z.enum(['strong', 'moderate', 'weak', 'uncertain']),
phenotype_consistency: z.enum(['consistent', 'partially_consistent', 'inconsistent', 'uncertain']),
uncertainty_flags: z.array(z.string()),
})
type GeneticsAudit = z.infer<typeof GeneticsAudit>
 
const WebClaimsAudit = z.object({
source_is_mock: z.boolean(),
claims: z.array(z.object({
source_label: z.string(),
claim: z.string(),
claim_type: z.enum(['forum_experience', 'general_web_content', 'commercial_promotion']),
overstatement_risk: z.enum(['low', 'moderate', 'high']),
commercial_intent: z.boolean(),
unsupported_elements: z.array(z.string()),
})),
})
type WebClaimsAudit = z.infer<typeof WebClaimsAudit>
 
const SafetyAudit = z.object({
source_is_mock: z.boolean(),
prohibited_outputs: z.array(z.string()),
allowed_outputs: z.array(z.string()),
required_disclaimers: z.array(z.string()),
patient_facing_answer_allowed_without_clinician: z.boolean(),
})
type SafetyAudit = z.infer<typeof SafetyAudit>
 
const TriageDecision = z.object({
decision: z.enum(['credible_lead', 'conflicting_evidence', 'misleading_or_commercial', 'needs_specialist_review']),
confidence: z.enum(['low', 'medium', 'high']),
conflicts: z.array(z.string()),
missing_evidence: z.array(z.string()),
unsafe_elements: z.array(z.string()),
safe_next_steps: z.array(z.string()),
patient_facing_answer_allowed: z.boolean(),
diagnosis_provided: z.boolean(),
rationale: z.string(),
})
type TriageDecision = z.infer<typeof TriageDecision>
 
// ---------------------------------------------------------------------------
// Agent configs
// ---------------------------------------------------------------------------
 
const symptomNormalizerConfig: AgentConfig = {
name: 'symptom-normalizer',
...providerConfig,
systemPrompt: `You are a medical information triage assistant.
You audit only a MOCK patient-reported symptom summary. You must not diagnose.
Extract symptom clusters, red flags, the patient's suspected condition from context,
and missing clinical information.
Return JSON matching the provided schema. Output ONLY valid JSON, no markdown, no code fences, and no explanatory prose. Output ONLY valid JSON, no markdown, no code fences, and no explanatory prose. Do not provide diagnosis or treatment advice.`,
maxTurns: 1,
maxTokens: 1800,
temperature: 0.1,
outputSchema: SymptomAudit,
}
 
const nonprofitEducationConfig: AgentConfig = {
name: 'nonprofit-education-auditor',
...providerConfig,
systemPrompt: `You audit only MOCK nonprofit / patient-advocacy educational content.
Your job is to identify what the patient-friendly content says, what it simplifies,
and whether the simplification may make symptom overlap look more disease-specific
than it is. Do not use outside medical knowledge.
Return JSON matching the provided schema. Output ONLY valid JSON, no markdown, no code fences, and no explanatory prose.`,
maxTurns: 1,
maxTokens: 1800,
temperature: 0.1,
outputSchema: NonprofitEducationAudit,
}
 
const guidelineAuditorConfig: AgentConfig = {
name: 'guideline-auditor',
...providerConfig,
systemPrompt: `You audit only a MOCK official disease guideline or expert-consensus style excerpt.
Identify the broad differential, why symptoms are not specific, whether specialist
evaluation is required, and safe non-diagnostic next steps. Do not diagnose.
Return JSON matching the provided schema. Output ONLY valid JSON, no markdown, no code fences, and no explanatory prose.`,
maxTurns: 1,
maxTokens: 1800,
temperature: 0.1,
outputSchema: GuidelineAudit,
}
 
const geneticsAuditorConfig: AgentConfig = {
name: 'genetics-auditor',
...providerConfig,
systemPrompt: `You audit only MOCK gene-phenotype evidence.
Assess whether the claimed gene or marker strongly supports the claimed rare disease,
whether the phenotype is consistent, and what uncertainty flags exist. Do not diagnose
or interpret real patient genetics.
Return JSON matching the provided schema. Output ONLY valid JSON, no markdown, no code fences, and no explanatory prose.`,
maxTurns: 1,
maxTokens: 1800,
temperature: 0.1,
outputSchema: GeneticsAudit,
}
 
const webClaimsAuditorConfig: AgentConfig = {
name: 'web-claims-auditor',
...providerConfig,
systemPrompt: `You audit only MOCK web, forum, and commercial claim snippets.
Classify each claim as forum experience, general web content, or commercial promotion.
Flag overstatement risk, commercial intent, and unsupported elements. Do not validate
claims using outside knowledge.
Return JSON matching the provided schema. Output ONLY valid JSON, no markdown, no code fences, and no explanatory prose.`,
maxTurns: 1,
maxTokens: 2000,
temperature: 0.1,
outputSchema: WebClaimsAudit,
}
 
const safetyBoundaryConfig: AgentConfig = {
name: 'safety-boundary-agent',
...providerConfig,
systemPrompt: `You audit only a MOCK medical safety policy.
Extract prohibited outputs, allowed outputs, required disclaimers, and whether a
patient-facing answer is allowed without a clinician. Do not generate medical advice.
Return JSON matching the provided schema. Output ONLY valid JSON, no markdown, no code fences, and no explanatory prose.`,
maxTurns: 1,
maxTokens: 1800,
temperature: 0.1,
outputSchema: SafetyAudit,
}
 
const arbiterConfig: AgentConfig = {
name: 'rare-disease-triage-arbiter',
...providerConfig,
systemPrompt: `You are the downstream arbiter for a rare disease information triage workflow.
You receive only structured outputs from source-isolated audits. You cannot access
the original fixtures. Your tasks:
1. Detect conflicts among the audits.
2. Identify misleading or commercial overclaims.
3. Enforce the safety boundary.
4. Decide whether the information is a credible lead, conflicting evidence,
misleading/commercial content, or needs specialist review.
5. Never provide diagnosis, treatment advice, dosing, or commercial recommendation.
Return JSON matching the TriageDecision schema. The seeded conflict should be explicit:
web/commercial content maps symptom similarity to one rare disease, while the guideline
keeps the differential broad and genetics evidence is weak or uncertain.`,
maxTurns: 1,
maxTokens: 3000,
temperature: 0.1,
outputSchema: TriageDecision,
}
 
// ---------------------------------------------------------------------------
// Build agents
// ---------------------------------------------------------------------------
 
function buildAgent(config: AgentConfig): Agent {
// Source isolation is enforced at runtime: fixtures are injected into prompts,
// and audit agents receive an empty tool registry with tools: [].
const registry = new ToolRegistry()
const executor = new ToolExecutor(registry)
return new Agent(config, registry, executor)
}
 
const symptomNormalizer = buildAgent(symptomNormalizerConfig)
const nonprofitEducationAuditor = buildAgent(nonprofitEducationConfig)
const guidelineAuditor = buildAgent(guidelineAuditorConfig)
const geneticsAuditor = buildAgent(geneticsAuditorConfig)
const webClaimsAuditor = buildAgent(webClaimsAuditorConfig)
const safetyBoundaryAgent = buildAgent(safetyBoundaryConfig)
const triageArbiter = buildAgent(arbiterConfig)
 
// ---------------------------------------------------------------------------
// Main execution
// ---------------------------------------------------------------------------
 
console.log('Rare Disease Information Triage - Source-Isolated Audit + Safety Arbitration')
console.log('='.repeat(88))
console.log(`Model backend: anthropic-compatible (${MODEL})`)
console.log('All fixtures are MOCK and contain no real patient data.')
console.log('Expected runtime conflict: simplified patient-facing content + commercial claim vs broad guideline differential + weak genetics evidence.\n')
 
async function runTimed(
name: string,
agent: Agent,
prompt: string,
): Promise<{ name: string, result: AgentRunResult, elapsedMs: number }> {
const start = performance.now()
const maxAttempts = 3
 
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
console.log(` [RUN] ${name} started... attempt ${attempt}/${maxAttempts}`)
const result = await agent.run(prompt)
const elapsedMs = performance.now() - start
 
if (result.success && result.structured) {
console.log(` [DONE] ${name} finished in ${Math.round(elapsedMs)}ms`)
return { name, result, elapsedMs }
}
 
const outputPreview = result.output ? result.output.slice(0, 240).replace(/\s+/g, ' ') : '<empty output>'
console.warn(` [RETRY] ${name} failed structured output on attempt ${attempt}/${maxAttempts}: ${outputPreview}`)
}
 
const result = await agent.run(prompt)
const elapsedMs = performance.now() - start
console.log(` [DONE] ${name} finished in ${Math.round(elapsedMs)}ms`)
return { name, result, elapsedMs }
}
 
function requireStructured<T>(name: string, result: AgentRunResult): T {
if (!result.success) {
console.error(`${name} failed: ${result.output}`)
process.exit(1)
}
 
if (!result.structured) {
console.error(`${name} did not return structured output`)
process.exit(1)
}
 
return result.structured as T
}
 
console.log('[Phase 1] Source-isolated audits\n')
 
const auditStart = performance.now()
const auditRuns: Array<{ name: string, result: AgentRunResult, elapsedMs: number }> = []
 
auditRuns.push(await runTimed(
'symptom-normalizer',
symptomNormalizer,
`Audit this MOCK patient-reported symptom summary. Use only this input.\n\n${patientSymptomSummary}`,
))
 
auditRuns.push(await runTimed(
'nonprofit-education-auditor',
nonprofitEducationAuditor,
`Audit this MOCK nonprofit / patient-advocacy educational content. Use only this input.\n\n${nonprofitPatientEducation}`,
))
 
auditRuns.push(await runTimed(
'guideline-auditor',
guidelineAuditor,
`Audit this MOCK official guideline / expert-consensus style excerpt. Use only this input.\n\n${officialGuidelineExcerpt}`,
))
 
auditRuns.push(await runTimed(
'genetics-auditor',
geneticsAuditor,
`Audit this MOCK gene-phenotype evidence. Use only this input.\n\n${genePhenotypeEvidence}`,
))
 
auditRuns.push(await runTimed(
'web-claims-auditor',
webClaimsAuditor,
`Audit these MOCK web/forum/commercial claim snippets. Use only this input.\n\n${webClaimsSnippets}`,
))
 
auditRuns.push(await runTimed(
'safety-boundary-agent',
safetyBoundaryAgent,
`Audit this MOCK medical safety policy. Use only this input.\n\n${medicalSafetyPolicy}`,
))
const auditElapsed = performance.now() - auditStart
 
const auditMap = new Map(auditRuns.map((run) => [run.name, run.result]))
 
const symptomAudit = requireStructured<SymptomAudit>('symptom-normalizer', auditMap.get('symptom-normalizer')!)
const nonprofitAudit = requireStructured<NonprofitEducationAudit>('nonprofit-education-auditor', auditMap.get('nonprofit-education-auditor')!)
const guidelineAudit = requireStructured<GuidelineAudit>('guideline-auditor', auditMap.get('guideline-auditor')!)
const geneticsAudit = requireStructured<GeneticsAudit>('genetics-auditor', auditMap.get('genetics-auditor')!)
const webClaimsAudit = requireStructured<WebClaimsAudit>('web-claims-auditor', auditMap.get('web-claims-auditor')!)
const safetyAudit = requireStructured<SafetyAudit>('safety-boundary-agent', auditMap.get('safety-boundary-agent')!)
 
console.log('\n[Phase 1 Summary]')
for (const run of auditRuns) {
const status = run.result.success ? 'OK' : 'FAILED'
console.log(` ${run.name.padEnd(30)} [${status}] ${Math.round(run.elapsedMs)}ms, ${run.result.tokenUsage.output_tokens} out tokens`)
}
console.log(` Audit wall time: ${Math.round(auditElapsed)}ms\n`)
 
console.log('[Phase 2] Downstream arbitration over structured audit outputs only\n')
 
const arbiterPrompt = `You are given structured outputs from six source-isolated audits.
You cannot access the original fixtures. Decide whether this case is a credible lead,
conflicting evidence, misleading/commercial, or needs specialist review.
SYMPTOM AUDIT:
${JSON.stringify(symptomAudit, null, 2)}
NONPROFIT EDUCATION AUDIT:
${JSON.stringify(nonprofitAudit, null, 2)}
OFFICIAL GUIDELINE / EXPERT-CONSENSUS AUDIT:
${JSON.stringify(guidelineAudit, null, 2)}
GENE-PHENOTYPE AUDIT:
${JSON.stringify(geneticsAudit, null, 2)}
WEB / FORUM / COMMERCIAL CLAIMS AUDIT:
${JSON.stringify(webClaimsAudit, null, 2)}
SAFETY BOUNDARY AUDIT:
${JSON.stringify(safetyAudit, null, 2)}
Rules:
- Do not produce a diagnosis.
- Do not recommend a treatment, dose, paid test, or commercial product.
- Surface the conflict between over-specific web/commercial claims and broader official/gene evidence.
- Return one JSON object matching the schema, no markdown.`
 
const arbiterStart = performance.now()
const arbiterResult = await triageArbiter.run(arbiterPrompt)
const arbiterElapsed = performance.now() - arbiterStart
 
const decision = requireStructured<TriageDecision>('rare-disease-triage-arbiter', arbiterResult)
 
console.log(` Arbiter [OK] ${Math.round(arbiterElapsed)}ms, ${arbiterResult.tokenUsage.output_tokens} out tokens\n`)
 
// ---------------------------------------------------------------------------
// Runtime assertions
// ---------------------------------------------------------------------------
 
const allowedConflictDecisions: TriageDecision['decision'][] = [
'conflicting_evidence',
'misleading_or_commercial',
'needs_specialist_review',
]
 
const asserts = [
{
name: 'decision should reflect conflict/safety concern',
pass: allowedConflictDecisions.includes(decision.decision),
},
{
name: 'patient-facing diagnosis should not be allowed',
pass: decision.patient_facing_answer_allowed === false,
},
{
name: 'diagnosis should not be provided',
pass: decision.diagnosis_provided === false,
},
{
name: 'conflicts should be surfaced',
pass: decision.conflicts.length > 0,
},
{
name: 'unsafe elements should be surfaced',
pass: decision.unsafe_elements.length > 0,
},
]
 
console.log('='.repeat(88))
console.log('RARE DISEASE INFORMATION TRIAGE DECISION')
console.log('='.repeat(88))
console.log(JSON.stringify(decision, null, 2))
console.log()
 
console.log('## Runtime Assertions\n')
let hasFailure = false
for (const assertion of asserts) {
console.log(`- ${assertion.pass ? 'PASS' : 'FAIL'}: ${assertion.name}`)
if (!assertion.pass) hasFailure = true
}
 
const totalOutputTokens =
auditRuns.reduce((sum, run) => sum + run.result.tokenUsage.output_tokens, 0) +
arbiterResult.tokenUsage.output_tokens
 
const totalInputTokens =
auditRuns.reduce((sum, run) => sum + run.result.tokenUsage.input_tokens, 0) +
arbiterResult.tokenUsage.input_tokens
 
console.log('\n## Token Usage\n')
console.log(`Input tokens: ${totalInputTokens}`)
console.log(`Output tokens: ${totalOutputTokens}`)
console.log('Estimated cost depends on the selected provider/model; this example prints token usage for transparency.')
 
console.log('\n' + '='.repeat(88))
 
if (hasFailure) {
console.error('Runtime assertion failed.')
process.exit(1)
}
 
console.log('Rare disease information triage example complete.\n')
 
// ---------------------------------------------------------------------------
// Production source adapter sketch
// ---------------------------------------------------------------------------
//
// This example intentionally uses committed MOCK fixtures so the demo is
// deterministic, safe, and runnable. A production version could replace fixtures
// with source adapters for patient intake forms, patient-advocacy education,
// official guideline repositories, gene-phenotype databases, web-claim crawlers,
// and an internally approved medical safety policy service. The source-isolated
// audit + downstream arbitration shape would remain the same.
View & edit on GitHub
// Enterprise

Taking this to production?

open-multi-agent is MIT-licensed and free to run yourself. When you need it delivered, integrated, or supported on a deadline, 元定义科技 (YuanASI) offers commercial delivery and support.

Enterprise support