오인식(high) 감지 시 직전 인식 정정 자동 주입 — 커밋 리뷰

마지막 업데이트 2026-07-22

bb04ee93 모카 · 2026-06-30 Feature 8 files +426 −14

기획자(PO) 관점의 요구사항을 Claude Code로 코드레벨 반영한 커밋. 오디오 분석 에이전트가 아동 발화를 오인식(high)했다고 판단하면, 핑퐁이가 다음 발화에서 스스로 정정하도록 문구를 게스트 Realtime 세션에 자동 주입한다. 이 로직은 host.tsx · session-card.tsx · monitor-dashboard 3곳에 동일하게 복제되어 있다 — 리뷰 시 가장 먼저 볼 지점.

무엇이, 왜 바뀌었나

오디오 분석 에이전트(Gemini/OpenAI)는 매 턴마다 아동 발화를 분석해 오인식 가능성을 low / medium / high로 판단한 JSON을 반환한다. 결과가 high이고 설정이 켜져 있으면, "직전 인식을 이렇게 잘못 들었으니 다음 발화에서 정정하라"는 지시문을 게스트 세션에 시스템 메시지로 주입한다. 판단·표시 자체는 기존 오디오 분석 에이전트 파이프라인(host.tsx:578 onResult) 그대로이며, 이 커밋은 그 결과에 반응하는 새 부수효과 하나를 추가한 것이다.

정정 문구는 관리자가 설정 화면에서 자유롭게 편집하는 템플릿이며, {heardContent}처럼 {키} 형태로 쓰면 활성 에이전트가 실제로 출력하는 JSON 필드 값으로 치환된다 — 에이전트 출력 스키마가 바뀌어도 템플릿을 고치지 않아도 되도록 설계했다.

흐름 ① — 설정 경로 (관리자 저장 → 전체 세션 반영)

1

설정 UI — 토글 · 문구 편집

components/sections/audio-analysis-agents-section.tsx

2

PUT — 저장

PUT /api/audio-analysis-agents/correction · withAuthAdmin

3

DynamoDB — 싱글톤 레코드

lib/db-queries.ts · AUDIO_ANALYSIS_CORRECTION_KEY

4

훅 — GET으로 로드해 상태 반영

use-audio-analysis-agents.ts · correctionInjection

흐름 ② — 런타임 주입 경로 (3개 파일에 동일 로직 복제)

아래 4단계를 components/pages/host.tsx, features/session/ui/session-card.tsx, app/monitor-dashboard/[group]/[roomId]/page.tsx 세 곳이 각자 onResult 콜백 안에 독립적으로 구현한다.

1

오디오 분석 결과 도착

onResult(result) — 3개 진입점 동일

2

조건 확인

enabled && probability === "high" && !error

host.tsx만 여기에 sessionRelay.isActive가 추가로 붙는다 — 나머지 두 곳엔 대응 가드가 diff상 보이지 않음.

3

문구 치환

buildCorrectionText(template, result)

4

게스트 세션에 주입

sendMessage(text) → 게스트 Realtime 세션, 다음 발화에 반영

코드로 보는 두 가지 지점

host.tsx만 있는 가드

components/pages/host.tsx if ( correctionInjection.enabled && result.probability === "high" && !result.error && sessionRelay.isActive ) { sessionRelay.sendMessage(buildCorrectionText(correctionInjection.template, result)); }
features/session/ui/session-card.tsx · app/monitor-dashboard/[group]/[roomId]/page.tsx if (correctionInjection.enabled && result.probability === "high" && !result.error) { monitorSession.sendMessage(buildCorrectionText(correctionInjection.template, result)); }

없는 키는 조용히 빈 문자열

types/db/audio-analysis-agent.types.ts return template.replace(/\{([a-zA-Z0-9_]+)\}/g, (_match, key) => { const value = record[key]; if (value === undefined || value === null) return ""; return typeof value === "string" ? value : String(value); });

레이어별 변경 요약

레이어파일핵심 변경
타입types/db/audio-analysis-agent.types.tsAudioCorrectionInjection, 기본 템플릿, buildCorrectionText/extractJsonKeysFromPrompt
DBlib/db-queries.tsgetAudioCorrectionInjection/setAudioCorrectionInjection (chatPreset 테이블 싱글톤 레코드)
API (신규)app/api/audio-analysis-agents/correction/route.tsGET(member)/PUT(admin), template 내용 검증 없음
use-audio-analysis-agents.tssetCorrectionInjection — 낙관적 업데이트 + 실패 시 롤백
설정 UIaudio-analysis-agents-section.tsx토글/문구/{키} 미리보기/활성 에이전트 키 칩 안내
런타임 ×3host.tsx · session-card.tsx · monitor-dashboard/page.tsx동일 조건 + 주입 호출 복제

리뷰 관전 포인트

구조

동일한 주입 조건 + 호출 로직이 3개 파일에 그대로 복제됨. 공용 훅(예: useCorrectionInjectionEffect(onResult))으로 추출할지 검토.

동작 확인

host.tsx의 조건에만 sessionRelay.isActive가 붙어 있음. monitorSession.sendMessage가 내부적으로 비활성 세션을 무시하는지 확인 필요 — 아니라면 세션카드/모니터 대시보드 경로에서 비활성 세션에도 메시지를 보낼 수 있음.

검증 없음

buildCorrectionText는 템플릿의 {"{키}"}가 활성 에이전트 출력에 없으면 빈 문자열로 조용히 치환한다. 설정 화면의 키 칩은 안내용일 뿐 저장 시점에 검증하지 않아, 오타난 키를 넣어도 저장은 성공하고 깨진 정정 문구가 주입될 수 있음.

영향 범위

enabled 기본값이 false라 기존 세션엔 즉시 영향 없음. 커밋 메시지대로 realtime·tts 두 모드가 같은 경로로 동작한다면, 두 모드에서 sendMessage 처리 방식이 실제로 동일한지 별도 확인 권장.

관련 문서

관련 문서: 오디오 분석 에이전트 — 통합 코드 레벨 문서 — 이 커밋이 손댄 onResult 진입점(스테이지 4)의 원본 파이프라인 문서. "V2엔 모달이 없다" 함정과 관련해, 이 커밋으로 V2(session-card·monitor-dashboard)의 onResult가 정정 주입 목적으로는 처음 연결됨.