Python 음성 인식 삽질기

Python 음성 인식 삽질기

영화 “Her” 같은 음성 어시스턴트를 만들어보고 싶었다. Verbi라는 오픈소스 프로젝트를 fork해서 커스터마이징했다.

음성 인식 → LLM 응답 → TTS 출력. 간단해 보였는데, 삽질이 많았다.


기본 구조

# 1. 음성 인식 (Whisper)
audio = record_audio()
text = transcribe(audio)

# 2. LLM 응답 (GPT-4)
response = generate_response(text)

# 3. TTS (Deepgram)
audio = text_to_speech(response)
play_audio(audio)

문제 1: Python 3.13 호환성

프로젝트를 실행하자마자 에러:

ModuleNotFoundError: No module named 'audioop'

SpeechRecognition 라이브러리가 audioop 모듈을 사용하는데, Python 3.13에서 deprecated 됐다.

해결: 라이브러리 업그레이드

pip install --upgrade SpeechRecognition

최신 버전(3.10.1+)에서 audioop 대신 순수 Python 구현을 사용한다.

# requirements.txt
SpeechRecognition>=3.10.1

문제 2: 마이크 녹음 AssertionError

녹음 중 갑자기 AssertionError:

AssertionError: Audio source must be entered before recording

원인

SpeechRecognition의 context manager 사용이 잘못됐다.

# 잘못된 코드
source = sr.Microphone()
recognizer.listen(source)  # AssertionError!

해결: with 문 사용

# 올바른 코드
with sr.Microphone() as source:
    recognizer.adjust_for_ambient_noise(source, duration=0.5)
    audio = recognizer.listen(source)

Microphone은 반드시 context manager로 사용해야 한다. __enter__에서 오디오 스트림을 열기 때문.


문제 3: 마이크 감도

조용한 환경에서는 잘 되는데, 약간의 소음만 있어도 인식이 이상해졌다.

원인

기본 에너지 임계값이 너무 낮았다.

해결: 환경 적응형 감도

def setup_microphone(recognizer, source):
    # 주변 소음 측정 (1초)
    recognizer.adjust_for_ambient_noise(source, duration=1.0)

    # 에너지 임계값 상향 (기본값의 1.5배)
    recognizer.energy_threshold *= 1.5

    # 동적 임계값 비활성화 (안정성)
    recognizer.dynamic_energy_threshold = False

    print(f"Energy threshold: {recognizer.energy_threshold}")

adjust_for_ambient_noise로 현재 환경의 소음 레벨을 측정하고, 임계값을 약간 높여서 잡음을 필터링.


문제 4: LLM 기반 이름 추출

온보딩에서 사용자 이름을 물어본다:

“안녕하세요! 이름이 뭐예요?”

사용자가 “저는 홍길동이에요”라고 하면, “홍길동”만 추출해야 한다.

해결: LLM으로 이름 추출

def extract_name(user_response: str) -> str:
    prompt = f"""
    다음 문장에서 사람 이름만 추출하세요.
    이름만 출력하고, 다른 설명은 하지 마세요.

    문장: "{user_response}"

    이름:
    """

    response = openai.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=20,
        temperature=0
    )

    return response.choices[0].message.content.strip()

결과:


문제 5: 온보딩 후 불필요한 대기

온보딩이 끝나면 바로 대화 모드로 넘어가야 하는데, 이상하게 음성 입력을 기다리고 있었다.

원인

온보딩 완료 후 listen() 호출이 중복됐다.

해결: 상태 머신 정리

class VoiceAssistant:
    def __init__(self):
        self.state = "IDLE"

    def run(self):
        if not self.is_onboarded():
            self.state = "ONBOARDING"
            self.do_onboarding()

        self.state = "LISTENING"
        while True:
            # 음성 인식
            text = self.listen()

            # 응답 생성 및 재생
            self.state = "RESPONDING"
            response = self.generate_response(text)
            self.speak(response)

            self.state = "LISTENING"

상태를 명확히 관리해서 중복 호출 방지.


최종 구조

class VoiceAssistant:
    def __init__(self):
        self.recognizer = sr.Recognizer()
        self.setup_recognizer()

    def setup_recognizer(self):
        with sr.Microphone() as source:
            self.recognizer.adjust_for_ambient_noise(source, duration=1.0)
            self.recognizer.energy_threshold *= 1.5
            self.recognizer.dynamic_energy_threshold = False

    def listen(self) -> str:
        with sr.Microphone() as source:
            print("Listening...")
            audio = self.recognizer.listen(source, timeout=5)

        # Whisper API로 전사
        return self.transcribe(audio)

    def transcribe(self, audio) -> str:
        audio_data = audio.get_wav_data()
        response = openai.audio.transcriptions.create(
            model="whisper-1",
            file=("audio.wav", audio_data),
            language="ko"
        )
        return response.text

    def speak(self, text: str):
        # Deepgram TTS
        audio = self.tts.speak(text)
        self.play_audio(audio)

삽질 정리

문제 해결
Python 3.13 audioop 없음 SpeechRecognition >= 3.10.1
AssertionError Microphone with 문 사용
마이크 감도 energy_threshold 조정
이름 추출 LLM prompt
상태 관리 상태 머신 패턴

결론

Python 음성 인식은 라이브러리 덕분에 쉬워 보이지만, 실제 환경에서는 삽질이 많다.

특히:

  1. Python 버전에 따른 호환성 문제
  2. 마이크 환경에 따른 감도 조정
  3. 비동기 처리와 상태 관리

“Hey Siri” 하나 만드는 데 Apple이 수천 명을 고용한 이유를 알겠다.

Back