
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 则填补了另一块空白:

AG-UI 通信场景示意图
AG-UI 的核心特性
根据官方文档,AG-UI 具有两大核心特性:
双向交互的实际案例:ChatGPT 的响应控制
在 ChatGPT 应用中,当您发送问题后,打开浏览器 “检查” 面板可观察到:

系统通过 https://chatgpt.com/backend-api/f/conversation 路由发送 SSE(服务器发送事件)来传输响应内容;
若您点击 “停止生成” 按钮,系统会向另一个路由 https://chatgpt.com/backend-api/stop_conversation 发送 GET 请求以终止响应。
这正是双向交互的典型场景 ——AG-UI 的设计初衷之一,就是将这类交互逻辑标准化。
AG-UI 的兼容性优势
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 智能体。
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 实现的核心代码:
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 智能体替换前端框架时大幅减少适配工作量提升系统灵活性和可维护性