跳到主要内容CopilotKit 与 LangGraph 集成:构建 Agent 原生应用前端交互框架 | 极客日志PythonAI大前端
CopilotKit 与 LangGraph 集成:构建 Agent 原生应用前端交互框架
CopilotKit 是用于构建 Agent 原生应用前端交互的开源框架,通过 AG-UI 协议实现与 LangGraph 后端的高效通信。对比了 useCoAgent 与 useAgent 两种 Hook 的状态管理与控制能力,以及 useRenderToolCall 与 useCoAgentStateRender 在工具调用和状态渲染上的区别。文章提供了前后端集成示例,指导开发者根据场景选择合适的 Hook 组合,实现多 Agent 协调、线程持久化及自定义 UI 渲染,提升 Agent 应用的交互体验。
BackendPro3 浏览 引言
随着大语言模型(LLM)技术的快速发展,AI Agent 应用正在从简单的聊天机器人演进为具备复杂推理、规划和工具调用能力的智能系统。LangGraph 作为 LangChain 生态中构建有状态、多步骤 Agent 工作流的核心框架,已被广泛应用于生产环境。然而,如何将这些后端 Agent 与前端用户界面进行高效、实时的交互,一直是开发者面临的技术挑战。
CopilotKit 正是为解决这一问题而生的开源框架。它通过 AG-UI(Agent-User Interaction Protocol)协议,为 LangGraph Agent 提供了标准化的前端集成方案,使开发者能够构建真正的 Agent 原生应用(Agent-Native Applications)。
本文将深入分析 CopilotKit 与 LangGraph 集成的核心机制,重点对比 useAgent 与 useCoAgent、useRenderToolCall 与 useCoAgentStateRender 这两组关键 Hook 的设计理念与应用场景。
一、架构概述
1.1 AG-UI 协议
AG-UI(Agent-User Interaction Protocol)是 CopilotKit 开发的开源、轻量级、基于事件的协议,用于标准化 AI Agent 与前端应用之间的交互。该协议定义了一套通用的事件流机制,涵盖消息传递、工具调用、状态同步等核心功能,使开发者无需编写定制化的集成代码即可实现 Agent 与 UI 的实时通信。
1.2 整体架构
CopilotKit 与 LangGraph 的集成架构分为三层:
┌─────────────────────────────────────────────────────────┐
│ Frontend (React) │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ │
│ │ CopilotKit │ │ useAgent │ │ useRenderTool │ │
│ │ Provider │ │ useCoAgent │ │ Call │ │
│ └─────────────┘ └──────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────┘
│ AG-UI Protocol │
┌─────────────────────────────────────────────────────────┐
│ CopilotKit Runtime │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LangGraphHttpAgent Adapter │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│ HTTP/SSE Stream │
┌─────────────────────────────────────────────────────────┐
│ LangGraph Backend │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ │
│ │ StateGraph │ │ Tools │ │ Checkpointer │ │
│ └─────────────┘ └──────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────┘
二、useAgent 与 useCoAgent 对比分析
2.1 useCoAgent:经典的状态共享 Hook
useCoAgent 是 CopilotKit 早期版本提供的核心 Hook,其设计目标是实现前端应用与 Agent 之间的双向状态共享。
核心特性
const { state, setState, running, stop } = useCoAgent<AgentState>({
name: "research_agent",
});
- 双向状态同步:前端可读取 Agent 状态,也可主动修改状态并同步至后端
- 运行状态监控:通过
running 属性实时获取 Agent 执行状态
- 执行控制:提供
stop() 方法用于中断 Agent 执行
适用场景
useCoAgent 适用于需要在应用任意位置访问和修改 Agent 状态的场景,例如:
- 构建状态面板展示 Agent 当前工作进度
- 实现用户对 Agent 状态的手动干预
- 在多个组件间共享 Agent 上下文
2.2 useAgent:v2 版本的增强型 Hook
useAgent 是 CopilotKit v1.50 版本引入的新一代 Hook,作为 useCoAgent 的超集,提供了更完整的 Agent 控制能力。
核心特性
import { useAgent } from "@copilotkit/react-core/v2";
const { agent } = useAgent({ agentId: "my-agent" });
agent.state;
agent.setState;
agent.messages;
agent.setMessages;
agent.sendMessage();
agent.runAgent();
新增能力
| 能力 | 描述 |
|---|
| 时间旅行(Time Travel) | 支持直接设置或覆盖消息历史,便于会话恢复、状态清理和交互重放 |
| 多 Agent 协调 | 支持在同一 UI 中并行运行多个 Agent,实现多 Agent 编排 |
| Agent 间感知 | Agent 可读取或采用其他 Agent 的消息,实现跨 Agent 状态共享 |
| 线程持久化 | 原生支持会话线程的存储、恢复和自动重连 |
多 Agent 协调示例
const { agent: langgraph } = useAgent({ agentId: "langgraph" });
const { agent: pydantic } = useAgent({ agentId: "pydantic" });
[langgraph, pydantic].forEach((agent) => {
agent.addMessage({
id: crypto.randomUUID(),
role: "user",
content: message,
});
agent.runAgent();
});
langgraph.setMessages(pydantic.messages);
2.3 对比总结
| 维度 | useCoAgent | useAgent |
|---|
| 版本 | v1.x 经典版本 | v1.50+ v2 版本 |
| 状态共享 | ✅ 支持 | ✅ 支持 |
| 运行控制 | ✅ 基础控制 | ✅ 增强控制 |
| 消息历史管理 | ❌ 不支持 | ✅ 支持 |
| 多 Agent 协调 | ❌ 不支持 | ✅ 支持 |
| 线程持久化 | ❌ 不支持 | ✅ 原生支持 |
| 导入路径 | @copilotkit/react-core | @copilotkit/react-core/v2 |
选型建议:对于新项目,推荐使用 useAgent 以获得更完整的功能支持;对于已有项目,useCoAgent 仍可正常使用,两者可在同一应用中混合使用。
三、useRenderToolCall 与 useCoAgentStateRender 对比分析
这两个 Hook 均用于在聊天界面中渲染自定义 UI 组件,但其触发机制和应用场景存在本质区别。
3.1 useRenderToolCall:工具调用渲染
useRenderToolCall 是一个纯渲染 Hook,用于在 Agent 调用工具时展示自定义 UI,而不执行任何业务逻辑。
核心特性
useRenderToolCall({
name: "internet_search",
render: ({ args, status, result }) => (
<div className="tool-card">
<div className="tool-header"> 🔍 {status === "complete" ? "搜索完成" : "搜索中..."}</div>
<div className="tool-query">查询:{args?.query}</div>
{status === "executing" && <Spinner />}
</div>
),
});
渲染参数
| 参数 | 类型 | 描述 |
|---|
args | object | 工具调用参数 |
status | `"executing" | "complete"` |
result | any | 工具执行结果(仅在 complete 状态可用) |
适用场景
- 展示搜索工具的查询进度和结果预览
- 渲染任务规划工具创建的待办列表
- 显示文件操作工具的执行状态
- 任何需要可视化工具调用过程的场景
3.2 useCoAgentStateRender:状态变更渲染
useCoAgentStateRender 用于基于 Agent 状态变更在聊天界面中渲染 UI 组件,其触发时机与工具调用无关,而是与 Agent 状态的更新周期绑定。
核心特性
useCoAgentStateRender<AgentState>({
name: "research_agent",
render: ({ state, status }) => {
if (status === "inProgress" && !state?.findings?.length) {
return (
<div className="findings-card loading">
<Spinner /> 正在研究中...
</div>
);
}
if (!state?.findings?.length) return null;
return (
<div className="findings-card">
<h3>🔍 研究发现:{state.research_topic}</h3>
<ul>{state.findings.map((finding, i) => (<li key={i}>{finding}</li>))}</ul>
{state.summary && (<p className="summary">{state.summary}</p>)}
</div>
);
},
});
渲染参数
| 参数 | 类型 | 描述 |
|---|
state | T | 当前 Agent 状态(泛型类型) |
status | `"inProgress" | "complete"` |
状态发射机制
在后端 LangGraph Agent 中,需要使用 copilotkit_emit_state 函数手动发射状态更新:
from copilotkit.langchain import copilotkit_emit_state
async def research_node(state: AgentState) -> dict:
findings = await perform_research(state["topic"])
await copilotkit_emit_state({"findings": findings, "research_topic": state["topic"]})
return {"findings": findings}
适用场景
- 展示 Agent 的整体工作进度
- 渲染累积的研究发现或分析结果
- 实现人机协作(Human-in-the-Loop)工作流的状态展示
- 任何需要基于 Agent 状态变更触发 UI 更新的场景
3.3 对比总结
| 维度 | useRenderToolCall | useCoAgentStateRender |
|---|
| 触发时机 | 工具调用时 | 状态变更时 |
| 渲染粒度 | 单次工具调用 | Agent 整体状态 |
| 数据来源 | 工具参数和结果 | Agent State |
| 后端配合 | 无需额外配置 | 需调用 copilotkit_emit_state |
| 典型用途 | 工具执行可视化 | 进度展示、结果汇总 |
- 若需要展示单个工具的调用过程和结果,使用
useRenderToolCall
- 若需要基于 Agent 整体状态变更渲染 UI,使用
useCoAgentStateRender
- 两者可在同一应用中组合使用,实现完整的 Agent 交互体验
四、实践示例
以下代码展示了如何在一个研究助手应用中综合运用上述 Hook:
4.1 前端实现
"use client";
import { CopilotKit, useCoAgent, useCoAgentStateRender, useRenderToolCall } from "@copilotkit/react-core";
import { CopilotChat } from "@copilotkit/react-ui";
interface AgentState {
messages: unknown[];
todos?: Array<{ task: string; done: boolean }>;
research_findings?: string[];
}
function ToolCallRenderer() {
useRenderToolCall({
name: "write_todos",
render: ({ args, status }) => {
const tasks = args?.tasks as string[] | undefined;
return (
<div className="tool-card">
<div className="tool-header"> 📋 {status === "complete" ? "计划已创建" : "正在创建计划..."}</div>
{tasks && (
<ul>{tasks.map((task, i) => <li key={i}>{task}</li>)}</ul>
)}
</div>
);
},
});
useRenderToolCall({
name: "internet_search",
render: ({ args, status }) => (
<div className="tool-card">
<div className="tool-header"> 🔍 {status === "complete" ? "搜索完成" : "搜索中..."}</div>
<div>查询:{args?.query}</div>
</div>
),
});
return null;
}
function AgentStatusPanel({ agentName }: { agentName: string }) {
const { state, running, stop } = useCoAgent<AgentState>({ name: agentName });
return (
<div className="status-panel">
<div className="status-header">
<span className={`status-dot ${running ? "running" : "idle"}`} />
<span>状态:{running ? "运行中..." : "空闲"}</span>
{running && <button onClick={() => stop()}>⏹ 停止</button>}
</div>
{state?.todos && state.todos.length > 0 && (
<div className="todos-preview">
<div>📋 任务计划:</div>
{state.todos.map((todo, i) => (
<div key={i} className={todo.done ? "done" : ""}>
{todo.done ? "✅" : "⏳"}{todo.task}
</div>
))}
</div>
)}
</div>
);
}
export default function App() {
return (
<CopilotKit runtimeUrl="/api/copilotkit" agent="deep_agent">
<AgentStatusPanel agentName="deep_agent" />
<ToolCallRenderer />
<CopilotChat labels={{ title: "研究助手", initial: "您好!请告诉我您想研究的主题。", }} />
</CopilotKit>
);
}
4.2 后端实现(LangGraph)
from typing import Annotated, List
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
class AgentState(TypedDict):
messages: Annotated[list, add_messages]
todos: List[dict]
research_findings: List[str]
@tool
def write_todos(tasks: List[str]) -> str:
"""创建任务计划"""
return f"已创建 {len(tasks)} 个任务"
@tool
def internet_search(query: str) -> str:
"""搜索互联网"""
return f"关于 '{query}' 的搜索结果..."
llm = ChatOpenAI(model="gpt-4")
tools = [write_todos, internet_search]
llm_with_tools = llm.bind_tools(tools)
async def agent_node(state: AgentState) -> dict:
response = await llm_with_tools.ainvoke(state["messages"])
return {"messages": [response]}
def build_graph():
graph = StateGraph(AgentState)
graph.add_node("agent", agent_node)
graph.add_node("tools", ToolNode(tools))
graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", should_continue)
graph.add_edge("tools", "agent")
return graph.compile()
五、总结
CopilotKit 为 LangGraph Agent 提供了完整的前端集成解决方案,通过 AG-UI 协议实现了 Agent 与 UI 之间的标准化通信。本文重点分析的四个核心 Hook 各有其适用场景:
- useCoAgent:适用于需要双向状态共享的基础场景
- useAgent:适用于需要完整 Agent 控制能力的高级场景,包括多 Agent 协调和线程持久化
- useRenderToolCall:适用于工具调用过程的可视化渲染
- useCoAgentStateRender:适用于基于 Agent 状态变更的 UI 渲染
开发者应根据具体业务需求选择合适的 Hook 组合,以构建流畅、直观的 Agent 原生应用体验。
参考资料
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online