Speaker Identity And A2A Service Context
이 문서는 온디바이스 test_sherpa_module 브랜치의 화자 분리/인식 PoC와 Cloud feature_a2a_task_orchestration_cloud 브랜치의 A2A Task Manager 구조를 하나의 서비스 확장 축으로 정리한다.
핵심 결론은 다음과 같다.
- 화자 분리/인식은 STT 이후 부가 기능이 아니라,
누가 말했는지,몇 명이 말했는지,신뢰할 수 있는 단일 화자인지를 판단하는 user context 계층이다. - 현재 온디바이스 PoC는 등록 화자 임베딩, diarization 구간, 화자 검색 점수, 다화자/겹침/거리 차단 정책의 골격을 이미 갖고 있다.
- Cloud A2A는 직접 음성을 분석하지 않고, 온디바이스가 만든
speaker_context를voice_context에 실어 받아 owner selection, memory update, workflow/task 정책에 반영하는 구조가 맞다. - 서비스 확장은 “화자 이름을 맞추는 기능”보다 “개인화/권한/다중 사용자 충돌/복합명령 소유자 결정”으로 보는 편이 더 실용적이다.
1. 분석 기준 브랜치와 소스
| 영역 | 브랜치 | 핵심 파일 |
|---|---|---|
| On-device Agent | test_sherpa_module |
SpeakerIdentificationEngine.kt, DiarizationEngine.kt, SpeakerStabilityFeature.kt, SpeakerListActivity.kt, SpeakerHistoryActivity.kt, docs/SPEAKER_THRESHOLDS.md |
| Cloud LLM Lambda | feature_a2a_task_orchestration_cloud |
gemini/a2a/runtime/orchestrator.py, task_manager.py, session_state.py, memory_context.py, planner/main_router_api.py |
| Wiki 기존 축 | shared llmwiki | docs/a2a/*, docs/sk-intellix/* |
주의:
- Cloud
new_logic브랜치는 이 문서의 근거로 사용하지 않는다. - 현재 문서는 PoC/설계 분석 문서이며, 실제 제품 반영 상태를 의미하지 않는다.
- 화자 임베딩 원본, 음성 파일, 개인 식별 정보는 문서에 남기지 않는다.
1-1. 하위 문서
| 문서 | 목적 |
|---|---|
| Entry Points | 관계성 높은 기능/책임 단위로 묶은 읽기 진입점 |
| Source Map | 온디바이스/Cloud 파일별 책임과 함수 흐름 상세 |
| PUI Launcher 화자 등록 Overlay 구현 설계 | 보호된 broadcast와 Service overlay, entrySource별 닫기·결과 상태, 추후 UI 가이드 반영 경계 |
| Speaker Enrollment Service Phase 1 Plan | 런처앱 음성 등록 화면과 온디바이스 AI 모듈 등록 엔진을 나누는 서비스화 1단계 계획 |
| Speaker Context Contract | speaker_context JSON 계약과 상태 전이 |
| Speaker Memory Management | 화자별 대화 기록, short/mid/long memory, profile 후보 관리 |
| Speaker Identity Service Planning | Voice ID, 바이탈 사인 Face ID, 컨트롤 앱 계정, Google 연동을 포함한 서비스 identity 모델 |
| Speaker Following And Personalization Strategic Roadmap | 화자를 추종하고 개인화하는 이유, 서비스 확장 지도, 단계형 미래 로드맵 |
| Service Opportunity Backlog | 화자 인식을 개인화, 바이탈, 가족 권한, 외부 계정, proactive care로 확장하는 가능성 백로그 |
| Service Scenarios | 단일 화자, 다화자 차단, 개인화, 권한, 복합명령 owner 시나리오 |
| TaskManager Integration | Cloud A2A Task Manager와 speaker owner/side-effect gate 연계 |
| Privacy And Safety | voiceprint, 대화 기록, 삭제, 동의, 로그 정책 |
| Test Plan | PoC부터 Cloud A2A/Task Manager까지 계층별 검증 계획 |
| Evidence And Gap Audit | 요구사항별 충족 증거와 제품화 전 남은 검증 공백 |
| Implementation Roadmap | 후속 제품 구현을 위한 티켓 분해, 수정 파일, 검증 게이트 |
| Traceability And Completion Audit | 사용자 요구사항, 근거 문서, 배포/검증 증거, 제품화 갭을 한 장에서 추적 |
먼저 어디서부터 봐야 할지 판단하기 어렵다면 Entry Points를 기준으로 본다. 이 페이지는 전체 이해, 온디바이스 화자 PoC, Cloud A2A 연결, 메모리/개인화/권한, 복합명령/TaskManager, 검증/운영 판단으로 문서를 다시 묶는다.
2. 온디바이스 PoC 구성
2.1 SpeakerIdentificationEngine
소스:
app/src/main/java/com/skmagic/ondeviceai/agent/service/module/SpeakerIdentificationEngine.kt
현재 역할:
SpeakerEmbeddingExtractor를 초기화해embedding.onnx기반 화자 임베딩을 추출한다.SpeakerEmbeddingManager를 통해 등록 화자를 검색한다.registerSpeakerFromWavPaths()는 동일 화자 이름에 대해 여러 WAV 파일을 받아 임베딩을 생성하고 등록한다.identifyFromSamples(),identifyFromSegment(),identifyFromWavPath()는 입력 음성 또는 구간에서 화자를 검색한다.searchEmbeddingWithScoreRaw()는 등록된 각 화자의 여러 임베딩과 cosine similarity를 계산해 최고 점수를 반환한다.
현재 계약상 의미:
| 항목 | 현재 값/동작 | 서비스 의미 |
|---|---|---|
| sample rate | 16000 |
STT/diarization/embedding 입력 정렬 기준 |
| default threshold | 0.45f |
등록 화자 accepted/unknown 경계 |
| embedding asset | embedding.onnx |
등록/검색에 공통 사용되는 모델 |
| 등록 단위 | speaker name + WAV paths | 사용자별 voiceprint seed |
| 검색 결과 | SearchResult(name, score) |
speaker_id와 confidence의 원천 |
제약:
- 등록 임베딩은 현재
registeredEmbeddingsin-memory 구조에 저장된다. - 앱 재시작 이후 영속 로딩/동기화 정책은 별도 설계가 필요하다.
- threshold는 환경/거리/다화자 여부에 따라 동적으로 바뀌어야 실제 서비스 안정성이 올라간다.
2.2 DiarizationEngine
소스:
app/src/main/java/com/skmagic/ondeviceai/agent/service/module/DiarizationEngine.kt
현재 역할:
- Sherpa ONNX
OfflineSpeakerDiarization을 사용한다. - segmentation 모델은
sherpa-onnx-speaker-diarization/segmentation.onnx를 사용한다. - embedding 모델은
embedding.onnx를 사용한다. diarize(samples)결과는DiarizationSegment(startSec, endSec, speakerId)목록이다.
서비스 의미:
- 단일 화자 여부 판정
- 다화자/겹침 상황 감지
- 특정 구간만 speaker identification에 넘기는 전처리
- “누가 명령했는가”와 “누가 주변에서 말했는가”를 분리할 기반
제약:
- diarization의
speakerId는 클러스터 ID이며, 등록 사용자 이름과 동일하지 않다. - 등록 사용자 이름으로 바꾸려면 각 segment의 embedding을
SpeakerIdentificationEngine으로 다시 검색해야 한다. - 짧은 발화에서는 diarization보다 speaker identification의 안정화 정책이 더 중요할 수 있다.
2.3 SpeakerStabilityFeature
소스:
app/src/main/java/com/skmagic/ondeviceai/agent/service/extension/SpeakerStabilityFeature.kt
현재 역할:
- 직전 확정 화자에게
sessionBoostScore = 0.04f를 부여한다. - 최근 화자에게
recentBoostScore = 0.02f를 부여한다. - 직전 화자 보정 유효 시간은
sessionDecayMs = 20_000L이다. speakerCount >= 3이면 boost를 비활성화한다.
서비스 의미:
- 같은 사용자가 연속으로 말하는 자연스러운 turn에서 인식 흔들림을 줄인다.
- 다화자 혼잡 상황에서는 보정을 꺼서 오인식 확정 위험을 낮춘다.
- Cloud A2A의 multi-turn owner persistence와 연결될 수 있다.
3. 현재 차단/재시도 정책
소스:
docs/SPEAKER_THRESHOLDS.md
app/src/main/res/values/strings.xml
app/src/main/res/values-en/strings.xml
현재 문서화된 정책:
| 상황 | 조건 | 안내/동작 |
|---|---|---|
| 거리 멂 | rms < 0.015 and hf_ratio < 0.18 |
가까이에서 다시 말하도록 안내 |
| 겹침 발화 | overlapRatio >= 0.35 |
한 명씩 말하도록 안내 |
| 화자 과다 | speakerCount >= 3 |
조용한 환경에서 다시 호출하도록 안내 |
| 다화자 대부분 unknown | speakerCount >= 2, unknownRatio >= 0.9, maxScore <= 0.5 |
다화자 안내 후 중단 |
| 저신뢰 | allUnknown, maxScore <= 0.5, multiSpeaker 또는 짧은 발화 |
가까이에서 다시 말하도록 안내 |
이 정책은 기능 실행 전에 적용되어야 한다. 이유는 speaker context가 불안정한 상태에서 개인화/권한/복합명령을 수행하면 사용자 경험보다 안전 리스크가 더 커지기 때문이다.
4. Cloud A2A와 연결되는 컨텍스트 계약
Cloud A2A의 현재 구조는 voice_context 중심이다.
관련 소스:
gemini/a2a/runtime/session_state.py
gemini/a2a/runtime/memory_context.py
gemini/a2a/runtime/orchestrator.py
gemini/a2a/runtime/task_manager.py
현재 normalize_voice_context()는 다음 필드를 기본화한다.
recognized_text
asr_confidence
audio_locale
slot_state
handoff_chain
replan_reason
selected_flow_id
owner_selection
화자 인식이 제품 경로에 들어오면 아래 additive field로 확장하는 것이 안전하다.
{
"voice_context": {
"recognized_text": "거실 청정하고 10분 뒤에 돌아와",
"session_id": "sess_001",
"asr_confidence": 0.94,
"audio_locale": "ko-KR",
"speaker_context": {
"contract_version": "speaker-context-v1",
"speaker_id": "user_001",
"display_name": "아빠",
"speaker_confidence": 0.72,
"speaker_state": "identified",
"speaker_count": 1,
"overlap_ratio": 0.0,
"unknown_ratio": 0.0,
"max_score": 0.72,
"safety_gate": "pass",
"personalization_allowed": true,
"authority_level": "normal",
"source": "ondevice_sherpa_poc"
}
}
}
권장 상태값:
| 필드 | 값 예시 | 의미 |
|---|---|---|
speaker_state |
identified, unknown, ambiguous, multi_speaker, overlap, far_speech |
Cloud가 개인화와 실행 위험을 판단하는 1차 상태 |
safety_gate |
pass, reprompt, block |
온디바이스가 이미 차단했는지 또는 Cloud가 보수적으로 처리해야 하는지 |
personalization_allowed |
true/false |
memory/profile 사용 가능 여부 |
authority_level |
normal, restricted, child, guest, unknown |
실행 권한 정책의 미래 확장 포인트 |
중요 원칙:
- Cloud는
speaker_id를 추론하지 않는다. 온디바이스가 산출한 값을 evidence로 사용한다. - Cloud는 speaker context를 current turn보다 우선하지 않는다. 현재 발화가 항상 우선이다.
- speaker confidence가 낮으면 개인화와 권한이 필요한 task를 보수적으로 처리한다.
- 다화자/겹침 상태에서는 Task Manager가 side-effect 있는 작업을 바로 실행하지 않도록 해야 한다.
5. A2A Task Manager와 서비스 확장 지점
Cloud A2A 현재 구조:
orchestrator.py는route_turn()결과를 바탕으로 route 실행을 조합한다.session_state.py는 owner selection, slot state, handoff chain, active workflow 정보를 구성한다.memory_context.py는memory_context를 정규화하고memory_update후보를 만든다.task_manager.py는 multi-step planner output을device_task_requests로 변환한다.
화자 컨텍스트가 들어오면 확장 가능한 지점은 다음이다.
| 확장 지점 | 현재 소스 | 적용 방식 |
|---|---|---|
| owner persistence | derive_owner_selection() |
직전 확정 화자와 active workflow owner가 다르면 확인 질문 |
| memory update | build_memory_update() |
short_term_pair에 speaker_id를 포함하고 profile 후보를 speaker별로 분리 |
| task execution gate | build_device_task_requests() |
speaker_state != identified이면 side-effect task를 confirmation 요구로 낮춤 |
| route planning | route_turn() / preclassifier |
speaker_context를 prompt support context로 제공하되 현재 발화 우선 |
| workflow continuation | workflow_context |
active workflow를 시작한 speaker와 후속 발화 speaker가 다르면 takeover 정책 적용 |
6. 서비스 확장 방향
6.1 1단계: 안전한 단일 화자 확인
목표:
- “지금 명령을 실행해도 되는 단일 화자인가”를 판단한다.
- unknown, overlap, too many speakers, far speech에서는 기능 실행 전 재시도 안내를 우선한다.
서비스 효과:
- TV/주변 대화/동시 발화로 인한 오동작 감소
- 이동/보안/스케줄 같은 side-effect 기능의 신뢰도 개선
6.2 2단계: 사용자별 대화 기록
현재 UI:
SpeakerListActivity는filesDir/speaker_id/{speakerName}디렉터리를 조회한다.SpeakerHistoryActivity는{speakerName}/history.txt의 session/turn JSON을 보여준다.
확장 방향:
- speaker별 최근 대화 pair를 short-term memory로 분리
- speaker별 선호 응답 톤, 자주 쓰는 기능, 공간명 alias 관리
- Cloud
memory_context.long_term.profile_items에 speaker scope 추가
6.3 3단계: 개인화 기능
예시:
- “내가 좋아하는 모드로 해줘”에서 speaker별 선호 모드를 적용
- “내 방 청정해줘”에서 speaker별 기본 공간을 참조
- “평소처럼 예약해줘”에서 speaker별 반복 스케줄 패턴을 추천
주의:
- speaker confidence가 낮으면 개인화 적용 금지
- 개인화 적용 여부는 로그에 남겨야 한다.
- 민감 profile은 사용자 삭제/동기화 정책이 필요하다.
6.4 4단계: 권한/보호자/게스트 정책
예시:
- child/guest speaker는 보안모드 해제, 홈잠금 해제, 개인 기록 조회를 제한
- 보호자 speaker는 Safe Care, 보안, 스케줄 관리 권한을 가질 수 있음
- unknown speaker는 설명/일반 대화는 허용하되 실행형 명령은 확인 질문 필요
주의:
- 이 단계는 제품 정책/법무/개인정보 기준이 필요하다.
- 단순 voiceprint만으로 고위험 권한을 자동 부여하면 안 된다.
6.5 5단계: 복합명령 소유자 관리
복합명령 예:
거실 청정하고 10분 뒤에 침실로 와
Task Manager 관점:
- step 1: 거실 청정
- step 2: 10분 대기
- step 3: 침실 이동
speaker context 적용:
- workflow를 시작한 speaker를
workflow_owner_speaker_id로 기록 - 후속 취소/변경 명령이 다른 speaker에서 오면 confirmation 또는 owner takeover 정책 적용
- 다화자 상태에서 “그거 취소해” 같은 지시어는 STT_NULL/clarify로 보내는 편이 안전
7. 권장 아키텍처
권장 흐름:
Audio
-> STT raw/processed
-> Diarization
-> Speaker Identification
-> Speaker Safety Gate
-> voice_context.speaker_context
-> Cloud A2A Router / Memory / Task Manager
-> Device task or TTS
권장 모듈 책임:
| 모듈 | 책임 |
|---|---|
| On-device audio pipeline | 음성 수집, STT, diarization, embedding, speaker gate |
| Speaker context builder | speaker_context 계약 생성, PII 최소화 |
| Cloud A2A router | 현재 발화와 speaker_context를 함께 보고 route/skill 선택 |
| Memory runtime | speaker별 short/mid/long memory 분리 |
| Task Manager | speaker_state와 workflow owner에 따른 실행/확인/차단 |
| UI/Settings | 화자 등록/조회/삭제, 기록 삭제, 권한 설정 |
8. 오픈 포인트
| 항목 | 현재 상태 | 필요 작업 |
|---|---|---|
| 등록 화자 영속화 | in-memory 중심 + UI 디렉터리 구조 일부 존재 | embedding 저장/로드/삭제 계약 필요 |
| speaker_context payload | 아직 Cloud 표준 필드 아님 | speaker-context-v1 계약 확정 필요 |
| Cloud planner prompt 반영 | A2A voice_context는 확장 가능 | speaker context를 evidence로 쓰는 prompt 규칙 필요 |
| Task Manager 권한 정책 | device_task_requests 생성 구조 존재 | side-effect task gate 정책 필요 |
| 개인정보/삭제 | 기록 삭제 UI 일부 존재 | voiceprint/profile 삭제와 sync 정책 필요 |
| 테스트 | PoC 수준 | 등록/인식/다화자/겹침/unknown/복합명령 owner 테스트 필요 |
9. 다음 문서 확장 계획
이 세션은 다음 문서로 확장한다.
| 문서 | 목적 |
|---|---|
source-map.md |
온디바이스/Cloud 파일별 책임과 함수 흐름 상세 |
speaker-context-contract.md |
speaker_context JSON 계약과 상태 전이 |
service-scenarios.md |
개인화/권한/복합명령/다화자 시나리오 |
privacy-and-safety.md |
음성 임베딩/기록/삭제/동의/권한 정책 |
test-plan.md |
PoC 재현 테스트와 서비스 확장 검증표 |
현재 1차 문서는 전체 축을 잡기 위한 overview이며, 이후 문서는 위 순서로 분해하는 것이 좋다.