AG-UI 协议

AG-UI(Agent-User Interaction)协议定义了 Agent 后端与 Web 或移动前端之间的通信格式,客户端通过 HTTP 发起一次运行,服务端通过 SSE 持续返回文本、工具调用和状态事件。

这样不同框架、不同编程语言编写的 Agent,可以使用同一套协议,复用一套前端 Agent 交互。

协议独立于 Microsoft Agent Framework,MAF 分别通过 Microsoft.Agents.AI.Hosting.AGUI.AspNetCoreMicrosoft.Agents.AI.AGUI 提供服务端与 .NET 客户端实现。

AG-UI 协议官网:https://docs.ag-ui.com/introduction


AG-UI 协议和 MAF 差别

采用统一协议后,后端可以连接 CopilotKit 等现成前端,客户端也可以更换服务端实现,而不必重新约定文本流、工具调用和状态事件。审批流程内容较多,下一章再单独展开。


在 .NET 实现中,托管层负责把 MAF 内容转换为协议事件:

MAF / Microsoft.Extensions.AI 概念AG-UI 协议对应说明
AIAgentAgent Endpoint每个 Agent 映射成一个 HTTP 端点
agent.RunStreamingAsync()SSE 事件流流式响应转成事件
AgentResponseUpdate / ChatResponseUpdateAG-UI 事件update 里的各种 AIContent 被翻译成不同事件
TextContentTEXT_MESSAGE_* 事件文本流式
FunctionCallContentTOOL_CALL_START/ARGS/END工具调用
FunctionResultContentTOOL_CALL_RESULT工具结果
DataContent(mediaType=application/jsonSTATE_SNAPSHOT状态快照
DataContent(mediaType=application/json-patch+jsonSTATE_DELTA状态增量(JSON Patch)
TextReasoningContentREASONING_* 事件推理内容或受保护的推理数据
AgentSession(ConversationId)threadId会话标识


后面的工具和状态管理都建立在这层转换之上。

服务端把每次 Agent 运行转换为一串 BaseEvent,每个事件作为一行 data: {json} 写入 SSE:

类别事件作用
运行RUN_STARTEDRUN_FINISHEDRUN_ERROR标记一次运行的开始、结束或错误
文本TEXT_MESSAGE_START/CONTENT/ENDdelta 流式传递助手消息
工具TOOL_CALL_START/ARGS/END/RESULT传递工具名称、参数和执行结果
状态STATE_SNAPSHOTSTATE_DELTA传递完整状态或 JSON Patch 增量
推理REASONING_*传递推理文本或受保护的推理数据

暴露 AG-UI 服务端点

服务端项目必须使用 Microsoft.NET.Sdk.Web,并安装 AG-UI 托管包。下面使用 Azure OpenAI 创建 Agent:

Microsoft.Agents.AI.Hosting.AGUI.AspNetCore
Microsoft.Agents.AI.OpenAI


最小服务端如下:

using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;
using Microsoft.Extensions.AI;
using OpenAI;
using System.ClientModel;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddHttpClient().AddLogging();
builder.Services.AddAGUIServer();   // 注册 AGUI 的 JSON 多态序列化器

var app = builder.Build();

IChatClient chatClient = new OpenAIClient(
        credential: new ApiKeyCredential(key: "1234"),
        options: new OpenAIClientOptions { Endpoint = new Uri("http://127.0.0.1:1234/v1") })
    .GetChatClient(model: "qwen/qwen3.5-9b")
    .AsIChatClient();

AIAgent agent = chatClient.AsAIAgent(
    name: "AGUIAssistant",
    instructions: "You are a helpful assistant.");

app.MapAGUIServer("/", agent);   // 把 Agent 挂到根路径

app.MapControllers();

app.Run();

读取 SSE 事件流

可以用 curl 直接观察原始事件流:

curl -N http://localhost:5216/ \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{ "messages": [{"role":"user","content":"2+2?"}] }'


返回(每行 data: 是一个事件):

image-20260820090818560

空闲时服务端默认每 15 秒发送一次 : keepalive 注释。这是 SSE 保活信息,不属于 AG-UI 事件,客户端可以忽略。


每个事件在 .NET 中都是 BaseEvent 的派生类型。BaseEventJsonConverter 根据 type 字段在协议事件与具体 CLR 类型之间转换,这也是服务端需要调用 AddAGUI() 注册序列化配置的原因。


使用 .NET 客户端

AG-UI 不止是服务端协议,.NET 也提供了客户端 AGUIChatClient(包 Microsoft.Agents.AI.AGUI),它实现了 IChatClient,能把任意 AG-UI 服务端当成本地 IChatClient 用。

Microsoft.Agents.AI.AGUI
Microsoft.Agents.AI
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.AGUI;
using Microsoft.Extensions.AI;

string serverUrl = "http://localhost:5216";

using HttpClient httpClient = new() { Timeout = TimeSpan.FromSeconds(60) };
AGUIChatClient chatClient = new(httpClient, serverUrl);

// 把 AGUIChatClient 包成 AIAgent
AIAgent agent = chatClient.AsAIAgent(name: "agui-client", description: "AG-UI Client");

AgentSession session = await agent.CreateSessionAsync();
List<ChatMessage> messages = new();

while (true)
{
    Console.Write("\nUser: ");
    string? message = Console.ReadLine();
    if (message is ":q" or "quit") break;
    if (string.IsNullOrWhiteSpace(message)) continue;

    messages.Add(new ChatMessage(ChatRole.User, message));

    await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session))
    {
        ChatResponseUpdate chatUpdate = update.AsChatResponseUpdate();
        foreach (AIContent content in update.Contents)
        {
            if (content is TextContent text)
                Console.Write(text.Text);
            else if (content is ErrorContent err)
                Console.WriteLine($"\n[Error: {err.Message}]");
        }
    }
}


AGUIChatClientIChatClient 调用转换为 RunAgentInput,使用 SseParser 读取事件,再转回 ChatResponseUpdate。它还会从消息中提取 application/json 状态,把 options.Tools 转成只有名称、描述和参数 schema 的前端工具声明。

客户端内部使用 FunctionInvokingChatClient 执行前端工具。来自服务端的后端工具调用会包装成 ServerFunctionCallContent,因此不会在客户端重复执行。AG-UI 要求每轮发送完整消息历史,所以客户端在进入这层调用前会移除 ConversationId,避免 FunctionInvokingChatClient 根据会话 ID 裁剪上下文。

image-20260820091541268


此外,我们还可以使用前端 SDK 接入 AGUI,这里不再赘述。