使用 Nextjs 和 AI SDK 实现 ChatGPT 聊天窗口

힘센캥거루
2025년 1월 3일(수정됨)
17
nextjs

最近我努力装饰博客并添加功能。

重新看了我的代码,发现完全不记得是怎么写的了。

今天再次意识到学习记录(TIL)的重要性。

所以我再次记录下来。

使用 Nextjs 和 AI SDK 实现 ChatGPT 聊天窗口-1

1. ChatGPT API 官方文档

首先,基本设置可以查看 官方文档

步骤如下。

  1. 将 API 密钥存为环境变量。在 Nextjs 环境下,在根文件夹创建 .env.local 文件,内容为 OPENAI_API_KEY="api_key_here"

  2. 使用 npm install openai 安装 openai 库。

  3. 编写代码。

官方文档中提供的代码如下。

import OpenAI from "openai";
const openai = new OpenAI();

const completion = await openai.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    {
      role: "user",
      content: "Write a haiku about recursion in programming.",
    },
  ],
});

console.log(completion.choices[0].message);

在 Nextjs 中用 yarn dev 运行可以看到效果不错。

所以想通过 api 路由实现,不过问题没那么简单。

2. 尝试一下

在开发初期,先忽略风格,直接实现功能更快。

简单创建一个表单,用 FormData 和 fetch 搭建一个简易聊天窗口。

"use client";

import { useRef, useState } from "react";

export default function Page() {
  const [value, setValue] = useState("");
  const messages = useRef<HTMLDivElement>(null);
  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setValue("");
    const me = document.createElement("p");
    me.textContent = value;
    messages.current?.appendChild(me);
    const formData = new FormData();
    formData.append("content", value);
    const res = await fetch("/api/test", {
      method: "POST",
      body: formData,
    });
    const resJson = await res.json();
    const p = document.createElement("p");
    p.textContent = resJson.content;
    messages.current?.appendChild(p);
  }
  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={value}
          onChange={(e) => setValue(e.currentTarget.value)}
        />
        <button type="submit"></button>
      </form>
      <div ref={messages} className="border-2">
        <p>聊天窗口</p>
      </div>
    </div>
  );
}

这样可以看到一个简单的输入框和聊天窗口。

输入内容后,利用 fetch 将 formdata 发送到 '/api/test' 并处理响应。

使用 Nextjs 和 AI SDK 实现 ChatGPT 聊天窗口-2

接下来编写 api 端的代码。

import { NextResponse } from "next/server";
import OpenAI from "openai";
const openai = new OpenAI();

export async function POST(req: Request) {
  const formData = await req.formData();
  const completion = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [
      { role: "system", content: "You are a helpful assistant." },
      {
        role: "user",
        content: formData.get("content") as string,
      },
    ],
  });
  console.log(completion.choices[0].message);
  return NextResponse.json(completion.choices[0].message);
}

从请求中解析消息,然后通过 openai 库发送并接收响应。

最后将响应以 json 格式返回。

现在可以进行对话了。

使用 Nextjs 和 AI SDK 实现 ChatGPT 聊天窗口-3

测试时会发现,响应过长需要等待较久。

这里出现了问题。

3. 问题点

问题在于我们的耐心并不强大。

在网络上若响应不够快,我们容易感到疲倦。

使用 Nextjs 和 AI SDK 实现 ChatGPT 聊天窗口-4

在 Chat GPT 中数据以打字形式传输是因为 API 以分块形式响应和输出。

我努力搜索了一些实现方法。

  1. 使用 socket 实现实时聊天

  2. 使用 openai 的流式响应对象

  3. 使用 Vercel 的 AI SDK 实现

其中第 3 种看起来最简单。

4. 试用 Vercel 的 AI SDK

不懂时,搜索与 官方文档是最好的办法。

在上述链接中找到适用于 Nextjs 的组件。

把它直接应用到项目中即可实现流式传输。

首先安装 AI SDK。

yarn add ai @ai-sdk/openai zod

这里的 zod 是用于输入验证的工具。

按以下路径设置路由。

import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";

// Allow streaming responses up to 30 seconds
export const maxDuration = 30;

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai("gpt-4o"),
    messages,
  });

  return result.toDataStreamResponse();
}

代码中的 maxDuration 是最大响应时间。

若在 30 秒内未完成响应,则中断流式传输。

客户端接收数据流对象响应。

接下来是 page 组件。

"use client";

import { useChat } from "ai/react";

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    api: "/api/aisdk",
  });
  return (
    <div className="flex flex-col w-full max-w-md py-24 mx-auto stretch">
      {messages.map((m) => (
        <div key={m.id} className="whitespace-pre-wrap">
          {m.role === "user" ? "User: " : "AI: "}
          {m.content}
        </div>
      ))}

      <form onSubmit={handleSubmit}>
        <input
          className="fixed bottom-0 w-full max-w-md p-2 mb-8 border border-gray-300 rounded shadow-xl"
          value={input}
          placeholder="Say something..."
          onChange={handleInputChange}
        />
      </form>
    </div>
  );
}

原本需要用 useState 和事件处理器复杂处理的东西,通过 useChat 就搞定了。

观察 useChat 中结构分解的变量在何处如何使用。

useChat 内接收多个对象选项。

这里输入了 API 地址。

现在运行看看。

使用 Nextjs 和 AI SDK 实现 ChatGPT 聊天窗口-5

通过简单的两次复制粘贴实现 ChatGPT 流式传输。

官方文档中还有关于创建天气相关 AI 的过程,可供参考。

5. useChat 和 streamText

每个函数的参数可以在官方文档中查到。

我会写一些我用过的。

1. useChat

useChat 内可以输入启动消息。

这样一打开页面消息就会显示。

const { messages, input, handleInputChange, handleSubmit } = useChat({
  api: "/api/aisdk",
  initialMessages: [
    {
      id: "first-message",
      role: "assistant",
      content: "想说什么就输入吧",
    },
  ],
});
使用 Nextjs 和 AI SDK 实现 ChatGPT 聊天窗口-6

2. streamText

在 streamText 中可以定义需要在响应中使用的函数。

如果对 ChatGPT 说 “请把 ‘强壮袋鼠’ 变成 ‘倒霉孩子’”,它不会正确回答。

使用 Nextjs 和 AI SDK 实现 ChatGPT 聊天窗口-7

这种情况下可以定义 “倒霉孩子” 函数并放入 streamText 中。

import { openai } from "@ai-sdk/openai";
import { streamText, tool } from "ai";
import { z } from "zod";

// Allow streaming responses up to 30 seconds
export const maxDuration = 30;

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai("gpt-4o"),
    messages,
    tools: {
      reverse: tool({
        description: "将名字变成倒霉孩子",
        parameters: z.object({
          person: z.string().describe("名字"),
        }),
        execute: async ({ person }) => {
          const newPerson = person.split("").reverse().join("");
          return {
            newPerson,
          };
        },
      }),
    },
  });
  return result.toDataStreamResponse();
}

tool 函数内 description 是函数说明,parameters 在对话中寻找对应名字内容并作为参数传入,execute 中处理后返回。

这样编写代码后,再进行提问时,会把输入字符串反转后返回。

使用 Nextjs 和 AI SDK 实现 ChatGPT 聊天窗口-8

还可以定义回答的最大长度。

例如指定 maxTokens 为下列位置时,回答就会变得非常简短。

 const result = streamText({
    ...
    maxTokens:10,
    ...
  });
使用 Nextjs 和 AI SDK 实现 ChatGPT 聊天窗口-9

6. 结论

本以为创建使用 Chat GPT 的服务会非常困难,但实际并不是。

有很多好用的库,只要有心就可以制作。

希望尽快制作教育用聊天机器人并投入服务。

댓글을 불러오는 중...