本指南可在任意安装了 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 提供者,扩展 PROVIDERS JSON:{"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服务将用户隔离到各个环境中。每个环境都有自己的访问密钥。

  1. 打开管理面板:http://localhost:8000/panel/

  2. 使用 .env 中的 MANAGEMENT_SECRET 进行登录。

  3. 点击创建环境并为其命名(例如“Development”)。

  4. 记下显示的环境 ID(如 viOu8BnjJHb0HGK091p 这样的短标识符)。

  5. 在环境中,点击创建新的访问密钥。

  6. 复制环境 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:

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-1 model。如果已明确配置 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

后续步骤

快速入门验证了单台机器上的端到端堆栈。若要部署到生产环境,请按顺序完成以下每一篇指南:

  1. 数据库、Redis 和存储:配置受管数据库、设置 TLS,并搭建生产级文件存储。

  2. LLM providers:配置明确的模型目录以及多提供商路由。

  3. JWT 身份验证:构建具有适当权限和多租户隔离功能的生产环境令牌端点。

  4. TinyMCE 集成:通过 CORS 和 CSP 将编辑器连接到生产环境的 AI 服务。

  5. 生产环境部署:部署到 Kubernetes 或 ECS,包含 TLS、伸缩性和可观测性。