Agents

LearnPanta's intelligence layer is a coordinated multi-agent system. Each agent has a specific role, distinct prompt logic, and defined input/output contracts.


Agent Overview

AgentRoleModelFile
CuratorContent generationGemini Pro + Searchcurator.py
AnalyzerBehavioral analysisGemini Flashllm.py
Feedback ProviderPerformance synthesisGemini Proactivities.py
Debrief OrchestratorBeat-synced review scriptGemini Flashrouters/debrief.py
Brief ExplainerPer-question paragraph (review sync)Gemini Flashrouters/debrief.py
InterviewerOral debrief (legacy)Gemini Prollm.py

Current UI Note: The AI Debrief Review/Explore tabs use the TLDraw scaffold agent in the frontend (ScaffoldDebriefTab.tsx) plus the /debrief/orchestrate script and /debrief/brief paragraph endpoints.


1. The Curator (Content Generation)

Role: Content Strategist & Fact-Finder

Model: Gemini Pro with Google Search grounding

Location: backend/app/agent/curator.py

Process

  1. Research Syllabus: Uses Google Search to find official exam specifications
  2. Extract Topics: Parses syllabus into weighted topic list
  3. Generate Questions: Creates MCQs per topic with explanations

Prompt Structure

ROLE: Senior Exam Content Developer for {exam_name}

TASK: Generate {count} MCQs for topic "{topic}"

MCQ QUALITY STANDARDS:
- Stem: Clear, single concept, no negatives
- Options: One correct, three plausible distractors
- Difficulty: Calibrated to exam level

OUTPUT SCHEMA:
{
  "questions": [{
    "question_text": "...",
    "options": ["A", "B", "C", "D"],
    "correct_answer": "B",
    "explanation": "...",
    "difficulty": "medium",
    "cognitive_level": "application"
  }]
}

ANTI-HALLUCINATION:
- Only generate questions for topics in official syllabus
- If unsure about facts, omit rather than fabricate

Key Methods

async def curate_exam(self, exam_id: int) -> Paper:
    """Full curation: research → topics → questions → paper"""
    
async def research_syllabus(self, exam_name: str) -> dict:
    """Google Search for official exam topics"""
    
async def generate_questions_for_topic(self, topic: str, count: int) -> list:
    """Generate MCQs for single topic"""

2. The Analyzer (Behavioral Analysis)

Role: Behavioral Analytics Engine

Model: Gemini Flash (speed-optimized)

Location: backend/app/services/llm.pyanalyze_telemetry()

Input Metrics

MetricTypeDescription
avg_time_per_question_msnumberAverage question duration
total_answer_changesnumberSum of answer modifications
tab_blur_countnumberWindow focus losses
idle_periodsnumberPeriods >30s without interaction
face_detection_ratenumber% time face detected (if camera)

Output Schema

{
  "focus_score": 85,
  "fatigue_detected": false,
  "anomalies": ["High answer change rate on Q5-Q10"],
  "summary": "Candidate maintained good focus with minor hesitation mid-exam"
}

Scoring Rules (Deterministic)

focus_score: computed by Gemini Flash from behavioral + biometric signals
fatigue_detected: inferred from time patterns + focus drift signals

3. The Holistic Feedback Provider

Role: Performance Coach

Model: Gemini Pro (deep reasoning)

Location: backend/app/agent/activities.pyfeedback_synthesis_agent()

Input Data

  • Exam Results: Score, per-question correctness
  • Behavioral Metrics: Time patterns, answer changes, focus data
  • Scratchpad Images: Visual evidence of reasoning (if captured)
  • Answer History: Full change log per question

Output Schema

{
  "readiness_score": 72,
  "accuracy_percent": 78.5,
  "behavior_score": 64,
  "behavior_penalty": 10,
  "summary": "Strong conceptual understanding with time management challenges",
  "strengths": [
    "Consistent performance in {Topic A}",
    "Good use of process of elimination"
  ],
  "improvement_areas": [
    "Review {Topic B} fundamentals",
    "Practice timed conditions"
  ],
  "recommendations": [
    "Focus 60% study time on {Topic B}",
    "Take practice exams under time pressure"
  ],
  "domain_accuracy": [
    {"tag": "risk-management", "score": 30, "question_count": 10},
    {"tag": "scheduling", "score": 80, "question_count": 10}
  ],
  "study_priority": "Topic B > Topic C > Topic A",
  "encouragement": "Your careful approach shows strong analytical skills..."
}

Prompt Framework

ROLE: Senior Exam Performance Analyst

ANALYSIS FRAMEWORK:
1. Score Context: Interpret percentage against exam pass rate
2. Behavioral Correlation: Link time patterns to performance
3. Scratchpad Analysis: Infer reasoning from visual evidence

CONSTRAINTS:
- Base all observations on provided data
- Specific recommendations, not generic advice
- Encouraging but honest tone

4. Debrief Orchestrator (Review Script Generator)

Role: Script Writer for beat-synced review

Model: Gemini Flash (speed-optimized)

Location: backend/app/routers/debrief.py

Purpose

Generates timed beat scripts for the guided review experience (speech, cursor actions, optional canvas instructions).

Output Schema

{
  "totalDurationMs": 45000,
  "beats": [
    {
      "id": "q1_explain",
      "startMs": 0,
      "speech": { "text": "Let's review..." },
      "cursor": { "action": "highlight", "target": { "type": "option", "id": "A" }, "color": "red" },
      "canvas": { "instruction": "Draw a simple diagram..." }
    }
  ]
}

5. Brief Explainer (Per-Question Paragraph)

Role: Generate a concise paragraph explanation per question for review sync.

Model: Gemini Flash

Location: backend/app/routers/debrief.py

Purpose

Provides a paragraph explanation (why correct is right, why others are wrong). This paragraph is passed as the message to the scaffold agent for drawing + narration sync.


6. The Interviewer (Oral Examiner) - Legacy

Role: Expert Oral Examiner

Model: Gemini Pro (conversational reasoning)

Location: backend/app/services/llm.pyconduct_interview_turn()

Purpose

Conducts voice-based post-exam debrief:

  1. Listens to candidate's explanation (transcribed)
  2. Evaluates clarity, accuracy, and confidence
  3. Asks probing follow-up questions

Evaluation Criteria

DimensionWeightIndicators
Clarity25%Structured explanation, no rambling
Accuracy35%Factually correct statements
Depth25%Beyond surface-level understanding
Confidence15%Conviction without arrogance

Response Patterns

If correct + confident → "Good explanation. Can you elaborate on..."
If correct + uncertain → "You're right. Let's build your confidence..."
If incorrect → "Interesting approach. Consider that..."

Prompt Engineering Standards

All agent prompts follow these conventions:

1. Structured Format

ROLE: [Professional persona with expertise]

TASK: [Specific action required]

CONTEXT:
- [Labeled data sections]

OUTPUT SCHEMA:
{
  "field": "type and description"
}

CONSTRAINTS:
- [Hard rules]

ANTI-HALLUCINATION:
- [Grounding requirements]

2. JSON Enforcement

All prompts request structured JSON with explicit types:

response = await client.generate_content(
    prompt,
    generation_config=GenerationConfig(
        response_mime_type="application/json",
        response_schema=OutputSchema
    )
)

3. Anti-Hallucination Rules

Every content-generating prompt includes:

  • "Only use information from provided context"
  • "If uncertain, state 'Unable to determine' rather than fabricate"
  • "Do not invent exam topics, statistics, or pass rates"

4. Deterministic Fallbacks

For critical calculations (scores, thresholds), use code-based logic rather than AI:

# Don't ask AI: "What's the focus score?"
# Do calculate: focus_score = 100 - (blurs * 5) - (idles * 3)

Adding New Agents

To add a new agent:

  1. Define the role in this handbook
  2. Create the activity in activities.py:
    @activity.defn
    async def new_agent_activity(input: NewAgentInput) -> NewAgentOutput:
        ...
    
  3. Add the prompt in llm.py or dedicated file
  4. Wire into workflow in workflows.py
  5. Document the prompt structure and I/O schema here

Next Steps