← 全部示例
// 场景实例

罕见病信息分诊(来源隔离的证据审查 + 安全仲裁)

把五类证据保持隔离,暴露冲突与夸大论断,并产出受安全约束的信息分诊结果。

适合评估健康信息系统安全模式的团队

01 使用情境

患者自述、科普材料、指南、遗传证据和商业论断的可靠性完全不同。过早混合可能把微弱重叠变成虚假确定性,甚至导向商业推荐。

五类明确标记的 Mock 证据

虚构的患者症状、公益科普、指南类内容、基因—表型证据,以及网络或商业论断。

02 工作方式

一支分工明确、交接清楚的团队。

每个专职 Agent 只处理它需要的证据;互不依赖的工作同时进行,需要上游结果的工作则按顺序等待。

  1. 隔离审查每类证据

    五位审查者独立工作,让来源与质量始终可见。

    并行执行
    患者自述审查员

    整理自述症状,但不做诊断。

    科普审查员

    核对公益科普论断。

    指南审查员

    保留官方类指南中的宽泛鉴别范围。

    遗传证据审查员

    记录微弱或不确定的基因—表型证据。

    网络论断审查员

    标记确定性夸大和付费检测推广。

  2. 在安全约束下仲裁

    下游仲裁者接收结构化审查,而不是混合后的原始来源。

    安全仲裁者

    检测冲突,并禁止诊断、治疗、剂量或商业推荐。

03 最终结果

受安全边界约束的结构化证据分诊

结果传达不确定性与来源分歧,不会把材料转换成医疗建议。

  1. 01五份可追溯的证据审查
  2. 02冲突与夸大论断标记
  3. 03通过 schema 校验的安全结果
  • 来源隔离减少证据洗白。
  • 并行审查保证流程效率。
  • 独立仲裁者统一应用跨来源与安全规则。
范围与限制
全部 fixtures 都是 Mock,不含真实患者数据。该示例不得用于诊断、治疗、剂量或商业推荐。
04 开发者实现

先运行,再查看具体实现。

上面是面向业务的解释;下面的命令、运行前提、API 和源码继续与真实仓库同步。

实现信息 AgentToolExecutorToolRegistry
场景实例 509 行

运行

在仓库的克隆里运行这个文件:

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

OMA 与 provider 无关——这个示例按上面的 key 编写,但你也可以用 OpenAI、Gemini、Groq 等任意 provider 运行。 全部 provider →

展开完整同步源码 · 509 行

完整示例,从固定的 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.
在 GitHub 查看 / 编辑
// 企业服务

要把它用到生产环境?

open-multi-agent 采用 MIT 许可、可自行免费运行。当你需要在期限内交付、集成,或获得支持时,元定义科技(YuanASI)提供商业交付与支持。

// 直接联系

把 Open Multi-Agent 用进真实业务

联系框架作者本人,帮你梳理 AI 落地目标、让 AI 真正与业务结合

可提供的工程服务
S-01

AI Agent 定制开发

业务梳理、Agent 设计、Prompt 评估、生产部署、私有化与持续支持。

S-02

多智能体系统集成

多 Agent 架构编排、RAG、CRM / ERP / API 对接、性能与稳定性调优。

S-03

企业 AI 咨询

AI 场景评估、技术选型、POC、ROI 估算与落地路线规划。