Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

22 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Confusion Detection FastAPI

κ΅μœ‘μƒ μ–Όκ΅΄ ν”„λ ˆμž„κ³Ό μˆ˜μ—… ν…μŠ€νŠΈλ₯Ό λ°”νƒ•μœΌλ‘œ 이해 μ €ν•˜ μ‹ ν˜Έλ₯Ό λΆ„μ„ν•˜κ³ , μš”μ•½κ³Ό μ½”μΉ­ κ°€μ΄λ“œλ₯Ό μƒμ„±ν•˜λŠ” FastAPI μ„œλΉ„μŠ€μž…λ‹ˆλ‹€.
ν˜„μž¬ κ΅¬ν˜„μ€ FER + MediaPipe + OpenAI 쑰합을 μ‚¬μš©ν•˜λ©°, 뢄석 APIλŠ” μ–Όκ΅΄ ν‘œμ • νŠΉμ§•κ³Ό GPT νŒλ‹¨μ„ ν•¨κ»˜ μ‚¬μš©ν•©λ‹ˆλ‹€.

ν•œλˆˆμ— 보기

ν•­λͺ© λ‚΄μš©
λŸ°νƒ€μž„ Python 3.11
ν”„λ ˆμž„μ›Œν¬ FastAPI 0.111.0, Uvicorn 0.29.0
API Prefix /ai-api
μ£Όμš” κΈ°λŠ₯ μ–Όκ΅΄ ν”„λ ˆμž„ 뢄석, κ°•μ˜ ν…μŠ€νŠΈ μš”μ•½, λŒ€μ‹œλ³΄λ“œ 기반 AI μ½”μΉ­
μ–Όκ΅΄ 감정 뢄석 fer==22.5.1
μ–Όκ΅΄ λžœλ“œλ§ˆν¬ mediapipe==0.10.33
LLM OpenAI Chat Completions
κΈ°λ³Έ λͺ¨λΈ gpt-5.4-mini
기본 포트 8000

μ•„ν‚€ν…μ²˜

flowchart LR
    A[Student Frame] --> B[FastAPI /analyze]
    B --> C[OpenCV Decode]
    C --> D[FER Emotion Features]
    C --> E[MediaPipe Landmark Features]
    D --> F[Feature Merge]
    E --> F
    F --> G[OpenAI Judge]
    G --> H[Signal Classification]
    H --> I[AnalyzeResponse]

    J[Lecture Transcript] --> K[FastAPI /summarize]
    K --> L[OpenAI Summary]
    L --> M[SummarizeResponse]

    N[Dashboard Data] --> O[FastAPI /coaching]
    O --> P[OpenAI Coaching]
    P --> Q[Fallback Coaching]
    P --> R[CoachingResponse]
    Q --> R
Loading

뢄석 흐름

flowchart TD
    A[Upload Image] --> B{Content-Type Valid?}
    B -- No --> X[400 Error]
    B -- Yes --> C{Empty or > 10MB?}
    C -- Yes --> X
    C -- No --> D[cv2.imdecode]
    D --> E[FER Emotion Detection]
    D --> F[MediaPipe Landmark Detection]
    E --> G[Feature Extraction]
    F --> G
    G --> H[OpenAI JSON Judgment]
    H --> I[Rule-based Signal Classification]
    I --> J[AnalyzeResponse]
Loading

μ‹ ν˜Έ λΆ„λ₯˜ κ·œμΉ™

flowchart TD
    A[GPT confused?] -->|No| B[FACIAL_INSTABILITY / LOW_SIGNAL]
    A -->|Yes| C{Face Detected?}
    C -->|No| D[GAZE_AWAY / FACE_MISSING]
    C -->|Yes| E{head_tilt_deg >= 20}
    E -->|Yes| F[GAZE_AWAY / HEAD_TURNED]
    E -->|No| G{ear < 0.18}
    G -->|Yes| H[GAZE_AWAY / EYES_CLOSED]
    G -->|No| I{top_emotion in fear/sad/surprise/angry/disgust}
    I -->|Yes| J[FACIAL_INSTABILITY / emotion subtype]
    I -->|No| K[FACIAL_INSTABILITY / CONFUSED_PATTERN]
Loading

감정별 signalSubtype 맀핑은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

top_emotion signal_subtype
fear FEAR_DOMINANT
sad SAD_DOMINANT
surprise SURPRISE_DOMINANT
angry ANGRY_TENSION
disgust DISGUST_TENSION

API κ°œμš”

Method Path μ„€λͺ…
POST /ai-api/analyze/{student_id} κ΅μœ‘μƒ μ–Όκ΅΄ ν”„λ ˆμž„ 1μž₯을 뢄석
POST /ai-api/summarize κ°•μ˜ 전사 ν…μŠ€νŠΈ μš”μ•½
POST /ai-api/coaching λŒ€μ‹œλ³΄λ“œ 집계 데이터 기반 μ½”μΉ­ 생성
GET /ai-api/health μ„œλ²„ 및 뢄석기 μ€€λΉ„ μƒνƒœ 확인

API 상세

1. POST /ai-api/analyze/{student_id}

κ΅μœ‘μƒ μ–Όκ΅΄ ν”„λ ˆμž„ 1μž₯을 λ°›μ•„ 이해 μ €ν•˜ μ—¬λΆ€λ₯Ό λΆ„μ„ν•©λ‹ˆλ‹€.

Request

  • Content-Type: multipart/form-data
  • Path parameter: student_id
  • Form field: file

Validation

  • ν—ˆμš© MIME: image/jpeg, image/png, image/webp
  • 빈 파일이면 400
  • 10MB 초과 μ‹œ 400

Response fields

  • studentId
  • confused
  • confidence
  • emotion
  • gptReason
  • signalType
  • signalSubtype
  • signalLabel
  • faceFeatures

confidenceλŠ” FER 감정 confidenceκ°€ μ•„λ‹ˆλΌ, GPTκ°€ νŒλ‹¨ν•œ "μ΄ν•΄ν•˜μ§€ λͺ»ν•œ 정도"μž…λ‹ˆλ‹€.

{
  "studentId": "student-42",
  "confused": true,
  "confidence": 0.82,
  "emotion": "fear",
  "gptReason": "학생이 ν˜„μž¬ λ‚΄μš©μ„ μ΄ν•΄ν•˜μ§€ λͺ»ν•œ κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€.",
  "signalType": "FACIAL_INSTABILITY",
  "signalSubtype": "FEAR_DOMINANT",
  "signalLabel": "ν‘œμ • 기반 λΆˆμ•ˆμ •",
  "faceFeatures": {
    "face_detected": true,
    "emotions": {
      "angry": 0.053,
      "disgust": 0.071,
      "fear": 0.712,
      "happy": 0.011,
      "sad": 0.084,
      "surprise": 0.049,
      "neutral": 0.02
    },
    "top_emotion": "fear",
    "confidence": 0.712,
    "brow_eye_ratio": 0.0382,
    "ear": 0.2113,
    "head_tilt_deg": 4.26
  }
}

faceFeatures.confidenceλŠ” FER κΈ°μ€€ 졜고 감정 점수이고, μ΅œμƒμœ„ confidence와 μ˜λ―Έκ°€ λ‹€λ¦…λ‹ˆλ‹€.

2. POST /ai-api/summarize

κ°•μ˜ 전사 ν…μŠ€νŠΈλ₯Ό μš”μ•½ν•˜κ³ , 보좩 μ„€λͺ…이 ν•„μš”ν•œ κ°œλ…κ³Ό 핡심 ν‚€μ›Œλ“œλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

Request

{
  "audioText": "ν•¨μˆ˜λŠ” μž…λ ₯을 λ°›μ•„ 좜λ ₯을 λ°˜ν™˜ν•˜λŠ” κ΅¬μ‘°μž…λ‹ˆλ‹€. ν‰κ· λ³€ν™”μœ¨κ³Ό λΉ„κ΅ν•˜λ©΄..."
}

Response

  • summary: ν•œκ΅­μ–΄ 2λ¬Έμž₯ μš”μ•½
  • recommendedConcept: μΆ”κ°€ μ„€λͺ…이 ν•„μš”ν•œ κ°œλ… 1개
  • keywords: μ΅œλŒ€ 5개, 각 ν•­λͺ© 3단어 μ΄ν•˜
{
  "summary": "이번 μ„€λͺ…은 ν•¨μˆ˜μ˜ 의미λ₯Ό μž…λ ₯κ³Ό 좜λ ₯ 관계 μ€‘μ‹¬μœΌλ‘œ μ •λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€. ν‰κ· λ³€ν™”μœ¨κ³Όμ˜ 차이λ₯Ό μ΄ν•΄ν•˜λŠ” 것이 λ‹€μŒ λ‹¨κ³„μž…λ‹ˆλ‹€.",
  "recommendedConcept": "ν‰κ· λ³€ν™”μœ¨κ³Ό ν•¨μˆ˜ 관계",
  "keywords": ["ν•¨μˆ˜", "μž…λ ₯ 좜λ ₯", "ν‰κ· λ³€ν™”μœ¨", "λŒ€μ‘ 관계", "κ°œλ… 비ꡐ"]
}

3. POST /ai-api/coaching

λŒ€μ‹œλ³΄λ“œμ—μ„œ μ§‘κ³„ν•œ μˆ˜μ—… λ°˜μ‘ 데이터λ₯Ό λ°”νƒ•μœΌλ‘œ, 강사가 λ°”λ‘œ ν™œμš©ν•  수 μžˆλŠ” μ½”μΉ­ λ©”μ‹œμ§€λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

Request μ£Όμš” ν•„λ“œ

  • date
  • curriculum
  • classId
  • classIds
  • participantCount
  • alertCount
  • avgConfusionPercent
  • topKeywords
  • topTopics
  • signalBreakdown
  • recentAlerts

Response fields

  • summary
  • priorityLevel
  • coachingTips
  • reExplainTopics
  • studentSignals
  • recommendedActionNow
  • sampleMentions

GPT 응닡 νŒŒμ‹±μ΄ μ‹€νŒ¨ν•˜λ©΄ _fallback_coaching()이 μž…λ ₯ 데이터 기반 κΈ°λ³Έ μ½”μΉ­ 문ꡬλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

4. GET /ai-api/health

{
  "status": "ok",
  "analyzer_ready": true
}

μŠ€ν‚€λ§ˆ 관계도

erDiagram
    ANALYZE_RESPONSE {
        string studentId
        boolean confused
        float confidence
        string emotion
        string gptReason
        string signalType
        string signalSubtype
        string signalLabel
        json faceFeatures
    }

    SUMMARIZE_REQUEST {
        string audioText
    }

    SUMMARIZE_RESPONSE {
        string summary
        string recommendedConcept
        string_list keywords
    }

    COACHING_REQUEST {
        string date
        string curriculum
        string classId
        string_list classIds
        int participantCount
        int alertCount
        int avgConfusionPercent
        string_list topKeywords
        string_list topTopics
    }

    COACHING_SIGNAL_ITEM {
        string signalType
        string label
        int count
    }

    COACHING_RECENT_ALERT_ITEM {
        string classId
        string capturedAt
        string topic
        string reason
        int confusionPercent
    }

    COACHING_RESPONSE {
        string summary
        string priorityLevel
        string_list coachingTips
        string_list reExplainTopics
        string_list studentSignals
        string recommendedActionNow
        string_list sampleMentions
    }

    COACHING_REQUEST ||--o{ COACHING_SIGNAL_ITEM : contains
    COACHING_REQUEST ||--o{ COACHING_RECENT_ALERT_ITEM : contains
Loading

μ½”λ“œ ꡬ쑰

fastapi/
β”œβ”€β”€ main.py
β”œβ”€β”€ analyzer.py
β”œβ”€β”€ models.py
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ Dockerfile.base
β”œβ”€β”€ docker-compose.yml
└── build-base.sh
  • main.py: FastAPI μ•±, λΌμš°ν„°, CORS, μš”μ²­ 검증, 응닡 λ³€ν™˜
  • analyzer.py: 이미지 λ””μ½”λ”©, νŠΉμ§• μΆ”μΆœ, GPT νŒλ‹¨, μš”μ•½/μ½”μΉ­, fallback 처리
  • models.py: Pydantic μš”μ²­/응닡 μŠ€ν‚€λ§ˆ

μ‹€ν–‰ 방법

둜컬 μ‹€ν–‰

python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txt

ν™˜κ²½ λ³€μˆ˜:

OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-5.4-mini

μ‹€ν–‰:

uvicorn main:app --host 0.0.0.0 --port 8000 --reload

λ¬Έμ„œ:

  • Swagger UI: http://localhost:8000/docs
  • ReDoc: http://localhost:8000/redoc

Docker μ‹€ν–‰

Base 이미지 생성:

bash ./build-base.sh

μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰:

docker compose up --build -d
docker compose logs -f

쀑지:

docker compose down

λ™μž‘ λ©”λͺ¨

  • μ„œλ²„ μ‹œμž‘ μ‹œ lifespan()μ—μ„œ FaceAnalyzerλ₯Ό 1회 μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.
  • Windowsμ—μ„œλŠ” face_landmarker.taskλ₯Ό μ‚¬μš©μž ν™ˆ 디렉터리에 μ €μž₯ν•˜κ³ , κ·Έ μ™Έ ν™˜κ²½μ—μ„œλŠ” ν”„λ‘œμ νŠΈ 디렉터리 κΈ°μ€€μœΌλ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€.
  • OPENAI_API_KEYκ°€ μ—†μœΌλ©΄ μ•± μ‹œμž‘ λ‹¨κ³„μ—μ„œ FaceAnalyzer μ΄ˆκΈ°ν™”κ°€ μ‹€νŒ¨ν•©λ‹ˆλ‹€.
  • 뢄석 μ‹€νŒ¨ fallback μ‘λ‹΅μ—μ„œλŠ” confused=false, confidence=0.0으둜 λ°˜ν™˜λ©λ‹ˆλ‹€.

About

No description or website provided.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages