feat: improve AI dialogue evaluation and capability boundaries
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
part of '../ai_service.dart';
|
||||
|
||||
/// Advisory evaluation for open-ended learner answers. Local rules still
|
||||
/// decide whether an attempt counts as evidence or changes learning state.
|
||||
class AiAnswerEvaluationCapability {
|
||||
const AiAnswerEvaluationCapability._(this._service);
|
||||
|
||||
final AiService _service;
|
||||
|
||||
static const descriptor = AiCapabilityDescriptor(
|
||||
id: 'answer-evaluation',
|
||||
promptVersion: 1,
|
||||
outputContract: 'WritingAiFeedback',
|
||||
);
|
||||
|
||||
Future<WritingAiFeedback?> evaluateWriting({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
required String lessonId,
|
||||
required String taskPrompt,
|
||||
required String answer,
|
||||
String level = 'A0',
|
||||
}) => _service.writingFeedback(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
lessonId: lessonId,
|
||||
taskPrompt: taskPrompt,
|
||||
answer: answer,
|
||||
level: level,
|
||||
);
|
||||
|
||||
Future<WritingAiFeedback?> evaluateAnswer({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
required String answerId,
|
||||
required String target,
|
||||
required String taskPrompt,
|
||||
required String answer,
|
||||
String level = 'A0',
|
||||
}) => _service.answerFeedback(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
answerId: answerId,
|
||||
target: target,
|
||||
taskPrompt: taskPrompt,
|
||||
answer: answer,
|
||||
level: level,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
part of '../ai_service.dart';
|
||||
|
||||
/// Stable metadata for an AI capability boundary.
|
||||
///
|
||||
/// [promptVersion] is bumped whenever prompt semantics or the expected model
|
||||
/// contract changes. [outputContract] names the validated application model;
|
||||
/// model output must never flow directly into learning state.
|
||||
class AiCapabilityDescriptor {
|
||||
const AiCapabilityDescriptor({
|
||||
required this.id,
|
||||
required this.promptVersion,
|
||||
required this.outputContract,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final int promptVersion;
|
||||
final String outputContract;
|
||||
}
|
||||
|
||||
/// Typed entry points for bounded AI features.
|
||||
///
|
||||
/// This is deliberately a capability registry, not an autonomous agent. The
|
||||
/// application remains responsible for sequencing, fallback, completion and
|
||||
/// mastery decisions.
|
||||
class AiCapabilities {
|
||||
AiCapabilities._(this._service);
|
||||
|
||||
final AiService _service;
|
||||
|
||||
late final speech = AiSpeechTranscriptionCapability._(_service);
|
||||
late final lexicon = AiLexiconExplanationCapability._(_service);
|
||||
late final dialogue = AiDialogueCoachCapability._(_service);
|
||||
late final evaluation = AiAnswerEvaluationCapability._(_service);
|
||||
late final review = AiReviewGenerationCapability._(_service);
|
||||
|
||||
List<AiCapabilityDescriptor> get descriptors => [
|
||||
AiSpeechTranscriptionCapability.descriptor,
|
||||
AiLexiconExplanationCapability.descriptor,
|
||||
AiDialogueCoachCapability.descriptor,
|
||||
AiAnswerEvaluationCapability.descriptor,
|
||||
AiReviewGenerationCapability.descriptor,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
part of '../ai_service.dart';
|
||||
|
||||
/// Supplies bounded dialogue suggestions while the application owns the turn
|
||||
/// state machine and all completion decisions.
|
||||
class AiDialogueCoachCapability {
|
||||
const AiDialogueCoachCapability._(this._service);
|
||||
|
||||
final AiService _service;
|
||||
|
||||
static const descriptor = AiCapabilityDescriptor(
|
||||
id: 'dialogue-coach',
|
||||
promptVersion: 2,
|
||||
outputContract: 'DialogueAiResponse or DialogueAiIntervention',
|
||||
);
|
||||
|
||||
Future<DialogueAiResponse?> reply({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
required List<Map<String, String>> history,
|
||||
required String aiGoal,
|
||||
required String learnerTask,
|
||||
String level = 'A0',
|
||||
List<String> allowedLanguage = const [],
|
||||
}) => _service.dialogueReply(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
history: history,
|
||||
aiGoal: aiGoal,
|
||||
learnerTask: learnerTask,
|
||||
level: level,
|
||||
allowedLanguage: allowedLanguage,
|
||||
);
|
||||
|
||||
Future<DialogueAiIntervention?> evaluateTurn({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
required String partnerLine,
|
||||
required String taskLabel,
|
||||
required String learnerText,
|
||||
String? turnId,
|
||||
String? hint,
|
||||
String level = 'A0',
|
||||
}) => _service.checkDialogueIntervention(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
partnerLine: partnerLine,
|
||||
taskLabel: taskLabel,
|
||||
learnerText: learnerText,
|
||||
turnId: turnId,
|
||||
hint: hint,
|
||||
level: level,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
part of '../ai_service.dart';
|
||||
|
||||
/// Read-only language explanations. Results may be cached for display but do
|
||||
/// not affect review scheduling, evidence or mastery.
|
||||
class AiLexiconExplanationCapability {
|
||||
const AiLexiconExplanationCapability._(this._service);
|
||||
|
||||
final AiService _service;
|
||||
|
||||
static const descriptor = AiCapabilityDescriptor(
|
||||
id: 'lexicon-explanation',
|
||||
promptVersion: 1,
|
||||
outputContract: 'Temporary definition or SentenceAnalysisResult',
|
||||
);
|
||||
|
||||
Future<String?> define({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
required String text,
|
||||
}) => _service.temporaryDefinition(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
text: text,
|
||||
);
|
||||
|
||||
Future<SentenceAnalysisResult?> analyzeSentence({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
required String text,
|
||||
}) => _service.analyzeSentence(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
text: text,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
part of '../ai_service.dart';
|
||||
|
||||
/// Generates bounded review material. Generation and audit remain explicit
|
||||
/// workflow steps so the model cannot autonomously publish a lesson.
|
||||
class AiReviewGenerationCapability {
|
||||
const AiReviewGenerationCapability._(this._service);
|
||||
|
||||
final AiService _service;
|
||||
|
||||
static const descriptor = AiCapabilityDescriptor(
|
||||
id: 'review-generation',
|
||||
promptVersion: 1,
|
||||
outputContract: 'GeneratedReviewVariant or audited GeneratedLesson',
|
||||
);
|
||||
|
||||
Future<GeneratedReviewVariant?> generateVariant({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
required String targetItemId,
|
||||
required String basePrompt,
|
||||
}) => _service.generateReviewVariant(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
targetItemId: targetItemId,
|
||||
basePrompt: basePrompt,
|
||||
);
|
||||
|
||||
Future<GeneratedLesson?> generateAdaptiveLesson({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
required String targetItemId,
|
||||
required String targetLabel,
|
||||
}) => _service.generateAdaptiveLesson(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
targetItemId: targetItemId,
|
||||
targetLabel: targetLabel,
|
||||
);
|
||||
|
||||
Future<bool> auditAdaptiveLesson({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
required GeneratedLesson lesson,
|
||||
}) => _service.auditGeneratedLesson(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
lesson: lesson,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
part of '../ai_service.dart';
|
||||
|
||||
/// Converts learner audio into a transcript. It does not award speech
|
||||
/// evidence; transcript confirmation remains an application decision.
|
||||
class AiSpeechTranscriptionCapability {
|
||||
const AiSpeechTranscriptionCapability._(this._service);
|
||||
|
||||
final AiService _service;
|
||||
|
||||
static const descriptor = AiCapabilityDescriptor(
|
||||
id: 'speech-transcription',
|
||||
promptVersion: 1,
|
||||
outputContract: 'String transcript',
|
||||
);
|
||||
|
||||
Future<String?> transcribe({
|
||||
required String filePath,
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
}) => _service.transcribeAudio(
|
||||
filePath: filePath,
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user