[AG-UI详解-01]创建一个类似ChatGPT应用与Agent实时交互

AG‑UI是微软在Microsoft Agent Framework (MAF)中定义的标准化前后端通信协议,用来构建Web/移动端的AI Agent UI。它让Agent不再只是后端逻辑,而是能以统一格式与前端实时交互、流式输出、同步状态,并渲染交互式组件。本系列将对协议本身以及背后的执行原理对AG-UI进行系统介绍。

1. AG-UI概述

AG-UI是一种协议,它使您能够构建具有实时流、状态管理和交互式UI组件等高级功能的基于Web的Agent应用程序。MAF与AG-UI的集成可实现Agent和Web客户端之间的无缝连接。AG-UI提供以下功能:

  • 远程Agent托管:将Agent部署为可供多个客户端访问的Web服务;
  • 实时流式传输:使用SSE传输Agent响应,以实现即时反馈;
  • 标准化通信:一致的消息格式,确保Agent交互的可靠性;
  • 会话管理:在多个请求之间保持对话上下文;
  • 高级功能:人机协作审批、状态同步和UI渲染等。

AG-UI可以应用到如下场景:

  • 构建与Agent交互的Web或移动应用程序;
  • 将Agent部署为可供多个并发用户访问的服务;
  • 实时传输Agent响应以提供即时用户反馈;
  • 实现用户在执行操作前确认的审批工作流;
  • 同步客户端和服务器之间的状态以实现交互式体验;
  • 根据Agent工具调用渲染自定义UI组件。

2. 将Agent暴露成HTTP终结点

如果你之前没有接触过AG-UI,可以利用如下这个演示实例体验一下如何利用此协议将一个Agent对象以ASP.NET Core路由终结点的形式暴露出来,并利用客户端程序采用HTTP/SSE的形式与它进行实时交互(实时返回Agent的输出),实际上我们目前看到的基于对于的AI工具(比如ChatGPT)就采用类似的交互方式。

如下所示的是一个ASP.NET Core程序。在这个程序中,我们根据指定的Microsoft Foundry项目地址创建了一个AIProjectClient对象,并使用部署的模型将其封装成一个AIAgent对象。我们直接调用WebApplication的MapAGUI扩展方法采用根路径("/")注册了一个路由终结点来调用这个AIAgent对象。在这之前我们还会调用IServiceCollection的AddAGUI扩展方法注册AG-UI所需的服务。

csharp 复制代码
using Azure.AI.Projects;
using Azure.Identity;
using DotNetEnv;
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;

Env.Load();
var projectUrl = Environment.GetEnvironmentVariable("PROJECT_URL")!;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
var model = Environment.GetEnvironmentVariable("MODEL")!;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient().AddLogging();
builder.Services.AddAGUI();
var app = builder.Build();

var agent = new AIProjectClient(
        new Uri(projectUrl),
        new AzureCliCredential())
    .AsAIAgent(
        model: model,
        name: "HistoryAssistant",
        instructions: "你是一个深谙中国古代历史的专家,善于根据正史,以公正客观的态度于人交流历史问题。对于用户提出的问题,请以简介概括性的语言予以答复,字数尽量保持在200字以内。");
app.MapAGUI("/", agent);

await app.RunAsync();

之前针对AG-UI的.NET SDK有如下两个实现:

最近后者的核心代码被移除,只保留一些胶水代码,比如针对ASP.NET Core将AIAgent暴露成采用AG-UI协议的终结点。代码NuGet包尚未发布,当新的NuGet保护之后,相关的AddAGUIMapAGUI方法将会替换成AddAGUIServerMapAGUIServer,关于这个更新的详细介绍,可以参考githu站点

3. 利用AGUIChatClient远程调用Agent终结点

我们创建一个控制台程序来模拟客户端程序,可以将其视为ChatGPT客户端应用。如下面的代码所示,我们创建根据创建的HttpClient和上面暴露出来的AG-UI终结点地址,创建了一个AGUIChatClient对象。AGUIChatClient类型实现了IChatClient接口,它会将目标终结点视为LLM,利用指定的HttpClient与之交互。正因为这是一个IChatClient对象,所以我们可以调用AsAIAgent扩展方法将其封装为一个AIAgent对象。

csharp 复制代码
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.AGUI;
using Microsoft.Extensions.AI;

using HttpClient httpClient = new() { Timeout = TimeSpan.FromSeconds(60) };
var chatClient = new AGUIChatClient(httpClient, "http://localhost:9999");

var agent = chatClient.AsAIAgent(
    name: "AGUIAgent",
    description: "AG-UI Client Agent");
var session = await agent.CreateSessionAsync();

while (true)
{
    Console.Write("\n$ (:q or quit to exit): ");
    var message = Console.ReadLine();
    if (message is ":q" or "quit")
    {
        break;
    }

    await foreach (AgentResponseUpdate update in agent.RunStreamingAsync([new ChatMessage(ChatRole.User, message)], session))
    {
        var chatUpdate = update.AsChatResponseUpdate();
        foreach (AIContent content in update.Contents)
        {
            if (content is TextContent textContent)
            {
                Console.Write(textContent.Text);
            }
            else if (content is ErrorContent errorContent)
            {
                Console.WriteLine($"\n[Error: {errorContent.Message}]");
            }
        }
    }
}

由于AGUIChatClient实现了远程调用的全部细节,所以客户端程序只需要以常规方式调用生成的AIAgent对象即可。在上面演示的程序中,我们创建了一个Session,并在此Session基础上开启了一个对话循环。为了提高用户体验,我们采用流的形式调用AIAgent,并实施输出ChatResponseUpdate的内容。如下的输出体现了我们与AI进行的一轮对话。

markdown 复制代码
$ (:q or quit to exit): 战国四大名将一般指的是谁?

"战国四大名将"通常指战国后期四位最具代表性的军事统帅:

- 白起(秦国):号称"人屠",长平之战重创赵国,是秦统一的重要功臣。
- 王翦(秦国):老成持重,率军灭楚、灭燕,奠定秦统一天下基础。
- 廉颇(赵国):赵国名将,以善守著称,"负荆请罪"故事主角之一。
- 李牧(赵国):战国末期名将,曾大破匈奴,并在肥之战重创秦军。

这一说法主要流行于后世,并非战国时期官方称谓。

$ (:q or quit to exit): 吴起、司马错、乐毅和魏无忌的军事才能都不弱于这四位吧

确实如此。所谓"战国四大名将"更多是后世综合战绩、影响力与知名度形成的概括,并非绝对排名。你提到的几人,军事才能都非常突出。

吴起兼具军事与改革才能,在魏、楚都取得显著战绩,是战国初期最顶尖的统帅之一。

司马错是秦国早期名将,主张"先取蜀",灭蜀后为秦国统一奠定战略基础,眼光极强。

乐毅率五国伐齐,连下七十余城,几乎灭齐,是战国最成功的联军统帅之一。

魏无忌(信陵君)虽非职业将领,但窃符救赵、统兵破秦,展现了极强的军事与政治号召力。

若论纯军事能力,他们未必逊于"战国四大名将",只是时代、战绩持续性及后世影响不同。

$ (:q or quit to exit): 如何正确评价锋芒被其父掩盖的王贲?

王贲是秦统一战争中极关键的统帅,实际战功并不逊于其父王翦,只是名气长期被"灭楚主帅"王翦遮盖。

他主要功绩包括:灭魏时引黄河、鸿沟灌大梁;随后灭燕、代,俘燕王喜;又南下灭齐,完成统一最后一环。秦最后统一六国,王贲直接参与了其中三国的覆灭。

与王翦相比,王贲更偏进取型,作战节奏快、攻坚能力强;王翦则更稳健老练。父子二人实际上形成了互补。

后世对王贲关注较少,一是《史记》记载篇幅有限;二是其功业大多发生在秦统一后期,容易被"秦始皇---王翦"这一更宏大的叙事掩盖。但若以实际灭国战绩论,王贲完全称得上一流名将。

4. 从HTTP请求和响应分析AG-UI

分析HTTP请求和响应的内容更能揭示AG-UI协议的本质。上面的输出体现了三轮对话,如下所示的是第一轮对话的请求。可以看出这是一个常规的POST请求,Conent-Type为application/json。

复制代码
POST http://localhost:9999/ HTTP/1.1
Host: localhost:9999
Content-Type: application/json; charset=utf-8
Content-Length: 268

{"threadId":"thread_eb2bd3b287494deb8736f079863bdbc7","runId":"run_8f8ed2f303824ab6acc9875e9ebdf908","messages":[{"name":null,"id":"1d008f8827e044758e1de9e44bd13989","role":"user","content":"战国四大名将一般指的是谁?"}],"context":[]}

请求的JSON包含四个字段:

  • threadId:表示Session或者Conversation的ID;
  • runId: 针对当前Agent调用的标识;
  • messages:输入的消息列表;
  • context:用于传递额外上下文(如前端状态、表单内容、UI状态)。

实际上作为调用AG-UI的请求内容还可以包含其他成员,我们将在后续内容进行介绍。如下这段内容为对应的响应,从Content-Type(text/event-stream)可以看出,服务端采用SSE以流的形式实施输出生成的内容(data表示的Chunk)。

复制代码
data: {"threadId":"thread_b62ca0e770654dbc9bbe7283b0cbffc5","runId":"run_d85eea336f6b498baf4c5b5d8258b858","type":"RUN_STARTED"}

data: {"messageId":"chatcmpl-E3SpqsP2hJDy8fw2XQJUBwglyemYh","role":"assistant","type":"TEXT_MESSAGE_START"}
data: {"messageId":"chatcmpl-E3SpqsP2hJDy8fw2XQJUBwglyemYh","delta":"\u201C","type":"TEXT_MESSAGE_CONTENT"}
...
data: {"messageId":"chatcmpl-E3SpqsP2hJDy8fw2XQJUBwglyemYh","delta":"\u3002","type":"TEXT_MESSAGE_CONTENT"}
data: {"messageId":"chatcmpl-E3SpqsP2hJDy8fw2XQJUBwglyemYh","type":"TEXT_MESSAGE_END"}
data: {"threadId":"thread_b62ca0e770654dbc9bbe7283b0cbffc5","runId":"run_d85eea336f6b498baf4c5b5d8258b858","result":null,"type":"RUN_FINISHED"}

AG-UI是一个完全采用事件驱动的协议,客户端和服务端完全利用事件的方式实现流式数据交换,上述的响应就包含了五种典型的事件:

  • RUN_STARTED:标志Agent调用开始;
  • TEXT_MESSAGE_STAR:标志文本消息传输开始;
  • TEXT_MESSAGE_CONTENT:传输具体的消息内容;
  • TEXT_MESSAGE_END:标志文本消息传输结束;
  • RUN_FINISHED:标志Agent调用结束;
相关推荐
冬奇Lab4 小时前
每日一个开源项目(第164篇):毕昇(BISHENG)- 面向企业的开源 LLM DevOps 平台
人工智能·开源·agent
字节跳动开源5 小时前
30 分钟搞定个人情报站:Viking AI 搜索让前沿资讯实时推到面前
数据挖掘·开源·agent
早点睡觉1497 小时前
Agent 工程实习复盘 03|从 Pickle 到 PostgreSQL:LangGraph Checkpoint 迁移、内存治理与可回滚发布
agent
早点睡觉1497 小时前
Agent 工程实习复盘 04|长对话越聊越容易崩?Request-only Summary 与 Token 预算治理
agent
武子康9 小时前
GPT-Red:自动化红队如何形成 Agent 安全数据飞轮(4 个闭环 + 6 类评测指标 + 5 类风险误读)
人工智能·openai·agent
阿里云大数据AI技术9 小时前
DataWorks Data Agent 实战课堂(二):一句话搞定数据集成+处理,实现端到端数据流水线
人工智能·agent
像我这样帅的人丶你还9 小时前
MCP + npm:给五年前的老系统接上AI
前端·javascript·agent
牧艺10 小时前
从 Tool Calling 到 MCP Server:把业务能力做成 Agent 可复用接口
agent·全栈·mcp
码上解惑10 小时前
从模型接入到应用运行:智能体开发平台的整体架构设计
人工智能·agent·智能体·spring ai