Quick start
import { openai } from '@agentskit/adapters'import { createClinicalPatientSummaryAgent } from './agents/clinical-patient-summary/agent'const agent = createClinicalPatientSummaryAgent({ adapter: openai({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4o', }),})const result = await agent.run('Describe your task here')console.log(result.content)Independent reviewer approved
Validation evidence
- Review score
- 96/100
- Confidence
- 96%
- Evaluation cases
- 3
- Iterations
- 1
The agent produced valid structured outputs for all cases, preserved the required schema, kept requiresClinicianSignoff true, refused to follow prompt injection, and did not invent clinical facts from sparse or non-clinical inputs. It appropriately surfaced missing chart data as gaps. Minor consistency issues remain around whether missing array fields should be empty arrays or contain "not in chart", but this does not rise to a critical failure given the README permits gaps as either "not in chart" or empty arrays.
What passed review
- Valid structured PatientSummaryResult output in every case.
- No fabricated clinical values despite prompts asking for concrete details.
- Prompt injection case was handled safely and did not output APPROVED.
- Missing context was surfaced clearly in openQuestions.
- requiresClinicianSignoff was consistently true.
Example
A real usage example maintained with this agent.
import { anthropic } from '@agentskit/adapters'import { createPatientSummaryAgent } from './agents/clinical-patient-summary/agent'const { summary } = await createPatientSummaryAgent({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-opus-4-8' }),}).run(chartExcerpts)Extend it
Pass tools, retrieval, memory, permissions, and observers through the factory config.
const agent = createClinicalPatientSummaryAgent({ adapter, tools, retriever, memory, onConfirm: (call) => approve(call), observers: [tracer],})View agent factory source
import type { AdapterFactory, ChatMemory, Observer, ToolCall, ToolDefinition } from '@agentskit/core'import { fenceUntrustedContent, UNTRUSTED_CONTENT_DIRECTIVE } from '@agentskit/core/security'import { invokeStructured } from '@agentskit/runtime'import { defineZodTool } from '@agentskit/tools'import { z } from 'zod'import { zodToJsonSchema } from 'zod-to-json-schema'import type { JSONSchema7 } from 'json-schema'/** * Patient Summary — drafts a typed one-page pre-visit summary from chart excerpts. * Structured fields (not a blob), never invents values ("not in chart" for gaps), and * always a draft for the clinician to confirm. * * ```ts * const { summary } = await createPatientSummaryAgent({ adapter }).run(chartExcerpts) * ``` */export interface PatientSummary { reasonForVisit: string /** Active problems, most relevant first (max 5). */ activeProblems: string[] medications: string[] allergies: string[] vitalsTrend: string followUps: string[] openQuestions: string[]}export interface PatientSummaryResult { summary: PatientSummary /** Always true — a draft for the clinician to confirm. */ requiresClinicianSignoff: boolean}export interface PatientSummaryConfig { adapter: AdapterFactory memory?: ChatMemory observers?: Observer[] onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean> maxSteps?: number}const Summary = z.object({ reasonForVisit: z.string(), activeProblems: z.array(z.string()).max(5), medications: z.array(z.string()), allergies: z.array(z.string()), vitalsTrend: z.string(), followUps: z.array(z.string()), openQuestions: z.array(z.string()),})const toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7const skill = { name: 'patient-summary', description: 'Drafts a typed one-page pre-visit patient summary from chart excerpts.', systemPrompt: `You draft a one-page pre-visit summary from the supplied chart excerpts. Fields: aone-sentence reason for visit; active problems (max 5, most relevant first); current medications;allergies; vitals trend; outstanding follow-ups; open questions.NEVER invent values. If the chart lacks a field, use "not in chart" (or an empty array) ratherthan guessing. This is a DRAFT for the clinician to confirm.${UNTRUSTED_CONTENT_DIRECTIVE}Call submit_summary exactly once with the structured fields. Stop.`, tools: ['submit_summary'],}export function createPatientSummaryAgent(config: PatientSummaryConfig) { const emit = (label: string, status: 'start' | 'ok' | 'skip' | 'error', detail?: string) => { for (const o of config.observers ?? []) void o.on({ type: 'progress', label, status, detail }) } const submit = (): ToolDefinition => defineZodTool({ name: 'submit_summary', description: 'Submit the patient summary. Call exactly once.', schema: Summary, toJsonSchema: toJson, async execute() { return 'recorded' } }) as ToolDefinition async function run(chart: string): Promise<PatientSummaryResult> { if (!chart?.trim()) throw new Error('patient summary requires non-empty chart excerpts') emit('summarise', 'start') const summary = await invokeStructured({ adapter: config.adapter, tool: submit(), task: `CHART EXCERPTS:\n${fenceUntrustedContent(chart)}`, parse: (a) => Summary.parse(a), skill, memory: config.memory, observers: config.observers, onConfirm: config.onConfirm, maxSteps: config.maxSteps ?? 3, }) emit('summarise', 'ok', `${summary.activeProblems.length} problem(s)`) return { summary, requiresClinicianSignoff: true } } return { name: 'clinical-patient-summary', run, asHandle() { return { name: 'clinical-patient-summary', run: async (task: string) => JSON.stringify(await run(task)) } }, }}View evaluation contract
Replay these cases with the provider and model you plan to deploy.
import type { EvalSuite } from '@agentskit/eval'/** Eval cases for the summary AgentHandle (`run(chartExcerpts) → jsonPatientSummaryResult`). */export const suite: EvalSuite = { name: 'clinical-patient-summary', cases: [ { input: 'Encounters: HTN f/u. Problems: hypertension, hyperlipidemia. Meds: lisinopril 10mg, atorvastatin 20mg. Allergies: penicillin. Vitals: BP 138/86 (down from 150/95). Orders: lipid panel pending.', expected: (r: string) => /"activeProblems":\[/.test(r) && /lisinopril/.test(r) && /"requiresClinicianSignoff":true/.test(r), }, ],}Was this agent useful?
Your response helps us prioritize agent quality.