AI Assistant是TinyMCE首次在编辑器中集成AI功能的迭代版本。TinyMCE AI是目前的AI解决方案,通过Chat、Quick Actions和Review模式提供深度文档感知的AI功能。TinyMCE AI可同时配合Tiny Cloud和自托管的TinyMCE编辑器使用,并能连接到托管的Tiny Cloud AI服务或自托管AI服务。
简介
本指南介绍如何在TinyMCE中使用Amazon Bedrock集成AI Assistant插件。Amazon Bedrock是一项用于在AWS上构建生成式AI应用程序的托管服务。使用Amazon Bedrock的优势在于它提供了多种基础模型,这些模型几乎无需修改即可互换使用。
以下示例直接在客户端加载AWS凭证。出于安全考虑,建议使用其他方法获取这些凭证。建议将API调用隐藏在服务器端代理之后,以避免将AWS凭证暴露给客户端。
这些示例使用Node.js并通过@aws-sdk/client-bedrock-runtime包调用AWS SDK for JavaScript来与Amazon Bedrock API交互。不过,您可以使用AWS SDK支持的任何开发环境。
这里以Anthropic Claude-3 Haiku模型为例。您可以将modelId替换为您想使用的模型。更多信息请参阅支持的模型。请注意,每个基础模型都有其自己的参数集。
若要了解字符串响应与流式响应的区别,请参阅插件页面上的respondWith对象。
先决条件
开始之前,您需要准备以下内容:
一个具有Amazon Bedrock访问权限的AWS账户。
该账户的AWS凭证。
一个已安装
@aws-sdk/client-bedrock-runtime包的Node.js环境。
以下示例旨在展示如何在客户端集成中使用身份验证凭证调用API。不建议在生产环境中采用此方式。建议仅通过代理服务器或实现服务器端集成来访问API,以防止未经授权的API访问。
字符串响应
本示例演示如何将AI Assistant插件与InvokeModel命令集成以生成字符串响应。
import { BedrockRuntimeClient, InvokeModelCommand } from "@aws-sdk/client-bedrock-runtime";
// 不建议在生产环境使用中直接提供访问凭证
// 建议配置代理服务器进行请求认证并授权访问
const AWS_ACCESS_KEY_ID = "<YOUR_ACCESS_KEY_ID>";
const AWS_SECRET_ACCESS_KEY = "<YOUR_SECRET_ACCESS_KEY>";
const AWS_SESSION_TOKEN = "<YOUR_SESSION_TOKEN>";
const config = {
region: "us-east-1",
credentials: {
accessKeyId: AWS_ACCESS_KEY_ID,
secretAccessKey: AWS_SECRET_ACCESS_KEY,
sessionToken: AWS_SESSION_TOKEN,
},
};
const client = new BedrockRuntimeClient(config);
const ai_request = (request, respondWith) => {
const payload = {
anthropic_version: "bedrock-2023-05-31",
max_tokens: 1000,
messages: [{
role: "user",
content: request.prompt
}],
};
const input = {
body: JSON.stringify(payload),
contentType: "application/json",
accept: "application/json",
modelId: "anthropic.claude-3-haiku-20240307-v1:0"
};
respondWith.string(async (_signal) => {
const command = new InvokeModelCommand(input);
const response = await client.send(command);
const decodedResponseBody = new TextDecoder().decode(response.body);
const responseBody = JSON.parse(decodedResponseBody);
const output = responseBody.content[0].text;
return await output;
});
};
tinymce.init({
selector: 'textarea',
plugins: 'ai code help',
toolbar: 'aidialog aishortcuts code help',
ai_request
});流式响应
本示例演示如何将AI Assistant插件与InvokeModelWithResponseStream命令集成以生成流式响应。
import { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } from "@aws-sdk/client-bedrock-runtime";
// 不建议在生产环境中于集成内部提供访问凭证。
// 建议设置代理服务器以验证请求并提供访问权限。
const AWS_ACCESS_KEY_ID = "<YOUR_ACCESS_KEY_ID>";
const AWS_SECRET_ACCESS_KEY = "<YOUR_SECRET_ACCESS_KEY>";
const AWS_SESSION_TOKEN = "<YOUR_SESSION_TOKEN>";
const config = {
region: "us-east-1",
credentials: {
accessKeyId: AWS_ACCESS_KEY_ID,
secretAccessKey: AWS_SECRET_ACCESS_KEY,
sessionToken: AWS_SESSION_TOKEN,
},
};
const client = new BedrockRuntimeClient(config);
const ai_request = (request, respondWith) => {
// 将每个之前的查询和响应作为独立消息添加
const conversation = request.thread.flatMap((event) => {
if (event.response) {
return [
{ role: 'user', content: event.request.query },
{ role: 'assistant', content: event.response.data }
];
} else {
return [];
}
});
// 插件提供的系统消息,用于将输出格式化为 HTML 内容。
const pluginSystemMessages = request.system.map((text) => ({
text
}));
const systemMessages = [
...pluginSystemMessages,
// 用于控制 AI 输出的额外系统消息
{ text: '不要在开头包含 html\`\`\` 或在结尾包含 \`\`\`。' },
{ text: '无需解释或模板内容,仅返回 HTML 响应。' }
]
const system = systemMessages.map((message) => message.text).join('\n');
// 构建发送至 API 的新查询
const text = request.context.length === 0 || conversation.length > 0
? request.query
: `Question: ${request.query} Context: """${request.context}"""`;
const messages = [
...conversation,
{
role: "user",
content: text
}
];
const payload = {
anthropic_version: "bedrock-2023-05-31",
max_tokens: 1000,
system,
messages,
};
const input = {
body: JSON.stringify(payload),
contentType: "application/json",
accept: "application/json",
modelId: "anthropic.claude-3-haiku-20240307-v1:0"
};
// Amazon Bedrock 不支持在流传输过程中取消响应,因此信号回调无用途。
respondWith.stream(async (_signal, streamMessage) => {
const command = new InvokeModelWithResponseStreamCommand(input);
const response = await client.send(command);
for await (const item of response.body) {
const chunk = JSON.parse(new TextDecoder().decode(item.chunk.bytes));
const chunk_type = chunk.type;
switch (chunk_type) {
case "message_start":
break;
case "content_block_start":
break;
case "content_block_delta":
const message = chunk.delta.text;
streamMessage(message);
break;
case "content_block_stop":
break;
case "message_delta":
break;
case "message_stop":
break;
default:
return Promise.reject("Stream error");
}
}
});
};
tinymce.init({
selector: 'textarea',
plugins: 'ai code help',
toolbar: 'aidialog aishortcuts code help',
ai_request
});