---
title: "模板工作流"
description: "使用 Tiptap AI 工具包将 AI 生成的内容填充到结构化模板中。"
canonical_url: "https://tiptap.zhcndoc.com/content-ai/capabilities/ai-toolkit/workflows/template"
---

# 模板工作流

使用 Tiptap AI 工具包将 AI 生成的内容填充到结构化模板中。

> **实验性功能:**
>
> 此功能属于实验性质，将来版本中可能会发生变化。

使用 AI 生成的内容填充结构化的 Tiptap 模板，并将结果插入编辑器中。

> **Interactive demo:** [template workflow](https://ai-toolkit-demos.vercel.app/template-workflow)

查看 [GitHub 上的源码](https://github.com/ueberdosis/ai-toolkit-demos)。

## 模板工作原理

Tiptap 模板是带有特殊属性的 Tiptap JSON 结构，用于标记动态部分：

- **`_templateSlot`**（字符串键）：整个节点将被 AI 生成的 HTML 内容替换。
- **`_templateIf`**（字符串键）：节点仅在布尔值为真时才包含。
- **`_templateAttributes`**（由 `{key, attribute}` 对象组成的数组）：将特定节点属性设为 AI 生成的值。

以下是一个 Tiptap JSON 格式的示例模板：

```json
{
  "type": "doc",
  "content": [
    {
      "type": "heading",
      "attrs": { "level": 1 },
      "content": [{ "type": "text", "text": "保密协议" }]
    },
    {
      "type": "paragraph",
      "attrs": { "_templateSlot": "parties" },
      "content": [{ "type": "text", "text": "当事方信息将在此生成。" }]
    },
    {
      "type": "paragraph",
      "attrs": { "_templateIf": "includeArbitration" },
      "content": [{ "type": "text", "text": "仲裁条款内容..." }]
    },
    {
      "type": "heading",
      "attrs": {
        "level": 1,
        "_templateAttributes": [{ "key": "sectionLevel", "attribute": "level" }]
      },
      "content": [{ "type": "text", "text": "适用法律" }]
    }
  ]
}
```

AI 会生成含有每个键对应值的 JSON 对象：

```json
{
  "parties": "<p>本协议由 <strong>Acme Corp</strong> 与 <strong>Beta LLC</strong> 签订。</p>",
  "includeArbitration": true,
  "sectionLevel": 2
}
```

## 技术栈

- [React](https://react.dev/) + [Next.js](https://nextjs.org/)
- [Vercel AI SDK](https://ai-sdk.dev/) + [OpenAI](https://openai.com/) 模型
- Tiptap AI 工具包

## 项目概览

此示例使用 AI 工具包的模板工作流，将 AI 生成内容填充到法律文档模板（保密协议）中。模板包含预定义部分的插槽、条件条款和动态属性。AI 填充动态部分，其余固定的法律模板内容保持不变。

## 安装

创建一个 [Next.js](https://nextjs.org/) 项目：

```bash
npx create-next-app@latest template-workflow
```

安装核心 Tiptap 包以及用于 OpenAI 的 [Vercel AI SDK](https://ai-sdk.dev/)：

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

安装 Tiptap AI 工具包：

> **Pro 版本包:**
>
> AI 工具包为专业版包。安装前请先根据 [私有注册表指南](https://tiptap.zhcndoc.com/guides/pro-extensions.md) 配置对私有 NPM 注册表的访问权限。

```bash
npm install @tiptap-pro/ai-toolkit @tiptap-pro/ai-toolkit-tool-definitions
```

## 服务端设置

创建一个 API 端点，使用 [Vercel AI SDK](https://ai-sdk.dev/) 调用 OpenAI 模型。

如果你的后端不是 TypeScript，请参阅 [非 TypeScript 后端](https://tiptap.zhcndoc.com/content-ai/capabilities/ai-toolkit/advanced-guides/non-typescript-backends.md)。

在 API 端点内部，使用 `createTemplateWorkflow` 将 HTML 模板转换为工作流配置。该函数会自动提取 HTML 中所有模板键，生成系统提示，并创建输出模式。

```ts
// app/api/template-workflow/route.ts
import { openai } from '@ai-sdk/openai'
import { createTemplateWorkflow } from '@tiptap-pro/ai-toolkit-tool-definitions'
import { Output, streamText } from 'ai'

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

  // 从 HTML 模板创建工作流。
  // 会自动提取模板键，生成提示和输出模式。
  const workflow = createTemplateWorkflow({ htmlTemplate })

  const result = streamText({
    model: openai('gpt-5.4-mini'),
    system: workflow.systemPrompt,
    prompt: JSON.stringify({
      task,
      context: '与任务相关的额外背景信息',
    }),
    output: Output.object({ schema: workflow.zodOutputSchema }),
  })

  return result.toTextStreamResponse()
}
```

## 客户端设置

客户端定义模板为 Tiptap JSON。使用 Vercel AI SDK 的 `useObject` 钩子来流式接收服务器部分返回的值。传入一个宽松的 Zod schema (`z.object({}).passthrough()`) 来接受服务器返回的任意属性。

随着部分值的到达，调用 `templateWorkflow` 并传入 `hasFinished` 和 `workflowId` 来逐步填充模板。`workflowId` 允许流式模式，方法会追踪多次调用间的插入范围，迭代替换内容。在调用 API 之前，使用 `createHtmlTemplate` 方法将模板转换为 HTML，供服务器解析模板键。

```tsx
// app/template-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 } from '@tiptap-pro/ai-toolkit'
import { useEffect, useState } from 'react'
import { v4 as uuid } from 'uuid'
import { z } from 'zod'

// 定义模板为 Tiptap JSON
const template = {
  type: 'doc',
  content: [
    {
      type: 'heading',
      attrs: { level: 1 },
      content: [{ type: 'text', text: '保密协议' }],
    },
    {
      type: 'paragraph',
      attrs: { _templateSlot: 'parties' },
      content: [{ type: 'text', text: '当事方详情...' }],
    },
    // ... 更多模板内容
  ],
}

// 宽松的 schema，接受服务器返回任意属性
const templateSchema = z.object({}).passthrough()

export default function Page() {
  const editor = useEditor({
    immediatelyRender: false,
    extensions: [StarterKit, AiToolkit],
    content: '<p>点击“生成”以填充模板。</p>',
  })

  const [workflowId, setWorkflowId] = useState('')

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

  // 监听部分结果流式到达
  useEffect(() => {
    if (!editor || !object) return

    const toolkit = getAiToolkit(editor)
    toolkit.templateWorkflow({
      template,
      values: object as Record<string, unknown>,
      hasFinished: !isLoading,
      workflowId,
    })
  }, [object, workflowId, editor, isLoading])

  const generate = () => {
    if (!editor) return

    const toolkit = getAiToolkit(editor)
    const htmlTemplate = toolkit.createHtmlTemplate(template)

    setWorkflowId(uuid())
    submit({
      htmlTemplate,
      task: '生成 Acme Corp 和 Beta LLC 之间的保密协议',
    })
  }

  if (!editor) return null

  return (
    <div>
      <EditorContent editor={editor} />
      <button onClick={generate} disabled={isLoading}>
        {isLoading ? '生成中…' : '生成'}
      </button>
    </div>
  )
}
```

## 最终效果

通过额外的 CSS 样式，效果是一个精致的模板工作流应用：

> **Interactive demo:** [template workflow](https://ai-toolkit-demos.vercel.app/template-workflow)

查看 [GitHub 上的源码](https://github.com/ueberdosis/ai-toolkit-demos)。

## 在文档部分插入模板

若要将模板结果插入到特定区域，而不是替换整个文档，请设置 `position` 选项。它可以是位置或一个 [Range](https://tiptap.zhcndoc.com/content-ai/capabilities/ai-toolkit/advanced-guides/concepts.md#range)。

```ts
// 在特定范围插入模板结果
toolkit.templateWorkflow({
  template,
  values: object,
  position: { from: 10, to: 50 },
  hasFinished: !isLoading,
  workflowId,
})
```

## 构建模板编辑器

首先，在编辑器中注册 `TemplateField` 扩展。此扩展将四个模板属性（`_templateSlot`、`_templateIf`、`_templateAttributes`、`_templateFieldMetadata`）作为全局属性添加到编辑器 schema 中的所有节点类型：

```tsx
import { TemplateField } from '@tiptap-pro/ai-toolkit'

const editor = new Editor({
  extensions: [StarterKit, TemplateField],
})
```

> **TemplateField 与 AiToolkit:**
>
> 你可以将 `TemplateField` 与 `AiToolkit` 扩展一起使用，或者单独使用它，若你只需要模板字段支持而不需要完整的 AI 工具包功能。

> **Interactive demo:** [AiToolkitTemplateEditor](https://deploy-preview-405--tiptap-pro.netlify.app/preview/Extensions/AiToolkitTemplateEditor/)

`TemplateField` 扩展提供命令，用于在当前选择的节点上设置和取消模板属性：

```tsx
// 将选中节点标记为模板插槽
editor.commands.setTemplateSlot('intro')

// 移除模板插槽
editor.commands.unsetTemplateSlot()

// 将选中节点标记为条件包含
editor.commands.setTemplateIf('showDisclaimer')

// 移除条件
editor.commands.unsetTemplateIf()

// 将模板键映射到节点属性
editor.commands.setTemplateAttributes([{ key: 'sectionLevel', attribute: 'level' }])

// 移除属性映射
editor.commands.unsetTemplateAttributes()
```

当你想要直接在模板中存储关于字段的额外信息时，请使用 `_templateFieldMetadata`。元数据值是可选的，可以包含任何 JSON 对象，以帮助你的应用程序稍后解释该字段：

```json
{
  "type": "paragraph",
  "attrs": {
    "_templateSlot": "intro",
    "_templateFieldMetadata": {
      "label": "Introduction",
      "group": "summary",
      "analyticsKey": "template.intro"
    }
  },
  "content": [{ "type": "text", "text": "Placeholder" }]
}
```

你可以将元数据附加到插槽字段、条件字段和属性字段。元数据不会改变模板的填充方式。它仅用于你自己的应用程序逻辑。

使用 `[_templateslot]` CSS 属性选择器为模板插槽设置样式。`TemplateField` 扩展在 DOM 中输出小写的模板属性：

```css
[_templateslot] {
  border: 2px dashed #6a00f5;
  border-radius: 4px;
  padding: 4px 8px;
  background-color: rgba(106, 0, 245, 0.05);
  position: relative;
}

[_templateslot]::after {
  content: attr(_templateslot);
  position: absolute;
  top: -10px;
  right: 4px;
  font-size: 10px;
  background: #6a00f5;
  color: white;
  padding: 0 4px;
  border-radius: 2px;
}
```

## 填充后保留模板字段

默认情况下，`templateWorkflow` 方法填充模板后会移除所有 `_templateSlot` 属性。设置 `preserveSlotAttr` 为 `true`，可在填充节点上保留这些属性：

```tsx
toolkit.templateWorkflow({
  template,
  values: object,
  hasFinished: !isLoading,
  workflowId,
  preserveSlotAttr: true,
})
```

当启用 `preserveSlotAttr` 时，每个填充节点都会保留其 `_templateSlot` 属性。这允许你：

- **识别 AI 填充的内容**：检查文档的哪些部分是由 AI 生成的。
- **重新填充模板字段**：将填充后的文档用作新模板以重新生成特定部分。
- **保持字段元数据可用**：稍后从填充后的文档中读取 `_templateFieldMetadata`。

## 重新填充模板字段

当 `preserveSlotAttr` 为 `true` 时，填充后的文档仍包含 `_templateSlot` 属性。你可以提取文档 JSON，并传回 `templateWorkflow` 来重新填充特定字段：

```tsx
// 将当前文档提取为模板
const currentDoc = editor.getJSON()

// 使用新值重新填充
const toolkit = getAiToolkit(editor)
toolkit.templateWorkflow({
  template: currentDoc,
  values: newValues,
  preserveSlotAttr: true,
})
```

这支持迭代式编辑流程，用户可请求 AI 重新生成文档的单个部分，而不会影响其他内容。

## 读取字段内容和元数据

AI 工具包还导出 `getTemplateFieldMatches()`，这是一个辅助函数，用于扫描 Tiptap JSON 文档中的字段名，并按文档顺序返回所有匹配的字段。

当你想要以下操作时，这很有用：

- 读取模板字段的当前内容
- 访问存储在该字段上的 `_templateFieldMetadata`
- 支持重新生成、分析或自定义 UI 标签等工作流

```ts
import { getTemplateFieldMatches } from '@tiptap-pro/ai-toolkit'

const matches = getTemplateFieldMatches({
  document: editor.getJSON(),
  fieldName: 'intro',
})

for (const match of matches) {
  console.log(match.fieldType)
  console.log(match.metadata)
  console.log(match.content)
}
```

每个结果包括：

- `fieldType`：匹配是否来自 `_templateSlot`、`_templateIf` 或 `_templateAttributes`
- `metadata`：`_templateFieldMetadata` 对象，或 `null`
- `content`：匹配节点的内容（Tiptap JSON 格式），空节点则为 `null`

## 控制哪些模板字段是必填的

默认情况下，所有模板字段都是必填的——AI 必须填写每个字段。要将特定字段设为可选，请在服务器端使用 `requiredSlots`、`requiredConditions` 和 `requiredAttributes`。仅列表中的字段是必填，未列出的字段则为可选：

```ts
// 服务器端：仅 "intro" 是必填，其他插槽为可选
const workflow = createTemplateWorkflow({
  htmlTemplate,
  requiredSlots: ['intro'],
})
```

AI 会始终填充必填插槽，也可能根据任务上下文选择填充其他插槽。

要将某类字段全部设为可选，请传入空数组：

```ts
// 所有插槽可选，但特定条件和属性为必填
const workflow = createTemplateWorkflow({
  htmlTemplate,
  requiredSlots: [],
  requiredConditions: ['includeDisclaimer'],
  requiredAttributes: ['headingLevel'],
})
```

## 相关指南

- [`templateWorkflow` 方法的 API 参考](https://tiptap.zhcndoc.com/content-ai/capabilities/ai-toolkit/api-reference/workflows.md#templateworkflow)
- [`createHtmlTemplate` 方法的 API 参考](https://tiptap.zhcndoc.com/content-ai/capabilities/ai-toolkit/api-reference/workflows.md#createhtmltemplate)
- [`createTemplateWorkflow` 工具的 API 参考](https://tiptap.zhcndoc.com/content-ai/capabilities/ai-toolkit/api-reference/workflows.md#createtemplateworkflow-server-side-utility)
