提要
在这篇文章中,你将学到如何构建一个结合了人机协作功能的原生代理研究画板应用,并通过Langraph、CopilotKit和Tavily来实现。
这里是我们将要讲的内容,在我们开始之前。
- 什么是AI?
- 使用LangGraph Studio构建并可视化LangGraph AI
- 使用CopilotKit为LangGraph AI构建UI
这里是我们即将开发的应用程序的预览。
什么是AI?更简单地说,AI智能体是能够自主执行任务、做出决策并与环境互动的软件程序。
这些系统是在这一背景下,在执行过程中可以进行研究、处理信息并确保可靠且可信的系统。
想了解更多关于AI助手的信息,可以在这里查看CopilotKit文档。
这是一张图片,点击可以查看。
CopilotKit 是一个全栈框架工具,用于帮助构建与用户互动的代理和副驾。它使你的代理能够接管你的应用程序,告诉用户它正在做什么,并生成完全定制的用户界面。
关注并浏览 CopilotKit 的 GitHub 页面(点星标)⭐️ 点击访问
先决条件为了完全理解本教程,你需要对 React 或 Next.js 有一些基本的认识。
我们还会用到以下内容。
- Python - 一种流行的编程语言,用于使用LangGraph构建AI代理的工具;请确保已在您的计算机上安装了这个版本的Python。
- LangGraph - 一个用于创建和部署AI代理的框架。它还帮助定义代理应执行的控制流程和操作。
- OpenAI API Key - 用于启用我们使用GPT模型执行各种任务;请确保您能够访问GPT-4模型,本教程需要它。
- Tavily AI - 一个搜索引擎,使AI代理能够进行研究,并在应用程序中获取实时知识。
- CopilotKit - 一个开源的AI副驾框架,用于构建自定义AI聊天机器人、应用程序内AI代理和文本区域。
- Docker - 一个用于开发、部署和运行容器化应用程序的平台。
接下来,你将学习如何使用Docker来构建和启动一个LangGraph代理程序,并通过LangGraph Studio可视化工作流程。
让我们直接开始吧。
首先,先从克隆这个仓库开始,获取一个基于Python的Langraph代理的代码:https://github.com/CopilotKit/open-research-ANA,该仓库包含一个Python基础的Langraph代理的代码。
克隆GitHub上的CopilotKit/open-research-ANA仓库
git clone https://github.com/CopilotKit/open-research-ANA.git
全屏开启 全屏关闭
该项目包含两个文件夹:代理和前端。要运行代理程序,请进入代理文件夹。
cd agent # 切换到agent目录
全屏显示 退出全屏
使用pip来安装所有相关依赖。
pip install -r requirements.txt
这行命令用于安装requirements.txt文件中列出的所有依赖包。
进入全屏/退出全屏
接下来,在代理所在的目录中创建一个 .env
文件。然后将 OpenAI、Tavily 和 LangSmith 的 API 密钥添加到环境配置变量中。
OPENAI_API_KEY=your_key
TAVILY_API_KEY=your_key
LANGSMITH_API_KEY=your_key
进入全屏 退出全屏
当你打开 agent/graph.py
文件时,它定义了一个 MasterAgent 类,进行一个研究流程。
它使用有向图(StateGraph)来管理状态和LangGraph AI节点、工具操作和来自人的反馈之间的关系。
该工作流旨在帮助生成研究报告,通过收集数据、提出大纲和撰写各部分,同时允许通过CopilotKit前端集成提供人类反馈。
要启动Langraph AI agent,请打开Docker应用并运行下面的命令来启动这个代理。
langgraph up
按一下进入全屏模式,再按一下退出全屏模式
当 LangGraph API 服务器启动后,通过提供的 LangGraph Studio 链接进入该界面。请从输出中记下 API URL(例如,http://localhost:8123)。我们将通过 CopilotKit Cloud 使用它来连接代理与前端。
之后,LangGraph将在LangGraph工作室中启动,你可以像下面这样对其进行可视化。
要测试LangGraph代理程序,可以在messages
中添加一条消息并点击提交。
代理将按照定义的工作流程通过连接的节点处理输入,并在消息中回复您,如下所示。
这是一张图片,你可以点击它查看详细内容。
在我们继续之前,让我们先谈一下代理助手中的一个关键概念,也称为人在回路(HITL)。HITL 让代理可以在执行过程中请求人类的输入或批准,从而让 AI 系统更加可靠且值得信赖。
你可以在这里了解更多关于人在环中的内容,点击这里访问 CopilotKit 文档。
在这种情形下,你可以通过点击其中一个节点并选中 Interrupt After
选项来给代理添加HITL,如下所示。
然后添加另一条消息,比如“关于AI模型的研究”,到状态变量messages
中,并点击提交按钮以。代理将开始研究AI模型,并在完成后,会要求你审阅各个部分的内容,并对你的反馈或具体修改提出建议,如下所示。
如图所示,
在 messages
状态变量中添加一个“是”的消息,然后点击提交按钮。代理会处理这个消息,并为您提供关于AI模型报告的大纲提案。然后,它会问您是否想要批准这个大纲,或者是否有需要修改的地方,如下面所示。
【图片说明】这是一张示例图片。
回复说“我想批准这个提纲”,并点击提交按钮。接着,代理人会编一份将AI模型分组的报告,并结束研究,如下。
现在我们已经学会了如何通过LangGraph Studio来可视化和测试LangGraph AI代理程序,让我们来看看如何为它添加一个前端UI,以实现与它的互动。
使用CopilotKit为LangGraph代理构建UI:在这一部分,你将学会如何通过CopilotKit云端将你的LangGraph AI代理连接到CopilotKit前端UI中。
咱们开始吧。
第一步:为 LangGraph AI 代理创建一个通道
要创建通往LangGraph AI代理的隧道,以便Copilot Cloud可以连接到它。记得我之前提到的API URL;使用提供的端口号。在我的情况下,我的端口号是8123。
运行这个命令来启动开发环境:在终端中输入 `npx copilotkit@latest dev --port 8123` 并指定端口号8123来启动开发工具。
全屏模式…退出全屏
选择一个项目,如图所示,隧道应该是活跃的并连接到 Copilot Cloud。
如图所示!
然后进入前端(或直接说“前端文件夹”,如果上下文需要的话)。
cd frontend
在前端目录下切换到当前工作目录,使用上述命令。
全屏模式,退出全屏模式
接下来,使用 pnpm 安装前端依赖。
运行此命令以安装pnpm所需的依赖包: `pnpm install`
全屏 退出全屏
接下来,在前端目录中创建一个名为 .env
的文件,然后将 OpenAI、Copilot Cloud 和 LangSmith 的 API 密钥添加到环境变量。
OPENAI_API_KEY=你的 OpenAI API 密钥
LANGSMITH_API_KEY=你的 LangSmith API 密钥
NEXT_PUBLIC_COPILOT_CLOUD_API_KEY=你的 Copilot Cloud API 密钥
全屏模式,退出全屏
使用下面的命令运行应用。
运行开发命令:pnpm run dev
全屏模式 退出全屏
导航到 http://localhost:3000/,你应该能看到 LangGraph AI 代理程序的前端。
(点击图片查看)
我们现在来看看如何用CopilotKit给LangGraph AI做UI设计。
第 2 步:设置 CopilotKit 提供器
要配置 CopilotKit 提供者,必须用 <CopilotKit>
组件包裹你应用中所有需要 Copilot 的部分。在大多数情况下,将整个应用包裹在 CopilotKit 提供者里是合适的,例如在你的 layout.tsx
文件中,如你在 frontend/src/app/layout.tsx
文件中看到的。
import { CopilotKit } from "@copilotkit/react-core";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" className="h-full">
{/* CopilotKit 组件用于集成一个 AI 代理 */}
<body className={`${lato.variable} ${noto.className} antialiased h-full`}>
<CopilotKit
{/* cripcion 从环境变量中传递 Copilot Cloud API 密钥 */}
publicApiKey={process.env.NEXT_PUBLIC_COPILOT_CLOUD_API_KEY}
{/* 禁用开发控制台(调试时设为 true) */}
showDevConsole={false}
{/* 指定 LangGraph 代理名称 */}
agent="agent"
>
<TooltipProvider>
<ResearchProvider>
{children}
</ResearchProvider>
</TooltipProvider>
</CopilotKit>
</body>
</html>
);
}
进入全屏 退出全屏
第三步:选择一个Copilot UI
要设置你的 Copilot UI 界面,请首先在你的根组件(通常是 layout.tsx
文件)中导入默认样式文件。
import "@copilotkit/react-ui/styles.css";
切换到全屏模式。退出全屏。
Copilot UI 自带一系列内置的 UI 模板;你可以从这些选项中选择你喜欢的一个,从 CopilotPopup、CopilotSidebar、CopilotChat 到 Headless UI 等。
在这种情形下,我们将用到定义于 src/components/chat.tsx
文件中的 CopilotChat。
// 表示此组件在客户端运行(Next.js 指令)
'use client'
// 从 CopilotKit React UI 库导入 CopilotChat 组件
import { CopilotChat } from "@copilotkit/react-ui";
// 从本地库文件导入聊天配置的常量
import { INITIAL_MESSAGE, MAIN_CHAT_INSTRUCTIONS, MAIN_CHAT_TITLE } from "@/lib/consts";
// @ts-expect-error -- 忽略此错误:抑制缺少/不正确的类型定义的 TypeScript 错误
import { CopilotChatProps } from "@copilotkit/react-ui/dist/components/chat/Chat";
// 定义 Chat 组件,接受类型为 CopilotChatProps 的 props 参数
export default function Chat(props: CopilotChatProps) {
return (
// 渲染带有自定义配置的 CopilotChat 组件实例
<CopilotChat
instructions={MAIN_CHAT_INSTRUCTIONS} // 传递预定义的聊天行为指令参数
labels={{
title: MAIN_CHAT_TITLE, // 设置聊天窗口标题为常量
initial: INITIAL_MESSAGE, // 设置聊天初始消息为常量
}}
className="h-full w-full font-noto" // 应用自定义 CSS 类,以实现全高宽和 Noto 字体
{...props} // 展开传入的其他 props 参数(例如,覆盖或自定义)
/>
);
}
点击全屏进入全屏模式,点击退出退出全屏
聊天组件被导入到了 src/app/page.tsx
文件,并在那里使用。然后,这个聊天在前端界面展示了,如下所示。
第 4 步:在您的 UI 和 LangGraph AI 代理之间建立共享状态
CoAgents 维护一个共享状态系统,使您的UI和代理程序执行无缝衔接。这个共享状态系统可以帮您做到以下几点:
- 显示代理当前的进度和阶段性成果
- 通过UI交互更新代理的状态信息
- 实时响应应用程序中状态的变化,实现即时反馈
更多关于CoAgents共享状态的信息,你可以在这里找到。
图片说明
为了在你的UI和LangGraph AI代理程序之间创建一个共享状态信息,首先你需要在前端定义并发出代理的状态,如agent/graph.py
文件所示。
# 异步方法处理工具执行并更新研究状态
async def tool_node(self, state: ResearchState, config: RunnableConfig):
# 自定义配置以在工具执行期间禁用向前端发送消息
config = copilotkit_customize_config(config, emit_messages=False)
msgs = [] # 用于存储工具消息的列表
tool_state = {} # 用于存储工具执行后更新的状态的字典
# 处理最后一个消息中的每个工具调用(假设最后一个消息是AIMessage)
for tool_call in state["messages"][-1].tool_calls:
tool = self.tools_by_name[tool_call["name"]] # 通过名称查找工具
# 简化消息结构以便临时访问工具
state['messages'] = {'HumanMessage' if isinstance(message, HumanMessage) else 'AIMessage': message.content for message in state['messages']}
tool_call["args"]["state"] = state # 将状态注入到工具参数中
# 异步执行工具并获取更新后的状态和消息
new_state, tool_msg = await tool.ainvoke(tool_call["args"])
tool_call["args"]["state"] = None # 执行后清除参数中的状态信息
# 将工具结果以ToolMessage的形式追加
msgs.append(ToolMessage(content=tool_msg, name=tool_call["name"], tool_call_id=tool_call["id"]))
# 使用研究数据构建更新后的工具状态信息
tool_state = {
"title": new_state.get("title", ""),
"outline": new_state.get("outline", {}),
"sections": new_state.get("sections", []),
"sources": new_state.get("sources", {}),
"proposal": new_state.get("proposal", {}),
"logs": new_state.get("logs", []),
"tool": new_state.get("tool", {}),
"messages": msgs
}
# 向前端发送更新状态
await copilotkit_emit_state(config, tool_state)
return tool_state # 返回更新的状态
全屏;退出全屏
接着,使用 CopilotKit 的 useCoAgent 钩子 在 src/components/research-context.tsx
文件中,将 LangGraph AI 代理的状态与你的前端 UI 进行共享。useCoAgent 钩子允许你在应用和代理之间进行双向状态共享。
// 表示此组件在客户端运行(Next.js 客户端指令)
'use client'
// 导入 React 的上下文、状态和效果相关的工具
import { createContext, useContext, useState, ReactNode, useEffect } from 'react';
// 从共享库导入 ResearchState 类型
import type { ResearchState } from '@/lib/types';
// 导入 CopilotKit 的管理代理状态的 hook
import { useCoAgent } from "@copilotkit/react-core";
// 导入一个用于与本地存储交互的自定义 hook
import useLocalStorage from "@/lib/hooks/useLocalStorage";
// 定义上下文值的形状
interface ResearchContextType {
state: ResearchState; // 当前的研究状态
setResearchState: (newState: ResearchState | ((prevState: ResearchState) => ResearchState)) => void; // 更新研究状态的设置器(类型兼容转换)
sourcesModalOpen: boolean; // 用于切换来源模态框的布尔值
setSourcesModalOpen: (open: boolean) => void; // 设置模态框状态的函数
runAgent: () => void; // 触发代理执行的函数
}
// 创建一个用于共享研究状态的上下文,默认为 undefined
const ResearchContext = createContext<ResearchContextType | undefined>(undefined);
// 定义一个 ResearchProvider 组件,将子组件包裹在上下文中
export function ResearchProvider({ children }: { children: ReactNode }) {
// 状态用于控制来源模态框的可见性
const [sourcesModalOpen, setSourcesModalOpen] = useState<boolean>(false);
// 使用 CopilotKit 的 useCoAgent hook 管理代理状态和执行
const { state: coAgentState, setState: setCoAgentsState, run } = useCoAgent<ResearchState>({
name: 'agent', // 代理名称(与后端配置匹配)
initialState: {}, // 代理的初始空状态
});
// 使用自定义 hook 通过本地存储管理研究状态,初始值为 null
// @ts-expect-error -- 强制 null:压制初始值为 null 的 TypeScript 错误
const [localStorageState, setLocalStorageState] = useLocalStorage<ResearchState>('research', null);
// 效果用于同步代理状态和本地存储
useEffect(() => {
// 检查代理的状态或本地存储是否为空
const coAgentsStateEmpty = Object.keys(coAgentState).length < 1;
const localStorageStateEmpty = localStorageState == null || Object.keys(localStorageState).length < 1;
// 如果本地存储中有数据但代理的状态为空,则初始化代理状态
if (!localStorageStateEmpty && coAgentsStateEmpty) {
setCoAgentsState(localStorageState);
return;
}
// 如果代理的状态中有数据但本地存储为空,则保存至本地存储
if (!coAgentsStateEmpty && localStorageStateEmpty) {
setLocalStorageState(coAgentState);
return;
}
// 如果两者都存在但不同,则用代理的状态更新本地存储
if (!localStorageStateEmpty && !coAgentsStateEmpty && JSON.stringify(localStorageState) !== JSON.stringify(coAgentState)) {
setLocalStorageState(coAgentState);
return;
}
}, [coAgentState, localStorageState, setCoAgentsState, setLocalStorageState]); // 效果的依赖项
// 将上下文值提供给子组件
return (
<ResearchContext.Provider value={{
state: coAgentState, // 当前从 CopilotKit 获取的研究状态
setResearchState: setCoAgentsState as ResearchContextType['setResearchState'], // 更新研究状态的设置器(类型兼容转换)
setSourcesModalOpen, // 切换来源模态框的函数
sourcesModalOpen, // 当前模态框状态
runAgent: run // 运行代理的函数
}}>
{children} // 渲染嵌套在提供器内的子组件
</ResearchContext.Provider>
);
}
// 自定义的 hook 用于访问研究上下文
export function useResearch() {
// 获取上下文值
const context = useContext(ResearchContext);
// 如果在 ResearchProvider 之外使用,抛出错误
if (context === undefined) {
throw new Error('useResearch 必须在 ResearchProvider 内使用');
}
return context; // 返回上下文值
}
全屏,退出全屏
接下来,在聊天 UI 中显示代理的状态。这有助于以更直观的方式让用户了解代理的状态。为此,你可以在 src/app/page.tsx
文件里使用 useCoAgentStateRender 钩子。
// ...
import {useCoAgentStateRender} from "@copilotkit/react-core";
// ...
// 导出默认的主页函数
export default function HomePage() {
// ...
// 使用CoAgentStateRender<ResearchState>
useCoAgentStateRender<ResearchState>(
{
// 名称: "代理"
name: "agent",
// 渲染: ({状态}) => {
render: ({ state }) => {
// 如果(状态日志?.长度 > 0)
if (state.logs?.length > 0) {
// 返回 <进度 日志={状态日志} />;
return <Progress logs={state.logs} />;
}
// return null;
return null;
},
},
// [researchState]
[researchState]
);
// ...
}
点击进入全屏模式 点击退出全屏
然后导航到http://localhost:3000/,并在聊天中输入“研究AI模型”,然后按“Enter”。你应该会在聊天中看到如下图所示的LangGraph AI代理状态。
看起来是一张图片链接,但是这里没有显示具体的描述。
第 5 步:在前端 UI 添加人工干预点
为了让LangGraph代理在聊天过程中于UI中请求人类输入或批准,可以在src/app/page.tsx
文件里的review_proposal
钩中使用CopiloKit的review_proposal
钩。
// ...
import { useCopilotAction } from "@copilotkit/react-core";
// ...
export default function HomePage() {
// ...
// 使用 CopilotKit 的 useCopilotAction 钩子定义一个自定义操作
useCopilotAction({
name: "review_proposal", // 操作的唯一名称
description:
"提示用户在提案生成后立即审阅结构提案", // 操作的目的描述
available: "remote", // 表示该操作远程可用(例如,由后端或代理程序触发)
parameters: [], // 此操作不需要任何参数
// @ts-expect-error -- null 元素是合法的:抑制 TypeScript 的错误,因为返回 null 是允许的
renderAndWaitForResponse: (
{ respond, status } // 渲染 UI 并等待用户响应的函数
) =>
status !== "complete" ? ( // 检查操作是否仍在进行
<ProposalViewer // 渲染自定义的 ProposalViewer 组件
onSubmit={(
approved,
proposal // 用户提交审阅时的回调
) =>
respond?.({
// 将响应发送回 CopilotKit
...proposal, // 该对象包含结构详情
approved, // 添加批准状态(true/false)
})
}
/>
) : null, // 操作完成时返回 null,以此隐藏 UI
});
// ...
}
进入全屏 退出全屏
然后访问 http://localhost:3000/。当 LangGraph代理 完成了关于 AI 模型的研究后,它会请求你批准该提案,如下所示。
选择你想看的部分,添加一些备注信息,接着点击 批准
按钮。LangGraph AI 代理将会开始撰写关于 AI 模型的相关内容的研究报告。
这是一张图片
第六步:在UI中实时显示LangGraph AI代理的回复
请在 src/app/page.tsx
文件中使用 src/lib/hooks/useStreamingContent.ts
文件中定义的 useStreamingContent 钩子来流式传输研究报告。
import { useStreamingContent } from "@/lib/hooks/useStreamingContent";
export default function HomePage() {
// ...
const streamingSection = useStreamingContent(researchState);
// ...
return (
// ...
{/* 文档查看器 */}
<DocumentsView
sections={sections ?? []}
streamingSection={streamingSection}
selectedSection={sections?.find((s) => s.id === selectedSectionId)}
onSelectSection={setSelectedSectionId}
/>
// ...
)
}
全屏显示/退出全屏
你应该看到右边流动的研究内容,如下所示。
在这次教程里,我们学到了很多。希望你已经掌握了如何使用CopilotKit为你的应用程序构建代理助手的用户界面,还学会了如何实时更新状态并实现人机协作的理念。
在GitHub GitHub链接 上查看完整的源代码。
关注 CopilotKit 的 Twitter 账号,打声招呼。如果你想做一些酷酷的东西,可以加入 Discord 社区大家一起玩。
(/copilotkit)
React 用户界面 + 优雅的基础设施用于构建 AI 副驾、应用程序内的 AI 代理、AI 聊天机器人和 AI 驱动的文本框 🪁
共同學習,寫下你的評論
評論加載中...
作者其他優質文章