長話短說

待辦事項清單是每個開發人員的經典專案。在當今世界,學習如何使用人工智慧進行建構並在你的投資組合中加入一些人工智慧專案是很棒的。

今天,我將逐步介紹如何使用嵌入式 AI 副駕駛來建立待辦事項列表,以實現一些 AI 魔法🪄。

圖片描述

我們將介紹如何:

  • 使用 Next.js、TypeScript 和 Tailwind CSS 建立待辦事項清單產生器 Web 應用。

  • 使用 CopilotKit 將 AI 功能整合到待辦事項清單產生器中。

  • 使用 AI 聊天機器人新增清單、將清單分配給某人、將清單標記為已完成以及刪除清單。

圖片描述


CopilotKit:建構應用內人工智慧副駕駛的框架

CopilotKit是一個開源的AI副駕駛框架。我們可以輕鬆地將強大的人工智慧整合到您的 React 應用程式中。

建造:

  • ChatBot:上下文感知的應用內聊天機器人,可以在應用程式內執行操作 💬

  • CopilotTextArea:人工智慧驅動的文字字段,具有上下文感知自動完成和插入功能📝

  • 聯合代理:應用程式內人工智慧代理,可以與您的應用程式和使用者互動🤖

圖片描述

{% cta https://go.copilotkit.ai/bonnie %} 明星 CopilotKit ⭐️ {% endcta %}


先決條件

要完全理解本教程,您需要對 React 或 Next.js 有基本的了解。

以下是建立人工智慧驅動的待辦事項清單產生器所需的工具:

  • 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 狀態檢索資料並將 AI copilot 新增至應用程式。

 npm install @copilotkit/react-ui @copilotkit/react-textarea @copilotkit/react-core @copilotkit/backend @copilotkit/shared

恭喜!您現在已準備好建立由人工智慧驅動的待辦事項清單產生器。

建立待辦事項清單產生器前端

在本節中,我將引導您完成使用靜態內容建立待辦事項清單產生器前端的過程,以定義生成器的使用者介面。

首先,請在程式碼編輯器中前往/[root]/src/app並建立一個名為types資料夾。在 types 資料夾中,建立一個名為todo.ts的文件,並新增以下程式碼來定義名為Todo的 TypeScript 介面。

Todo介面定義了一個物件結構,其中每個待辦事項都必須具有idtextisCompleted狀態,同時也可以選擇具有assignedTo屬性。

export interface Todo {
  id: string;
  text: string;
  isCompleted: boolean;
  assignedTo?: string;
}

然後轉到程式碼編輯器中的/[root]/src/app並建立一個名為components的資料夾。在 Components 資料夾中,建立三個名為Header.tsxTodoList.tsxTodoItem.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文件,新增以下程式碼,匯入TodoListHeader元件並定義名為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 功能整合到待辦事項清單產生器

在本節中,您將學習如何將 AI 副駕駛新增至待辦事項清單產生器,以使用 CopilotKit 產生清單。

CopilotKit 提供前端和後端套件。它們使您能夠插入 React 狀態並使用 AI 代理在後端處理應用程式資料。

首先,我們將 CopilotKit React 元件新增至待辦事項清單產生器前端。

將 CopilotKit 新增至待辦事項清單產生器前端

在這裡,我將引導您完成將待辦事項清單產生器與 CopilotKit 前端整合以促進清單產生的過程。

首先,使用下面的程式碼片段匯入/[root]/src/app/components/TodoList.tsx檔案頂部的useCopilotReadableuseCopilotAction自訂掛鉤。

import { useCopilotAction, useCopilotReadable } from "@copilotkit/react-core";

TodoList函數內的狀態變數下方,新增以下程式碼,該程式碼使用useCopilotReadable掛鉤來新增將作為應用程式內聊天機器人的上下文產生的待辦事項清單。該鉤子使副駕駛可以讀取待辦事項清單。

useCopilotReadable({
    description: "The user's todo list.",
    value: todos,
  });

在上面的程式碼下方,新增以下程式碼,該程式碼使用useCopilotAction掛鉤來設定名為updateTodoList的操作,該操作將啟用待辦事項清單的產生。

此操作採用一個名為 items 的參數,該參數可以產生待辦事項列表,並包含一個根據給定提示產生待辦事項列表的處理程序函數。

在處理函數內部, todos狀態會使用新產生的 todo 清單進行更新,如下所示。

// 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掛鉤來設定名為deleteTodo的操作,該操作使您能夠刪除待辦事項。

該操作採用名為id 的參數,該參數可讓您透過id 刪除待辦事項,並包含一個處理函數,該函數透過過濾掉具有給定id 的已刪除待辦事項來更新待辦事項狀態。

// 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));
    },

    // Provide feedback or a message while the action is processing
    render: "Deleting a todo item...",
  });

之後,請前往/[root]/src/app/page.tsx檔案並使用下面的程式碼匯入頂部的 CopilotKit 前端套件和樣式。

import { CopilotKit } from "@copilotkit/react-core";
import { CopilotPopup } from "@copilotkit/react-ui";
import "@copilotkit/react-ui/styles.css";

然後使用CopilotKit包裝CopilotPopupTodoList元件,如下所示。 CopilotKit元件指定 CopilotKit 後端端點 ( /api/copilotkit/ ) 的 URL,而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 後端加入博客

在這裡,我將引導您完成將待辦事項清單產生器與 CopilotKit 後端整合的過程,該後端處理來自前端的請求,並提供函數呼叫和各種 LLM 後端(例如 GPT)。

首先,在根目錄中建立一個名為.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());
}

如何產生待辦事項列表

現在轉到您之前整合的應用程式內聊天機器人,並給它一個提示,例如「我想去健身房做全身運動。加入到我應該遵循的鍛煉程序列表”

生成完成後,您應該會看到應遵循的全身運動程序列表,如下所示。

圖片描述

您可以透過向聊天機器人發出「將待辦事項清單指派給 Doe」之類的提示來將待辦事項清單指派給某人。

圖片描述

您可以透過向聊天機器人提供「將待辦事項清單標記為已完成」等提示來將待辦事項清單標記為已完成。

圖片描述

您可以透過向聊天機器人發出「刪除待辦事項清單」之類的提示來刪除待辦事項清單。

圖片描述

恭喜!您已完成本教學的專案。

結論

CopilotKit是一款令人難以置信的工具,可讓您在幾分鐘內將 AI Copilot 加入到您的產品中。無論您是對人工智慧聊天機器人和助理感興趣,還是對複雜任務的自動化感興趣,CopilotKit 都能讓您輕鬆實現。

如果您需要建立 AI 產品或將 AI 工具整合到您的軟體應用程式中,您應該考慮 CopilotKit。

您可以在 GitHub 上找到本教學的源程式碼:https://github.com/TheGreatBonnie/AIpoweredToDoListGenerator


原文出處:https://dev.to/copilotkit/how-to-build-an-ai-powered-to-do-list-nextjs-gpt4-copilotkit-20i4


共有 0 則留言