CopilotKit: 给应用添加AI能力

大多数 AI 集成都过度设计了。即使是最基础的添加 AI 助手教程也充斥着 LangGraph 工作流和 FastAPI 服务器。但你根本不需要这些。

CopilotKit: 给应用添加AI能力
微信 ezpoda免费咨询:AI编程 | AI模型微调| AI私有化部署
AI工具导航 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo

无需外部代理框架,只需要你选择的 LLM。

大多数 AI 集成都过度设计了。

即使是最基础的添加 AI 助手教程也充斥着 LangGraph 工作流和 FastAPI 服务器。但你根本不需要这些。

今天,我们将不使用任何外部代理框架或编排层来构建一个 AI。它能知道你的应用里有什么,并且能真正执行操作。

你还将学习如何实现生成式 UI 模式,这种模式可以渲染实际的组件,而不仅仅是文本响应。

让我们开始吧。

1、为什么 AI 集成会变得复杂

给应用添加 AI 听起来很简单,直到你真正尝试。

当你超越基本的聊天界面时,你立即会遇到一个根本问题:LLM 本身不能做任何事情。它们只能生成文本。

要构建一个真正能执行操作的 AI——读取应用状态、更新数据、调用 API——你需要自己构建这些"管道"。

这些管道就是 AI 代理。核心上,每个代理都运行相同的循环:

  1. 观察:获取上下文(用户输入、工具结果、记忆)
  2. 推理:决定下一步做什么
  3. 行动:调用工具、写文件、访问 API,无论需要什么
无

理论上很简单。但实际上,你最终要编写管理这个循环的编排层、向 LLM 暴露应用函数的工具注册表、跟踪代理知道什么的state管理,以及处理模型出错时的错误处理。

context = [initial_event]
while True:
  next_step = await llm.determine_next_step(context)
  context.append(next_step)
  if next_step.intent == "done":
    return next_step.final_answer
  result = await execute_step(next_step)
  context.append(result)

在完整的代理设置中,LLM 处于中心位置——跨数据源、工具、模型和外部服务进行编排。

无

这就是为什么教程都教 LangGraph 和 FastAPI 服务器。对于复杂的自动化管道,这种复杂性是合理的。

但如果你的目标只是在产品中添加一个 AI 助手——一个能理解你的 UI、读取你的数据并能代表用户执行操作的 AI,你实际上在构建大量基础设施来解决一个更小的问题。

CopilotKit 处理循环、上下文、流式传输和前端集成,所以你不必自己连接任何这些。

无

对于本教程,我们将使用 CopilotKit 但不使用任何外部代理框架,只需要 hooks 和你选择的 LLM。

2、CopilotKit 实际做了什么

CopilotKit 是一个开源框架,用于向你的应用添加 AI 代理,它抽象了你刚才读到的所有内容。

你不再需要构建自己的代理循环、工具注册表和流式传输层,而是获得一组直接插入现有前端的 hooks 和预构建组件。

心智模型很简单,你向 AI 提供两样东西:

  • 你应用的状态 — 让它理解屏幕上有什么以及用户正在操作什么
  • 一组工具 — 让它能够真正做一些有用的事情,而不仅仅是回复文本

实际上,这映射到两个 hooks:

  • useAgentContext — 与 AI 共享应用状态。你传入的任何东西都会成为 LLM 上下文的一部分:当前用户、选中的项目、加载的数据——AI 知道你应用知道的任何事情。
  • useFrontendTool — 定义 AI 可以触发的操作。你给它一个名称、描述和处理程序。LLM 根据用户的问题决定何时调用它。

结果是,向你的应用添加 AI 助手变成了一个前端问题,而不是基础设施问题。

你的 UI、代理、工具都在一个统一的交互循环中。

无

3、如何连接所有内容(1 分钟)

你可以跟随官方文档或直接跟着这个教程。我会详细解释每个部分的作用和原因。

我使用 Next.js 和 TypeScript,但这适用于任何 React/Angular 框架!

// creates a nextjs app  
npx create-next-app@latest .
无

安装你需要的三个 CopilotKit 包:

npm install @copilotkit/react-core @copilotkit/react-ui @copilotkit/runtime
  • @copilotkit/react-core/v2 : hooks 和内置聊天组件
  • @copilotkit/react-ui/v2 : 样式
  • @copilotkit/runtime : 后端运行时和 LLM 适配器
无

app/api/copilotkit/route.ts 创建 API 路由。

这是接收来自 UI 的消息、通过代理循环运行它们并流式传输响应的单一端点。

  • BuiltInAgent 运行观察 → 推理 → 行动循环
  • CopilotRuntime 管理会话、流式传输和工具执行

这就是你的全部后端。

import { CopilotRuntime, copilotRuntimeNextJSAppRouterEndpoint } from "@copilotkit/runtime";
import { BuiltInAgent } from "@copilotkit/runtime/v2";
import { NextRequest } from "next/server";

const builtInAgent = new BuiltInAgent({
  model: "openai:gpt-5",
  // apiKey: process.env.OPENAI_API_KEY,
});
const runtime = new CopilotRuntime({
  agents: { default: builtInAgent },
});
export const POST = async (req: NextRequest) => {
  const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
    runtime,
    endpoint: "/api/copilotkit",
  });
  return handleRequest(req);
};

在根目录创建 .env.local 并添加你的 OpenAI API key

OPENAI_API_KEY=sk-proj-...

如果你想切换到任何其他 LLM 提供商,只需要将修改后的字符串传递给 API 路由中的 BuiltInAgent。其他一切都保持不变。

// Anthropic
const builtInAgent = new BuiltInAgent({ model: "anthropic:claude-sonnet-4-5" });

// Google
const builtInAgent = new BuiltInAgent({ model: "google:gemini-2.0-flash" });

将匹配的 key 添加到 .env.local 就完成了。对于 Azure OpenAI、AWS Bedrock 或 Ollama 等自定义模型,请查看模型选择文档

现在,通过在 app/layout.tsx 中包装你的应用来连接前端到后端。

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

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <CopilotKit runtimeUrl="/api/copilotkit">
          {children}
        </CopilotKit>
      </body>
    </html>
  );
}

CopilotKit 是其下每个 hook 依赖的上下文提供者。runtimeUrl 指向你刚创建的路由。

有很多内置组件,如 CopilotSidebarCopilotChatCopilotPopup。对于这个例子,我将添加聊天侧边栏。

import { CopilotSidebar } from "@copilotkit/react-core/v2";

export default function Page() {
  return (
    <main>
      <h1>Your App</h1>
      <CopilotSidebar />
    </main>
  );
}

运行 npm run dev,你现在就拥有了一个可用的 AI 侧边栏助手!🎉

无

它可以回应任何问题,但它完全不知道你应用里实际有什么。它不知道你的数据、你的 UI 状态或用户正在查看的内容。接下来的两个步骤将解决这个问题。

4、给 AI 提供上下文

默认情况下,AI 对你应用里的内容一无所知。你可以问它"我在食物上花了多少钱?",它完全不知道——它只看到对话,而不是你应用的状态。

useAgentContext 解决了这个问题。它在每个回合都将你的 React 状态推送到代理的上下文窗口中,这样 AI 始终有你的 UI 中当前内容的快照。

让我们构建一个简单的支出追踪器来看看这是如何工作的。

"use client";
import { useState } from "react";
import { useAgentContext, CopilotSidebar } from "@copilotkit/react-core/v2";

type Expense = {
  id: number;
  description: string;
  amount: number;
  category: string;
};
const initialExpenses: Expense[] = [
  { id: 1, description: "Groceries", amount: 85, category: "Food" },
  { id: 2, description: "Netflix", amount: 15, category: "Entertainment" },
  { id: 3, description: "Uber", amount: 22, category: "Transport" },
];
export default function Page() {
  const [expenses, setExpenses] = useState<Expense[]>(initialExpenses);
  useAgentContext({
    description: "The user's current expense list. Each item has an id, description, amount in dollars, and category.",
    value: expenses,
  });
  return (
    <main className="p-8">
      <h1 className="text-2xl font-bold mb-4">My Expenses</h1>
      <ul className="space-y-2">
        {expenses.map((e) => (
          <li key={e.id} className="flex justify-between border-b py-2">
            <span>
              {e.description}{" "}
              <span className="text-gray-400 text-sm">({e.category})</span>
            </span>
            <span>${e.amount}</span>
          </li>
        ))}
      </ul>
      <CopilotSidebar />
    </main>
  );
}

expenses 是你正常的 React 状态。useAgentContext 接收它并在每个回合将其注入 LLM 的上下文。

现在,如果用户问"我最大的支出是什么?",AI 正在查看与你的 UI 渲染的相同列表。

无

5、让 AI 执行操作

感知是一回事。能够行动才是让代理真正有用的关键。

useFrontendTool 让你给 AI 一组它可以触发的操作。你用一个名称、一个描述和一个处理程序定义一个工具——LLM 根据用户的问题决定何时调用它。

LLM 读取 description 字段来理解每个工具的作用以及每个参数期望什么,所以写清楚这些,模型就能从随意的对话输入中准确地填充它们。

在同一页面内添加 useFrontendTool

parameters 接受一个 Zod schema(一个 TypeScript 优先的验证库)。如果你以前没用过,概念很简单:用 z.object({...}) 定义你的数据形状,每个字段都有一个 .describe() 调用,告诉 LLM 这意味着什么。

npm install zod 安装它。

"use client";
import { useState } from "react";
import { useAgentContext, useFrontendTool, CopilotSidebar } from "@copilotkit/react-core/v2";
import { z } from "zod";

// ... type and initialExpenses stay the same
export default function Page() {
  const [expenses, setExpenses] = useState<Expense[]>(initialExpenses);
  // ... useAgentContext
  useFrontendTool({
  name: "addExpense",
  description:
    "Add a new expense when the user mentions spending money on something.",
  parameters: z.object({
    description: z
      .string()
      .describe("What the expense was for, e.g. Lunch, Taxi, Coffee"),
    amount: z.number().describe("How much was spent in dollars"),
    category: z
      .string()
      .describe("Category: Food, Transport, Entertainment, Health, or Other"),
  }),
  handler: async ({ description, amount, category }) => {
    setExpenses((prev) => [
      ...prev,
      { id: Date.now(), description, amount, category },
    ]);
  },
});
  return (
    // ... remains the same
  );
}

你可以发送一个示例查询,比如"我昨晚晚餐花了 40 美元"。代理用 { description: "Dinner", amount: 40, category: "Food" } 调用 addExpense,你的处理程序更新 expenses,新项目立即出现在列表中。

无

这就是观察 → 推理 → 行动的循环端到端运行。

代理通过 useAgentContext 观察你的消息和你当前的支出,推断出 addExpense 是正确的调用,然后用正确的参数调用你的处理程序来执行操作。CopilotKit 处理了中间的一切。

6、奖励:生成式 UI

AI 可以读取你的数据并更新它。但到目前为止,它只能以文本形式回复。

生成式 UI 是一个新想法:代理不是描述结果,而是渲染实际的 UI。例如,如果有人问他们的支出情况,应用会显示一个分类卡片。

CopilotKit 通过 useFrontendTool 上的 render 属性支持这个功能。不是文本回复,工具返回一个 React 组件——在聊天中内联渲染,使用你自己的设计系统。

让我们在同一组件中添加一个摘要工具。

import { ToolCallStatus, useFrontendTool } from "@copilotkit/react-core/v2";

useFrontendTool({
  name: "showSpendingSummary",
  description:
    "Call this when the user asks for a summary or overview of their expenses.",
  parameters: z.object({}),
  handler: async () => {
    const summary = expenses.reduce(
      (acc, e) => {
        acc[e.category] = (acc[e.category] ?? 0) + e.amount;
        return acc;
      },
      {} as Record<string, number>,
    );
    const total = expenses.reduce((sum, e) => sum + e.amount, 0);
    return JSON.stringify({ summary, total });
  },
  render: ({ result, status }) => {
    return (
      <div className="rounded-lg border p-4 mt-2 space-y-3">
        <p className="font-semibold text-sm">
          {status === ToolCallStatus.InProgress ? "Calculating..." : "Spending Breakdown"}
        </p>
        {status === ToolCallStatus.Complete && result && (
          <>
            {Object.entries(
              (JSON.parse(result) as { summary: Record<string, number>; total: number }).summary
            ).map(([category, amount]) => (
              <div key={category} className="flex justify-between text-sm">
                <span className="text-gray-600">{category}</span>
                <span className="font-medium">${amount}</span>
              </div>
            ))}
            <div className="flex justify-between text-sm font-semibold border-t pt-2">
              <span>Total</span>
              <span>${(JSON.parse(result) as { summary: Record<string, number>; total: number }).total}</span>
            </div>
          </>
        )}
      </div>
    );
  },
});

这里发生了什么:

  • parameters: z.object({}) 是空的。LLM 不需要传递任何东西,它只需要决定何时调用工具
  • handler 做实际的工作,从 expenses 状态计算类别总计并返回它们
  • ToolCallStatus 给你工具调用的确切生命周期状态
  • result 是你的处理程序返回的 JSON 字符串,在 render 内部解析回对象

当你问"总结我的支出"时,LLM 读取查询,查看上下文,识别出 showSpendingSummary 是要调用的正确工具并触发它。

无

这就是模式:LLM 决定何时行动,你的代码做工作,你的组件显示结果。

这个最小支出追踪器的工作实现在这个 GitHub 仓库example/basic 分支中可用。

main 分支用完整的看板进一步延伸了相同的模式:

  • 通过询问 AI 在列之间移动任务
  • 用自然语言添加、删除和重新分配任务
  • 用生成式 UI 获取可视化看板摘要

这里是演示!

大多数感觉神奇的 AI 功能并非如此。它们只是一个知道屏幕上有什么并且连接了一些操作的应用。就这样。

困难的部分一直是基础设施。事实证明,它不一定必须如此。


原文链接: Add AI to your app in 5 minutes汇智网翻译整理,版权归原作者所有,转载需标明出处