本指南可在任意安装了 Docker 的机器上搭建一个完全可运行的本地栈:
MySQL 8.0:对话历史记录和元数据
Redis:缓存与会话状态
TinyMCE AI 服务:本地部署的 AI 后端
一个最小化的 token 服务器(Node.js):为编辑器签名 JWT
一个集成 TinyMCE 的浏览器页面:验证端到端流程
此快速入门旨在生产部署前验证栈的各个组件。生产环境工程师可以先阅读本节了解概念流程,然后继续阅读 生产环境部署。
开始之前
请确认以下内容已安装且可访问:
docker --version # 需要 20.10+ 版本(或使用 podman --version 检查 Podman 4+) node --version # 需要 18+ 版本 npm --version
此外,请准备好以下凭据:
TinyMCE 许可证密钥和容器注册表凭据(来自 Tiny 账户代表)
至少一个 LLM 提供商的 API 密钥(OpenAI、Anthropic 或 Google)
使用 Docker Compose 快速入门
创建项目文件夹
mkdir tinymceai-onpremise && cd tinymceai-onpremise
向容器注册表认证
服务镜像位于 registry.containers.tiny.cloud/ai-service-tiny。
对于 Docker:
docker login -u tiny -p [access-token] registry.containers.tiny.cloud
对于 Podman:
podman login -u tiny -p [access-token] registry.containers.tiny.cloud
所有账户均使用用户名tiny进行认证。请将[access-token]替换为 Tiny 账户代表提供的访问令牌。如果尚未收到凭据,请联系 Tiny 支持。
拉取 AI 服务镜像
docker pull registry.containers.tiny.cloud/ai-service-tiny:latest
对于 Podman,请使用 podman pull 替代。对于生产环境,请固定特定的版本标签(例如 :5.1.0),而不是使用 :latest,以确保部署可复现并避免意外的破坏性变更。
创建 docker-compose.yml(数据层)
此 compose 文件将启动 AI 服务所依赖的数据层服务(MySQL 和 Redis)。AI 服务本身将在下一步单独启动,这样可以独立升级或重新配置它。
请严格按照以下内容创建文件。缩进使用两个空格,切勿使用制表符。
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-changeme}
MYSQL_DATABASE: ai_service
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
mysql_data:请固定使用mysql:8.0,而不是mysql:8。:8标签会解析为 MySQL 8.4,该版本会拒绝某些旧版 compose 文件传递的服务器选项。详情请参阅 MySQL 版本固定。
同样支持 PostgreSQL。有关等效的 compose 文件,请参阅 数据库、Redis 和存储。切换前请查阅 PostgreSQL 模式前提条件。
如果 AI 服务需要访问主机(例如在主机上运行的自托管 Ollama),请在下方“启动”部分的 docker run 命令中添加 --add-host=host.docker.internal:host-gateway。Docker Desktop(macOS、Windows)和 Podman 4+ 会自动注入此别名;原生 Linux Docker 则不会。
创建 .env 文件
# --- 必需:Tiny 提供的 AI 服务许可证密钥 --- AI_LICENSE_KEY='paste-ai-license-key-here' # --- 必需:用于登录管理面板的强密钥 --- MANAGEMENT_SECRET='replace-with-strong-secret' # --- 必需:数据库密码(必须与 docker-compose.yml 中一致)--- DB_PASSWORD='replace-with-db-password' # --- 必需:至少一个 LLM 提供商密钥 --- OPENAI_API_KEY='paste-openai-key-here' # ANTHROPIC_API_KEY='paste-anthropic-key-here' # GOOGLE_API_KEY='paste-google-key-here' # --- 创建环境后填写(见下文“创建环境和访问密钥”)。由 token 服务器使用,而非 AI 服务。--- AI_ENV_ID='paste-environment-id-here' AI_API_SECRET='paste-api-secret-here'
AI_LICENSE_KEY用于激活 AI 服务容器。
tinymce.init()中的license_key初始化选项用于激活自托管编辑器中的 TinyMCE 高级功能。这些是来自不同来源的不同凭据——它们不能互换。请参阅概览页面中的 凭据部分。
启动 MySQL 和 Redis
docker compose up -d
等待约15秒以让 MySQL 初始化,然后验证:
docker compose ps
两个数据层容器(MySQL 和 Redis)应在 STATUS 列中显示 healthy。如果 MySQL 仍然显示 starting,请再等待10秒并重新运行。
启动 AI 服务
AI 服务作为独立容器运行,不在 Docker Compose 堆栈内。这种分离允许在不重启数据库和 Redis 的情况下升级或重新配置 AI 服务。
首先,找到 Docker Compose 网络名称(Docker 从文件夹名称创建):
docker network ls --format '{{.Name}}' | grep default在下面的 --network 中使用匹配的网络名称。首先,加载 .env 文件:
set -a && source .env && set +a
然后启动 AI 服务容器。docker run 命令将友好的 .env 名称映射到服务自身的环境变量——例如,AI_LICENSE_KEY 作为 LICENSE_KEY 传递到容器,MANAGEMENT_SECRET 作为 ENVIRONMENTS_MANAGEMENT_SECRET_KEY 传递:
docker run --init -d -p 8000:8000 \
--network <compose-network-name> \
--name ai-service \
-e LICENSE_KEY="$AI_LICENSE_KEY" \
-e ENVIRONMENTS_MANAGEMENT_SECRET_KEY="$MANAGEMENT_SECRET" \
-e DATABASE_DRIVER='mysql' \
-e DATABASE_HOST='mysql' \
-e DATABASE_USER='root' \
-e DATABASE_PASSWORD="$DB_PASSWORD" \
-e DATABASE_DATABASE='ai_service' \
-e REDIS_HOST='redis' \
-e PROVIDERS='{"openai":{"type":"openai","apiKeys":["'"$OPENAI_API_KEY"'"]}}' \
-e STORAGE_DRIVER='database' \
-e ALLOWED_ORIGINS='http://localhost:3000' \
-e ENABLE_METRIC_LOGS='true' \
registry.containers.tiny.cloud/ai-service-tiny:latest此启动命令通过
PROVIDERS值和$OPENAI_API_KEY配置单个 OpenAI 提供者。AI 服务仅接收docker run命令中显式传递的环境变量,因此仅在.env文件中添加PROVIDERS、MODELS或其他提供者设置无效。要使用任何其他提供者——
openai-compatible(例如 Databricks、Ollama 或 vLLM)、Azure、Amazon Bedrock 或 Google Vertex——请直接编辑docker run命令:替换PROVIDERS值并添加MODELS数组。MODELS变量是必需的,除了直接的 OpenAI、Anthropic 和 Google,它们具有内置的模型路由。参见 LLM providers 获取完整的PROVIDERS和MODELS配置。
AI 服务在启动时联系 license.containers.tiny.cloud 以验证许可证密钥。确保从容器可以访问此端点。在此检查中不发送客户数据。如果容器已从之前尝试中存在,请先用 docker rm -f ai-service 删除它。docker network ls返回的网络名称已包含_default后缀(例如,tinymceai-onpremise_default)。在--network中直接使用完整名称。对于多个 LLM 提供者,扩展PROVIDERSJSON:{"openai":{…},"anthropic":{…}}。
上面的启动命令以基本对话支持启动 AI 服务。要在对话中启用 web search,在docker run命令中添加WEBSEARCH_ENABLED='true'和WEBSEARCH_ENDPOINT(指向搜索后端)。参见 Web search 获取完整配置、端点契约和 SerpAPI 示例。
对于 Podman,用 podman run 替换 docker run,并使用 Podman pod 而不是 compose 网络。参见 Production deployment 获取 Podman 特定指南。参见 Podman deployment 获取完整示例。
对于原生数据库(数据库在主机或托管服务上运行,而不是在 Docker 中),删除 --network 标志并设置 DATABASE_HOST=host.docker.internal(Docker Desktop 和 Podman 4+)。在原生 Linux Docker 上,额外传递 --add-host=host.docker.internal:host-gateway。
等待五秒,然后验证:
curl http://localhost:8000/health
预期响应:
{"serviceName":"on-premises-http","uptime":5.123}成功启动日志(docker logs ai-service)Server is listening on port 8000.
启动日志不报告服务加载的数据库、Redis 或存储配置,因此请通过 Verifying the installation 中的检查确认这些。
如果容器立即退出,请运行
docker logs ai-service。最常见的原因记录在 故障排除指南中。前三个原因是:格式错误的AI_LICENSE_KEY(换行导致的换行),缺少PostgreSQL schema,以及PROVIDERS中的JSON语法错误。
创建环境和访问密钥
AI服务将用户隔离到各个环境中。每个环境都有自己的访问密钥。
打开管理面板:http://localhost:8000/panel/
使用
.env中的MANAGEMENT_SECRET进行登录。点击创建环境并为其命名(例如“Development”)。
记下显示的环境 ID(如
viOu8BnjJHb0HGK091p这样的短标识符)。在环境中,点击创建新的访问密钥。
复制环境 ID 和访问密钥。
使用新值更新 .env:
AI_ENV_ID='paste-environment-id-here' AI_API_SECRET='paste-api-secret-here'
务必通过管理面板 UI 创建环境。有关环境和访问密钥管理的详细信息,请参阅 JWT 认证指南。
创建令牌服务器
令牌服务器为编辑器签署 JSON Web Tokens (JWTs)。下面的 Node.js 示例仅用于演示;JWT 认证指南包含8种语言(Node、Django、Flask、Laravel、Rails、.NET、Go、Spring Boot)的生产就绪端点。
创建 package.json:
{
"name": "tinymceai-onpremise-demo",
"private": true,
"scripts": {
"start": "node token-server.js"
},
"dependencies": {
"dotenv": "^16.0.0",
"express": "^4.18.0",
"jsonwebtoken": "^9.0.0",
"tinymce": "^8.5.0"
}
}创建 token-server.js:
require('dotenv').config({ override: true });
const express = require('express');
const jwt = require('jsonwebtoken');
const path = require('path');
const PORT = process.env.PORT || 3000;
const AI_ENV_ID = process.env.AI_ENV_ID;
const AI_API_SECRET = process.env.AI_API_SECRET;
const AI_SERVICE_URL = process.env.AI_SERVICE_URL || 'http://localhost:8000';
if (!AI_ENV_ID || !AI_API_SECRET) {
console.error('ERROR: AI_ENV_ID and AI_API_SECRET must be set in .env');
console.error('Create an environment first: visit http://localhost:8000/panel/');
process.exit(1);
}
const app = express();
app.use(express.json());
app.use('/tinymce', express.static(path.join(__dirname, 'node_modules', 'tinymce')));
app.post('/api/ai-token', (req, res) => {
const token = jwt.sign({
aud: AI_ENV_ID,
sub: 'demo-user-001',
user: { name: 'Demo User', email: 'demo@example.com' },
auth: {
ai: {
permissions: [
'ai:conversations:*',
'ai:models:agent',
'ai:actions:system:*',
'ai:reviews:system:*'
]
}
}
}, AI_API_SECRET, { algorithm: 'HS256', expiresIn: '1h' });
res.json({ token });
});
app.get('/', (req, res) => {
res.send(`<!DOCTYPE html>
<html>
<head>
<title>TinyMCE AI on-premises Demo</title>
<script src="/tinymce/tinymce.min.js" referrerpolicy="origin"></script>
</head>
<body style="max-width: 900px; margin: 40px auto; font-family: system-ui;">
<h1>TinyMCE AI on-premises Demo</h1>
<p>Select text and use the AI toolbar, or open the AI chat sidebar.</p>
<textarea id="editor"><p>Select this text and try the AI features above. Ask the AI to rewrite it, summarize it, or change the tone.</p></textarea>
<script>
tinymce.init({
selector: '#editor',
license_key: 'your-license-key',
plugins: 'tinymceai',
toolbar: 'undo redo | blocks | bold italic | tinymceai-chat tinymceai-review tinymceai-quickactions',
height: 500,
tinymceai_service_url: '${AI_SERVICE_URL}',
tinymceai_token_provider: () =>
fetch('/api/ai-token', { method: 'POST' })
.then(r => r.json())
.then(data => ({ token: data.token }))
});
</script>
</body>
</html>`);
});
app.listen(PORT, () => {
console.log('Editor: http://localhost:' + PORT);
console.log('Token API: http://localhost:' + PORT + '/api/ai-token');
console.log('AI Service: ' + AI_SERVICE_URL);
});将token-server.js列表中的'your-license-key'替换为 TinyMCE 编辑器许可证密钥。这是 凭据部分所述的编辑器许可证密钥,而非 AI 服务的AI_LICENSE_KEY。有关获取和应用它的详细信息,请参阅 许可证密钥。
安装并运行
npm install
在启动演示之前,TinyMCE AI 插件必须存在于 TinyMCE 插件目录中。将 tinymceai 文件夹复制到 node_modules/tinymce/plugins/,或使用 external_plugins 选项从单独的路径加载它。
npm start
如果端口 3000 被之前的运行占用,请先停止现有进程:
lsof -ti :3000 | xargs kill
打开演示
在浏览器中打开 http://localhost:3000。编辑器会加载 AI 工具栏。选择文本并尝试 AI 功能。响应会从选定的 large language model (LLM) provider 实时流式传输,并完全在本地基础设施内处理。
TinyMCE AI 本地服务现在正在运行。
验证安装
完成快速入门后,在命令行运行以下每个检查以端到端演练流水线。
步骤 1:健康检查
此检查内容:AI 服务容器正在运行,并已连接到数据库和 Redis。
curl http://localhost:8000/health
预期响应:
{"serviceName":"on-premises-http","uptime":12.345}包含 serviceName 和 uptime 的 JSON 响应确认容器健康。如果请求失败或超时,请检查 docker logs ai-service 查找启动错误。
步骤 2:生成令牌
此检查内容:令牌服务器可以使用 API Secret 和 Environment ID 签署有效的 JWT。
curl -s -X POST http://localhost:3000/api/ai-token | python3 -m json.tool
预期响应:
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}包含 token 字段的 JSON 响应确认令牌服务器正在运行并能签署 JWT。如果服务器返回错误,请验证 AI_ENV_ID 和 AI_API_SECRET 已在 .env 中设置。
步骤 3:创建对话并发送消息
此检查内容:完整链路 — JWT 验证、权限、环境注册、LLM provider 认证以及 SSE 流式传输。
TOKEN=$(curl -s -X POST http://localhost:3000/api/ai-token | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")
curl -s -X POST http://localhost:8000/v1/conversations \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":"verify-1","title":"Verification"}'下面的命令使用内置的agent-1model。如果已明确配置MODELS,请将agent-1替换为已配置模型的id。请参阅 Defining the model list。
curl -s -N -X POST http://localhost:8000/v1/conversations/verify-1/messages \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"prompt":"Say hello in five words.","model":"agent-1"}'预期响应:一条 Server-Sent Events 流:
event: message-metadata
data: {"messageId":"abc123"}
event: text-delta
data: {"textDelta":"Hello "}
event: text-delta
data: {"textDelta":"there, "}
event: text-delta
data: {"textDelta":"friend!"}先出现 message-metadata 事件,随后是 text-delta 事件,且没有 error 事件,确认整个流水线工作正常:容器健康、数据库连接、Redis 连接、JWT 签名与验证、权限、环境注册、LLM provider 认证以及 SSE 流式传输。
如果流中发出 event: error,请检查 data 载荷。Provider 错误(API key 无效、IAM 拒绝访问、模型不可用)包含在 Server-Sent Events (SSE) 响应中。HTTP 状态码保持为 200。当 PROVIDERS 中的某个 provider 条目没有服务可用的凭据时,请求会在流开始之前失败,HTTP 状态码为 500,容器日志中会出现 NoValidApiKeysFoundError 原因。请参阅 Troubleshooting 指南中的 LLM provider errors 部分获取详情。
更新配置
修改.env值后,必须重新创建容器以加载新的环境变量。简单的重启(docker restart或docker compose restart)会保留旧值。
# Recreate the data layer (MySQL, Redis):docker compose up -d --force-recreate# Recreate the standalone AI service:docker stop ai-service && docker rm ai-service# Then re-run the launch script from "Launch the AI service" above.
对于 Kubernetes,请更新 Secret 并触发 rollout restart:
kubectl rollout restart deployment/ai-service -n tinymce-ai
停止并清理
# Stop the AI service (standalone Docker) docker stop ai-service && docker rm ai-service # Stop the Docker Compose stack docker compose down # Remove all data including volumes (destructive) docker compose down -v
对于 Kubernetes,将 deployment 缩放为零或删除它。数据库的 persistent volumes 除非显式删除否则会保留。
kubectl delete deployment ai-service -n tinymce-ai
后续步骤
快速入门验证了单台机器上的端到端堆栈。若要部署到生产环境,请按顺序完成以下每一篇指南:
数据库、Redis 和存储:配置受管数据库、设置 TLS,并搭建生产级文件存储。
LLM providers:配置明确的模型目录以及多提供商路由。
JWT 身份验证:构建具有适当权限和多租户隔离功能的生产环境令牌端点。
TinyMCE 集成:通过 CORS 和 CSP 将编辑器连接到生产环境的 AI 服务。
生产环境部署:部署到 Kubernetes 或 ECS,包含 TLS、伸缩性和可观测性。