fix(voice): hear "یادداشت" as a note request, and stop the note at the next instruction

Two reports from live dictation in fa.

The locale trigger vocabulary listed only verbs that ask for a note ("بنویس",
"یادداشت کن"). Naming the field itself — "توی یادداشت‌ها بنویس", "در توضیحات" —
reported no commentTrigger, and the resolver discards a comment without one, so
the note was silently dropped. Added the field-name shape to fa, en and nl.

Where the dictated note ends was never stated, so the model answered it
inconsistently: "توی توضیحات بنویس سلام چطوری و بفرست برای لابراتوآر سانشاین"
sometimes put the lab dispatch inside the comment. Added a no-overlap rule — a
clause reported as a lab, tooth, prosthesis or deadline is an instruction and
must not appear in comment as well — with that sentence as a worked example.

Both rules go in the prompt and in the JSON-schema field descriptions, because
the schema description is what the model reads while filling the field. No code
enforcement for the second: stripping an extracted clause out of free-speech
note text by string matching is guesswork, and the review sheet shows the note
before it is saved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-11 19:33:49 +08:00
parent b71b4e9ca9
commit 78fd3eb092
4 changed files with 61 additions and 8 deletions

View File

@@ -10,14 +10,17 @@ const LOCALE_NOTES: Record<string, string> = {
'"شش بالا راست" = upper right six -> arch "upper", side "patient_right", position 6.',
'A jaw is spoken as "فک بالا" (upper jaw) or "فک پایین" (lower jaw), sometimes just',
'"بالا"/"پایین" in context, or "هر دو فک" (both jaws).',
'A note is asked for with "بنویس", "یادداشت کن", "این را یادداشت کن", "بنویس که",',
'"در توضیحات بنویس". Anything else the clinician says is not a note.',
'A note is asked for with "بنویس", "بنویس که", "یادداشت کن", "این را یادداشت کن",',
'or by naming the field the words go into: "یادداشت", "یادداشت‌ها", "توضیحات",',
'"کامنت", "کامنت‌ها" — as in "توی یادداشت‌ها بنویس", "در توضیحات بنویس",',
'"یادداشت: ...". Anything else the clinician says is not a note.',
].join(' '),
nl: [
'The clinician is speaking Dutch, where FDI is standard. "zesentwintig" and "26" are',
'tooth 26. The descriptive form is "rechtsboven zes" = upper right six. A jaw is',
'"bovenkaak" (upper) or "onderkaak" (lower), or "beide kaken" (both).',
'A note is asked for with "schrijf op", "noteer", "zet in de notities". Anything else',
'A note is asked for with "schrijf op", "noteer", "zet in de notities", or by naming',
'the field the words go into ("notitie", "notities", "opmerkingen"). Anything else',
'the clinician says is not a note.',
].join(' '),
en: [
@@ -25,7 +28,8 @@ const LOCALE_NOTES: Record<string, string> = {
'all tooth 26. The descriptive form is "upper right six". A jaw is "upper jaw"/"lower',
'jaw", or "both jaws".',
'A note is asked for with "write this in the notes", "note that", "add a note",',
'"put in the comments". Anything else the clinician says is not a note.',
'"put in the comments", or by naming the field the words go into ("note", "notes",',
'"comments", "remarks"). Anything else the clinician says is not a note.',
].join(' '),
};
@@ -176,6 +180,12 @@ export function buildExtractionPrompt(
' If nobody asked, BOTH are null. Never sweep up leftover speech, filler, small talk or',
' a diagnosis nobody asked you to record. A comment without a trigger is discarded, so',
' guessing costs the clinician the note.',
' WHERE THE NOTE ENDS: the dictated words end where the next instruction begins. Any',
' clause you also reported in another field — a lab, a tooth, a prosthesis, a deadline —',
' is that instruction, so it never belongs in "comment" as well. Nothing may appear in',
' two fields. Example: "توی توضیحات بنویس سلام چطوری و بفرست برای لابراتوآر سانشاین" ->',
' commentTrigger "توی توضیحات بنویس", comment "سلام چطوری", and the lab goes to labId.',
' "و بفرست برای لابراتوآر سانشاین" is an instruction, not part of the note.',
'- labMatchExact: true only when the spoken name matched a lab name exactly.',
].join('\n');

View File

@@ -159,14 +159,17 @@ export const VOICE_INTENT_JSON_SCHEMA = {
description:
'The note the clinician explicitly dictated, in the spoken language, WITHOUT the ' +
'words that asked for it. Null unless they actually asked for a note. Never put ' +
'leftover speech here.',
'leftover speech here. It ends where the next instruction begins: a clause you ' +
'also reported as a lab, a tooth, a prosthesis or a deadline is that instruction, ' +
'and must not appear here too.',
},
commentTrigger: {
type: ['string', 'null'],
description:
'The exact words that asked for a note, copied from the transcript (e.g. ' +
'"بنویس که", "write this in the notes"). Null when nobody asked. A comment with ' +
'no trigger is discarded.',
'"بنویس که", "توی یادداشت‌ها بنویس", "write this in the notes"). Naming the field ' +
'itself counts as asking ("یادداشت", "توضیحات", "notes", "comments"). Null when ' +
'nobody asked. A comment with no trigger is discarded.',
},
prosthesis: {
type: 'array',