mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 10:16:24 +00:00
[포트 통일] - ai-python 서비스 포트를 8087로 완전 통일 - 모든 문서에서 8086 참조 제거 - README.md, 개발 가이드 문서 전부 8087로 업데이트 변경 파일: - ai-python/README.md - develop/dev/ai-frontend-integration-guide.md - develop/dev/dev-*.md (5개 파일) [meeting-ai 테스트] 테스트 완료 항목: ✓ 회의록 통합 및 취합 ✓ AI 한줄 요약/상세 요약 생성 ✓ 회의 전체 결정사항 추출 ✓ TODO 자동 추출 (9개) ✓ 통계 정보 생성 ✓ 주요 키워드 추출 (10개) 테스트 파일: - develop/test/meeting-ai-test-data.json (테스트 데이터) - develop/test/consolidate-response.json (API 응답) - develop/test/meeting-ai-test-result.md (상세 결과서)
467 lines
13 KiB
Markdown
467 lines
13 KiB
Markdown
# STT 서비스 배치 방식 구현 완료 보고서
|
|
|
|
**작성일**: 2025-10-27
|
|
**작성자**: 준호 (Backend Developer)
|
|
|
|
---
|
|
|
|
## 📋 개요
|
|
|
|
**STT 서비스를 배치 처리 방식으로 구현 완료**
|
|
|
|
- **핵심**: 5초마다 Redis에 축적된 오디오를 배치 처리하여 Azure Speech로 텍스트 변환
|
|
- **장점**: 비용 절감, 문맥 이해 향상, AI 분석 효율 증가
|
|
- **기술**: Java/Spring Boot, Azure Speech SDK, Redis Stream, WebSocket
|
|
|
|
---
|
|
|
|
## 🏗️ 최종 아키텍처
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ Frontend (회의 화면) │
|
|
│ - 오디오 캡처 (매초) │
|
|
│ - WebSocket으로 실시간 전송 │
|
|
└────────────────────┬─────────────────────────────────────┘
|
|
│ WebSocket (ws://localhost:8084/ws/audio)
|
|
↓
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ STT Service (Java/Spring Boot) │
|
|
│ 포트: 8084 │
|
|
├──────────────────────────────────────────────────────────┤
|
|
│ 1. AudioWebSocketHandler │
|
|
│ - WebSocket 메시지 수신 (JSON/Binary) │
|
|
│ - Base64 디코딩 │
|
|
│ ↓ │
|
|
│ 2. AudioBufferService │
|
|
│ - Redis Stream에 오디오 청크 저장 │
|
|
│ - Key: audio:stream:{meetingId} │
|
|
│ - TTL: 1분 │
|
|
│ ↓ │
|
|
│ 3. Redis Stream (버퍼) │
|
|
│ - 오디오 청크 임시 저장 │
|
|
│ - 5초 분량 축적 │
|
|
│ ↓ │
|
|
│ 4. AudioBatchProcessor (@Scheduled) │
|
|
│ - 5초마다 실행 │
|
|
│ - Redis에서 청크 조회 → 병합 │
|
|
│ - Azure Speech API 호출 │
|
|
│ - TranscriptSegment DB 저장 │
|
|
│ - Event Hub 이벤트 발행 │
|
|
└────────────────────┬─────────────────────────────────────┘
|
|
│ Event Hub (transcription-events)
|
|
↓
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ AI Service (Python/FastAPI) │
|
|
│ 포트: 8087 │
|
|
│ - Event Hub에서 텍스트 수신 │
|
|
│ - Redis에 텍스트 축적 (슬라이딩 윈도우 5분) │
|
|
│ - Claude API 분석 → SSE로 프론트엔드 전송 │
|
|
└──────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 구현 컴포넌트
|
|
|
|
### 1. Redis Stream 설정
|
|
|
|
**파일**: `stt/src/main/java/com/unicorn/hgzero/stt/config/RedisStreamConfig.java`
|
|
|
|
```java
|
|
@Configuration
|
|
public class RedisStreamConfig {
|
|
@Bean(name = "audioRedisTemplate")
|
|
public RedisTemplate<String, byte[]> audioRedisTemplate(...) {
|
|
// 오디오 데이터 저장용
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. AudioChunkDto
|
|
|
|
**파일**: `stt/src/main/java/com/unicorn/hgzero/stt/dto/AudioChunkDto.java`
|
|
|
|
```java
|
|
@Data
|
|
@Builder
|
|
public class AudioChunkDto {
|
|
private String meetingId;
|
|
private byte[] audioData; // 오디오 데이터
|
|
private Long timestamp; // 타임스탬프 (밀리초)
|
|
private Integer chunkIndex; // 순서
|
|
private String format; // audio/webm
|
|
private Integer sampleRate; // 16000
|
|
}
|
|
```
|
|
|
|
### 3. AudioBufferService
|
|
|
|
**파일**: `stt/src/main/java/com/unicorn/hgzero/stt/service/AudioBufferService.java`
|
|
|
|
**핵심 기능**:
|
|
- `bufferAudioChunk()`: 오디오 청크를 Redis Stream에 저장
|
|
- `getAudioChunks()`: 회의별 오디오 청크 조회
|
|
- `mergeAudioChunks()`: 청크 병합 (5초 분량)
|
|
- `clearProcessedChunks()`: 처리 완료 후 Redis 정리
|
|
|
|
### 4. AudioWebSocketHandler
|
|
|
|
**파일**: `stt/src/main/java/com/unicorn/hgzero/stt/controller/AudioWebSocketHandler.java`
|
|
|
|
**핵심 기능**:
|
|
- WebSocket 연결 관리
|
|
- 텍스트 메시지 처리 (JSON 형식)
|
|
- `type: "start"`: 녹음 시작
|
|
- `type: "chunk"`: 오디오 청크 (Base64)
|
|
- `type: "stop"`: 녹음 종료
|
|
- 바이너리 메시지 처리 (직접 오디오 데이터)
|
|
|
|
### 5. AzureSpeechService
|
|
|
|
**파일**: `stt/src/main/java/com/unicorn/hgzero/stt/service/AzureSpeechService.java`
|
|
|
|
**핵심 기능**:
|
|
- `recognizeAudio()`: 오디오 데이터를 텍스트로 변환
|
|
- Azure Speech SDK 사용
|
|
- 시뮬레이션 모드 지원 (API 키 없을 때)
|
|
|
|
**설정**:
|
|
```yaml
|
|
azure:
|
|
speech:
|
|
subscription-key: ${AZURE_SPEECH_SUBSCRIPTION_KEY:}
|
|
region: ${AZURE_SPEECH_REGION:eastus}
|
|
language: ko-KR
|
|
```
|
|
|
|
### 6. AudioBatchProcessor
|
|
|
|
**파일**: `stt/src/main/java/com/unicorn/hgzero/stt/service/AudioBatchProcessor.java`
|
|
|
|
**핵심 기능**:
|
|
- `@Scheduled(fixedDelay = 5000)`: 5초마다 실행
|
|
- 활성 회의 목록 조회
|
|
- 각 회의별 오디오 처리:
|
|
1. Redis에서 오디오 청크 조회
|
|
2. 청크 병합 (5초 분량)
|
|
3. Azure Speech API 호출
|
|
4. TranscriptSegment DB 저장
|
|
5. Event Hub 이벤트 발행
|
|
6. Redis 정리
|
|
|
|
---
|
|
|
|
## 📊 데이터 흐름
|
|
|
|
### 1. 오디오 수신 (실시간)
|
|
|
|
**Frontend → STT Service:**
|
|
```javascript
|
|
// WebSocket 연결
|
|
const ws = new WebSocket('ws://localhost:8084/ws/audio');
|
|
|
|
// 녹음 시작
|
|
ws.send(JSON.stringify({
|
|
type: 'start',
|
|
meetingId: 'meeting-123'
|
|
}));
|
|
|
|
// 오디오 청크 전송 (매초)
|
|
ws.send(JSON.stringify({
|
|
type: 'chunk',
|
|
meetingId: 'meeting-123',
|
|
audioData: base64AudioData,
|
|
timestamp: Date.now(),
|
|
chunkIndex: 0,
|
|
format: 'audio/webm',
|
|
sampleRate: 16000
|
|
}));
|
|
```
|
|
|
|
**STT Service:**
|
|
```java
|
|
// AudioWebSocketHandler
|
|
handleTextMessage() {
|
|
AudioChunkDto chunk = parseMessage(message);
|
|
audioBufferService.bufferAudioChunk(chunk);
|
|
}
|
|
|
|
// AudioBufferService
|
|
bufferAudioChunk(chunk) {
|
|
redis.opsForStream().add("audio:stream:meeting-123", chunk);
|
|
}
|
|
```
|
|
|
|
### 2. 배치 처리 (5초마다)
|
|
|
|
```java
|
|
@Scheduled(fixedDelay = 5000)
|
|
public void processAudioBatch() {
|
|
// 1. 활성 회의 조회
|
|
Set<String> meetings = audioBufferService.getActiveMeetings();
|
|
|
|
for (String meetingId : meetings) {
|
|
// 2. 오디오 청크 조회 (최근 5초)
|
|
List<AudioChunkDto> chunks = audioBufferService.getAudioChunks(meetingId);
|
|
|
|
// 3. 청크 병합
|
|
byte[] mergedAudio = audioBufferService.mergeAudioChunks(chunks);
|
|
|
|
// 4. Azure Speech 인식
|
|
RecognitionResult result = azureSpeechService.recognizeAudio(mergedAudio);
|
|
|
|
// 5. DB 저장
|
|
saveTranscriptSegment(meetingId, result);
|
|
|
|
// 6. Event Hub 발행
|
|
publishTranscriptionEvent(meetingId, result);
|
|
|
|
// 7. Redis 정리
|
|
audioBufferService.clearProcessedChunks(meetingId);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Event Hub 이벤트 발행
|
|
|
|
```java
|
|
TranscriptionEvent.SegmentCreated event = TranscriptionEvent.SegmentCreated.of(
|
|
segmentId,
|
|
meetingId,
|
|
result.getText(),
|
|
"참석자",
|
|
LocalDateTime.now(),
|
|
5.0, // duration
|
|
result.getConfidence(),
|
|
warningFlag
|
|
);
|
|
|
|
eventPublisher.publishAsync("transcription-events", event);
|
|
```
|
|
|
|
### 4. AI 서비스 수신 (Python)
|
|
|
|
```python
|
|
# AI Service (Python)
|
|
async def on_event(partition_context, event):
|
|
event_data = json.loads(event.body_as_str())
|
|
|
|
if event_data["eventType"] == "TranscriptSegmentReady":
|
|
meetingId = event_data["meetingId"]
|
|
text = event_data["text"]
|
|
|
|
# Redis에 텍스트 축적
|
|
await redis_service.add_transcript_segment(meetingId, text, timestamp)
|
|
|
|
# Claude API 분석 트리거
|
|
await analyze_and_emit_suggestions(meetingId)
|
|
```
|
|
|
|
---
|
|
|
|
## ⚙️ 설정 및 실행
|
|
|
|
### 1. 환경 변수 설정
|
|
|
|
**IntelliJ 실행 프로파일** (`.run/STT.run.xml`):
|
|
```xml
|
|
<option name="env">
|
|
<map>
|
|
<entry key="AZURE_SPEECH_SUBSCRIPTION_KEY" value="your-key-here"/>
|
|
<entry key="AZURE_SPEECH_REGION" value="eastus"/>
|
|
<entry key="REDIS_HOST" value="20.249.177.114"/>
|
|
<entry key="REDIS_PORT" value="6379"/>
|
|
<entry key="REDIS_PASSWORD" value="Hi5Jessica!"/>
|
|
<entry key="EVENTHUB_CONNECTION_STRING" value="Endpoint=sb://..."/>
|
|
</map>
|
|
</option>
|
|
```
|
|
|
|
### 2. 서비스 시작
|
|
|
|
```bash
|
|
# IntelliJ에서 STT 실행 프로파일 실행
|
|
# 또는
|
|
|
|
# Gradle로 실행
|
|
cd stt
|
|
./gradlew bootRun
|
|
```
|
|
|
|
### 3. 로그 확인
|
|
|
|
```bash
|
|
tail -f stt/logs/stt.log
|
|
```
|
|
|
|
**예상 로그**:
|
|
```
|
|
2025-10-27 12:00:00 - Azure Speech Service 초기화 완료 - Region: eastus, Language: ko-KR
|
|
2025-10-27 12:00:05 - WebSocket 연결 성공 - sessionId: abc123
|
|
2025-10-27 12:00:10 - 오디오 청크 버퍼링 완료 - meetingId: meeting-123
|
|
2025-10-27 12:00:15 - 배치 처리 시작 - 활성 회의: 1개
|
|
2025-10-27 12:00:15 - 음성 인식 성공: 신제품 개발 일정에 대해 논의하고 있습니다.
|
|
2025-10-27 12:00:15 - Event Hub 이벤트 발행 완료
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 테스트 방법
|
|
|
|
### 1. WebSocket 테스트 (JavaScript)
|
|
|
|
```javascript
|
|
const ws = new WebSocket('ws://localhost:8084/ws/audio');
|
|
|
|
ws.onopen = () => {
|
|
console.log('WebSocket 연결 성공');
|
|
|
|
// 녹음 시작
|
|
ws.send(JSON.stringify({
|
|
type: 'start',
|
|
meetingId: 'test-meeting'
|
|
}));
|
|
|
|
// 5초 동안 오디오 청크 전송 (시뮬레이션)
|
|
for (let i = 0; i < 5; i++) {
|
|
setTimeout(() => {
|
|
ws.send(JSON.stringify({
|
|
type: 'chunk',
|
|
meetingId: 'test-meeting',
|
|
audioData: 'dGVzdCBhdWRpbyBkYXRh', // Base64
|
|
timestamp: Date.now(),
|
|
chunkIndex: i
|
|
}));
|
|
}, i * 1000);
|
|
}
|
|
|
|
// 10초 후 녹음 종료
|
|
setTimeout(() => {
|
|
ws.send(JSON.stringify({
|
|
type: 'stop'
|
|
}));
|
|
}, 10000);
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
console.log('응답:', JSON.parse(event.data));
|
|
};
|
|
```
|
|
|
|
### 2. Redis 확인
|
|
|
|
```bash
|
|
redis-cli -h 20.249.177.114 -p 6379 -a Hi5Jessica!
|
|
|
|
# 활성 회의 목록
|
|
SMEMBERS active:meetings
|
|
|
|
# 오디오 스트림 확인
|
|
XRANGE audio:stream:test-meeting - +
|
|
|
|
# 스트림 길이
|
|
XLEN audio:stream:test-meeting
|
|
```
|
|
|
|
### 3. 데이터베이스 확인
|
|
|
|
```sql
|
|
-- 텍스트 세그먼트 조회
|
|
SELECT * FROM transcript_segment
|
|
WHERE recording_id = 'test-meeting'
|
|
ORDER BY timestamp DESC
|
|
LIMIT 10;
|
|
```
|
|
|
|
---
|
|
|
|
## 📈 성능 특성
|
|
|
|
| 항목 | 값 | 비고 |
|
|
|------|-----|------|
|
|
| **배치 주기** | 5초 | @Scheduled(fixedDelay = 5000) |
|
|
| **지연 시간** | 최대 5초 | 사용자 경험에 영향 없음 |
|
|
| **Azure API 호출 빈도** | 1/5초 | 실시간 방식 대비 1/5 감소 |
|
|
| **Redis TTL** | 1분 | 처리 지연 대비 |
|
|
| **오디오 청크 크기** | 가변 | 프론트엔드 전송 주기에 따름 |
|
|
|
|
---
|
|
|
|
## ✅ 장점
|
|
|
|
1. **비용 최적화**
|
|
- Azure Speech API 호출 빈도 1/5 감소
|
|
- 비용 절감 효과
|
|
|
|
2. **문맥 이해 향상**
|
|
- 5초 분량을 한 번에 처리
|
|
- 문장 단위 인식으로 정확도 향상
|
|
|
|
3. **AI 분석 효율**
|
|
- 일정량의 텍스트가 주기적으로 생성
|
|
- AI가 분석하기 적합한 분량
|
|
|
|
4. **안정성**
|
|
- 재시도 로직 구현 용이
|
|
- 일시적 네트워크 오류 대응
|
|
|
|
5. **확장성**
|
|
- 여러 회의 동시 처리 가능
|
|
- Redis로 분산 처리 가능
|
|
|
|
---
|
|
|
|
## ⚠️ 주의사항
|
|
|
|
### 1. Azure Speech API 키 관리
|
|
|
|
- `.run/STT.run.xml`에 실제 API 키 설정 필요
|
|
- Git에 커밋하지 않도록 주의
|
|
|
|
### 2. Redis 연결
|
|
|
|
- Redis 서버가 실행 중이어야 함
|
|
- 연결 정보 확인 필요
|
|
|
|
### 3. Event Hub 설정
|
|
|
|
- Event Hub 연결 문자열 필요
|
|
- AI 서비스와 동일한 Event Hub 사용
|
|
|
|
### 4. 배치 주기 조정
|
|
|
|
- 5초 주기는 기본값
|
|
- 필요시 `application.yml`에서 조정 가능
|
|
|
|
---
|
|
|
|
## 🔄 다음 단계
|
|
|
|
1. **프론트엔드 연동**
|
|
- WebSocket 클라이언트 구현
|
|
- 오디오 캡처 및 전송
|
|
|
|
2. **E2E 테스트**
|
|
- 실제 음성 데이터로 테스트
|
|
- Azure Speech API 연동 검증
|
|
|
|
3. **AI 서비스 통합 테스트**
|
|
- Event Hub 통신 확인
|
|
- SSE 스트리밍 검증
|
|
|
|
4. **성능 최적화**
|
|
- 배치 주기 조정
|
|
- Redis 메모리 사용량 모니터링
|
|
|
|
---
|
|
|
|
## 📞 문의
|
|
|
|
**STT 서비스**: 준호 (Backend Developer)
|
|
**AI 서비스**: 서연 (AI Specialist)
|
|
|
|
---
|
|
|
|
**최종 업데이트**: 2025-10-27
|