diff --git a/INDEX.md b/INDEX.md index 15bdd9a..2b79be2 100644 --- a/INDEX.md +++ b/INDEX.md @@ -171,7 +171,7 @@ DB 도메인별: | [esign/README.md](features/esign/README.md) | 전자서명 | | [equipment/README.md](features/equipment/README.md) | 설비관리 (API Phase 1 완료 + DB 스키마) | | [boards/README.md](features/boards/README.md) | 게시판 | -| [ai/README.md](features/ai/README.md) | AI 분석 | +| [ai/README.md](features/ai/README.md) | AI 기능 (리포트, 토큰사용량, 단가설정, R2 비용추적) | | [card-vehicle/README.md](features/card-vehicle/README.md) | 법인카드·차량 | | [settlement/README.md](features/settlement/README.md) | 정산 | | [sales/stock-production.md](features/sales/stock-production.md) | 재고생산관리 (내부 오더 방식, 수주 테이블 공유) | @@ -284,6 +284,7 @@ DB 도메인별: | [vehicle-api.md](frontend/api-specs/vehicle-api.md) | 차량관리 API 명세 (20개 엔드포인트: 차량목록, 차량일지, 정비이력, 사진) | | [stock-production-api.md](frontend/api-specs/stock-production-api.md) | 재고생산관리 API 명세 (기존 수주 API + STOCK 타입) | | [stock-receiving-changes-20260317.md](frontend/api-specs/stock-receiving-changes-20260317.md) | 입고관리/재고현황 변경사항 (로트번호 자동채번, 재고 조정 이동, API 스펙) | +| [ai-token-usage-api.md](frontend/api-specs/ai-token-usage-api.md) | AI 토큰사용량 API 명세 (2개 엔드포인트 + 화면 가이드 + 타입 정의) | | [vehicle-react-implementation.md](plans/vehicle-react-implementation.md) | 차량관리 React 구현 요청서 (3개 메뉴, 컴포넌트 구조, 타입 정의) | | [stock-production-react-request.md](plans/stock-production-react-request.md) | 재고생산관리 React 구현 요청서 (수주 화면 단순화, API 스펙 포함) | | [bending-lot-react-request.md](plans/bending-lot-react-request.md) | 절곡품 LOT 재고생산 React 구현 요청서 (캐스케이딩 드롭다운, LOT 자동생성, 취소 복원) | diff --git a/features/ai/README.md b/features/ai/README.md index 6cb0ec3..e81647e 100644 --- a/features/ai/README.md +++ b/features/ai/README.md @@ -1,58 +1,52 @@ -# AI 분석 리포트 (AI Report) +# AI 기능 > **상태**: API 구현 완료 -> **최종 갱신**: 2026-02-27 +> **최종 갱신**: 2026-03-18 --- ## 1. 개요 -Google Gemini API를 활용한 재무 분석 리포트 자동 생성 시스템. 테넌트의 비즈니스 데이터(지출, 매출, 매입, 입출금, 카드/계좌, 미수금)를 자동 수집하여 AI 기반 분석 리포트를 생성한다. +SAM 서비스의 AI 관련 기능을 통합 관리한다. AI 리포트 생성, 토큰 사용량 추적, 비용 계산을 포함한다. **핵심 기능:** -- 일간/주간/월간 재무 분석 리포트 자동 생성 -- Gemini 2.0 Flash 모델 연동 +- 일간/주간/월간 재무 분석 리포트 자동 생성 (Gemini) - 토큰 사용량 자동 추적 및 비용 계산 (USD/KRW) -- 전월 동기간 대비 변화율 분석 +- AI 모델별 단가 설정 및 환율 관리 +- 저장소(R2) 비용 추적 --- ## 2. 모델 -| 모델 | 테이블 | 설명 | Traits | -|------|--------|------|--------| -| `AiReport` | `ai_reports` | AI 리포트 (유형, 내용, 상태) | BelongsToTenant | -| `AiTokenUsage` | `ai_token_usages` | 토큰 사용량 추적 (모델, 메뉴, 비용) | BelongsToTenant | -| `AiPricingConfig` | `ai_pricing_configs` | AI 모델별 단가 설정 (3600초 캐시) | BelongsToTenant | -| `AiVoiceRecording` | `ai_voice_recordings` | 음성 녹음 (GCS URI, STT 결과) | BelongsToTenant | +| 모델 | 테이블 | 설명 | +|------|--------|------| +| `AiReport` | `ai_reports` | AI 리포트 (유형, 내용, 상태) | +| `AiTokenUsage` | `ai_token_usages` | 토큰 사용량 추적 (모델, 메뉴, 비용) | +| `AiPricingConfig` | `ai_pricing_configs` | AI 모델별 단가 설정 (1시간 캐시) | + +--- + +## 3. AI 리포트 + +Google Gemini API를 활용한 재무 분석 리포트 자동 생성 시스템. 테넌트의 비즈니스 데이터를 자동 수집하여 AI 기반 분석 리포트를 생성한다. **리포트 유형:** daily, weekly, monthly **리포트 상태:** pending → completed / failed -**분석 영역 (6개):** -- 지출 (Withdrawal), 매출 (Sale), 매입 (Purchase) -- 입출금 (Deposit), 카드/계좌, 미수금 (Receivable) +**분석 영역 (6개):** 지출, 매출, 매입, 입출금, 카드/계좌, 미수금 ---- - -## 3. 서비스 +### 3.1 서비스 | 서비스 | 주요 메서드 | 설명 | |--------|-----------|------| | `AiReportService` | list | 리포트 목록 (필터: report_type, status, 날짜) | | | show | 리포트 상세 | -| | generate | 리포트 생성 (데이터 수집 → Gemini API 호출 → 저장) | +| | generate | 리포트 생성 (데이터 수집 → Gemini API → 저장) | | | delete | 리포트 삭제 | -**내부 처리 흐름:** -``` -generate() → collectBusinessData() → buildPrompt() → callGeminiApi() → saveTokenUsage() -``` - ---- - -## 4. API 엔드포인트 +### 3.2 API 엔드포인트 | HTTP | URI | 설명 | |------|-----|------| @@ -63,20 +57,70 @@ generate() → collectBusinessData() → buildPrompt() → callGeminiApi() → s --- -## 5. FormRequest +## 4. AI 토큰 사용량 -| Request | 주요 검증 | -|---------|----------| -| `AiReportListRequest` | per_page, report_type (in), status (in), start_date, end_date | -| `AiReportGenerateRequest` | report_date (nullable, before_or_equal:today), report_type (nullable) | +테넌트별 AI 서비스 사용량을 추적하고 비용을 계산한다. 서비스 사용자가 **설정 > 토큰사용량** 메뉴에서 자기 테넌트의 사용 내역을 확인할 수 있다. + +### 4.1 헬퍼 + +| 클래스 | 메서드 | 용도 | +|--------|--------|------| +| `AiTokenHelper` | `saveGeminiUsage()` | Gemini API 호출 후 토큰 기록 | +| | `saveClaudeUsage()` | Claude API 호출 후 토큰 기록 | +| | `saveR2StorageUsage()` | R2 파일 업로드 비용 기록 | +| | `saveSttUsage()` | STT 사용 시간 비용 기록 | + +### 4.2 서비스 + +| 서비스 | 주요 메서드 | 설명 | +|--------|-----------|------| +| `AiTokenUsageService` | list | 사용량 목록 + 통계 (필터: 기간, 메뉴명) | +| | getPricing | 단가 설정 조회 (읽기 전용) | + +### 4.3 API 엔드포인트 + +| HTTP | URI | 설명 | +|------|-----|------| +| GET | `/v1/settings/ai-token-usage` | 토큰 사용량 목록 + 통계 | +| GET | `/v1/settings/ai-token-usage/pricing` | 단가 설정 조회 | + +> 상세 API 명세: [frontend/api-specs/ai-token-usage-api.md](../../frontend/api-specs/ai-token-usage-api.md) + +--- + +## 5. 단가 설정 (AiPricingConfig) + +| provider | model_name | 과금 방식 | 용도 | +|----------|-----------|----------|------| +| `gemini` | gemini-2.0-flash | 입력/출력 토큰당 | AI 리포트 등 | +| `claude` | claude-3-haiku | 입력/출력 토큰당 | Claude 호출 | +| `google-stt` | latest_long | 15초당 $0.009 | 음성인식 (MNG) | +| `google-gcs` | cloud-storage | 1000건당 $0.005 | GCS 저장소 (MNG) | +| `cloudflare-r2` | cloud-storage | 1M건당 $0.0045 | R2 저장소 (서비스) | + +> 환율 기본값: 1 USD = 1,400 KRW (활성 레코드의 `exchange_rate` 필드) +> 캐시: 1시간 TTL (`ai_pricing_{provider}`) + +--- + +## 6. MNG vs 서비스 차이 + +| 항목 | MNG (백오피스) | 서비스 (API+React) | +|------|---------------|-------------------| +| 테넌트 범위 | 전체 테넌트 조회 | 자기 테넌트만 | +| 저장소 | GCS | R2 | +| 단가 관리 | 편집 가능 | 조회만 | +| STT 추적 | 있음 | 해당 없음 | --- ## 관련 문서 - [DB 스키마 — 공통](../../system/database/commons.md) +- [프론트엔드 API 명세](../../frontend/api-specs/ai-token-usage-api.md) +- [이관 기획서](../../plans/ai-token-usage-service-migration.md) - Swagger: `/api-docs` → Reports 섹션 --- -**최종 업데이트**: 2026-02-27 +**최종 업데이트**: 2026-03-18 diff --git a/frontend/api-specs/ai-token-usage-api.md b/frontend/api-specs/ai-token-usage-api.md new file mode 100644 index 0000000..5c2eca8 --- /dev/null +++ b/frontend/api-specs/ai-token-usage-api.md @@ -0,0 +1,352 @@ +# AI 토큰사용량 API 명세 + +> **작성일**: 2026-03-18 +> **상태**: API 구현 완료, React 구현 대기 +> **메뉴 위치**: 설정 > 토큰사용량 + +--- + +## 1. 개요 + +테넌트 사용자가 자기 테넌트의 AI 사용량과 비용을 조회하는 API. MNG의 AI 토큰사용량과 동일한 역할이지만, 서비스 환경에 맞게 조정되었다. + +**MNG와의 차이:** +- 테넌트 필터 없음 (인증된 사용자의 테넌트로 자동 필터) +- 단가 설정은 읽기 전용 (MNG에서 관리) +- 저장소 비용은 R2 기준 (GCS 아닌) + +--- + +## 2. API 엔드포인트 + +### 2.1 토큰 사용량 목록 + 통계 + +``` +GET /api/v1/settings/ai-token-usage +``` + +**Query Parameters:** + +| 파라미터 | 타입 | 필수 | 설명 | +|---------|------|:----:|------| +| `start_date` | string (YYYY-MM-DD) | - | 시작일 | +| `end_date` | string (YYYY-MM-DD) | - | 종료일 (start_date 이후) | +| `menu_name` | string | - | 호출 메뉴명 필터 (드롭다운 선택값) | +| `per_page` | integer | - | 페이지당 건수 (기본 20, 범위 10~100) | +| `page` | integer | - | 페이지 번호 (기본 1) | + +**Response:** + +```json +{ + "success": true, + "data": { + "items": { + "data": [ + { + "id": 1, + "model": "gemini-2.0-flash", + "menu_name": "AI리포트-일간", + "prompt_tokens": 2500, + "completion_tokens": 800, + "total_tokens": 3300, + "cost_usd": "0.001320", + "cost_krw": "1.85", + "request_id": "a1b2c3d4-...", + "created_by": 5, + "creator": { + "id": 5, + "name": "홍길동" + }, + "created_at": "2026-03-18T10:30:00.000000Z", + "updated_at": "2026-03-18T10:30:00.000000Z" + } + ], + "current_page": 1, + "last_page": 3, + "per_page": 20, + "total": 45, + "from": 1, + "to": 20 + }, + "stats": { + "total_count": 45, + "total_prompt_tokens": 125000, + "total_completion_tokens": 38000, + "total_total_tokens": 163000, + "total_cost_usd": 0.0652, + "total_cost_krw": 91.28 + }, + "menu_names": [ + "AI리포트-일간", + "AI리포트-주간", + "AI리포트-월간", + "명함OCR" + ] + } +} +``` + +**핵심 포인트:** +- `stats`: 현재 필터 조건의 전체 집계 (페이지네이션 무관) +- `menu_names`: 해당 테넌트에서 사용한 고유 메뉴명 목록 (필터 드롭다운용) +- `creator`: 사용자 정보 (nullable — 시스템 호출 시 null) + +--- + +### 2.2 단가 설정 조회 + +``` +GET /api/v1/settings/ai-token-usage/pricing +``` + +**Response:** + +```json +{ + "success": true, + "data": { + "pricing": [ + { + "id": 1, + "provider": "claude", + "model_name": "claude-3-haiku", + "input_price_per_million": "0.2500", + "output_price_per_million": "1.2500", + "unit_price": null, + "unit_description": null, + "exchange_rate": "1400.00", + "is_active": true, + "description": "Claude 3 Haiku" + }, + { + "id": 5, + "provider": "cloudflare-r2", + "model_name": "cloud-storage", + "input_price_per_million": "0.0000", + "output_price_per_million": "0.0000", + "unit_price": "0.004500", + "unit_description": "per 1,000,000 Class A operations", + "exchange_rate": "1400.00", + "is_active": true, + "description": "Cloudflare R2 Storage (S3 compatible)" + }, + { + "id": 2, + "provider": "gemini", + "model_name": "gemini-2.0-flash", + "input_price_per_million": "0.1000", + "output_price_per_million": "0.4000", + "unit_price": null, + "unit_description": null, + "exchange_rate": "1400.00", + "is_active": true, + "description": "Gemini 2.0 Flash" + } + ], + "exchange_rate": 1400.0 + } +} +``` + +**pricing 레코드 구분:** +- `input_price_per_million` / `output_price_per_million`: AI 토큰 기반 (gemini, claude) +- `unit_price` / `unit_description`: 단위 기반 (R2 저장소, STT) +- 두 유형이 동시에 null이 아닌 경우는 없음 + +--- + +## 3. 화면 구성 가이드 + +### 3.1 메뉴 위치 + +``` +설정 (Settings) +├── ... +└── 토큰사용량 (/settings/ai-token-usage) +``` + +### 3.2 UI 구조 + +``` +┌─────────────────────────────────────────────────────┐ +│ AI 토큰 사용량 [단가 확인] │ +├─────────────────────────────────────────────────────┤ +│ │ +│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ +│ │총 호출수│ │입력토큰│ │출력토큰│ │비용 USD│ │비용 KRW│ │ +│ │ 45건 │ │ 125K │ │ 38K │ │ $0.065│ │ 91원 │ │ +│ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ │ +│ │ +│ 시작일[____] 종료일[____] 메뉴[▼____] [조회][초기화]│ +│ │ +│ ┌────┬──────┬──────┬──────┬──────┬───────┬──────┐ │ +│ │No. │ 일시 │카테고리│ 메뉴 │ 모델 │ 토큰 │ 비용 │ │ +│ ├────┼──────┼──────┼──────┼──────┼───────┼──────┤ │ +│ │ 1 │03-18│AI리포트│ 일간 │gemini│ 3,300│$0.001│ │ +│ │ 2 │03-17│명함OCR│ 인식 │gemini│ 1,200│$0.000│ │ +│ └────┴──────┴──────┴──────┴──────┴───────┴──────┘ │ +│ │ +│ ◀ 1 2 3 ▶ 20건 / 총 45건 │ +└─────────────────────────────────────────────────────┘ +``` + +### 3.3 통계 카드 (5개) + +| 카드 | 데이터 소스 | 포맷 | +|------|-----------|------| +| 총 호출 수 | `stats.total_count` | `toLocaleString()` | +| 입력 토큰 | `stats.total_prompt_tokens` | `toLocaleString()` | +| 출력 토큰 | `stats.total_completion_tokens` | `toLocaleString()` | +| 총 비용 (USD) | `stats.total_cost_usd` | `$0.0000` (소수점 4자리) | +| 총 비용 (KRW) | `stats.total_cost_krw` | `0원` (정수 반올림) | + +### 3.4 테이블 컬럼 + +| 컬럼 | 필드 | 정렬 | 비고 | +|------|------|:----:|------| +| No. | 자동 번호 | 중앙 | | +| 사용일시 | `created_at` | 좌 | `YYYY-MM-DD HH:mm` 포맷 | +| 카테고리 | `menu_name` 기반 | 좌 | 카테고리 매핑 함수 적용 | +| 호출메뉴 | `menu_name` | 좌 | | +| 모델 | `model` | 좌 | | +| 입력토큰 | `prompt_tokens` | 우 | 천단위 콤마 | +| 출력토큰 | `completion_tokens` | 우 | 천단위 콤마 | +| 전체토큰 | `total_tokens` | 우 | 천단위 콤마 | +| 비용(USD) | `cost_usd` | 우 | `$0.0000` | +| 비용(KRW) | `cost_krw` | 우 | `0원` | +| 사용자 | `creator.name` | 좌 | null이면 `-` 표시 | + +### 3.5 카테고리 매핑 + +`menu_name` 필드를 기반으로 카테고리를 분류한다: + +```typescript +function getCategory(menuName: string): string { + if (!menuName) return '-'; + if (menuName.startsWith('AI리포트')) return 'AI리포트'; + if (menuName.includes('명함')) return '명함OCR'; + if (menuName.includes('사업자등록증')) return 'OCR'; + if (menuName.startsWith('파일업로드')) return '파일저장소'; + return menuName; +} +``` + +### 3.6 필터 + +- **서버 사이드 필터링** (클라이언트 필터링 아님) +- 시작일 / 종료일: date picker +- 메뉴: `menu_names` 배열로 드롭다운 구성 +- 조회 버튼 클릭 시 API 재호출 +- 초기화 버튼: 모든 필터 초기화 후 재호출 + +### 3.7 단가 확인 모달 (읽기 전용) + +`[단가 확인]` 버튼 클릭 시 모달 표시. `GET /pricing` API 호출. + +**구성:** +- AI 토큰 단가 테이블 (provider, model, 입력단가, 출력단가) +- 저장소/서비스 단가 테이블 (provider, 단위가격, 단위설명) +- 현재 환율 표시 +- "단가 변경은 관리자에게 문의하세요" 안내 문구 + +--- + +## 4. TypeScript 타입 정의 + +```typescript +interface AiTokenUsageItem { + id: number; + model: string; + menu_name: string; + prompt_tokens: number; + completion_tokens: number; + total_tokens: number; + cost_usd: string; + cost_krw: string; + request_id: string; + created_by: number | null; + creator: { id: number; name: string } | null; + created_at: string; +} + +interface AiTokenUsageStats { + total_count: number; + total_prompt_tokens: number; + total_completion_tokens: number; + total_total_tokens: number; + total_cost_usd: number; + total_cost_krw: number; +} + +interface AiTokenUsageListResponse { + items: PaginatedResponse; + stats: AiTokenUsageStats; + menu_names: string[]; +} + +interface AiPricingItem { + id: number; + provider: string; + model_name: string; + input_price_per_million: string | null; + output_price_per_million: string | null; + unit_price: string | null; + unit_description: string | null; + exchange_rate: string; + is_active: boolean; + description: string | null; +} + +interface AiPricingResponse { + pricing: AiPricingItem[]; + exchange_rate: number; +} +``` + +--- + +## 5. 컴포넌트 구조 + +``` +src/app/[locale]/(protected)/settings/ai-token-usage/ +└── page.tsx + +src/components/settings/AiTokenUsage/ +├── index.tsx ← 메인 (UniversalListPage 패턴) +├── actions.ts ← Server Actions +├── types.ts ← TypeScript 타입 +├── AiTokenUsageConfig.ts ← 카테고리 매핑, 포맷 유틸 +└── PricingModal.tsx ← 단가 조회 모달 (읽기 전용) +``` + +--- + +## 6. Server Actions + +```typescript +// actions.ts +'use server'; + +export async function getAiTokenUsageList(params?: { + start_date?: string; + end_date?: string; + menu_name?: string; + per_page?: number; + page?: number; +}): Promise> + +export async function getAiTokenUsagePricing(): + Promise> +``` + +--- + +## 관련 문서 + +- [features/ai/README.md](../../features/ai/README.md) — AI 기능 전체 개요 +- [이관 기획서](../../plans/ai-token-usage-service-migration.md) — MNG→서비스 이관 상세 기획 + +--- + +**최종 업데이트**: 2026-03-18