8.1 KiB
8.1 KiB
HUTAMS — 지능형 무전 관제 플랫폼 아키텍처 문서
Heavy-rail Unified Traffic Analysis & Monitoring System
LTE-R 기반 무전 통신을 실시간으로 수집·전사·분석하는 AI 관제 플랫폼
목차
1. 프로젝트 개요
| 항목 | 내용 |
|---|---|
| 언어 | Python 3.11+ |
| 웹 프레임워크 | FastAPI 0.115 + Uvicorn |
| STT 엔진 | faster-whisper (large-v3-turbo, INT8 양자화) |
| LLM | Qwen3 0.8B GGUF (llama-cpp-python, GPU 가속) |
| DB | SQLite (whisper.db via SQLAlchemy) |
| 프론트엔드 | Vanilla JS + Bootstrap 5 |
| 실시간 통신 | WebSocket (FastAPI native) |
2. 전체 파이프라인 다이어그램
[ 오디오 소스 ]
│ Line-in / 마이크 (mic 모드)
│ 로컬 wav 파일 (mock 모드)
↓
[ RadioListener / MockAudioListener ] ← 백그라운드 데몬 스레드
│ VAD (RMS 임계치 기반 음성구간 감지)
│ 침묵 1.8초 지속 → 녹음 종료 → 임시 .wav 생성
↓
[ app/services/stt_service.py :: WhisperSTTService ]
│ faster-whisper → full_text + segments[] 생성
│ domain_dict.post_process_correction() (RapidFuzz 교정)
│ HARDCODED_FIXES (신호질로→신호 진로 등)
│ guess_speaker (룰 정규식 → LLM 체이닝 fallback)
↓
[ app/services/llm_service.py :: LocalLLMService ]
│ Qwen3 0.8B GGUF
│ 제목 / 요약 / 키워드 / 긴급도 추출
│ _parse_response() — <think> 태그 제거 후 정규식 파싱
↓
[ SQLite (whisper.db) ]
│ TranscriptionRecord — LLM 메타 포함
│ TranscriptionSegment — 화자 뱃지 포함
↓
[ WebSocket ws_manager.broadcast() ]
↓
[ 브라우저 (index.html) ]
├── Live Mode: 타자기 효과 즉시 렌더링
└── Test Mode: 업로드 → REST POST → 동일 렌더링
3. 디렉토리 구조
Wisper/
├── app/
│ ├── core/
│ │ ├── config.py # Pydantic Settings (환경변수 관리)
│ │ ├── dictionary.py # 철도 도메인 사전 + 하드코딩 교정
│ │ └── exceptions.py # 도메인 예외 클래스
│ ├── db/
│ │ ├── database.py # SQLAlchemy 엔진 + SessionLocal
│ │ └── models.py # ORM 모델 (TranscriptionRecord, TranscriptionSegment)
│ ├── models/
│ │ ├── stt.py # STTRequest / STTResponse / STTSegment Pydantic 모델
│ │ └── record.py # RecordListResponse 등 API 응답 모델
│ ├── routers/
│ │ └── records.py # GET /api/v1/records 라우터
│ ├── services/
│ │ ├── audio_listener.py # RadioListener (실제 마이크 VAD 감청)
│ │ ├── audio_parser.py # pydub 포맷 변환 + 무음 제거
│ │ ├── llm_service.py # LocalLLMService (Qwen3 GGUF 래퍼)
│ │ ├── mock_audio_listener.py # MockAudioListener (파일 스트리밍 시뮬레이터)
│ │ └── stt_service.py # WhisperSTTService + guess_speaker()
│ ├── static/
│ │ └── audio/ # 영구 저장된 무전 오디오 파일 (/static/audio/{filename})
│ ├── templates/
│ │ └── index.html # 대시보드 SPA (Bootstrap 5 + Vanilla JS)
│ ├── main.py # FastAPI 앱 진입점 / lifespan / WS 매니저
│ └── .env # 로컬 환경변수 오버라이드
├── whisper.db # SQLite 데이터베이스
└── ARCHITECTURE.md # 이 문서
4. 핵심 파이프라인 상세
4.1 VAD (음성 활동 감지)
| 파라미터 | 기본값 | 설명 |
|---|---|---|
THRESHOLD |
600 | RMS 볼륨 임계치 (0~32767 범위) |
SILENCE_LIMIT |
1.8s | 침묵 지속 시간 초과 시 발화 종료 판정 |
PRE_ROLL_SECS |
0.3s | 발화 시작 직전 버퍼 (첫 음절 잘림 방지) |
RECORD_TIMEOUT |
30s | 단일 녹음 최대 시간 (무한 루프 방지) |
4.2 화자 분리 (Speaker Diarization)
1차: 룰 기반 정규식 (guess_speaker)
- "전철 OO" 패턴 → 관제
- 숫자 + "열차" 패턴 → 열차
- 짧은 응답어 + Gap ≥ 1.5s → 교차 할당 휴리스틱
2차: LLM 체이닝 Fallback (guess_speaker_with_llm)
- 룰에서 "미상" 반환 시 Qwen3 0.8B 추론 (max_tokens=256)
<think>블록 제거 후 "관제" / "열차" 키워드 파싱
4.3 LLM 메타데이터 추출
system: "철도 관제 무전 분석 시스템. 지정된 포맷으로만 답변."
user: 1-Shot 예시 → 실제 무전 텍스트
format: 제목: ... / 요약: ... / 키워드: ... / 긴급도: 긴급|일반
5. API 엔드포인트 스펙
| Method | Path | 설명 |
|---|---|---|
GET |
/ |
대시보드 HTML |
POST |
/api/v1/transcribe |
오디오 파일 업로드 → STT + LLM 분석 |
GET |
/api/v1/records |
이력 목록 조회 (?limit=50&skip=0&keyword=검색어) |
GET |
/static/audio/{filename} |
저장된 무전 오디오 스트리밍 |
WS |
/api/v1/ws/live |
실시간 감청 결과 WebSocket 구독 |
POST /api/v1/transcribe 요청 형식
Content-Type: multipart/form-data
Fields:
audio : UploadFile (m4a, mp3, wav 등)
language : str (기본: "ko")
base_datetime: ISO8601 str (선택, 예: 2026-03-07T09:00:00+09:00)
WebSocket 수신 메시지 포맷
{
"type": "stt_result",
"text": "좌천 하선 궤도 검측차 신호 진로 확인...",
"title": "좌천 하선 신호 진로 확인",
"summary": "궤도 검측차가 신호 진로를 확인하고 통과함.",
"keywords": "좌천 하선, 검측차, 신호, 통과",
"urgency": "일반",
"language": "ko",
"segments": [
{
"start_sec": 8.11,
"end_sec": 41.12,
"text": "좌천 하선 있는 궤도 검측차는...",
"speaker": "관제"
}
]
}
6. 환경변수(.env) 설정법
app/.env 파일(또는 프로젝트 루트 .env)에 아래 항목을 설정:
# ─── Whisper ─────────────────────────────────────
WHISPER_MODEL_NAME=large-v3-turbo
# ─── LLM ─────────────────────────────────────────
LLM_ENABLED=true
LLM_MODEL_PATH=./Qwen3.5-0.8B-Q4_K_M.gguf
# LLM_ENABLED=false 로 설정 시 LLM 분석 전체 스킵
# ─── 실시간 감청 소스 ─────────────────────────────
# "mock" : 로컬 파일 시뮬레이션 (개발/테스트용)
# "mic" : 서버 PC 실제 마이크/Line-in (상용 운영용)
AUDIO_SOURCE=mock
MOCK_AUDIO_PATH=./sample1.m4a
7. 서버 구동 & 운영 가이드
개발 서버 기동
# 프로젝트 루트에서
.\.venv\Scripts\uvicorn.exe app.main:app --host 127.0.0.1 --port 28000
Mock 시뮬레이션 모드 확인 절차
.env에서AUDIO_SOURCE=mock설정 (기본값)MOCK_AUDIO_PATH=./sample1.m4a경로 확인- 서버 기동 후
http://127.0.0.1:28000접속 - 우측 상단 Live Mode 토글 ON
- 서버 로그에서
🧪 [Mock] 발화 감지,📡 WebSocket broadcast확인 - 브라우저 우측 화면에 타자기 효과로 무전 내용이 실시간 렌더링됨
실제 마이크 감청 전환 방법
# app/.env
AUDIO_SOURCE=mic
재기동 시 🎙️ RadioListener(실마이크) 기동 로그 확인 후,
Line-in 단자에 무전 수신기 연결 → Live Mode 토글 ON으로 즉시 감청 시작.