完整教程展示如何用现代 AI 框架快速构建生产级 Agent 应用,包含最佳实践和开箱即用的代码。
CopilotKit 现已支持 Angular
Angular 支持 · 使用 Angular 构建智能体应用和生成式 UI · 现已推出
AI 智能体正在逐渐接近真实世界的应用场景,但大多数开发者仍然觉得构建 AI 智能体十分复杂。
因此,我们将构建两个实用的智能体:Post Generator,利用实时 Web 搜索起草 LinkedIn/X 内容;Stack Analyzer,检查 GitHub 仓库并生成结构化报告。
我们将使用 Next.js 前端、FastAPI 后端、CopilotKit、LangGraph 工作流以及 Google Gemini。你将从中了解项目架构、核心概念、提示词以及实用的开发内容。
欢迎查看 CopilotKit GitHub 仓库并点个 ⭐️
我们将采用全栈架构构建两个实用的智能体:
✅ Post Generator Agent:基于实时 Google 搜索结果生成 LinkedIn/X 帖子。
下面是用户生成帖子时所发生过程的简化调用序列。
[User types prompt]
↓
Next.js UI (CopilotChat)
↓ (POST /api/copilotkit → GraphQL)
Next.js API route (copilotkit)
↓ (forwards)
FastAPI backend (/copilotkit)
↓ (LangGraph workflow)
Post Generator graph nodes
↓ (calls → Google Gemini + web search)
Streaming responses & tool-logs
↓
Frontend UI renders chat + tool logs + final postcards
✅ Stack Analyzer Agent:分析公开的 GitHub 仓库,包括元数据、README 和代码清单,并推断其技术栈。
下面是用户分析某个仓库技术栈时所发生过程的简化调用序列。
[User pastes GitHub URL]
↓
Next.js UI (/stack-analyzer)
↓
/api/copilotkit → FastAPI
↓
Stack Analysis graph nodes (gather_context → analyze → end)
↓
Streaming tool-logs & structured analysis cards
这就是我们接下来要构建的内容!
我们将使用以下核心技术栈构建这些智能体。
Next.js 15:使用 TypeScript 的前端框架
CopilotKit SDK:将智能体嵌入 UI(@copilotkit/react-core、@copilotkit/runtime、@copilotkit/react-ui)
FastAPI 和 Uvicorn:用于以 API 形式提供智能体服务的后端框架
LangGraph(StateGraphs):用于构建有状态的智能体工作流
通过 google-genai(官方 SDK)使用 Google Gemini:负责推理和文本生成的 LLM
LangChain 的 Google 适配器:用于将 Gemini 接入 LangChain 工作流
Pydantic:用于生成结构化的 JSON 工具输出
下面是项目的高层架构。
我们的目录结构如下。agent 目录包含托管 LangGraph 智能体的 Python/FastAPI 后端,frontend 目录则包含 Next.js 15 应用,包括 UI 路由、API 路由以及共享组件。
.
├── assets/
├── frontend/ ← Next.js 15 App (UI + API routes)
│ ├── app/
│ │ ├── layout.tsx ← Wraps the app with <CopilotKit>
│ │ ├── post-generator/ ← Post Generator UI routes
│ │ ├── stack-analyzer/ ← Stack Analyzer UI routes
│ │ └── api/ ← Next.js API routes used by the UI
│ │ ...
│ ├── contexts/LayoutContext.tsx
│ ├── wrapper.tsx ← CopilotKit provider wrapper
│ ├── components/ ← Shared UI components
│ │ ...
├── agent/ ← FastAPI + LangGraph “agents” (Python)
│ ├── main.py ← Registers agents and exposes them via FastAPI
│ ├── posts_generator_agent.py ← Workflow for content creation agent
│ ├── stack_agent.py ← Workflow for repo analysis agent
│ ├── prompts.py ← Shared prompt templates
│ ├── agent.py ← Core agent classes and helpers
│ ...
└── README.md ← Project overview and setup instructions
项目的 GitHub 仓库已经提供,并部署在 copilot-kit-deepmind.vercel.app。如果你愿意,可以自行探索。在接下来的章节中,我将介绍具体实现以及所有关键概念。
最容易跟随本文实践的方式是克隆该仓库,不过我也会讲解如何从头开始构建。
git clone https://github.com/CopilotKit/CopilotKit-Deepmind.git
cd copilotkit-deepmind
添加必要的 API Key。
分别在 agent 和 frontend 目录下创建 .env 文件,并将你的 Gemini API Key 添加到文件中。我附上了相关文档链接,方便你按照说明操作。
两个目录使用相同的命名约定。
GOOGLE_API_KEY=<<your-gemini-key-here>>
接下来创建前端。为了方便你了解整体布局,这里再次列出前端的项目结构。
frontend/
├── app/
│ ├── page.tsx ← landing redirect
│ ├── post-generator/page.tsx← Post Generator UI
│ ├── stack-analyzer/page.tsx← Stack Analyzer UI
│ ├── api/
│ │ ├── copilotkit/route.ts← CopilotKit router endpoint
│ │ └── chat/route.ts ← OpenAI research demo
│ ├── contexts/LayoutContext.tsx
│ ├── wrapper.tsx ← CopilotKit provider wrapper
│ └── prompts/prompts.ts ← UI prompt templates
├── components/… ← shared UI components (tool-logs, cards, posts…)
└── layout.tsx, globals.css, etc.
如果你还没有前端项目,可以创建一个使用 TypeScript 的 Next.js 应用,然后安装 CopilotKit 包。在克隆的仓库中,这些内容已经准备好了,因此只需在 frontend 目录下运行 pnpm i 安装依赖。
// creates a nextjs app with typescript
npx create-next-app@latest frontend
安装所需的 CopilotKit 包。
pnpm install copilotkit @copilotkit/react-core @copilotkit/react-ui @copilotkit/runtime @copilotkit/runtime-client-gql
copilotkit 是一个较底层的 SDK,其中包含适用于 Python 的后端工具。这里使用它连接状态图、发送状态更新以及与 Gemini 通信。
@copilotkit/react-core 提供核心上下文和逻辑,用于将 React 应用连接到 CopilotKit 后端和 MCP 服务器。
@copilotkit/react-ui 提供 <CopilotChat /> 等现成的 UI 组件,可用于快速构建 AI 聊天或助手界面。
@copilotkit/runtime 是服务端运行时库。它允许你声明智能体、将其连接到 LangGraph 工作流,并通过 API 端点将它们暴露出去。
@copilotkit/runtime-client-gql 是用于 GraphQL 传输的客户端。Next.js API 路由会在底层使用它,在浏览器与后端之间进行代理。
<CopilotKit> 组件必须包裹应用中能够感知 Copilot 的部分。在大多数情况下,最好让它包裹整个应用,例如将其放在 layout.tsx 中。
根布局使用 LayoutProvider 和 CopilotKit 客户端包装器包裹所有内容:
import "./globals.css"
import { LayoutProvider } from "./contexts/LayoutContext"
import Wrapper from "./wrapper"
export default function RootLayout({ children }) {
return (
<html lang="en">
<LayoutProvider>
<Wrapper>
<body>{children}</body>
</Wrapper>
</LayoutProvider>
</html>
)
}
LayoutProvider(frontend\app\contexts\LayoutContext.tsx)为布局状态设置 React 上下文,并使用 usePathname() 检测当前路径,根据当前路由(/post-generator 或其他路由)选择处于活动状态的智能体。
"use client"
import { usePathname } from "next/navigation"
import React, { createContext, useContext, useState } from "react"
interface LayoutState { … }
interface LayoutContextType {
layoutState: LayoutState
updateLayout: (updates: Partial<LayoutState>) => void
}
const LayoutContext = createContext<LayoutContextType | undefined>(undefined)
const defaultLayoutState = { agent: "post_generation_agent", … }
export function LayoutProvider({ children }) {
const pathname = usePathname()
const [layoutState, setLayoutState] = useState({
...defaultLayoutState,
agent: (pathname == "/post-generator"
? "post_generation_agent"
: "stack_analysis_agent"),
})
const updateLayout = (updates) =>
setLayoutState((prev) => ({ ...prev, ...updates }))
return (
<LayoutContext.Provider value={{ layoutState, updateLayout }}>
{children}
</LayoutContext.Provider>
)
}
export function useLayout() {
return useContext(LayoutContext)
}
...
下面是 CopilotKit 客户端包装器(frontend\app\wrapper.tsx)的代码。每个页面都会渲染在该包装器内部,这样 UI 组件就知道应该调用哪个智能体以及从哪里调用。
"use client"
import { CopilotKit } from "@copilotkit/react-core";
import { useLayout } from "./contexts/LayoutContext";
export default function Wrapper({ children }: { children: React.ReactNode }) {
const { layoutState } = useLayout()
return (
<CopilotKit runtimeUrl="/api/copilotkit" agent={layoutState.agent}>
{children}
</CopilotKit>
)
}
Next.js API 路由 app/api/copilotkit/route.ts 中提供的 CopilotKit Runtime 端点,只负责将所有智能体和图调用代理到 FastAPI 后端。
我们不会让浏览器直接调用 Python 智能体,而是在两者之间引入一个轻量级代理。
避免 CORS 和跨域问题
让 Next.js 负责身份验证、特定于环境的路由以及打包
为 React UI 提供统一的 GraphQL/REST 数据结构(不会有 Python 载荷泄漏到客户端)
在此示例中,我们只使用了一个智能体;但如果你希望运行多个 LangGraph 智能体,请参阅官方的 Multi-Agent 指南。
import { CopilotRuntime, copilotRuntimeNextJSAppRouterEndpoint, GoogleGenerativeAIAdapter } from "@copilotkit/runtime";
import { NextRequest } from "next/server";
// You can use any service adapter here for multi-agent support.
const serviceAdapter = new GoogleGenerativeAIAdapter();
const runtime = new CopilotRuntime({
remoteEndpoints: [{ url: process.env.NEXT_PUBLIC_LANGGRAPH_URL || "http://localhost:8000/copilotkit" }],
});
export const POST = async (req: NextRequest) => {
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
runtime,
serviceAdapter,
endpoint: "/api/copilotkit",
});
return handleRequest(req);
};
下面是对以上代码的简单说明:
CopilotRuntime:连接支持 Copilot 的 UI 与智能体端点的内部引擎。
GoogleGenerativeAIAdapter:该适配器将 Google Gemini 接入,作为智能体工作流的底层 LLM。
remoteEndpoints:指定智能体逻辑所在的位置,例如由后端提供服务的端点。
copilotRuntimeNextJSAppRouterEndpoint:一个辅助函数,用于封装传入的 req,并将其路由到 Copilot Runtime 进行智能体处理。它会返回一个 handleRequest 方法。
最后一件事是:每当有人访问主页 / 路由时,在 frontend\app\page.tsx 中将其重定向到 /post-generator 路由。
"use client"
import "@copilotkit/react-ui/styles.css";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { useLayout } from "./contexts/LayoutContext";
export default function GoogleDeepMindChatUI() {
const router = useRouter();
const { updateLayout } = useLayout();
useEffect(() => {
updateLayout({ agent: "post_generation_agent" });
router.push("/post-generator");
}, [router]);
return (
<></>
)
}
接下来使用 CopilotChat UI(<CopilotChat>)、建议以及用于渲染最终帖子的自定义操作,为帖子生成器创建前端(frontend/app/post-generator/page.tsx)。
实际代码库还包含智能体切换、快捷操作和实时工具日志等额外 UI 功能。为了表达清晰,我在此处对它们进行了精简,你可以查看代码了解完整 UI。
import { CopilotChat, useCopilotChatSuggestions } from "@copilotkit/react-ui"
import { initialPrompt, suggestionPrompt } from "../prompts/prompts"
useCopilotChatSuggestions({
available: "enabled",
instructions: suggestionPrompt,
})
return (
<div className="…" >
{/* …sidebar & header omitted… */}
{/* Chat canvas */}
<CopilotChat
className="h-full p-2"
labels={{ initial: initialPrompt }}
/>
{/* Post previews (rendered after generation) */}
<div className="flex gap-6 mt-6">
<LinkedInPostPreview title="Generated Title" content="Generated LinkedIn content…" />
<XPostPreview title="Generated Title" content="Generated X content…" />
</div>
</div>
)
系统提示词和建议提示词来自 app/prompts/prompts.ts。
export const initialPrompt = "Hi! I am a Langgraph x Gemini-powered AI agent capable of performing web search and generating LinkedIn and X (Twitter) posts.\n\n Click on the suggestions to get started."
export const suggestionPrompt = "Generate suggestions that revolve around the creation/generation of LinkedIn and X (Twitter) posts on any specific topics."
在完整的 UI 代码中,我们还使用 useCopilotAction 定义了一个 generate_post 操作。它可以让智能体返回结构化的 LinkedIn/X 帖子,随后将其渲染为预览。为简单起见,下面是精简后的代码。
import { useCopilotAction } from "@copilotkit/react-core"
import { XPostCompact, LinkedInPostCompact } from "@/components/ui/posts"
useCopilotAction({
name: "generate_post",
description: "Render a LinkedIn and X post",
parameters: {
tweet: { title: "string", content: "string" },
linkedIn: { title: "string", content: "string" }
},
render: ({ args }) => (
<>
{args.tweet?.content && (
<XPostCompact title={args.tweet.title} content={args.tweet.content} />
)}
{args.linkedIn?.content && (
<LinkedInPostCompact title={args.linkedIn.title} content={args.linkedIn.content} />
)}
</>
)
})
为了方便调试,我们还使用 useCoAgentStateRender 渲染 tool_logs,它会在智能体工作期间显示实时的工具调用。
import { useCoAgentStateRender } from "@copilotkit/react-core"
import { ToolLogs } from "@/components/ui/tool-logs"
useCoAgentStateRender({
name: "post_generation_agent",
render: (state) => (
<ToolLogs logs={state?.state?.tool_logs || []} />
)
})
下面是代码的最终输出效果。
这里不会介绍 Badge、textarea、x-post、linkedin-post 和 button 等基础组件的代码。你可以在代码仓库的 frontend/components/ui 中查看所有组件。
技术栈分析页面(frontend/app/stack-analyzer/page.tsx)接入 stack_analysis_agent 并渲染一组卡片。与之前一样,我精简了智能体切换、快捷操作和实时工具日志等额外 UI 功能。你可以查看代码了解完整 UI。
它与我们之前所做的完全相同,因此这里跳过代码说明。
import { CopilotChat, useCopilotChatSuggestions } from "@copilotkit/react-ui"
import { initialPrompt1, suggestionPrompt1 } from "../prompts/prompts"
import { StackAnalysisCards } from "@/components/ui/stack-analysis-cards"
import { ToolLogs } from "@/components/ui/tool-logs"
useCoAgentStateRender({
name: "stack_analysis_agent",
render: (state) => <ToolLogs logs={state?.state?.tool_logs || []} />,
})
useCopilotChatSuggestions({
available: "enabled",
instructions: suggestionPrompt1,
})
return (
<div className="…" >
{/* …sidebar omitted… */}
<CopilotChat
className="h-full p-2"
labels={{ initial: initialPrompt1 }}
/>
{state.show_cards && <StackAnalysisCards analysis={state.analysis} />}
</div>
)
系统提示词和建议提示词来自 app/prompts/prompts.ts。
export const initialPrompt1 = 'Hi! I am a Langgraph x Gemini-powered AI agent capable of performing analysis of Public GitHub Repositories.\n\n Click on the suggestions to get started.'
export const suggestionPrompt1 = `Generate suggestions that revolve around the analysis of Public GitHub Repositories. Only provide suggestions from these public URLs:
[
"https://github.com/freeCodeCamp/freeCodeCamp",
"https://github.com/EbookFoundation/free-programming-books",
"https://github.com/jwasham/coding-interview-university",
"https://github.com/kamranahmedse/developer-roadmap",
"https://github.com/public-apis/public-apis",
"https://github.com/donnemartin/system-design-primer",
"https://github.com/facebook/react",
"https://github.com/tensorflow/tensorflow",
"https://github.com/trekhleb/javascript-algorithms",
"https://github.com/twbs/bootstrap",
"https://github.com/vinta/awesome-python",
"https://github.com/ohmyzsh/ohmyzsh",
"https://github.com/tldr-pages/tldr",
"https://github.com/ytdl-org/youtube-dl",
"https://github.com/taigaio/taiga-back"
]`
下面是代码的最终输出效果。
这里不会介绍 Badge、textarea、stack-analysis-cards 和 button 等基础组件的代码。你可以在代码仓库的 frontend/components/ui 中查看所有组件。
/agent 目录下是一个 FastAPI 服务器,它对外提供两个基于 LangGraph 的智能体。下面再次列出后端的项目结构,方便你理解整个布局。
agent/
├── main.py ← FastAPI + CopilotKitSDK wiring
├── posts_generator_agent.py ← “Post Generator” graph & nodes
├── stack_agent.py ← “Stack Analysis” graph & nodes
├── prompts.py ← system prompts
├── pyproject.toml
└── agent.py ← Core agent classes and helpers
后端使用 Poetry,而不是 requirements.txt。如果你的系统尚未安装 Poetry,请先安装。
pip install poetry
然后进入 agent 目录,使用以下命令初始化一个新的 Poetry 项目。
cd agent
poetry init # creates a pyproject.toml here (answer prompts or skip with --no-interaction)
这将生成全新的 pyproject.toml 和 poetry.lock,这意味着你的后端现在拥有了独立的虚拟环境。
目前,大多数 AI 生态系统组件(LangChain、LangGraph、Google SDK)最高仅支持 Python 3.12,因此请使用以下命令让 Poetry 采用兼容的 Python 版本:poetry env use python3.12。
然后安装依赖项。
fastapi:用于提供 agent 端点(/copilotkit)的 Web 框架。
uvicorn:运行 FastAPI 的 ASGI 服务器,用于生产或开发模式。
copilotkit:CopilotKit Python SDK,用于将 LangGraph 工作流与 CopilotKit 状态流集成。
langgraph:用于将 agent 定义为节点图(chat、analyze、end)的状态机框架。
langchain:提供核心抽象(RunnableConfig、消息类型等)供节点内部使用。
langchain-google-genai:Google Gemini 模型的 LangChain 包装器(如 ChatGoogleGenerativeAI)。
google-genai:Gemini 的官方 Google 客户端 SDK,用于低级调用(如 genai.Client)。
pydantic:模式验证(StructuredStackAnalysis),强制严格的 JSON 输出。
python-dotenv:加载 .env 文件来管理 API 密钥(如 GOOGLE_API_KEY)。
现在运行以下命令来生成具体版本的 poetry.lock 文件。
poetry install
所有 agent 都在单个 FastAPI 服务器(agent/main.py)后面,挂载在 /copilotkit 上。
from fastapi import FastAPI
import uvicorn
from copilotkit.integrations.fastapi import add_fastapi_endpoint
from copilotkit import CopilotKitSDK, LangGraphAgent
from posts_generator_agent import post_generation_graph
from stack_agent import stack_analysis_graph
app = FastAPI()
sdk = CopilotKitSDK(
agents=[
LangGraphAgent(
name="post_generation_agent",
description="An agent that can help with the generation of LinkedIn posts and X posts.",
graph=post_generation_graph,
),
LangGraphAgent(
name="stack_analysis_agent",
description="Analyze a GitHub repository URL to infer purpose and tech stack (frontend, backend, DB, infra).",
graph=stack_analysis_graph,
),
]
)
add_fastapi_endpoint(app, sdk, "/copilotkit")
# 一个简单的端点,用来确认服务器在线
@app.get("/healthz")
def health():
return {"status": "ok"}
def main():
"""运行 uvicorn 服务器。"""
port = int(os.getenv("PORT", "8000"))
uvicorn.run(
"main:app",
host="0.0.0.0",
port=port,
reload=True,
)
if __name__ == "__main__":
main()
幕后发生的事情:
启动一个 FastAPI 服务器
在 CopilotKit 内部注册两个 LangGraph agent(post_generation_agent、stack_analysis_agent)
在 /copilotkit 上暴露它们,以便前端可以与之通信
两个 agent 都表示为 LangGraph 状态机,用若干异步节点连接在一起。
每个 agent 文件(无论是 posts_generator_agent.py 还是 stack_agent.py)都遵循相同的 LangGraph 骨架:
添加节点(每个节点 = 异步函数)
连接边(START → … → END)
用 MemorySaver() 编译
但是每个节点实际做什么会有所不同。
"Post Generator"工作流定义在 posts_generator_agent.py 中。它将三个节点(chat_node、fe_actions_node、end_node)连接到编译的 StateGraph 中。
大致流程如下:
chat_node:通过 genai.Client 调用 Google Gemini,可选地调用 web-search 工具,将中间工具日志流回 UI
fe_actions_node:对聊天结果进行后处理以生成最终的 LinkedIn/X 帖子
end_node:完成工作流
类似地,"Stack Analyzer"工作流定义在 stack_agent.py 中。它也将三个节点(gather_context_node、analyze_with_gemini_node、end_node)连接到编译的 StateGraph 中。
# OpenAI 风格的工具,确保 JSON 模式被强制执行
@tool("return_stack_analysis", args_schema=StructuredStackAnalysis)
def return_stack_analysis_tool(**kwargs) -> Dict[str, Any]:
"""以严格 JSON 形式返回最终栈分析。"""
# …验证并返回…
validated = StructuredStackAnalysis(**kwargs)
return validated.model_dump(exclude_none=True)
# ...
workflow = StateGraph(StackAgentState)
workflow.add_node("gather_context", gather_context_node)
workflow.add_node("analyze", analyze_with_gemini_node)
workflow.add_node("end", end_node)
workflow.add_edge(START, "gather_context")
workflow.add_edge("gather_context", "analyze")
workflow.add_edge("analyze", END)
workflow.set_entry_point("gather_context")
workflow.set_finish_point("end")
stack_analysis_graph = workflow.compile(checkpointer=MemorySaver())
与 Post Generator 不同,这个 agent 要大得多(~500 行)。我不会粘贴所有内容,而是会逐个节点讲解关键代码片段。
你可以在仓库中查看完整实现(包括重试、详细日志和模式验证)。
每个节点及其实际功能:
✅ 1. gather_context_node:这个节点从用户消息中解析 GitHub URL,通过 GitHub API 获取元数据(仓库信息、语言、README、根文件、manifest),并将其存储在 state["context"] 中供下游分析使用。
async def gather_context_node(state: StackAgentState, config: RunnableConfig):
last_user_content = state["messages"][-1].content if state["messages"] else ""
parsed = _parse_github_url(last_user_content)
if not parsed:
return Command(goto="analyze", update={...})
owner, repo = parsed
repo_info = _fetch_repo_info(owner, repo)
languages = _fetch_languages(owner, repo)
readme = _fetch_readme(owner, repo)
root_items = _list_root(owner, repo)
manifests = _fetch_manifest_contents(owner, repo, repo_info.get("default_branch"), root_items)
context = {"owner": owner, "repo": repo, "repo_info": repo_info,
"languages": languages, "readme": readme,
"root_files": _summarize_root_files(root_items),
"manifests": manifests}
return Command(goto="analyze", update={"context": context, ...})
✅ 2. analyze_with_gemini_node:从仓库上下文构建结构化输出提示,询问 Gemini(gemini-2.5-pro)进行分析。Gemini 需要调用 return_stack_analysis 工具,该工具强制执行严格的 JSON 模式。
async def analyze_with_gemini_node(state: StackAgentState, config: RunnableConfig):