Skip to content

Student Assessment

StudentAssessment is the student-facing assessment modality. It manages the answer submission flow, tracks knowledge state, and uses the AlgorithmEngine for adaptive question selection.

pub struct StudentAssessment {
state: State<StudentAssessmentSynced, StudentAssessmentEphemeral>,
services: Arc<StudentAssessmentServices>,
cmd_tx: CommandSender<...>,
cmd_rx: CommandReceiver<...>,
}

The services include the AlgorithmEngine for adaptive question selection:

pub struct StudentAssessmentServices {
pub agent: Arc<AgentService>,
pub algorithm: AlgorithmEngine,
}
pub struct StudentAssessmentSynced {
pub meta: ResourceMeta,
pub session_id: String,
pub student_id: String,
pub placements: Vec<ComponentPlacement<QuestionSequencePosition>>,
pub answers: HashMap<String, AnswerData>,
pub knowledge_snapshot: Vec<(usize, f32)>, // sparse knowledge vector
pub indicator_snapshot: Vec<(usize, f32)>, // sparse skill indicator
pub status: StudentAssessmentStatus,
}
pub struct StudentAssessmentEphemeral {
pub current_question_index: usize,
pub precomputed_next: Option<PrecomputedBranch>,
pub output: Vec<AssessmentElement>,
}

The precomputed_next field holds pre-calculated next questions for both correct and incorrect outcomes, enabling instant transitions.

QuestionSequencePosition describes a question’s place in the sequence:

pub struct QuestionSequencePosition {
pub sequence_number: usize,
pub question_id: String,
}
InProgress → Paused → InProgress
|
└→ Completed
StatusMeaning
InProgressStudent is actively answering questions
PausedStudent paused or teacher paused the session
CompletedAssessment finished (all questions answered or ended)
IntentParametersPurpose
StartBegin the assessment, compute first question
SubmitAnsweranswer: StringSubmit answer for current question
SkipQuestionSkip current question without answering
PausePause the assessment
ResumeResume from pause
EndEnd the assessment early
RecompileForce recompilation (hidden)

Async operations produce feedback that arrives on the next dispatch cycle:

pub enum StudentAssessmentFeedback {
PrecomputedBranch {
if_correct: Option<String>, // question ID if answer is correct
if_incorrect: Option<String>, // question ID if answer is incorrect
},
NextQuestionComputed {
question_id: String,
},
}
  1. Start — spawns async computation via AlgorithmEngine to select the first question
  2. NextQuestionComputed feedback — adds new placement to the sequence
  3. Precomputed branches — while student answers, the engine pre-computes what comes next for both outcomes
  4. SubmitAnswer — stores answer data, triggers knowledge update (direct + indirect), selects next question
  5. Knowledge update — direct impact on tested skills, indirect propagation via distance matrix, clipped to [KL, KU]

When an answer is submitted:

1. Direct knowledge = current + (±1 × score_factor × DIRECT_MULTIPLIER) per tested skill
2. Indirect knowledge = propagation along distance matrix edges to untested skills
3. Clip to [KL, KU] bounds
4. Store updated knowledge_snapshot (sparse)
5. Compute next question from updated state

Uses nalgebra::DVector internally for linear algebra operations.

pub struct StudentAssessmentSnapshot {
pub id: String,
pub name: String,
pub session_id: String,
pub student_id: String,
pub placements: Vec<StudentQuestionPlacement>,
pub answers: HashMap<String, AnswerData>,
pub current_question_index: usize,
pub status: StudentAssessmentStatus,
pub version: u64,
pub export_ready: bool,
}