Quick start
import { openai } from '@agentskit/adapters'import { createClinicalNoteSummariserAgent } from './agents/clinical-note-summariser/agent'const agent = createClinicalNoteSummariserAgent({ 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 three cases, preserved the clinical-note purpose, did not invent SOAP content from non-clinical inputs, surfaced missing sections, required clinician signoff, and resisted the explicit injection attempt. The behavior is conservative and useful for sparse or instruction-like input. Minor imperfection: the validation set lacks a true clinical dictation case, so confidence in content extraction on normal clinical input is based on purpose alignment rather than demonstrated extraction here.
What passed review
- Valid JSON-shaped NoteResult outputs with all required SOAP fields present.
- Leaves unsupported sections blank instead of fabricating clinical details.
- Always sets requiresClinicianSignoff to true.
- Flags uncertainty and missing fields clearly.
- Rejects prompt injection and treats it as untrusted input.
Example
A real usage example maintained with this agent.
import { anthropic } from '@agentskit/adapters'import { createNoteSummariserAgent } from './agents/clinical-note-summariser/agent'const { note, missingFields, requiresClinicianSignoff } = await createNoteSummariserAgent({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-opus-4-8' }),}).run(dictationText)Extend it
Pass tools, retrieval, memory, permissions, and observers through the factory config.
const agent = createClinicalNoteSummariserAgent({ 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'/** * SOAP Generator — turns clinician dictation into a typed SOAP note (Subjective, * Objective, Assessment, Plan). Structured output (each section addressable, not one * blob), missing sections surfaced (never silently blank), and ALWAYS a draft for * clinician sign-off. * * ```ts * const { note, missingFields } = await createNoteSummariserAgent({ adapter }).run(dictation) * ``` */export interface SoapNote { subjective: string objective: string assessment: string plan: string}export interface NoteResult { note: SoapNote /** SOAP sections the dictation didn't cover — for the clinician to fill, not invent. */ missingFields: string[] /** Always true — output is a draft, never a finalised record. */ requiresClinicianSignoff: boolean}export interface NoteSummariserConfig { adapter: AdapterFactory memory?: ChatMemory observers?: Observer[] onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean> maxSteps?: number}const Soap = z.object({ subjective: z.string(), objective: z.string(), assessment: z.string(), plan: z.string(), missingFields: z.array(z.string()).default([]),})const toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7const skill = { name: 'note-summariser', description: 'Converts clinician dictation into a typed SOAP note (draft for sign-off).', systemPrompt: `You convert clinician dictation into a SOAP note: Subjective, Objective, Assessment, Plan.Preserve clinical facts verbatim. Do NOT infer diagnoses the clinician did not state. Standardiseunits (mg, mL, bpm, mmHg). If a section is not covered in the dictation, leave it empty and addits name to missingFields — never invent content.${UNTRUSTED_CONTENT_DIRECTIVE}Call submit_note exactly once with { subjective, objective, assessment, plan, missingFields }. Stop.`, tools: ['submit_note'],}export function createNoteSummariserAgent(config: NoteSummariserConfig) { 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_note', description: 'Submit the SOAP note. Call exactly once.', schema: Soap, toJsonSchema: toJson, async execute() { return 'recorded' } }) as ToolDefinition async function run(dictation: string): Promise<NoteResult> { if (!dictation?.trim()) throw new Error('note summariser requires non-empty dictation') emit('summarise', 'start') const r = await invokeStructured({ adapter: config.adapter, tool: submit(), task: `CLINICIAN DICTATION:\n${fenceUntrustedContent(dictation)}`, parse: (a) => Soap.parse(a), skill, memory: config.memory, observers: config.observers, onConfirm: config.onConfirm, maxSteps: config.maxSteps ?? 3, }) emit('summarise', 'ok', `${r.missingFields.length} missing section(s)`) const { missingFields, ...note } = r return { note, missingFields, requiresClinicianSignoff: true } } return { name: 'clinical-note-summariser', run, asHandle() { return { name: 'clinical-note-summariser', 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 SOAP AgentHandle (`run(dictation) → jsonNoteResult`). */export const suite: EvalSuite = { name: 'clinical-note-summariser', cases: [ { input: 'Patient reports productive cough x3 days. Temp 38.1, lungs clear. Likely viral URI. Plan: rest, fluids, recheck if worse.', expected: (r: string) => /"assessment":/.test(r) && /"plan":/.test(r) && /"requiresClinicianSignoff":true/.test(r), }, { input: 'Patient says knee hurts after running.', expected: (r: string) => /"missingFields":\[/.test(r), }, ],}Was this agent useful?
Your response helps us prioritize agent quality.