首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >AG-UI 不是炒作!这是 AI 开发者迟早要学的通信协议,从原理到落地一篇搞懂

AG-UI 不是炒作!这是 AI 开发者迟早要学的通信协议,从原理到落地一篇搞懂

作者头像
HELLO程序员
发布2026-06-26 18:49:51
发布2026-06-26 18:49:51
1140
举报

AG-UI:完善 AI 工具开发拼图的关键一环

AG-UI(智能体用户交互协议)是完善 AI 工具开发生态拼图的又一重要组件。本文将带您了解 AG-UI 究竟是什么,以及作为 Web 开发者和软件工程师的我们应当如何应对。

我坚信,AG-UI 并非又一场转瞬即逝的炒作,而是我们迟早都需要的技术标准。

01

什么是 AG-UI(智能体用户交互协议,Agent User Interaction Protocol)?

AG-UI 官方拥有完善的网站和相对规范的文档,因此我不会对其中内容进行过多修改,核心定义如下:

AG-UI 通过开放协议,标准化了前端应用与 AI 智能体(AI Agents)之间的连接方式。

AG-UI 用于智能体与应用间通信的协议示意图

现有重要 AI 协议对比

目前行业内已存在两大关键 AI 协议,而 AG-UI 则填补了另一块空白:

  • 以标准化方式为智能体(或大语言模型 LLM)提供工具调用能力。
  • 让不同 AI 智能体能够使用统一的 “语言”(协议)相互交互。
  • 解决的核心问题是 “AI 智能体与前端应用如何交互”,为两者提供统一的通信标准。

AG-UI 通信场景示意图

AG-UI 的核心特性

根据官方文档,AG-UI 具有两大核心特性:

  • AG-UI 本质是事件驱动型协议,智能体可发送 16 种不同类型的事件(下文将详细介绍)。
  • 客户端(前端)在发起请求后,仍能对后续的工作流或任务执行操作(如取消响应生成)。

双向交互的实际案例:ChatGPT 的响应控制

在 ChatGPT 应用中,当您发送问题后,打开浏览器 “检查” 面板可观察到:

系统通过 https://chatgpt.com/backend-api/f/conversation 路由发送 SSE(服务器发送事件)来传输响应内容;

若您点击 “停止生成” 按钮,系统会向另一个路由 https://chatgpt.com/backend-api/stop_conversation 发送 GET 请求以终止响应。

这正是双向交互的典型场景 ——AG-UI 的设计初衷之一,就是将这类交互逻辑标准化。

AG-UI 的兼容性优势

  • 只需确保事件符合 AG-UI 规范即可,不强制其他额外约束,适配成本低。
  • 支持 SSE、Webhook、Websocket 等多种传输协议,开发者可根据业务场景选择最适合的方式。

AG-UI 的核心:事件类型(Events)

事件是 AG-UI 的核心构建块,不同类型的事件名称直观反映了其功能,主要分为以下几类(示例):

事件分类

具体事件示例

功能说明

生命周期事件

RunStarted、RunFinished、RunError

标记智能体任务的启动、完成、错误状态

文本消息事件

TextMessageStart、TextMessageEnd、TextMessageContent

控制文本消息的开始、结束及内容传输

工具调用事件

ToolCallStart、ToolCallEnd、ToolCallArgs

管理智能体工具调用的启动、结束及参数传递

(注:细心的开发者可能会发现事件命名的小差异,例如生命周期事件用过去式(Started/Finished),其他事件用现在式(Start/End);部分事件用 “Finish”,部分用 “End”。这或许是细节问题,无需过度纠结。)

综上,我们可将 AG-UI 简单理解为:前端 AI 应用与 AI 智能体之间的 “通用通信语言”。

02

为何采用 AG-UI 至关重要?

AG-UI 的重要性,可参考 MCP 协议的普及逻辑:如今几乎所有工作流类产品都支持 MCP,这使得开发者无需修改一行代码,就能在不同平台复用整套工具集。

AG-UI 的价值类似,但聚焦于前端适配 —— 一旦前端应用支持 AG-UI,就能 “即插即用” 地对接所有支持该协议的后端服务,无需为不同服务单独开发适配逻辑,大幅降低跨平台、跨服务的集成成本。

03

前端开发者需了解哪些内容?

目前支持 AG-UI 的库和工具还不多,但如果您正在开发 AI 聊天机器人等前端 AI 应用,建议优先考虑使用 AG-UI 实现与 AI 智能体的通信。

以下是一个 TypeScript 示例代码(该代码大概率由 AI 生成),展示了 AG-UI 的基础用法:

在查看代码前需注意:发送消息时,需按规范传入id(唯一标识)、role(角色,如 “user”)和content(内容);示例中通过 Mastra SDK 创建 AI 智能体。

代码语言:javascript
复制
代码语言:javascript
复制
import * as readline from "readline"
import { agent } from "./agent"
import { randomUUID } from "node:crypto"
// 创建命令行交互接口
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
})
async function chatLoop() {
  console.log("🤖 AG-UI助手已启动!")
  console.log("输入消息后按Enter发送,按Ctrl+D退出。\n")
  return new Promise<void>((resolve) => {
    const promptUser = () => {
      rl.question("> ", async (input) => {
        // 处理空输入
        if (input.trim() === "") {
          promptUser()
          return
        }
        console.log("")
        // 处理消息时暂停输入
        rl.pause()
        // 将用户消息添加到对话中(符合AG-UI规范)
        agent.messages.push({
          id: randomUUID(), // 生成唯一消息ID
          role: "user",     // 角色为“用户”
          content: input.trim(), // 用户输入内容
        })
        try {
          // 运行智能体并绑定AG-UI事件处理器
          await agent.runAgent(
            {}, // 无需额外配置
            {
              // 文本消息开始事件:提示“助手回复中”
              onTextMessageStartEvent() {
                process.stdout.write("🤖 助手: ")
              },
              // 文本消息内容事件:流式输出回复内容
              onTextMessageContentEvent({ event }) {
                process.stdout.write(event.delta)
              },
              // 文本消息结束事件:回复完成后换行
              onTextMessageEndEvent() {
                console.log("\n")
              },
            }
          )
        } catch (error) {
          console.error("❌ 错误:", error)
        }
        // 恢复输入,等待下一次用户提问
        rl.resume()
        promptUser()
      })
    }
    // 处理Ctrl+D退出事件
    rl.on("close", () => {
      console.log("\n👋 感谢使用AG-UI助手!")
      resolve()
    })
    // 启动用户提问循环
    promptUser()
  })
}
async function main() {
  await chatLoop()
}
main().catch(console.error)

额外建议:尽量使用成熟的 AG-UI 库而非自研实现预计未来几个月内会出现更多 “即插即用” 的工具库,降低开发成本。

04

后端开发者需了解哪些内容?

如果您正在开发基于 OpenAI、Claude 等模型的聊天机器人 API,可将其改造为 AG-UI 兼容版本 —— 这样所有支持 AG-UI 的前端应用都能 “开箱即用” 地对接您的服务,无需额外适配。

示例:将 OpenAI API 改造为 AG-UI兼容版本

改造逻辑可概括为四步:

1.创建 OpenAI 客户端,并发送RUN_STARTED事件(标记任务启动);

2.将用户消息发送至 OpenAI 的chat.completions接口,并开启流式传输(stream=True);

3.将 OpenAI 返回的每个数据块转换为 AG-UI 规范的事件(如TEXTMESSAGECHUNK文本块、TOOLCALLCHUNK工具调用块)并转发;

4.任务完成时发送RUNFINISHED事件出错则发送RUNERROR事件。

以下是基于 FastAPI 实现的核心代码:

代码语言:javascript
复制
import os
import uuid
import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
from ag_ui.core import (
    RunAgentInput,
    EventType,
    RunStartedEvent,
    RunFinishedEvent,
    RunErrorEvent,
)
from ag_ui.encoder import EventEncoder
from openai import OpenAI
app = FastAPI(title="AG-UI OpenAI Server")
# Initialize OpenAI client - uses OPENAI_API_KEY from environment
client = OpenAI()
@app.post("/")
async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request):
    """OpenAI agentic chat endpoint"""
    accept_header = request.headers.get("accept")
    encoder = EventEncoder(accept=accept_header)
    async def event_generator():
        try:
            yield encoder.encode(
                RunStartedEvent(
                    type=EventType.RUN_STARTED,
                    thread_id=input_data.thread_id,
                    run_id=input_data.run_id
                )
            )
            # Call OpenAI's API with streaming enabled
            stream = client.chat.completions.create(
                model="gpt-4o",
                stream=True,
                # Convert AG-UI tools format to OpenAI's expected format
                tools=[
                    {
                        "type": "function",
                        "function": {
                            "name": tool.name,
                            "description": tool.description,
                            "parameters": tool.parameters,
                        }
                    }
                    for tool in input_data.tools
                ] if input_data.tools else None,
                # Transform AG-UI messages to OpenAI's message format
                messages=[
                    {
                        "role": message.role,
                        "content": message.content or "",
                        # Include tool calls if this is an assistant message with tools
                        **({"tool_calls": message.tool_calls} if message.role == "assistant" and hasattr(message, 'tool_calls') and message.tool_calls else {}),
                        # Include tool call ID if this is a tool result message
                        **({"tool_call_id": message.tool_call_id} if message.role == "tool" and hasattr(message, 'tool_call_id') else {}),
                    }
                    for message in input_data.messages
                ],
            )
            message_id = str(uuid.uuid4())
            # Stream each chunk from OpenAI's response
            for chunk in stream:
                # Handle text content chunks
                if chunk.choices[0].delta.content:
                    yield encoder.encode({
                        "type": EventType.TEXT_MESSAGE_CHUNK,
                        "message_id": message_id,
                        "delta": chunk.choices[0].delta.content,
                    })
                # Handle tool call chunks
                elif chunk.choices[0].delta.tool_calls:
                    tool_call = chunk.choices[0].delta.tool_calls[0]
                    yield encoder.encode({
                        "type": EventType.TOOL_CALL_CHUNK,
                        "tool_call_id": tool_call.id,
                        "tool_call_name": tool_call.function.name if tool_call.function else None,
                        "parent_message_id": message_id,
                        "delta": tool_call.function.arguments if tool_call.function else None,
                    })
            yield encoder.encode(
                RunFinishedEvent(
                    type=EventType.RUN_FINISHED,
                    thread_id=input_data.thread_id,
                    run_id=input_data.run_id
                )
            )
        except Exception as error:
            yield encoder.encode(
                RunErrorEvent(
                    type=EventType.RUN_ERROR,
                    message=str(error)
                )
            )
    return StreamingResponse(
        event_generator(),
        media_type=encoder.get_content_type()
    )
def main():
    """Run the uvicorn server."""
    port = int(os.getenv("PORT", "8000"))
    uvicorn.run(
        "example_server:app",
        host="0.0.0.0",
        port=port,
        reload=True
    )
if __name__ == "__main__":
    main()

我的预测:未来几个月内,行业将出现更多 AG-UI “即插即用包开发者无需手动实现上述逻辑只需引入库即可快速适配 AG-UI

05

五·总结

尽管 AG-UI 目前仍处于推广初期,但已有不少开发者和团队开始将其集成到库和工具中作为开发者我们应持续关注这一技术标准在合适的场景下尝试采用——这将帮助我们在未来更换软件组件(如切换 AI 智能体替换前端框架时大幅减少适配工作量提升系统灵活性和可维护性

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 HELLO程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档