Copilot
- Position your cursor at the end of a paragraph where you want to add or modify text.
- Press Control + Space to trigger Copilot
- Copilot will automatically suggest completions as you type.
- Choose from the suggested completions:
- Tab:Accept the entire suggested completion
- Command + Right Arrow: Complete one character at a time
- Escape: Cancel the Copilot
'use client';
import * as React from 'react';
import { Plate, usePlateEditor } from 'platejs/react';
import { EditorKit } from '@/components/editor/editor-kit';
import { CopilotKit } from '@/components/editor/plugins/copilot-kit';
import { copilotValue } from '@/registry/examples/values/copilot-value';
import { Editor, EditorContainer } from '@/components/ui/editor';
export default function CopilotDemo() {
const editor = usePlateEditor({
plugins: [...CopilotKit, ...EditorKit],
value: copilotValue,
});
return (
<Plate editor={editor}>
<EditorContainer variant="demo">
<Editor />
</EditorContainer>
</Plate>
);
}


功能特性
- 在输入时渲染幽灵文本建议
- 两种触发模式:
- 快捷键(如
Ctrl+Space)。再次按下可获取替代建议。 - 防抖模式:在段落末尾空格后自动触发
- 快捷键(如
- 使用 Tab 接受建议或使用
Cmd+→逐词接受 - 内置支持 Vercel AI SDK 补全 API
套件使用
安装
添加 Copilot 功能最快的方式是使用 CopilotKit,它包含预配置的 CopilotPlugin 以及 MarkdownKit 和它们的 Plate UI 组件。
'use client';
import type { TElement } from 'platejs';
import { faker } from '@faker-js/faker';
import { CopilotPlugin } from '@platejs/ai/react';
import { serializeMd, stripMarkdown } from '@platejs/markdown';
import { GhostText } from '@/components/ui/ghost-text';
import { MarkdownKit } from './markdown-kit';
export const CopilotKit = [
...MarkdownKit,
CopilotPlugin.configure(({ api }) => ({
options: {
completeOptions: {
api: '/api/ai/copilot',
body: {
system: `You are an advanced AI writing assistant, similar to VSCode Copilot but for general text. Your task is to predict and generate the next part of the text based on the given context.
Rules:
- Continue the text naturally up to the next punctuation mark (., ,, ;, :, ?, or !).
- Maintain style and tone. Don't repeat given text.
- For unclear context, provide the most likely continuation.
- Handle code snippets, lists, or structured text if needed.
- Don't include """ in your response.
- CRITICAL: Always end with a punctuation mark.
- CRITICAL: Avoid starting a new block. Do not use block formatting like >, #, 1., 2., -, etc. The suggestion should continue in the same block as the context.
- If no context is provided or you can't generate a continuation, return "0" without explanation.`,
},
onError: () => {
// Mock the API response. Remove it when you implement the route /api/ai/copilot
api.copilot.setBlockSuggestion({
text: stripMarkdown(faker.lorem.sentence()),
});
},
onFinish: (_, completion) => {
if (completion === '0') return;
api.copilot.setBlockSuggestion({
text: stripMarkdown(completion),
});
},
},
debounceDelay: 500,
renderGhostText: GhostText,
getPrompt: ({ editor }) => {
const contextEntry = editor.api.block({ highest: true });
if (!contextEntry) return '';
const prompt = serializeMd(editor, {
value: [contextEntry[0] as TElement],
});
return `Continue the text up to the next punctuation mark:
"""
${prompt}
"""`;
},
},
shortcuts: {
accept: {
keys: 'tab',
},
acceptNextWord: {
keys: 'mod+right',
},
reject: {
keys: 'escape',
},
triggerSuggestion: {
keys: 'ctrl+space',
},
},
})),
];
GhostText: 渲染幽灵文本建议。
添加套件
import { createPlateEditor } from 'platejs/react';
import { CopilotKit } from '@/components/editor/plugins/copilot-kit';
const editor = createPlateEditor({
plugins: [
// ...其他插件,
...CopilotKit,
// 将使用 Tab 键的插件放在 CopilotKit 之后以避免冲突
// IndentPlugin,
// TabbablePlugin,
],
});Tab 键处理: Copilot 插件使用 Tab 键来接受建议。为避免与其他使用 Tab 的插件(如 IndentPlugin 或 TabbablePlugin)冲突,请确保 CopilotKit 在插件配置中位于它们之前。
添加 API 路由
Copilot 需要一个服务器端 API 端点来与 AI 模型通信。添加预配置的 Copilot API 路由:
import type { NextRequest } from 'next/server';
import { createOpenAI } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
const {
apiKey: key,
model = 'gpt-4o-mini',
prompt,
system,
} = await req.json();
const apiKey = key || process.env.OPENAI_API_KEY;
if (!apiKey) {
return NextResponse.json(
{ error: 'Missing OpenAI API key.' },
{ status: 401 }
);
}
const openai = createOpenAI({ apiKey });
try {
const result = await generateText({
abortSignal: req.signal,
maxTokens: 50,
model: openai(model),
prompt: prompt,
system,
temperature: 0.7,
});
return NextResponse.json(result);
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
return NextResponse.json(null, { status: 408 });
}
return NextResponse.json(
{ error: 'Failed to process AI request' },
{ status: 500 }
);
}
}
配置环境
确保您的 OpenAI API 密钥已设置在环境变量中:
OPENAI_API_KEY="您的-api-密钥"手动使用
安装
pnpm add @platejs/ai @platejs/markdown
添加插件
import { CopilotPlugin } from '@platejs/ai/react';
import { MarkdownPlugin } from '@platejs/markdown';
import { createPlateEditor } from 'platejs/react';
const editor = createPlateEditor({
plugins: [
// ...其他插件,
MarkdownPlugin,
CopilotPlugin,
// 将使用 Tab 键的插件放在 CopilotPlugin 之后以避免冲突
// IndentPlugin,
// TabbablePlugin,
],
});MarkdownPlugin: 用于将编辑器内容序列化为提示词发送。CopilotPlugin: 启用 AI 驱动的文本补全。
Tab 键处理: Copilot 插件使用 Tab 键来接受建议。为避免与其他使用 Tab 的插件(如 IndentPlugin 或 TabbablePlugin)冲突,请确保 CopilotPlugin 在插件配置中位于它们之前。
配置插件
import { CopilotPlugin } from '@platejs/ai/react';
import { serializeMd, stripMarkdown } from '@platejs/markdown';
import { GhostText } from '@/components/ui/ghost-text';
const plugins = [
// ...其他插件,
MarkdownPlugin.configure({
options: {
remarkPlugins: [remarkMath, remarkGfm, remarkMdx],
},
}),
CopilotPlugin.configure(({ api }) => ({
options: {
completeOptions: {
api: '/api/ai/copilot',
onError: () => {
// 模拟 API 响应。在实现路由 /api/ai/copilot 后移除
api.copilot.setBlockSuggestion({
text: stripMarkdown('这是一个模拟建议。'),
});
},
onFinish: (_, completion) => {
if (completion === '0') return;
api.copilot.setBlockSuggestion({
text: stripMarkdown(completion),
});
},
},
debounceDelay: 500,
renderGhostText: GhostText,
},
shortcuts: {
accept: { keys: 'tab' },
acceptNextWord: { keys: 'mod+right' },
reject: { keys: 'escape' },
triggerSuggestion: { keys: 'ctrl+space' },
},
})),
];completeOptions: 配置 Vercel AI SDKuseCompletion钩子。api: AI 补全路由的端点。onError: 处理错误的回调(用于开发期间的模拟)。onFinish: 处理完成建议的回调。此处将建议设置到编辑器中。
debounceDelay: 用户停止输入后自动触发建议的延迟时间(毫秒)。renderGhostText: 用于内联显示建议的 React 组件。shortcuts: 定义与 Copilot 建议交互的键盘快捷键。
添加 API 路由
在 app/api/ai/copilot/route.ts 创建 API 路由处理程序来处理 AI 请求。此端点将接收来自编辑器的提示词并调用 AI 模型。
import type { NextRequest } from 'next/server';
import { createOpenAI } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
const {
apiKey: key,
model = 'gpt-4o-mini',
prompt,
system,
} = await req.json();
const apiKey = key || process.env.OPENAI_API_KEY;
if (!apiKey) {
return NextResponse.json(
{ error: '缺少 OpenAI API 密钥。' },
{ status: 401 }
);
}
const openai = createOpenAI({ apiKey });
try {
const result = await generateText({
abortSignal: req.signal,
maxTokens: 50,
model: openai(model),
prompt: prompt,
system,
temperature: 0.7,
});
return NextResponse.json(result);
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
return NextResponse.json(null, { status: 408 });
}
return NextResponse.json(
{ error: '处理 AI 请求失败' },
{ status: 500 }
);
}
}然后,在 .env.local 中设置您的 OPENAI_API_KEY。
系统提示词
系统提示词定义了 AI 的角色和行为。修改 completeOptions 中的 body.system 属性:
CopilotPlugin.configure(({ api }) => ({
options: {
completeOptions: {
api: '/api/ai/copilot',
body: {
system: {
system: `您是一个高级 AI 写作助手,类似于 VSCode Copilot,但适用于通用文本。您的任务是根据给定上下文预测并生成文本的下一部分。
规则:
- 自然地继续文本直到下一个标点符号(., ,, ;, :, ? 或 !)。
- 保持风格和语气。不要重复给定文本。
- 对于不明确的上下文,提供最可能的延续。
- 如果需要,处理代码片段、列表或结构化文本。
- 不要在响应中包含 """。
- 关键:始终以标点符号结尾。
- 关键:避免开始新块。不要使用块格式化如 >, #, 1., 2., - 等。建议应继续在与上下文相同的块中。
- 如果未提供上下文或无法生成延续,返回 "0" 而不解释。`,
},
},
// ... 其他选项
},
// ... 其他插件选项
},
})),用户提示词
用户提示词(通过 getPrompt)决定发送给 AI 的上下文内容。您可以自定义它以包含更多上下文或以不同方式格式化:
CopilotPlugin.configure(({ api }) => ({
options: {
getPrompt: ({ editor }) => {
const contextEntry = editor.api.block({ highest: true });
if (!contextEntry) return '';
const prompt = serializeMd(editor, {
value: [contextEntry[0] as TElement],
});
return `继续文本直到下一个标点符号:
"""
${prompt}
"""`;
},
// ... 其他选项
},
})),Plate Plus
自定义
切换 AI 模型
在 API 路由中配置不同的 AI 模型和 provider:
import { createOpenAI } from '@ai-sdk/openai';
import { createAnthropic } from '@ai-sdk/anthropic';
export async function POST(req: NextRequest) {
const {
model = 'gpt-4o-mini',
provider = 'openai',
prompt,
system
} = await req.json();
let aiProvider;
switch (provider) {
case 'anthropic':
aiProvider = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
break;
case 'openai':
default:
aiProvider = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });
break;
}
const result = await generateText({
model: aiProvider(model),
prompt,
system,
maxTokens: 50,
temperature: 0.7,
});
return NextResponse.json(result);
}在 CopilotPlugin 中配置模型:
CopilotPlugin.configure(({ api }) => ({
options: {
completeOptions: {
api: '/api/ai/copilot',
body: {
model: 'claude-3-haiku-20240307', // 用于补全的快速模型
provider: 'anthropic',
system: '您的系统提示词...',
},
},
// ... 其他选项
},
})),更多 AI provider 和模型,请参阅 Vercel AI SDK 文档。
自定义触发条件
控制何时自动触发建议:
CopilotPlugin.configure(({ api }) => ({
options: {
triggerQuery: ({ editor }) => {
// 仅在段落块中触发
const block = editor.api.block();
if (!block || block[0].type !== 'p') return false;
// 标准检查
return editor.selection &&
!editor.api.isExpanded() &&
editor.api.isAtEnd();
},
autoTriggerQuery: ({ editor }) => {
// 自动触发的自定义条件
const block = editor.api.block();
if (!block) return false;
const text = editor.api.string(block[0]);
// 在疑问词后触发
return /\b(what|how|why|when|where)\s*$/i.test(text);
},
// ... 其他选项
},
})),安全考虑
为 Copilot API 实施安全最佳实践:
export async function POST(req: NextRequest) {
const { prompt, system } = await req.json();
// 验证提示词长度
if (!prompt || prompt.length > 1000) {
return NextResponse.json({ error: '无效提示词' }, { status: 400 });
}
// 速率限制(使用您偏好的解决方案实现)
// await rateLimit(req);
// 敏感内容过滤
if (containsSensitiveContent(prompt)) {
return NextResponse.json({ error: '内容被过滤' }, { status: 400 });
}
// 处理 AI 请求...
}安全指南:
- 输入验证: 限制提示词长度并验证内容
- 速率限制: 通过请求限制防止滥用
- 内容过滤: 过滤敏感或不适当内容
- API 密钥安全: 切勿在客户端暴露 API 密钥
- 超时处理: 优雅处理请求超时
插件
CopilotPlugin
用于 AI 驱动的文本补全建议的插件。
- 默认: 检查:
- 上方块不为空
- 上方块以空格结尾
- 无现有建议
- 默认:
0 - 默认: 使用祖先节点的 markdown 序列化
- 默认: 检查:
- 选择未展开
- 选择在块末尾
自动触发 copilot 的附加条件。
AI 补全配置选项。参见 AI SDK useCompletion 参数。
自动触发建议的防抖延迟。
从建议文本中提取下一个单词的函数。
生成 AI 补全提示词的函数。
渲染幽灵文本建议的组件。
触发 copilot 的条件。
转换
tf.copilot.accept()
接受当前建议并将其应用到编辑器内容中。
默认快捷键: Tab
tf.copilot.acceptNextWord()
仅接受当前建议的下一个单词,允许逐步接受建议。
示例快捷键: Cmd + →
API
api.copilot.reject()
将插件状态重置为初始条件:
默认快捷键: Escape
api.copilot.triggerSuggestion()
触发新的建议请求。请求可能会根据插件配置进行防抖。
示例快捷键: Ctrl + Space
api.copilot.setBlockSuggestion()
为块设置建议文本。
api.copilot.stop()
停止正在进行的建议请求并清理:
- 取消防抖的触发调用
- 中止当前 API 请求
- 重置中止控制器

