@AppFunction 하나로, 내 앱의 기능을 AI Agent에게 제공할 수 있습니다.
플랫폼이 앱의 기능을 어떻게 발견하고, 에이전트가 어떻게 호출하는지 전체 그림을 이해합니다.
의존성 추가부터 AppFunctionService 정의, 어노테이션 작성까지 직접 해봅니다.
adb 직접 호출과 Testing Agent 두 가지 경로로 동작을 검증합니다.
sdkmanager, avdmanager, emulator, adb를 몰라도 됩니다.
SDK 설치 · 에뮬레이터 관리 · 프로젝트 생성 · 앱 배포까지 단일 진입점으로 묶여 있습니다.
android -h오늘 실제로 쓸 명령어는 파란색으로 표시했습니다.android CLI를 이용하여 SDK 36 에뮬레이터를 생성해줘”
에이전트가 android info, android sdk list로 현재 SDK 상태를 스스로 확인합니다.
API 36 시스템 이미지가 없으면 android sdk install로 내려받습니다. (수 GB, 시간이 걸립니다)
android emulator create 후 emulator list로 결과까지 확인해서 알려줍니다.
별도 스키마 언어나 서버 등록이 없습니다. 기존 함수에 @AppFunction을 붙이면 끝입니다.
isDescribedByKDoc = true — 여러분이 이미 쓰던 주석이 그대로 LLM을 위한 함수 설명이 됩니다.
내 앱 프로세스 안에서, 내 권한으로 실행됩니다. 화면을 띄우지 않고 백그라운드 실행도 가능합니다.
할일 추가, 목록 조회, 완료 처리, 마감일 변경.
곡·에피소드 재생, 플레이리스트 추가, 재생목록 검색.
한 앱의 결과를 다른 앱의 입력으로. 에이전트가 두 함수를 이어 붙입니다.
일정 생성·조회·변경, 참석자 추가, 빈 시간 찾기.
add-appfunctions의 createTask 예제. “할일로 추가해줘” 한 문장이 이 함수로 연결됩니다.isDescribedByKDoc = true — “Creates a task…” 주석이 그대로 에이전트가 읽는 명세가 됩니다.
AppFunctionInvalidArgumentException을 던져, 에이전트가 사용자에게 다시 물어보게 합니다.내 앱이 @AppFunction으로 선언한 함수가, 설치 시점에 AppFunctionManager(플랫폼)에 메타데이터로 색인됩니다.
에이전트 앱이 “지금 이 기기에서 부를 수 있는 함수 목록”을 플랫폼에 요청합니다. 앱 이름을 미리 알 필요가 없습니다.
모델이 사용자의 말과 함수 설명을 매칭해 하나를 고르고, 플랫폼을 통해 내 앱 프로세스에서 실행합니다.
아무 앱이나 다른 앱의 기능을 마음대로 부를 수 있으면 안 되겠죠. 그래서 권한이 관문입니다.
agent 앱을 run_privileged.sh로 권한을 부여해 실행합니다androidx.appfunctions 라이브러리의 어노테이션만으로 기능을 공개합니다.
@AppFunctionServiceEntryPointApp Function들을 담을 서비스를 선언합니다. — ChatApp 샘플 실제 코드KSP가 이 클래스를 상속한 구현 서비스(ChatAppFunctionService)를 생성합니다. 우리는 비즈니스 로직만 씁니다.
@AndroidEntryPoint + @Inject — 기존 Repository 계층을 그대로 재사용합니다. App Function을 위해 아키텍처를 바꿀 필요가 없습니다.
생성될 메타데이터 XML 파일명. Manifest의 android.app.appfunctions.v2 값과 짝을 맞춥니다.
@AppFunction · isDescribedByKDocKDoc이 곧 LLM에게 전달되는 함수 명세가 됩니다.“Call searchContacts first” — 함수 간 호출 순서를 문장으로 알려주면 모델이 그 순서를 지킵니다.
@throws에 “If thrown, ask the user to…”를 적으면, 실패가 대화의 다음 질문으로 이어집니다.
@AppFunctionStringValueConstraint(enumValues = [...])로 허용 값을 못 박을 수 있습니다.
@AppFunctionSerializable파라미터·반환 타입으로 쓸 커스텀 데이터 클래스를 등록합니다.각 필드 위 주석이 그대로 필드 설명으로 XML에 들어갑니다. 모델이 값을 잘못 채우는 걸 막아줍니다.
String Int Long Double Float BooleanList<T>, nullable, 기본값Uri, PendingIntent 같은 Android 타입@AppFunctionSerializable 중첩makeCall은 PendingIntent를 돌려줍니다. 에이전트가 “여기서부터는 앱 화면에서 이어가세요”라고 넘길 수 있습니다.세션 기준 1.0.0-alpha10 — alpha 단계라 API가 바뀔 수 있습니다. 샘플과 버전을 맞춰주세요.
함수를 여러 모듈에 나눠 두었다면, 앱 모듈에서 이를 합쳐주는 aggregate 단계가 필요합니다. ChatApp 샘플은 appfunctions.aggregateAppFunctions 컨벤션 플러그인으로 처리합니다.
네. BIND_APP_FUNCTION_SERVICE 권한을 가진 시스템만 바인딩할 수 있습니다. 일반 앱은 접근할 수 없습니다.
@AppFunctionServiceEntryPoint(serviceName = …)에 적은 이름으로 클래스가 생성됩니다. 그 이름을 그대로 씁니다.
appFunctionXmlFileName에 적은 파일명 + .xml. 두 값이 어긋나면 함수가 노출되지 않습니다.
App Function은 기본적으로 UI 스레드에서 실행됩니다. 네트워크·DB 작업이 있다면 반드시 suspend로 선언하고 적절한 디스패처로 전환하세요.
PendingIntent를 돌려주고 나머지는 앱 화면에서 이어가는 패턴도 고려하세요.로그인하지 않은 사용자, 무료 플랜, 아직 준비 중인 기능 — 상황에 따라 특정 함수만 숨길 수 있습니다.
@AppFunction 선언 시점에 XML의 <enabledByDefault>로 굳고, 이 API는 그 위에 사용자/앱 상태별 재정의를 얹습니다.adb shell cmd app_function앱을 설치한 뒤, 시스템이 내 함수를 실제로 부를 수 있는지 확인합니다.이 명령은 내가 이미 스펙을 알고 파라미터를 직접 채워 넣는 방식입니다.
우리가 진짜 원하는 건 Gemini 같은 에이전트가 내 함수를 스스로 발견해서, 사용자의 한 문장으로 여러 앱을 넘나드는 워크플로를 수행하는 것입니다.
agent 앱으로 “진짜 에이전트가 부르는” 경로를 확인합니다.Agent 앱으로 ChatApp의 sendMessage App Function을 호출해봅니다.
ChatApp — 함수를 제공하는 앱agent — 함수를 호출하는 앱내 앱에서 “사용자가 한 문장으로 시킬 만한 행동” 하나를 골라 App Function으로 열어봅니다.
동작하지 않아도 됩니다. 어떤 App Function을 설계했는지 보여주기만 해도 충분합니다.
플랫폼에 등록된 App Function 목록을 조회하고, 수동 입력 또는 LLM 에이전트 두 방식으로 실행해봅니다. 통합 문제 진단(troubleshoot) 기능도 있습니다.
EXECUTE_APP_FUNCTIONS는 일반 앱이 받을 수 없는 권한이라, 스크립트가 shell 신원으로 권한을 부여해 실행합니다.
purge-allowlist-cache 순으로 확인하세요.searchContacts처럼, ID를 얻는 함수가 대개 먼저 필요합니다AppFunctionService 상속 abstract 클래스@AppFunctionServiceEntryPoint 선언Android 팀이 배포하는 스킬입니다. AI 코딩 도구에 연결하면 어노테이션·Manifest·KDoc 보일러플레이트를 규칙에 맞게 작성해줍니다.
어떤 기능을 에이전트에게 공개하기로 했는지
KDoc을 어떻게 썼는지 — 가장 재미있는 부분입니다
안 된 것도 공유해주세요. 다 같이 봅니다
isDescribedByKDocAPI 표면 전체는 appfunctions/api/current.txt에서 한눈에 볼 수 있습니다.