안드로이드 AI 에이전트 구축, 파트 10: 작업 도중 예상치 못한 화면 처리
파트 9에서는 커서가 있는 단계 목록을 따라 이동하고, 확인을 위해 일시 중지하며, 클라이언트 측에 상태를 유지하는 플랫한 태스크 실행기(task executor)를 구축했습니다.
이 실행기에 내재된 가정은 다음과 같습니다: 에이전트가 특정 단계에 도착하면 예상되는 화면이 그곳에 존재한다는 것입니다.
하지만 종종 그렇지 않습니다.
권한 요청 다이얼로그가 나타납니다. 앱이 밤사이에 업데이트되어 UI가 변경되었습니다. 로딩 스피너가 여전히 실행 중입니다. 이전 단계는 성공했지만, 광고, 등급 요청 프롬프트, '새로운 기능' 오버레이와 같은 부수 효과(side effect)를 유발했습니다.
에이전트는 사용자에게 매번 물어보지 않고도, 동시에 잘못된 작업을 조용히 수행하지 않으면서 이러한 상황을 처리해야 합니다.
"예상치 못한" 것이 실제로 의미하는 바
여기에는 세 가지 뚜렷한 사례가 있습니다:
- 일시적 방해물 (Transient obstruction) — 계획의 일부는 아니지만 해제하거나 기다릴 수 있는 다이얼로그, 오버레이 또는 스피너입니다. 근본적인 화면은 여전히 존재합니다.
- 탐색 이탈 (Navigation drift) — 이전 단계가 부수 효과를 일으켰거나 앱의 흐름 자체가 변경되어 예상했던 것과 다른 화면으로 이동한 경우입니다.
- 하드 실패 (Hard failure) — 대상 요소가 현재 화면에 존재하지 않으며, 새로운 LLM(Large Language Model) 결정 없이는 복구할 수 있는 경로가 없는 경우입니다.
이 세 가지 상황을 모두 동일하게 처리하는 것이 실수입니다. 스피너는 기다려야 합니다. 권한 다이얼로그는 인라인으로 처리해야 합니다. 하드 실패는 새로운 계획이 필요합니다.
현재 화면 상태 감지하기
data class ScreenSnapshot(
val packageName: String?,
val activityName: String?,
)
각 단계를 실행하기 전에, 실행기는 스냅샷을 캡처하고 이를 해당 단계가 예상하는 것과 비교합니다.
방해물 감지기 (The obstruction detector)
object ObstructionDetector {
private val DISMISS_TEXTS = setOf(
"not now", "maybe later", "skip", "no thanks",
}
방해물 인라인 처리하기
suspend fun handleObstruction(
obstruction: ObstructionDetector.ObstructionType,
service: StareBrainService
)
권한(Permissions)은 절대 자동으로 처리되지 않습니다. 에이전트가 사용자 대신 '마이크 접근 허용(allow microphone access)' 대화 상자를 해제하는 것은 기술적으로 가능하더라도 신뢰 위반입니다. 대신 확인 단계로 격상됩니다.
실행기(executor)에 연결하기
suspend fun executeStep(step: AgentStep): StepResult {
...