AG-UI:把 Agent 与前端之间的“私有暗号”变成标准协议

做 Agent 应用时,难点往往不只是模型调用,而是如何把流式文本、工具执行、页面状态和人工确认稳定地传到前端。AG-UI 解决的正是这一层问题。

一、AG-UI 是什么

AG-UI(Agent-User Interaction Protocol)是一套连接 AI Agent 与用户界面的开放、轻量、事件驱动协议。

它不负责模型推理,而是定义:前端如何发起一次 Agent 运行,以及 Agent 如何持续反馈执行过程。

  • MCP:Agent 如何连接工具、资源和提示词
  • A2A:Agent 如何发现并协作其他 Agent
  • AG-UI:Agent 如何与用户界面实时交互

AG-UI 当前还提供能力发现和协议握手机制,可作为前端入口连接支持 MCP、A2A 的 Agent。它不会替代 MCP 或 A2A,而是把这些能力统一呈现到 UI。

二、为什么不能只定义一个 SSE 接口

SSE 只是传输方式。实际项目仍要自行约定文本增量、工具进度、共享状态、人工确认、取消和异常等格式。

接入前

text 复制代码
/chat/stream       → 流式回答
/tool/status       → 工具进度
/page/state        → 页面状态
/action/confirm    → 人工确认
/error             → 异常信息

接入 AG-UI 后

text 复制代码
POST /ag-ui
  请求:RunAgentInput
  响应:一条有类型的 AG-UI Event Stream

差别不只是少写几个接口,而是文本、工具、状态和生命周期开始使用同一种交互语言。

AG-UI 简化交互流程

三、AG-UI 如何简化交互

1. 一个输入携带完整上下文

text 复制代码
RunAgentInput
├── threadId / runId     会话与运行标识
├── messages             历史消息
├── tools                前端可执行工具
├── state                当前共享状态
├── context              页面或业务上下文
└── resume               中断恢复信息

后端无需再从多个接口拼装消息、工具和页面上下文。

2. 一条事件流覆盖完整运行过程

text 复制代码
生命周期:RUN_STARTED / RUN_FINISHED / RUN_ERROR
文本消息:TEXT_MESSAGE_START / CONTENT / END
工具调用:TOOL_CALL_START / ARGS / END / TOOL_CALL_RESULT
共享状态:STATE_SNAPSHOT / STATE_DELTA

前端只建立一套连接,并按事件语义更新 UI:

text 复制代码
TEXT_MESSAGE_CONTENT  → 追加流式文本
TOOL_CALL_*           → 展示工具进度或交互组件
STATE_DELTA           → 更新页面局部状态
RUN_FINISHED          → 结束 loading 或进入中断状态

3. 工具调用直接变成生成式 UI

Agent 不再只能返回 Markdown。它可以调用前端工具,由页面渲染卡片、图表、按钮或表单。用户操作结果再回到 Agent,形成连续的人机协作。

4. 共享状态不再依赖轮询

STATE_SNAPSHOT 发送完整状态,STATE_DELTA 发送增量变化。适合表单协作、长任务进度、结构化数据编辑和页面恢复。

5. 人工确认有标准的暂停与恢复方式

AG-UI 能力模型支持 approvals、interventions、feedback 和 interrupts。敏感操作可先返回 interrupt,用户确认或修改参数后,再通过 RunAgentInput.resume 恢复。

6. Agent 可以声明自身能力

客户端可读取 Agent capabilities,判断它是否支持传输方式、工具调用、结构化输出、状态同步、多模态和人工确认,再动态启用对应组件。

7. MCP 负责连接工具,AG-UI 负责连接用户

text 复制代码
用户操作
  → AG-UI 发送上下文
  → Agent 通过 MCP 调用工具
  → AG-UI 返回工具、文本和状态事件
  → 前端实时展示过程与结果

新增 MCP Server 后,工具来源虽然变化,但前端仍消费同一套 AG-UI 事件。

四、AG-UI 带来的主要收益

  • 统一交互模型:文本、工具、状态、生命周期使用同一套事件
  • 减少私有接口:不必分别设计工具进度、状态同步和确认回调
  • 降低前端耦合:替换模型或 Agent 框架时,UI 协议基本不变
  • 提升实时体验:回答、进度、工具参数和状态都能增量呈现
  • 支持生成式 UI:Agent 可以驱动卡片、图表、按钮和表单
  • 支持共享状态:Agent 与页面围绕同一份状态协作
  • 支持人机协同:审批、中断、修改参数和恢复运行有统一表达
  • 支持能力发现:前端可以根据 Agent capabilities 动态适配
  • 兼容 MCP 与 A2A:工具和其他 Agent 能力可以进入同一用户体验

五、一套可迁移的接入结构

目录不必完全一致,关键是找到四个职责:Agent 构建、AG-UI Endpoint、服务端 Runtime 和 UI 渲染。

text 复制代码
backend/
├── app/agents/chat_agent.py    # 构建 Agent graph
├── app/api/ag_ui.py            # 挂载 AG-UI Endpoint
└── app/main.py                 # FastAPI 入口

frontend/
├── src/app/api/copilotkit/route.ts  # Runtime 代理
├── src/app/providers.tsx            # Provider
└── src/features/agent/
    ├── agent-chat.tsx                # 聊天 UI
    └── use-agent-tools.tsx           # 前端工具

单文件、DDD、插件化或微服务项目同样适用,只需把示例文件映射到自己的扩展点。

AG-UI 通用接入架构图

六、Python 侧:把现有 Agent 暴露为 AG-UI Endpoint

1. 安装依赖

bash 复制代码
pip install fastapi uvicorn langgraph ag-ui-protocol ag-ui-langgraph

2. 保留现有 Agent 构建方式

示例:app/agents/chat_agent.py

python 复制代码
from my_agent.workflow import build_workflow


def create_chat_graph():
    """返回已经 compile 的 LangGraph graph。"""
    return build_workflow().compile()

my_agent.workflow 是占位。实际项目应继续复用自己的模型、工具、checkpoint、依赖注入和可观测性配置。

3. 使用 CopilotKit 前端能力时接入 Middleware

如果只需要基础 AG-UI 消息和事件传输,协议适配器本身即可工作;如果还要使用 useFrontendTool、共享状态、中断恢复等 CopilotKit 能力,则应在创建 Agent 时加入 CopilotKitMiddleware。

bash 复制代码
pip install copilotkit deepagents

示例:在 Agent Builder 中注册 Middleware

python 复制代码
from copilotkit import CopilotKitMiddleware
from deepagents import create_deep_agent

agent = create_deep_agent(
    model=model,
    tools=tools,
    middleware=[CopilotKitMiddleware()],
)

如果项目使用自定义 Descriptor 或工厂,可以把 Middleware 作为配置传入:

python 复制代码
middleware=(CopilotKitMiddleware(),)

AG-UI Endpoint 负责协议传输和事件流,CopilotKitMiddleware 负责让 Agent 执行过程识别前端工具、上下文及 CopilotKit 交互能力。两者职责不同,完整的生成式 UI 接入通常需要同时配置。

4. 单独封装协议路由

示例:app/api/ag_ui.py

python 复制代码
from ag_ui_langgraph import add_langgraph_fastapi_endpoint
from fastapi import FastAPI

from app.agents.chat_agent import create_chat_graph


def register_ag_ui(app: FastAPI) -> None:
    add_langgraph_fastapi_endpoint(
        app,
        create_chat_graph(),
        "/ag-ui",
    )

这个文件只负责协议适配。以后更换 Agent 时,只需要替换 create_chat_graph()。

5. 挂载到 FastAPI

示例:app/main.py

python 复制代码
from fastapi import FastAPI
from app.api.ag_ui import register_ag_ui

app = FastAPI()
register_ag_ui(app)

如果 graph 依赖数据库、异步 checkpoint 或运行时配置,可在 lifespan 中初始化:

python 复制代码
from contextlib import asynccontextmanager
from fastapi import FastAPI


@asynccontextmanager
async def lifespan(app: FastAPI):
    await initialize_dependencies()
    register_ag_ui(app)
    yield
    await close_dependencies()


app = FastAPI(lifespan=lifespan)

6. 启动服务

bash 复制代码
uvicorn app.main:app --host 0.0.0.0 --port 8000

至此,后端得到一个接收 RunAgentInput、返回 AG-UI 事件流的 POST /ag-ui。

非 LangGraph 项目可以选择对应适配器;没有现成适配器时,也可使用 RunAgentInput、EventEncoder 和 StreamingResponse 手动发送事件。

七、Next.js 侧:建立 Runtime 代理并渲染 UI

以下采用 App Router。Pages Router 的目录不同,但 Runtime、Provider 和 HttpAgent 的职责相同。

1. 安装依赖

bash 复制代码
pnpm add @ag-ui/client @copilotkit/runtime @copilotkit/react-core zod

2. 配置后端地址

示例:.env.development

bash 复制代码
BACKEND_API_URL=http://localhost:8080

该变量只在服务端读取,不需要暴露为 NEXT_PUBLIC_*。

3. 创建 Runtime 代理

示例:src/app/api/copilotkit/route.ts

typescript 复制代码
import { HttpAgent } from '@ag-ui/client';
import {
  CopilotRuntime,
  createCopilotRuntimeHandler
} from '@copilotkit/runtime/v2';

const backendUrl = process.env.BACKEND_API_URL;
if (!backendUrl) throw new Error('BACKEND_API_URL is required');

const runtime = new CopilotRuntime({
  agents: {
    assistant: new HttpAgent({
      agentId: 'assistant',
      url: `${backendUrl}/ag-ui`
    })
  }
});

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: '/api/copilotkit',
  mode: 'single-route'
});

export const GET = handler;
export const POST = handler;
export const OPTIONS = handler;

这层代理用于隐藏后端地址、集中处理认证,并把浏览器请求转发到 AG-UI Endpoint。已有 BFF 或 API Gateway 的项目也可把 HttpAgent 注册在那里。

4. 挂载 Provider

示例:src/app/providers.tsx

tsx 复制代码
'use client';

import { CopilotKitProvider } from '@copilotkit/react-core/v2';

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <CopilotKitProvider
      runtimeUrl='/api/copilotkit'
      useSingleEndpoint>
      {children}
    </CopilotKitProvider>
  );
}

Provider 可以挂在根布局,也可以只包裹需要 Agent 的业务页面。使用内置聊天组件时,还需要在布局文件中引入样式:

tsx 复制代码
import '@copilotkit/react-core/v2/styles.css';

5. 渲染聊天组件

示例:src/features/agent/agent-chat.tsx

tsx 复制代码
'use client';

import { CopilotChat } from '@copilotkit/react-core/v2';
import { useAgentTools } from './use-agent-tools';

export function AgentChat() {
  useAgentTools();

  return (
    <CopilotChat
      agentId='assistant'
      labels={{ welcomeMessageText: '今天想处理什么?' }}
    />
  );
}

agentId='assistant' 必须与 Runtime key 和 HttpAgent.agentId 一致。

6. 注册前端工具

示例:src/features/agent/use-agent-tools.tsx

tsx 复制代码
'use client';

import { useFrontendTool } from '@copilotkit/react-core/v2';
import { z } from 'zod';

export function useAgentTools() {
  useFrontendTool({
    name: 'show_summary',
    description: '在页面中展示结构化摘要',
    parameters: z.object({
      title: z.string(),
      items: z.array(z.string())
    }),
    handler: async ({ title }) => `已展示摘要:${title}`,
    render: ({ args }) => (
      <section>
        <h3>{args.title}</h3>
        <ul>
          {args.items.map((item) => <li key={item}>{item}</li>)}
        </ul>
      </section>
    )
  });
}

Agent 触发 show_summary 时,前端直接渲染结构化组件,而不是让模型拼一段固定格式 Markdown。Vue、原生 TypeScript 或自研组件库也可采用相同映射思路。

八、从输入到渲染的完整调用链

  1. 用户在聊天组件输入消息
  2. Provider 将请求发送到 /api/copilotkit
  3. Next.js Runtime 根据 agentId 找到 HttpAgent
  4. HttpAgent 向 Python /ag-ui 发送 RunAgentInput
  5. Agent 运行模型并按需调用 MCP 或业务工具
  6. Python 持续返回文本、工具和状态事件
  7. Runtime 将事件流转发给浏览器
  8. 聊天组件和前端工具按事件语义更新 UI

业务代码可以变化,但浏览器与 Agent 之间的协议保持稳定。

九、接入检查点

  • 后端地址是 Next.js 服务端可访问的地址
  • 网关关闭 SSE 缓冲,并允许足够长的超时时间
  • Agent 名称、Runtime key 和页面 agentId 保持一致
  • 工具名和参数 Schema 与 Agent 输出一致
  • threadId、runId、messageId 保持稳定
  • 高风险工具增加鉴权、参数校验和人工确认
  • AG-UI、CopilotKit 和 Python 适配器成套升级验证

十、适用边界

AG-UI 不会替代鉴权、会话存储、限流和业务权限;这些仍属于应用自身职责。对于一次性返回简单文本的接口,普通 REST 也可能更轻量。

十一、总结

AG-UI 的核心价值不是"再做一个聊天接口",而是把分散的文本流、工具调用、共享状态、人工确认和运行生命周期统一成可复用的实时事件契约。

MCP 负责让 Agent 接上工具,A2A 负责让 Agent 接上其他 Agent,AG-UI 则负责让这些能力真正进入用户界面。

无论目录、Agent 框架和 UI 组件库如何变化,接入时只要找到四个位置即可:Agent 构建、AG-UI Endpoint、服务端 Runtime、前端渲染。

参考资料

相关推荐
hpoenixf6 分钟前
聊天记录、上下文、记忆、检查点,Agent 怎样接上过去?
agent
hpoenixf12 分钟前
Prompt、Context、RAG、Memory,模型的输入从哪里来?
agent
hpoenixf16 分钟前
Tool、Function Calling、MCP、Skill,一次行动怎样发生?
agent
Java后端的Ai之路5 小时前
RAG 还是 Agent 自主检索?Claude Code 删掉向量库之后,我把选型讲透了
开发语言·人工智能·prompt·agent·rag
泯泷7 小时前
一个小改动,让 Claude 在 SWE-bench 上刷新纪录:工具描述有多重要
llm·agent·claude
qq_158895411118 小时前
AI 音乐生成完成后,音频 URL 为什么会过期?一套可恢复的交付方案
python·api·ai音乐
小虎AI生活9 小时前
WorkBuddy 积分不够烧?我找到了每天白送 5000 的平台
aigc·ai编程
Csvn9 小时前
模型挂了全站 502?降级链让它先保住“有响应”(R04)
人工智能·aigc·agent
Warson_L10 小时前
高性能架构设计
python·go
databook11 小时前
新数据到手,先跑哪几步检查
python·数据分析