评论工作流

构建一个允许 AI 管理你 Tiptap 文档中评论和线程的工作流。

查看GitHub 上的源码

技术栈

项目概览

此示例使用 AI 工具包的评论工作流来管理文档中的线程和评论。AI 可以创建新线程、添加评论、更新评论、删除评论并管理线程状态。

安装

创建一个 Next.js 项目:

npx create-next-app@latest comments-workflow

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

npm install @tiptap/react @tiptap/starter-kit ai @ai-sdk/react @ai-sdk/anthropic zod uuid

安装 Tiptap AI 工具包和评论扩展:

专业版包

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

npm install @tiptap-pro/ai-toolkit @tiptap-pro/ai-toolkit-tool-definitions @tiptap-pro/extension-comments

服务器配置

创建一个使用 Vercel AI SDK 调用 Anthropic 模型的 API 端点。如果你的后端不是 TypeScript,请参阅非 TypeScript 后端

在 API 端点中,使用 createEditThreadsWorkflow 函数创建并配置评论工作流。该工作流包含一个可直接使用的系统提示,用于指示 AI 模型如何管理评论。

用户消息应包含:

  • nodes:文档的节点(由 tiptapRead 获得)
  • threads:文档中已有的线程(由 getThreads 获得)
  • task:AI 需要执行的任务。例如,添加一个改进引言的建议性评论
// app/api/comments-workflow/route.ts
import { openai } from '@ai-sdk/openai'
import { createEditThreadsWorkflow } from '@tiptap-pro/ai-toolkit-tool-definitions'
import { Output, streamText } from 'ai'

export async function POST(req: Request) {
  const { content, threads, task } = await req.json()

  // 创建并配置评论工作流(使用默认设置)。
  // 包含可直接使用的系统提示和输出模式。
  const workflow = createEditThreadsWorkflow()

  const result = streamText({
    model: openai('gpt-5.4-mini'),
    // 系统提示
    system: workflow.systemPrompt,
    // 用户消息
    prompt: JSON.stringify({
      content,
      threads,
      task,
    }),
    output: Output.object({ schema: workflow.zodOutputSchema }),
  })

  return result.toTextStreamResponse()
}

客户端配置

创建一个 React 组件,渲染集成了评论扩展的编辑器并应用评论操作。

首先,工作流开始时调用 tiptapRead 方法获取文档内容,调用 getThreads 方法获取现有线程。

然后,调用 API 端点来启动工作流。使用 editThreadsWorkflow 方法应用评论操作。

// app/comments-workflow/page.tsx
'use client'

import { experimental_useObject as useObject } from '@ai-sdk/react'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { AiToolkit, getAiToolkit, editThreadsWorkflowOutputSchema } from '@tiptap-pro/ai-toolkit'
import { Comments } from '@tiptap-pro/extension-comments'
import { useEffect, useState } from 'react'
import { v4 as uuid } from 'uuid'

// 创建一个简单的评论提供者
const commentsProvider = {
  threads: [],
  getThreads: () => commentsProvider.threads,
  createThread: (thread) => {
    commentsProvider.threads.push(thread)
    return thread
  },
  updateThread: (id, data) => {
    const index = commentsProvider.threads.findIndex((t) => t.id === id)
    if (index !== -1) {
      commentsProvider.threads[index] = { ...commentsProvider.threads[index], ...data }
    }
  },
  deleteThread: (id) => {
    commentsProvider.threads = commentsProvider.threads.filter((t) => t.id !== id)
  },
}

export default function Page() {
  const editor = useEditor({
    immediatelyRender: false,
    extensions: [
      StarterKit,
      AiToolkit,
      Comments.configure({
        provider: commentsProvider,
      }),
    ],
    content: `<h1>含评论的文档</h1><p>这是一个示例文档,AI 可以在其中添加和管理评论。</p>`,
  })

  const [task, setTask] = useState('添加一个建议改进此文档的评论')

  const { submit, isLoading, object } = useObject({
    api: '/api/comments-workflow',
    schema: editThreadsWorkflowOutputSchema,
  })

  const operations = object?.operations ?? []

  // 实时应用传入的操作
  useEffect(() => {
    if (!editor || !object?.operations) return

    const toolkit = getAiToolkit(editor)
    toolkit.editThreadsWorkflow({
      operations: object.operations,
      workflowId,
      isStreaming: isLoading,
    })
  }, [editor, object, workflowId, isLoading])

  if (!editor) return null

  const manageComments = () => {
    setWorkflowId(uuid())

    const toolkit = getAiToolkit(editor)

    // 获取文档内容和现有线程
    const { nodes } = toolkit.tiptapRead()
    const { threads } = toolkit.getThreads()

    // 调用 API 端点启动工作流
    submit({ nodes, threads, task })
  }

  return (
    <div>
      <EditorContent editor={editor} />

      <input
        type="text"
        value={task}
        onChange={(e) => setTask(e.target.value)}
        placeholder="请输入评论任务..."
      />

      <button onClick={manageComments} disabled={isLoading}>
        {isLoading ? '处理中...' : '使用 AI 管理评论'}
      </button>
    </div>
  )
}

可用操作

评论工作流支持以下操作:

操作格式描述
创建线程['createThread', nodeHash, htmlContent]在指定位置创建新线程
创建评论['createComment', threadId, content]向现有线程添加评论
更新评论['updateComment', threadId, commentId, content]更新现有评论
删除评论['removeComment', threadId, commentId]从线程中删除评论
删除线程['removeThread', threadId]删除整个线程
解决线程['resolveThread', threadId]标记线程为已解决
解除解决线程['unresolveThread', threadId]标记线程为未解决

最终效果

通过额外的 CSS 样式,效果是一个精致的评论管理应用:

查看GitHub 上的源码

文档部分评论

要只对文档的某一子部分添加评论,可以在 tiptapRead 上设置 range 选项。该参数接收一个带有指定区域的 Range

例如,只对选中的内容评论:

const range = editor.state.selection

// 只读取文档中选中的部分
const { content } = toolkit.tiptapRead({ range })
const { threads } = toolkit.getThreads()

const operations = callApi(content, threads)

// 应用评论操作
toolkit.editThreadsWorkflow({ operations, workflowId })

对于大型文档,可以使用 tiptapReadChunks 方法将文档拆分为多个块并行处理。每个块都包含一个带有该块区域的 Range

相关指南