UI 도구
adk-ui crate는 AI 에이전트가 도구 호출을 통해 풍부한 사용자 인터페이스를 동적으로 생성할 수 있도록 합니다. 에이전트는 폼, 카드, 알림, 테이블, 차트 등을 렌더링할 수 있으며, 이 모든 기능은 프런트엔드에서 사용할 수 있도록 JSON로 직렬화되는 타입 안전한 Rust API를 통해 제공됩니다.
만들게 될 것

주요 개념:
- 폼 - 다양한 필드 유형으로 사용자 입력 수집
- 카드 - 작업 버튼과 함께 정보 표시
- 테이블 - 행과 열로 구조화된 데이터 표시
- 차트 - 막대형, 선형, 영역형, 원형 차트로 데이터 시각화
- 알림 - 알림 및 상태 메시지 표시
- 모달 - 확인 대화상자 및 집중된 상호작용
- 토스트 - 간단한 상태 알림
- 프로토콜 상호 운용성 - UI를 A2UI, AG-UI 또는 MCP Apps 페이로드로 출력
예시: 분석 대시보드

예시: 등록 폼

작동 방식
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 1: User requests something │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ User: "I want to register for an account" │
│ │
│ ↓ │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ AI AGENT (LLM) │ │
│ │ "I should show a registration form" │ │
│ └──────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 2: Agent calls render_form tool │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 📞 Tool Call: render_form({ │
│ title: "Registration", │
│ fields: [ │
│ {name: "email", type: "email"}, │
│ {name: "password", type: "password"} │
│ ] │
│ }) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 3: Frontend renders the form │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 📋 Registration │ │
│ │ ──────────────────────────────────────────── │ │
│ │ Email: [________________________] │ │
│ │ Password: [________________________] │ │
│ │ │ │
│ │ [ Register ] │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ✅ User sees an interactive form, fills it out, clicks Register │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP 4: Form submission sent back to agent │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 📩 Event: { │
│ type: "form_submit", │
│ data: { email: "user@example.com", password: "***" } │
│ } │
│ │
│ Agent: "Great! I'll process your registration and show a success alert" │
│ │
│ 📞 Tool Call: render_alert({ │
│ title: "Registration Complete!", │
│ variant: "success" │
│ }) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
개요
UI 도구를 사용하면 에이전트가 다음을 수행할 수 있습니다.
- textarea 지원을 포함한 동적 폼을 통해 사용자 입력 수집
- 카드, 알림 및 알림 메시지로 정보 표시
- 테이블 및 대화형 차트(Recharts)로 데이터 표시
- 진행 상태 및 로딩 상태 표시(스피너, 스켈레톤)
- 여러 구성 요소를 사용한 대시보드 레이아웃 생성
- 모달을 통해 사용자 확인 요청
- 상태 업데이트를 위한 토스트 알림 표시
빠른 시작
Cargo.toml에 추가합니다.
[dependencies]
adk-ui = { git = "https://github.com/zavora-ai/adk-ui" }
adk-agent = "2.1.0"
adk-model = "2.1.0"
기본 사용법
use adk_rust::prelude::*;
use adk_ui::UiToolset;
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let model = Arc::new(GeminiModel::from_env("gemini-3.7-flash")?);
// Get all 10 UI tools
let ui_tools = UiToolset::all_tools();
// Create AI agent with UI tools
let mut builder = LlmAgentBuilder::new("ui_agent")
.model(model)
.instruction(r#"
You are a helpful assistant that uses UI components to interact with users.
Use render_form for collecting information.
Use render_card for displaying results.
Use render_alert for notifications.
Use render_modal for confirmation dialogs.
Use render_toast for brief status messages.
"#);
for tool in ui_tools {
builder = builder.tool(tool);
}
let agent = builder.build()?;
Ok(())
}
A2UI JSONL (render_screen / render_page / render_kit)
이 도구는 A2UI 렌더러와의 호환성을 위해 A2UI v0.9 JSONL을 출력합니다.
render_screen (단일 화면)
{
"surface_id": "main",
"components": [
{ "id": "root", "component": "Column", "children": ["title", "cta"] },
{ "id": "title", "component": "Text", "text": "Welcome", "variant": "h1" },
{ "id": "cta_label", "component": "Text", "text": "Continue", "variant": "body" },
{ "id": "cta", "component": "Button", "child": "cta_label", "action": { "event": { "name": "continue" } } }
]
}
render_page (다중 섹션 페이지)
{
"title": "Release Notes",
"description": "Highlights for the latest launch.",
"sections": [
{
"heading": "What’s new",
"body": "Three big improvements shipped this week.",
"bullets": ["Faster onboarding", "Better search", "New dashboards"],
"actions": [{ "label": "View details", "action": "view_details", "variant": "borderless" }]
}
]
}
render_kit (카탈로그 + 토큰 + 템플릿)
{
"name": "Fintech Pro",
"version": "0.1.0",
"brand": { "vibe": "trustworthy", "industry": "fintech" },
"colors": { "primary": "#2F6BFF" },
"typography": { "family": "Source Sans 3" },
"templates": ["auth_login", "dashboard"]
}
React 렌더러를 사용하여 A2UI JSONL을 소비합니다.
import {
A2uiStore,
A2uiSurfaceRenderer,
applyParsedMessages,
parseJsonl,
} from "@zavora-ai/adk-ui-react";
const store = new A2uiStore();
const parsed = parseJsonl(jsonl);
applyParsedMessages(store, parsed);
export function App() {
return <A2uiSurfaceRenderer store={store} surfaceId="main" />;
}
사용 가능한 도구
render_form
사용자 입력을 수집하기 위한 대화형 양식을 렌더링합니다.
{
"title": "Registration Form",
"description": "Create your account",
"fields": [
{"name": "username", "label": "Username", "type": "text", "required": true},
{"name": "email", "label": "Email", "type": "email", "required": true},
{"name": "password", "label": "Password", "type": "password", "required": true},
{"name": "newsletter", "label": "Subscribe to newsletter", "type": "switch"}
],
"submit_label": "Register"
}
다음과 같이 렌더링됩니다:
┌─────────────────────────────────────────────────┐
│ 📋 Registration Form │
│ Create your account │
│ ───────────────────────────────────────────── │
│ │
│ Username * │
│ ┌─────────────────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────────────────┘ │
│ │
│ Email * │
│ ┌─────────────────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────────────────┘ │
│ │
│ Password * │
│ ┌─────────────────────────────────────────┐ │
│ │ •••••••• │ │
│ └─────────────────────────────────────────┘ │
│ │
│ Subscribe to newsletter [○] │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ Register │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────┘
필드 유형: text, email, password, number, date, select, multiselect, switch, slider, textarea
render_card
선택적 작업 버튼이 있는 정보 카드를 표시합니다.
{
"title": "Order Confirmed",
"description": "Order #12345",
"content": "Your order has been placed successfully. Expected delivery: Dec 15, 2025.",
"actions": [
{"label": "Track Order", "action_id": "track", "variant": "primary"},
{"label": "Cancel", "action_id": "cancel", "variant": "danger"}
]
}
버튼 변형: primary, secondary, danger, ghost, outline
render_alert
알림 및 상태 메시지를 표시합니다.
{
"title": "Payment Successful",
"description": "Your payment of $99.00 has been processed.",
"variant": "success"
}
변형: info, success, warning, error
render_confirm
작업을 수행하기 전에 사용자 확인을 요청합니다.
{
"title": "Delete Account",
"message": "Are you sure you want to delete your account? This action cannot be undone.",
"confirm_label": "Delete",
"cancel_label": "Keep Account",
"variant": "danger"
}
render_table
표 형식의 데이터를 표시합니다.
{
"title": "Recent Orders",
"columns": [
{"header": "Order ID", "accessor_key": "id"},
{"header": "Date", "accessor_key": "date"},
{"header": "Amount", "accessor_key": "amount"},
{"header": "Status", "accessor_key": "status"}
],
"data": [
{"id": "#12345", "date": "2025-12-10", "amount": "$99.00", "status": "Delivered"},
{"id": "#12346", "date": "2025-12-11", "amount": "$149.00", "status": "Shipped"}
]
}
render_chart
데이터 시각화를 생성합니다.
{
"title": "Monthly Sales",
"chart_type": "bar",
"x_key": "month",
"y_keys": ["revenue", "profit"],
"data": [
{"month": "Jan", "revenue": 4000, "profit": 2400},
{"month": "Feb", "revenue": 3000, "profit": 1398},
{"month": "Mar", "revenue": 5000, "profit": 3800}
]
}
차트 유형: bar, line, area, pie
render_progress
선택적 단계와 함께 작업 진행률을 표시합니다.
{
"title": "Installing Dependencies",
"value": 65,
"description": "Installing package 13 of 20...",
"steps": [
{"label": "Download", "completed": true},
{"label": "Extract", "completed": true},
{"label": "Install", "current": true},
{"label": "Configure", "completed": false}
]
}
render_layout
여러 섹션으로 대시보드 레이아웃을 생성합니다.
{
"title": "System Status",
"description": "Current system health overview",
"sections": [
{
"title": "Services",
"type": "stats",
"stats": [
{"label": "API Server", "value": "Healthy", "status": "operational"},
{"label": "Database", "value": "Degraded", "status": "warning"},
{"label": "Cache", "value": "Down", "status": "error"}
]
},
{
"title": "Recent Errors",
"type": "table",
"columns": [{"header": "Time", "key": "time"}, {"header": "Error", "key": "error"}],
"rows": [{"time": "10:30", "error": "Connection timeout"}]
}
]
}
섹션 유형: stats, table, chart, alert, text
render_modal
확인 또는 집중적인 상호작용을 위한 모달 대화 상자를 표시합니다.
{
"title": "Confirm Deletion",
"message": "Are you sure you want to delete this item? This action cannot be undone.",
"size": "medium",
"closable": true,
"confirm_label": "Delete",
"cancel_label": "Cancel",
"confirm_action": "delete_confirmed"
}
크기: small, medium, large, full
render_toast
상태 업데이트를 위한 간단한 토스트 알림을 표시합니다.
{
"message": "Settings saved successfully",
"variant": "success",
"duration": 5000,
"dismissible": true
}
변형: info, success, warning, error
필터링된 도구
에이전트에 필요한 도구만 선택합니다.
let toolset = UiToolset::new()
.without_chart() // Disable charts
.without_table() // Disable tables
.without_progress() // Disable progress
.without_modal() // Disable modals
.without_toast(); // Disable toasts
// Or use forms only
let forms_only = UiToolset::forms_only();
UI 이벤트 처리
사용자가 렌더링된 UI와 상호작용하면(양식 제출, 버튼 클릭) 이벤트가 에이전트로 다시 전송됩니다.
use adk_ui::{UiEvent, UiEventType};
// UiEvent structure
pub struct UiEvent {
pub event_type: UiEventType, // FormSubmit, ButtonClick, InputChange
pub action_id: Option<String>,
pub data: Option<HashMap<String, Value>>,
}
// Convert to message for agent
let message = ui_event.to_message();
UI 업데이트 스트리밍
실시간 UI 업데이트의 경우 UiUpdate을 사용하여 ID별로 컴포넌트를 패치합니다.
use adk_ui::{UiUpdate, UiOperation};
let update = UiUpdate {
target_id: "progress-bar".to_string(),
operation: UiOperation::Patch,
payload: Some(Component::Progress(Progress {
id: Some("progress-bar".to_string()),
value: 75,
label: Some("75%".to_string()),
})),
};
작업: Replace, Patch, Append, Remove
컴포넌트 스키마
28개의 모든 컴포넌트 유형은 스트리밍 업데이트를 위한 선택적 id 필드를 지원합니다.
원자: Text, Button, Icon, Image, Badge
입력: TextInput, NumberInput, Select, MultiSelect, Switch, DateInput, Slider, Textarea
레이아웃: Stack, Grid, Card, Container, Divider, Tabs
데이터: Table, List, KeyValue, CodeBlock
시각화: Chart (Recharts를 통한 막대, 선, 영역, 파이 차트)
피드백: Alert, Progress, Toast, Modal, Spinner, Skeleton
React 클라이언트
독립 실행형 adk-ui 저장소에서 참조 React 구현을 제공합니다.
npm install @zavora-ai/adk-ui-react
React 클라이언트에는 다음이 포함됩니다.
- Rust 스키마와 일치하는 TypeScript 유형
- 28개 모든 유형을 위한 컴포넌트 렌더러
- 대화형 차트를 위한 Recharts 통합
- Markdown 렌더링 지원
- 다크 모드 지원
- 폼 제출 처리
- Modal 및 Toast 컴포넌트
아키텍처
Agent ──[render_* tool]──> UiResponse (JSON)
│
│ SSE
▼
Client (React)
│
└──> UiEvent (user action) ──> Agent
프로토콜 상호 운용성
13개의 모든 렌더링 도구는 protocol 인수를 통해 프로토콜을 인식하는 출력을 지원합니다:
| 프로토콜 | 설명 |
|---|---|
a2ui | A2UI v0.9에 맞춘 JSONL 표면(render_screen, render_page의 기본값) |
ag_ui | 하이브리드 AG-UI 지원: 기본적으로 호환성 래퍼를 제공하며, adk-server 클라이언트를 위한 프로토콜 네이티브 런타임 전송을 추가로 제공 |
mcp_apps | MCP 앱 페이로드와 ui:// 리소스 간 호환성, 추가형 브리지 헬퍼, 알림 흐름, 런타임 요청 필드 및 HTML/리소스 어댑터 |
protocol이 생략되면 도구는 기본 출력 형식을 사용합니다(대부분의 도구에서는 레거시 UiResponse JSON, render_screen/render_page/render_kit에서는 A2UI).
프로토콜 선택 예시:
{
"protocol": "mcp_apps",
"mcp_apps": {
"resource_uri": "ui://demo/surface"
}
}
상호 운용성 어댑터
adk-ui에는 프로토콜 변환을 위한 어댑터 기본 요소가 포함되어 있습니다.
A2uiAdapter— 표준 표면을 A2UI JSONL로 변환AgUiAdapter— AG-UI 이벤트 페이로드로 변환McpAppsAdapter— MCP Apps 리소스 페이로드로 변환
이들은 모든 도구에서 일관된 변환을 지원하는 공유 UiProtocolAdapter 트레이트를 구현합니다.
지원 중단 일정
레거시 adk_ui 런타임 프로필에는 지원 중단 메타데이터가 포함되어 있습니다.
| 날짜 | 주요 일정 |
|---|---|
| 2026-02-07 | 사용 중단 예고 |
| 2026-12-31 | 지원 종료 목표일 |
대체 항목: a2ui, ag_ui, mcp_apps
이 메타데이터는 UI_PROTOCOL_CAPABILITIES 상수를 통해 노출되며, /api/ui/capabilities에서 adk-server에 의해 제공됩니다.
능력 응답은 또한 implementationTier, specTrack, summary 및 limitations을 보고하므로, 클라이언트는 하이브리드 또는 호환성 하위 집합과 완전한 네이티브 프로토콜 지원을 구분할 수 있습니다.
adk-server이 호스트 측 경계인 경우, /api/ui/initialize, /api/ui/message, /api/ui/update-model-context, /api/ui/notifications/poll, /api/ui/notifications/resources-list-changed 및 /api/ui/notifications/tools-list-changed에서 추가적인 MCP Apps 브리지 헬퍼도 노출합니다. 이러한 엔드포인트는 기존 ADK 런타임 계약을 유지하며, 직접 요청 본문 또는 ui/... 메서드를 사용하는 JSON-RPC와 유사한 envelope를 모두 허용합니다.
런타임 소비자의 경우 adk-server은 다음도 지원합니다.
- AG-UI 네이티브 SSE 직렬화를 위한
x-adk-ui-transport: protocol_native또는uiTransport: "protocol_native" - 기존
newMessage과 함께input/agUiInput을 통한 AG-UI 이중 경로 요청 입력 mcpAppsInitialize,mcpAppsRequest및mcpAppsInitialized를 통한/api/run_sse내부의 MCP Apps 브리지 envelope
이러한 추가 기능은 선택 사항입니다. 기존 /api/run 및 /api/run_sse 소비자는 네이티브 AG-UI 전송 모드를 명시적으로 요청하지 않는 한 계속해서 일반 ADK 래퍼를 받습니다.
프레임워크가 소유한 MCP Apps 도구 응답의 경우, adk-server::ui_types은 이제 다음을 노출합니다.
- 타입이 지정된 호스트/앱 브리지 상태를 위한
McpUiBridgeSnapshot - 추가 응답 envelope를 위한
McpUiToolResult - 브리지 메타데이터를 위한
McpUiToolResultBridge(protocolVersion,structuredContent,hostInfo,hostCapabilities,hostContext,appInfo,appCapabilities,initialized)
브리지/세션 상태를 도구 응답으로 승격할 때는 McpUiBridgeSnapshot::build_tool_result(...)을 우선 사용합니다. 이는 호환성을 중시하는 호스트를 위해 resourceUri 및 인라인 html 폴백을 유지하면서 도구 결과의 형태를 표준화합니다.
임베디드 또는 브라우저 호스트 매핑의 경우, 추가적인 HTTP 브리지는 다음과 같이 호스트/앱 흐름에 대응합니다.
ui/initialize->/api/ui/initializeui/message->/api/ui/messageui/update-model-context->/api/ui/update-model-contextnotifications/resources/list_changed->/api/ui/notifications/resources-list-changednotifications/tools/list_changed->/api/ui/notifications/tools-list-changed- 대기 중인 호스트 알림 ->
/api/ui/notifications/poll
직접 /api/ui/* 브리지 엔드포인트는 MCP Apps 호스트에 권장되는 수명 주기 경로입니다. 런타임 측 MCP Apps 요청 필드는 혼합 클라이언트 또는 레거시 클라이언트를 위한 추가적인 호환성 경로로 계속 사용할 수 있습니다.
예제
독립형 adk-ui 저장소에 실행 가능한 UI 예제가 포함되어 있습니다:
| 예제 | 설명 | 실행 명령 |
|---|---|---|
ui_agent | 콘솔 데모 | cargo run --bin ui_agent |
ui_server | HTTP 서버와 SSE | cargo run --bin ui_server |
ui_react_client | React 프런트엔드 | cd ui_react_client && npm run dev |
adk-ui 저장소의 해당 예제를 실행하여 Rust 및 React 패키지가 동일한 릴리스 라인을 유지하도록 하세요.
샘플 프롬프트
다음 프롬프트로 UI 도구를 테스트하세요:
# Forms
"I want to register for an account"
"Create a contact form"
"Create a feedback form with a comments textarea"
# Cards
"Show me my profile"
"Display a product card for a laptop"
# Alerts
"Show a success message"
"Display a warning about expiring session"
# Modals
"I want to delete my account" (shows confirmation modal)
"Show a confirmation dialog before submitting"
# Toasts
"Show a success toast notification"
"Display an error toast"
# Tables
"Show my recent orders"
"List all users"
# Charts
"Show monthly sales chart"
"Display traffic trends as a line chart"
"Show revenue breakdown as a pie chart"
# Progress & Loading
"Show upload progress at 75%"
"Display a loading spinner"
"Show skeleton loading state"
# Dashboards
"Show system status dashboard"