SoC TaskManager Before/After Change Model
이 장은 SoC 업체 설명용 문서다. 핵심은 “기존 DeviceAgent 구조에 TaskManager layer를 왜 추가하는지”, “구조가 어떻게 바뀌는지”, “이 변경으로 어떤 이점이 생기는지”를 한 번에 설명하는 것이다.
1. 설명 대상
이 문서는 아래 상황에서 사용한다.
| 대상 | 설명 목적 |
|---|---|
| SoC/DeviceAgent 업체 | 기존 구조에 어떤 layer를 추가해야 하는지 이해 |
| Cloud/On-device 개발자 | DeviceAgent가 어떤 형태의 task/event/reason contract를 제공해야 하는지 이해 |
| 내부 리뷰어 | 단순 LLM 연동이 아니라 SoC 실행 프레임워크화 작업임을 확인 |
2. 기존 DeviceAgent 구조
현재 DeviceAgent는 이미 많은 기능을 수행한다. 주요 진입과 실행 경로는 다음 source에서 확인된다.
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/DeviceAgent.java
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/main/api/MainApi.java
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/app/AppCmd.java
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/framework/command/*.java
대표 흐름:
External method / Bundle
-> DeviceAgent
-> MainApi.executeMethod(...)
-> 개별 command/domain handler
-> AppCmd.sendModuleCommand_* 또는 AppCmd.sendModuleCallback_main(...)
소스에서 확인되는 기존 구조의 특징:
| 코드 위치 | 역할 | TaskManager 관점의 해석 |
|---|---|---|
DeviceAgent.java |
외부 method와 Bundle을 받아 MainApi.executeMethod(...)로 전달 |
기존 외부 entry |
MainApi.java |
executeMethodInternal(...)에서 main state, blocked status, FrameworkCommandBridge, 기능별 method를 처리 |
실제 legacy 실행 중심 |
FrameworkCommandBridge / framework/command/*.java |
청정/이동/설정 등 일부 기능을 command handler로 분리 | domain handler는 유지 대상 |
CleanOperationCommandHandler.java |
setAirCleanerOperation 내부 조건, IoT report, policy gate, executor 수행 |
TaskManager가 대체하면 안 되는 domain logic |
AppCmd.java |
module command/callback 전송 | 기존 callback path, TaskManager event도 이 경로를 사용 |
이 구조는 단일 기능 호출에는 동작한다. 하지만 Cloud LLM, MQTT 서버, 앱, 예약, 로컬 음성처럼 source가 늘어나고 복합명령이 들어오면 아래 문제가 생긴다.
| 문제 | 설명 |
|---|---|
| source별 실행 경로 분산 | Cloud/MQTT/App/예약이 각자 직접 method를 호출하면 정책과 이벤트가 달라진다. |
| 복합명령 순서 관리 어려움 | “거실 이동 -> 청정 -> 안방 이동” 같은 순차 task의 상태와 실패 처리가 호출자 쪽으로 밀린다. |
| 실패 이유 구조화 부족 | 자연어 메시지나 기능별 callback만으로는 replan/retry/report 판단이 어렵다. |
| terminal event 보장 부족 | accepted task가 최종적으로 완료/실패/취소됐는지 공통적으로 확인하기 어렵다. |
| 프레임워크화 어려움 | 기능 함수는 많지만 admission, queue, workflow, reason, event bus가 한 layer로 묶이지 않는다. |
3. 추가되는 TaskManager Layer
TaskManager layer는 기존 domain 기능을 대체하지 않는다. 기존 기능 앞에 공통 실행 관문을 추가한다.
Cloud / MQTT / App / Local Voice / Internal Schedule
-> submitTask / submitWorkflow
-> TaskManager
-> validation / policy / queue / workflow
-> TaskExecutorRegistry
-> 기존 domain handler 또는 SoC domain executor
-> onTaskEvent
핵심 source:
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/task/TaskManager.java
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/task/TaskPolicyRegistry.java
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/task/TaskBundleValidator.java
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/task/TaskExecutorRegistry.java
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/task/TaskExecutorBootstrap.java
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/task/TaskReasonContract.java
apps/DeviceAgent/app/src/main/java/com/sk/airbot/deviceagent/task/DevicePlanningContextProvider.java
실제 연결 방식:
MainApi.executeMethod(method, indata, outdata)
if TaskManager.handleControlMethod(method, indata, outdata, factory):
return
TaskManager.executeLegacy(method, indata, outdata, legacyRunnable)
즉 TaskManager는 기존 기능을 우회하거나 삭제하는 방식이 아니다. submitTask, submitWorkflow, getTaskStatus 같은 TaskManager API만 먼저 처리하고, 나머지는 기존 executeMethodInternal(...)로 보낸다.
4. 변경되는 구조
| 축 | 기존 | 변경 후 |
|---|---|---|
| 명령 유입 | method 단위 직접 호출 | source-agnostic task/workflow 등록 |
| 실행 전 검증 | 기능별/분산 처리 | TaskBundleValidator, policy, state/resource check |
| 실행 순서 | 호출자가 관리 | TaskManager workflow가 currentStepIndex와 subtask 상태 관리 |
| domain 연결 | 개별 handler 직접 호출 | TaskExecutorRegistry를 통해 executor binding |
| 실패 이유 | 기능별 error/toast/callback | TaskReasonContract의 reason_code, reason_params |
| 결과 통지 | 분산 callback | onTaskEvent 공통 event |
| 상위 연동 | Cloud 중심으로 오해 가능 | Cloud/MQTT/App/예약 모두 같은 framework 사용 |
4.1 실행 path 비교
단일 청정 명령
기존:
method = setAirCleanerOperation
-> MainApi.executeMethodInternal(...)
-> FrameworkCommandBridge.tryHandle(...)
-> CleanOperationCommandHandler.handleSetAirCleanerOperation(...)
-> AppCmd.sendModuleCommand_iot(...)
-> domain policy/executor
TaskManager 적용 후:
method = submitTask
taskMethod = setAirCleanerOperation
source = cloud_a2a | mqtt_server | app_remote
-> TaskManager.submitTask(...)
-> validation: action/mode/speed/operation 확인
-> policy: cleaning queue, ASYNC, timeout, cancelMethod
-> executor: CleaningAmpTaskExecutor 또는 legacy adapter
-> CleanOperationCommandHandler / domain manager
-> onTaskEvent
차이:
- 외부 source는 더 이상
setAirCleanerOperation을 직접 호출하지 않아도 된다. - TaskManager가 accepted/rejected/running/completed/failed 상태를 공통으로 남긴다.
- 실제 청정 조건 판단은 기존
CleanOperationCommandHandler와 cleaning domain이 계속 수행한다.
복합 이동/청정 명령
기존:
호출자가 setMoveTo, setAirCleanerOperation, setMoveTo를 직접 순서대로 호출해야 함
각 단계 실패 시 어느 단계가 실패했는지 공통 payload가 약함
TaskManager 적용 후:
method = submitWorkflow
subTasks:
1. setMoveTo(positionName=거실)
2. setAirCleanerOperation(action=1, mode=...)
3. setMoveTo(positionName=안방)
-> currentStepIndex/currentStepMethod 갱신
-> WORKFLOW_STEP_STARTED / WORKFLOW_STEP_COMPLETED
-> 실패 시 parent workflow reason_code 기록
5. 기대 이점
| 이점 | 설명 |
|---|---|
| source 확장성 | MQTT 서버가 들어와도 새 실행 경로를 만들지 않고 같은 submitTask/submitWorkflow를 사용한다. |
| 복합명령 대응 | 여러 기능을 workflow로 묶고, step별 진행/완료/실패를 추적할 수 있다. |
| 실패 대응 명확화 | PATH_BLOCKED, ROOM_NOT_FOUND, LOW_BATTERY 같은 구조화 reason으로 retry/replan/report를 분기한다. |
| Cloud 비용 절감 | RUNNING, PROGRESS, COMPLETED는 로컬/서버 상태 갱신으로 처리하고, 판단이 필요한 실패만 Cloud replan으로 올린다. |
| 업체 검증 가능성 | API sample, event trace, reason code, logcat evidence로 완료 여부를 검증할 수 있다. |
| 장기 프레임워크화 | DeviceAgent 내부 기능 묶음에서 SoC 실행 layer로 분리할 수 있다. |
5.1 정량/정성 효과
| 관점 | 기존 | 변경 후 기대효과 |
|---|---|---|
| 신규 source 추가 | source마다 command path와 callback 정책을 새로 맞출 위험 | source trace만 추가하고 같은 TaskManager API 사용 |
| 복합명령 | 호출자가 순서/실패/재시도를 직접 관리 | workflow가 step 상태와 terminal event를 관리 |
| 장애 분석 | 기능별 log/callback을 찾아야 함 | taskId, workflowName, reason_code로 추적 |
| 서버 연동 | Cloud/MQTT/App이 각자 다른 형태를 요구할 수 있음 | 공통 onTaskEvent payload를 source별로 소비 |
| 테스트 | 기능 단위 수동 테스트 위주 | API, workflow, reason, trace, logcat evidence로 분리 검증 |
| 장기 유지보수 | 기능이 늘수록 직접 호출/분기 증가 | policy/validator/executor registry에 method 추가 |
6. 업체 구현 관점의 핵심 요구
업체는 “기존 command handler를 모두 버리고 새로 만들라”가 아니라 아래 순서로 연결하면 된다.
- 기존
MainApi.executeMethod(...)기반 기능은 유지한다. - 외부 source는 직접 기능 method를 호출하지 않고
submitTask또는submitWorkflow로 들어오게 한다. - TaskManager가 validation, policy, queue, workflow를 먼저 처리한다.
- 실제 기기 동작은
TaskExecutorRegistry를 통해 기존 domain handler 또는 새 domain executor에 위임한다. - 모든 task는 accepted 이후 terminal event를 보장한다.
- 실패는 자유 문자열이 아니라 reason code와 params로 제공한다.
- Cloud source는 replan 후보를, MQTT/App/예약 source는 ack/status/report/retry 정책을 각각 적용한다.
6.1 업체가 특히 조심해야 할 부분
| 주의점 | 이유 |
|---|---|
| TaskManager에 domain business logic을 중복 구현하지 않기 | 기존 handler에 이미 lock mode, policy gate, cleaning transaction, map/movement 조건이 있음 |
| get/status 조회를 무조건 queue task로 만들지 않기 | get 계열은 DIRECT 또는 planning context query로 처리하는 편이 안전 |
| workflow 등록 시점에 모든 step 조건을 미리 확정하지 않기 | 1번 step 완료 후 기기 상태가 바뀌므로 2번 step 조건은 실행 직전에 확인해야 함 |
| Cloud LLM 전용 field로 고정하지 않기 | MQTT/App/예약 source도 같은 API를 타야 하므로 source, external_command_id 계열 trace 필요 |
COMPLETED마다 Cloud 호출하지 않기 |
정상 진행/완료는 상태 갱신이고, 판단이 필요한 실패만 상위 판단 후보 |
| 자유 자연어 reason만 제공하지 않기 | 업체/서버/Cloud가 동일하게 처리하려면 reason code와 params가 필요 |
7. 업체 설명 한 문장
이번 변경은 DeviceAgent에 Cloud LLM 전용 기능을 붙이는 작업이 아니라, 기존 직접 method 호출 구조 앞에 TaskManager 실행 layer를 추가해 Cloud/MQTT/App/예약/로컬 음성 명령을 하나의 SoC task framework로 수용하는 리팩터링이다.