在本文中,您將學習如何建立人工智慧驅動的電子表格應用程式,該應用程式允許您使用簡單的英語命令執行各種會計功能並輕鬆與資料互動。
我們將介紹如何:
使用 Next.js 建立 Web 應用程式,
使用 React Spreadsheet 建立電子表格應用程式,以及
使用 CopilotKit 將 AI 整合到軟體應用程式中。
讓電子表格更容易使用、更有趣
CopilotKit是一個開源的AI副駕駛平台。我們可以輕鬆地將強大的人工智慧整合到您的 React 應用程式中。
建造:
ChatBot:上下文感知的應用內聊天機器人,可以在應用程式內執行操作 💬
CopilotTextArea:人工智慧驅動的文字字段,具有上下文感知自動完成和插入功能📝
聯合代理:應用程式內人工智慧代理,可以與您的應用程式和使用者互動🤖
{% cta https://github.com/CopilotKit/CopilotKit %} Star CopilotKit ⭐️ {% endcta %}
(請原諒 AI 的拼字錯誤並給 CopilotKit 加上星號:)
現在回到文章!
要完全理解本教程,您需要對 React 或 Next.js 有基本的了解。
以下是建立人工智慧驅動的電子表格應用程式所需的工具:
React Spreadsheet - 一個簡單的包,使我們能夠在 React 應用程式中加入電子表格。
OpenAI API - 提供 API 金鑰,使我們能夠使用 ChatGPT 模型執行各種任務。
Tavily AI - 一個搜尋引擎,使人工智慧代理能夠在應用程式中進行研究並存取即時知識。
CopilotKit - 一個開源副駕駛框架,用於建立自訂 AI 聊天機器人、應用程式內 AI 代理程式和文字區域。
首先,透過在終端機中執行以下程式碼片段來建立 Next.js 應用程式:
npx create-next-app spreadsheet-app
選擇您首選的配置設定。在本教學中,我們將使用 TypeScript 和 Next.js App Router。
接下來,安裝OpenAI 函式庫、 Heroicons和React Spreadsheet套件及其相依性:
npm install openai react-spreadsheet scheduler @heroicons/react
最後,安裝 CopilotKit 軟體套件。這些套件使我們能夠從 React 狀態檢索資料並將 AI copilot 新增至應用程式。
npm install @copilotkit/react-ui @copilotkit/react-textarea @copilotkit/react-core @copilotkit/backend
恭喜!您現在已準備好建立應用程式。
在本節中,我將引導您使用 React Spreadsheet 建立電子表格應用程式。
該應用程式分為兩個元件: Sidebar
和SingleSpreadsheet
。
要設定這些元件,請導航至 Next.js 應用程式資料夾並建立一個包含以下檔案的components
資料夾:
cd app
mkdir components && cd components
touch Sidebar.tsx SingleSpreadsheet.tsx
將新建立的元件匯入到app/page.tsx
檔案中。
"use client";
import React, { useState } from "react";
//👇🏻 import the components
import { SpreadsheetData } from "./types";
import Sidebar from "./components/Sidebar";
import SingleSpreadsheet from "./components/SingleSpreadsheet";
const Main = () => {
return (
<div className='flex'>
<p>Hello world</p>
</div>
);
};
export default Main;
接下來,建立將包含電子表格資料的 React 狀態,並將它們作為 props 傳遞到元件中。
const Main = () => {
//👇🏻 holds the title and data within a spreadsheet
const [spreadsheets, setSpreadsheets] = React.useState<SpreadsheetData[]>([
{
title: "Spreadsheet 1",
data: [
[{ value: "" }, { value: "" }, { value: "" }],
[{ value: "" }, { value: "" }, { value: "" }],
[{ value: "" }, { value: "" }, { value: "" }],
],
},
]);
//👇🏻 represents the index of a spreadsheet
const [selectedSpreadsheetIndex, setSelectedSpreadsheetIndex] = useState(0);
return (
<div className='flex'>
<Sidebar
spreadsheets={spreadsheets}
selectedSpreadsheetIndex={selectedSpreadsheetIndex}
setSelectedSpreadsheetIndex={setSelectedSpreadsheetIndex}
/>
<SingleSpreadsheet
spreadsheet={spreadsheets[selectedSpreadsheetIndex]}
setSpreadsheet={(spreadsheet) => {
setSpreadsheets((prev) => {
console.log("setSpreadsheet", spreadsheet);
const newSpreadsheets = [...prev];
newSpreadsheets[selectedSpreadsheetIndex] = spreadsheet;
return newSpreadsheets;
});
}}
/>
</div>
);
};
此程式碼片段建立了 React 狀態,用於保存電子表格資料及其索引,並將它們作為 props 傳遞到元件中。
Sidebar
元件接受所有可用的電子表格, SingleSpreadsheet
元件接收所有電子表格,包括更新電子表格資料的setSpreadsheet
函數。
將下面的程式碼片段複製到Sidebar.tsx
檔案中。它顯示應用程式中的所有電子表格,並允許使用者在它們之間進行切換。
import React from "react";
import { SpreadsheetData } from "../types";
interface SidebarProps {
spreadsheets: SpreadsheetData[];
selectedSpreadsheetIndex: number;
setSelectedSpreadsheetIndex: (index: number) => void;
}
const Sidebar = ({
spreadsheets,
selectedSpreadsheetIndex,
setSelectedSpreadsheetIndex,
}: SidebarProps) => {
return (
<div className='w-64 h-screen bg-gray-800 text-white overflow-auto p-5'>
<ul>
{spreadsheets.map((spreadsheet, index) => (
<li
key={index}
className={`mb-4 cursor-pointer ${
index === selectedSpreadsheetIndex
? "ring-2 ring-blue-500 ring-inset p-3 rounded-lg"
: "p-3"
}`}
onClick={() => setSelectedSpreadsheetIndex(index)}
>
{spreadsheet.title}
</li>
))}
</ul>
</div>
);
};
export default Sidebar;
更新SingleSpreadsheet.tsx
文件,如下所示:
import React from "react";
import Spreadsheet from "react-spreadsheet";
import { SpreadsheetData, SpreadsheetRow } from "../types";
interface MainAreaProps {
spreadsheet: SpreadsheetData;
setSpreadsheet: (spreadsheet: SpreadsheetData) => void;
}
//👇🏻 adds a new row to the spreadsheet
const addRow = () => {
const numberOfColumns = spreadsheet.rows[0].length;
const newRow: SpreadsheetRow = [];
for (let i = 0; i < numberOfColumns; i++) {
newRow.push({ value: "" });
}
setSpreadsheet({
...spreadsheet,
rows: [...spreadsheet.rows, newRow],
});
};
//👇🏻 adds a new column to the spreadsheet
const addColumn = () => {
const spreadsheetData = [...spreadsheet.data];
for (let i = 0; i < spreadsheet.data.length; i++) {
spreadsheet.data[i].push({ value: "" });
}
setSpreadsheet({
...spreadsheet,
data: spreadsheetData,
});
};
const SingleSpreadsheet = ({ spreadsheet, setSpreadsheet }: MainAreaProps) => {
return (
<div className='flex-1 overflow-auto p-5'>
{/** -- Spreadsheet title ---*/}
<div className='flex items-start'>
{/** -- Spreadsheet rows and columns---*/}
{/** -- Add column button ---*/}
</div>
{/** -- Add row button ---*/}
</div>
);
};
export default SingleSpreadsheet;
- The `SingleSpreadsheet.tsx` file includes the addRow and addColumn functions.
- The `addRow` function calculates the current number of rows, adds a new row, and updates the spreadsheet accordingly.
- Similarly, the `addColumn` function adds a new column to the spreadsheet.
- The `SingleSpreadsheet` component renders placeholders for the user interface elements.
更新SingleSpreadsheet
元件以呈現電子表格標題、其資料以及新增行和列按鈕。
return (
<div className='flex-1 overflow-auto p-5'>
{/** -- Spreadsheet title ---*/}
<input
type='text'
value={spreadsheet.title}
className='w-full p-2 mb-5 text-center text-2xl font-bold outline-none bg-transparent'
onChange={(e) =>
setSpreadsheet({ ...spreadsheet, title: e.target.value })
}
/>
{/** -- Spreadsheet rows and columns---*/}
<div className='flex items-start'>
<Spreadsheet
data={spreadsheet.data}
onChange={(data) => {
console.log("data", data);
setSpreadsheet({ ...spreadsheet, data: data as any });
}}
/>
{/** -- Add column button ---*/}
<button
className='bg-blue-500 text-white rounded-lg ml-6 w-8 h-8 mt-0.5'
onClick={addColumn}
>
+
</button>
</div>
{/** -- Add row button ---*/}
<button
className='bg-blue-500 text-white rounded-lg w-8 h-8 mt-5 '
onClick={addRow}
>
+
</button>
</div>
);
為了確保一切按預期工作,請在app
資料夾中建立一個types.ts
文件,其中包含應用程式中聲明的所有靜態類型。
export interface Cell {
value: string;
}
export type SpreadsheetRow = Cell[];
export interface SpreadsheetData {
title: string;
rows: SpreadsheetRow[];
}
恭喜! 🎉 您的電子表格應用程式應該可以完美執行。在接下來的部分中,您將了解如何新增 AI 副駕駛,以使用 CopilotKit 自動執行各種任務。
在這裡,您將學習如何將 AI 副駕駛加入到電子表格應用程式,以使用 CopilotKit 自動執行複雜的操作。
CopilotKit 提供前端和後端套件。它們使您能夠插入 React 狀態並使用 AI 代理在後端處理應用程式資料。
首先,我們將 CopilotKit React 元件新增到應用程式前端。
在app/page.tsx
中,將以下程式碼片段加入Main
元件的頂部。
import "@copilotkit/react-ui/styles.css";
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotSidebar } from "@copilotkit/react-ui";
import { INSTRUCTIONS } from "./instructions";
const HomePage = () => {
return (
<CopilotKit url='/api/copilotkit'>
<CopilotSidebar
instructions={INSTRUCTIONS}
labels={{
initial: "Welcome to the spreadsheet app! How can I help you?",
}}
defaultOpen={true}
clickOutsideToClose={false}
>
<Main />
</CopilotSidebar>
</CopilotKit>
);
};
const Main = () => {
//--- Main component //
};
export default HomePage;
- I imported the CopilotKit, its sidebar component, and CSS file to use its frontend components within the application.
- The [CopilotKit component](https://docs.copilotkit.ai/reference/CopilotKit) accepts a `url` prop that represents the API server route where CopilotKit will be configured.
- The Copilot component also renders the [CopilotSidebar component](https://docs.copilotkit.ai/reference/CopilotSidebar) , allowing users to provide custom instructions to the AI copilot within the application.
- Lastly, you can export the `HomePage` component containing the `CopilotSidebar` and the `Main` components.
從上面的程式碼片段中,您會注意到CopilotSidebar
元件有一個instructions
屬性。此屬性使您能夠為 CopilotKit 提供額外的上下文或指導。
因此,在app
資料夾中建立instructions.ts
檔案並將這些命令複製到該檔案中。
接下來,您需要將 CopilotKit 插入應用程式的狀態以存取應用程式的資料。為了實現這一點,CopilotKit 提供了兩個鉤子: useCopilotAction和useMakeCopilotReadable 。
useCopilotAction掛鉤可讓您定義 CopilotKit 執行的動作。它接受包含以下參數的物件:
name
- 操作的名稱。
description
- 操作的描述。
parameters
- 包含所需參數清單的陣列。
render
- 預設的自訂函數或字串。
handler
- 由操作觸發的可執行函數。
useCopilotAction({
name: "sayHello",
description: "Say hello to someone.",
parameters: [
{
name: "name",
type: "string",
description: "name of the person to say greet",
},
],
render: "Process greeting message...",
handler: async ({ name }) => {
alert(`Hello, ${name}!`);
},
});
useMakeCopilotReadable掛鉤向 CopilotKit 提供應用程式狀態。
import { useMakeCopilotReadable } from "@copilotkit/react-core";
const appState = ...;
useMakeCopilotReadable(JSON.stringify(appState));
現在,讓我們回到電子表格應用程式。在SingleSpreadsheet
元件中,將應用程式狀態傳遞到 CopilotKit 中,如下所示。
import {
useCopilotAction,
useMakeCopilotReadable,
} from "@copilotkit/react-core";
const SingleSpreadsheet = ({ spreadsheet, setSpreadsheet }: MainAreaProps) => {
//👇🏻 hook for providing the application state
useMakeCopilotReadable(
"This is the current spreadsheet: " + JSON.stringify(spreadsheet)
);
// --- other lines of code
};
接下來,您需要在SingleSpreadsheet
元件中新增兩個操作,該元件在使用者更新電子表格資料並使用 CopilotKit 新增資料行時執行。
在繼續之前,請在app
資料夾中建立一個包含canonicalSpreadsheetData.ts
檔案的utils
資料夾。
cd app
mkdir utils && cd utils
touch canonicalSpreadsheetData.ts
將下面的程式碼片段複製到檔案中。它接受對電子表格所做的更新,並將其轉換為電子表格中資料行所需的格式。
import { SpreadsheetRow } from "../types"
export interface RowLike {
cells: CellLike[] | undefined;
}
export interface CellLike {
value: string;
}
export function canonicalSpreadsheetData(
rows: RowLike[] | undefined
): SpreadsheetRow[] {
const canonicalRows: SpreadsheetRow[] = [];
for (const row of rows || []) {
const canonicalRow: SpreadsheetRow = [];
for (const cell of row.cells || []) {
canonicalRow.push({value: cell.value});
}
canonicalRows.push(canonicalRow);
}
return canonicalRows;
}
現在,讓我們使用SingleSpreadsheet
元件中的useCopilotAction
掛鉤建立操作。複製下面的第一個操作:
import { canonicalSpreadsheetData } from "../utils/canonicalSpreadsheetData";
import { PreviewSpreadsheetChanges } from "./PreviewSpreadsheetChanges";
import { SpreadsheetData, SpreadsheetRow } from "../types";
import { useCopilotAction } from "@copilotkit/react-core";
useCopilotAction({
name: "suggestSpreadsheetOverride",
description: "Suggest an override of the current spreadsheet",
parameters: [
{
name: "rows",
type: "object[]",
description: "The rows of the spreadsheet",
attributes: [
{
name: "cells",
type: "object[]",
description: "The cells of the row",
attributes: [
{
name: "value",
type: "string",
description: "The value of the cell",
},
],
},
],
},
{
name: "title",
type: "string",
description: "The title of the spreadsheet",
required: false,
},
],
render: (props) => {
const { rows } = props.args
const newRows = canonicalSpreadsheetData(rows);
return (
<PreviewSpreadsheetChanges
preCommitTitle="Replace contents"
postCommitTitle="Changes committed"
newRows={newRows}
commit={(rows) => {
const updatedSpreadsheet: SpreadsheetData = {
title: spreadsheet.title,
rows: rows,
};
setSpreadsheet(updatedSpreadsheet);
}}
/>
)
},
handler: ({ rows, title }) => {
// Do nothing.
// The preview component will optionally handle committing the changes.
},
});
上面的程式碼片段執行使用者的任務並使用 CopilotKit 產生 UI 功能顯示結果預覽。 suggestSpreadsheetOverride
操作傳回一個自訂元件 ( PreviewSpreadsheetChanges
),該元件接受以下內容為 props:
要新增到電子表格的新資料行,
一些文字 - preCommitTitle
和postCommitTitle
,以及
更新電子表格的commit
函數。
您很快就會學會如何使用它們。
在元件資料夾中建立PreviewSpreadsheetChanges
元件,並將下列程式碼片段複製到檔案中:
import { CheckCircleIcon } from '@heroicons/react/20/solid'
import { SpreadsheetRow } from '../types';
import { useState } from 'react';
import Spreadsheet from 'react-spreadsheet';
export interface PreviewSpreadsheetChanges {
preCommitTitle: string;
postCommitTitle: string;
newRows: SpreadsheetRow[];
commit: (rows: SpreadsheetRow[]) => void;
}
export function PreviewSpreadsheetChanges(props: PreviewSpreadsheetChanges) {
const [changesCommitted, setChangesCommitted] = useState(false);
const commitChangesButton = () => {
return (
<button
className="inline-flex items-center gap-x-2 rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
onClick={() => {
props.commit(props.newRows);
setChangesCommitted(true);
}}
>
{props.preCommitTitle}
</button>
);
}
const changesCommittedButtonPlaceholder = () => {
return (
<button
className=" inline-flex items-center gap-x-2 rounded-md bg-gray-100 px-3.5 py-2.5 text-sm font-semibold text-green-600 shadow-sm cursor-not-allowed"
disabled
>
{props.postCommitTitle}
<CheckCircleIcon className="-mr-0.5 h-5 w-5" aria-hidden="true" />
</button>
);
}
return (
<div className="flex flex-col">
<Spreadsheet
data={props.newRows}
/>
<div className="mt-5">
{changesCommitted ? changesCommittedButtonPlaceholder() : commitChangesButton() }
</div>
</div>
);
}
PreviewSpreadsheetChanges
元件傳回一個電子表格,其中包含從請求產生的資料和一個按鈕(帶有preCommitTitle
文字),該按鈕允許您將這些變更提交到主電子表格表(透過觸發commit
函數)。這可確保您在將結果新增至電子表格之前對結果感到滿意。
將下面的第二個操作加入到SingleSpreadsheet
元件。
useCopilotAction({
name: "appendToSpreadsheet",
description: "Append rows to the current spreadsheet",
parameters: [
{
name: "rows",
type: "object[]",
description: "The new rows of the spreadsheet",
attributes: [
{
name: "cells",
type: "object[]",
description: "The cells of the row",
attributes: [
{
name: "value",
type: "string",
description: "The value of the cell",
},
],
},
],
},
],
render: (props) => {
const status = props.status;
const { rows } = props.args
const newRows = canonicalSpreadsheetData(rows);
return (
<div>
<p>Status: {status}</p>
<Spreadsheet
data={newRows}
/>
</div>
)
},
handler: ({ rows }) => {
const canonicalRows = canonicalSpreadsheetData(rows);
const updatedSpreadsheet: SpreadsheetData = {
title: spreadsheet.title,
rows: [...spreadsheet.rows, ...canonicalRows],
};
setSpreadsheet(updatedSpreadsheet);
},
});
appendToSpreadsheet
操作透過在電子表格中新增資料行來更新電子表格。
以下是操作的簡短示範:
[https://www.youtube.com/watch?v=kGQ9xl5mSoQ]
最後,在Main
元件中新增一個操作,以便在使用者提供指令時建立一個新的電子表格。
useCopilotAction({
name: "createSpreadsheet",
description: "Create a new spreadsheet",
parameters: [
{
name: "rows",
type: "object[]",
description: "The rows of the spreadsheet",
attributes: [
{
name: "cells",
type: "object[]",
description: "The cells of the row",
attributes: [
{
name: "value",
type: "string",
description: "The value of the cell",
},
],
},
],
},
{
name: "title",
type: "string",
description: "The title of the spreadsheet",
},
],
render: (props) => {
const { rows, title } = props.args;
const newRows = canonicalSpreadsheetData(rows);
return (
<PreviewSpreadsheetChanges
preCommitTitle="Create spreadsheet"
postCommitTitle="Spreadsheet created"
newRows={newRows}
commit={ (rows) => {
const newSpreadsheet: SpreadsheetData = {
title: title || "Untitled Spreadsheet",
rows: rows,
};
setSpreadsheets((prev) => [...prev, newSpreadsheet]);
setSelectedSpreadsheetIndex(spreadsheets.length);
}}
/>
);
},
handler: ({ rows, title }) => {
// Do nothing.
// The preview component will optionally handle committing the changes.
},
});
恭喜!您已成功為此應用程式建立所需的操作。現在,讓我們將應用程式連接到 Copilotkit 後端。
在本教程的開頭,我向您介紹了Tavily AI (一個為 AI 代理提供知識的搜尋引擎)和 OpenAI(一個使我們能夠存取GPT-4 AI 模型的庫)。
在本部分中,您將了解如何取得 Tavily 和 OpenAI API 金鑰並將它們整合到 CopilotKit 中以建立高級智慧應用程式。
造訪Tavily AI 網站,建立一個帳戶,然後將您的 API 金鑰複製到您專案的.env.local
檔案中。
接下來,導覽至OpenAI 開發者平台,建立 API 金鑰,並將其複製到.env.local
檔案中。
以下是.env.local
檔案的預覽,其中包括 API 金鑰並指定要使用的 OpenAI 模型。請注意,存取 GPT-4 模型需要訂閱 ChatGPT Plus 。
TAVILY_API_KEY=<your_API_key>
OPENAI_MODEL=gpt-4-1106-preview
OPENAI_API_KEY=<your_API_key>
回到我們的應用程式,您需要為 Copilot 建立 API 路由。因此,建立一個包含route.ts
的api/copilotkit
資料夾並新增一個tavily.ts
檔案。
cd app
mkdir api && cd api
mkdir copilotkit && cd copilotkit
touch route.ts tavily.ts
在tavily.ts
檔案中建立一個函數,該函數接受使用者的查詢,使用 Tavily Search API 對查詢進行研究,並使用OpenAI GPT-4 模型總結結果。
import OpenAI from "openai";
export async function research(query: string) {
//👇🏻 sends the request to the Tavily Search API
const response = await fetch("https://api.tavily.com/search", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
api_key: process.env.TAVILY_API_KEY,
query,
search_depth: "basic",
include_answer: true,
include_images: false,
include_raw_content: false,
max_results: 20,
}),
});
//👇🏻 the response
const responseJson = await response.json();
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! });
//👇🏻 passes the response into the OpenAI GPT-4 model
const completion = await openai.chat.completions.create({
messages: [
{
role: "system",
content: `Summarize the following JSON to answer the research query \`"${query}"\`: ${JSON.stringify(
responseJson
)} in plain English.`,
},
],
model: process.env.OPENAI_MODEL,
});
//👇🏻 returns the result
return completion.choices[0].message.content;
}
最後,您可以透過將使用者的查詢傳遞到函數中並向 CopilotKit 提供其回應來執行route.ts
檔案中的research
函數。
import { CopilotBackend, OpenAIAdapter } from "@copilotkit/backend";
import { Action } from "@copilotkit/shared";
import { research } from "./tavily";
//👇🏻 carries out a research on the user's query
const researchAction: Action<any> = {
name: "research",
description: "Call this function to conduct research on a certain query.",
parameters: [
{
name: "query",
type: "string",
description:
"The query for doing research. 5 characters or longer. Might be multiple words",
},
],
handler: async ({ query }) => {
console.log("Research query: ", query);
const result = await research(query);
console.log("Research result: ", result);
return result;
},
};
export async function POST(req: Request): Promise<Response> {
const actions: Action<any>[] = [];
if (process.env.TAVILY_API_KEY!) {
actions.push(researchAction);
}
const copilotKit = new CopilotBackend({
actions: actions,
});
const openaiModel = process.env.OPENAI_MODEL;
return copilotKit.response(req, new OpenAIAdapter({ model: openaiModel }));
}
恭喜!您已完成本教學的專案。
CopilotKit是一款令人難以置信的工具,可讓您在幾分鐘內將 AI Copilot 加入到您的產品中。無論您是對人工智慧聊天機器人和助理感興趣,還是對複雜任務的自動化感興趣,CopilotKit 都能讓您輕鬆實現。
如果您需要建立 AI 產品或將 AI 工具整合到您的軟體應用程式中,您應該考慮 CopilotKit。
您可以在 GitHub 上找到本教學的源程式碼:
https://github.com/CopilotKit/spreadsheet-demo
感謝您的閱讀!
原文出處:https://dev.to/copilotkit/build-an-ai-powered-spreadsheet-app-nextjs-langchain-copilotkit-109d