Fixes #33 — verb examples must actually use the verb
The example sentences in VerbDetailView (and the new vocab practice modes) frequently used the wrong verb — a "tener" set would show "Él estaba leyendo un libro" (estar), "Nosotros vamos a viajar" (ir), "Tú debes estudiar" (deber). The model drifted off the target verb partway through generating the 6-example batch, and nothing checked the output. Two defenses: Prompt grounding — VerbExampleGenerator.generate now takes a formsByTense map (tenseId → conjugated forms, from the new ReferenceStore.conjugatedForms). Each tense line in the prompt lists the verb's exact conjugated forms and instructs the model to use one of them. The model echoes a real form instead of recalling (and mis-recalling) the conjugation. Output validation — every generated sentence is checked against the conjugation table via accent/case-folded whole-word matching. Any sentence that doesn't contain a real conjugated form of the verb is rejected. Failures trigger one regeneration pass; anything still wrong is dropped rather than displayed. Better to show 4 correct examples than 6 with 2 wrong. Cache invalidation — VerbExampleCache now persists a versioned wrapper (version 2). Pre-fix cached example sets — which may contain wrong-verb sentences — fail the version check and are discarded, so they regenerate cleanly under the new path. Callers updated: VerbDetailView, VocabFlashcardPracticeView, VocabMultipleChoicePracticeView all build formsByTense from ReferenceStore and pass it through. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,19 @@ struct ReferenceStore {
|
||||
return (try? context.fetch(descriptor)) ?? []
|
||||
}
|
||||
|
||||
/// Map of tenseId → conjugated forms for a verb, used to ground and
|
||||
/// validate LLM-generated example sentences.
|
||||
func conjugatedForms(verbId: Int, tenseIds: [String]) -> [String: [String]] {
|
||||
var map: [String: [String]] = [:]
|
||||
for tenseId in tenseIds {
|
||||
let forms = fetchForms(verbId: verbId, tenseId: tenseId)
|
||||
.map(\.form)
|
||||
.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty }
|
||||
if !forms.isEmpty { map[tenseId] = forms }
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
func fetchForm(verbId: Int, tenseId: String, personIndex: Int) -> VerbForm? {
|
||||
let descriptor = FetchDescriptor<VerbForm>(
|
||||
predicate: #Predicate<VerbForm> { form in
|
||||
|
||||
Reference in New Issue
Block a user