# Quick Training API — 개발자 문서

> Edge 단말 / 외부 애플리케이션 / Claude Code 에이전트에서 Quick Training 서비스를 호출하기 위한 REST API 명세

- **Base URL**: `http://localhost:8000` (개발) / `http://platform.finetheai.com:18000` (운영)
- **API Prefix**: `/api/quick-train`
- **Content-Type**: `application/json` (파일 업로드는 `multipart/form-data`)
- **인증**: 현재 세션 쿠키 기반 (로그인 필요). 세션 없이 익명 사용 가능하나 24시간 후 자동 삭제.
- **문서 버전**: v1.3 (2026-04-15)

---

## 목차

1. [워크플로우 개요](#워크플로우-개요)
2. [인증](#인증)
3. [세션 관리](#세션-관리) — 3 endpoints
4. [이미지 관리](#이미지-관리) — 4 endpoints
5. [클래스 관리](#클래스-관리) — 4 endpoints
6. [라벨링](#라벨링) — 2 endpoints
7. [학습](#학습) — 4 endpoints (기본값 조회 포함)
8. [모델 다운로드](#모델-다운로드) — 1 endpoint (5 formats)
9. [추론 프리뷰](#추론-프리뷰) — 1 endpoint
10. [Edge 배포](#edge-배포) — 1 endpoint
11. [전체 예제 코드](#전체-예제-코드)
12. [에러 코드](#에러-코드)
13. [제약사항](#제약사항)

---

## 워크플로우 개요

```
[1] 세션 생성            POST /sessions
[2] 이미지 업로드        POST /sessions/{sid}/images
[3] 클래스 정의          POST /sessions/{sid}/classes
[4] 각 이미지 라벨링     POST /sessions/{sid}/labels/{img_id}
[5] 학습 시작            POST /sessions/{sid}/train
[6] 진행률 모니터링      GET  /sessions/{sid}/status  (polling)
[7] 모델 다운로드        GET  /sessions/{sid}/download/{fmt}
[8] 선택: 프리뷰/배포    GET  /sessions/{sid}/preview/{img_id}
                        POST /sessions/{sid}/deploy/{device_id}
```

---

## 인증

플랫폼은 **하이브리드 인증**을 지원합니다.

| 방식 | 사용처 | 필수 헤더/쿠키 |
|------|--------|----------------|
| **API Key** (권장, 외부 통합) | 서버-서버 호출, CLI, 자동화 | `X-API-Key: ftai_live_...` |
| **세션 쿠키** | 브라우저 UI | `Cookie: session=...` |

### A. API Key 발급 (외부 개발자용 권장)

#### 1) 키 발급 (브라우저에서 1회)
1. 플랫폼 로그인 → 사이드바 **`System > 🔑 API Keys`** 메뉴
2. **`+ 새 키 발급`** 클릭
3. 이름, 스코프(`quick-train`), 만료일 지정
4. 발급된 키 즉시 복사 (이후 재조회 불가)

#### 2) API 호출 시 헤더 추가

```bash
curl -H "X-API-Key: ftai_live_YOUR_KEY_HERE" \
     http://platform.finetheai.com:18000/api/quick-train/sessions
```

```python
import requests

headers = {"X-API-Key": "ftai_live_YOUR_KEY_HERE"}
res = requests.get("http://platform.finetheai.com:18000/api/quick-train/sessions",
                    headers=headers)
```

```javascript
fetch('http://platform.finetheai.com:18000/api/quick-train/sessions', {
    headers: { 'X-API-Key': 'ftai_live_YOUR_KEY_HERE' }
});
```

#### 3) 사용 내역 조회
모든 API Key 호출은 서버에 자동 기록됩니다. **`System > 🔑 API Keys`** 페이지의 각 키 옆 **`사용`** 버튼으로 확인:
- 호출 시각, HTTP 메서드/경로, 응답 상태, 처리 시간, IP
- 일별 트렌드, 상위 엔드포인트
- 90일간 보관

#### 4) 보안 권장사항
- **키를 코드/Git에 직접 포함 금지** — 환경변수 사용 권장 (`os.environ['FTAI_KEY']`)
- 키 노출 의심 시 즉시 **비활성화** 후 재발급
- 만료일을 90~180일로 설정하여 주기적 교체
- 스코프 최소 권한 원칙 (전체 권한 `all` 대신 필요한 것만)

### B. 세션 쿠키 (브라우저 전용)

```bash
# 1. 인증 코드 발송
POST /api/auth/send-code     { "email": "user@finetheai.com" }
# 2. 코드 검증
POST /api/auth/verify-code   { "email": "user@finetheai.com", "code": "123456" }
# → 응답 Set-Cookie: session=...
```

이후 동일 쿠키를 모든 요청에 포함하면 인증됨.

### C. 인증 실패 응답

| 상태 | 의미 |
|------|------|
| 401 | API Key 미제공 또는 무효 |
| 401 | 만료된 키 / 비활성화된 키 |
| 403 | 스코프 권한 부족 (예: `quick-train` 키로 다른 API 호출) |

---

## 세션 관리

### 1. 세션 목록 조회

**`GET /api/quick-train/sessions`**

| Query | Type | 설명 |
|-------|------|------|
| `user_email` | string | (선택) 특정 사용자의 세션만 조회 |

**응답 예시**:
```json
{
  "sessions": [
    {
      "session_id": "a1b2c3d4e5f6",
      "name": "결함검출",
      "user_email": "user@finetheai.com",
      "created_at": "2026-04-14T10:30:00",
      "updated_at": "2026-04-14T11:15:22",
      "image_count": 25,
      "label_count": 25,
      "classes": [
        {"id": 0, "name": "good", "color": "#10B981"},
        {"id": 1, "name": "defect", "color": "#EF4444"}
      ],
      "training_config": {"base_model": "yolo11n.pt", "epochs": 30, ...},
      "training_status": "done",
      "model_available": true
    }
  ]
}
```

---

### 2. 세션 생성

**`POST /api/quick-train/sessions`**

**요청 바디**:
```json
{
  "name": "결함검출",
  "user_email": "user@finetheai.com"
}
```

| 필드 | Type | 필수 | 설명 |
|------|------|------|------|
| `name` | string | X | 세션 이름 (기본: "Session-XXXXXX") |
| `user_email` | string | X | 사용자 이메일 (미지정 시 익명 세션, 24h 후 삭제) |

**응답**: 세션 메타 (201 Created)

---

### 3. 세션 상세 조회

**`GET /api/quick-train/sessions/{session_id}`**

응답: 세션 메타 객체. 없으면 `404`.

---

### 4. 세션 삭제

**`DELETE /api/quick-train/sessions/{session_id}`**

이미지/라벨/학습 데이터 모두 삭제. 진행 중 학습은 중지됩니다.

```json
{"success": true}
```

---

## 이미지 관리

### 5. 이미지 업로드 (다중)

**`POST /api/quick-train/sessions/{session_id}/images`**

**Content-Type**: `multipart/form-data`
**Body**: `files` 필드에 여러 파일 첨부

**지원 포맷**: `.jpg`, `.jpeg`, `.png`, `.bmp`
**파일당 크기 제한**: 20MB

**응답 예시**:
```json
{
  "uploaded": [
    {"image_id": "abc12345", "filename": "abc12345.jpg", "original_name": "photo1.jpg", "size_bytes": 245678},
    {"image_id": "def67890", "filename": "def67890.png", "original_name": "photo2.png", "size_bytes": 389201}
  ],
  "errors": [],
  "total": 2
}
```

---

### 6. 이미지 목록

**`GET /api/quick-train/sessions/{session_id}/images`**

```json
{
  "images": [
    {
      "image_id": "abc12345",
      "filename": "abc12345.jpg",
      "size_bytes": 245678,
      "has_label": true
    }
  ]
}
```

---

### 7. 이미지 파일 서빙

**`GET /api/quick-train/sessions/{session_id}/images/{image_id}/file`**

응답: 이미지 바이너리 (`image/jpeg`, `image/png` 등).
`<img src="...">` 또는 fetch로 직접 사용 가능.

---

### 8. 이미지 삭제

**`DELETE /api/quick-train/sessions/{session_id}/images/{image_id}`**

연결된 라벨도 함께 삭제됩니다.

```json
{"success": true}
```

---

## 클래스 관리

### 9. 클래스 목록

**`GET /api/quick-train/sessions/{session_id}/classes`**

```json
{
  "classes": [
    {"id": 0, "name": "good", "color": "#10B981"},
    {"id": 1, "name": "defect", "color": "#EF4444"}
  ]
}
```

---

### 10. 클래스 추가

**`POST /api/quick-train/sessions/{session_id}/classes`**

```json
{
  "name": "defect",
  "color": "#EF4444"
}
```

| 필드 | Type | 필수 | 설명 |
|------|------|------|------|
| `name` | string | O | 클래스 이름 (중복 시 기존 반환) |
| `color` | string | X | HEX 색상 (미지정 시 자동 할당) |

**응답**: 생성된 클래스 (`id` 자동 할당)

---

### 11. 클래스 수정

**`PUT /api/quick-train/sessions/{session_id}/classes/{class_id}`**

```json
{"name": "new_name", "color": "#3B82F6"}
```

둘 다 선택. 변경할 것만 지정.

---

### 12. 클래스 삭제

**`DELETE /api/quick-train/sessions/{session_id}/classes/{class_id}`**

⚠️ 삭제 시 남은 클래스의 `id`가 재할당됩니다. 이미 저장된 라벨의 `class_id`와 불일치할 수 있으니 주의.

---

## 라벨링

### 13. 라벨 저장

**`POST /api/quick-train/sessions/{session_id}/labels/{image_id}`**

```json
{
  "labels": [
    {"class_id": 0, "x": 100, "y": 50, "w": 80, "h": 60},
    {"class_id": 1, "x": 250, "y": 120, "w": 100, "h": 90}
  ],
  "image_width": 1920,
  "image_height": 1080
}
```

| 필드 | Type | 설명 |
|------|------|------|
| `labels[]` | array | 바운딩박스 배열 |
| `labels[].class_id` | int | 클래스 ID |
| `labels[].x, y, w, h` | float | **픽셀 좌표** (좌상단 원점) |
| `image_width` | int | 원본 이미지 너비 (정규화용) |
| `image_height` | int | 원본 이미지 높이 |

서버는 내부적으로 YOLO txt 포맷(normalized `cx cy w h`)으로 변환 저장.

**응답**:
```json
{"image_id": "abc12345", "label_count": 2, "total_labeled": 15}
```

---

### 14. 라벨 조회

**`GET /api/quick-train/sessions/{session_id}/labels/{image_id}?image_width=1920&image_height=1080`**

| Query | Type | 설명 |
|-------|------|------|
| `image_width` | int | 픽셀 좌표로 역변환할 이미지 너비 |
| `image_height` | int | 이미지 높이 |

`image_width/height` 둘 다 지정 시 픽셀 좌표 반환, 아니면 정규화 좌표.

```json
{
  "labels": [
    {"class_id": 0, "x": 100, "y": 50, "w": 80, "h": 60}
  ]
}
```

---

## 학습

### 15. 학습 파라미터 기본값 조회

**`GET /api/quick-train/defaults`**

학습 시작 UI/CLI 에서 표시할 **기본값** 및 **허용 범위**를 단일 소스로 반환합니다.
서버 기본값이 변경되면 이 엔드포인트만 바라보면 항상 최신 값 사용 가능.

**응답 예시**:
```json
{
  "defaults": {
    "base_model": "yolo11n.pt",
    "epochs": 30,
    "imgsz": 640,
    "batch": 8,
    "patience": 50,
    "lr0": 0.01,
    "optimizer": "auto"
  },
  "ranges": {
    "epochs":    {"min": 10, "max": 500, "step": 10},
    "imgsz":     {"choices": [320, 416, 512, 640, 768, 960, 1280]},
    "batch":     {"min": 1, "max": 64, "step": 1},
    "patience":  {"min": 0, "max": 200, "step": 5},
    "lr0":       {"min": 0.0001, "max": 0.1, "step": 0.0001},
    "optimizer": {"choices": ["auto", "SGD", "Adam", "AdamW"]},
    "base_model":{"choices": ["yolo11n.pt", ..., "rtdetr-x.pt"]}
  },
  "base_models": {
    "yolo11n.pt": {"family": "YOLO11", "label": "YOLO11n (Nano)", "params_m": 2.6, "flops_g": 6.5, "coco_map50_95": 39.5, "use_case": "Edge / 라즈베리파이 / 모바일, 최고 속도"},
    "yolov10b.pt": {"family": "YOLOv10", "label": "YOLOv10b (Balanced)", "params_m": 19.1, "flops_g": 92.0, "coco_map50_95": 52.5, "use_case": "정확도↔속도 균형"},
    "yolov9e.pt": {"family": "YOLOv9", "label": "YOLOv9e (Extended)", "params_m": 57.3, "flops_g": 189.0, "coco_map50_95": 55.6, "use_case": "YOLOv9 최고 정확도"},
    "rtdetr-x.pt": {"family": "RT-DETR", "label": "RT-DETR-X", "params_m": 67.0, "flops_g": 222.5, "coco_map50_95": 54.8, "use_case": "Transformer 최고 정확도"}
    /* ... 23개 모델 전체 (아래 표 참조) ... */
  }
}
```

### 지원 base_model 전체 목록 (23종)

Quick Training 은 내부적으로 `ultralytics.YOLO(base_model)` 를 호출하므로, Ultralytics가 공식 지원하는 **YOLOv8 / YOLOv9 / YOLOv10 / YOLO11 + RT-DETR** 를 모두 사용할 수 있습니다. 지정한 가중치 파일이 로컬에 없으면 최초 학습 시 자동 다운로드됩니다. 지표는 COCO val2017 기준.

#### YOLO11 (최신, 기본 권장)

| `base_model` | 레이블 | 파라미터 | FLOPs | mAP50-95 | 권장 용도 |
|--------------|--------|---------:|------:|---------:|-----------|
| `yolo11n.pt` | YOLO11n (Nano) | 2.6 M | 6.5 G | 39.5 | Edge / 모바일 · **최고 속도** (기본값) |
| `yolo11s.pt` | YOLO11s (Small) | 9.4 M | 21.5 G | 47.0 | 경량 서버 / Jetson · 속도↔정확도 균형 |
| `yolo11m.pt` | YOLO11m (Medium) | 20.1 M | 68.0 G | 51.5 | GPU 서버 · 정확도 우선 |
| `yolo11l.pt` | YOLO11l (Large) | 25.3 M | 86.9 G | 53.4 | 고성능 GPU · 고정밀 검사 |
| `yolo11x.pt` | YOLO11x (X-Large) | 56.9 M | 194.9 G | 54.7 | A100/H100급 · 최고 정확도 |

#### YOLOv10 (NMS-free, 2024)

| `base_model` | 레이블 | 파라미터 | FLOPs | mAP50-95 | 권장 용도 |
|--------------|--------|---------:|------:|---------:|-----------|
| `yolov10n.pt` | YOLOv10n | 2.3 M | 6.7 G | 38.5 | End-to-end, NMS 불필요, Edge |
| `yolov10s.pt` | YOLOv10s | 7.2 M | 21.6 G | 46.3 | 경량 실시간 파이프라인 |
| `yolov10m.pt` | YOLOv10m | 15.4 M | 59.1 G | 51.1 | GPU 실시간 추론 |
| `yolov10b.pt` | YOLOv10b (Balanced) | 19.1 M | 92.0 G | 52.5 | 정확도↔속도 균형 |
| `yolov10l.pt` | YOLOv10l | 24.4 M | 120.3 G | 53.2 | 고정밀 (NMS-free 장점) |
| `yolov10x.pt` | YOLOv10x | 29.5 M | 160.4 G | 54.4 | NMS-free 계열 최고 정확도 |

#### YOLOv9

| `base_model` | 레이블 | 파라미터 | FLOPs | mAP50-95 | 권장 용도 |
|--------------|--------|---------:|------:|---------:|-----------|
| `yolov9t.pt` | YOLOv9t (Tiny) | 2.0 M | 7.7 G | 38.3 | 초경량, Edge |
| `yolov9s.pt` | YOLOv9s | 7.1 M | 26.4 G | 46.8 | 범용 경량 |
| `yolov9m.pt` | YOLOv9m | 20.0 M | 76.3 G | 51.4 | 중형 GPU |
| `yolov9c.pt` | YOLOv9c (Compact) | 25.3 M | 102.1 G | 53.0 | GELAN 컴팩트 |
| `yolov9e.pt` | YOLOv9e (Extended) | 57.3 M | 189.0 G | **55.6** | YOLOv9 최고 정확도 |

#### YOLOv8 (안정적 레거시)

| `base_model` | 레이블 | 파라미터 | FLOPs | mAP50-95 | 권장 용도 |
|--------------|--------|---------:|------:|---------:|-----------|
| `yolov8n.pt` | YOLOv8n | 3.2 M | 8.7 G | 37.3 | 레거시 호환, Edge |
| `yolov8s.pt` | YOLOv8s | 11.2 M | 28.6 G | 44.9 | 안정적 경량 |
| `yolov8m.pt` | YOLOv8m | 25.9 M | 78.9 G | 50.2 | 검증된 중형 |
| `yolov8l.pt` | YOLOv8l | 43.7 M | 165.2 G | 52.9 | 대형, 고정밀 |
| `yolov8x.pt` | YOLOv8x | 68.2 M | 257.8 G | 53.9 | YOLOv8 최고 정확도 |

#### RT-DETR (Transformer 기반)

| `base_model` | 레이블 | 파라미터 | FLOPs | mAP50-95 | 권장 용도 |
|--------------|--------|---------:|------:|---------:|-----------|
| `rtdetr-l.pt` | RT-DETR-L | 32.0 M | 103.5 G | 53.0 | Transformer, 밀집 객체 우수 |
| `rtdetr-x.pt` | RT-DETR-X | 67.0 M | 222.5 G | 54.8 | Transformer 최고 정확도 |

#### 선택 가이드

- **Edge / 모바일 배포** (`/deploy/{device_id}` + ONNX) → `yolo11n.pt`, `yolov10n.pt`, `yolov9t.pt`, `yolov8n.pt`
- **실시간 추론 + NMS 불필요** → YOLOv10 계열 (end-to-end)
- **일반 공정 검사 (기본 추천)** → `yolo11s.pt` 또는 `yolo11m.pt`
- **미세결함 · 고해상도** → `yolo11x.pt`, `yolov9e.pt`, `yolov10x.pt` + `imgsz=1280`
- **밀집 객체 · 소형 타겟 다수** → `rtdetr-l.pt` / `rtdetr-x.pt`
- **레거시 파이프라인 호환** → YOLOv8 계열 (`yolov8s.pt` 가장 안정적)
- **배치 주의**: 무거운 모델(`x`, `e`)은 `batch` 를 낮춰야 GPU OOM 방지 (예: `yolov8x.pt` 640px → batch 2~4 권장)

> **Tip**: 위 목록에 없는 ultralytics 호환 모델 (예: `yolov8n-seg.pt`, `yolov8n-cls.pt`) 을 직접 지정해도 동작하지만 **Quick Training은 Detection 전용**으로 최적화되어 있어 seg/cls 모델은 권장하지 않습니다.

---

### 16. 학습 시작

**`POST /api/quick-train/sessions/{session_id}/train`**

```json
{
  "base_model": "yolo11n.pt",
  "epochs": 30,
  "imgsz": 640,
  "batch": 8,
  "patience": 50,
  "lr0": 0.01,
  "optimizer": "auto"
}
```

| 필드 | 기본값 | 설명 |
|------|--------|------|
| `base_model` | `yolo11n.pt` | 사전학습 모델 — **YOLOv8 / v9 / v10 / 11 + RT-DETR 총 23종** 지원. 상세 비교표는 §15 참조 |
| `epochs` | `30` | 학습 에포크 수 (10 ~ 500) |
| `imgsz` | `640` | 입력 이미지 크기 (320 / 416 / 512 / 640 / 768 / 960 / 1280) |
| `batch` | `8` | 배치 크기 (1 ~ 64) |
| `patience` | `50` | **Early Stopping patience** — mAP 개선 없이 연속 N 에포크 이후 조기 종료. `0` = 비활성 |
| `lr0` | `0.01` | 초기 학습률 (0.0001 ~ 0.1) |
| `optimizer` | `auto` | 옵티마이저 (`auto` / `SGD` / `Adam` / `AdamW`) |

모든 필드는 **선택 사항**이며, 누락 시 서버 기본값이 적용됩니다. 최신 기본값/범위는 `GET /defaults` 엔드포인트로 확인하세요.

**Patience 동작**:
- `patience = 50` → 50 에포크 동안 검증 mAP가 개선되지 않으면 조기 종료
- `patience = 0` → early stopping 비활성 (`epochs` 만큼 끝까지 학습)

**전제 조건**:
- 최소 4장의 라벨된 이미지 필요
- 최소 1개 클래스 정의 필요
- 해당 세션에서 학습 진행 중이면 400 오류
- 서버 전체 동시 학습 한도 초과 시 429 (`Retry-After: 60`)

**응답**:
```json
{
  "started": true,
  "epochs": 30,
  "concurrent_running": 1,
  "max_concurrent": 2
}
```

학습은 백그라운드 스레드에서 실행됩니다. 이후 저장되는 세션 메타의 `training_config` 에는 위 7개 필드가 모두 기록되어, `GET /sessions/{sid}` 로 실제 사용된 값을 재확인할 수 있습니다.

---

### 17. 학습 상태 조회 (폴링)

**`GET /api/quick-train/sessions/{session_id}/status`**

```json
{
  "session_id": "a1b2c3d4e5f6",
  "status": "running",
  "running": true,
  "current_epoch": 15,
  "total_epochs": 30,
  "progress_pct": 50.0,
  "metrics": {
    "mAP50": 0.824,
    "mAP50_95": 0.612,
    "precision": 0.891,
    "recall": 0.756
  },
  "logs": [
    "[INFO] 데이터셋 준비 중...",
    "[INFO] 학습 시작: yolo11n.pt epochs=30 imgsz=640"
  ],
  "error": null,
  "started_at": "2026-04-14T10:30:00",
  "finished_at": null,
  "model_available": false
}
```

**`status` 값**: `idle` / `preparing` / `running` / `done` / `failed` / `cancelled`

권장 폴링 주기: **2~5초**. 학습 완료 시 `running: false` + `model_available: true`.

---

### 18. 학습 중지

**`POST /api/quick-train/sessions/{session_id}/stop`**

현재 에포크 완료 후 중지. 중지된 학습은 `best.pt`까지만 사용 가능.

```json
{"success": true}
```

---

## 모델 다운로드

### 19. 모델 파일 다운로드

**`GET /api/quick-train/sessions/{session_id}/download/{fmt}`**

| `fmt` | 포맷 | 권장 용도 | 변환 시간 |
|-------|------|----------|-----------|
| `pt` | PyTorch | 서버/Edge GPU 추론 (주력) | 즉시 |
| `onnx` | ONNX | 범용 (CPU/GPU, TensorRT) | 최초 30~60초 |
| `torchscript` | TorchScript | PyTorch Mobile | 최초 30초 |
| `engine` | TensorRT | NVIDIA GPU 최대 속도 | 최초 1~5분 (GPU 필요) |
| `tflite` | TFLite | ARM Edge (Raspberry Pi) | 최초 1~2분 |

**응답**: `application/octet-stream` 바이너리 파일

### 예시: Python으로 다운로드
```python
import requests

sid = "a1b2c3d4e5f6"
res = requests.get(f"http://localhost:8000/api/quick-train/sessions/{sid}/download/onnx")
with open("my_model.onnx", "wb") as f:
    f.write(res.content)
```

---

## 추론 프리뷰

### 20. 학습 모델로 이미지 추론

**`GET /api/quick-train/sessions/{session_id}/preview/{image_id}?conf=0.25`**

| Query | Type | 기본 | 설명 |
|-------|------|------|------|
| `conf` | float | 0.25 | confidence 임계값 (0.01~0.99) |

**응답 예시**:
```json
{
  "image_id": "abc12345",
  "total_detections": 2,
  "detections": [
    {
      "class_id": 1,
      "class_name": "defect",
      "color": "#EF4444",
      "confidence": 0.9234,
      "bbox": [125.3, 48.7, 210.5, 145.2]
    },
    {
      "class_id": 0,
      "class_name": "good",
      "color": "#10B981",
      "confidence": 0.8512,
      "bbox": [320.1, 80.5, 445.8, 195.3]
    }
  ]
}
```

`bbox` 형식: **`[x1, y1, x2, y2]`** (좌상단, 우하단 픽셀 좌표)

**전제**: 학습 완료 상태 (`model_available: true`)

---

## Edge 배포

### 21. Edge 디바이스로 모델 배포

**`POST /api/quick-train/sessions/{session_id}/deploy/{device_id}`**

ONNX로 자동 변환 후 Edge Devices 시스템과 연동할 메타데이터 반환.
실제 배포는 Edge Devices 메뉴에서 최종 확인.

**응답**:
```json
{
  "success": true,
  "device_id": "EDGE-001",
  "model_path": "/path/to/best.onnx",
  "model_name": "quick_결함검출",
  "session_id": "a1b2c3d4e5f6",
  "classes": [{"id": 0, "name": "good", "color": "#10B981"}, ...],
  "hint": "Edge Devices 메뉴에서 이 모델을 선택하여 배포하세요"
}
```

---

## 전체 예제 코드

### Python 예제 — 전체 워크플로우

```python
import os
import requests
import time
from pathlib import Path

BASE_URL = "http://platform.finetheai.com:18000/api/quick-train"

# API Key 헤더 (환경변수 권장)
HEADERS = {"X-API-Key": os.environ["FTAI_API_KEY"]}  # ftai_live_...

# 1. 세션 생성
r = requests.post(f"{BASE_URL}/sessions", headers=HEADERS, json={
    "name": "결함검출_v1",
    "user_email": "dev@finetheai.com"
})
session = r.json()
sid = session["session_id"]
print(f"세션 생성: {sid}")

# 2. 이미지 업로드 (여러 장)
image_files = Path("./training_images").glob("*.jpg")
files = [("files", (p.name, open(p, "rb"), "image/jpeg")) for p in image_files]
r = requests.post(f"{BASE_URL}/sessions/{sid}/images", headers=HEADERS, files=files)
uploaded = r.json()["uploaded"]
print(f"업로드 완료: {len(uploaded)}장")

# 3. 클래스 추가
for cls_name in ["good", "defect"]:
    requests.post(f"{BASE_URL}/sessions/{sid}/classes", headers=HEADERS, json={"name": cls_name})

# 4. 각 이미지 라벨링 (예: 이미지 중앙에 단일 박스)
from PIL import Image
for img_meta in uploaded:
    img_id = img_meta["image_id"]
    # 이미지 크기 조회
    img_bytes = requests.get(f"{BASE_URL}/sessions/{sid}/images/{img_id}/file", headers=HEADERS).content
    img = Image.open(io.BytesIO(img_bytes))
    W, H = img.size
    # 중앙 박스 라벨
    requests.post(f"{BASE_URL}/sessions/{sid}/labels/{img_id}", headers=HEADERS, json={
        "labels": [{
            "class_id": 0, "x": W*0.25, "y": H*0.25,
            "w": W*0.5, "h": H*0.5
        }],
        "image_width": W,
        "image_height": H,
    })

# 5. 학습 시작 (patience 로 조기 종료 가능)
requests.post(f"{BASE_URL}/sessions/{sid}/train", headers=HEADERS, json={
    "base_model": "yolo11n.pt",
    "epochs": 100,        # 상한. 실제로는 patience 로 더 빨리 종료
    "imgsz": 640,
    "batch": 8,
    "patience": 20,       # 20 epoch 동안 mAP 개선 없으면 종료 (0=비활성)
    "lr0": 0.01,
    "optimizer": "auto",
})

# 6. 진행률 폴링
while True:
    status = requests.get(f"{BASE_URL}/sessions/{sid}/status", headers=HEADERS).json()
    print(f"  Epoch {status['current_epoch']}/{status['total_epochs']} "
          f"({status['progress_pct']}%)")
    if not status["running"]:
        if status["status"] == "done":
            print(f"✓ 학습 완료: mAP50={status['metrics'].get('mAP50', 0):.3f}")
        else:
            print(f"✗ 실패: {status['error']}")
        break
    time.sleep(5)

# 7. 모델 다운로드
for fmt in ["pt", "onnx"]:
    r = requests.get(f"{BASE_URL}/sessions/{sid}/download/{fmt}", headers=HEADERS)
    out_path = f"my_model.{fmt}"
    Path(out_path).write_bytes(r.content)
    print(f"✓ 저장: {out_path} ({len(r.content)/1024/1024:.1f} MB)")

# 8. 프리뷰 (임의 이미지로 추론 테스트)
test_img_id = uploaded[0]["image_id"]
preview = requests.get(f"{BASE_URL}/sessions/{sid}/preview/{test_img_id}?conf=0.3", headers=HEADERS).json()
print(f"검출 결과: {preview['total_detections']}건")
for det in preview["detections"]:
    print(f"  - {det['class_name']}: {det['confidence']*100:.1f}% "
          f"bbox={det['bbox']}")
```

---

### curl 예제 — 간단 스크립트

```bash
#!/bin/bash
BASE="http://platform.finetheai.com:18000/api/quick-train"
KEY="ftai_live_YOUR_API_KEY_HERE"

# 모든 요청에 X-API-Key 헤더 필수
CURL="curl -s -H X-API-Key:$KEY"

# 세션 생성
SID=$($CURL -X POST "$BASE/sessions" -H "Content-Type: application/json" \
      -d '{"name": "demo"}' | jq -r .session_id)
echo "세션: $SID"

# 이미지 업로드
$CURL -X POST "$BASE/sessions/$SID/images" \
     -F "files=@./img1.jpg" -F "files=@./img2.jpg"

# 클래스 추가
$CURL -X POST "$BASE/sessions/$SID/classes" \
     -H "Content-Type: application/json" -d '{"name": "defect"}'

# 학습 시작 (라벨링 후)
$CURL -X POST "$BASE/sessions/$SID/train" \
     -H "Content-Type: application/json" \
     -d '{"base_model": "yolo11n.pt", "epochs": 30}'

# 상태 확인
watch -n 3 "$CURL $BASE/sessions/$SID/status | jq '.current_epoch, .metrics'"

# 다운로드
$CURL -o model.pt   "$BASE/sessions/$SID/download/pt"
$CURL -o model.onnx "$BASE/sessions/$SID/download/onnx"
```

---

### Claude Code 에이전트 예제

```python
# Claude Code가 고객의 학습 요청을 자동 처리하는 예시

async def auto_train_pipeline(client_email: str, dataset_path: str):
    """고객 이미지를 받아 자동 학습 후 ONNX 모델 반환"""

    # 1. 세션 자동 생성 (이메일 연동)
    session = await api_post("/api/quick-train/sessions", {
        "name": f"auto_{datetime.now().strftime('%Y%m%d_%H%M')}",
        "user_email": client_email,
    })
    sid = session["session_id"]

    # 2. 이미지 일괄 업로드
    await upload_images_from_folder(sid, dataset_path)

    # 3. 사전학습된 분류기로 자동 라벨링 (선택)
    await auto_label_with_pretrained(sid)

    # 4. 학습 시작 + 완료 대기
    await api_post(f"/api/quick-train/sessions/{sid}/train", {
        "base_model": "yolo11s.pt", "epochs": 50,
    })

    while True:
        status = await api_get(f"/api/quick-train/sessions/{sid}/status")
        if not status["running"]:
            break
        await asyncio.sleep(10)

    # 5. 모델 다운로드 및 반환
    model_bytes = await api_get_raw(f"/api/quick-train/sessions/{sid}/download/onnx")
    return {
        "model_onnx": model_bytes,
        "metrics": status["metrics"],
        "session_id": sid,
    }
```

---

## 에러 코드

| 코드 | 원인 | 대응 |
|------|------|------|
| `400` | 필수 필드 누락, 잘못된 값, 학습 시작 불가 상태 | 요청 검증 |
| `404` | 세션/이미지/클래스 없음 | ID 확인 |
| `413` | 파일 크기 초과 (20MB) | 이미지 압축 |
| `500` | 서버 내부 오류 (학습 실패 등) | 로그 확인 |

**학습 시작 불가 케이스** (400):
- 라벨된 이미지 4장 미만
- 이미 학습 진행 중
- 클래스 미정의

---

## 제약사항

| 항목 | 제한 |
|------|------|
| 업로드 파일 크기 | 파일당 20MB |
| 지원 이미지 포맷 | JPG, JPEG, PNG, BMP |
| 학습 최소 이미지 | 4장 이상 라벨됨 |
| 동시 학습 | 세션당 1개, 서버 전체 순차 처리 |
| 익명 세션 보관 | 24시간 (이후 자동 삭제) |
| 로그인 세션 보관 | 무기한 |
| 최대 에포크 | 500 (기본 30, Early Stopping `patience` 로 자동 종료 권장) |
| 지원 기본 모델 | YOLOv8 (5) + YOLOv9 (5) + YOLOv10 (6) + YOLO11 (5) + RT-DETR (2) = **총 23종** |
| ONNX/TensorRT 변환 | 최초 1회 캐시, 재요청 시 즉시 |

---

## 관련 문서

- [Quick Training 로드맵](QUICK_TRAIN_ROADMAP.md)
- [Physical AI Platform 로드맵](PHYSICAL_AI_PLATFORM_ROADMAP.md)
- Swagger UI: `http://localhost:8000/docs` — 실시간 API 테스트 가능

---

## 피드백

- API 이슈: `platform.finetheai.com/support` 또는 Ops Board
- 문서 개선: `docs/QUICK_TRAIN_API.md` 수정 PR

*finetheAi Platform Team — 2026-04-15*
- **v1.3**: `base_model` YOLOv8/v9/v10/11 + RT-DETR 전 라인업 23종 지원, family별 비교표 분리
- **v1.2**: YOLO11 전체 라인업(n/s/m/l/x) 지원 + 모델별 파라미터·FLOPs·mAP 표 추가
- **v1.1**: `/defaults` 엔드포인트 및 `patience`/`lr0`/`optimizer` 파라미터 추가
