졸업 프로젝트/React-Native

GPT-4o 기반 음성 챗봇 구현 및 AI 모델을 이용한 졸음 감지

ansui 2025. 5. 12. 02:49

우리 앱 서비스는 음성 분석 기반 졸음 감지 및 개인화된 음성 대화를 통한 졸음 운전 예방 서비스이다.

 

내가 구현한 주요 기능은 다음과 같다.

1. GPT-4o 기반의 개인화된 음성 챗봇 구현

2. 졸음 감지 AI 모델과 연결하여 졸음 감지 시 큰소리 알림

 

이 글에서는 해당 기능을 어떻게 구현했는지 자세히 소개하려한다.

 

0. 음성 대화 로직

기능 구현에 대해 이야기하기 전에 기능의 로직에 대해 먼저 설명하겠다.

 

사용자는 첫 회원가입 시 이름, 성별, 생년월일, 직업, 관심사를 입력한다.

이 정보는 DB에 저장되고 음성 챗봇 구현 시 프롬프트 엔지니어링하여 개인 맞춤형 대화를 구현하였다. 

 

사용자가 대화 시작 버튼을 누르면 GPT-4o 모델을 기반으로 음성 대화를 나눌 수 있다.

먼저 GPT가 대화를 시작하고 말이 끝나면 사용자의 음성 녹음을 시작한다.

사용자 발화가 종료되면 이 음성을 Whisper API자체 구현한 AI 졸음 감지 모델에 각각 전송한다.

Whisper API음성을 텍스트로 변환해주는 Open AI의 모듈로 응답으로 Text를 반환해준다.

AI 졸음 감지 모델은 딥러닝으로 학습시킨 모델에 사용자의 음성을 넣으면 응답으로 yawn, sleepy가 true인지 false인지 전달해준다. 졸음이 감지 될 경우 true값이 오고 두 기준 중 하나라도 true로 인식되면 졸음으로 감지하여 큰 알람 소리를 울리도록 설계하였다.

 

알람 소리가 10초 동안 울리고 스트레칭 안내 여부를 묻는다.

1. 사용자의 응답이 긍정일 경우, 운전 중 할 수 있는 간단한 스트레칭을 안내해주고 자연스럽게 대화를 이어나간다.

2. 사용자의 응답이 부정일 경우, 자연스럽게 대화를 이어나간다.

 

요약

1. 회원가입 시 사용자 정보(이름, 성별, 생년월일, 직업, 관심사)를 입력 받아 DB에 저장한다.

2. 사용자가 “대화 시작” 버튼을 누르면 GPT-4o가 먼저 말을 걸고, 이후 사용자의 음성 발화를 기다린다.

3. 사용자의 발화가 끝나면:

     - Whisper API를 통해 음성을 텍스트로 변환 (STT)

     - AI 졸음 감지 모델에 음성을 전송해 졸음 여부 판단

4. 졸음이 감지되면 알람 재생 및 스트레칭 유도

5. 사용자 응답에 따라 대화를 자연스럽게 이어간다.


 1. Whisper API를 이용한 STT

*Whisper API

https://platform.openai.com/docs/guides/speech-to-text

 

사용자의 음성은 실시간으로 Whisper API를 통해 텍스트로 변환되고, 해당 텍스트는 GPT-4o에 전달되어 응답을 생성한다.

또한 음성에서 텍스트로 변환된 사용자 발화 내에 졸음 키워드가 포함되어 있는 지 판단하여 대화 흐름을 조정한다.

 

아래 코드는 Whisper API에 사용자 음성 녹음 파일을 전송하고 변환된 text를 응답으로 받는 코드이다.

const response = await FileSystem.uploadAsync(whisperEndpoint, uri, {
        headers: {
          Authorization: `Bearer ${OPENAI_API_KEY}`,
          "Content-Type": "multipart/form-data",
        },
        httpMethod: "POST",
        uploadType: FileSystem.FileSystemUploadType.MULTIPART,
        fieldName: "file",
        mimeType: "audio/mpeg",
        parameters: {
          model: "whisper-1",
        },
      });

 

샘플 음성에 대해 텍스트로 변환 후 정확도를 측정하였다. 평균 정확도 및 탐지율이 95% 이상으로 측정되었다.

 

2. GPT-4o를 이용한 음성 챗봇 구현

axios 모듈을 이용하여 GPT-4o와 대화를 할 수 있도록 코드를 짰다.

생성된 응답은 TTS(Text-to-Speech)로 변환되어 다시 사용자에게 전달된다.

 

아래 코드는 텍스트로 변환한 사용자의 음성을 GPT-4o에 전송하고 응답을 받는 코드이다.

messages 부분프롬프트 엔지니어링을 하는 부분이다.

  try {
      const response = await axios.post(
        "https://api.openai.com/v1/chat/completions",
        {
          model: "gpt-4o",
          messages: [
            {
              role: "system",
              content: generatePrompt(driverInfo), // 프롬프트 엔지니어링
            },
            { role: "user", content: message },
          ],
        },
        {
          headers: {
            Authorization: `Bearer ${OPENAI_API_KEY}`,
            "Content-Type": "application/json",
          },
          cancelToken: source.token,
        }
      );

      botFunc(response);
    } catch (err) {
      console.error(err);
    } finally {
      setLoading(false);
    }

 

위 messages에 들어간 generatePrompt 함수는 아래 사진과 같다.

아래 사진과 같이 사용자 정보를 기반으로 system prompt를 생성하였다.

여러번 시행착오를 겪으며 가장 자연스럽고 몰입감 있는 대화 경험을 제공할 수 있도록 수정하였다.

 

generatePrompt.ts

 

아래는 사용자 정보의 예시이다.

초기 입력화면에서 사용자가 입력한 정보가 이 형식으로 DB에 저장되고 대화 시작 시 불러와 프롬프트 엔지니어링에 사용한다.

다양한 사용자 프로필 설정으로 시뮬레이션한 결과, 평균적으로 3~5회 응답 내에서 개인화 된 대화 흐름이 유지되었다.

 

3. AI를 이용한 졸음 감지

사용자의 발화가 끝날 때마다 실시간으로 사용자 음성을 AI 모델에 전송하고 졸음 감지 결과를 받아온다.

 

아래 코드는 자체적으로 구현한 AI 졸음 감지 모델에 사용자 음성 녹음 파일을 전송하고 text로 결과를 받는 코드이다.

yawn, sleepy로 각각 하품 감지, 졸린 음성 감지 결과를 result로 받고 이 중 하나라도 true로 측정되면 졸음으로 인식한다.

졸음으로 인식하면 playAlarm() 함수가 작동하고 그렇지 않으면 대화를 계속 이어나간다. 

const sleepRes = await FileSystem.uploadAsync(AIEndPoint, uri, {
        headers: {
          Authorization: `Bearer ${OPENAI_API_KEY}`,
          "Content-Type": "multipart/form-data",
        },
        httpMethod: "POST",
        uploadType: FileSystem.FileSystemUploadType.MULTIPART,
        fieldName: "file",
        mimeType: "audio/mpeg",
      });

      const result = JSON.parse(sleepRes.body);
      console.log(result);
	  
      // 졸음 감지 시 playAlarm();
      if (result.yawn || result.sleepy) {
        setLoading(true);
        willSendRef.current = false;

        await playAlarm();
        setLoading(false);
        return;
      }

 

playAlarm() 함수

미리 저장해둔 alarm.mp3를 재생하고 재생이 끝나면 Stretching 여부를 묻는다.

알림소리를 재생하기 위해 React-Native Expo의 Audio 라이브러리를 사용하였다.

아래 링크의 공식 문서를 참고하여 코드를 구현하였다.

 

sound에 내 mp3를 저장하고 라이브러리에 내장되어있는 playAsync() 함수를 이용하여 쉽게 mp3 파일을 재생할 수 있다.

 

*React-Native Expo Audio

https://docs.expo.dev/versions/latest/sdk/audio/

const playAlarm = async () => {
    try {
      const { sound } = await Audio.Sound.createAsync(
        require("../assets/sounds/alarm.mp3")
      );

      playbackObject.current = sound;
      await sound.playAsync();

      willSendRef.current = true;
      setTimeout(() => {
        askStretching();
      }, 10000);
    } catch (error) {
      console.error("Failed to load and play sound", error);
    }
  };

이번 프로젝트를 통해 STT, TTS, AI 모델 연동, 개인화 음성 챗봇, 알림 시스템 등 다양한 기술을 하나의 흐름으로 통합할 수 있었다.

 

Whisper의 STT 정확도가 높았고 자체 졸음 감지 모델과의 연동도 테스트 결과 유의미한 결과를 낼 수 있었다.

 

사용자 정보에 기반한 프롬프트 엔지니어링은 단순 템플릿 수준이 아닌, 대화 흐름 전체를 제어하는 중요한 요소였다. 같은 메시지라도 성별, 나이, 관심사에 따라 다르게 반응해야 하기 때문에 다양한 프로필을 기반으로 시뮬레이션을 돌리고, 그 결과를 바탕으로 prompt를 수차례 개선했다. 이 과정에서 프롬프트 엔지니어링에 대해 깊이 있는 학습을 할 수 있었다.