详细教程展示 AI 代码生成工具架构实现,是学习 AI 编程工具设计的优质资源。
在本文中,你将学习如何构建 Vercel V0.dev 的克隆版。这是一个非常适合添加到个人作品集、磨炼 AI 技能的优秀项目。
Next.js:应用框架 🖥️
OpenAI:大语言模型(LLM)🧠
使用 CopilotKit 将 AI 集成到应用中 🪁
CopilotKit 是一个开源 AI Copilot 平台。我们让你能够轻松地将强大的 AI 集成到 React 应用中。
ChatBot:具备上下文感知能力、可以在应用内执行操作的聊天机器人 💬
ChatBot:具备上下文感知能力、可以在应用内执行操作的聊天机器人 💬
CopilotTextArea:由 AI 驱动的文本字段,支持上下文感知的自动补全与内容插入 📝
CopilotTextArea:由 AI 驱动的文本字段,支持上下文感知的自动补全与内容插入 📝
Co-Agents:能够与你的应用和用户交互的应用内 AI 智能体 🤖
Co-Agents:能够与你的应用和用户交互的应用内 AI 智能体 🤖
为 CopilotKit 点个 Star ⭐️(请原谅 AI 的拼写错误,再顺手点个 Star 吧 :)
现在回到本文。
要开始本教程,你需要准备以下内容:
一个文本编辑器(VS Code、Cursor)
具备 React、Next.js、Typescript 和 Tailwind CSS 的基础知识。
在你的 PC/Mac 上安装 Node.js
一个包管理器(npm)
在你的 React 项目中安装 CopilotKit
v0 是 Vercel 开发的一款生成式用户界面(UI)工具,用户可以输入提示词并描述自己的想法,工具会将其转换为用于创建 Web 界面的 UI 代码。它利用生成式 AI,以及 React、Tailwind CSS 和 Shadcn UI 等开源工具,根据用户提供的描述生成代码。
下面是一个使用 v0 生成的 Web 应用 UI 示例:https://v0.dev/t/nxGnMd1uVGc
完成本分步教程后,这个克隆版项目将满足以下需求:
用户输入:用户以提示词的形式输入文本,描述希望生成的 UI。这将通过 CopilotKit 聊天机器人完成,该机器人由 CopilotSidebar 提供。
CopilotKit 集成:使用 CopilotKit 为 Web 应用提供生成 UI 的 AI 功能。
渲染 UI:提供一个切换功能,用于在 UI 的 React/JSX 代码和渲染后的 UI 之间切换。
在终端中打开你的工作区文件夹,然后运行以下命令创建一个新的 Next.js 应用:
npx create-next-app@latest copilotkit-v0-clone
这会创建一个名为 copilotkit-v0-clone 的新目录,其中包含 Next.JS 项目结构,并安装所需的依赖项。终端中会显示一系列选项,除最后一个选项外,其余全部选择 Yes,因为建议使用默认的导入别名。其他提示项会安装本项目将使用的 Typescript 和 TailwindCSS。
使用 cd 命令进入项目目录,如下所示:
cd copilotkit-v0-clone
阅读文档以了解更多信息。
运行以下命令安装 CopilotKit 后端包:
npm i @copilotkit/backend
然后访问 https://platform.openai.com/api-keys 获取你的 GPT 4 OpenAI API 密钥。
获取 API 密钥后,在根目录中创建一个 .env.local 文件。.env.local 文件内容应如下所示:
OPENAI_API_KEY=Your OpenAI API key
在 app 目录中创建 api/copilot/openai 目录,并创建一个名为 route.ts 的文件。此文件用作 CopilotKit 请求及 OpenAI 交互的后端端点。它负责处理传入的请求,使用 CopilotKit 对其进行处理,并返回相应的响应。
我们将在 route.ts 文件中创建一个 POST 请求函数。在该 POST 请求中,创建 CopilotBackend 类的新实例,此类提供了处理 CopilotKit 请求的方法。随后,我们调用 CopilotBackend 实例的 response 方法,将请求对象(req)和 OpenAIAdapter 类的新实例作为参数传入。该方法使用 CopilotKit 和 OpenAI API 处理请求,并返回响应。
如下方代码所示,我们从 @copilotkit/backend 包中导入 CopilotBackend 和 OpenAIAdapter 类。这些类是与 CopilotKit 和 OpenAI API 交互所必需的。
import { CopilotBackend, OpenAIAdapter } from "@copilotkit/backend";
export const runtime = "edge";
export async function POST(req: Request): Promise<Response> {
const copilotKit = new CopilotBackend();
return copilotKit.response(req, new OpenAIAdapter());
}
我们将使用 Shadcn UI 组件库中的组件。为了继续操作,先运行 shadcn-ui init 命令来设置 Shadcn UI 组件库:
npx shadcn-ui@latest init
然后根据以下问题配置 components.json:
Which style would you like to use? › Default
Which color would you like to use as base color? › Slate
Do you want to use CSS variables for colors? › no / yes
我们将使用 Shadcn UI 中的 button 和 dialog 组件。现在来安装它们!要安装 button,请运行以下命令:
npx shadcn-ui@latest add button
要安装 dialog 组件,请运行以下命令:
npx shadcn-ui@latest add dialog
阅读文档以了解更多信息。运行以下命令安装 CopilotKit 前端包:
npm i @copilotkit/react-core @copilotkit/react-ui
根据 CopilotKit 文档,要使用 CopilotKit,我们必须设置前端包装器,让任意 React 应用都通过 Copilot 进行处理。当提示词传递给 CopilotKit 时,它会通过指定的 URL 将提示词发送给 OpenAI,后者随后返回响应。
在 app 目录中,更新 layout.tsx 文件。该文件将定义应用的布局结构,并将 CopilotKit 集成到前端。
输入以下代码:
"use client";
import { CopilotKit } from "@copilotkit/react-core";
import "@copilotkit/react-textarea/styles.css"; // also import this if you want to use the CopilotTextarea component
import "@copilotkit/react-ui/styles.css";
import { Inter } from "next/font/google";
import "./globals.css";
import { CopilotSidebar, } from "@copilotkit/react-ui";
const inter = Inter({ subsets: ["latin"] });
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<CopilotKit url="/api/copilotkit/openai/">
<CopilotSidebar defaultOpen>{children}</CopilotSidebar>
</CopilotKit>
</body>
</html>
);
}
此组件表示应用的根布局。它使用 CopilotKit 包装整个应用,并根据我们在第 2 步中创建的后端配置,指定 CopilotKit 后端端点的 URL(/api/copilotkit/openai/)。此外,它还包含一个 CopilotSidebar 组件,作为 CopilotKit 的侧边栏,并将 children 属性作为其内容传入。
现在来创建应用的结构。它将包含 Header、Sidebar 和 Preview screen。
对于 Header,请进入 components 目录,即 src/components,然后创建 header.tsx 文件并输入以下代码:
import { CodeXmlIcon } from "lucide-react";
import { Button } from "./ui/button";
const Header = (props: { openCode: () => void }) => {
return (
<div className="w-full h-20 bg-white flex justify-between items-center px-4">
<h1 className="text-xl font-bold">Copilot Kit</h1>
<div className="flex gap-x-2">
<Button
className=" px-6 py-1 rounded-md space-x-1"
variant={"default"}
onClick={props.openCode}
>
<span>Code</span> <CodeXmlIcon size={20} />
</Button>
</div>
</div>
);
};
export default Header;
对于 Sidebar,创建 sidebar.tsx 文件并输入以下代码:
import { ReactNode } from "react";
const Sidebar = ({ children }: { children: ReactNode }) => {
return (
<div className="w-[12%] min-h-full bg-white rounded-md p-4">
<h1 className="text-sm mb-1">History</h1>
{children}
</div>
);
};
export default Sidebar;
接下来,对于 Preview screen,创建 preview-screen.tsx 文件并输入以下代码:
const PreviewScreen = ({ html_code }: { html_code: string }) => {
return (
<div className="w-full h-full bg-white rounded-lg shadow-lg p-2 border">
<div dangerouslySetInnerHTML={{ __html: html_code }} />
</div>
);
};
export default PreviewScreen;
现在将它们组合起来,打开 page.tsx 文件并粘贴以下代码:
"use client";
import { useState } from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import Header from "@/components/header";
import Sidebar from "@/components/sidebar";
import PreviewScreen from "@/components/preview-screen";
import { Input } from "@/components/ui/input";
export default function Home() {
const [code, setCode] = useState<string[]>([
<h1 class="text-red-500">Hello World</h1>,
]);
const [codeToDisplay, setCodeToDisplay] = useState<string>(code[0] || "");
const [showDialog, setShowDialog] = useState<boolean>(false);
const [codeCommand, setCodeCommand] = useState<string>("");
return ( <> <main className="bg-white min-h-screen px-4"> <Header openCode={() => setShowDialog(true)} /> <div className="w-full h-full min-h-[70vh] flex justify-between gap-x-1 "> <Sidebar> <div className="space-y-2"> {code.map((c, i) => ( <div key={i} className="w-full h-20 p-1 rounded-md bg-white border border-blue-600" onClick={() => setCodeToDisplay(c)} > v{i} </div> ))} </div> </Sidebar>
<div className="w-10/12"> <PreviewScreen html_code={codeToDisplay || ""} /> </div> </div> <div className="w-8/12 mx-auto p-1 rounded-full bg-primary flex my-4 outline-0"> <Input type="text" placeholder="Enter your code command" className="w-10/12 p-6 rounded-l-full outline-0 bg-primary text-white" value={codeCommand} onChange={(e) => setCodeCommand(e.target.value)} /> <button className="w-2/12 bg-white text-primary rounded-r-full" onClick={() => generateCode.run(context)} > Generate </button> </div> </main> <Dialog open={showDialog} onOpenChange={setShowDialog}> <DialogContent> <DialogHeader> <DialogTitle>View Code.</DialogTitle> <DialogDescription> You can use the following code to start integrating into your application. </DialogDescription> <div className="p-4 rounded bg-primary text-white my-2"> {readableCode} </div> </DialogHeader> </DialogContent> </Dialog> </> ); }
下面我们来拆解一下上述代码:
`const [code, setCode] = useState<string[]>([]);` 将用于保存生成的代码。
`const [codeToDisplay, setCodeToDisplay] = useState<string>(code[0] || "");` 将用于保存显示在预览屏幕上的代码。
`const [showDialog, setShowDialog] = useState<boolean>(false);` 将保存对话框的状态,该对话框用于显示可供复制的生成代码。
在下面的代码中,我们遍历以字符串数组形式保存的生成代码,并将其显示在侧边栏上。这样,当我们选中其中一项时,它就会显示在预览屏幕上。
<Sidebar> <div className="space-y-2"> {code.map((c, i) => ( <div key={i} className="w-full h-20 p-1 rounded-md bg-white border border-blue-600" onClick={() => setCodeToDisplay(c)} > v{i} </div> ))} </div> </Sidebar>
在 `<PreviewScreen html_code={codeToDisplay} />` 中,我们将需要展示的代码发送到预览屏幕。预览屏幕组件接收 CopilotKit 生成的代码字符串,并使用 `dangerouslySetInnerHTML` 渲染生成的代码。
下面是一个 Dialog 组件,它将显示 CopilotKit 生成的代码,用户可以复制这些代码并将其添加到自己的代码中。
<Dialog open={showDialog} onOpenChange={setShowDialog}> <DialogContent> <DialogHeader> <DialogTitle>View Code.</DialogTitle> <DialogDescription> You can use the following code to start integrating into your application. </DialogDescription> <div className="p-4 rounded bg-primary text-white my-2"> {readableCode} </div> </DialogHeader> </DialogContent> </Dialog>
## 第 6 步:实现应用的主要逻辑
在这一步中,我们将把 CopilotKit 集成到 v0 克隆应用中,以实现由 AI 驱动的 UI 生成。我们将使用 CopilotKit 的 React Hooks 管理状态,让 Copilot 能够读取组件并对组件执行操作,同时与 OpenAI API 交互。
在 `page.tsx` 文件中导入以下内容:
import { CopilotTask,
useCopilotContext, useMakeCopilotReadable, } from "@copilotkit/react-core";
然后,在 Home 组件中使用 `CopilotTask` 定义一个 `generateCode` 任务:
const readableCode = useMakeCopilotReadable(codeToDisplay);
const generateCode = new CopilotTask({ instructions: codeCommand, actions: [ { name: "generateCode", description: "Create Code Snippet with React.js, tailwindcss.", parameters: [ { name: "code", type: "string", description: "Code to be generated", required: true, }, ], handler: async ({ code }) => { setCode((prev) => [...prev, code]); setCodeToDisplay(code); }, }, ], });
const context = useCopilotContext();
我们使用 `useMakeCopilotReadable` 传入现有代码,并确保这些代码具有可读性。然后,我们使用 `CopilotTask` 生成 UI,并将 `generateCode` 任务绑定到生成按钮上,从而允许用户通过与按钮组件交互来生成代码片段。该操作由用户交互触发,并在调用时执行一个异步处理函数。
处理函数会把生成的代码添加到 `code` 数组中,更新应用状态以包含新生成的代码片段,同时还会将生成的代码发送到预览屏幕中进行显示和渲染,这些代码同样可以复制。此外,`instructions` 属性指定了发送给 Copilot 的命令,该命令保存在 `codeCommand` 状态变量中。
有关 `CopilotTask` 工作原理的完整说明,请查看此处的文档:https://docs.copilotkit.ai/reference/CopilotTask
## 第 7 步:运行 v0 克隆应用
至此,我们已经完成了 v0 克隆应用的设置,接下来可以运行以下命令启动开发服务器:
npm run dev
可以在浏览器中通过以下 URL 访问该 Web 应用:
http://localhost:3000
然后,你可以输入提示词并点击 Generate。下面是一些示例:
定价页面:如下图所示,这是生成的 UI,其中包含一个可在 UI 和 React 代码之间切换的按钮:
如果点击右上角的 Code `</>` 按钮,它就会切换到所生成 UI 对应的 React 代码,如下所示:
一个注册页面 UI 示例:
以及一个结账页面:
要克隆该项目并在本地运行,请打开终端并执行以下命令:
git clone https://github.com/Tabintel/v0-copilot-next
然后运行 `npm install` 安装项目所需的全部依赖,再运行 `npm run dev` 启动 Web 应用。
总而言之,你可以使用 CopilotKit 构建一个 v0 克隆应用,通过提示词生成 UI 设计。CopilotKit 的能力并不局限于 UI 提示词,它还可以用于构建 AI 驱动的 PowerPoint 生成器、AI 简历生成器等应用。可能性是无穷无尽的。现在就体验 CopilotKit,让你的 AI 创意变为现实。你可以在 GitHub 上获取完整源代码,也可以通过文档进一步了解 CopilotKit 的使用方法。
另外,别忘了给 CopilotKit 点个 Star!⭐
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。