ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • a2ui: AI 에이전트가 처음 보는 Android UI를 그리는 법
    Android📱 2026. 8. 3. 19:18

    AI 에이전트는 추론도 하고 도구도 호출하고 대화도 잘합니다. 그런데 네이티브 화면은 못 그립니다. 앱이 어떤 컴포넌트를 가졌는지 모르기 때문이죠. 구글 Android 팀이 오픈소스로 개발 중인 a2ui(Agent to User Interface)는 바로 이 간극을 "공유 계약"으로 메우려는 AndroidX 모듈입니다. 이 글에서 그 설계 아이디어를 정리합니다.

    먼저 짚고 갈 점: a2ui는 2026년 7월 기준 미출시·비stable·개발 중(work in progress)입니다. Compose 렌더러와 메시지 orchestration은 아직 없어서, 오늘 앱에 의존해 쓸 수 있는 artifact가 아닙니다. 다만 프로토콜과 데이터 모델은 이미 존재해 개념을 미리 볼 수 있습니다.

    풀려는 문제

    에이전트가 화면을 그리게 하려는 두 가지 흔한 우회책은 모두 한계가 있습니다.

    • 텍스트/마크다운 반환 → 네이티브 컨트롤·제스처를 다 버리게 됩니다.
    • 모든 화면을 미리 정의 → 유연한 에이전트를 쓰는 의미가 사라집니다.

    필요한 것은 공유 계약(shared contract)입니다. 클라이언트가 제공하는 UI 빌딩블록을 기계가 읽을 수 있게 기술하는 것. a2ui의 답은 JSON Schema입니다.

    핵심 아이디어

    동작 흐름은 네 단계입니다.

    1. 클라이언트가 컴포넌트를 name + description + propertySchema자기 기술합니다.
    2. 에이전트가 그 카탈로그를 읽고 적합한 컴포넌트·함수를 선택합니다.
    3. surface 프로토콜로 UI/데이터를 스트리밍합니다.
    4. 클라이언트가 스키마로 검증 후 렌더링하고, 사용자 액션·에러를 회신합니다.

    핵심 분담: 클라이언트는 "무엇이 존재하는지"를 통제하고, 에이전트는 "무엇을 보여줄지"를 통제합니다.

    스스로를 기술하는 컴포넌트

    a2ui에서 컴포넌트는 고정 위젯이 아니라 자기 자신을 광고하는 정의입니다. name·description은 컴파일러가 아니라 LLM이 읽으라고 쓴 것이고, propertySchema가 받는 속성 형태를 기술합니다.

    public interface A2uiCoreComponentDefinition {
        public val name: String          // 에이전트에게 제공될 이름
        public val description: String   // 언제 쓸지 에이전트가 알도록
        public val propertySchema: A2uiSchema
    }

    A2uiCoreCatalog는 컴포넌트 정의 + 호출 가능 함수(A2uiFunction) + 테마 스키마의 모음입니다. 덕분에 에이전트는 앱과 함께 컴파일될 필요가 없습니다. 이름·설명·스키마만 읽고 무엇을 보여줄지 스스로 결정합니다.

    공유 언어로서의 JSON Schema

    A2uiSchema는 sealed 타입으로, 밖으로 직렬화만 합니다(toJsonSchema(), 역파서는 없음). 에이전트에게 광고하고 들어오는 payload를 검증하는 용도입니다.

    • 리프 타입: String/Number/Boolean/Array/Object/Enum/Const/Ref
    • 조합자: AnyOf/OneOf/AllOf
    • commontypes 어휘: data binding({path}), child list(정적 배열 또는 동적 템플릿), A2uiDynamicValueSchema(리터럴/바인딩/함수호출의 oneOf), action(서버 이벤트 또는 클라 함수호출)

    Surface 프로토콜

    UI 단위는 surface(surfaceId)이고, 서버가 메시지로 이를 구동합니다.

    메시지 역할
    A2uiCreateSurfaceMessage 카탈로그에 묶인 surface 생성
    A2uiUpdateComponentsMessage 컴포넌트 삽입/교체
    A2uiUpdateDataModelMessage data model의 path에 값 쓰기 (null = 삭제)
    A2uiDeleteSurfaceMessage surface 파괴

    클라이언트에서 서버로의 회신은 A2uiUserAction(click/swipe...)과 A2uiClientError(VALIDATION_FAILED/RUNTIME_ERROR)입니다. 에러조차 에이전트가 읽고 진단하도록 설계된 점이 인상적입니다.

    데이터 바인딩 (스트리밍)

    컴포넌트 템플릿은 한 번만 전송하고, 데이터는 delta로 스트리밍합니다. 속성이 리터럴 대신 data model로의 바인딩을 가질 수 있고, 경로는 JSON Pointer(A2uiDataPath, RFC 6901)입니다. 예컨대 /settings/volume가 갱신되면 그 경로에 바인딩된 속성만 업데이트되고 컴포넌트를 다시 보내지 않습니다.

    정리하면 컴포넌트 트리 = 구조, 데이터 모델 = 상태이고, 둘을 경로로 결합합니다.

    플랫폼 독립 설계

    엔진은 A2uiCoreDataModel·A2uiCoreComponentRegistry인터페이스로 두고, 호스트가 네이티브 reactive state로 구현합니다(Compose라면 SnapshotStateMap). 그래서 엔진 코드는 Compose를 전혀 언급하지 않습니다. 프로토콜 계층이 특정 UI 툴킷에 종속되지 않는 것이죠.

    오늘의 실제 상태 (정직한 평가)

    4개 Gradle 모듈의 성숙도가 제각각입니다.

    • a2ui-model — 가장 성숙. 프로토콜 메시지, A2uiComponentPayload, A2uiDataPath, 전체 A2uiSchema 계층이 구현·테스트됨. 진짜 기반.
    • a2ui-engine — 부분적. surface 모델·검증기는 구현됐지만 data model/registry/catalog는 구현체 없는 인터페이스이고, orchestration이 없음.
    • a2ui-core — Kotlin 소스가 아예 없는 빈 모듈. (A2uiCore* 클래스들은 실제로 engine에 있음)
    • a2ui-compose — placeholder 하나. 렌더러 없음.

    이 밖에도 @Serializable/JSON 코덱 부재(와이어 파싱 미작성), SurfaceActor 큐 미구현 등 공백이 있습니다.

    지금 할 수 있는 것

    • 렌더러·orchestration이 없어 앱에 넣어 에이전트가 화면을 그리게 할 수는 없습니다.
    • 다만 공개 계층인 a2ui-model로 프로토콜 타입을 구성하고 컴포넌트 계약을 스키마로 기술한 뒤 toJsonSchema()로 에이전트용 JSON Schema를 방출할 수 있습니다. 서버 측 계약·스키마 어휘 프로토타이핑에는 지금도 유용합니다.

    정리

    a2ui의 핵심은 "에이전트에게 화면 그리는 법을 가르친다"가 아니라, "앱이 가진 UI 능력을 기계가 읽을 수 있는 계약으로 광고한다"입니다. 클라이언트가 존재를, 에이전트가 표현을 나눠 갖는 이 구도는 앞으로 에이전트 기반 UI의 표준 패턴이 될 가능성이 있습니다. 아직 개발 중이지만 프로토콜 설계를 미리 들여다볼 가치는 충분합니다.

    이 글은 개인 개발 학습 위키의 정리 노트를 기반으로 작성되었습니다.

Designed by Tistory.