完整教程展示用 Next.js、GPT4 和 CopilotKit 为应用嵌入 AI 副驾驶功能。提供即学即用的 AI 集成方案。
待办事项清单是每位开发者都会接触的经典项目。在当今这个时代,学习如何使用 AI 进行开发,并在作品集中加入一些 AI 项目,是非常有价值的。
今天,我将逐步讲解如何构建一个嵌入 AI 副驾驶的待办事项清单,为应用增添一些 AI 魔法 🪄。
使用 Next.js、TypeScript 和 Tailwind CSS 构建待办事项清单生成器 Web 应用。
使用 CopilotKit 将 AI 功能集成到待办事项清单生成器中。
使用 AI 聊天机器人添加清单、将清单分配给某人、将清单标记为已完成,以及删除清单。
CopilotKit:用于构建应用内 AI 副驾驶的框架
CopilotKit 是一个开源的 AI 副驾驶框架。它能让你轻松地将强大的 AI 集成到 React 应用中。
ChatBot:能够感知上下文并在应用内执行操作的聊天机器人 💬
CopilotTextArea:支持上下文感知自动补全和内容插入的 AI 文本字段 📝
Co-Agents:能够与你的应用和用户交互的应用内 AI 智能体 🤖
要充分理解本教程,你需要具备 React 或 Next.js 的基础知识。
以下是构建 AI 驱动的待办事项清单生成器所需的工具:
Nanoid——一个小巧、安全、适用于 URL 的 JavaScript 唯一字符串 ID 生成器。
OpenAI API——提供 API 密钥,使你能够使用 ChatGPT 模型执行各种任务。
CopilotKit——一个开源副驾驶框架,用于构建自定义 AI 聊天机器人、应用内 AI 智能体和文本区域。
首先,在终端中运行以下代码片段,创建一个 Next.js 应用:
npx create-next-app@latest todolistgenerator
选择你偏好的配置选项。本教程将使用 TypeScript 和 Next.js App Router。
接下来,安装 Nanoid 包及其依赖项。
npm i nanoid
最后,安装 CopilotKit 相关包。这些包使我们能够从 React state 中获取数据,并将 AI 副驾驶添加到应用中。
npm install @copilotkit/react-ui @copilotkit/react-textarea @copilotkit/react-core @copilotkit/backend @copilotkit/shared
恭喜!现在你已经可以开始构建 AI 驱动的待办事项清单生成器了。
在本节中,我将带你完成待办事项清单生成器前端的创建过程。我们会先使用静态内容来定义生成器的用户界面。
首先,在代码编辑器中前往 /[root]/src/app,创建一个名为 types 的文件夹。在 types 文件夹中创建一个名为 todo.ts 的文件,并添加以下代码,以定义名为 Todo 的 TypeScript 接口。
Todo 接口定义了一种对象结构:每个待办事项都必须包含 id、text 和 isCompleted 状态,同时可以选择性地包含 assignedTo 属性。
export interface Todo {
id: string;
text: string;
isCompleted: boolean;
assignedTo?: string;
}
然后,在代码编辑器中前往 /[root]/src/app,创建一个名为 components 的文件夹。在 components 文件夹中创建三个文件,分别命名为 Header.tsx、TodoList.tsx 和 TodoItem.tsx。
在 Header.tsx 文件中添加以下代码,它定义了一个名为 Header 的函数组件,用于渲染生成器的导航栏。
import Link from "next/link";
export default function Header() {
return (
<>
<header className="flex flex-wrap sm:justify-start sm:flex-nowrap z-50 w-full bg-gray-800 border-b border-gray-200 text-sm py-3 sm:py-0 ">
<nav
className="relative max-w-7xl w-full mx-auto px-4 sm:flex sm:items-center sm:justify-between sm:px-6 lg:px-8"
aria-label="Global">
<div className="flex items-center justify-between">
<Link
className="w-full flex-none text-xl text-white font-semibold p-6"
href="/"
aria-label="Brand">
To-Do List Generator
</Link>
</div>
</nav>
</header>
</>
);
}
在 TodoItem.tsx 文件中添加以下代码,它定义了一个名为 TodoItem 的 React 函数组件。该组件使用 TypeScript 确保类型安全,并定义组件接收的 props。
import { Todo } from "../types/todo"; // Importing the Todo type from a types file
// Defining the interface for the props that the TodoItem component will receive
interface TodoItemProps {
todo: Todo; // A single todo item
toggleComplete: (id: string) => void; // Function to toggle the completion status of a todo
deleteTodo: (id: string) => void; // Function to delete a todo
assignPerson: (id: string, person: string | null) => void; // Function to assign a person to a todo
hasBorder?: boolean; // Optional prop to determine if the item should have a border
}
// Defining the TodoItem component as a functional component with the specified props
export const TodoItem: React.FC<TodoItemProps> = ({
todo,
toggleComplete,
deleteTodo,
assignPerson,
hasBorder,
}) => {
return (
<div
className={
"flex items-center justify-between px-4 py-2 group" +
(hasBorder ? " border-b" : "") // Conditionally adding a border class if hasBorder is true
}>
<div className="flex items-center">
<input
className="h-5 w-5 text-blue-500"
type="checkbox"
checked={todo.isCompleted} // Checkbox is checked if the todo is completed
onChange={() => toggleComplete(todo.id)} // Toggle completion status on change
/>
<span
className={`ml-2 text-sm text-white ${
todo.isCompleted ? "text-gray-500 line-through" : "text-gray-900" // Apply different styles if the todo is completed
}`}>
{todo.assignedTo && (
<span className="border rounded-md text-xs py-[2px] px-1 mr-2 border-purple-700 uppercase bg-purple-400 text-black font-medium">
{todo.assignedTo} {/* Display the assigned person's name if available */}
</span>
)}
{todo.text} {/* Display the todo text */}
</span>
</div>
<div>
<button
onClick={() => deleteTodo(todo.id)} // Delete the todo on button click
className="text-red-500 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-5 h-5">
<path
strokeLinecap="round"
strokeLinejoin="round"
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
/>
</svg>
</button>
<button
onClick={() => {
const name = prompt("Assign person to this task:");
assignPerson(todo.id, name);
}}
className="ml-2 text-blue-500 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-5 h-5">
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M18 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0ZM3 19.235v-.11a6.375 6.375 0 0 1 12.75 0v.109A12.318 12.318 0 0 1 9.374 21c-2.331 0-4.512-.645-6.374-1.766Z"
/>
</svg>
</button>
</div>
</div>
);
};
在 TodoList.tsx 文件中添加以下代码,它定义了一个名为 TodoList 的 React 函数组件。该组件用于管理和显示待办事项列表。
"use client";
import { TodoItem } from "./TodoItem"; // Importing the TodoItem component
import { nanoid } from "nanoid"; // Importing the nanoid library for generating unique IDs
import { useState } from "react"; // Importing the useState hook from React
import { Todo } from "../types/todo"; // Importing the Todo type
// Defining the TodoList component as a functional component
export const TodoList: React.FC = () => {
// State to hold the list of todos
const [todos, setTodos] = useState<Todo[]>([]);
// State to hold the current input value
const [input, setInput] = useState("");
// Function to add a new todo
const addTodo = () => {
if (input.trim() !== "") {
// Check if the input is not empty
const newTodo: Todo = {
id: nanoid(), // Generate a unique ID for the new todo
text: input.trim(), // Trim the input text
isCompleted: false, // Set the initial completion status to false
};
setTodos([...todos, newTodo]); // Add the new todo to the list
setInput(""); // Clear the input field
}
};
// Function to handle key press events
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === "Enter") {
// Check if the Enter key was pressed
addTodo(); // Add the todo
}
};
// Function to toggle the completion status of a todo
const toggleComplete = (id: string) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, isCompleted: !todo.isCompleted } : todo
)
);
};
// Function to delete a todo
const deleteTodo = (id: string) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
// Function to assign a person to a todo
const assignPerson = (id: string, person: string | null) => {
setTodos(
todos.map((todo) =>
todo.id === id
? { ...todo, assignedTo: person ? person : undefined }
: todo
)
);
};
return (
<div>
<div className="flex mb-4">
<input
className="border rounded-md p-2 flex-1 mr-2"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyPress} // Add this to handle the Enter key press
/>
<button
className="bg-blue-500 rounded-md p-2 text-white"
onClick={addTodo}>
Add Todo
</button>
</div>
{todos.length > 0 && ( // Check if there are any todos
<div className="border rounded-lg">
{todos.map((todo, index) => (
<TodoItem
key={todo.id} // Unique key for each todo item
todo={todo} // Pass the todo object as a prop
toggleComplete={toggleComplete} // Pass the toggleComplete function as a prop
deleteTodo={deleteTodo} // Pass the deleteTodo function as a prop
assignPerson={assignPerson} // Pass the assignPerson function as a prop
hasBorder={index !== todos.length - 1} // Conditionally add a border to all but the last item
/>
))}
</div>
)}
</div>
);
};
接下来,打开 /[root]/src/page.tsx 文件,添加以下代码。该代码会导入 TodoList 和 Header 组件,并定义一个名为 Home 的函数组件。
import Header from "./components/Header";
import { TodoList } from "./components/TodoList";
export default function Home() {
return (
<>
<Header />
<div className="border rounded-md max-w-2xl mx-auto p-4 mt-4">
<h1 className="text-2xl text-white font-bold mb-4">
Create a to-do list
</h1>
<TodoList />
</div>
</>
);
}
接下来,删除 globals.css 文件中的 CSS 代码,并添加以下 CSS 代码。
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
height: 100vh;
background-color: rgb(16, 23, 42);
}
最后,在命令行中运行命令 npm run dev,然后访问 http://localhost:3000/。
现在,你应该能在浏览器中看到待办事项列表生成器的前端,如下所示。
在本节中,你将学习如何使用 CopilotKit 为待办事项列表生成器添加一个 AI 副驾驶,以生成列表。
CopilotKit 同时提供前端和后端软件包。借助这些软件包,你可以接入 React 状态,并在后端使用 AI 智能体处理应用程序数据。
首先,让我们将 CopilotKit React 组件添加到待办事项列表生成器的前端。
在这里,我将带你完成待办事项列表生成器与 CopilotKit 前端的集成过程,从而实现列表生成功能。
首先,使用下面的代码片段,在 /[root]/src/app/components/TodoList.tsx 文件顶部导入 useCopilotReadable 和 useCopilotAction 自定义 Hook。
import { useCopilotAction, useCopilotReadable } from "@copilotkit/react-core";
在 TodoList 函数内部的状态变量下方添加以下代码。该代码使用 useCopilotReadable Hook,将即将生成的待办事项列表作为应用内聊天机器人的上下文。这个 Hook 能让副驾驶读取待办事项列表。
useCopilotReadable({
description: "The user's todo list.",
value: todos,
});
在上述代码下方添加以下代码。该代码使用 useCopilotAction Hook 设置一个名为 updateTodoList 的操作,用于生成待办事项列表。
该操作接收一个名为 items 的参数,用于生成待办事项列表;同时包含一个处理函数,负责根据给定的提示词生成待办事项列表。
在处理函数内部,todos 状态会使用新生成的待办事项列表进行更新,如下所示。
// Define the "updateTodoList" action using the useCopilotAction function
useCopilotAction({
// Name of the action
name: "updateTodoList",
// Description of what the action does
description: "Update the users todo list",
// Define the parameters that the action accepts
parameters: [
{
// The name of the parameter
name: "items",
// The type of the parameter, an array of objects
type: "object[]",
// Description of the parameter
description: "The new and updated todo list items.",
// Define the attributes of each object in the items array
attributes: [
{
// The id of the todo item
name: "id",
type: "string",
description:
"The id of the todo item. When creating a new todo item, just make up a new id.",
},
{
// The text of the todo item
name: "text",
type: "string",
description: "The text of the todo item.",
},
{
// The completion status of the todo item
name: "isCompleted",
type: "boolean",
description: "The completion status of the todo item.",
},
{
// The person assigned to the todo item
name: "assignedTo",
type: "string",
description:
"The person assigned to the todo item. If you don't know, assign it to 'YOU'.",
// This attribute is required
required: true,
},
],
},
],
// Define the handler function that executes when the action is invoked
handler: ({ items }) => {
// Log the items to the console for debugging purposes
console.log(items);
// Create a copy of the existing todos array
const newTodos = [...todos];
// Iterate over each item in the items array
for (const item of items) {
// Find the index of the existing todo item with the same id
const existingItemIndex = newTodos.findIndex(
(todo) => todo.id === item.id
);
// If an existing item is found, update it
if (existingItemIndex !== -1) {
newTodos[existingItemIndex] = item;
}
// If no existing item is found, add the new item to the newTodos array
else {
newTodos.push(item);
}
}
// Update the state with the new todos array
setTodos(newTodos);
},
// Provide feedback or a message while the action is processing
render: "Updating the todo list...",
});
在上述代码下方添加以下代码。该代码使用 useCopilotAction Hook 设置一个名为 deleteTodo 的操作,让你能够删除待办事项。
该操作接收一个名为 id 的参数,让你能够根据 ID 删除待办事项;同时包含一个处理函数,它会根据给定的 ID 过滤掉已删除的待办事项,从而更新 todos 状态。
// Define the "deleteTodo" action using the useCopilotAction function
useCopilotAction({
// Name of the action
name: "deleteTodo",
// Description of what the action does
description: "Delete a todo item",
// Define the parameters that the action accepts
parameters: [
{
// The name of the parameter
name: "id",
// The type of the parameter, a string
type: "string",
// Description of the parameter
description: "The id of the todo item to delete.",
},
],
// Define the handler function that executes when the action is invoked
handler: ({ id }) => {
// Update the state by filtering out the todo item with the given id
setTodos(todos.filter((todo) => todo.id !== id));
},
// 在操作处理期间提供反馈或提示信息 render: "Deleting a todo item...", });
完成后,打开 `/[root]/src/app/page.tsx` 文件,使用以下代码在文件顶部导入 CopilotKit 前端包和样式。
```tsx
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotPopup } from "@copilotkit/react-ui";
import "@copilotkit/react-ui/styles.css";
然后使用 CopilotKit 包裹 CopilotPopup 和 TodoList 组件,如下所示。CopilotKit 组件指定了 CopilotKit 后端端点的 URL(/api/copilotkit/),而 CopilotPopup 会渲染应用内聊天机器人,你可以向它输入提示词来生成待办事项列表。
export default function Home() {
return (
<>
<Header />
<div className="border rounded-md max-w-2xl mx-auto p-4 mt-4">
<h1 className="text-2xl text-white font-bold mb-4">
Create a to-do list
</h1>
<CopilotKit runtimeUrl="/api/copilotkit">
<TodoList />
<CopilotPopup
instructions={
"Help the user manage a todo list. If the user provides a high level goal, " +
"break it down into a few specific tasks and add them to the list"
}
defaultOpen={true}
labels={{
title: "Todo List Copilot",
initial: "Hi you! 👋 I can help you manage your todo list.",
}}
clickOutsideToClose={false}
/>
</CopilotKit>
</div>
</>
);
}
完成后,运行开发服务器并访问 http://localhost:3000。你应该会看到应用内聊天机器人已经集成到了待办事项列表生成器中。
在这里,我将带你完成待办事项列表生成器与 CopilotKit 后端的集成过程。该后端负责处理来自前端的请求,并提供函数调用以及 GPT 等多种 LLM 后端。
首先,在根目录中创建一个名为 .env.local 的文件。然后在该文件中添加以下用于保存 ChatGPT API 密钥的环境变量。
OPENAI_API_KEY="Your ChatGPT API key”
要获取 ChatGPT API 密钥,请访问 https://platform.openai.com/api-keys。
接下来,进入 /[root]/src/app 并创建一个名为 api 的文件夹。在 api 文件夹中,再创建一个名为 copilotkit 的文件夹。
在 copilotkit 文件夹中创建一个名为 route.ts 的文件,其中包含用于设置处理 POST 请求的后端功能的代码。
// Import the necessary modules from the "@copilotkit/backend" package
import { CopilotRuntime, OpenAIAdapter } from "@copilotkit/backend";
// Define an asynchronous function to handle POST requests
export async function POST(req: Request): Promise<Response> {
// Create a new instance of CopilotRuntime
const copilotKit = new CopilotRuntime({});
// Use the copilotKit to generate a response using the OpenAIAdapter
// Pass the incoming request (req) and a new instance of OpenAIAdapter to the response method
return copilotKit.response(req, new OpenAIAdapter());
}
现在,打开之前集成的应用内聊天机器人,并向它输入这样的提示词:“I want to go to the gym to do a full body workout. add to the list workout routine I should follow”
生成完成后,你应该会看到一份需要遵循的全身锻炼计划列表,如下所示。
你可以通过向聊天机器人输入类似“assign the to-do list to Doe.”的提示词,将待办事项列表分配给某个人。
你可以通过向聊天机器人输入类似“mark the to-do list as completed.”的提示词,将待办事项列表标记为已完成。
你可以通过向聊天机器人输入类似“delete the todo list.”的提示词,删除待办事项列表。
恭喜!你已经完成了本教程的项目。
CopilotKit 是一款非常出色的工具,让你可以在几分钟内为自己的产品添加 AI Copilot。无论你对 AI 聊天机器人和助手感兴趣,还是希望自动执行复杂任务,CopilotKit 都能让这一切变得简单。
如果你需要构建 AI 产品,或将 AI 工具集成到自己的软件应用中,可以考虑使用 CopilotKit。
你可以在 GitHub 上找到本教程的源代码:https://github.com/TheGreatBonnie/AIpoweredToDoListGenerator
部分评论可能仅对已登录的访客可见。登录后即可查看全部评论。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。