feat: improve AI dialogue evaluation and capability boundaries

This commit is contained in:
shen
2026-09-19 18:58:14 -07:00
parent 335d0304b1
commit 7306dc0f85
23 changed files with 1308 additions and 452 deletions
@@ -2,6 +2,7 @@ import 'answer_rules.dart';
import 'core_items.dart';
import 'course_catalog.dart';
import 'course_models.dart';
import '../models.dart';
/// A reply that is at least one real word; turns without a declared rule
/// accept it.
@@ -17,6 +18,24 @@ bool coreItemUsedIn(String id, String input) {
return matchesRule(input, rule);
}
LocalDialogueVerdict evaluateSegmentDialogueLocally(
String segmentId,
int stage,
String response,
) {
final segment = findLessonSegment(segmentId);
if (segment == null || stage >= segment.dialogueRequiredTerms.length) {
return LocalDialogueVerdict.rejected;
}
final text = normalizeAnswer(response);
final terms = segment.dialogueRequiredTerms[stage];
if (terms.isNotEmpty && groupSatisfied(text, terms)) {
return LocalDialogueVerdict.accepted;
}
if (!_isRealReply(text)) return LocalDialogueVerdict.rejected;
return LocalDialogueVerdict.uncertain;
}
bool matchesSegmentDialogue(String segmentId, int stage, String response) {
final segment = findLessonSegment(segmentId);
if (segment == null || stage >= segment.dialogueRequiredTerms.length) {
@@ -24,10 +43,7 @@ bool matchesSegmentDialogue(String segmentId, int stage, String response) {
}
final text = normalizeAnswer(response);
final terms = segment.dialogueRequiredTerms[stage];
// A turn that declares no reviewed term (adapted units) just needs a real
// reply, matching how free-scene turns are validated.
if (terms.isEmpty) return _isRealReply(text);
return groupSatisfied(text, terms);
return terms.isEmpty ? _isRealReply(text) : groupSatisfied(text, terms);
}
bool matchesSegmentIndependent(String segmentId, String response) {
@@ -64,15 +80,32 @@ String? independentShortfall(String segmentId, String response) {
/// Whole-lesson and free-scene dialogues validate the same way segment
/// dialogues do: the turn has to contain the language the turn is teaching.
LocalDialogueVerdict evaluateDialogueStageLocally(
LessonDialogue script,
int stage,
String response,
) {
final text = normalizeAnswer(response);
if (stage < 0 || stage >= script.requiredTerms.length) {
return _isRealReply(text)
? LocalDialogueVerdict.uncertain
: LocalDialogueVerdict.rejected;
}
final terms = script.requiredTerms[stage];
if (terms.isNotEmpty && groupSatisfied(text, terms)) {
return LocalDialogueVerdict.accepted;
}
if (!_isRealReply(text)) return LocalDialogueVerdict.rejected;
return LocalDialogueVerdict.uncertain;
}
bool matchesDialogueStage(LessonDialogue script, int stage, String response) {
final text = normalizeAnswer(response);
if (stage < 0 || stage >= script.requiredTerms.length) {
// No declared requirement: accept anything that is actually a word.
return _isRealReply(text);
}
final terms = script.requiredTerms[stage];
if (terms.isEmpty) return _isRealReply(text);
return groupSatisfied(text, terms);
return terms.isEmpty ? _isRealReply(text) : groupSatisfied(text, terms);
}
String dialogueTaskLabel(LessonDialogue script, int stage) =>