手把手教你实现仿 Claude AI 项目

207 阅读2分钟

hello 大家好,我是张XX,这篇文章打算跟大家聊聊如何用 AI 生成 RevealJS Html 演示文稿代码并预览。如果大家有任何问题,欢迎私信我。

最近 claude 非常火,聊天就能帮你写代码,而且可以直接预览代码生成的效果,在前端圈掀起了不小的波澜。

本文将教会你如何仿照 claude 基于对话创建代码的效果,来实现对话生成 RevealJS 的 HTML 代码并预览的功能。

项目的 Demo 效果如下:

99999.png

项目源码地址见:ai-ppt

你将学到以下内容:

  • 如何构建 AI 聊天组件
  • 如何预览生成的 html 代码

本文的内容将基于 gemini flash 模型。

0. 前置准备

本教程需要 React 的基本知识。

你还需要在 Google 的 Gemini 平台上拥有一个帐户。 你可以在这里创建一个

1. 项目初始化

首先创建 next 项目:

npx create-next-app@latest revealjs-ai-tutorial

进入目录

cd revealjs-ai-tutorial

2. 实现 chat 组件

2.1 Message UI 组件

首先实现 Message UI 组件,新建文件 /components/chat.tsx, 截取部分组件代码如下:

export default function Chat() {
  return (
    <div id="chat-container">
     {messages.map((message, index) => (
       <div className="py-2 px-4 shadow-sm whitespace-pre-wrap" key={index}>
        {message.content.map((content, id) => {
          if (content.type === 'text') {
            return <p key={content.text} className="flex-1">{content.text}</p>
          }
        })}
        {message.meta &&
          <div className="mt-4 flex justify-start items-start border rounded-md">
            <div className="p-2 self-stretch border-r w-14 flex items-center justify-center">
              <Terminal strokeWidth={2} className="text-[#FF8800]"/>
            </div>
            <div className="p-2 flex flex-col space-y-1 justify-start items-start min-w-[100px]">
              <span className="font-bold font-sans text-sm text-primary">{message.meta.title}</span>
              <span className="font-sans text-sm">{message.meta.description}</span>
            </div>
          </div>
        }
      </div>
   ))}
   </div>
  )
}

组件效果如下图所示:

image.png

可以发现,组件是基于 message 这个数据的内容渲染的,而这个 message 正是大模型提供的数据结构,因此下一步我们需要实现访问大模型的接口,也是整个核心所在。

2.2 创建 Chat API

首先我们在 app 目录下新建 api/chat/route.ts 目录,新建 /api/chat 接口:


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

  // todo: 此处需实现调用 Gemini 模型
}

我们创建了 /api/chat 接口,接下来就是要在接口中实现对 Gemini 模型的调用。 在这里我们需要用到一个 npm 库 ai-sdk, 它为开发者封装了各种 AI 服务(如语言模型,生成式AI, 图像识别等), 极大的简化的 AI 模型的集成流程,使开发者能够快速的将 AI 功能嵌入应用中。

ai-sdk 目前支持多个主流的 AI 服务,如 OpenAI, Anthropic(Claude模型), Google Gemini 等。 接下来我们就使用 ai-sdk 来访问 Google Gemini模型。

我们来看一下具体的 ai-sdk 使用示例:

import { createGoogleGenerativeAI } from '@ai-sdk/google';

export async function POST(req) {
  const { messages } = await req.json()
  
  // 创建 Google Generative AI 客户端,使用的 google 模型为 gemini-1.5-flash, 这是一个免费模型
  const client = createGoogleGenerativeAI({ apiKey: process.env.GEMINI_API_KEY})
  ('models/gemini-1.5-flash-latest')
  
  const stream = await streamObject({
    model: client,
    schema: artifactSchema,
    system: `
      Generate a visually appealing reveal.js presentation in HTML.
      The presentation should include the following slides: appealing cover, bullet points with links, conclusion and end page.
      more than 6 slides.
      use the template: ${htmlTemplate}
    `,
    messages
  })

  return stream.toTextStreamResponse()
}

代码中的 streamObject 是一个函数,用于创建AI 生成内容的流式对象,这个流会从 Google Generative AI 模型中获取生成结果。

该函数接受一个对象作为参数,其中包含以下关键项:

  • model: 将前面创建的 client 传递给 model 参数,这个模型将用于生成文本或处理自然语言任务。

  • schema:是为生成的演示文稿设定的格式规范。具体包括commentary(描述生成步骤),title(对生成结果的简短标题), description(对生成内容的描述), code(生成的 html 内容)。 这些字段信息将携带在返回的字段 中。

  • system: 是传递给 AI 模型的系统提示(prompt)

  • messages:用户输入的上下文信息,这些消息将作为输入帮助模型生成合适的内容。

最后通过 stream.toTextStreamResponse() 返回流处理式响应。因为响应是以流的形式传输的,客户端可以在逐步接收数据时逐步处理,而不需要等待整个响应完成。这种流式处理方式的好处是能够处理大段文本输出,适合生成较大内容、需要持续响应或有实时更新需求的场景。

2.3 调用 Chat API

在 2.1 章节中我们实现了 Chat 组件的 UI 部分,但 message 数据如何获取,数据结构是什么,都尚未交代,本章就来说明这部分内容。

import { experimental_useObject as useObject } from 'ai/react'

export default function Chat() {
  const [messages, setMessages] = useState<ChatMessage[]>([])
  const { object, submit } = useObject({
    api: '/api/chat'
  })

  useEffect(() => {
    if (object) {
      setMessages([
        ...message, {
          content: [{
            type: 'text',
            text: object.commentary
          }, {
            type: 'code',
            text: object.code
          }],
          meta: {
            title: object.title,
            description: object.description
          }
        }
      ])
  }, [object])
  
  const handleSubmit = () => {
      submit({
        messages: addMessage({role: 'user', content}),
      })
  }
  
  return (
   // ...
  )
}

ai/react 模块导入 experimental_useObject,并将其重命名为 useObject。这个 hook 用于与后端进行交互。

调用 useObject hook, 其中 api 字段指向后端的 /api/chat 接口。并返回 objectsubmit 函数。

  • object 是从后端 API 获取的对象,包含聊天的响应内容
  • submit 用于提交用户问题时,向后端发送请求。

使用 useEffect hook,当 object 发生变化时执行该副作用。如果 object 存在,将聊天响应的内容添加到 messages 数组中。

至此,我们实现了调用 api/chat 接口,访问大模型,并将返回的字段渲染到 UI 上。接下来就是如何预览生成的 html 代码。

3. 预览生成的 RevealJS HTML

预览的容器是一个 iframe, 在这里利用了 iframe 的 dataURI 属性,这个 URI 允许浏览器将一段 HTML 内容直接嵌入到页面中。

export function Preview({
  result,
}) {
  if (!result) return null

  const encodedHTML = encodeURIComponent(result);
  const dataURI = `data:text/html;charset=utf-8,${encodedHTML}`;

  return (
    <div className="w-full h-full">
      <iframe
        className="h-full w-full"
        loading="lazy"
        src= {dataURI}
      />
    </div>
  )
}

总结

我们学习了如何基于 ai-sdk 实现聊天对话,并将生成的 RevealJS Html 进行预览。 感谢大家对本文的支持~欢迎点赞收藏,在评论区留下你的高见 🌹🌹🌹