제공자 연결과 TTS 전달 — 이전 연결의 결과가 새 작업에 섞이지 않도록
먼저 역할 구분: AI Agent는 실행하고 프레임워크는 연결·작업을 관리한다.
이 문서의 TTS 전달은 합성·재생 엔진이 아니라 AI Agent의 실행 결과를 Task에 연결하는 내부 처리다.
TtsManagedHost 통합 관리 객체와 LLM 종료 결합은 내부 구현으로 추가됐다. 실제 생산자 실행 키 전달과 Android 자동 결선은 남아 있다.
아래 수치는 기존 연결·전달 큐 구현 당시의 검증 기록이다. 최신 호스트 검증은 진행 현황에서 구분해 본다.
전체 구조에서 어디인가
전체 모듈·폴더 지도의 기능 상태 관리 → LLM 도메인 → Task 결과 처리 부분을 확대한 문서다. 새 대형 서비스를 추가하는 대신 기존 폴더의 책임을 보완한다. 기본 Runtime은 Runtime·State·Evidence·Policy·Resource·Capability·Task의 7개를 유지한다.
그림의 A~E는 읽는 순서이며 모두 동일 프로세스에 자동 연결됐다는 뜻은 아니다. 파랑은 내부 소스, 황색 점선은 실제 생산자·Android 호스트와의 미연결 경계다. 구현 검증 기록은 아래 검증 절과 진행 현황에서 구분한다.
1. 왜 서비스 생명주기 외에 연결 식별이 필요한가
예를 들어 LLM 서비스는 계속 READY인데 음성 제공자만 끊겼다 다시 연결될 수 있다. 서비스 세대만 비교하면 이전 연결에서 늦게 온 READY를 새 연결의 준비 완료로 잘못 받아들일 수 있다. 따라서 기능 ID + 소유 서비스 세대 + Capability 세대 + 연결 식별자를 함께 확인한다.
| 상태의 종류 | 소유자 | 의미 |
|---|---|---|
| 서비스 READY | control/lifecycle/ |
내부 서비스가 요청을 처리할 준비가 됨 |
| 제공자 연결·준비 | control/capability/ |
지금 연결된 실행 제공자의 관측 상태 |
| TTS 실행 관측의 해석 | domain/llm/completion/ |
AI Agent가 보내는 해당 발화의 시작·완료·중단 관측을 해석함. 실제 합성·재생 주체가 아님 |
| Task 진행·종료 | execution/task/ |
어느 작업·시도가 어떤 결과로 끝났는가 |
| 자원 HELD·FREE | control/resource/ |
누가 자원을 소유하며 재사용 확인이 됐는가 |
READY 관측은 가용성 조회의 입력일 뿐이다. Policy 승인이나 Resource 확보를 대신하지 않는다. 연결 확인과 Task 확인을 여러 서비스에 걸친 원자적 실행 승인으로 간주하지 않는다. 전달 직전 검사 이후 이미 호출에 진입한 동작을 연결 해제로 소급 취소할 수는 없다.
2. 폴더와 클래스별 책임
아래 경로는 skix-framework/base/services/core/java/com/sk/airbot/frameworkservice/server/ 기준이다.
| 폴더 / 클래스 | 맡는 일 | 넘지 않는 경계 |
|---|---|---|
control/capability/CapabilityManagerService |
현재 연결 발급·교체, 준비 상태 순서 검사 | 작업 완료·물리 정지 판단 안 함 |
control/capability/ProviderConnectionToken |
기능·서비스 세대·연결 식별을 불변 값으로 보존 | Binder 인증·권한 토큰 아님 |
control/capability/ProviderReadinessUpdateResult |
반영·이전 연결·잘못된 순서의 결과 구분 | 문자열로 거부 사유를 흩뜨리지 않음 |
domain/llm/completion/TtsObservationDelivery |
실행 키가 있는 관측을 제한된 FIFO로 전달·재시도 | 현재 작업 키를 추정하지 않음 |
domain/llm/completion/TtsTaskCompletionService |
MR6의 시작·IDLE·STOP·웨이크워드 의미 해석 | 연결 이력이나 자원 소유권 관리 안 함 |
execution/task/completion/TaskCompletionCoordinator |
작업·시도·서비스 세대와 완료 관측 연결 | TTS 장치를 직접 제어하지 않음 |
테스트는 base/services/tests/src/ 아래 같은 패키지에 배치한다.
공개 SDK Manager나 도메인 Constants를 우회해 레거시 Bundle을 새 서버에 퍼뜨리는 변경은 하지 않는다.
3. 연결 시작부터 해제까지
- 기능 등록 후
beginProviderConnection(id)를 호출한다. 소유 서비스와 Capability가 READY여야 한다. - 새 연결은 NOT_READY로 시작한다. 이전 연결 토큰은 즉시 현재 연결 자격을 잃는다.
updateProviderReadiness(token, sequence, readiness)에 양수 증가 sequence를 보낸다.- 현재 연결의 DISCONNECTED가 수락되면 연결은 닫힌다. 늦은 READY로 다시 살릴 수 없다.
- 재연결은 새 토큰을 발급받아 시작한다. 기존 연결의 해제 통지는 새 연결을 닫지 못한다.
같은 sequence를 다시 보내거나 역순 관측을 보내면 상태를 바꾸지 않는다. 한 번 연결 관리가 시작된 기능은 서비스 세대만 가진 기존 API로 준비 상태를 갱신할 수 없다. 이 제한은 연결 해제와 서비스 재시작 뒤에도 유지한다. 기능 등록은 기본 128개로 제한하고 기능별 연결 메타데이터만 보관하며 연결 이력은 쌓지 않는다.
4. TTS 관측을 유실하지 않고 전달하는 방법
입력은 연결 토큰, TaskExecutionKey, completionKey, eventId, 양수 sequence, 정수 status를 보존한다.
TTS 문장 원문은 대기열에 넣지 않는다. 입력 수락은 완료 처리 성공과 다르다.
| 상황 | 전달 계약 | 호출자 책임 |
|---|---|---|
| 정상 입력 | 현재 세션 검사 후 FIFO 수락 | drain 호출 |
| 같은 이벤트의 같은 내용 | 중복 적재하지 않음 | 새 ID로 바꿔 재전송하지 않음 |
| 같은 eventId의 다른 sequence·상태 / 역순 | CONFLICT / OUT_OF_ORDER로 거부 | 원본 순서·내용 확인 |
| 큐가 가득 참 | 미수락 반환, 순서 번호를 소비하지 않음 | 원본 보존 후 다시 입력 |
| 완료 서비스 BUSY_RETRY | 같은 머리 항목 유지, 즉시 반환 | 나중에 drain 재호출 |
| 완료 호출 예외 | 명시적으로 닫지 않은 동일 세션에 현재 항목 보존 | 유효하면 재시도, 무효이면 drain/close로 폐기 수 확인 |
| 연결·실행 무효화 | 아직 미전달인 입력 폐기 | 폐기 수 확인·새 실행 식별 사용 |
기본 큐 용량은 64개이며 전달 중인 항목도 포함한다. 중복 이력도 동일 상한으로 제한하며 이력에서 빠진 옛 입력은 마지막 수락 sequence로 거부한다. 한 번의 drain은 용량 이내에서만 진행하고 단일 drain만 허용한다. 완료 처리 호출은 큐 잠금 밖에서 실행해 재진입 시 잠금 대기나 중복 전달을 만들지 않는다.
예를 들어 시작 1번 뒤에 종료 2번이 BUSY_RETRY라면 종료 2번 그대로 재전달한다. 뒤에 STOP 3번이 대기해도 2번을 추월하지 않는다. 새 이벤트 ID나 새 sequence를 만들어 재시도하지 않는다. 이미 전달에 진입한 호출은 close로 소급 취소하지 못한다. 폐기 수는 그 호출과 구분한다. close한 세션은 BUSY나 예외가 돌아와도 되살리지 않는다. 이전 drain 호출이 끝나기 전에는 새 재생 세션을 시작하지 않는다.
호스트의 재연결 처리 순서는 이전 세션 drain 또는 정확한 키로 close → 폐기 수 기록 → 새 begin이다.
begin은 반환값이 없는 시작 API이므로 이전 큐를 몰래 버리며 교체하지 않는다.
대기 입력이 0개라도 무효 세션은 먼저 정리해야 한다.
오래된 입력의 STALE_SESSION 거부와 무효화 중 전달 예외는 대기 입력을 몰래 폐기하지 않는다.
호스트가 다음 drain/close에서 폐기 수를 확인한다. 예외가 난 전달 중 항목 자체를 새 세션에 복원하지는 않는다.
5. MR6 행동과 실제 장치 연결의 남은 차이
MR6 task_manager의 d1d8dfbc4c807a32990f4350cf09c49f159f5848에서
LlmTtsTaskExecutor는 완료 연결을 먼저 등록한 뒤 TTS 명령을 보낸다.
LlmTaskCompletionBridge는 처리·재생 시작 후 IDLE을 성공으로, STOP·웨이크워드를 중단으로 취급한다.
시작 전 IDLE은 완료가 아니다. 이 의미를 기존 MR7 완료 서비스에서 유지한다.
다만 원본 장치 콜백은 status, ttsType만 전달하고 실행 키가 없다.
이번 내부 전달 계약이 있다고 기존 콜백을 현재 작업에 자동 연결하지 않는다.
생산자부터 실행 키를 반환하는 경로와 재시도 호스트를 연결해야 제품 동작이 된다.
현재 MR7에도 남아 있는 혼합 책임
apps/DeviceAgent/.../llm/LlmManager.java의 setStatus는 상태 콜백을 보낸 뒤,
IDLE이면 TiltingCond.AIRSENSOR를 해제하고 대기 중인 후속 TTS를 시작한다.
즉 상태 통지·구동 조건 변경·다음 발화 선택이 아직 한 메서드에 함께 있다.
새 전달 큐는 이 제품 분기를 그대로 옮기는 장소가 아니다.
후속 이관 시 LLM의 관측 해석, 도메인 간 구동 정책, 후속 발화 선택으로 나누되
원본 동작을 먼저 특성 테스트로 고정해야 한다. 이번 변경은 그 구동 분기를 수정하지 않는다.
| 현재 원본의 조건·순서 | 이관 시 책임 후보 | 먼저 고정할 검증 |
|---|---|---|
| 상태 콜백을 보낸 다음 IDLE 후처리 수행 | LLM 관측 발행과 제품 후처리 분리 | 콜백에서 읽는 TTS 종류가 초기화 전 값이라는 순서 |
| IDLE이면 AIRSENSOR 틸팅 조건 해제 | 구동 도메인이 조건을 소유하고 도메인 간 정책으로 요청 | 실제 AIRSENSOR 발화인지, 다른 발화의 종료가 조건을 지우는지 |
| IDLE이며 pendingTTS가 있으면 후속 발화 요청 후 비움 | LLM 후속 발화 선택 또는 Task 실행 요청 | 요청 실패·재진입·중단 시 대기 발화 보존 여부 |
위 표는 이관 후보와 검증 항목이며 새 정책을 적용했다는 뜻은 아니다. 특히 원본에는 AIRSENSOR 안내 TTS일 때만 조건을 설정하도록 보완이 필요하다는 TODO가 있다. 현재 동작을 기록하는 테스트와 제품 사양에 맞는 목표 동작의 테스트를 구분해야 한다.
연결 해제는 Resource HELD를 해제하거나 FREE로 만들지 않는다. 자원 재사용은 새 관측 토큰을 캡처한 뒤 실제 상태를 다시 읽어 확인해야 한다. 과거 IDLE에 새 토큰을 붙이는 방식은 금지하며 TTS 논리 완료와 물리 정지도 구분한다.
6. 검증과 후속 연결
구현 커밋은 연결 보호 b5f133c, TTS 전달 보완 a90a2d3, 폐기 수 보존 fce8b64·67d5588이다.
초안 검토에서 발견한 종료 후 세션 잔류, 무제한 이력, close 후 재적재 문제를 수정했다.
실제 Task·Capability fixture로 실패를 먼저 재현하고 회귀 검사를 추가했다.
| 2026-09-08 검증 | 결과 | 증명하는 범위 |
|---|---|---|
| 서버 JVM 전체 강제 재실행 | 38개 클래스 / 265개 통과 | 새 연결 10개·전달 19개와 기존 서버 회귀 |
| SDK JVM 전체 강제 재실행 | 11개 클래스 / 37개 통과 | SDK 계약 회귀 |
| release Java 컴파일 | 통과 | Java 소스·의존성 컴파일, APK 설치 아님 |
| 폴더·레이어 및 각 회귀, Resource snapshot 정적 검사 | 통과 | canonical 경로와 계층 경계 |
위 JVM 검사는 실패·오류·건너뜀 모두 0이다. TTS 완료 서비스의 기존 25개 검사도 서버 전체 수에 포함한다. 전달 중 close→BUSY/예외, FULL 재시도, 중복 이력 상한, 재연결·외부 Task 종료 후 정리를 검증한다. Wiki 회귀 65개 통과, lint 오류 0개·기존 최신성 경고 8개이며 정적 사이트 빌드도 통과했다. 최종 독립 사양·코드 품질 검토도 통과했다. 재연결 중 예외가 난 머리 항목과 대기 항목은 다음 명시 정리에서 함께 집계하며, 먼저 close한 세션에는 항목을 되살리지 않는다. 실기기 미검증: Android 생산자 실행 키 전달, 자동 호스트 결선, APK 설치, AOSP 제품 부팅, 실제 스피커 출력과 물리 정지 확인은 이번 내부 계약 검사에 포함하지 않는다.
후속 순서는 생산자의 실행 키 보존 → 호스트 재시도·연결 해제 연동 → 도메인 물리 상태 재확인 → Policy·Resource를 거치는 실제 실행 경로 → Android 제품 통합이다. AMR·Cleaning의 완료 조건을 TTS의 IDLE 규칙으로 통일하지 않고 각각의 MR6 원본 사양을 분석한다.