AI 代理聊天机器人

构建一个简单的 AI 代理聊天机器人,能读取和编辑 Tiptap 文档。

查看GitHub 上的源码

技术栈

安装

创建一个 Next.js 项目:

npx create-next-app@latest ai-agent-chatbot

安装核心的 Tiptap 包以及用于 OpenAI 的 Vercel AI SDK

npm install @tiptap/react @tiptap/starter-kit ai @ai-sdk/react @ai-sdk/openai

安装 Tiptap AI 工具包以及 Vercel AI SDK 的工具定义。

专业包

AI 工具包是一个专业版包。在安装之前,请按照私有注册表指南设置对私有 NPM 注册表的访问权限。

npm install @tiptap-pro/client-ai-toolkit @tiptap-pro/ai-toolkit-ai-sdk

API 端点

创建一个使用 Vercel AI SDK 调用 OpenAI 模型的 API 端点。包含 Tiptap AI Toolkit 的 工具定义

如果你的后端使用的不是 TypeScript,而是其他编程语言,请参阅本指南

// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai'
import { toolDefinitions } from '@tiptap-pro/ai-toolkit-ai-sdk'
import { createAgentUIStreamResponse, ToolLoopAgent, UIMessage } from 'ai'

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

  const agent = new ToolLoopAgent({
    model: openai('gpt-5.4-mini'),
    instructions: '你是一个可以编辑富文本文档的助手。',
    tools: toolDefinitions(),
  })

  return createAgentUIStreamResponse({
    agent,
    uiMessages: messages,
  })
}

要访问 OpenAI API,请在 OpenAI 控制台 创建一个 API 密钥,并将其作为环境变量添加。Vercel AI SDK 会自动检测该环境变量。

# .env
OPENAI_API_KEY=your-api-key

当 AI 模型接收到请求时,它会决定调用可用工具来读取文档并对其进行编辑。有关更多详细信息,请参阅可用工具列表

客户端设置

当 AI 模型决定调用某个工具时,需要在客户端执行它。这可以通过 executeTool 方法 实现:

import { Editor } from '@tiptap/react'
import { getAiToolkit } from '@tiptap-pro/client-ai-toolkit'

// 创建一个 Tiptap Editor 实例
const editor = new Editor()

// 获取 Tiptap AI Toolkit 实例
const toolkit = getAiToolkit(editor)

// AI 决定调用 `tiptapRead` 工具来读取文档
// 使用 `executeTool` 方法执行该工具并获取结果
const result = toolkit.executeTool({
  // 要执行的工具名称
  toolName: 'tiptapRead',
  // AI 生成的输入
  input: {},
})
// 将结果返回给 AI 模型

要在你的应用中实现这一点,请创建一个客户端 React 组件,用于渲染 Tiptap 编辑器和一个简单的聊天 UI。该组件利用 Vercel AI SDK 中的 useChat hook 来调用 API 端点并管理聊天对话。

// app/page.tsx
'use client'

import { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls } from 'ai'
import { useChat } from '@ai-sdk/react'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { useState } from 'react'
import { AiToolkit, getAiToolkit } from '@tiptap-pro/client-ai-toolkit'

export default function Page() {
  const editor = useEditor({
    immediatelyRender: false,
    extensions: [StarterKit, AiToolkit],
    content: `<h1>AI 代理演示</h1><p>请让 AI 改进这段内容。</p>`,
  })

  // 解决问题:https://github.com/vercel/ai/issues/8148
  const editorRef = useRef(editor)
  editorRef.current = editor

  const { messages, sendMessage, addToolOutput } = useChat({
    transport: new DefaultChatTransport({ api: '/api/chat' }),
    sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
    async onToolCall({ toolCall }) {
      if (!editor) return

      const { toolName, input, toolCallId } = toolCall

      // 使用 AI 工具包执行工具
      const toolkit = getAiToolkit(editor)
      const result = toolkit.executeTool({
        toolName,
        input,
      })

      addToolOutput({ tool: toolName, toolCallId, output: result.output })
    },
  })

  const [input, setInput] = useState('用一个关于 Tiptap 的短篇故事替换最后一段')

  if (!editor) return null

  return (
    <div>
      <EditorContent editor={editor} />
      {messages?.map((message) => (
        <div key={message.id} style={{ whiteSpace: 'pre-wrap' }}>
          <strong>{message.role}</strong>
          <br />
          {message.parts
            .filter((p) => p.type === 'text')
            .map((p) => p.text)
            .join('\n')}
        </div>
      ))}
      <form
        onSubmit={(e) => {
          e.preventDefault()
          sendMessage({ text: input })
          setInput('')
        }}
      >
        <input value={input} onChange={(e) => setInput(e.target.value)} />
      </form>
    </div>
  )
}

最终效果

配合额外的 CSS 样式,效果是一个简单但精致的 AI 聊天机器人应用:

查看GitHub 上的源码

下一步

  • 让你的用户通过 审查更改指南 审查 AI 生成的更改
  • 使用 流式传输指南 添加实时工具流式传输,以查看更改如何实时发生
  • 你的文档是否包含自定义节点和标记?通过 模式感知指南 了解如何让你的 AI 代理理解它们。