[AI Generation] Make Teacher Personality Generation Non-Interactive - Part 1/2

Resolved 💬 3 comments Opened Nov 30, 2025 by UberBlackCat Closed Jan 30, 2026

[AI Generation] Make Teacher Personality Generation Non-Interactive - Part 1/2

Parent: #410 (Epic) | Dependencies: None

Description

Remove the two-stage clarification flow from teacher personality generation. Instead of asking follow-up questions, the AI should infer all personality details directly from the initial description.

Example:

  • Input: "A friendly cafe waiter"
  • Current: Returns 3-5 questions about formality, correction style, etc.
  • New: Generates complete personality with inferred low formality, gentle corrections, casual tone

Scope

Included:

  • ✅ Teacher personality generation service (teacher_personality_generation_service.py)
  • ✅ Teacher personality schemas (teacher_personality.py)
  • ✅ Teacher personality API endpoint (/api/admin/teacher-personalities/generate)
  • ✅ Admin personality edit form (AdminPersonalityEdit.tsx)
  • ✅ API client (teacherPersonalityApi.ts)
  • ✅ Tests (test_teacher_personality_generation.py)

NOT Included:

  • ❌ Lesson generators (handled in Part 2/2)
  • ❌ Other generation features

Rationale

Why single-stage is better:

  • Users can see results immediately and edit if needed
  • LLMs are sophisticated enough to infer appropriate characteristics
  • Follows "generate first, refine later" UX pattern (industry best practice)
  • Reduces cognitive load - no abstract questions upfront

Technical Context

Current Two-Stage Flow:

  1. Stage 1 (answers=None): Check if clarification needed
  • Service: _build_initial_prompt() (lines 133-207)
  • Returns: {"needs_clarification": true, "questions": [...]}
  1. Stage 2 (answers provided): Generate with answers
  • Service: _build_generation_prompt() (lines 210-289)
  • Returns: {"needs_clarification": false, "personality": {...}}

Files implementing pattern:

  • backend/src/services/teacher_personality_generation_service.py (lines 18-131)
  • backend/src/api/schemas/teacher_personality.py (lines 223-286)
  • backend/src/api/admin/teacher_personalities.py (lines 144-299)
  • frontend/src/pages/admin/AdminPersonalityEdit.tsx (lines 92-97, 310-365)
  • frontend/src/utils/teacherPersonalityApi.ts
  • backend/tests/test_teacher_personality_generation.py (lines 69-93)

Implementation Approach

1. Backend Service (teacher_personality_generation_service.py)

Remove two-stage logic:

# OLD signature:
def generate_personality_from_description(
    db: Session,
    description: str,
    language: str,
    answers: Optional[Dict[str, str]] = None  # ❌ Remove this
) -> Dict[str, Any]:

# NEW signature:
def generate_personality_from_description(
    db: Session,
    description: str,
    language: str
) -> Dict[str, Any]:  # Always returns {"personality": {...}}

Changes:

  • Remove answers parameter (line 22)
  • Remove conditional logic checking for answers (lines 49-58)
  • Remove _build_initial_prompt() function (lines 133-207)
  • Rename _build_generation_prompt()_build_prompt() and simplify (lines 210-289)
  • Remove needs_clarification response logic (lines 102-110)
  • Always return {"personality": {...}} structure

New single-stage prompt:

def _build_prompt(description: str, language: str) -> str:
    """Build prompt that infers all details from description."""
    return f"""Create a comprehensive teacher personality configuration from this description.

DESCRIPTION: {description}
TARGET LANGUAGE: {language}

CRITICAL INSTRUCTIONS:
1. INFER all missing details from the description rather than asking questions
2. Make reasonable assumptions about formality, correction style, and other attributes
3. Generate a complete, internally consistent personality
4. If description is brief, infer appropriate defaults based on context clues

INFERENCE EXAMPLES:
- "friendly cafe waiter" → infer low formality, gentle corrections, casual tone
- "strict business mentor" → infer high formality, direct corrections, formal tone
- "patient beginner tutor" → infer gentle corrections, frequent encouragement

PERSONALITY SCHEMA:
{{
  "name": "unique_snake_case_name",
  "language": "{language}",
  "role_description": "...",
  "tone": "...",
  "conversational_style": "...",
  "correction_style": "gentle|balanced|direct",
  "formality": "low|medium|high",
  "behavior_guidelines": ["guideline1", "guideline2", ...5-10 guidelines...],
  "example_phrases": ["phrase1 in {language}", "phrase2 in {language}", ...10-15 phrases...],
  "behavior_config": {{...}},
  "language_style": {{...}},
  "encouragement": {{...}},
  "consistency": {{...}}
}}

Return valid JSON with the complete personality:
{{
  "personality": {{ ...personality object... }}
}}
"""

2. Backend Schema (teacher_personality.py)

Remove two-stage response:

# REMOVE (lines 244-247):
answers: Optional[Dict[str, str]] = Field(
    None,
    description="Optional clarification answers from Stage 1 questions (Stage 2 only)"
)

# REMOVE (lines 256-286):
class TeacherPersonalityGenerateResponse(BaseModel):
    stage: str = Field(...)
    questions: Optional[List[str]] = None
    personality: Optional[Dict[str, Any]] = None

# REPLACE WITH:
class TeacherPersonalityGenerateResponse(BaseModel):
    """Response for AI-assisted personality generation (single-stage)."""
    personality: Dict[str, Any] = Field(
        ...,
        description="Generated personality configuration matching TeacherPersonalityCreateRequest schema"
    )

3. Backend API (teacher_personalities.py)

Simplify endpoint (lines 144-299):

  • Remove two-stage response logic (lines 230-259)
  • Always return generated personality directly
  • Update docstring (remove "Stage 1" and "Stage 2" references)
  • Update error handling

4. Frontend UI (AdminPersonalityEdit.tsx)

Remove two-stage state:

// REMOVE (lines 95-96):
const [aiQuestions, setAiQuestions] = useState<string[]>([]);
const [aiAnswers, setAiAnswers] = useState<Record<string, string>>({});

// REMOVE (lines 363-365):
const handleAIAnswerChange = () => { ... }

Simplify generation:

const handleAIGenerate = async () => {
  if (!token || !aiDescription.trim()) return;

  setAiGenerating(true);
  setMessage(null);

  try {
    const response = await generatePersonalityWithAI(token, {
      description: aiDescription,
      language: aiLanguage,
    });

    // Directly populate form with generated personality
    populateFormFromGenerated(response.personality);
    setAiGenerationOpen(false);
    setMessage({
      type: 'success',
      text: 'AI personality generated! Review and edit as needed.',
    });
  } catch (error) {
    // Error handling...
  } finally {
    setAiGenerating(false);
  }
};

Simplify modal UI (lines 1181-1280):

  • Remove conditional rendering based on aiQuestions.length
  • Show only description input (no question/answer forms)
  • Single "Generate" button (not "Next" then "Generate")

5. Frontend API Client (teacherPersonalityApi.ts)

Update types:

export interface GeneratePersonalityRequest {
  description: string;
  language: string;
  // REMOVE: answers?: Record<string, string>;
}

export interface GeneratePersonalityResponse {
  personality: TeacherPersonalityCreateRequest;
  // REMOVE: stage, questions
}

6. Tests (test_teacher_personality_generation.py)

Remove two-stage tests:

  • Remove TestGenerationServiceStage1 class (lines 69-93)
  • Remove test_brief_description_returns_questions()
  • Remove test_detailed_description_skips_questions()

Add inference tests:

def test_brief_description_infers_defaults(db_session, mock_llm_response, complete_personality_data):
    """AI infers reasonable defaults from brief descriptions."""
    mock_llm_response({"personality": complete_personality_data})

    result = generation_service.generate_personality_from_description(
        db=db_session,
        description="A friendly tutor",  # Brief
        language="en"
    )

    assert "personality" in result
    # Verify AI inferred appropriately:
    assert result["personality"]["formality"] == "low"  # from "friendly"
    assert result["personality"]["correction_style"] == "gentle"

def test_ambiguous_description_still_generates(db_session, mock_llm_response, complete_personality_data):
    """Even ambiguous descriptions generate without asking questions."""
    mock_llm_response({"personality": complete_personality_data})

    result = generation_service.generate_personality_from_description(
        db=db_session,
        description="a teacher",  # Very vague
        language="en"
    )

    # Should still generate, not ask questions
    assert "personality" in result
    assert isinstance(result["personality"], dict)

Acceptance Criteria

Backend

  • [ ] generate_personality_from_description() no longer accepts answers parameter
  • [ ] Two-stage prompt logic removed (_build_initial_prompt() deleted)
  • [ ] New prompt explicitly instructs AI to infer rather than ask questions
  • [ ] Response schema simplified (no stage, questions fields)
  • [ ] Endpoint always returns direct results ({"personality": {...}})
  • [ ] Tests updated to verify inference behavior
  • [ ] All tests pass: docker compose -f docker/docker-compose.yml exec backend pytest tests/test_teacher_personality_generation.py -v

Frontend

  • [ ] Generation modal shows only description input (no question forms)
  • [ ] Single "Generate" button (no two-step flow)
  • [ ] Form auto-populates immediately after generation
  • [ ] Success message guides user to review/edit generated content
  • [ ] TypeScript types updated (no stage, questions, answers)
  • [ ] Frontend builds: cd frontend && npm run build

Quality Assurance

  • [ ] Inference quality test: Generate 10 personalities from brief descriptions ("friendly waiter", "strict teacher", etc.)
  • [ ] Edge case handling: Test with vague descriptions ("a teacher") - should still generate reasonable defaults
  • [ ] Non-English descriptions: Test with descriptions in non-English for non-English personalities
  • [ ] Consistency check: Generated personalities should be internally consistent (tone matches example phrases)
  • [ ] User testing: 2-3 admins test the new flow and confirm it's faster/easier

Integration

  • [ ] End-to-end test: Generate personality from "friendly tutor" → form populates with low formality, gentle corrections
  • [ ] Manual test: UI feels faster and more streamlined
  • [ ] No references to "Stage 1" or "Stage 2" remain in active code (search codebase)

Files to Modify

Backend (3 files)

  • backend/src/services/teacher_personality_generation_service.py (lines 18-289)
  • backend/src/api/schemas/teacher_personality.py (lines 223-286)
  • backend/src/api/admin/teacher_personalities.py (lines 144-299)

Frontend (2 files)

  • frontend/src/pages/admin/AdminPersonalityEdit.tsx (lines 92-97, 310-365, 1181-1280)
  • frontend/src/utils/teacherPersonalityApi.ts

Tests (1 file)

  • backend/tests/test_teacher_personality_generation.py (lines 69-93)

Total: 6 files

Testing

# Backend tests
docker compose -f docker/docker-compose.yml exec backend pytest tests/test_teacher_personality_generation.py -v

# Frontend build
cd frontend && npm run build

# Manual testing
1. Navigate to Admin → Teacher Personalities → Create
2. Click "Generate with AI"
3. Enter brief description: "friendly waiter"
4. Select language: "en"
5. Click "Generate" (should be single button, not "Next")
6. Verify form populates immediately with complete personality
7. Verify inferred values are appropriate:
   - formality: "low" (from "friendly")
   - correction_style: "gentle" or "balanced"
   - tone: includes "friendly"
8. Edit generated personality and save

Migration & Deployment

Database

  • ✅ No database schema changes required
  • ✅ No migrations needed

API Changes

  • ⚠️ Breaking change: Request schema removes answers field
  • ⚠️ Breaking change: Response schema changes from two-stage to single-stage
  • ✅ Can deploy independently (no dependencies on Part 2/2)

Deployment Steps

  1. Deploy backend changes first (backward compatible - old frontend still works initially)
  2. Deploy frontend changes second
  3. Test end-to-end flow
  4. Monitor for 24-48 hours

Rollback Plan

  • Keep commented-out two-stage code for 2 weeks
  • If generation quality degrades significantly, can revert
  • Monitor user feedback and generation success rate

Notes

  • Self-contained: This part has no dependencies and can be deployed independently
  • Testing focus: Inference quality is critical - test with various description types
  • User guidance: Success message should mention "Review and edit as needed" to set expectations
  • Prompt engineering: Single-stage prompt is longer and more detailed than two-stage prompts combined

Definition of Done

  • [ ] All acceptance criteria met
  • [ ] All tests pass (backend + frontend build)
  • [ ] Manual testing completed with 3 different description types
  • [ ] Code reviewed (if applicable)
  • [ ] Can deploy independently without breaking existing functionality
  • [ ] No references to Stage 1/Stage 2 remain in teacher personality code

---

Generated with Claude Code

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗