カスタムコンポーネントを作成する¶
rizzcharts サンプルを例に、A2UI でカスタムコンポーネントを定義、実装、登録する方法を説明します。このガイドは、Angular コードを中心にコンポーネントを作成することに焦点を当てています。
概要¶
新しいコンポーネントの作成には、主に 4 つの手順があります。
- カタログスキーマを定義する: JSON Schema にコンポーネントのプロパティと型を指定します。
- コンポーネントを定義する(クライアント): 使用しているフレームワーク(例: Angular)で UI を実装します。
- レンダラーに登録する(クライアント): コンポーネントをクライアント側カタログへ追加します。
- エージェントから呼び出す:
send_a2ui_json_to_clientを通じて、そのコンポーネントを使うようエージェントに指示します。
1. カタログスキーマを定義する¶
カタログスキーマは、カタログの API を定義します。利用可能なコンポーネントとそのプロパティを列挙し、エージェントはそれを使って UI payload を構築します。
このスキーマは、クライアントとサーバー(エージェント)の間の契約として機能します。 レンダリングが機能するには、双方がこのスキーマに合意している必要があります。クライアントは対応しているカタログを通知し、サーバーは互換性のあるものを選択します。この handshake の仕組みについては A2UI Catalog Negotiation を参照してください。
rizzcharts の例では、カタログスキーマは rizzcharts_catalog_definition.json に定義されています。
以下は Chart コンポーネントのスキーマです。
"Chart": {
"type": "object",
"description": "An interactive chart that uses a hierarchical list of objects for its data.",
"properties": {
"type": {
"type": "string",
"description": "The type of chart to render.",
"enum": [
"doughnut",
"pie"
]
},
"title": {
"type": "object",
"description": "The title of the chart. Can be a literal string or a data model path.",
"properties": {
"literalString": {
"type": "string"
},
"path": {
"type": "string"
}
}
},
"chartData": {
"type": "object",
"description": "The data for the chart, provided as a list of items. Can be a literal array or a data model path.",
"properties": {
"literalArray": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "number"
},
"drillDown": {
"type": "array",
"description": "An optional list of items for the next level of data.",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"value": {
"type": "number"
}
},
"required": [
"label",
"value"
]
}
}
},
"required": [
"label",
"value"
]
}
},
"path": {
"type": "string"
}
}
}
},
"required": [
"type",
"chartData"
]
}
2. コンポーネントを実装する(クライアント)¶
クライアント側フレームワークを使ってコンポーネントを実装します。Angular の場合、コンポーネントは @a2ui/angular が提供する DynamicComponent を拡張する必要があります。
rizzcharts の例では、Chart コンポーネントは chart.ts に定義されています。
import {DynamicComponent} from '@a2ui/angular';
import * as Primitives from '@a2ui/web_core/types/primitives';
import * as Types from '@a2ui/web_core/types/types';
import {Component, computed, input, Signal, signal} from '@angular/core';
@Component({
selector: 'a2ui-chart',
template: `
<div>
<h2>{{ resolvedTitle() }}</h2>
<canvas baseChart [data]="currentData()" [type]="chartType()"></canvas>
</div>
`,
})
export class Chart extends DynamicComponent<Types.CustomNode> {
readonly type = input.required<string>();
protected readonly chartType = computed(() => this.type() as ChartType);
readonly title = input<Primitives.StringValue | null>();
protected readonly resolvedTitle = computed(() => super.resolvePrimitive(this.title() ?? null));
readonly chartData = input.required<Primitives.StringValue | null>();
// ... data resolution logic using super.resolvePrimitive for data paths
}
コンポーネントを実装するときは、次の重要点を意識してください。
DynamicComponentを拡張する: データバインディング解決のためのresolvePrimitiveにアクセスできます。- Angular Inputs を使う: スキーマのプロパティを Angular input にマッピングします。
3. レンダラーに登録する(クライアント)¶
コンポーネントを実装したら、クライアントカタログに登録します。これにより、エージェントが使用するコンポーネント名が実装クラスにマッピングされます。
rizzcharts の例では、これは catalog.ts で行われます。
import {Catalog, DEFAULT_CATALOG} from '@a2ui/angular';
import {inputBinding} from '@angular/core';
export const RIZZ_CHARTS_CATALOG = {
...DEFAULT_CATALOG,
Chart: {
type: () => import('./chart').then(r => r.Chart),
bindings: ({properties}) => [
inputBinding('type', () => ('type' in properties && properties['type']) || undefined),
inputBinding('title', () => ('title' in properties && properties['title']) || undefined),
inputBinding(
'chartData',
() => ('chartData' in properties && properties['chartData']) || undefined,
),
],
},
} as Catalog;
登録時の重要点は次のとおりです。
- Lazy Loading:
import()を使ってコンポーネントコードを lazy-load します。 - Input Bindings:
inputBindingを使って、スキーマのプロパティを Angular input にマッピングします。
4. エージェントから呼び出す¶
カスタムコンポーネントを使用するには、自分のカタログを理解する A2UI SDK のツールでエージェントを初期化します。SDK はカタログの解決と、モデルへの例の提供を処理します。
流れは次のようにつながります。
4.1 セッション準備(Executor)¶
実行レイヤー(例: RizzchartsAgentExecutor)は受信メッセージをインターセプトし、A2UI が有効かどうか、クライアントがどのカタログをサポートしているかを検出します。カタログを解決し、セッション状態に保存します。
# In agent_executor.py
use_ui = try_activate_a2ui_extension(context)
if use_ui:
# Resolve catalog based on client capabilities
a2ui_catalog = self.schema_manager.get_selected_catalog(
client_ui_capabilities=capabilities
)
examples = self.schema_manager.load_examples(a2ui_catalog, validate=True)
# Save to session (Event contains state_delta)
await runner.session_service.append_event(
session,
Event(
actions=EventActions(
state_delta={
_A2UI_ENABLED_KEY: True,
_A2UI_CATALOG_KEY: a2ui_catalog,
_A2UI_EXAMPLES_KEY: examples,
}
),
),
)
4.2 エージェントツール設定¶
Agent は SendA2uiToClientToolset を使って、A2UI をクライアントへ送るためのツールをエージェントに提供します。
from a2ui.adk.send_a2ui_to_client_toolset import SendA2uiToClientToolset
a2ui_catalog = self.schema_manager.get_selected_catalog(
client_ui_capabilities=capabilities
)
agent.tools = [
SendA2uiToClientToolset(
a2ui_catalog=a2ui_catalog,
a2ui_enabled=True,
)
]
4.3 ツール実行¶
LLM による SendA2uiToClientToolset のツール呼び出しは、A2A Agent Executor で A2uiEventConverter によってインターセプトされます。これにより、ツール呼び出しは A2UI payload を持つ A2A Dataparts に自動変換されます。