跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
TypeScriptNode.jsAI大前端

AG-UI:构建 AI 前端交互的统一协议

综述由AI生成AG-UI(Agent User Interaction Protocol)是 CopilotKit 团队提出的开源协议,旨在规范 AI Agent 与前端用户界面的通信流程。它通过统一的事件流、实时交互、工具编排和状态管理机制,解决了 AI 应用开发中的技术碎片化、实时性困难及人机协作缺失等问题。核心包含 17 种标准化事件类型,支持流式文本传输、工具调用审批(Human-in-the-loop)及共享状态同步。该协议兼容多种语言框架(如 LangGraph、React),提供前后端解耦的标准化接口,降低 AI 集成成本,提升用户体验。

雾岛听风发布于 2026/4/6更新于 2026/5/2140 浏览
AG-UI:构建 AI 前端交互的统一协议

AG-UI:构建 AI 前端交互的统一协议

引言

随着人工智能技术的飞速发展,AI Agent 已经从概念走向实际应用。然而,在将这些智能体集成到前端应用中时,开发者面临着一个关键挑战:如何实现 AI Agent 与用户界面之间的高效、标准化交互?

AG-UI(Agent User Interaction Protocol)正是为解决这一痛点而诞生的开源协议。它不仅仅是一个技术规范,更是连接 AI 智能体与前端应用的桥梁,让开发者能够构建真正智能化的用户体验。它能够让开发者构建出真正嵌入 UI 应用、感知上下文、实时协同的智能体,而不仅仅是一个在后台提供文本答案的 API 服务。

一、AG-UI 是什么?

1.1 核心定义

AG-UI(Agent-User Interaction Protocol) 是由 CopilotKit 团队提出的开源、轻量级协议,专门用于规范 AI Agent 与前端用户界面之间的通信流程,是一套开源的Agent 与 UI 界面之间的交互协议。它的核心使命是:

标准化前端应用与 AI 智能体的连接方式,通过开放协议实现通用的 AI 驱动系统通信。

在这里插入图片描述

1.2 协议定位

在这里插入图片描述

在 AI 生态系统中,AG-UI 与其他协议形成互补关系:

  • MCP(Model Context Protocol):定义 AI 模型调用外部工具的协议规范
  • A2A(Agent-to-Agent Protocol):规范智能体之间的通信协议
  • AG-UI:专注于智能体与用户(前端应用)之间的交互规范

这种分工明确的设计让 AG-UI 能够专注于解决人机交互层面的问题,而不与其他协议产生冲突。

二、为什么需要 AG-UI?

在 AG-UI 出现之前,AI 应用开发面临诸多挑战:

技术碎片化
  • 每个 AI 框架(LangGraph、CrewAI、Mastra 等)都有独特的事件机制和 API
  • 不同模型提供商的接口格式各异,增加适配成本
  • 缺乏统一的前后端同步机制
实时性困难
  • 用户期待实时的流式响应体验
  • 传统的请求 - 响应模式无法满足复杂 AI 工作流需求
  • 状态同步和进度反馈机制不完善
人机协作缺失
  • 用户难以对 AI 执行过程进行实时干预
  • 缺乏标准化的人机协作(Human-in-the-loop)机制
  • 工具调用和状态共享缺乏统一规范

这些问题会导致开发者在和多个 AI 服务对接或构建复杂 AI 应用时重写大量代码。

假设有一套通信协议,可以统一前端应用和后端 Agent 之间的交互格式,做到前后端解耦,让大家各司其职。AG-UI 的出现,就是为了解决这个问题。

三、AG-UI 核心架构

3.1 整体架构设计

在这里插入图片描述

  • Application:直接与用户交互的前端应用层,比如 ChatGPT、Cursor 等任何 AI powered 应用。
  • AG-UI Client:在前端侧负责与后端 Agent 进行通信,使用 AG-UI 协议。
  • Agent:后端 Agent 通常和 AI 服务或其他 Agent 对接,用来处理用户请求。

3.2 核心特性

统一事件流

AG-UI 的核心是单一序列的 JSON 事件,简化了代理与前端的实时同步,确保流畅无碍的通信,不论代理使用何种"语言"或内部实现。

实时交互

支持 LLM 逐步生成的 token 立即显示,提供流畅、自然的用户体验,并实现人机协同工作流程。

工具编排

现代代理可调用函数、运行代码、访问 API。AG-UI 确保前端能实时显示进度和结果,支持人工批准并无缝恢复运行。

共享状态

通过高效传输差异更新,代理能够生成逐步演进的计划、表格或代码文件夹,节省带宽并保持同步状态。

并发与取消

支持用户同时发出多个查询、中途停止或切换线程,通过线程 ID、运行 ID 和有序的关闭路径确保稳定运行。

安全边界

协议内建权限管理、身份认证等机制,提供企业级的 CORS,确保数据传输的安全性和可靠性。

3.3 事件 Events

AG-UI 定义了17 种标准化事件类型,构成完整的 AI 交互生命周期管理体系。基于官方文档,以下是完整的事件系统架构:

EventType 枚举定义
enum EventType {
  TEXT_MESSAGE_START = "TEXT_MESSAGE_START",
  TEXT_MESSAGE_CONTENT = "TEXT_MESSAGE_CONTENT",
  TEXT_MESSAGE_END = "TEXT_MESSAGE_END",
  TOOL_CALL_START = "TOOL_CALL_START",
  TOOL_CALL_ARGS = "TOOL_CALL_ARGS",
  TOOL_CALL_END = "TOOL_CALL_END",
  TOOL_CALL_RESULT = "TOOL_CALL_RESULT",
  STATE_SNAPSHOT = "STATE_SNAPSHOT",
  STATE_DELTA = "STATE_DELTA",
  MESSAGES_SNAPSHOT = "MESSAGES_SNAPSHOT",
  RAW = "RAW",
  CUSTOM = "CUSTOM",
  RUN_STARTED = "RUN_STARTED",
  RUN_FINISHED = "RUN_FINISHED",
  RUN_ERROR = "RUN_ERROR",
  STEP_STARTED = "STEP_STARTED",
  STEP_FINISHED = "STEP_FINISHED",
}
AG-UI 事件分类总览
事件分类事件类型事件名称功能描述使用场景
生命周期事件(5)流程控制、错误处理RUN_STARTED运行开始标记 Agent 执行开始初始化 UI 状态,显示加载状态
STEP_STARTED步骤开始标记单个步骤开始显示当前执行步骤
STEP_FINISHED步骤完成标记单个步骤完成更新步骤状态,显示进度
RUN_FINISHED运行完成标记整个执行完成清理状态,显示最终结果
RUN_ERROR运行错误标记执行出现错误错误处理,显示错误信息
文本消息事件(3)实时对话、流式输出TEXT_MESSAGE_START消息开始开始新的文本消息创建消息容器
TEXT_MESSAGE_CONTENT消息内容流式传输消息内容实时显示打字效果
TEXT_MESSAGE_END消息结束标记消息传输完成完成消息渲染
工具调用事件(4)功能扩展、透明度TOOL_CALL_START工具调用开始开始调用外部工具显示工具调用状态
TOOL_CALL_ARGS工具参数传输工具调用参数显示调用参数信息
TOOL_CALL_RESULT工具调用结果返回工具执行结果显示工具返回的数据
TOOL_CALL_END工具调用结束工具调用完成显示调用结果
状态管理事件(3)数据同步、一致性STATE_SNAPSHOT状态快照完整状态数据同步完整应用状态
STATE_DELTA状态变更增量状态更新高效更新部分状态
MESSAGES_SNAPSHOT消息快照完整消息历史同步对话历史
特殊事件(2)系统集成、定制化RAW原始事件未处理的原始数据调试和扩展用途
CUSTOM自定义事件用户定义的事件特殊业务逻辑处理
事件流程示例
sequenceDiagram
    participant User as 👤 用户
    participant Frontend as 🖥️ 前端
    participant Agent as 🤖 AI Agent
    participant Tool as 🛠️ 工具
    User->>Frontend: 发送消息
    Frontend->>Agent: 用户输入
    Agent->>Frontend: RUN_STARTED
    Agent->>Frontend: STEP_STARTED
    Agent->>Frontend: TEXT_MESSAGE_START
    Agent->>Frontend: TEXT_MESSAGE_CONTENT (流式)
    Agent->>Frontend: TEXT_MESSAGE_CONTENT (流式)
    Agent->>Frontend: TEXT_MESSAGE_END
    Agent->>Frontend: TOOL_CALL_START
    Agent->>Frontend: TOOL_CALL_ARGS
    Agent->>Tool: 执行工具
    Tool->>Agent: 工具结果
    Agent->>Frontend: TOOL_CALL_RESULT
    Agent->>Frontend: TOOL_CALL_END
    Agent->>Frontend: STATE_SNAPSHOT
    Agent->>Frontend: STEP_FINISHED
    Agent->>Frontend: RUN_FINISHED
    Frontend->>User: 显示完整响应

3.4 Agents 智能体

Agent 是 AG-UI 里的核心组成部分,负责处理前端发起的请求,和 LLM 交互并生成响应(响应需遵循 Events 格式),同时还要管理对话状态和消息历史。在 Agent 底层可以和其他任意的 AI 服务连接(比如任意的 LLM、定制的 AI 系统、RAG、其他 Agent 等等)。

Agent 具备丰富的交互能力:

  • 基本的文本传输: 通过 TEXT_MESSAGE_*系列事件实现流式文本交互
  • 工具调用: 在 AG-UI 的规范下,Agent 可以使用哪些 Tools 是由前端告知的。当 Agent 决策需要使用工具时,可以通过一系列事件(TOOL_CALL_START -> TOOL_CALL_ARGS -> TOOL_CALL_END)通知到前端,前端在收到事件后,可决定是否给用户展示对应交互,告知需要调用哪些工具以及对应参数,由用户决定是否调用。调用结果会通过 Message 传递给 Agent。
  • 状态管理: Agent 可以向前端传递最新完整的状态(STATE_SNAPSHOT)或增量同步状态(STATE_DELTA),可以让前端应用从中断状态恢复。
  • 多 Agent 交互: Agent 可以通过 A2A 等其他协议和其他 Agent 交互,此过程可以不让前端用户感知(只要不发送 Event 就行)。
  • Human-in-the-Loop 控制: 可以将人在回路的控制能力作为 Tool 注入给 Agent,由 Agent 决定在必要时候让人进行决策(仍然通过工具调用链路)
  • …

3.5 Messages

传统与 LLM 通信时,message 中的 role 通常被分为 system、user、assistant。在 AG-UI 中,developer、工具调用的结果也被当成一种消息类型(tool)。

核心特性:

  • 流式传输: 通过 TEXT_MESSAGE_* 实现打字机效果
  • 角色管理: 支持 developer、user、assistant、system、tool 等多种角色
  • 消息历史: MESSAGES_SNAPSHOT 提供完整消息快照(上下文)
  • 工具集成: 工具调用结果作为独立的消息类型处理

对话示例:

[
  // User
  { id: "msg_1", role: "user", content: "What's the weather in New York?" },
  // Assistant response with tool call
  {
    id: "msg_2",
    role: "assistant",
    content: "Let me check the weather for you.",
    toolCalls: [
      {
        id: "call_1",
        type: "function",
        function: {
          name: "get_weather",
          arguments: '{"location": "New York", "unit": "celsius"}'
        }
      }
    ]
  },
  // 注意,这里的工具调用其实是由 Agent 通知给前端,由前端用户决定是否调用工具,并把调用结果传给 Agent,让 Agent 继续后续流程
  // Tool result
  {
    id: "result_1",
    role: "tool",
    content: '{"temperature": 22, "condition": "Partly Cloudy", "humidity": 65}',
    toolCallId: "call_1"
  },
  // Assistant's final response using tool results
  {
    id: "msg_3",
    role: "assistant",
    content: "The weather in New York is partly cloudy with a temperature of 22°C and 65% humidity."
  }
]

3.6 状态管理

除了基本的同步聊天消息之外,可以同步任意的状态,让人和 AI 的操作可以衔接操作。

官方提供了一个比较有意思的 Demo:让 AI 优化一份菜谱,菜谱在前端使用富交互展示的,随着 AI Agent 的优化,前端交互可实时进行更新。

https://feature-viewer-langgraph.vercel.app/feature/shared_state

核心特性:

  • 状态快照: STATE_SNAPSHOT 提供完整应用状态的一次性同步,一般用于初始状态同步、中断状态恢复等
  • 增量更新: STATE_DELTA 支持高效的部分状态更新,使用 JSON Patch,可以流式快速更新
  • 复杂交互: 前端应用可以实现更复杂交互的流式更新,给用户更好的交互体验

3.7 工具调用 Tools

需要和前端交互的工具可以在前端定义,并通过协议传给 Agent,当 Agent 认为需要使用工具时,会使用 Event 向前端发消息,前端可向用户展示被调用的工具名称和参数,让用户决定是否继续。

在 AG-UI 中,工具可以:

  • 获取更多的信息: 如天气查询、搜索引擎等
  • 操作外部系统: 如发送邮件、创建文档等
  • 让人进行信息输入或二次确认: Human-in-the-loop 机制

核心特性:

  • 前端定义: 工具由前端定义并传递给 Agent
  • 用户控制: 用户可以决定是否执行工具调用
  • 参数透明: TOOL_CALL_ARGS 支持复杂参数的流式传输
  • 结果返回: TOOL_CALL_RESULT 提供工具执行结果的标准化返回
  • 执行监控: 完整的工具调用生命周期跟踪

工具调用流程:

// 1. 开始工具调用 - Agent 通知前端需要调用工具
const toolStart: ToolCallStartEvent = {
  type: EventType.TOOL_CALL_START,
  toolCallId: "tool_456",
  toolCallName: "get_weather",
  parentMessageId: "msg_123"
};

// 2. 传输参数 - 显示给用户工具调用的参数
const toolArgs: ToolCallArgsEvent = {
  type: EventType.TOOL_CALL_ARGS,
  toolCallId: "tool_456",
  delta: '{"location": "New York", "unit": "celsius"}'
};

// 3. 返回结果 - 用户确认后,前端执行工具并返回结果
const toolResult: ToolCallResultEvent = {
  type: EventType.TOOL_CALL_RESULT,
  messageId: "msg_124",
  toolCallId: "tool_456",
  content: '{"temperature": 22, "condition": "Partly Cloudy", "humidity": 65}',
  role: "tool"
};

// 4. 结束调用 - 工具调用完成
const toolEnd: ToolCallEndEvent = {
  type: EventType.TOOL_CALL_END,
  toolCallId: "tool_456"
};

工具调用事件序列:
ToolCallStart -> ToolCallArgs -> ToolCallResult -> ToolCallEnd

四、AG-UI 的技术优势

4.1 灵活性与兼容性

事件结构灵活性
  • 事件无需完全匹配 AG-UI 格式,只需保持兼容性
  • 现有框架可以最小化改动适配 AG-UI
  • 支持渐进式迁移策略

4.2 开发者友好性

丰富的 SDK 支持
  • TypeScript SDK:提供完整的类型定义和开发工具
  • Python SDK:支持主流 AI 框架集成
  • 多语言扩展:社区驱动的其他语言实现
现成的集成方案

AG-UI 已与多个主流 AI 框架深度集成:

FrameworkStatusAG-UI Resources
No-framework✅ Supported➡️ Docs coming soon
LangGraph✅ Supported➡️ Demo
Mastra✅ Supported➡️ Demo
CrewAI✅ Supported➡️ Demo
AG2✅ Supported➡️ Demo
Agno✅ Supported➡️ Docs
LlamaIndex✅ Supported➡️ Docs
Pydantic AI🛠️ In Progress–
Vercel AI SDK🛠️ In Progress–
Google ADK🛠️ In Progress–
OpenAI Agent SDK💡 Open to Contributions–
AWS Bedrock Agents💡 Open to Contributions–
Cloudflare Agents💡 Open to Contributions–
Strands Agents SDK💡 Open to Contributions–
Language SDKStatusAG-UI Resources
.NET🛠️ In Progress➡️ PR
Nim🛠️ In Progress➡️ PR
Rust🛠️ In Progress

五、演示 Demo

5.1 CopilotKit 的演示 Demo

CopilotKit 可以被看作 AG-UI 协议实现的一个框架。它提供了完整的前后端集成方案,使开发者能够快速将 AI Copilot 引入应用,包括:TypeScript/React 前端组件库、Python/Node SDK、以及可选的云端代理服务等。

  • 后端 Agent:使用 Python + LangGraph 构建的一个 Workflow 智能体。
  • 前端应用:使用 React+TS 构建简单的 Demo。
  • Copilot 助手:前端接入 CopilotKit 给应用嵌入的智能助手。
5.1.1 后端 python + Copilotkit LangGraph SDK
pip install copilotkit ...
import os
from dotenv import load_dotenv
load_dotenv()
from fastapi import FastAPI
import uvicorn
from copilotkit.integrations.fastapi import add_fastapi_endpoint
from copilotkit import CopilotKitRemoteEndpoint, LangGraphAgent
from sample_agent.agent import graph

app = FastAPI()
sdk = CopilotKitRemoteEndpoint(
    agents=[
        LangGraphAgent(
            name="sample_agent",
            description="一个模拟智能体",
            graph=graph,
        )
    ],
)
add_fastapi_endpoint(app, sdk, "/copilotkit")

def main():
    port = int(os.getenv("PORT", "8080"))
    uvicorn.run(
        "sample_agent.demo:app",
        host="0.0.0.0",
        port=port,
        reload=True,
    )

if __name__ == "__main__":
    main()
5.1.2 前端 React + Copilotkit React-ui
  1. 创建前端应用
npx create-next-app@latest
  1. 集成 Copilot SDK
npm install @copilotkit/react-ui @copilotkit/react-core
npm install @copilotkit/runtime class-validator
  1. 增加路由
... const runtime = new CopilotRuntime({ remoteEndpoints: [{url: "http://localhost:8080/copilotkit"},], }); export const POST = async (req: NextRequest) => { const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ runtime, serviceAdapter, endpoint: "/api/copilotkit", }); return handleRequest(req); };
  1. 配置全局 CopilotKit 功能(agent 是后端 agent 的名字)
<CopilotKit agent="sample_agent" runtimeUrl="/api/copilotkit" showDevConsole={false}>
  {children}
</CopilotKit>
  1. 增加 Copilot 界面,与 Agent 对话,实现智能交互体验。可以选择 sidebar(边栏)、popup(弹出式)、chat(聊天)等多种 UI 形式。这里我们添加一个 sidebar 形式的 Copilot 到主页。
import { CopilotSidebar } from "@copilotkit/react-ui";
import { useCoAgent, useCoAgentStateRender, useCopilotAction, useLangGraphInterrupt } from "@copilotkit/react-core";

export default function App() {
  return (
    <>
      <Home/>
      <CopilotSidebar
        defaultOpen={true}
        instructions={"您应尽可能地帮助用户。请根据您拥有的数据以最佳方式回答问题。"}
        labels={{ title: "智能 AI Copilot", initial: `# 👋 您好! 我是你的智能 Copilot。演示功能: - **共享状态**: 搜索历史实时的展示 - **前端工具**: 调用前端工具打招呼 - **生成式 UI**: 获取天气信息展示卡片 - **HITL 流程**: 工具调用的人工审核` }}
      />
    </>
  );
}
5.1.3 工具调用(不仅可以调用后端设置的工具(比如搜索、访问数据库、MCP),还可以调用前端定义的 UI'工具'(比如更改样式))
  1. 使用 langchain_mcp_adapters.client 提供的 MCPClient 连接 tavily-mcp,注册 agent 工具
from langchain_mcp_adapters.client import MultiServerMCPClient
...
async def get_all_tools():
    """统一的工具准备函数,避免重复初始化 MCP 客户端
    Returns:
        list: 包含所有可用工具的列表
    """
    global _all_tools
    # 如果已经初始化过,直接返回
    if _all_tools is not None:
        return _all_tools
    # 创建 MCP 客户端以获取搜索工具
    try:
        client = MultiServerMCPClient(
            {
                "tavily-mcp": {
                    "command": "npx",
                    "args": ["-y", "tavily-mcp"],
                    "env": {**os.environ},
                    "transport": "stdio"
                }
            }
        )
        # 获取 MCP 工具
        mcp_tools = await client.get_tools()
        _all_tools = mcp_tools + [get_weather]
        logger.info(f"工具初始化成功,可用工具:{[tool.name for tool in _all_tools]}")
    except Exception as e:
        logger.warning(f"⚠️ MCP 工具初始化失败:{e}")
        # 如果 MCP 工具失败,只使用邮件工具
        _all_tools = [get_weather]
        logger.info(f"使用备用工具:{[tool.name for tool in _all_tools]}")
    return _all_tools
  1. 使用 useCopilotAction hook 来注册一个前端 Action,创建一个前端 Action 向用户弹出简单的 Alert 消息和天气获取
... useCopilotAction({
  name: "get_weather",
  description: "获取指定位置的天气信息。",
  available: "disabled", // 保持为 disabled,确保不被当作前端工具
  render: ({status, args, result}) => {
    return (
      <p className="text-gray-500 mt-2">
        {status !== "complete" && "Calling weather API..."}
        {status === "complete" && <WeatherCard location={args.location} result={result} themeColor="#3b82f6" />}
      </p>
    );
  },
});
useCopilotAction({
  name: "sayHello", // Action 名称,Agent 将通过此名称来调用工具
  description: "向指定用户问好", // 对该 Action 的描述(供 Agent 理解用途)
  parameters: [ // 定义参数列表
    { name: "name", type: "string", description: "要问好的对象名字" }
  ],
  render: "正在发送问候...", // (可选) 执行时在 Chat 中显示的提示文本
  handler: async ({ name }) => { // 定义具体执行逻辑的函数(异步支持)
    alert(`Hello, ${name}!`); // 这里在浏览器弹出提示框
    return('问候已发送给' + name); // 返回结果给 agent
  }
});
...
  1. 并将前端 Action 作为工具给 Agent 使用
def should_continue(state: AgentState):
    last_message = state["messages"][-1]
    if not hasattr(last_message, 'tool_calls') or not last_message.tool_calls:
        return END
    # 检查工具调用是前端还是后端
    tool_call_name = last_message.tool_calls[0].get("name")
    frontend_actions = state["copilotkit"]["actions"]
    is_frontend_action = any(
        action.get("name") == tool_call_name for action in frontend_actions
    )
    # 如果是前端动作,则结束,让 copilotkit 前端处理
    if is_frontend_action:
        return END
    else:
        # 否则,转到后端工具节点
        return "tool_node"
5.1.4 状态共享
  1. 定义 Agent 的 State
class AgentState(CopilotKitState):
    search_history: list[dict] = []
  1. 增加搜索记录
... # 更新状态信息
updated_state = {"messages": response}
# 如果是搜索工具,更新搜索历史 - 搜索开始阶段
if response.tool_calls[0].get("name") in ["tavily-search", "tavily-extract", "tavily-crawl"]:
    search_history = state.get("search_history", [])
    search_query = response.tool_calls[0].get("args", {})
    # 创建搜索历史记录 - 开始时标记为未完成
    search_record = {
        "query": search_query.get("query", ""),
        "completed": False,
        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
        "tool_name": response.tool_calls[0].get("name")
    }
    logger.info(f"🔍 添加搜索查询到历史 (开始): {search_record}")
    search_history.append(search_record)
    updated_state["search_history"] = search_history
return updated_state
...
  1. 前端使用 CopilotKit 提供的 useCoAgent 这个 hook 函数,useCoAgentStateRender 获取实时状态,
... const {state} = useCoAgent<AgentState>({
  name: "sample_agent",
  initialState: { search_history: [] },
});
...
useCoAgentStateRender<AgentState>({
  name: "sample_agent",
  render: ({ status, state, nodeName }) => {
    return (
      <div>
        {state.search_history?.map((search, index) => (
          <div key={index}>
            {search.completed ? "✅" : "❌"} 正在执行:{search.query}
            {search.completed ? "" : "..."}
          </div>
        ))}
      </div>
    );
  },
});
...
const [localHistory, setLocalHistory] = useState<AgentState['search_history']>([]);
useEffect(() => {
  if (state.search_history && state.search_history.length > 0 ) {
    const latestSearch = state.search_history[0];
    if(latestSearch.query.length <=0){ return; }
    setLocalHistory(prevHistory => {
      if (prevHistory.length > 0 && prevHistory[prevHistory.length - 1].query === latestSearch.query) {
        const newHistory = [...prevHistory];
        newHistory[prevHistory.length - 1] = latestSearch;
        return newHistory;
      } else {
        return [...prevHistory, latestSearch];
      }
    });
  }
}, [state.search_history]);
5.1.5 HITL(Human-in-the-loop)人工审核(人机协作)流程
  1. Agent 增加中断环节
approval_request = {
    "type": "tool_approval_request",
    "tool_name": tool_call.get("name"),
    "tool_args": tool_call.get("args", {}),
    "tool_id": tool_call.get("id"),
    "timestamp": "2025-07-08"
}
# 拒绝
approve_status = interrupt(approval_request)
if approve_status in ["rejected", "reject"]:
    ....
# 如果审核通过,执行工具调用
elif approve_status in ["approved", "approve"]:
    ....
  1. 前端增加中断反馈 hook
useLangGraphInterrupt({
  render: ({ event, resolve }) => {
    const { tool_name, tool_args } = event.value;
    return (
      <div className="bg-gradient-to-br from-blue-50 to-indigo-50 border border-blue-200 rounded-2xl p-6 my-4 shadow-lg">
        {/* 标题 */}
        <div className="flex items-center gap-3 mb-4">
          <div className="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center">
            <span className="text-lg">🔧</span>
          </div>
          <div>
            <h3 className="text-lg font-bold text-gray-800">工具调用审核</h3>
            <p className="text-sm text-gray-600">请确认是否执行以下工具调用</p>
          </div>
        </div>
        {/* 工具信息 */}
        <div className="bg-white rounded-xl p-4 mb-4 border border-gray-100">
          <div className="grid grid-cols-1 gap-3">
            <div>
              <label className="block text-xs font-medium text-gray-500 mb-1">工具名称</label>
              <div className="bg-gray-50 px-3 py-2 rounded-lg">
                <code className="text-blue-600 font-mono text-sm">{tool_name}</code>
              </div>
            </div>
            <div>
              <label className="block text-xs font-medium text-gray-500 mb-1">参数</label>
              <div className="bg-gray-50 px-3 py-2 rounded-lg max-h-24 overflow-y-auto">
                <pre className="text-xs text-gray-700 whitespace-pre-wrap font-mono">
                  {JSON.stringify(tool_args, null, 2)}
                </pre>
              </div>
            </div>
          </div>
        </div>
        {/* 操作按钮 */}
        <div className="mt-4">
          <div className="flex gap-2">
            <button type="button" onClick={() => resolve("approve")} className="flex-1 bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200 flex items-center justify-center gap-2 text-sm">
              <span>✅</span> 通过
            </button>
            <button type="button" onClick={() => resolve("reject")} className="flex-1 bg-red-500 hover:bg-red-600 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200 flex items-center justify-center gap-2 text-sm">
              <span>❌</span> 拒绝
            </button>
          </div>
        </div>
      </div>
    );
  }
});

5.2 nodejs 示例

import express, {Request, Response} from 'express';
import dotenv from 'dotenv';
dotenv.config();
import {RunAgentInputSchema, RunAgentInput, EventType, Message} from '@ag-ui/core';
import {EventEncoder} from '@ag-ui/encoder';
import {v4 as uuidv4} from 'uuid';
import OpenAI from 'openai';

const app = express();
app.use(express.json());

app.post('/awp', async (req: Request, res: Response) => {
  console.log('app.post > req:');
  try {
    // 解析请求体
    const input: RunAgentInput = RunAgentInputSchema.parse(req.body);
    // 设置 SSE headers
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');
    const encoder = new EventEncoder();
    // 发送 started 事件
    const runStarted = {
      type: EventType.RUN_STARTED,
      threadId: input.threadId,
      runId: input.runId
    };
    res.write(encoder.encode(runStarted));
    // 初始化 OpenAI 客户端
    const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
    // 将 前端传入的 AG-UI 消息转换为 OpenAI 消息格式
    const requestMessages = input.messages
      .filter((msg: Message) => ['user', 'system', 'assistant'].includes(msg.role))
      .map((msg: Message) => ({
        role: msg.role as 'user' | 'system' | 'assistant',
        content: msg.content || ''
      }));
    // 生成消息 ID
    const messageId = uuidv4();
    // 发送'文本消息开始'事件
    const textMessageStart = {
      type: EventType.TEXT_MESSAGE_START,
      messageId,
      role: 'assistant'
    };
    res.write(encoder.encode(textMessageStart));
    // 创建流式传输完成请求
    const stream = await client.chat.completions.create({
      model: 'gpt-4o',
      messages: requestMessages,
      stream: true
    });
    console.log('🚀 > app.post > stream:', stream);
    // 处理流并发送'文本消息内容'事件
    for await (const chunk of stream) {
      if (chunk.choices[0]?.delta?.content) {
        const content = chunk.choices[0].delta.content;
        const textMessageContent = {
          type: EventType.TEXT_MESSAGE_CONTENT,
          messageId,
          delta: content
        };
        res.write(encoder.encode(textMessageContent));
      }
    }
    // 发送'文本消息结束'事件
    const textMessageEnd = {
      type: EventType.TEXT_MESSAGE_END,
      messageId
    };
    res.write(encoder.encode(textMessageEnd));
    console.log('🚀 > app.post > TEXT_MESSAGE_END:');
    // 发送 finished 事件
    const runFinished = {
      type: EventType.RUN_FINISHED,
      threadId: input.threadId,
      runId: input.runId
    };
    res.write(encoder.encode(runFinished));
    console.log('🚀 > app.post > RUN_FINISHED:');
    // 结束响应
    res.end();
  } catch (error) {
    res.status(422).json({error: 'Internal Server Error'});
  }
});

app.listen(3001, () => {
  console.log('Server running on http://localhost:3001');
});

六、总结

AG-UI 协议和 CopilotKit 框架代表了智能应用开发的新规范。它们不仅解决了 AI 集成的技术难题,更重要的是建立了一套标准化的开发模式,让每个开发者都能轻松构建智能应用。从前端工程师的角度,无需深究复杂的后端 AI 推理流程,只需使用熟悉的 React 组件和 Hooks,即可调动强大的 AI 为应用服务。从后端 AI 工程师的角度,你也无需操心前端展示,只要按照协议产出标准事件,UI 就会自动配合渲染。这种清晰的前后端职责分离与协同,使 AI 应用开发更加高效与标准化。

对于开发者:这意味着更高的开发效率和更低的学习成本
对于企业:这意味着更快的 AI 能力落地和更好的用户体验
对于用户:这意味着更自然的人机交互和更智能的应用体验


参考资源:

  • AG-UI 官方文档
  • Copilotkit 文档
  • AG-UI GitHub 仓库
  • CopilotKit Github 仓库
  • Discord 社区

目录

  1. AG-UI:构建 AI 前端交互的统一协议
  2. 引言
  3. 一、AG-UI 是什么?
  4. 1.1 核心定义
  5. 1.2 协议定位
  6. 二、为什么需要 AG-UI?
  7. 技术碎片化
  8. 实时性困难
  9. 人机协作缺失
  10. 三、AG-UI 核心架构
  11. 3.1 整体架构设计
  12. 3.2 核心特性
  13. 统一事件流
  14. 实时交互
  15. 工具编排
  16. 共享状态
  17. 并发与取消
  18. 安全边界
  19. 3.3 事件 Events
  20. EventType 枚举定义
  21. AG-UI 事件分类总览
  22. 事件流程示例
  23. 3.4 Agents 智能体
  24. 3.5 Messages
  25. 3.6 状态管理
  26. 3.7 工具调用 Tools
  27. 四、AG-UI 的技术优势
  28. 4.1 灵活性与兼容性
  29. 事件结构灵活性
  30. 4.2 开发者友好性
  31. 丰富的 SDK 支持
  32. 现成的集成方案
  33. 五、演示 Demo
  34. 5.1 CopilotKit 的演示 Demo
  35. 5.1.1 后端 python + Copilotkit LangGraph SDK
  36. 5.1.2 前端 React + Copilotkit React-ui
  37. 5.1.3 工具调用(不仅可以调用后端设置的工具(比如搜索、访问数据库、MCP),还可以调用前端定义的 UI“工具”(比如更改样式))
  38. 5.1.4 状态共享
  39. 如果是搜索工具,更新搜索历史 - 搜索开始阶段
  40. 5.1.5 HITL(Human-in-the-loop)人工审核(人机协作)流程
  41. 拒绝
  42. 如果审核通过,执行工具调用
  43. 5.2 nodejs 示例
  44. 六、总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • GitHub Copilot 与 Claude Code 功能对比分析
  • 图数据结构详解:基于 Java 的雷达扫描系统实现
  • (第一篇)破冰篇:Spring AI 到底是什么?核心亮点拆解与实战指南
  • Ultrascale FPGA System Management Wizard 配置指南:温度电压监测
  • 基于 Web 的汽车销售系统设计与实现
  • CTF 夺旗赛入门教程:从零开始掌握网络安全竞技
  • 零成本实现个人项目公网访问:ngrok 前端穿透方案
  • OpenCode 开源 AI 编程助手工具介绍
  • Linux 系统学习:深入剖析 Git 原理与使用进阶
  • Floweb 超轻量级浮动浏览器功能解析
  • MCP 插件配置实战:browser-tools-mcp 示例
  • 前缀和算法原理与实战
  • OpenClaw、OpenAkita、MiniMax Agent 及 Kimi Claw 个人 AI 助手对比分析
  • Stable Diffusion v1.5 Archive 跨平台效果一致性保障与复现验证
  • CV-UNet 图像抠图 WebUI 快速部署指南
  • WSL 2 Ubuntu 22.04 安装及 D 盘迁移配置指南
  • 想成为黑客或信息安全从业者,该如何开始?
  • 2024 国内最新完整 AI 大模型清单及介绍
  • 本地部署 DeskClaw:构建人机协同的自动化工作流
  • AI 大模型中的“幻觉”现象:成因与应对

相关免费在线工具

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online