Back to OSS
Swift Package エージェント / プロトコル

swift-a2ui

Google A2UI プロトコルの Swift 実装。LLM エージェントがクライアントに型安全なリッチ UI を描画する

Swift
agenta2uiswiftui

title: swift-a2ui README created: 2026-06-27 tags: [a2ui, swift, spm, llm, ui-protocol] status: active

swift-a2ui

Google A2UI プロトコルの Swift 完全実装 — LLM エージェントがクライアントにリッチ UI を描画するための型安全なライブラリ群

概要

swift-a2uiA2UI (Agent-to-UI) プロトコル の Swift 実装です。LLM エージェントが JSON メッセージを通じてクライアント上に宣言的な UI サーフェスを生成・更新・削除し、ユーザーのアクションを受け取ってレスポンスを返す仕組みを、コンパイル時型安全な Swift API として提供します。

主な特徴

  • A2UI v0.10 完全対応: createSurface / updateComponents / updateDataModel / callFunction / actionResponse の全メッセージ型を実装
  • 型安全なカタログ: Swift の型システムを SSOT とした LLM 向けスキーマ生成(JSON ファイルとの乖離をコンパイル時に検出)
  • AnyView ゼロのジェネリックレンダラー: カタログを型パラメータとした A2UISurfaceView<Catalog> で、型消去なしに SwiftUI へ描画
  • 公式プロトコルへの忠実な準拠: Python SDK (a2ui.adk / a2ui.a2a) の設計をそのまま Swift に写したモジュール構成
  • マルチエージェント対応: A2A プロトコル統合とサーフェス所有権台帳によるオーケストレーション

モジュール構成

13 のモジュールを役割ごとに 5 グループに分けて説明します。

グループ 1 — コアプロトコル層

A2UI の有線フォーマット(JSON)を Swift の型として定義する最下層。SwiftUI も LLM クライアントも依存しない。

モジュール 役割
A2UICore ServerMessage / ClientMessage の enum、CreateSurface / UpdateComponents / UpdateDataModel 等の個別メッセージ型、UserActionDataBindingDynamicString / DynamicBoolean / DynamicNumber などのバインダブル値型

主な型:

// サーバ → クライアント
public enum ServerMessage: Sendable, Equatable, Codable {
    case createSurface(CreateSurface)
    case updateComponents(UpdateComponents)
    case updateDataModel(UpdateDataModel)
    case deleteSurface(DeleteSurface)
    case callFunction(CallFunctionMessage)
    case actionResponse(ActionResponseMessage)
}

// クライアント → サーバ
public enum ClientMessage: Sendable, Equatable, Codable {
    case action(UserAction)
    case error(ClientError)
    case functionResponse(FunctionResponse)
}

グループ 2 — コンポーネントカタログ

LLM が使えるコンポーネントのパレットを型として定義し、その定義から LLM 向け JSON Schema を自動生成する層。

モジュール 役割
A2UICatalog 18 コンポーネントの Swift 型定義、ComponentCatalog プロトコル、ComponentSchema / SchemaRenderer による型駆動スキーマ生成、カテゴリ enum (display / layout / input)

内蔵コンポーネント(BasicComponentCatalog):

カテゴリ コンポーネント
Display Text, Image, Icon, Video, AudioPlayer
Layout Row, Column, List, Card, Tabs, Modal, Divider
Input Button, TextField, CheckBox, ChoicePicker, Slider, DateTimeInput

スキーマは Swift 型から自動生成されるため、実装とスキーマが乖離することはない:

// スキーマ生成
let schema: String = BasicComponentCatalog.catalogSchemaJSON()

// カタログ ID
let id: String = BasicComponentCatalog.catalogId
// → "https://a2ui.org/specification/v0_10/catalogs/basic/catalog.json"

グループ 3 — LLM プロンプト・パーサー

エージェントに渡すシステムプロンプトの組み立てと、LLM 出力からの A2UI メッセージ抽出を担う。

モジュール 役割
A2UIPrompt A2UIPromptBuilder — システムプロンプトの組み立て(role / workflow rules / UI description / スキーマブロック)。カタログ・メッセージ種別の pruning(allowlist による絞り込み)。A2UIExample — 型付きコンポーネントから生成する参照サーフェス例文
A2UIPromptCompact A2UIPromptCompactBuilderfunctions を使わないアプリ向けに common_types から FunctionCall 型を除去した軽量版
A2UIParser A2UIStreamingParser — ストリーミング LLM 出力から <a2ui-json> ブロックをインクリメンタル抽出。A2UIPayloadFixer — LLM が生成する JSON の典型的な崩れを自動補正。JSONSanitizer

プロンプトの組み立て:

// 標準 builder(カタログ・メッセージ全種)
let builder = A2UIPromptBuilder()
let prompt = builder.buildSystemPrompt(
    role: "You are a helpful assistant that renders UI.",
    uiDescription: "Show a card with a title and a confirm button."
)

// presenter プリセット(コンテンツ提示向け 9 コンポーネント + 3 メッセージに絞る)
let presenterBuilder = A2UIPromptBuilder.presenter()

// カスタム pruning
let builder = A2UIPromptBuilder(
    allowedComponents: ["Text", "Column", "Row", "Button", "Image"],
    allowedMessages: ["CreateSurfaceMessage", "UpdateComponentsMessage"]
)

ストリーミングパーサーの使い方:

let parser = A2UIStreamingParser()

for chunk in llmStream {
    let parts = parser.feed(chunk)
    for part in parts {
        if let messages = part.messages {
            // A2UI ServerMessage 配列を描画系へ流す
            await surface.apply(messages)
        }
        if let text = part.text {
            // プレーンテキスト部分
            print(text)
        }
    }
}
let finalParts = parser.finalize()

グループ 4 — サーフェス状態 / レンダラー

クライアント上のサーフェス状態管理と SwiftUI 描画を担う。

モジュール 役割
A2UISurface DataModel — JSON Pointer による読み書きとリアクティブなパス購読(Bubble & Cascade 通知)。ComponentValidator、テンプレート展開 (ChildList)
A2UIRuntime DataContext — スコープ付きバインディング解決。TemplateExpander{componentId, path} テンプレートをコレクションスコープで展開
A2UITyped A2UICatalog プロトコル(associatedtype Node: ComponentNode)、CombinedNode<Primary, Fallback> による型安全なカタログ合成、BasicCatalogBasicComponentComponentNode として公開)、A2UIValidation
A2UITypedRenderer RenderableCatalog プロトコル、A2UISurfaceView<Catalog> (SwiftUI View)、NodeView<Catalog>RenderContext<Catalog>(双方向バインディング / checks 評価 / 子レンダリングの文脈)

A2UISurfaceView の使い方:

import SwiftUI
import A2UITypedRenderer

// 1. サーフェスを作成(@Observable なので @State に持てる)
@State var surface = TypedSurface<BasicCatalog>(rootId: "root", nodes: [])

// 2. サーフェスメッセージを適用するハンドラ
func apply(_ message: ServerMessage) {
    switch message {
    case .updateComponents(let msg):
        guard let nodes = try? TypedSurface<BasicCatalog>.decodeNodes(
            fromJSONArray: JSONEncoder().encode(msg.components)
        ) else { return }
        surface.applyUpdateComponents(nodes)
    case .updateDataModel(let msg):
        surface.applyUpdateDataModel(path: msg.path, value: msg.value)
    default:
        break
    }
}

// 3. SwiftUI に埋め込む
var body: some View {
    A2UISurfaceView(surface, busy: isGenerating)
}

カスタムカタログの合成:

// 独自コンポーネントを BasicCatalog に上乗せ
enum AppCatalog: A2UICatalog {
    typealias Node = CombinedNode<MyNode, BasicComponent>
    static let catalogId = "com.example.my-app"
}

// BasicEmbeddingNode 準拠で BasicCatalog のレンダラーを再利用
extension MyNode: BasicEmbeddingNode { ... }

グループ 5 — エージェント統合 / オーケストレーション

LLM エージェントへの A2UI ツール提供と、マルチエージェント構成を担う。

モジュール 役割
A2UIAgentTool SendA2UIToClientTool<Catalog> — LLM が send_a2ui_json_to_client ツールを呼ぶ公式パターン。JSON の解析・自動補正・バリデーション(allowlist 準拠)を行い、validated_a2ui_json を返す。A2UIToolResultExtractor — ツール結果から [ServerMessage] を取り出す
A2UIAgent A2UIPresenterAgent — presenter(コンテンツ提示)型エージェントの自己記述一式。systemPrompt() / tools() / agentExtension() / hostOutputConstraint() を提供。ホストは注入するだけで UI の全知識はこのモジュールに閉じる
A2UIA2A A2A プロトコルとの統合。Part.a2ui(_:) で A2UI メッセージを application/a2ui+json データパートとして包む。A2UIExtension — エージェントカードへの A2UI プロトコル宣言。A2UIClientCapabilities / A2UIClientDataModel / A2UIMessageMetadata
A2UIOrchestration SurfaceOwnership — サーフェス所有権台帳(どのエージェントがどのサーフェスを持つか)。owner(ofUserActionIn:) による確定的な UserAction ルーティング。outboundMetadata(_:capabilities:for:) によるデータモデル・ストリッピング(エージェント間のデータ漏洩防止)

A2UIPresenterAgent の注入例:

import A2UIAgent
import A2ACore
import LLMClient

// presenter エージェントに必要なものはすべてライブラリ側が持つ
let agentName = A2UIPresenterAgent.defaultName

// 1. system prompt
let systemPrompt = A2UIPresenterAgent.systemPrompt(language: "Japanese")

// 2. tools(カタログパレットはカスタマイズ可能)
let tools = A2UIPresenterAgent.tools(
    components: ["Column", "Row", "Text", "Image", "Icon", "List", "Card", "Button", "Divider"]
)

// 3. A2A card の extensions に宣言
let agentExtension = A2UIPresenterAgent.agentExtension()

// 4. オーケストレータの system prompt に注入する制約
let constraint = A2UIPresenterAgent.hostOutputConstraint(agentName: agentName)

オーケストレーション(SurfaceOwnership):

import A2UIOrchestration

var ownership = SurfaceOwnership()

// サブエージェントの応答を受け取るたびに所有権を記録
ownership.record(surfacesCreatedIn: responseparts, by: "a2ui")

// UserAction をルーティング(LLM 呼び出し不要)
if let agent = ownership.owner(ofUserActionIn: incomingParts) {
    // 対象エージェントへ直接転送
    await router.send(to: agent, parts: incomingParts)
}

// 送信前にデータモデルをエージェントの所有サーフェスだけに絞る
let metadata = try ownership.outboundMetadata(
    baseMetadata,
    capabilities: clientCapabilities,
    for: "a2ui"
)

インストール

Swift Package Manager

Package.swiftdependencies に追加:

.package(url: "https://github.com/no-problem-dev/swift-a2ui.git", from: "0.0.1"),

ターゲットの dependencies で必要なモジュールを列挙:

.target(
    name: "MyApp",
    dependencies: [
        // 最小構成(コアのみ)
        .product(name: "A2UICore", package: "swift-a2ui"),

        // プロンプト組み立て + パーサー
        .product(name: "A2UIPrompt", package: "swift-a2ui"),
        .product(name: "A2UIParser", package: "swift-a2ui"),

        // SwiftUI レンダラー(iOS/macOS UI)
        .product(name: "A2UITypedRenderer", package: "swift-a2ui"),

        // LLM エージェントツール
        .product(name: "A2UIAgentTool", package: "swift-a2ui"),

        // presenter エージェント自己記述
        .product(name: "A2UIAgent", package: "swift-a2ui"),

        // A2A 統合 / オーケストレーション
        .product(name: "A2UIA2A", package: "swift-a2ui"),
        .product(name: "A2UIOrchestration", package: "swift-a2ui"),
    ]
)

対応プラットフォーム

プラットフォーム 最小バージョン
macOS 14.0 (Sonoma)
iOS 17.0

Swift ツールズバージョン: 6.2


外部依存パッケージ

パッケージ 用途
no-problem-dev/swift-structured-data JSON パース / シリアライズ (StructuredValue, JSONParser)
no-problem-dev/swift-a2a A2A プロトコルコア (AgentCard, Part, StreamResponse)
no-problem-dev/swift-design-system SwiftUI デザイントークン(カラー / スペーシング / モーション / Glass Card)
no-problem-dev/swift-markdown-view Text コンポーネントの Markdown レンダリング
no-problem-dev/swift-llm-client Tool / TurnEndingTool プロトコル(SendA2UIToClientTool の基底)

ライセンス

LICENSE を参照してください。


最終更新: 2026-06-27

© 2026 Kyoichi Taniguchi. All rights reserved.