01

아리 아바타 대화

기능설계 표본 ① — 실벗 · 원챗 · DeepSeek 3자 연동의 핵심

항목내용
기능 IDFEAT-ARI-CHAT
연결 시스템마스터 PART2 아리엔진 · 시스템② 놀이 · ⑧ 정서존엄
한 줄 정의어르신이 말을 걸면(또는 아리가 먼저 걸면), 아바타가 음성·표정으로 대화하고 기억한다
관련 액터PWA실벗서버원챗DeepSeek
왜 이 기능을 첫 표본으로 — 3개 서버(실벗·원챗·DeepSeek)가 얽히는 가장 복잡한 연동입니다. 이게 6계층으로 설계되면 나머지는 더 쉽습니다.

설계 7계층 체크리스트

계층이 문서의 섹션
① UI화면 명세 (대화 화면 SCR-ARI-01)
② Flow대화 상태 머신 + 먼저 말걸기 트리거
③ Datachat_sessions / chat_messages / ari_memory DDL
④ API대화 송수신 · 기억 · 먼저말걸기 엔드포인트
⑤ SeqSTT→실벗→DeepSeek→원챗→TTS 시퀀스 + 폴백
⑥ Comp프론트/백엔드 컴포넌트·서비스 구조
⑦ Test검수 체크리스트 (개발 완료 판정 기준)

① UI · 화면 명세

SCR-ARI-01 아리 대화 화면

요소사양 · 동작
아바타 영역(상단 50%)원챗 아바타 렌더(말할 때 입모양·표정 변화). 유휴 시 미소·눈깜빡임 idle 모션
자막(아바타 하단)아리 발화 텍스트를 큰 글씨(28px+)로 동시 표시(난청 대비)
대화 말풍선 리스트최근 대화 스크롤. 어르신=오른쪽, 아리=왼쪽. 글씨 크기 사용자 설정 반영
🎤 큰 마이크 버튼화면 하단 중앙 대형 원형. 누르고 말하기(PTT) 또는 탭 토글. 녹음 중 파형 애니메이션
⌨️ 텍스트 입력(보조)마이크 옆 작은 키보드 아이콘. 음성이 어려운 분 대비
🔊 다시듣기 버튼아리 마지막 말 다시 음성 재생
접근성최소 폰트 28px, 터치 영역 ≥ 64px, 명도대비 AA, 음성 우선

화면 상태별 표시

상태아바타마이크 버튼자막
idle(대기)미소·눈깜빡임🎤 "눌러서 말하기"이전 대화
listening(듣는중)고개 끄덕·경청 표정🔴 파형 애니메이션(실시간 STT 미리보기)
thinking(생각중)잠깐 위 보는 모션비활성 + 점점점…"음… 생각하고 있어요"
speaking(말하는중)입모양·표정 동기화비활성발화 텍스트 실시간
error(오류)갸웃 표정🎤 재시도"잘 못 들었어요. 다시 말씀해 주실래요?"
마법 UX 연계 — 마스터 PART5 원칙 적용: 큰 버튼·음성 우선·실수해도 안전(오인식 시 부드러운 재요청)·아바타가 길잡이.

② Flow · 대화 상태 머신

idle listening thinking speaking idle
전이트리거예외
idle→listening마이크 버튼 탭 / "아리야" 호출어마이크 권한 거부 → 안내+텍스트 입력 유도
listening→thinking발화 종료(무음 1.5s) 또는 버튼 해제STT 빈 결과 → error("다시 말씀해 주실래요?")
thinking→speaking실벗 서버 응답 수신(SSE)LLM/원챗 타임아웃 → 폴백 응답으로 speaking
speaking→idleTTS 재생 완료사용자가 새로 말하면 즉시 listening(인터럽트)

먼저 말 걸기 (Proactive) 트리거

마스터 PART2 "먼저 말 건다" 원칙의 구현. 서버 스케줄러가 조건 충족 시 원챗 발송 API로 푸시.

트리거조건발화 예
아침 안부매일 기상시간대(프로필) 첫 활동 or 08:00"순자님, 좋은 아침이에요. 잘 주무셨어요?"
복약 알림medications 스케줄 시각"혈압약 드실 시간이에요. 제가 같이 셀게요."
무응답 안부N시간 무활동(SOS 임계 미만)"오늘 조용하시네요. 뭐 하고 계세요?"
어제 이어가기전일 대화 키워드 기억"어제 무릎 아프다 하셨는데 좀 어떠세요?"
존엄 가드 — "싫다/그만"이라고 하면 즉시 물러나고 빈도를 낮춘다(강요하지 않는다 원칙).

③ Data · DB 스키마

기존 p25의 chat_sessions·chat_messages 재활용 + 아리 장기기억 ari_memory 신설.

sbdb · DDL
-- 대화 세션 (기존 p25 보강) CREATE TABLE chat_sessions ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, onechat_sid VARCHAR(64), -- 원챗 세션 매핑 ID persona_mode ENUM('companion','guardian','navigator','mentor','cheer') DEFAULT 'companion', started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_active TIMESTAMP NULL, INDEX idx_user (user_id), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 대화 메시지 CREATE TABLE chat_messages ( id BIGINT PRIMARY KEY AUTO_INCREMENT, session_id BIGINT NOT NULL, role ENUM('user','ari') NOT NULL, content TEXT NOT NULL, audio_url VARCHAR(255), -- TTS/녹음 파일 sentiment JSON, -- {"emotion":"sad","score":0.7} tokens INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_session (session_id, created_at), FOREIGN KEY (session_id) REFERENCES chat_sessions(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ★ 신설: 아리 장기 기억 (관계의 핵심) CREATE TABLE ari_memory ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, mem_type ENUM('fact','preference','event','health','family'), mem_key VARCHAR(100), -- "무릎통증","좋아하는노래" mem_value TEXT, -- "오른쪽 무릎이 비오면 아픔" importance TINYINT DEFAULT 5, -- 1~10 회상 우선순위 last_used TIMESTAMP NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_user_type (user_id, mem_type), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
설계 포인트 — ari_memory가 "기억하는 AI"를 만드는 핵심. 대화 종료 시 LLM이 중요 정보를 추출해 여기에 저장하고, 다음 대화 시 프롬프트에 주입한다.

④ API · 엔드포인트 명세

M경로설명
POST/api/v1/ai/chat발화 전송 → 아리 응답 스트리밍(SSE)
GET/api/v1/ai/sessions/{id}/messages대화 이력 조회(페이지네이션)
POST/api/v1/ai/proactive/check(스케줄러용) 먼저말걸기 조건 평가·발송
GET/api/v1/ai/memory아리 기억 조회(가족 공유범위 적용)
POST /api/v1/ai/chat — 요청
{ "session_id": 123, // 없으면 신규 생성 "input_type": "voice", // voice | text "text": "오늘 날씨가 좋네", // STT 변환 결과 또는 타이핑 "want_audio": true // 아바타 TTS 음성 URL 요청 여부 }
응답 — text/event-stream (SSE)
// 1) 생각중 event: status data: {"state":"thinking"} // 2) 토큰 스트리밍 (자막 실시간 표시) event: token data: {"t":"그러게요, "} event: token data: {"t":"산책 다녀오시면 어때요?"} // 3) 완료 (아바타 렌더용 원챗 정보 + 음성) event: done data: { "message_id": 456, "audio_url": "/storage/audio/456.mp3", "avatar": { "onechat_render": "wss://onechat.kiam.kr/...token..." }, "sentiment": { "emotion":"positive" } }
에러 — 원챗/LLM 장애 시 UPSTREAM_LLM/UPSTREAM_ONECHAT이 아니라, 사용자에겐 폴백 응답을 done으로 정상 전달(아래 ⑤ 참조). 어르신이 "고장"을 느끼지 않게 한다.

⑤ Seq · 연동 시퀀스

한 번의 대화가 4개 액터를 거치는 전체 흐름.

PWA STT
어르신 음성 → 브라우저 STT로 텍스트 변환 → POST /ai/chat
실벗서버
JWT 검증 → 세션 로드 → ari_memory + 최근 대화 + 프로필로 프롬프트 구성(페르소나·말투 주입)
실벗 DeepSeek
프롬프트 전송 → 응답 토큰 스트림 수신 → 클라이언트로 SSE token 중계
실벗 원챗
완성 텍스트 → 원챗 아바타 렌더/TTS 요청 → audio_url + 아바타 wss 토큰 수신
실벗
메시지 저장(chat_messages) → 감정분석 저장 → SSE done 전송
PWA TTS+렌더
원챗 아바타 입모양·표정 재생 + 음성 출력 + 자막 표시 → idle 복귀
실벗 (비동기)
대화 종료 시 LLM이 중요정보 추출 → ari_memory upsert (다음 대화에서 "기억")

장애 폴백 시퀀스

장애 지점폴백 동작
DeepSeek 타임아웃사전 정의 안전 응답("제가 잠깐 멍했네요. 다시 말씀해 주실래요?") + 재시도 큐
원챗 렌더 실패아바타 없이 자막 + 브라우저 TTS로 대체(대화 자체는 지속)
네트워크 끊김오프라인 안내 + 마지막 입력 로컬 보관 후 복구 시 자동 전송
원칙 — 어떤 장애에도 어르신은 "대화가 끊겼다"가 아니라 "아리가 잠깐 그랬네" 정도로 느끼게 한다.

⑥ Comp · 컴포넌트 / 모듈 구조

프론트엔드 (PWA)

features/ariChat/ ├─ AriChatView # SCR-ARI-01 화면 컨테이너 ├─ AvatarStage # 원챗 아바타 wss 연결·렌더 ├─ SubtitleBox # 큰 글씨 자막(SSE token 수신) ├─ MessageList # 대화 말풍선 ├─ MicButton # PTT 녹음 + STT + 상태머신 ├─ useChatStream.js # SSE 구독 훅 (status/token/done) └─ ttsPlayer.js # audio_url 재생 / 폴백 Web Speech

백엔드 (PHP)

app/ ├─ Controllers/AiChatController.php # /ai/chat, /sessions, /memory ├─ Services/ │ ├─ DeepSeekService.php # LLM 호출·스트리밍 │ ├─ OneChatService.php # 아바타 렌더·TTS·발송 │ ├─ AriPromptBuilder.php # 기억+프로필+페르소나 → 프롬프트 │ ├─ AriMemoryService.php # ari_memory 추출·저장 │ └─ ProactiveScheduler.php # 먼저말걸기(cron) └─ Models/ ChatSession ChatMessage AriMemory
모듈책임
AriPromptBuilder관계의 핵심. 기억·프로필·페르소나·안전가드를 조합해 "순자님만의 아리" 프롬프트 생성
OneChatService원챗 API 추상화. 장애 시 폴백 신호 반환(컨트롤러가 자막+브라우저TTS로 전환)
ProactiveSchedulercron 주기 실행 → 트리거 평가 → 원챗 발송. 존엄 가드(빈도 제한) 적용
표본 완성 — 이 6계층으로 "아리 대화"는 개발 착수 가능한 수준이 됐습니다. 다음 d02 SOS는 같은 틀에 생명 안전·상태머신·3주체 알림을 더해 설계합니다.

⑦ Test · 검수 체크리스트

개발 완료 판정 기준. 이 체크리스트를 모두 통과해야 "아리 대화" 기능이 완성된 것으로 본다. (QA 검수표 겸용)

정상 흐름 (Happy Path)

ID시나리오기대 결과
T-01🎤 마이크 버튼 누르고 "오늘 날씨 좋네" 발화STT 인식 → 아리 답변 음성+자막 출력, 아바타 입모양 동기화
T-02텍스트 입력창에 직접 타이핑 후 전송STT 없이도 동일하게 답변 생성·출력
T-03"다시듣기" 버튼 탭직전 아리 답변이 다시 음성 재생
T-04대화 종료 후 같은 회원이 다음날 재접속이전 대화의 핵심(ari_memory)이 프롬프트에 주입되어 "어제 ~하셨죠?" 식 회상
T-05아침 9시 (먼저 말걸기 트리거)아리가 먼저 안부 인사 발송(원챗) — 존엄 가드 빈도 제한 준수

예외 / 장애 (Failure Path)

ID시나리오기대 결과
T-06브라우저 마이크 권한 거부음성 비활성·안내문구 노출, 텍스트 입력으로 자동 폴백 (기능 중단 없음)
T-07DeepSeek LLM 502/타임아웃UPSTREAM_LLM 정형 응답 + 어르신용 문구("잠시 후 다시 말씀해 주세요")
T-08원챗 아바타 API 장애아바타 폴백 → 자막 + 브라우저 TTS로 대화 계속 (OneChatService 폴백 신호)
T-09JWT 만료 상태로 /ai/chat 호출AUTH_EXPIRED(401) → 토큰 재발급/재로그인 유도
T-10네트워크 끊김 (SSE 중단)마지막 수신분까지 표시 + 재연결 안내, 중복 메시지 저장 없음
T-11부정 발화·위기 신호("죽고싶어") 감지안전가드 발동 → 정서 지지 응답 + (설정 시) 보호자/상담 안내 트리거
완료 판정 — T-01~T-11 전부 통과 시 FEAT-ARI-CHAT 개발 완료. 특히 T-06·T-07·T-08(폴백 3종)은 어르신 경험상 필수 — 어떤 외부 장애에도 대화가 끊기지 않아야 한다.
표본 ① 최종 완성 — UI→Flow→DB→API→Seq→Comp→Test 7계층으로 "아리 대화"를 끝까지 설계했습니다. 이 틀이 나머지 모든 기능의 표준이 됩니다.