mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 16:06:23 +00:00
- AI 서비스 CORS 설정 업데이트 - 회의 진행 프로토타입 수정 - 빌드 리포트 및 로그 파일 업데이트 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
386 lines
11 KiB
Markdown
386 lines
11 KiB
Markdown
# 실시간 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 키 발급 및 통합 테스트
|