2026-03-16 09:01:01 +08:00
# UI Schema Protocol
> **NOTE**: This document is the single source of truth. All implementations must follow this specification.
## Overview
A generic UI schema for rendering tool/agent execution results. Designed for AI Agent / Tool ecosystem with extensibility.
**Design Philosophy ** : Keep only "primitive components + layout containers". Frontend only needs to recursively render the root layout tree.
## Version
- **Current**: `2.0`
- **Status**: Active
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ UiSchemaRenderer (root) │
│ - version / locale / status / theme │
│ - meta (protocol-level metadata) │
│ - root (UiLayoutNode - stack or grid) │
├─────────────────────────────────────────────────────────────┤
│ Rendering Flow: │
│ 1. Backend returns UiSchemaRenderer with root layout │
│ 2. Frontend recursively renders root layout tree │
│ 3. Layout nodes (stack/grid) contain children │
│ 4. Primitive nodes (text/icon/button/etc) are leaves │
└─────────────────────────────────────────────────────────────┘
```
---
## Data Types
### UiStatus
``` typescript
type UiStatus = 'info' | 'success' | 'warning' | 'error' | 'pending' ;
```
### IconSource
``` typescript
type IconSource = 'icon' | 'emoji' | 'url' ;
```
### TextFormat
``` typescript
type TextFormat = 'plain' | 'markdown' ;
```
### TextRole
``` typescript
type TextRole = 'title' | 'subtitle' | 'body' | 'caption' | 'code' ;
```
### ButtonStyle
``` typescript
type ButtonStyle = 'primary' | 'secondary' | 'ghost' | 'danger' ;
```
### LayoutDirection
``` typescript
type LayoutDirection = 'vertical' | 'horizontal' ;
```
### LayoutAppearance
``` typescript
type LayoutAppearance = 'plain' | 'card' | 'section' ;
```
### LayoutAlign
``` typescript
type LayoutAlign = 'start' | 'center' | 'end' | 'stretch' ;
```
### LayoutJustify
``` typescript
type LayoutJustify = 'start' | 'center' | 'end' | 'space-between' ;
```
### RendererTheme
``` typescript
type RendererTheme = 'default' | 'light' | 'dark' ;
```
---
## Root Structure
``` typescript
interface UiSchemaRenderer {
version : string ; // "2.0"
locale : string ; // "zh-CN"
status : UiStatus ;
theme : RendererTheme ;
// Protocol-level metadata (not rendered)
meta ? : {
requestId? : string ;
toolId? : string ;
traceId? : string ;
userId? : string ;
} ;
// Root layout node
root : UiLayoutNode ;
}
```
---
## Node Types
### Primitive Components
#### 1. Text Node
``` typescript
interface UiTextNode extends UiBaseNode {
type : 'text' ;
content : string ;
format : TextFormat ; // 'plain' | 'markdown'
role : TextRole ; // 'title' | 'subtitle' | 'body' | 'caption' | 'code'
status? : UiStatus ;
maxLines? : number ;
visible? : boolean ;
}
```
#### 2. Icon Node
``` typescript
interface UiIconNode extends UiBaseNode {
type : 'icon' ;
source : IconSource ; // 'icon' | 'emoji' | 'url'
value : string ;
color? : string ;
size? : number ;
visible? : boolean ;
}
```
#### 3. Badge Node
``` typescript
interface UiBadgeNode extends UiBaseNode {
type : 'badge' ;
label : string ;
status : UiStatus ;
visible? : boolean ;
}
```
#### 4. Button Node
``` typescript
interface UiButtonNode extends UiBaseNode {
type : 'button' ;
label : string ;
style : ButtonStyle ;
disabled? : boolean ;
icon? : UiIconSpec ;
action : UiActionPayload ;
visible? : boolean ;
}
```
#### 5. Key-Value Node
``` typescript
interface UiKvNode extends UiBaseNode {
type : 'kv' ;
items : UiKvItem [ ] ;
columns? : number ;
visible? : boolean ;
}
interface UiKvItem {
key : string ;
label? : string ;
value : any ;
copyable? : boolean ;
}
```
#### 6. Divider Node
``` typescript
interface UiDividerNode extends UiBaseNode {
type : 'divider' ;
inset? : number ;
visible? : boolean ;
}
```
### Layout Containers
#### 7. Stack Node
``` typescript
interface UiStackNode extends UiBaseNode {
type : 'stack' ;
direction : LayoutDirection ; // 'vertical' | 'horizontal'
gap? : number ;
appearance : LayoutAppearance ; // 'plain' | 'card' | 'section'
status? : UiStatus ;
align? : LayoutAlign ;
justify? : LayoutJustify ;
wrap? : boolean ;
children : UiNode [ ] ;
visible? : boolean ;
}
```
#### 8. Grid Node
``` typescript
interface UiGridNode extends UiBaseNode {
type : 'grid' ;
columns : number ;
gap? : number ;
appearance : LayoutAppearance ;
status? : UiStatus ;
children : UiNode [ ] ;
visible? : boolean ;
}
```
### Base Node
``` typescript
interface UiBaseNode {
id? : string ;
visible? : boolean ;
}
```
### Node Union
``` typescript
type UiNode =
| UiTextNode
| UiIconNode
| UiBadgeNode
| UiButtonNode
| UiKvNode
| UiDividerNode
| UiStackNode
| UiGridNode ;
type UiLayoutNode = UiStackNode | UiGridNode ;
```
---
## Action Payloads
``` typescript
type UiActionPayload =
| NavigateAction
| UrlAction
| EventAction
| ToolAction
| CopyAction
| PayloadAction ;
// Navigation action
interface NavigateAction {
type : 'navigation' ;
path : string ;
params? : Record < string , any > ;
}
2026-03-18 13:35:25 +08:00
// Navigation Contract (current implementation constraint)
// 1) path MUST be an internal app route and MUST be fully materialized
// (e.g. '/todo/123', not '/todo/:id').
// 2) path MUST NOT include query string or fragment.
// 3) params, when provided, is treated as query params only.
// 4) params values MUST be scalar (string | number | boolean).
// 5) Backend MUST generate path from route catalog
// `backend/src/core/config/static/route/frontend_routes.yaml`.
2026-03-16 09:01:01 +08:00
// URL action
interface UrlAction {
type : 'url' ;
url : string ;
target ? : '_self' | '_blank' ;
}
// Event action
interface EventAction {
type : 'event' ;
event : string ;
payload? : Record < string , any > ;
}
// Tool action
interface ToolAction {
type : 'tool' ;
toolId : string ;
params? : Record < string , any > ;
}
// Copy action
interface CopyAction {
type : 'copy' ;
content : string ;
successMessage? : string ;
}
// Payload action
interface PayloadAction {
type : 'payload' ;
payload : Record < string , any > ;
submitTo? : string ;
}
```
---
## Icon Specification
``` typescript
interface UiIconSpec {
source : IconSource ;
value : string ;
color? : string ;
size? : number ;
}
```
---
## Usage Patterns---
## JSON Examples
### Example 1: Simple Text
``` json
{
"version" : "2.0" ,
"locale" : "zh-CN" ,
"status" : "success" ,
"theme" : "default" ,
"root" : {
"type" : "stack" ,
"direction" : "vertical" ,
"gap" : 12 ,
"appearance" : "plain" ,
"children" : [
{
"type" : "text" ,
"content" : "操作成功" ,
"role" : "title"
} ,
{
"type" : "text" ,
"content" : "您的事项已创建完成" ,
"role" : "body"
}
]
}
}
```
### Example 2: Card with Actions
``` json
{
"version" : "2.0" ,
"locale" : "zh-CN" ,
"status" : "success" ,
"theme" : "default" ,
"root" : {
"type" : "stack" ,
"direction" : "vertical" ,
"gap" : 16 ,
"appearance" : "card" ,
"children" : [
{
"type" : "text" ,
"content" : "日程已创建" ,
"role" : "title"
} ,
{
"type" : "kv" ,
"items" : [
{ "key" : "title" , "label" : "主题" , "value" : "Q1 规划会议" } ,
{ "key" : "time" , "label" : "时间" , "value" : "2026-03-15 14:00" }
] ,
"columns" : 2
} ,
{
"type" : "stack" ,
"direction" : "horizontal" ,
"gap" : 8 ,
"children" : [
{
"type" : "button" ,
"label" : "查看详情" ,
"style" : "primary" ,
"action" : { "type" : "navigation" , "path" : "/calendar/evt_abc123" }
} ,
{
"type" : "button" ,
"label" : "删除" ,
"style" : "danger" ,
"action" : { "type" : "tool" , "toolId" : "calendar.delete" , "params" : { "eventId" : "evt_abc123" } }
}
]
}
]
}
}
```
### Example 3: Error Status Panel
``` json
{
"version" : "2.0" ,
"locale" : "zh-CN" ,
"status" : "error" ,
"theme" : "default" ,
"root" : {
"type" : "stack" ,
"direction" : "vertical" ,
"gap" : 12 ,
"appearance" : "card" ,
"status" : "error" ,
"children" : [
{
"type" : "stack" ,
"direction" : "horizontal" ,
"gap" : 8 ,
"align" : "center" ,
"justify" : "space-between" ,
"children" : [
{
"type" : "text" ,
"content" : "删除失败" ,
"role" : "title"
} ,
{
"type" : "badge" ,
"label" : "ERROR" ,
"status" : "error"
}
]
} ,
{
"type" : "text" ,
"content" : "您没有权限执行此操作" ,
"role" : "body" ,
"status" : "error"
} ,
{
"type" : "stack" ,
"direction" : "horizontal" ,
"gap" : 8 ,
"children" : [
{
"type" : "button" ,
"label" : "重试" ,
"style" : "primary" ,
"action" : { "type" : "tool" , "toolId" : "user.delete" , "params" : { "userId" : "u1" } }
} ,
{
"type" : "button" ,
"label" : "联系管理员" ,
"style" : "secondary" ,
"action" : { "type" : "url" , "url" : "mailto:admin@example.com" }
}
]
}
]
}
}
```
### Example 4: Grid Layout
``` json
{
"version" : "2.0" ,
"locale" : "zh-CN" ,
"status" : "info" ,
"theme" : "default" ,
"root" : {
"type" : "grid" ,
"columns" : 3 ,
"gap" : 16 ,
"appearance" : "plain" ,
"children" : [
{
"type" : "card" ,
"children" : [
{ "type" : "text" , "content" : "今日订单" , "role" : "title" } ,
{ "type" : "text" , "content" : "128" , "role" : "subtitle" }
]
} ,
{
"type" : "card" ,
"children" : [
{ "type" : "text" , "content" : "待处理" , "role" : "title" } ,
{ "type" : "text" , "content" : "24" , "role" : "subtitle" , "status" : "warning" }
]
} ,
{
"type" : "card" ,
"children" : [
{ "type" : "text" , "content" : "总收入" , "role" : "title" } ,
{ "type" : "text" , "content" : "¥8,640" , "role" : "subtitle" , "status" : "success" }
]
}
]
}
}
```
### Example 5: Section Layout
``` json
{
"version" : "2.0" ,
"locale" : "zh-CN" ,
"status" : "success" ,
"theme" : "default" ,
"root" : {
"type" : "stack" ,
"direction" : "vertical" ,
"gap" : 24 ,
"appearance" : "plain" ,
"children" : [
{
"type" : "stack" ,
"direction" : "vertical" ,
"gap" : 12 ,
"appearance" : "section" ,
"children" : [
{ "type" : "text" , "content" : "基本信息" , "role" : "title" } ,
{ "type" : "kv" , "items" : [
{ "key" : "name" , "label" : "姓名" , "value" : "张三" } ,
{ "key" : "email" , "label" : "邮箱" , "value" : "zhangsan@example.com" , "copyable" : true }
] }
]
} ,
{
"type" : "stack" ,
"direction" : "vertical" ,
"gap" : 12 ,
"appearance" : "section" ,
"children" : [
{ "type" : "text" , "content" : "账户设置" , "role" : "title" } ,
{ "type" : "button" , "label" : "修改密码" , "style" : "secondary" , "action" : { "type" : "navigation" , "path" : "/settings/password" } } ,
{ "type" : "button" , "label" : "退出登录" , "style" : "ghost" , "action" : { "type" : "event" , "event" : "logout" } }
]
}
]
}
}
```
---
---
## UiHints (Descriptive UI)
### Overview
UiHints is a **descriptive ** UI representation designed for AI agents to express UI intent with minimal token cost. It describes **what to show ** , not **how to render ** .
The `ui_compiler` transforms UiHints into UiSchemaRenderer for frontend rendering.
### Design Principles
1. **Descriptive not Rendered ** : Express content intent, not visual instructions
2. **Minimal Token Cost ** : Simple structure, semantic field names
3. **Composable ** : Supports nested sections and mixed content
4. **Compilable ** : Mechanical transformation to UiSchemaRenderer
5. **Lossless ** : Main content fields in hints are preserved in renderer
### Intent Types
Intent is a **weak hint ** - it only affects default layout style, not field presence.
| Intent | Default Layout |
|--------|----------------|
| `message` | plain |
| `data` | card |
| `list` | plain |
| `status` | card |
| `form` | section |
| `mixed` | card |
### UiHints Payload
``` typescript
interface UiHintsPayload {
version : string ; // "2.1"
intent : UiHintIntent ; // Primary display intent (weak hint)
status : UiStatus ; // Overall status
title? : string ; // Top-level title
description? : string ; // Top-level description
body? : string ; // Top-level main body text
bodyFormat ? : "plain" | "markdown" ; // Body text format
items? : UiHintKvItem [ ] ; // Top-level key-value items
listItems? : UiHintListItem [ ] ; // Top-level list items
sections? : UiHintSection [ ] ; // Grouped sections
actions? : UiHintAction [ ] ; // Top-level actions
icon? : UiHintIcon ; // Top-level icon
meta? : Record < string , any > ; // Extra meta
}
interface UiHintSection {
title? : string ;
description? : string ;
icon? : UiHintIcon ;
content? : string ;
contentFormat ? : "plain" | "markdown" ;
items? : UiHintKvItem [ ] ;
listItems? : UiHintListItem [ ] ;
actions? : UiHintAction [ ] ;
}
interface UiHintListItem {
id? : string ;
title : string ;
subtitle? : string ;
description? : string ;
icon? : UiHintIcon ;
status? : UiHintStatus ;
actions? : UiHintAction [ ] ;
}
interface UiHintKvItem {
key : string ;
label? : string ;
value? : any ;
copyable? : boolean ;
}
interface UiHintAction {
label : string ;
style ? : "primary" | "secondary" | "ghost" | "danger" ;
disabled? : boolean ;
action : UiHintActionTarget ;
}
type UiHintActionTarget =
| { type : "navigation" ; path : string ; params? : Record < string , any > }
| { type : "url" ; url : string ; target ? : "_self" | "_blank" }
| { type : "event" ; event : string ; payload? : Record < string , any > }
| { type : "tool" ; toolId : string ; params? : Record < string , any > }
| { type : "copy" ; content : string ; successMessage? : string }
| { type : "payload" ; payload : Record < string , any > ; submitTo? : string } ;
interface UiHintIcon {
source : "icon" | "emoji" | "url" ;
value : string ;
color? : string ;
size? : number ;
}
```
### Compilation Flow
```
Agent Output (UiHints 2.1)
│
▼
ui_compiler
│
▼
UiSchemaRenderer (for frontend rendering)
```
### Example
**Input (UiHints) ** :
``` json
{
"intent" : "status" ,
"status" : "success" ,
"title" : "日程已创建" ,
"body" : "本次创建已成功完成。" ,
"items" : [
{ "key" : "title" , "label" : "主题" , "value" : "Q1 规划会议" } ,
{ "key" : "time" , "label" : "时间" , "value" : "2026-03-15 14:00" }
] ,
"actions" : [
{ "label" : "查看详情" , "style" : "primary" , "action" : { "type" : "navigation" , "path" : "/calendar/evt_123" } } ,
{ "label" : "删除" , "style" : "danger" , "action" : { "type" : "tool" , "toolId" : "calendar.delete" , "params" : { "eventId" : "evt_123" } } }
]
}
```
**Output (UiSchemaRenderer) ** :
``` json
{
"version" : "2.0" ,
"locale" : "zh-CN" ,
"status" : "success" ,
"theme" : "default" ,
"root" : {
"type" : "stack" ,
"appearance" : "card" ,
"status" : "success" ,
"children" : [
{
"type" : "stack" ,
"direction" : "horizontal" ,
"gap" : 8 ,
"children" : [
{ "type" : "text" , "content" : "日程已创建" , "role" : "title" } ,
{ "type" : "badge" , "label" : "SUCCESS" , "status" : "success" }
] ,
"justify" : "space-between" ,
"align" : "center"
} ,
{ "type" : "text" , "content" : "本次创建已成功完成。" , "role" : "body" } ,
{ "type" : "kv" , "items" : [ . . . ] } ,
{
"type" : "stack" ,
"direction" : "horizontal" ,
"gap" : 8 ,
"children" : [
{ "type" : "button" , "label" : "查看详情" , "style" : "primary" , . . . } ,
{ "type" : "button" , "label" : "删除" , "style" : "danger" , . . . }
]
}
]
}
}
```
### Python Implementation
- `schemas.agent.ui_hints.UiHintsPayload` - Descriptive UI model (v2.1)
- `core.agentscope.runtime.ui_compiler.compile(hints)` - Compile to UiSchemaRenderer