diff --git a/server/src/analyze/prompt.rs b/server/src/analyze/prompt.rs index d8aa0ae..04b77c6 100644 --- a/server/src/analyze/prompt.rs +++ b/server/src/analyze/prompt.rs @@ -2,7 +2,7 @@ use super::AnalysisInput; /// System prompt for the consolidation LLM. From docs/projektplan.md:365-369. /// Temperature 0 is set on the request; the prompt enforces "no interpretation". -pub const SYSTEM_PROMPT: &str = "Du bist ein medizinischer Assistent. Fasse die folgenden diktierten Abschnitte zu einem zusammenhängenden Text zusammen. Bereinigen und strukturieren, nichts hinzufügen, keine medizinische Interpretation, keine Diagnose, keine Arztbrief-Struktur. Entferne Redundanzen. Spätere Aufnahmen haben Vorrang — Korrekturen, Nachträge und Widersprüche zugunsten der chronologisch letzten Aussage auflösen. Antworte auf Deutsch. Nur der zusammengefasste Text, keine Einleitung, kein Abschlusssatz.\n\nWICHTIG: Das Diktat wurde automatisch transkribiert und enthält typische ASR-Fehler — phonetisch ähnliche Verwechslungen (z.B. \"Corona\" statt \"Koronar\", \"Spolade\" statt \"Schokolade\", \"hochgradig in Faust\" statt \"hochgradig infaust\"), falsch geschriebene Medikamentennamen, zerschnittene Komposita. Korrigiere solche Fehler AKTIV: der Arzt hat das Original-Audio zur Verifikation und kann jede Korrektur gegenprüfen. Bei sicherer Korrektur: schreibe das korrigierte Wort. Bei Unsicherheit, wie die Korrektur lauten müsste: behalte das Original bei.\n\nMarkiere jede korrigierte oder zweifelhafte Stelle mit ==text== — das signalisiert dem Arzt \"bitte prüfen\". Markiere zusätzlich: unvollständige oder unsichere Angaben im Diktat (z.B. \"Dosis weiß ich nicht\", abgebrochene Sätze) sowie Zahlen ohne klaren Kontext oder Einheit. Sei sparsam — geläufige, klar gesprochene Fachbegriffe bleiben unmarkiert."; +pub const SYSTEM_PROMPT: &str = "Du bist ein medizinischer Assistent. Fasse die folgenden diktierten Abschnitte zu einem zusammenhängenden Text zusammen. Bereinigen und strukturieren, nichts hinzufügen, keine medizinische Interpretation, keine Diagnose, keine Arztbrief-Struktur. Entferne Redundanzen. Spätere Aufnahmen haben Vorrang — Korrekturen, Nachträge und Widersprüche zugunsten der chronologisch letzten Aussage auflösen. Antworte auf Deutsch. Nur der zusammengefasste Text, keine Einleitung, kein Abschlusssatz.\n\nWICHTIG: Das Diktat wurde automatisch transkribiert und enthält typische ASR-Fehler — phonetisch ähnliche Verwechslungen (z.B. \"Corona\" statt \"Koronar\", \"Spolade\" statt \"Schokolade\", \"hochgradig in Faust\" statt \"hochgradig infaust\"), falsch geschriebene Medikamentennamen, zerschnittene Komposita. Korrigiere solche Fehler AKTIV: der Arzt hat das Original-Audio zur Verifikation und kann jede Korrektur gegenprüfen. Bei sicherer Korrektur: schreibe das korrigierte Wort. Bei Unsicherheit, wie die Korrektur lauten müsste: behalte das Original bei.\n\nMarkiere jede korrigierte oder zweifelhafte Stelle mit ==text== — das signalisiert dem Arzt \"bitte prüfen\". Markiere zusätzlich: unvollständige oder unsichere Angaben im Diktat (z.B. \"Dosis weiß ich nicht\", abgebrochene Sätze) sowie Zahlen ohne klaren Kontext oder Einheit. Sei sparsam — geläufige, klar gesprochene Fachbegriffe bleiben unmarkiert.\n\nDer Text kann Hinweise der Form `Wort [?Kandidat]` enthalten — das sind automatische Vorschläge aus einem Fach-Wörterbuch (Medikamente, Anatomie, Wirkstoffe). Prüfe mit dem Satzkontext: passt der Kandidat, verwende ihn und markiere mit ==. Passt er nicht, behalte das Original-Wort unverändert und ohne Markierung. Die eckige-Klammer-Annotation selbst darf niemals im Output erscheinen."; /// Render the user-content portion of the chat request from the persisted /// JSON input. Recordings with blank text are skipped — they carry no signal diff --git a/server/src/analyze/worker.rs b/server/src/analyze/worker.rs index 7da5ef0..05c73d6 100644 --- a/server/src/analyze/worker.rs +++ b/server/src/analyze/worker.rs @@ -13,25 +13,22 @@ use crate::{BusyGuard, WorkerBusy}; /// Consume analyze jobs sequentially. The external LLM tolerates parallelism, /// but sequential processing keeps the architecture simple and shields the /// provider from bursts. Trivial to lift later via `tokio::spawn(process(..))`. -/// -/// `_vocab` is the gazetteer for proper-name correction. Currently unused; -/// wired into `process()` in a follow-up step. pub async fn run( mut rx: AnalyzeReceiver, config: Arc, client: reqwest::Client, worker_busy: WorkerBusy, - _vocab: Arc, + vocab: Arc, ) { info!( - vocab_entries = _vocab.len(), + vocab_entries = vocab.len(), "Analyze worker started" ); let timeout = Duration::from_secs(config.llm_timeout_seconds); while let Some(job) = rx.recv().await { let _guard = BusyGuard::new(worker_busy.clone()); - process(&job.case_dir, &config, &client, timeout).await; + process(&job.case_dir, &config, &client, &vocab, timeout).await; } warn!("Analyze worker stopped (channel closed)"); @@ -41,6 +38,7 @@ async fn process( case_dir: &Path, config: &Config, client: &reqwest::Client, + vocab: &Gazetteer, timeout: Duration, ) { let input_path = case_dir.join(ANALYSIS_INPUT_FILE); @@ -80,7 +78,12 @@ async fn process( return; } + // Render the raw transcript bundle, then inject gazetteer hints for + // tokens that phonetically resemble known proper names. The LLM sees + // annotations of the form `Token [?Candidate]` and decides in context + // whether to adopt them. let user_content = prompt::render_prompt(&input); + let user_content = vocab.annotate(&user_content); let total_chars = user_content.chars().count(); info!( case = %case_dir.display(),