# 실시간 AI 제안 스트리밍 개발 가이드 ## 📋 개요 회의 진행 중 STT로 변환된 텍스트를 실시간으로 분석하여 논의사항/결정사항을 AI가 제안하는 기능 **개발 일시**: 2025-10-24 **개발자**: AI Specialist (서연) **사용 기술**: Claude API, Azure Event Hub, Redis, SSE (Server-Sent Events) --- ## 🎯 구현된 기능 ### ✅ **1. Claude API 클라이언트** - **파일**: `ai/src/main/java/com/unicorn/hgzero/ai/infra/client/ClaudeApiClient.java` - **기능**: - Anthropic Claude API (claude-3-5-sonnet) 호출 - 실시간 텍스트 분석하여 논의사항/결정사항 추출 - JSON 응답 파싱 및 DTO 변환 ### ✅ **2. Azure Event Hub Consumer** - **파일**: `ai/src/main/java/com/unicorn/hgzero/ai/infra/config/EventHubConfig.java` - **기능**: - STT Service의 `TranscriptSegmentReady` 이벤트 구독 - 실시간 음성 변환 텍스트 수신 - SuggestionService로 전달하여 AI 분석 트리거 ### ✅ **3. 실시간 텍스트 축적 로직** - **파일**: `ai/src/main/java/com/unicorn/hgzero/ai/biz/service/SuggestionService.java` - **메서드**: `processRealtimeTranscript()` - **기능**: - Redis Sorted Set을 이용한 슬라이딩 윈도우 (최근 5분 텍스트 유지) - 임계값 도달 시 자동 AI 분석 (10개 세그먼트 = 약 100-200자) ### ✅ **4. SSE 스트리밍** - **API**: `GET /api/suggestions/meetings/{meetingId}/stream` - **Controller**: `SuggestionController:111` - **기능**: - Server-Sent Events로 실시간 AI 제안사항 전송 - 멀티캐스트 지원 (여러 클라이언트 동시 연결) - 자동 리소스 정리 (연결 종료 시) --- ## 🏗️ 아키텍처 ``` [회의 진행 중] ↓ ┌─────────────────────────────────────┐ │ 1. STT Service (Azure Speech) │ │ - 음성 → 텍스트 실시간 변환 │ └─────────────────────────────────────┘ ↓ Azure Event Hub ↓ (TranscriptSegmentReady Event) ↓ ┌─────────────────────────────────────┐ │ 2. AI Service (Event Hub Consumer) │ │ - 이벤트 수신 │ │ - Redis에 텍스트 축적 │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ 3. Redis (슬라이딩 윈도우) │ │ - 최근 5분 텍스트 유지 │ │ - 임계값 체크 (10 segments) │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ 4. Claude API (Anthropic) │ │ - 누적 텍스트 분석 │ │ - 논의사항/결정사항 추출 │ └─────────────────────────────────────┘ ↓ ┌─────────────────────────────────────┐ │ 5. SSE 스트리밍 │ │ - 클라이언트에 실시간 전송 │ └─────────────────────────────────────┘ ``` --- ## ⚙️ 설정 방법 ### **1. Claude API 키 발급** 1. [Anthropic Console](https://console.anthropic.com/) 접속 2. API Keys → Create Key 3. 생성된 API Key 복사 ### **2. 환경 변수 설정** **application.yml** 또는 **환경 변수**에 추가: ```bash # Claude API 설정 export CLAUDE_API_KEY="sk-ant-api03-..." export CLAUDE_MODEL="claude-3-5-sonnet-20241022" export CLAUDE_MAX_TOKENS="2000" export CLAUDE_TEMPERATURE="0.3" # Azure Event Hub 설정 (이미 설정됨) export AZURE_EVENTHUB_CONNECTION_STRING="Endpoint=sb://hgzero-eventhub-ns.servicebus.windows.net/;..." export AZURE_EVENTHUB_NAME="hgzero-eventhub-name" export AZURE_EVENTHUB_CONSUMER_GROUP_TRANSCRIPT="ai-transcript-group" # Redis 설정 (이미 설정됨) export REDIS_HOST="20.249.177.114" export REDIS_PORT="6379" export REDIS_PASSWORD="Hi5Jessica!" export REDIS_DATABASE="4" ``` ### **3. 의존성 확인** `ai/build.gradle`에 이미 추가됨: ```gradle dependencies { // Common module implementation project(':common') // Redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' // Anthropic Claude SDK implementation 'com.anthropic:anthropic-sdk-java:0.1.0' // Azure Event Hubs implementation "com.azure:azure-messaging-eventhubs:${azureEventHubsVersion}" implementation "com.azure:azure-messaging-eventhubs-checkpointstore-blob:${azureEventHubsCheckpointVersion}" // Spring WebFlux for SSE streaming implementation 'org.springframework.boot:spring-boot-starter-webflux' } ``` --- ## 🚀 실행 방법 ### **1. AI Service 빌드** ```bash cd /Users/jominseo/HGZero ./gradlew :ai:build -x test ``` ### **2. AI Service 실행** ```bash cd ai ./gradlew bootRun ``` 또는 IntelliJ Run Configuration 사용 ### **3. 클라이언트 테스트 (회의진행.html)** ```javascript // SSE 연결 const meetingId = "MTG-2025-001"; const eventSource = new EventSource(`/api/suggestions/meetings/${meetingId}/stream`); // AI 제안사항 수신 eventSource.addEventListener('ai-suggestion', (event) => { const suggestion = JSON.parse(event.data); console.log('실시간 AI 제안:', suggestion); // 논의사항 UI 업데이트 suggestion.discussionTopics.forEach(topic => { addDiscussionToUI(topic); }); // 결정사항 UI 업데이트 suggestion.decisions.forEach(decision => { addDecisionToUI(decision); }); }); // 에러 처리 eventSource.onerror = (error) => { console.error('SSE 연결 오류:', error); eventSource.close(); }; // 회의 종료 시 연결 종료 function endMeeting() { eventSource.close(); } ``` --- ## 📊 데이터 흐름 ### **Event Hub 이벤트 구조** ```json { "recordingId": "REC-20250123-001", "meetingId": "MTG-2025-001", "transcriptId": "TRS-SEG-001", "text": "안녕하세요, 오늘 회의를 시작하겠습니다.", "timestamp": 1234567890, "confidence": 0.92, "eventTime": "2025-01-23T10:30:00Z" } ``` ### **Claude API 응답 구조** ```json { "discussions": [ { "topic": "보안 요구사항 검토", "reason": "안건에 포함되어 있으나 아직 논의되지 않음", "priority": "HIGH" } ], "decisions": [ { "content": "React로 프론트엔드 개발", "confidence": 0.9, "extractedFrom": "프론트엔드는 React로 개발하기로 했습니다" } ] } ``` ### **SSE 스트리밍 응답** ``` event: ai-suggestion id: 12345 data: {"discussionTopics":[...],"decisions":[...]} event: ai-suggestion id: 12346 data: {"discussionTopics":[...],"decisions":[...]} ``` --- ## 🔧 주요 설정값 | 설정 | 값 | 설명 | |------|-----|------| | `MIN_SEGMENTS_FOR_ANALYSIS` | 10 | AI 분석 시작 임계값 (세그먼트 수) | | `TEXT_RETENTION_MS` | 300000 (5분) | Redis 텍스트 보관 기간 | | `CLAUDE_MODEL` | claude-3-5-sonnet-20241022 | 사용 Claude 모델 | | `CLAUDE_MAX_TOKENS` | 2000 | 최대 응답 토큰 수 | | `CLAUDE_TEMPERATURE` | 0.3 | 창의성 수준 (0-1) | --- ## 🐛 트러블슈팅 ### **1. Event Hub 연결 실패** **증상**: `Event Hub Processor 시작 실패` 로그 **해결**: ```bash # 연결 문자열 확인 echo $AZURE_EVENTHUB_CONNECTION_STRING # Consumer Group 확인 echo $AZURE_EVENTHUB_CONSUMER_GROUP_TRANSCRIPT ``` ### **2. Claude API 호출 실패** **증상**: `Claude API 호출 실패` 로그 **해결**: ```bash # API 키 확인 echo $CLAUDE_API_KEY # 네트워크 연결 확인 curl -X POST https://api.anthropic.com/v1/messages \ -H "x-api-key: $CLAUDE_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" ``` ### **3. Redis 연결 실패** **증상**: `Unable to connect to Redis` 로그 **해결**: ```bash # Redis 연결 테스트 redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD ping # 응답: PONG ``` ### **4. SSE 스트림 끊김** **증상**: 클라이언트에서 연결이 자주 끊김 **해결**: ```javascript // 자동 재연결 로직 추가 function connectSSE(meetingId) { const eventSource = new EventSource(`/api/suggestions/meetings/${meetingId}/stream`); eventSource.onerror = (error) => { console.error('SSE 연결 오류, 5초 후 재연결...'); eventSource.close(); setTimeout(() => connectSSE(meetingId), 5000); }; return eventSource; } ``` --- ## 📈 성능 최적화 ### **1. Redis 메모리 관리** - 슬라이딩 윈도우로 최근 5분만 유지 - 회의 종료 시 자동 삭제 - TTL 설정 고려 (향후 추가) ### **2. Claude API 호출 최적화** - 임계값 도달 시에만 호출 (불필요한 호출 방지) - 비동기 처리로 응답 대기 시간 최소화 - 에러 발생 시 빈 응답 반환 (서비스 중단 방지) ### **3. SSE 연결 관리** - 멀티캐스트로 여러 클라이언트 동시 지원 - 연결 종료 시 자동 리소스 정리 - Backpressure 버퍼링으로 과부하 방지 --- ## 🔜 향후 개발 계획 ### **Phase 2: AI 정확도 향상** - [ ] 회의 안건 기반 맥락 분석 - [ ] 과거 회의록 참조 (RAG) - [ ] 조직별 용어 사전 통합 ### **Phase 3: 성능 개선** - [ ] Redis TTL 자동 설정 - [ ] Claude API 캐싱 전략 - [ ] 배치 분석 옵션 추가 ### **Phase 4: 모니터링** - [ ] AI 제안 정확도 측정 - [ ] 응답 시간 메트릭 수집 - [ ] 사용량 대시보드 구축 --- ## 📚 참고 자료 - [Anthropic Claude API 문서](https://docs.anthropic.com/claude/reference/messages) - [Azure Event Hubs 문서](https://learn.microsoft.com/en-us/azure/event-hubs/) - [Server-Sent Events 스펙](https://html.spec.whatwg.org/multipage/server-sent-events.html) - [Redis Sorted Sets 가이드](https://redis.io/docs/data-types/sorted-sets/) --- ## ✅ 체크리스트 - [x] Claude API 클라이언트 구현 - [x] Azure Event Hub Consumer 구현 - [x] Redis 슬라이딩 윈도우 구현 - [x] SSE 스트리밍 구현 - [x] SuggestionService 통합 - [ ] Claude API 키 발급 및 설정 - [ ] 통합 테스트 (STT → AI → SSE) - [ ] 프론트엔드 연동 테스트 --- **개발 완료**: 2025-10-24 **다음 단계**: Claude API 키 발급 및 통합 테스트