类型:安装指南 + 使用手册 + 实战排障备忘
整理日期:2026-10-18
验证环境:Ubuntu Server、Docker Compose、SillyTavern Docker 镜像、Node.js v24.21.0、EasyTier、sub2api、mihomo 代理
适用对象:希望用 SillyTavern 接入 OpenAI 兼容接口,长期写中文小说、角色扮演,并启用向量检索记忆的用户。

0. 先说结论

这套方案不需要额外部署 ChromaDB。SillyTavern 自带 Vector Storage 扩展,选择 Local (Transformers) 时可以在服务端运行 Embedding 模型,将聊天记录向量化后按语义检索。写长篇小说时,推荐 世界书 + 作者注释 + Summarize + 向量检索 结合使用。

本次实际排障中,“全部向量化”最初提示 23 条消息失败,服务器日志报 TypeError: fetch failed、ETIMEDOUT;最终通过正确填写容器代理地址与端口、启用 Node.js 环境代理、换用多语言 Embedding 模型,再次向量化成功。


1. 本次环境与架构

电脑/手机浏览器
    |
    | http://<Ubuntu主机IP>:18088(仅可信网络)
    v
SillyTavern(Docker)
    |-- 小说生成 -> sub2api(OpenAI 兼容接口)
    |                http://10.144.144.1:18080/v1
    |
    |-- 聊天向量化 -> Local (Transformers) -> 本地模型
    |                    首次从 Hugging Face 下载
    |                    经 EasyTier -> HTTP代理 10.144.144.1:17890
    |
    |-- 角色卡 / 世界书 / 作者注释 / Summarize
    v
宿主机持久化目录 ~/ai-workstation/data/sillytavern/

本次实测参数

项目

值

SillyTavern 镜像

ghcr.io/sillytavern/sillytavern:latest

容器名称

sillytavern

浏览器端口

宿主机 18088 → 容器 8000

API 网关

http://10.144.144.1:18080/v1(sub2api)

EasyTier 远端设备地址

10.144.144.1

HTTP 代理地址

http://10.144.144.1:17890

代理容器端口映射

宿主机 17890 → 容器 7890

实测 Node.js 版本

v24.21.0

中文 Embedding 模型

Xenova/paraphrase-multilingual-MiniLM-L12-v2

特别注意:这些 IP、端口和路径对应本文作者的机器,不是 SillyTavern 的默认值。迁移部署时必须根据自己的网络修改。sub2api 和代理属于现有外部服务,本文不负责从零安装它们。


2. 安装 Docker 和 Compose

如果 Ubuntu 已经能正常执行 docker ps 与 docker compose version,可以直接跳到第 3 节。以下用于全新 Ubuntu 主机,参考 Docker 官方 Ubuntu 安装说明。

# 安装基础依赖
sudo apt update
sudo apt install -y ca-certificates curl

# 导入 Docker 官方仓库签名
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# 配置官方 APT 软件源
sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF_DOCKER
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF_DOCKER

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker

# 验证
docker --version
docker compose version
sudo docker run --rm hello-world

如果当前用户无 Docker 权限,可以暂时在命令前加 sudo。也可以把当前用户加入 docker 组,但docker 组基本等同于拥有宿主机 root 级权限,不要随意授权不可信账户。


3. 准备持久化目录

本文将 Compose 文件和数据分开存放,便于备份和迁移。

mkdir -p ~/docker_compose/sillytavern
mkdir -p ~/ai-workstation/data/sillytavern/{config,data,plugins,extensions}
cd ~/docker_compose/sillytavern

目录作用:

宿主机目录

容器目录

保存内容

config

/home/node/app/config

config.yaml 等服务配置

data

/home/node/app/data

用户数据、人物、聊天、世界书、向量存储等

plugins

/home/node/app/plugins

服务端插件

extensions

/home/node/app/public/scripts/extensions/third-party

第三方前端扩展

不能只备份 Compose 文件,因为真正的聊天和角色信息保存在持久化的数据目录中。


4. 编写 docker-compose.yml

下面是本次已成功运行的网络参数整理版,包含代理设置。若没有 mihomo/EasyTier,应删掉 HTTP_PROXY、HTTPS_PROXY、NODE_USE_ENV_PROXY,并根据实际情况调整 NO_PROXY。

services:
  sillytavern:
    image: ghcr.io/sillytavern/sillytavern:latest
    container_name: sillytavern
    hostname: sillytavern
    restart: unless-stopped

    ports:
      - "18088:8000"

    environment:
      - NODE_ENV=production
      - FORCE_COLOR=1
      - HTTP_PROXY=http://10.144.144.1:17890
      - HTTPS_PROXY=http://10.144.144.1:17890
      - NODE_USE_ENV_PROXY=1
      - NO_PROXY=localhost,127.0.0.1,10.144.144.1

    extra_hosts:
      - "host.docker.internal:host-gateway"

    volumes:
      - ~/ai-workstation/data/sillytavern/config:/home/node/app/config
      - ~/ai-workstation/data/sillytavern/data:/home/node/app/data
      - ~/ai-workstation/data/sillytavern/plugins:/home/node/app/plugins
      - ~/ai-workstation/data/sillytavern/extensions:/home/node/app/public/scripts/extensions/third-party

关于代理的三个细节:

  1. 这里采用 Compose 列表格式 - KEY=VALUE,不可混写成 - KEY: VALUE。

  2. 列表格式中应写 - NODE_USE_ENV_PROXY=1,不要写 - NODE_USE_ENV_PROXY="1",否则引号可能成为变量值的一部分。

  3. 17890 是该代理服务暴露在远端宿主机的端口;7890 是其容器内部端口。跨机器经 EasyTier 访问时使用前者。

如果只允许 SSH 隧道或本机访问,可将端口映射改为 127.0.0.1:18088:8000;需要局域网访问时再明确控制防火墙和访问认证。不要将未经保护的酒馆直接暴露公网。

启动容器

# 在 docker-compose.yml 所在目录执行
docker compose config -q          # YAML 校验,无输出一般即通过
docker compose pull
docker compose up -d

docker compose ps
docker logs --tail 80 sillytavern

第一次启动会拉取镜像、生成配置,耗时取决于网络。latest 标签会随版本变化;生产环境需要更可控时可固定为经过验证的版本标签。


5. 首次访问、监听与安全配置

浏览器访问:

http://<Ubuntu主机的局域网IP>:18088

如果打不开,先看日志、端口和本机访问:

docker compose ps
docker logs --tail 100 sillytavern
curl -I http://127.0.0.1:18088

SillyTavern 的 config.yaml 位于本文挂载的:

~/ai-workstation/data/sillytavern/config/config.yaml

如果需要从其他设备连接,通常需要将已存在的 listen 配置设为 true,同时正确配置白名单和/或认证。不同镜像版本的默认行为可能不同,以当前配置和启动日志为准。

示例(将示例网段换成真正使用的内网):

listen: true
whitelistMode: true
whitelist:
  - 127.0.0.1
  - ::1
  - 192.168.31.0/24

不要直接覆盖整个 config.yaml;在已生成的文件中编辑已有字段,并保留原有其他设置。Docker 网络、反向代理或 NAT 情况下,酒馆看到的客户端 IP 可能不同,应根据日志中的拒绝来源调整规则。不要为了方便直接关闭全部安全检查。

安全建议:使用私有网络或可信反向代理;为远程访问配置有效认证;代理、SillyTavern、sub2api 的端口不要无防护暴露公网。官方不建议直接公网端口转发酒馆。


6. 接入 sub2api / Gemini

这一步让酒馆负责前端、角色和提示词,让现有的 sub2api 负责模型 API 转发。

6.1 先测 API 是否通

从 Ubuntu 宿主机执行(把密钥替换成自己的,不要把真实密钥贴进博客):

curl -i http://10.144.144.1:18080/v1/models

未带密钥返回 401 Unauthorized,可能表示接口网络已经接通,但鉴权失败,并不一定是地址错误。

带密钥验证时推荐交互输入,减少密钥暴露在 shell 历史记录里的机会:

read -rsp 'sub2api key: ' SUB2API_KEY; echo
curl -sS -H "Authorization: Bearer ${SUB2API_KEY}" \
  http://10.144.144.1:18080/v1/models
unset SUB2API_KEY

确认返回的模型列表中包含你有权限使用的实际模型 ID。模型名称会随网关配置变化,以 /v1/models 返回值为准。

6.2 酒馆界面配置

进入顶部 API Connections(API 连接,插头图标):

  • API 类型:Chat Completion / 聊天补全。

  • 来源:Custom (OpenAI-compatible) / 自定义(兼容 OpenAI)。

  • API 地址:http://10.144.144.1:18080/v1。

  • API Key:填入 sub2api 的密钥。

  • 模型:从 /v1/models 返回的列表选择,例如本次使用过的 gemini-3.8-flash-high(属于网关中的模型标识,能否使用以实际返回为准)。

  • 执行连接或发送测试消息,确认获得模型回复。

地址填写 /v1,不要多填 /chat/completions。如果模型能列出但回复失败,检查是否拥有调用权限、模型标识是否正确、网关服务是否有上游错误。


7. 将 SillyTavern 用作小说创作工具

本次使用场景是万界旅行长篇小说:小伊为第三人称主角,阿莫尔和纳西妲按故事时间线逐渐加入;用户在故事外以观众/导演身份观看。

7.1 角色卡(Character Card)

角色卡负责规定 GM/小说作者是谁、世界基础、人物控制权、叙述方式。如果使用“观众模式”,角色卡中不要保留“禁止控制用户角色”“每轮等待玩家选择”等互动模式规则。

核心描述可以这样写:

你是长篇中文小说《界枢》的主持人和作者,不是第四名队员。
采用第三人称有限视角,以小伊为主要视角人物。
小伊、阿莫尔、纳西妲及其他角色都由作者自主描写。
人物必须遵循已建立的人设、能力限制、世界规则与剧情事实。
不输出选项菜单,不反复询问用户下一步做什么。
从小伊的第001次新人任务开始,不得把未来已规划的事件提前当作既定历史。
小说优先通过人物行动、对白和实际后果推进,减少模板化旁白。

7.2 世界书(World Info)

世界书用于长期稳定的设定:界律规则、界枢城、角色核心档案、人物关系、不同世界的知识。

条目类别

建议

界律、主要规则、当前主角核心人设

常驻条目(蓝色)

阿莫尔/纳西妲的细致服饰、技能细节

关键词触发(绿色)

具体世界的风俗、地名、敌人

关键词触发(绿色)

已完成任务的重大事实

定期人工确认并保存

蓝色常驻别放过多超长细节,否则上下文浪费严重。世界书的作用是在适当时提供设定,不是自动写入准确的剧情存档。

7.3 提示词管理器与作者注释

在 AI Response Configuration / 提示词管理器 中,为小说另存独立的预设,明确第三人称有限视角与自主推进要求。检查 Main Prompt 和 Post-History Instructions,清理与“观众模式”冲突的旧提示词。

Author's Note(作者注释) 适合记录当前任务编号、场景、伤势和临时风格重点。可先设为聊天内插入、深度 1、频率 1。注意:聊天专属作者注释不会自动传到新聊天,需要确认并重新设置。

示例:

【当前进度】第001次新人任务;小伊尚未与阿莫尔、纳西妲正式组队。
【叙述】第三人称有限视角,以小伊为主角,自主续写自然中文小说。
【限制】人物能力不能无依据增强;保留时间、位置、物资及行为后果的连续性。

7.4 如何开始、如何继续

新建聊天,检查没有携带旧任务的作者注释、历史摘要或“第004次组队”世界书条目;先从第001次新人试炼开场。首次可发送:

【导演指令】从小伊的第一次任务开始,使用第三人称有限视角写正式小说正文。
由你自主安排人物行动与剧情,不用询问我的选择,不生成选项菜单。
不要急于让阿莫尔和纳西妲登场,重视人物行动、空间关系、实际困难与后果。

以后发送“继续”即可继续生成;它不会在后台无人触发地一直写。发现设定冲突时,用导演指令纠正,然后根据情况修改世界书或作者注释。


8. 长篇小说的四层记忆

层次

作用

推荐存放内容

世界书

稳定事实

世界机制、长期人物性格、固定能力

作者注释

当下状态

当前场景、目标、任务进度、伤势

Summarize

阶段摘要

最近数章发生的事、关系变化、重大决定

Vector Storage

历史语义检索

过去的聊天片段、旧约定、相似事件

8.1 Summarize(总结)

在顶部 Extensions / 扩展 → Summarize(总结) 中开启,来源选择 Main API 即可复用当前模型。可以先手动试一次,再决定是否每约 15~30 条消息更新一次(实际以篇幅和上下文消耗调整)。

建议摘要重点保留:时间地点、任务条件、角色状态、装备和金钱变化、重要对话、承诺、关系变化、尚未解决的问题。

摘要是模型写的,可能遗漏或编造内容,需要定期人工检查;不建议将其视为绝对可靠的数据库。

8.2 Vector Storage(向量存储)

SillyTavern 自带向量扩展。其本地向量存储使用 Vectra,并保存在酒馆用户数据目录中的 vectors 文件夹。它不等同于已有 ChromaDB,不能直接填 ChromaDB 的 IP 就完成对接。

开启路径:顶部 Extensions / 扩展 → Vector Storage / 向量存储。

本次使用的初始配置:

选项

取值

Vectorization Source

Local (Transformers)

Enabled for chat messages

开启

Query messages

3

Score threshold

0.25

Insert#

3

Retain# / 保持#

5

Chunk size

400

Include hidden messages

关闭

Vector Summarization

暂时关闭

文件向量化

暂时关闭

注入模板:

【与当前剧情相关的历史片段】
{{text}}

{{text}} 是占位符,不能删。这些值只是本次运行的起始参数,不是通用最优值。

8.3 修改中文 Embedding 模型

SillyTavern 默认的本地模型是 Cohee/jina-embeddings-v2-base-en,更偏向英文;中文小说可先换成支持多语言的:

Xenova/paraphrase-multilingual-MiniLM-L12-v2

打开挂载目录下的 config.yaml,修改已有 extensions.models.embedding:

extensions:
  models:
    autoDownload: true
    embedding: Xenova/paraphrase-multilingual-MiniLM-L12-v2

这段仅表示配置的层级,不要覆盖原配置文件的其他 extensions 字段,也不要重复添加同名节点。修改后重启容器:

docker restart sillytavern

首次使用 Local (Transformers) 时会从 Hugging Face 下载模型配置、Tokenizer 和 ONNX 权重。模型能否成功下载依赖服务器实际网络;默认主要由 Node.js 本地运行,通常不需要 RTX 3090 显存。

换了 Embedding 模型或向量来源后,旧索引需要重新生成,不同模型的向量不应混用。

8.4 首次生成和验证

在“聊天消息向量化”区域点击 Vectorize All / 全部向量化,完成后点击 View Stats / 查看统计数据,确认当前聊天向量数量增加。聊天向量区域的 Purge Vectors 只清理当前聊天对应索引;不要误点文件向量化区域的同名按钮。

成功生成向量不等于一定能准确找回故事伏笔;后续仍需要通过实际剧情与提示词检查验证检索质量。


9. 本次排障全记录:23 条消息被跳过

问题一:点“全部向量化”后失败

界面提示约 23 条消息未能向量化。查看容器日志:

docker logs --since 10m sillytavern 2>&1 | \
  grep -Ei 'error|vector|embed|transform|onnx|fetch|timeout' | tail -n 80

最初的关键输出:

Initializing transformers.js pipeline for task feature-extraction
with model Cohee/jina-embeddings-v2-base-en
[TypeError: fetch failed]
AggregateError [ETIMEDOUT]
connect ETIMEDOUT 199.59.149.206:443
connect ENETUNREACH <某IPv6地址>:443

含义:酒馆尝试初始化本地 Embedding 模型,但对外请求失败。ETIMEDOUT 代表连接超时,ENETUNREACH 代表对应网络不可达。当时不应先删除数据库或重装容器,应先确认模型下载路径与代理。

问题二:Compose 的 environment 写法混用

错误例子:

environment:
  - NODE_ENV=production
  - HTTP_PROXY: http://10.144.144.1:7890

这里在列表格式的 - 后写了映射对象。统一改成:

environment:
  - NODE_ENV=production
  - HTTP_PROXY=http://10.144.144.1:17890
  - HTTPS_PROXY=http://10.144.144.1:17890
  - NODE_USE_ENV_PROXY=1
  - NO_PROXY=localhost,127.0.0.1,10.144.144.1

同时将错误写法 - NODE_USE_ENV_PROXY="1" 改为 - NODE_USE_ENV_PROXY=1。

修改 Compose 后不能只执行 docker restart,需要重新创建容器才能载入新的环境变量:

docker compose config -q
docker compose up -d --force-recreate sillytavern

问题三:代理宿主机端口与容器端口混淆

已有 mihomo 服务端口映射为:

0.0.0.0:17890  -->  7890/tcp

因为 SillyTavern 通过 EasyTier 访问另一台机器的宿主机,应当使用 10.144.144.1:17890,而不是直接用内部端口 7890。即使远程地址能 ping 通,也不代表 HTTP 代理端口一定可用。

此处代理服务的实际监听地址必须可从对应 Docker 容器到达。将代理暴露为 0.0.0.0:17890 时,需要限制来源,避免形成公开代理。

问题四:Node.js 是否真的走代理?

首先确认容器内的 Node.js 和环境变量:

docker exec sillytavern node -p \
"'Node: ' + process.version + ' | NODE_USE_ENV_PROXY: ' +
JSON.stringify(process.env.NODE_USE_ENV_PROXY) + ' | HTTPS_PROXY: ' +
process.env.HTTPS_PROXY"

本次实测:

Node: v24.21.0 | NODE_USE_ENV_PROXY: "1" | HTTPS_PROXY: http://10.144.144.1:17890

这里 JSON.stringify 输出的 "1" 正常;若出现额外转义的内层引号,则需要检查变量值。Node.js 22.21.0+ / 24.x 的支持版本可通过 NODE_USE_ENV_PROXY=1 启用内置代理功能;旧版本不能假定相同行为。

问题五:验证代理到 Hugging Face 的完整链路

第一层:宿主机经 HTTP 代理访问。

curl -vL --max-time 20 --noproxy "" \
  -x http://10.144.144.1:17890 \
  -o /dev/null -w '\nHTTP: %{http_code}\n' \
  'https://huggingface.co/Xenova/paraphrase-multilingual-MiniLM-L12-v2/resolve/main/config.json'

本次日志显示:

Connected to 10.144.144.1 port 17890
CONNECT huggingface.co:443 HTTP/1.1
HTTP/1.1 200 Connection established
HTTP/2 307
HTTP/2 200
HTTP: 200

其中 307 是模型文件请求的正常重定向,-L 让 curl 跟随后得到 200。这证明代理能够访问模型配置文件,但不能保证大体积权重下载必然成功。

第二层:容器中用 Node.js fetch 访问。

docker exec sillytavern node -e "
fetch('https://huggingface.co/Xenova/paraphrase-multilingual-MiniLM-L12-v2/resolve/main/config.json', {
  signal: AbortSignal.timeout(15000)
})
.then(r => console.log('HTTP:', r.status))
.catch(e => console.error('ERROR:', e))
"

实测结果:

HTTP: 200

第三层:在酒馆重新点“全部向量化”。

模型下载与初始化最终成功,向量化成功。这才证明本次从下载到向量生成的整体流程已经跑通。

快速诊断表

现象

优先检查

fetch failed

Hugging Face 下载、代理、DNS、Node 网络

ETIMEDOUT

连接超时、代理未生效、出口不通

ENETUNREACH

网络路由或 IPv6 不可达

宿主机 curl 连接代理失败

代理地址、宿主机端口映射、防火墙

curl 成功、Node fetch 失败

NODE_USE_ENV_PROXY、Node 版本、容器环境和网络

日志还是旧 Embedding 模型名

config.yaml 修改位置或重启未生效

Node fetch 成功但仍无法向量化

ONNX 权重下载、模型文件路径、运行时错误

返回 401(sub2api)

服务可达但密钥未提供或无效

新聊天带入旧剧情

世界书、作者注释、Summarize、历史记录未隔离


10. 日常维护、更新与备份

常用命令

# 查看服务
docker compose ps

# 实时日志
docker logs -f --tail 100 sillytavern

# 检查容器运行环境
docker exec sillytavern node -v
docker exec sillytavern printenv | grep -i proxy

# 查看映射目录
docker inspect sillytavern --format \
'{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'

# 修改 config.yaml 后重启
docker restart sillytavern

# 修改 docker-compose.yml 环境变量后重建
docker compose up -d --force-recreate sillytavern

# 更新镜像前先备份,再执行
docker compose pull sillytavern
docker compose up -d sillytavern

数据备份

重要数据在 ~/ai-workstation/data/sillytavern/。备份前建议先停止服务,以减少聊天写入过程中打包产生不一致的概率:

# 在 Compose 所在目录操作
docker compose stop sillytavern
mkdir -p ~/backups

tar -czf ~/backups/sillytavern-$(date +%F-%H%M).tar.gz \
  -C ~/ai-workstation/data sillytavern

docker compose start sillytavern

同时备份 docker-compose.yml,以及另外保存 API 密钥(不要把密钥公开在仓库或博客里)。迁移时恢复映射目录与 Compose 后再启动即可。

经验与限制

  1. 向量记忆不是精确数据库:金额、装备数量、任务完成状态等应有可靠的独立记录。

  2. “继续”不是精确的检索关键词:向量检索依赖周边聊天上下文,必要时可以用“导演指令”补充场景与人物。

  3. 不要将所有角色细节设为常驻:世界书过大不仅费 Token,也会和当前剧情争夺注意力。

  4. 不要频繁无检查升级 latest:尤其是正在连载的长篇小说,建议有备份、有回滚点。

  5. 修改代理、模型、索引分开验证:先确认每层网络和配置正确,再考虑删缓存或重装。


11. 最终验收清单

  • Docker、Compose 正常工作,sillytavern 容器运行中。

  • 通过可信网络可以打开酒馆网页,并配置白名单/认证。

  • 自定义 OpenAI 兼容接口可以连接 sub2api,并返回模型回复。

  • 小说的角色卡、世界书、作者注释与提示词没有互相冲突。

  • Summarize 能生成并保存阶段摘要。

  • config.yaml 使用正确的中文 Embedding 模型。

  • curl 经代理访问 Hugging Face 返回 HTTP 200。

  • 容器内 Node.js fetch 返回 HTTP 200。

  • Vector Storage “全部向量化”成功,“查看统计数据”有向量。

  • 已有定期备份策略,不向公网开放无保护的酒馆和代理端口。

12. 参考资料


最后一句备忘:以后再看到 Vectorization partial、fetch failed,别急着怀疑数据库。先确认 Embedding 模型、容器代理环境变量、宿主机映射端口和 Node.js 实际能否访问 Hugging Face。这里每一层都验证通过,本次故障才最终解决。