AGENT SKILL

openevidence-reference-architecture

Implement OpenEvidence reference architecture with best-practice project layout. Use when designing new clinical AI integrations, reviewing project structure, or establishing architecture standards for healthcare applications. Trigger with phrases like "openevidence architecture", "openevidence best practices", "openevidence project structure", "clinical ai architecture".

License 许可
MIT
最近更新
2026-11-15
Stars
★ 2.0k
Forks
233

01 前言 Front Matter

name
openevidence-reference-architecture
author
Jeremy Longshore <jeremy@intentsolutions.io>
license
MIT
version
1.0.0
description
Implement OpenEvidence reference architecture with best-practice project layout. Use when designing new clinical AI integrations, reviewing project structure, or establishing architecture standards for healthcare applications. Trigger with phrases like "openevidence architecture", "openevidence best practices", "openevidence project structure", "clinical ai architecture".
allowed-tools
Read, Grep

03 SKILL.md 正文

OpenEvidence Reference Architecture

Overview

Production-ready architecture patterns for OpenEvidence clinical AI integrations in healthcare environments.

Prerequisites

  • Understanding of layered architecture
  • OpenEvidence SDK knowledge
  • Healthcare compliance requirements
  • TypeScript/Node.js project setup

Project Structure

clinical-evidence-api/
├── src/
│   ├── openevidence/
│   │   ├── client.ts           # Singleton client wrapper
│   │   ├── config.ts           # Environment configuration
│   │   ├── types.ts            # TypeScript types
│   │   ├── errors.ts           # Custom error classes
│   │   ├── cache.ts            # Caching layer
│   │   └── handlers/
│   │       ├── webhooks.ts     # Webhook handlers
│   │       └── events.ts       # Event processing
│   ├── services/
│   │   └── clinical/
│   │       ├── index.ts        # Service facade
│   │       ├── query.ts        # Clinical query service
│   │       ├── deepconsult.ts  # DeepConsult service
│   │       └── drug-info.ts    # Drug information service
│   ├── api/
│   │   ├── routes/
│   │   │   ├── clinical.ts     # Clinical query endpoints
│   │   │   └── webhooks.ts     # Webhook endpoints
│   │   └── middleware/
│   │       ├── auth.ts         # Authentication
│   │       ├── audit.ts        # HIPAA audit logging
│   │       └── rate-limit.ts   # Rate limiting
│   ├── integrations/
│   │   ├── ehr/
│   │   │   ├── fhir.ts         # FHIR integration
│   │   │   ├── epic.ts         # Epic EHR hooks
│   │   │   └── cerner.ts       # Cerner integration
│   │   └── notifications/
│   │       ├── email.ts        # Email notifications
│   │       └── push.ts         # Push notifications
│   ├── compliance/
│   │   ├── hipaa/
│   │   │   ├── audit-log.ts    # HIPAA audit logging
│   │   │   ├── phi-handler.ts  # PHI sanitization
│   │   │   └── encryption.ts   # Data encryption
│   │   └── retention.ts        # Data retention policies
│   └── jobs/
│       ├── cleanup.ts          # Data cleanup job
│       └── reporting.ts        # Usage reporting job
├── tests/
│   ├── unit/
│   │   └── services/
│   ├── integration/
│   │   └── openevidence/
│   └── clinical-validation/
│       └── known-answers/
├── config/
│   ├── default.json
│   ├── development.json
│   ├── staging.json
│   └── production.json
├── docs/
│   ├── architecture.md
│   ├── runbook.md
│   └── api-reference.md
└── scripts/
    ├── migrate.sh
    └── deploy.sh

Layer Architecture

┌─────────────────────────────────────────────────────┐
│                  API Layer                           │
│   (Controllers, Routes, Webhooks, Middleware)        │
├─────────────────────────────────────────────────────┤
│                Service Layer                         │
│    (Business Logic, Orchestration, Validation)       │
├─────────────────────────────────────────────────────┤
│              OpenEvidence Layer                      │
│     (Client, Types, Caching, Error Handling)         │
├─────────────────────────────────────────────────────┤
│             Compliance Layer                         │
│       (HIPAA Audit, PHI Handling, Encryption)        │
├─────────────────────────────────────────────────────┤
│            Integration Layer                         │
│    (EHR/FHIR, Notifications, External Services)      │
├─────────────────────────────────────────────────────┤
│            Infrastructure Layer                      │
│      (Database, Cache, Queue, Monitoring)            │
└─────────────────────────────────────────────────────┘

Key Components

Step 1: Client Wrapper with Caching & Monitoring

// src/openevidence/client.ts
import { OpenEvidenceClient } from '@openevidence/sdk';
import { ClinicalQueryCache } from './cache';
import { MetricsCollector } from '../monitoring/metrics';
import { HIPAAAuditLogger } from '../compliance/hipaa/audit-log';

export interface OpenEvidenceServiceConfig {
  apiKey: string;
  orgId: string;
  baseUrl: string;
  timeout: number;
  cache: ClinicalQueryCache;
  metrics: MetricsCollector;
  auditLogger: HIPAAAuditLogger;
}

export class OpenEvidenceService {
  private client: OpenEvidenceClient;
  private cache: ClinicalQueryCache;
  private metrics: MetricsCollector;
  private auditLogger: HIPAAAuditLogger;

  constructor(config: OpenEvidenceServiceConfig) {
    this.client = new OpenEvidenceClient({
      apiKey: config.apiKey,
      orgId: config.orgId,
      baseUrl: config.baseUrl,
      timeout: config.timeout,
    });
    this.cache = config.cache;
    this.metrics = config.metrics;
    this.auditLogger = config.auditLogger;
  }

  async query(
    request: ClinicalQueryRequest,
    context: RequestContext
  ): Promise<ClinicalQueryResponse> {
    const timer = this.metrics.startTimer('clinical_query');

    try {
      // Check cache
      const cached = await this.cache.get(request.question, request.context);
      if (cached) {
        this.metrics.incrementCounter('cache_hit');
        timer.stop({ cached: 'true' });
        return cached;
      }
      this.metrics.incrementCounter('cache_miss');

      // Query API
      const response = await this.client.query(request);

      // Cache response
      await this.cache.set(request.question, request.context, response);

      // Audit log (without PHI)
      await this.auditLogger.logQuery(context.userId, context.userRole, response.id, true);

      timer.stop({ cached: 'false' });
      return response;
    } catch (error: any) {
      this.metrics.incrementCounter('query_error', { error: error.code });
      await this.auditLogger.logQuery(context.userId, context.userRole, null, false);
      timer.stop({ error: 'true' });
      throw error;
    }
  }

  async deepConsult(
    request: DeepConsultRequest,
    context: RequestContext
  ): Promise<string> {
    // Return consultId for async tracking
    const consultId = await this.client.deepConsult.create(request);
    await this.auditLogger.logDeepConsult(context.userId, context.userRole, consultId, true);
    return consultId;
  }

  async health(): Promise<HealthStatus> {
    try {
      const start = Date.now();
      await this.client.health.check();
      return {
        status: 'healthy',
        latencyMs: Date.now() - start,
      };
    } catch (error: any) {
      return { status: 'unhealthy', error: error.message };
    }
  }
}

Step 2: Service Facade

// src/services/clinical/index.ts
import { OpenEvidenceService } from '../../openevidence/client';
import { PHIHandler } from '../../compliance/hipaa/phi-handler';

export class ClinicalEvidenceService {
  constructor(
    private openEvidence: OpenEvidenceService,
    private phiHandler: PHIHandler
  ) {}

  async queryClinicalEvidence(
    question: string,
    patientContext: PatientContext | undefined,
    context: RequestContext
  ): Promise<FormattedClinicalAnswer> {
    // 1. Sanitize input (remove PHI)
    const sanitizedRequest = this.phiHandler.sanitizeQuery(question, patientContext);

    // 2. Query OpenEvidence
    const response = await this.openEvidence.query(sanitizedRequest, context);

    // 3. Format response for clinical use
    return this.formatClinicalAnswer(response);
  }

  async requestDeepConsult(
    question: string,
    options: DeepConsultOptions,
    context: RequestContext
  ): Promise<{ consultId: string; estimatedTime: number }> {
    const sanitizedQuestion = this.phiHandler.sanitizeText(question);

    const consultId = await this.openEvidence.deepConsult({
      question: sanitizedQuestion,
      context: {
        specialty: options.specialty,
        researchFocus: options.researchFocus,
        timeframe: options.timeframe,
      },
    }, context);

    return {
      consultId,
      estimatedTime: 180, // 3 minutes typical
    };
  }

  async checkDrugInteraction(
    drugs: string[],
    context: RequestContext
  ): Promise<DrugInteractionResult> {
    const question = `What are the drug interactions between ${drugs.join(' and ')}?`;

    const response = await this.openEvidence.query({
      question,
      context: {
        specialty: 'pharmacology',
        urgency: 'urgent',
      },
    }, context);

    return this.parseDrugInteractionResponse(response);
  }

  private formatClinicalAnswer(response: ClinicalQueryResponse): FormattedClinicalAnswer {
    return {
      summary: response.answer.split('.')[0] + '.',
      detailedAnswer: response.answer,
      citations: response.citations.map(c => ({
        source: c.source,
        title: c.title,
        year: c.year,
      })),
      confidence: {
        score: response.confidence,
        level: response.confidence > 0.9 ? 'high' : response.confidence > 0.7 ? 'moderate' : 'low',
      },
      disclaimer: 'For clinical decision support only. Verify with current guidelines.',
    };
  }

  private parseDrugInteractionResponse(response: ClinicalQueryResponse): DrugInteractionResult {
    // Parse response to structured format
    const answerLower = response.answer.toLowerCase();

    return {
      hasInteraction: !answerLower.includes('no significant interaction'),
      severity: this.determineSeverity(answerLower),
      details: response.answer,
      citations: response.citations,
    };
  }

  private determineSeverity(text: string): 'major' | 'moderate' | 'minor' | 'none' {
    if (text.includes('contraindicated') || text.includes('major')) return 'major';
    if (text.includes('moderate') || text.includes('caution')) return 'moderate';
    if (text.includes('minor')) return 'minor';
    return 'none';
  }
}

Step 3: EHR Integration Layer

// src/integrations/ehr/fhir.ts
import { ClinicalEvidenceService } from '../../services/clinical';

// HL7 FHIR CDS Hooks implementation
interface CDSHooksRequest {
  hook: string;
  hookInstance: string;
  context: {
    patientId: string;
    encounterId?: string;
    medications?: FHIRMedication[];
    conditions?: FHIRCondition[];
  };
  prefetch?: {
    patient?: FHIRPatient;
    medications?: FHIRBundle<FHIRMedication>;
  };
}

interface CDSHooksResponse {
  cards: CDSCard[];
}

export class FHIRIntegration {
  constructor(private clinicalService: ClinicalEvidenceService) {}

  async handleCDSHook(request: CDSHooksRequest): Promise<CDSHooksResponse> {
    switch (request.hook) {
      case 'medication-prescribe':
        return this.handleMedicationPrescribe(request);
      case 'order-sign':
        return this.handleOrderSign(request);
      default:
        return { cards: [] };
    }
  }

  private async handleMedicationPrescribe(
    request: CDSHooksRequest
  ): Promise<CDSHooksResponse> {
    const medications = request.context.medications?.map(
      m => m.medicationCodeableConcept?.text
    ).filter(Boolean) as string[];

    if (medications.length < 2) {
      return { cards: [] };
    }

    // Check for drug interactions
    const interaction = await this.clinicalService.checkDrugInteraction(
      medications,
      { userId: 'system', userRole: 'cds-hook' }
    );

    if (!interaction.hasInteraction) {
      return { cards: [] };
    }

    return {
      cards: [{
        uuid: crypto.randomUUID(),
        summary: `Drug Interaction Alert: ${medications.join(', ')}`,
        detail: interaction.details,
        indicator: interaction.severity === 'major' ? 'critical' : 'warning',
        source: {
          label: 'OpenEvidence Clinical Decision Support',
          url: 'https://openevidence.com',
        },
        suggestions: interaction.severity === 'major' ? [{
          label: 'Review alternatives',
          uuid: crypto.randomUUID(),
        }] : undefined,
      }],
    };
  }

  private async handleOrderSign(request: CDSHooksRequest): Promise<CDSHooksResponse> {
    // Similar implementation for order signing
    return { cards: [] };
  }
}

Data Flow Diagram

User/EHR Request
       │
       ▼
┌─────────────────┐
│   API Gateway   │
│  (Auth, Rate)   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐    ┌─────────────────┐
│  PHI Sanitizer  │───▶│   HIPAA Audit   │
└────────┬────────┘    └─────────────────┘
         │
         ▼
┌─────────────────┐    ┌─────────────────┐
│  Clinical       │───▶│   Cache Layer   │
│  Query Service  │    │   (Redis)       │
└────────┬────────┘    └────────┬────────┘
         │                      │
         ▼ (cache miss)         │
┌─────────────────┐             │
│  OpenEvidence   │◄────────────┘
│  API Client     │   (cache hit)
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  OpenEvidence   │
│  Cloud API      │
└─────────────────┘

Configuration Management

// src/config/index.ts
import convict from 'convict';

const config = convict({
  env: {
    doc: 'Application environment',
    format: ['development', 'staging', 'production'],
    default: 'development',
    env: 'NODE_ENV',
  },
  openevidence: {
    apiKey: {
      doc: 'OpenEvidence API key',
      format: String,
      default: '',
      env: 'OPENEVIDENCE_API_KEY',
      sensitive: true,
    },
    orgId: {
      doc: 'OpenEvidence organization ID',
      format: String,
      default: '',
      env: 'OPENEVIDENCE_ORG_ID',
    },
    baseUrl: {
      doc: 'OpenEvidence API base URL',
      format: 'url',
      default: 'https://api.openevidence.com',
      env: 'OPENEVIDENCE_BASE_URL',
    },
    timeout: {
      doc: 'Request timeout in milliseconds',
      format: 'int',
      default: 30000,
      env: 'OPENEVIDENCE_TIMEOUT',
    },
  },
  cache: {
    enabled: {
      doc: 'Enable caching',
      format: Boolean,
      default: true,
    },
    ttlSeconds: {
      doc: 'Default cache TTL',
      format: 'int',
      default: 3600,
    },
  },
  hipaa: {
    auditLogRetentionDays: {
      doc: 'HIPAA audit log retention',
      format: 'int',
      default: 2190, // 6 years
    },
  },
});

config.loadFile(`./config/${config.get('env')}.json`);
config.validate({ allowed: 'strict' });

export default config;

Output

  • Layered architecture with separation of concerns
  • HIPAA-compliant data handling
  • EHR integration ready
  • Comprehensive caching
  • Health checks and monitoring

Architecture Checklist

  • [ ] Layered architecture implemented
  • [ ] PHI sanitization in place
  • [ ] HIPAA audit logging
  • [ ] Caching strategy configured
  • [ ] EHR integration patterns
  • [ ] Health checks implemented
  • [ ] Configuration management
  • [ ] Error boundaries at each layer

Resources

Flagship Skills

For multi-environment setup, see openevidence-multi-env-setup.

04 同仓库更多 Skills

Dicklesworthstone
Dicklesworthstone/pi_agent_rust

web-artifacts-builder

Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.

前端开发
★ 2.0k
Dicklesworthstone
Dicklesworthstone/pi_agent_rust

webapp-testing

Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.

测试与质量前端开发
★ 2.0k
Dicklesworthstone
Dicklesworthstone/pi_agent_rust

neuropixels-analysis

Neuropixels neural recording analysis. Load SpikeGLX/OpenEphys data, preprocess, motion correction, Kilosort4 spike sorting, quality metrics, Allen/IBL curation, AI-assisted visual analysis, for Neuropixels 1.0/2.0 extracellular electrophysiology. Use when working with neural recordings, spike sorting, extracellular electrophysiology, or when the user mentions Neuropixels, SpikeGLX, Open Ephys, Kilosort, quality metrics, or unit curation.

★ 2.0k
Dicklesworthstone
Dicklesworthstone/pi_agent_rust

skill-creator

Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.

Skill与智能体开发
★ 2.0k
Dicklesworthstone
Dicklesworthstone/pi_agent_rust

notebooklm

Use this skill to query your Google NotebookLM notebooks directly from Claude Code for source-grounded, citation-backed answers from Gemini. Browser automation, library management, persistent auth. Drastically reduced hallucinations through document-only responses.

Skill与智能体开发
★ 2.0k
Dicklesworthstone
Dicklesworthstone/pi_agent_rust

slack-gif-creator

Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack."

邮件与沟通图像与多媒体
★ 2.0k