Przejdź do głównej zawartości

SOAP Generation

Na tej stronie

SOAP Generation to kluczowa funkcja AI Suite - automatyczne tworzenie notatek medycznych SOAP z transkrypcji wizyty i kontekstu pacjenta.


sequenceDiagram
participant User
participant UI as Visit Editor
participant Hook as useSOAPGeneration
participant Client as UnifiedAIClient
participant BE as Backend
participant LLM as LLM Provider
User->>UI: Click "Generate SOAP"
UI->>Hook: generateSOAP(patient, visit, visitId)
Hook->>Hook: Build context
Note over Hook: Patient data<br/>Visit type<br/>Transcript<br/>Preferences
Hook->>Client: generateSOAP(context)
Client->>BE: unified_ai_generate_soap
BE->>BE: Build prompt
BE->>LLM: Send to LLM
LLM-->>BE: Generated SOAP
BE-->>Client: SOAP sections
Client-->>Hook: Parsed response
Hook->>UI: Update editedData.soap
UI->>UI: Auto-save

Plik: src/hooks/ai/useSOAPGeneration.ts

const {
isGenerating,
progress,
error,
generateSOAP,
cancelGeneration,
} = useSOAPGeneration();
// Generate SOAP
await generateSOAP(
patientData, // AINotesPatientData
visitData, // AINotesVisitData
visitId,
templateType?, // optional template
userId
);

interface AINotesPatientData {
name: string; // "Burek"
species: string; // "pies"
breed: string; // "owczarek niemiecki"
age: number; // 5
weight: number; // 32.5
owner: string; // "Jan Kowalski"
medicalHistory?: string[];
allergies?: string[];
}
interface AINotesVisitData {
visitType: string; // "consultation"
reasonForVisit: string; // "kuleje na prawą tylną łapę"
transcript: string; // Full transcript from recording
vetName: string; // "dr Nowak"
}

AI generuje 4 sekcje SOAP:

interface SOAPResponse {
subjective: string; // S - wywiad, objawy zgłaszane
objective: string; // O - badanie fizyczne
assessment: string; // A - diagnoza
plan: string; // P - zalecenia
}
S (Subjective):
Właściciel zgłasza, że pies kuleje na prawą tylną łapę od 2 dni.
Kulawizna nasiliła się po spacerze. Brak apetytu od wczoraj.
O (Objective):
Pacjent w stanie ogólnym dobrym. Temp. 38.5°C. Bolesność przy
palpacji prawego stawu kolanowego. Obrzęk w okolicy kolana.
Pozostałe stawy bez zmian.
A (Assessment):
Podejrzenie urazu więzadła krzyżowego przedniego prawego kolana.
Diagnostyka różnicowa: zapalenie stawu, uraz mięśni.
P (Plan):
1. RTG prawego kolana
2. Metacam 0.1mg/kg p.o. 1x dziennie przez 5 dni
3. Ograniczenie aktywności
4. Kontrola za 5 dni lub wcześniej przy pogorszeniu

flowchart TB
subgraph Input
Transcript[Audio Transcript]
Patient[Patient Data]
Preferences[User Preferences]
end
subgraph Processing
Context[Build Context]
Prompt[Generate Prompt]
LLM[LLM Call]
Parse[Parse Response]
end
subgraph Output
SOAP[SOAP Sections]
Save[Auto-save]
end
Transcript --> Context
Patient --> Context
Preferences --> Context
Context --> Prompt
Prompt --> LLM
LLM --> Parse
Parse --> SOAP
SOAP --> Save

SOAP generation respektuje preferencje użytkownika:

PreferenceEffect
note_style'soap' vs 'narrative' format
ai_precision_level'minimal' / 'balanced' / 'complete'
tone_of_voice'clinical' / 'professional' / 'friendly'
content_format'paragraph' vs 'bullets'

Różne typy wizyt mogą mieć różne prompty:

// Built-in templates
const TEMPLATE_TYPES = {
consultation: 'Standard consultation SOAP',
vaccination: 'Vaccination record format',
surgery: 'Surgical procedure notes',
followup: 'Follow-up visit format',
emergency: 'Emergency case notes',
};

useVisitSelection.ts
const generateSOAP = async () => {
const patientData = buildPatientData(selectedPatient);
const visitData = buildVisitData(editedData, transcript);
const result = await soapGeneration.generateSOAP(
patientData,
visitData,
selectedVisit.id,
undefined,
user.user_id
);
// Update edited data
setEditedData(prev => ({
...prev,
soap: {
subjective: result.subjective,
objective: result.objective,
assessment: result.assessment,
plan: result.plan,
}
}), { source: 'system' });
// Trigger auto-save after delay
setTimeout(() => saveVisit(true), 500);
};

Backend może zapisać surowe sugestie w visit.ai_sugestie:

// Parse AI suggestions into SOAP sections
function parseAiSuggestions(aiSugestie: string): ParsedSuggestions {
// Recognizes Polish keywords
const keywords = {
subjective: ['wywiad', 'anamneza', 'subjective'],
objective: ['badanie', 'examination', 'objective'],
assessment: ['rozpoznanie', 'diagnosis', 'assessment'],
plan: ['zalecenia', 'plan', 'treatment'],
};
// Parse and return { interview, exam, diagnosis, plan }
}

try {
await generateSOAP(patient, visit, visitId);
} catch (error) {
if (error.code === 'RATE_LIMIT') {
showToast('Zbyt wiele zapytań. Spróbuj ponownie za chwilę.');
} else if (error.code === 'CONTEXT_TOO_LONG') {
showToast('Transkrypcja jest zbyt długa. Skróć nagranie.');
} else {
showToast('Błąd generowania SOAP. Spróbuj ponownie.');
}
}

Podczas generowania wyświetlany jest toast z postępem:

// toastState in useVisitSelection
{
isVisible: true,
message: 'Generowanie SOAP...',
progress: 60, // 0-100
}