DeviceAgent Device Context v2 Contract
device_context.v2는 DeviceAgent가 가진 기능과 상태를 Cloud A2A Planner가
안전하게 조합하기 위한 관찰 계약이다. 사용자 발화별 분기 규칙을 늘리는
대신, Planner가 Capability Card와 현재 상태 증거를 함께 보고 계획하도록 한다.
1. 왜 v2가 필요한가
현재 MR6 DevicePlanningContextProvider의 device_context.v1은 다음 정보를
제공한다.
- main state
- 배터리
- Map과 공간 목록
- 현재 위치
- 실내 공기질
- 청정 상태
- 이동 상태
- TaskManager queue와 running summary
- 이동·청정·복귀·TTS·schedule 기본 capability flag
이 정보만으로는 이동과 청정 workflow는 만들 수 있지만, 아래 판단은 불완전하다.
- 음성 세션 중 보안 순찰을 멈출지
- VitalSign과 LiveView가 카메라를 동시에 요구하는지
- AWS 연결값이 현재값인지 오래된 cache인지
- 사람 감지가 현재 감지인지 과거 event인지
- PUI 취소가 진행 중 task의 terminal인지
- 필터·열·CPU/RAM 상태 때문에 장기 task를 미룰지
v2는 더 많은 raw 값을 Cloud에 보내는 계약이 아니다. 각 domain이 raw 상태를 의미 상태, 신선도, 출처, 완료 증거로 바꿔 제공하는 계약이다.
2. 두 개의 context surface
| Surface | 소비자 | 내용 | 원칙 |
|---|---|---|---|
device_context.v2 |
DeviceAgent, on-device bridge, TaskManager, debug UI | 전체 semantic domain 상태와 evidence | 내부 계약. raw media와 credential은 금지 |
planner_context_summary.v2 |
Cloud A2A Planner | 후보 Capability Card가 요구한 allowlist만 포함 | token-safe, privacy-safe, TTS와 분리 |
전체 v2 snapshot을 LLM prompt에 그대로 넣지 않는다. Router가 후보 capability를
선정하면 context_reads의 합집합과 기본 admission 상태만 축약한다.
사용자 목표
-> catalog/shadow owner 후보
-> Capability Card 후보
-> context_reads 합집합
-> freshness/privacy 검사
-> planner_context_summary.v2
-> LLM plan
-> DeviceAgent grounding/admission
이 구조에서 LLM은 “무슨 목표를 어떤 capability 순서로 달성할지”를 판단하고, DeviceAgent는 “현재 기기에서 실제로 허용되고 실행 가능한지”를 판정한다.
3. 공통 domain envelope
모든 domain은 같은 envelope를 사용한다.
{
"available": true,
"freshness": {
"observed_at_ms": 1785326399950,
"received_at_ms": 1785326399980,
"age_ms": 50,
"ttl_ms": 2000,
"state": "fresh"
},
"provenance": {
"source_component": "DeviceAgent",
"source_symbol": "MovingController.getCurrentPosition",
"source_event": "movement_state_snapshot",
"source_kind": "domain_state",
"sequence": 41
},
"planner_exposure": "summary",
"sensitivity": "sensitive",
"data": {}
}
| 필드 | 의미 |
|---|---|
available |
producer가 유효한 의미 상태를 만들 수 있는지 |
unavailable_reason |
unavailable을 빈 값과 구분하는 reason |
observed_at_ms |
실제 상태를 관찰한 시각 |
received_at_ms |
context aggregator가 수신한 시각 |
age_ms, ttl_ms, state |
stale 판단 |
source_component, source_symbol |
값의 실제 코드 소유자 |
source_event, sequence |
callback/event 순서와 중복 제거 근거 |
planner_exposure |
summary, condition-only, internal-only, blocked |
sensitivity |
일반, 민감, 제한 정보 |
field_evidence |
domain보다 빠르게 변하는 핵심 필드의 별도 evidence |
available=false, state=stale, 값 누락은 서로 다른 상태다. Planner가 이 세
상태를 모두 “false”로 해석하면 조건부 task가 잘못 실행될 수 있다.
현재 Cloud condition evaluator는 원본 JSON path를 직접 읽는다. 값이 없을 때
not_equals가 참으로 평가되거나 지원하지 않는 operator가 fail-open되는 경로가
있어, v2 전환 시에는 KNOWN / UNKNOWN / STALE 3상태 평가와 fail-closed를
적용해야 한다.
4. 실제 기능과 관찰 데이터
아래 표는 “무엇을 할 수 있는가”뿐 아니라 시나리오 조합에 필요한 입력, 관찰, 완료 증거를 함께 정리한다.
4.1 이동·Map
| 기능 | 실제 소유자 | 입력 | 진행 관찰 | 완료·실패 증거 | 조합 예 |
|---|---|---|---|---|---|
| 공간 이동 | MovementTaskExecutor.setMoveTo |
room/area/position, timeout | current/target room, moving/paused/blocked | movement.arrived, COMPLETED/FAILED/CANCELLED |
이동 후 청정·VitalSign |
| 스테이션 복귀 | MovementTaskExecutor.returnToStation |
timeout | 이동 상태, station 접근 | movement.stationCharging |
workflow 최종 보상 |
| 이동 중지 | stopMovement |
active task correlation | controller idle | physical idle + terminal event | 사용자 취소 |
| Follow-me | AMR/Movement domain | enable | follow status | 현재 M2, terminal 보강 필요 | 따라오며 콘텐츠 |
| Map/공간 조회 | MapManager |
없음 | revision, edit 상태, room catalog | snapshot | 자연어 공간 grounding |
Planner에는 방 이름과 runtime ID만 제공하고 x/y/theta는 runtime grounding에
남긴다. 임의 좌표 생성, 부분 이름 첫 일치, 숫자 변환 실패의 0 fallback은
admission에서 거부해야 한다.
4.2 청정·공기질
| 기능 | 실제 소유자 | 입력 | 진행 관찰 | 완료·실패 증거 | 조합 예 |
|---|---|---|---|---|---|
| 고정 청정 | CleaningAmpTaskExecutor |
action/mode/speed/AI | running, paused, target area | cleaning.started/stopped |
현재 방 즉시 청정 |
| 전체 청정 | cleaning domain | mode/time/speed | area progress | cleaning.reportDone |
전 공간 관리 |
| 선택 공간 청정 | startSelectiveAirClear |
Area 목록 | current area/step | step complete/report done | 여러 방 순차 청정 |
| 공기질 기반 청정 | startAirSensorAirClear |
sensor/time policy | grade/trend | report done | 상태 적응형 청정 |
| 청정 중지 | stopCleaning, ampStop |
active task | stopped state | cleaning.stopped |
이동 전 선행 정지 |
| 공기질 해석 | AirQualityManager |
sensor snapshot | grade, dominant factor, trend | fresh snapshot | ODL 관찰 후 DEF 설명 |
PM·CO2 등의 raw 값은 내부 evidence로 보존할 수 있지만 Planner에는 등급·추세· 주요 원인처럼 의사결정에 필요한 semantic summary를 우선 제공한다.
4.3 Vision·사람·조도
| 기능 | 실제 소유자 | raw 입력 | Planner용 의미 상태 | 정책 |
|---|---|---|---|---|
| 사람 감지 | VisionAIManager |
class/confidence/bbox | present/absent/uncertain | raw frame/bbox 금지 |
| 지속 감지 | WssVisionObserver |
시간 누적 detection | sustained presence | 보안 domain만 직접 소유 |
| 저조도 | Vision report | light level | low_light true/false | evidence 품질 판단 |
| 카메라 상태 | Vision service health | 연결/오류 | normal/degraded/unavailable | capability 차단 |
| 화면 틸팅·매너 | Vision local scenario | person state | domain-owned trigger | Planner raw 제어 금지 |
Vision은 단독 사용자 capability보다 관찰 producer다. 새로운 시나리오는
person.present를 조건으로 쓸 수 있지만 confidence threshold와 debounce는
Vision domain이 소유해야 한다.
4.4 WSS 보안
| 기능 | 실제 소유자 | 입력 | 상태 모델 | 완료 증거 | 현재 범위 |
|---|---|---|---|---|---|
| 가용성 확인 | checkSecurityBasicAvailability |
사용자/정책 상태 | reason bitmask | immediate result | TaskManager |
| 보안 시작 | startSecurityMode |
mode/areas/repeat | running/stage/current area | security.started |
TaskManager |
| 순찰 | WSS domain state machine | Area list | moving/person/verify/upload | security.patrolCompleted |
domain-owned |
| pause/resume/stop | MainApiTaskExecutor + WssManager |
current session | paused/running/stopped | security.paused/resumed/stopped |
TaskManager 코드 연결, 실기기 검증 필요 |
| PIN/얼굴 검증 | WSS verify | verification request | verify stage/result | typed result 필요 | M2 |
| 증거 업로드 | WSS + Streaming/AWS | event asset/correlation | queued/uploading/retry | upload result | 민감 |
WSS는 카메라·Vision·AMR motion·security session을 독점한다. 음성 세션, VitalSign, LiveView와의 충돌은 공통 resource lease로 승격해야 한다.
4.5 LiveView·Streaming
| 기능 | 실제 소유자 | 입력 | 관찰 | 조합 제한 |
|---|---|---|---|---|
| 시작·종료 | StreamingManager |
user/room/signed URL/correlation | starting/running/stopping/stopped | explicit consent |
| 원격 수동 이동 | IStreamingControl |
movement/rotation | obstacle/keep-out/error | AMR manual lease |
| 영상 업로드 | Streaming callback | key/URL/expiry | upload lifecycle | raw URL prompt 금지 |
| Vision 전환 | StreamingManager |
stream lifecycle | Vision enabled/disabled | camera lease |
현재 exported Binder 인증과 TaskManager terminal 계약이 완성되지 않아
Capability Card는 NO_GO다. 문서에 있다고 Planner에 노출해서는 안 된다.
4.6 LLM·STT·TTS·콘텐츠
| 기능 | 실제 소유자 | 관찰 상태 | 조합 의미 |
|---|---|---|---|
| STT | on-device Agent + LlmManager |
listening/processing | 사용자의 개입 구간 |
| Cloud/on-device LLM | on-device Agent | processing/ready/error | local/cloud fallback |
| TTS | LlmTtsTaskExecutor, LlmManager |
requested/processing/playing/stopped | 음성 출력 자원 |
| 콘텐츠 | DEF/FRG/STM/ETR agent | session owner, response result | 이동·청정과 병행 가능 |
| stop/pause/resume | voice runtime | session state | 다른 장기 workflow 개입 |
setLlmTts 성공은 playback 완료가 아니다. “설명이 끝나면 이동”을 보장하려면
tts.playbackCompleted 또는 동등한 terminal evidence가 필요하다.
4.7 AWS·네트워크
| 기능 | 실제 소유자 | 관찰 상태 | Agent 활용 |
|---|---|---|---|
| MQTT 연결 | AWSIoTMQTTClient |
connected/reconnecting/disconnected | Cloud 의존 단계 admission |
| command ingress | CmdTopic, IotTaskManagerBridge |
accepted/correlation | legacy/managed 경계 |
| response publish | IotAgent | last result/error/time | callback 재전송 |
| Wi-Fi | platform/network callback | connected/quality | local fallback |
| OTA·signed URL | update/AWS domain | available/expiry/result | 유지보수·업로드 |
getAwsConnect는 live probe가 아니라 callback cache이므로
connected_cached + observed_at + last_success + last_error를 함께 제공한다.
MQTT accepted는 물리 task 완료가 아니다.
4.8 Schedule·Interaction
| 시간 모델 | 소유자 | 예 | terminal |
|---|---|---|---|
| 반복 제품 예약 | Product ScheduleManager |
매일 8시 웰컴 | product schedule lifecycle |
| 일회성 절대 시각 | TaskManager scheduler | 20분 뒤 청정 | scheduled admission event |
| 단계 상대 지연 | workflow after_step + delayMs |
도착 30초 후 앱 실행 | dependency + delay elapsed |
| event continuation | TaskManager workflow | 청정 끝나면 복귀 | completion target |
Interaction은 started -> arrived -> actionStarted -> actionEnded -> returning
-> completed를 자체 소유한다. Planner는 내부 단계를 다시 분해하지 않고
product_interaction 하나로 다루는 것이 안전하다.
4.9 VitalSign·앱 세션
| 기능 | 실제 소유자 | 관찰 | 완료 판단 |
|---|---|---|---|
| 앱 실행 | setLauncherScreen |
app session started | 시작만 확인 |
| 앱 상태 | MainApi.setAppStatus |
started/ended | 세션 종료 |
| 측정 결과 | VitalSign app | 현재 typed result 없음 | 성공·취소 구분 불가 |
현재 app.sessionEnded를 측정 성공으로 간주하면 안 된다. VitalSign은
복합 workflow의 마지막 단계 또는 사용자 확인 checkpoint로 제한한다.
4.10 건강·정비·리소스
| 관찰 | 실제 source | 의미 상태 | 활용 |
|---|---|---|---|
| 배터리 | BatteryManager, ErrorHandler |
normal/low/charging/work budget | 장기 task 축소·복귀 |
| CPU/RAM/thermal | TaskResourceMonitor |
normal/high/critical | admission 지연 |
| 필터·부품 | filter/part managers | normal/replacement due | 예방 정비 |
| firmware | UpdateManager |
idle/downloading/installing/failed | 실행 차단 |
| 오류 | ErrorHandler |
active/resolved, domain | 원인 기반 replan |
| 자원 lease | 현재 domain별 분산 | camera/motion/mic/display owner | 충돌 방지 |
현재 TaskResourcePolicy는 opt-in CPU/RAM/thermal threshold만 공통 처리한다.
카메라·Vision·VitalSign·WSS·Streaming·마이크·디스플레이 lease는 아직
domain별 정책에 흩어져 있다.
5. v2 domain과 source
| v2 domain | 현재 producer | Planner 노출 | 권장 기본 TTL |
|---|---|---|---|
core |
StateManager |
ready/main interaction owner | 2초 |
battery |
BatteryManager, ErrorHandler |
잔량·저전력·작업 budget | 10초 |
spatial |
MapManager, MovingController, AMR |
방·위치·이동 의미 상태 | 2초, Map은 revision |
environment |
AirQualityManager, CleaningTransaction |
공기질 등급·추세·청정 상태 | 30초 |
voice_llm |
LlmManager, on-device Agent |
voice/TTS/LLM owner | 2초 |
policy |
state/policy managers | blocker와 확인 필요 | 5초 또는 event |
network |
IotAgent callback, Wi-Fi | AWS cache·최근 성공·오류 | 30초 |
perception |
Vision semantic adapter | 사람·저조도·camera health | 5초 |
security |
WssManager, WssTransaction |
stage·area·pause reason | 5초 |
streaming |
StreamingManager |
lifecycle·owner | 5초 |
application |
setAppStatus, app bridge |
app/VitalSign session | 5초 |
maintenance |
filter/update/error managers | 건강 요약 | 1시간 |
resource |
TaskResourceMonitor, lease registry 목표 |
pressure·owner | 10초 |
task_manager |
TaskManager, completion store |
workflow/step/queue/reason | 2초 |
capabilities |
executor/capability registry adapter | enabled/blocked/reason | 5분 또는 revision |
TTL은 목표 기본값이다. 현재 MR6가 domain별 TTL을 모두 구현했다는 뜻이 아니다. sensor 주기, callback 특성, 제품 정책에 따라 설정으로 조정한다.
특히 TaskResourceMonitor.updateSnapshot은 현재 production producer 연결이
확인되지 않고 test에서만 호출된다. 따라서 실제 producer를 연결하기 전
resource.available=false로 내보내야 하며, unavailable을 정상 자원 상태로
간주해서는 안 된다. map.editable도 v1에서는 “편집 가능”이 아니라
실질적으로 “편집 중” 값이므로 v2에서는 policy.map_edit.active로 바로잡는다.
6. Planner 축약 규칙
- catalog/shadow가 route/flow 후보를 만든다.
- 후보 flow를 Capability Card ID로 변환한다.
- Card의
context_reads합집합만 선택한다. - 각 값의 freshness와 policy exposure를 검사한다.
- stale 값은 현재값처럼 쓰지 않고
refresh,ask,block중 하나를 선택한다. - Planner에는 room 이름, 의미 상태, 허용 조건 path를 전달한다.
- 좌표, raw frame, bbox, signed URL, PIN·얼굴 정보, unrestricted log는 제외한다.
- 최종 실행 직전 DeviceAgent가 fresh context로 admission을 다시 검사한다.
현재 Cloud의 _compact_device_context_for_planner는 v1의 위치·공기질·배터리·
TaskManager 일부를 고정 추출한다. v2 목표는 이 목록을 발화 keyword로 늘리는
것이 아니라 Capability Card context_reads 기반 projector로 교체하는 것이다.
7. TTS와 debug plane 분리
Planner context와 사용자 응답은 같은 데이터 구조가 아니다.
| Debug/Planner에는 가능 | TTS에서는 금지 |
|---|---|
| condition path | air_quality.aq_level 같은 raw key |
| room runtime ID | 위치 ID 숫자 |
| freshness/provenance | “context 기준으로” |
| reason payload | capabilities.room_cleaning=true |
| raw measurement evidence | 값 전체 나열 |
TTS presenter는 semantic state를 자연어로 바꾼다.
spatial.location.room_name=거실→ “현재 거실에 있습니다.”environment.air_quality.grade=bad→ “현재 공기질이 좋지 않은 편입니다.”battery.estimated_work_budget=low→ “긴 작업 전에는 충전하는 편이 안전합니다.”- unknown/stale → “현재 상태를 다시 확인해야 합니다.”
8. 기능 조합 방법
새 시나리오는 아래 순서로 조립한다.
Goal
-> Context-only capability로 현재 상태 확인
-> Direct capability 후보 선택
-> Resource graph와 policy gate 계산
-> Dependency/condition/time graph 생성
-> 각 step의 completion target 지정
-> local retry와 Cloud replan 경계 지정
-> compensation과 사용자 개입 정의
예시 A: 상태 적응형 청정
air_quality_assessment
-> [bad && battery work budget sufficient]
-> move_to_room
-> start_room_cleaning
-> [improvement trend or timeout]
-> return_to_station
-> voice_content summary
예시 B: 도착 후 VitalSign
move_to_room
-> movement.arrived
-> after_step_delay
-> vital_sign_session
-> user checkpoint
-> return_to_station
VitalSign result가 typed terminal이 아니므로 현재는 자동으로 다음 step을 성공 처리하지 않는다.
예시 C: 보안 순찰 중 음성 개입
security_patrol
-> voice session observed
-> local pause
-> voice_content
-> fresh security/battery/network context
-> local resume or bounded Cloud replan
-> security terminal
-> return_to_station
예시 D: 예방 정비 Agent
device_health_assessment
-> filter trend + cleaning effectiveness + recent failures
-> no side effect: explain
-> user confirms maintenance action
-> schedule_workflow or supported maintenance capability
9. 재계획 경계
Cloud LLM은 모든 progress event에 호출하지 않는다.
| Event/상태 | 기본 처리 |
|---|---|
| 정상 progress, step completed | TaskManager가 다음 step 수행 |
| 짧은 장애물, 일시 network 흔들림 | bounded local retry |
| stale context | local refresh |
| 정책 차단, 자원 충돌 | DeviceAgent가 block reason 반환 |
| 목표 변경, 대체 capability 필요 | Cloud replan |
| 사용자 확인 필요 | workflow checkpoint |
| 반복 실패, budget 초과 | 중단·보상·사용자 통지 |
일반 task timeout은 FAILED + reason_code, 예약 admission의 busy/missed-run은
BLOCKED + reason_code로 구분한다. completion target은 task event 이름이
아니라 executor가 기다리는 물리 상태다.
10. 구현 순서
- MR6에 v1과 병행하는 v2 semantic aggregator를 추가한다.
- domain별 producer adapter와 freshness/provenance를 붙인다.
- on-device bridge에 v2 snapshot과 ordered task-event relay를 연결한다.
- Cloud projector를 Capability Card
context_reads기반으로 교체한다. - DeviceAgent admission에서 fresh context와 resource lease를 재검사한다.
- TTS presenter와 debug UI를 분리한다.
- v1 consumer를 유지한 채 dual-read 후 v2로 전환한다.
device_context.v1 key를 즉시 제거하지 않는다. v2 전환 기간에는
v1 snapshot -> v2 adapter와 v2 -> legacy compact view를 함께 둔다.
11. 검증 기준
- JSON Schema로 full context sample 검증
- 모든 Capability Card
context_reads가 v2 domain에 resolve - stale/unknown/unavailable 조건 테스트
- raw frame, bbox, credential, unrestricted log가 Planner summary에 없는지 검사
- TTS에 raw key/ID/debug 문구가 노출되지 않는지 검사
- task event sequence와 duplicate 처리 검증
- PUI cancel과 physical terminal correlation 검증
- VitalSign success/cancel typed result가 없을 때 자동 성공 금지
- LiveView security gate 전 Planner 노출 금지
12. 구현 근거 경로
| Domain | MR6 source |
|---|---|
| v1 context | apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/task/DevicePlanningContextProvider.java |
| Task context API | .../task/TaskManager.java의 writeDevicePlanningContext, refreshPlanningContextIfRequired |
| 이동 완료 | .../task/MovementTaskExecutor.java, TaskCompletionStateStore.java |
| 청정 완료 | .../task/CleaningAmpTaskExecutor.java, CleaningTransaction |
| Vision | .../recognition/VisionAIManager.java, WSS WssVisionObserver |
| WSS | .../wss/WssManager.java, .../wss/data/WssTransaction.java |
| Streaming | .../streaming/StreamingManager.java, StreamingControlStub.java |
| LLM/음성 | .../llm/LlmManager.java, LlmTtsTaskExecutor.java |
| 정책 | .../settings/Settings.java, WssPolicyChecker.java, StateManager.java |
| Wi-Fi/AWS | DeviceAgent Utils, IotAgent AWSIoTMQTTClient, IotTaskManagerBridge |
| VitalSign | MainApi.setAppStatus, AppSessionCompletionBridge.java, SKLauncher2 app status |
| 정비 | UsageTimeManager.java, FilterBodyManager.java, UpdateManager.java, error managers |
| 리소스 | TaskResourceMonitor.java, TaskResourcePolicy.java |
Cloud 소비 근거:
gemini/a2a/planner/main_router_api.py::_compact_device_context_for_plannergemini/a2a/runtime/task_manager.py::_context_path_valuegemini/a2a/runtime/task_manager.py::_condition_matchesgemini/a2a/runtime/orchestrator.py의 device context presentergemini/a2a/runtime/workflow_state.py의 task-event correlation
현재 on-device
feature_a2a_task_orchestration_bridge@b857a620에는 VoiceContext,
getDevicePlanningContext, orchestration workflow/task 제출, /task_event
relay 경로가 있다. 다만 ordered dispatcher·deferred schedule helper 일부는
untracked WIP이고 runtime 연결이 끝나지 않았다. v2 문서와 sample은 현재 배포
완료를 뜻하지 않으며, additive parser, size/token budget, durable event relay
반영이 별도 필요하다.