A complete .NET 9 implementation of the A2UI (Agent to UI) protocol for Blazor applications.
blazor-dotnet/
├── src/
│ ├── A2UI.Core/ # 核心协议处理库
│ ├── A2UI.Blazor.Components/ # Blazor 组件库
│ ├── A2UI.AgentSDK/ # Agent 端 SDK
│ └── A2UI.Theming/ # 主题系统
├── samples/
│ ├── A2UI.Sample.BlazorServer/ # Blazor Server 示例
│ ├── A2UI.Sample.BlazorWasm/ # Blazor WASM 示例
│ └── A2UI.Sample.Agent/ # .NET Agent 示例
├── tests/
│ ├── A2UI.Core.Tests/ # 核心库测试
│ └── A2UI.Blazor.Components.Tests/ # 组件测试
└── A2UI.Blazor.sln # 解决方案文件
A2UI (Agent to UI) 是一个声明式 UI 协议,用于代理驱动的界面。AI 代理生成丰富的交互式 UI,可在各个平台(Web、移动、桌面)上原生渲染,而无需执行任意代码。
- 安全性:声明式数据,而非代码。代理从客户端的可信目录请求组件。无代码执行风险。
- 原生体验:无 iframe。客户端使用自己的 UI 框架渲染。继承应用样式、可访问性和性能。
- 可移植性:一个代理响应可在任何地方工作。相同的 JSON 可在 Web(Lit/Angular/React/Blazor)、移动(Flutter/SwiftUI)、桌面上渲染。
# 从解决方案根目录
dotnet restore
dotnet build# Blazor Server 示例
cd samples/A2UI.Sample.BlazorServer
dotnet run
# Blazor WASM 示例
cd samples/A2UI.Sample.BlazorWasm
dotnet run访问 https://localhost:5001/a2ui-demo 查看交互式演示。
在您的 Blazor 项目中添加以下项目引用:
<ItemGroup>
<ProjectReference Include="path\to\A2UI.Core\A2UI.Core.csproj" />
<ProjectReference Include="path\to\A2UI.Blazor.Components\A2UI.Blazor.Components.csproj" />
<ProjectReference Include="path\to\A2UI.AgentSDK\A2UI.AgentSDK.csproj" />
<ProjectReference Include="path\to\A2UI.Theming\A2UI.Theming.csproj" />
</ItemGroup>在 Program.cs 中注册 A2UI 服务:
using A2UI.Core.Processing;
using A2UI.Theming;
var builder = WebApplication.CreateBuilder(args);
// 添加 Blazor 服务
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
// 添加 A2UI 服务 (Blazor Server 使用 Scoped)
builder.Services.AddScoped<MessageProcessor>();
builder.Services.AddScoped<DataBindingResolver>(sp =>
new DataBindingResolver(sp.GetRequiredService<MessageProcessor>()));
builder.Services.AddScoped<EventDispatcher>();
builder.Services.AddSingleton<ThemeService>();
var app = builder.Build();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();注意:Blazor Server 使用
AddScoped,Blazor WebAssembly 使用AddSingleton。
@page "/demo"
@using A2UI.Core.Processing
@inject MessageProcessor MessageProcessor
@rendermode InteractiveServer
<A2UISurface SurfaceId="my-surface" />
@code {
protected override void OnInitialized()
{
// 创建 A2UI 消息
var messages = new List<ServerToClientMessage>
{
new ServerToClientMessage
{
BeginRendering = new BeginRenderingMessage
{
SurfaceId = "my-surface",
Root = "root-component"
}
},
new ServerToClientMessage
{
SurfaceUpdate = new SurfaceUpdateMessage
{
SurfaceId = "my-surface",
Components = new List<ComponentDefinition>
{
new ComponentDefinition
{
Id = "root-component",
Component = new Dictionary<string, object>
{
["Text"] = new Dictionary<string, object>
{
["text"] = new Dictionary<string, object>
{
["literalString"] = "Hello A2UI!"
}
}
}
}
}
}
}
};
// 处理消息
foreach (var msg in messages)
{
MessageProcessor.ProcessMessage(msg);
}
}
}使用 SurfaceBuilder 流式 API 快速构建界面:
using A2UI.AgentSDK;
using A2UI.AgentSDK.Builders;
// 创建简单的文本卡片
var messages = A2UIQuickStart.CreateTextCard(
surfaceId: "my-surface",
title: "欢迎使用 A2UI",
body: "这是一个使用 Agent SDK 创建的卡片界面"
);
// 创建表单界面
var messages = new SurfaceBuilder("form-surface")
.AddColumn("root", col => col
.AddChild("title")
.AddChild("name-field")
.AddChild("email-field")
.AddChild("submit-btn"))
.AddText("title", text => text
.WithText("用户注册")
.WithUsageHint(A2UIConstants.TextUsageHints.H2))
.AddTextField("name-field", field => field
.WithPlaceholder("请输入姓名")
.WithValue("$.name"))
.AddTextField("email-field", field => field
.WithPlaceholder("请输入邮箱")
.WithValue("$.email"))
.AddButton("submit-btn", btn => btn
.WithChild("submit-text")
.WithAction("submit_form")
.AsPrimary())
.AddText("submit-text", text => text
.WithText("提交"))
.WithRoot("root")
.Build();
// 处理消息
foreach (var msg in messages)
{
MessageProcessor.ProcessMessage(msg);
}订阅用户操作事件:
@inject EventDispatcher EventDispatcher
@code {
protected override void OnInitialized()
{
// 订阅用户操作
EventDispatcher.UserActionDispatched += OnUserAction;
}
private async void OnUserAction(object? sender, UserActionEventArgs e)
{
var action = e.Action;
Console.WriteLine($"用户操作: {action.Name}");
Console.WriteLine($"来源组件: {action.SourceComponentId}");
Console.WriteLine($"Surface ID: {action.SurfaceId}");
// 获取上下文数据
if (action.Context != null)
{
foreach (var (key, value) in action.Context)
{
Console.WriteLine($" {key}: {value}");
}
}
// 根据操作更新 UI
if (action.Name == "submit_form")
{
var name = action.Context?["name"];
var email = action.Context?["email"];
// 处理表单提交...
// 更新 UI 显示结果
var resultMessages = A2UIQuickStart.CreateTextCard(
action.SurfaceId,
"提交成功",
$"欢迎 {name}!"
);
foreach (var msg in resultMessages)
{
MessageProcessor.ProcessMessage(msg);
}
await InvokeAsync(StateHasChanged);
}
}
public void Dispose()
{
EventDispatcher.UserActionDispatched -= OnUserAction;
}
}创建一个 Agent 服务来处理用户查询:
public class MyAgent
{
public Task<List<ServerToClientMessage>> ProcessQueryAsync(string query)
{
// 根据查询生成相应的 UI
if (query.Contains("天气"))
{
return Task.FromResult(CreateWeatherUI());
}
else if (query.Contains("列表"))
{
return Task.FromResult(CreateListUI());
}
return Task.FromResult(CreateDefaultUI());
}
private List<ServerToClientMessage> CreateWeatherUI()
{
return new SurfaceBuilder("weather-surface")
.AddCard("card", card => card
.WithChild("content"))
.AddColumn("content", col => col
.AddChild("icon")
.AddChild("temp")
.AddChild("desc"))
.AddIcon("icon", icon => icon
.WithIcon("☀️"))
.AddText("temp", text => text
.WithText("25°C")
.WithUsageHint(A2UIConstants.TextUsageHints.H1))
.AddText("desc", text => text
.WithText("晴朗"))
.WithRoot("card")
.Build();
}
}参考 samples/A2UI.Sample.BlazorServer/Components/Pages/A2UIDemo.razor 查看完整的聊天界面实现,包括:
- 💬 消息历史记录
- ⚡ 动态 UI 生成
- 🎯 用户操作处理
- 🔄 实时状态更新
A2UI 支持以下标准组件:
| 组件 | 说明 | 示例用途 |
|---|---|---|
Text |
文本显示 | 标题、段落、标签 |
Button |
按钮 | 提交、取消、操作 |
Card |
卡片容器 | 内容分组 |
Row / Column |
布局容器 | 横向/纵向布局 |
TextField |
文本输入框 | 表单输入 |
CheckBox |
复选框 | 选项选择 |
DateTimeInput |
日期时间选择器 | 日期选择 |
Slider |
滑块 | 数值调整 |
MultipleChoice |
单选/多选 | 选项列表 |
List |
列表 | 数据展示 |
Tabs |
选项卡 | 内容切换 |
Modal |
模态框 | 弹窗、确认 |
Image |
图片 | 图片显示 |
Icon |
图标 | 装饰、状态 |
Video |
视频播放器 | 视频内容 |
AudioPlayer |
音频播放器 | 音频内容 |
Divider |
分割线 | 内容分隔 |
使用 BoundValue 进行数据绑定:
// 设置数据
var dataMessages = new List<ServerToClientMessage>
{
new ServerToClientMessage
{
DataModelUpdate = new DataModelUpdateMessage
{
SurfaceId = "my-surface",
Path = "/",
Contents = new List<DataEntry>
{
new DataEntry { Key = "username", ValueString = "张三" },
new DataEntry { Key = "age", ValueNumber = 25 },
new DataEntry { Key = "isVip", ValueBoolean = true }
}
}
}
};
// 在组件中使用数据绑定
.AddText("name-display", text => text
.WithValue("$.username")) // 绑定到数据模型A2UI.Core- 核心协议库A2UI.Blazor- Blazor 组件库A2UI.AgentSDK- Agent 开发 SDKA2UI.Theming- 主题系统
- .NET 9.0
- Blazor Server(通过 SignalR)
- Blazor WebAssembly(通过 HttpClient + SSE)
- 符合 A2UI 0.8 协议规范
- A2UI 组件 JSON 使用指南 - 详细的组件使用文档和 JSON 示例
Apache 2.0 - 参见 LICENSE