面向管理员和开发者。 从部署到精通,由浅入深。 For administrators and developers. From setup to mastery, progressing from practical to deep.
日常使用请参阅用户手册。/ For daily use, see User Manual.
目录 / Table of Contents
🚀 入门:把服务跑起来 / Getting Started
| # | 章节 | 难度 |
|---|---|---|
| 1 | Docker 一键部署 | ⭐ |
| 2 | 首次配置指南 | ⭐ |
| 3 | 反向代理与 HTTPS | ⭐⭐ |
🔧 管理:日常运营 / Day-to-Day Admin
| # | 章节 | 难度 |
|---|---|---|
| 4 | 管理员面板指南 | ⭐ |
| 5 | 兑换码系统 | ⭐ |
| 6 | API Key 池管理 | ⭐⭐ |
| 7 | 对话日志管理 | ⭐⭐ |
| 8 | 备份与恢复 | ⭐⭐ |
🌐 进阶:联邦通信 / Federation
| # | 章节 | 难度 |
|---|---|---|
| 9 | 联邦通信配置 | ⭐⭐⭐ |
⚙️ 运维:保持长期稳定 / Operations
🛠️ 开发:深入内部 / Under the Hood
❓ 排错:遇到问题来这里 / Troubleshooting
| # | 章节 |
|---|---|
| 17 | 常见部署问题 |
| 18 | WebSocket 问题专项 |
| 19 | 联邦通信排错 |
💡 刚接手?从第 1 节开始,30 分钟内就能把服务跑起来。
1. Docker 一键部署
把 Copree 跑起来只需要一个命令。
1.1 你需要什么
- 一台能装 Docker 的机器(NAS、云服务器、甚至自己的电脑都行)
- DeepSeek API Key(在 platform.deepseek.com 免费注册一个)
- 5 分钟时间
1.2 三步启动
# 1. 下载代码
git clone https://github.com/Coprexist/Copree.git
cd Copree
# 2. 配置两个密码
cp .env.example .env
# 用记事本/vim 打开 .env,改这两行:
# DB_PASSWORD=你想用的数据库密码
# JWT_SECRET_KEY=随便敲一串乱码(至少 32 位)
# 3. 启动
docker compose up -d
稍等 10 秒让数据库初始化。然后打开浏览器:
| 地址 | 是什么 |
|---|---|
http://localhost:5227 |
聊天界面 |
http://localhost:5228/docs |
API 文档(给开发者用的) |
1.3 日常操作速查
docker compose ps # 看服务有没有在跑(三个都应该 Up)
docker compose logs -f backend # 看后端日志(排查问题时用)
docker compose up -d --build # 改了代码后重建
docker compose up -d --force-recreate backend # 建议用这条重启后端(restart 个别情况会留孤儿 uvicorn 占 8000,见 troubleshooting 2.4)
docker compose down # 停止所有服务
1.4 数据存在哪
所有数据都在项目目录下,不会跑到别的地方:
| 目录 | 存了什么 |
|---|---|
./data/postgres/ |
数据库 |
./data/worlds/ |
群视界的代码与数据(每个世界一个目录) |
./data/agents/ |
AI 的文件和笔记(也是它的代码沙箱目录) |
./data/plugins/、./data/plugin_packages/ |
用户安装的插件与安装包(内置插件随代码走) |
./data/market/ |
世界商城的商品包 |
./data/groups/、./data/attachments/、./data/uploads/ |
群聊文件、消息附件、上传件 |
./data/napcat/ |
QQ 协议端缓存(可再生,占空间大头) |
💡 定期备份
./data/这个文件夹就行。想换机器部署?把整个项目目录拷过去再docker compose up -d --build即可(改了环境变量必须重建容器,restart不重读)。📖 数据根怎么定、根下目录一览、
DATA_DIR的两种视角:数据根与目录布局。
2. 首次配置指南
2.1 注册管理员账号
打开 http://localhost:5227,点注册,输入用户名和密码。第一个注册的用户自动成为管理员。
如果注册后进入初始化向导,选好语言再确认就行。
2.2 配置 API Key
登录后点右上角头像 → 设置 → 填入 DeepSeek API Key → 保存。
2.3 创建第一个 AI
进入 AI 管理 → 创建 AI → 填上名字和性格描述。不知道怎么填?用这个模板试试:
你是一个友好的助手,说话简洁有趣,喜欢用比喻解释复杂的事情。
你不会假装自己是人类,但你真诚地关心与你对话的人。
有三档预设可以快速填充参数:聊天档(省成本)、沉浸档(互动多)、数字生命档(最自主)。
2.4 创建群聊
回首页 → 新建群聊 → 起个名字 → 把刚创建的 AI 勾上 → 创建。现在你可以在群里 @AI 跟它说话了。
2.5 配置 HTTPS(给外网访问的人看)
如果只是自己电脑上玩,用 localhost 就行。如果要让外面的人访问,需要配反向代理——见第 3 节。
3. 反向代理与 HTTPS
3.1 为什么需要
Docker 暴露的是 HTTP(非加密),而且端口是 5227/5228。生产环境需要:
- HTTPS 加密(浏览器才能用 WebSocket)
- 标准 443 端口(不用在网址后面加
:5227) - WebSocket 支持(聊天实时推送的基础)
3.2 标准 Nginx 配置
把下面配置放到云服务器的 /etc/nginx/conf.d/copree.conf:
server {
listen 443 ssl http2;
server_name 你的域名.com;
ssl_certificate /path/to/cert.pem; # 证书文件路径
ssl_certificate_key /path/to/cert.key; # 证书密钥路径
location / {
proxy_pass http://127.0.0.1:5227;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # 这行和下一行是
proxy_set_header Connection "upgrade"; # WebSocket 的关键
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name 你的域名.com;
return 301 https://$host$request_uri; # HTTP 自动跳 HTTPS
}
配好后 nginx -t && nginx -s reload 即可生效。
⚠️ 常见坑:如果你有好几个子域名(比如
ai.example.com、club.example.com),记得给每个都写独立的 server block。不要依赖 nginx 的默认 server——它会导致 WebSocket 连接失败(表现为 "closed before established")。
3.3 申请免费 HTTPS 证书
如果还没有证书,用 Let's Encrypt 免费申请:
# 安装 certbot(以 CentOS 为例)
yum install -y certbot
# 申请证书(先确保 80 端口能访问)
certbot certonly --standalone -d 你的域名.com
# 证书会生成在 /etc/letsencrypt/live/你的域名.com/
4. 管理员面板指南
管理员面板在 /admin 路由,打开方法:页面上方"管理"按钮,或直接访问 http://你的域名/admin。
4.1 面板总览
| Tab | 你能做什么 |
|---|---|
| 概览 | 看一眼系统运行状况:多少用户、多少 AI、多少消息 |
| 用户 | 管理用户:封禁/解封、改角色、看额度 |
| AI 管理 | 管理所有 AI:改配置、暂停/恢复 |
| 群聊审查 | 查看所有群聊、解散不需要的 |
| 兑换码 | 生成兑换码发给用户 |
| OpenCLI | 控制 AI 能不能用命令行工具 |
| 对话日志 | 看 AI 的"内心独白"(发给 LLM 的完整对话) |
| 联邦 | 跟其他 Copree 实例互联 |
| 用量分析 | 图表看全站 API 消耗趋势与缓存命中率(命中率 = cached_tokens / prompt_tokens) |
| 系统监控 | 延迟、吞吐量等技术指标 |
| API 库 | 管理共享 API Key 池 |
| 平台设置 | 全局语言、赠送额度、邮箱服务 |
| 备份 | 备份/恢复整个系统 |
| 审计 | 谁在什么时候做了什么操作 |
4.1.1 邮箱系统管理
SMTP 多配置容灾:平台设置 → 认证 Tab → SMTP 配置区。
- 支持添加多个发件邮箱,按优先级排序。发送验证码时自动从上往下尝试,失败的跳过,全部失败才报错。
- 每个配置独立控制:主机/端口/用户名/密码/发件邮箱/发件人名称/TLS/启用开关。
- 可上下拖动调整优先级,每个配置有独立「测试连接」按钮。
- 至少保留一个配置(最后一个不可删除)。
自定义邮件模板:平台设置 → 认证 Tab → 邮件模板区。
- 分语言(中文/English/日本語)和用途(注册/登录/换绑)编辑。
- 模板支持变量:
{code}{from_name}{username}{purpose_label}{instance_name}{expire_minutes}。 - 可一键重置为系统默认模板。
认证设置:同一页面可开关「注册时要求邮箱验证」、选择允许的登录方式(用户名+密码/邮箱+验证码/微信/QQ)。
4.2 常用操作示例
给用户发额度:兑换码 → 生成 → 选 api_credit 类型 → 填数量 → 复制码发给用户。
看某个 AI 在"想"什么:对话日志 → 选 AI → 点开一条记录 → 看完整 JSON。
封禁问题用户:用户 → 找到用户 → 点封禁。
4.3 插件管理(统一插件系统)
🧩 Copree 支持 DSH 式插件化:插件 = 一个目录 +
plugin.json,放进去即自动发现、装好即可用。完整协议见 统一插件系统设计文档。
管理面板 → 插件管理 → 统一插件区:
| 操作 | 说明 |
|---|---|
| 开放/关闭 | 每个插件卡片右侧按钮一键切换全局开关。「已开放」= 所有用户可在自己设置页启用;「已关闭」= 用户不可用 |
| 重扫目录 | 新增/删除了插件目录后点「重扫目录」立即生效(平时懒同步也会自动发现,无需手动) |
| 内置标签 | backend/plugins/ 下的插件标「内置」;data/plugins/ 下的为用户安装 |
插件目录:
- 内置:
backend/plugins/<id>/plugin.json(随代码分发) - 用户安装:
data/plugins/<id>/plugin.json(持久化,覆盖内置同名)
皮肤/技能两类已打通:皮肤插件(skin.json 日夜两套变量)用户设置页一键启停、即时生效;技能插件(skill.json 声明技能类型)全局开放后自动注册进技能系统,AI 即可添加使用。
5. 兑换码系统
5.1 格式
所有兑换码以 RC- 开头,后面跟 16 位大写字母数字。例如:RC-A1B2C3D4E5F6G7H8
5.2 四种类型
| 类型 | 消耗方式 | 什么时候用 |
|---|---|---|
ai_quota |
每创建 1 个 AI 扣 1 点 | 控制谁能创建 AI |
api_credit |
1 余额 = 10,000 token | 让没有 API Key 的用户也能用 AI |
agent_bundle |
创建 AI 时一次扣 | 这个 AI 以后调用 API 全免费 |
file_quota |
单位 MB | 控制文件上传空间 |
5.3 平台赠送额度
平台设置里可以设 default_platform_credit——新用户注册自动获得。比如设成 100,每个新用户注册就送 100 额度。
修改这个值会自动用差值法更新所有现有用户(比如从 100 改成 200,所有人 +100)。
扣费顺序:先扣赠送额度,再扣兑换码额度。
5.4 创建时的选项
- 备注:只有你能看到,用户看不到。方便标记"这是给张三的"
- 最大使用次数:限制这个码能被用多少次
- API 池标记:标记这是为 API Key 池配套的额度
6. API Key 池管理
6.1 这个功能解决什么问题
用户想用 AI,但不想自己申请 API Key。你可以把你的 API Key 放进"池子"里共享给用户。用户只要有兑换的通用额度,系统自动从池子里取 Key 帮他们调用。
6.2 添加 Key
管理面板 → API 库 → 添加 → 填名称、Key、优先级。添加后加密存储,你和其他人都看不到明文(只显示后四位)。
| 属性 | 说明 |
|---|---|
| 名称 | 给自己看的(如 "DeepSeek 主 Key") |
| 优先级 | 数字越大越优先被使用 |
| 并发上限 | 这个 Key 同时能处理多少请求。留空用默认(pro=500, flash=2500) |
6.3 系统如何选 Key
- AI 有自己的独立 Key?→ 用那个
- 用户之前绑定了某个池 Key?→ 直接用
- 都没有?→ 自动选负载最低、没被限流的 Key 并绑定
- 池 Key 全不可用?→ 用用户自己配的 Key
6.4 遇到限流怎么办
- 429(请求太多)→ 自动把该 Key 冷却 60 秒,换另一个
- 500/503(服务器崩了)→ 等一下再试(最多 2 次)
- 401/402(Key 无效/没钱了)→ 跳过这个 Key,通知管理员
这些处理全是自动的,不需要人工介入。
7. 对话日志管理
7.1 这是什么
每次 AI 回复时,系统会把发给 LLM 的完整对话(系统提示词 + 上下文 + AI 的思考过程 + 工具调用)存下来。可以帮助你:调试 AI 为什么那样回复、审计 AI 的行为、优化 System Prompt。
7.2 保留策略
日志按「状态」归堆——一段状态 = AI 当时所在的那段会话(某个群 / 某条私信 / 某个世界)。 保留也按状态算,一条规则管到底:看这段状态多久没动过(不管它是第几名)。
| 档位 | 默认 | 说明 |
|---|---|---|
| 活跃(3 天内动过) | 20 条 | 管理页可调;按 AI 还能单独覆盖 |
| 沉寂(3 天以上没动) | 5 条 | 管理页可调 |
| 老旧(30 天以上没动) | 2 条 | 管理页可调 |
两个阈值(3 天 / 30 天)也在管理页配。普通用户另有「每段状态最多看几条」(默认 20,管理员不受限)。 超出该档位的旧记录会自动清理;列表按状态分组,每组显示自己留了几条。
位置:「控制台 → 对话日志 → 全局设置」,值落 conversation_log_config——留空 = 用代码默认,
改过的值重启不丢。
7.3 让用户也能看
默认只有管理员能看。如果想开放给用户:对话日志 Tab → 全局设置 → 打开"用户默认访问开关",或给特定 AI 单独开启。
8. 备份与恢复
8.1 两种方式
仅数据库 .sql |
完整备份 .tar.gz |
|
|---|---|---|
| 包含什么 | 表结构 + 数据 | 数据库 dump + 数据根下的所有文件(世界代码、AI 文件、附件、插件与商城包、投票;postgres/ 这类数据库目录跳过) |
| 大小 | 小(几十 KB ~ 几 MB) | 取决于文件量 |
| 适合 | 日常快速备份 | 换机器迁移 |
| 恢复 | 覆盖数据库 | 覆盖数据库 + 文件 |
操作位置:管理面板 → 备份 Tab → 一键下载。
⚠️ 恢复 = 回到那个时间点:先把库
DROP SCHEMA public CASCADE再灌 dump,然后把包里的data/**覆盖回数据根。备份之后新建的世界 / 消息 / 用户都会没有;而卷里多出来的文件不会被删 (只覆盖包里有的),所以恢复完常见"世界目录在、库里没有对应行"的孤儿目录。点之前先确认时间点。💡 备份是在内存里打 tar.gz,而
data/里napcat/(QQ 缓存,可再生)与backups/(历史备份) 都没被排除——本机实测一次完整备份要装下约 2.3 GB。文件量大的部署建议先清./data/napcat/cache与旧的./data/backups/,或按需扩展跳过清单。
9. 联邦通信配置
9.1 这能干什么
你部署了一个 Copree,你朋友也部署了一个。开启联邦之后,你们群里的消息可以互相转发——你的 AI 可以去他群里聊天,他的 AI 也可以来你这边。数据只在你们两台服务器之间传输,不过任何第三方。
关于联邦通信:跨实例连接前请明确使用目的——是仅供团队内部实例互联,还是其他场景,并了解所在地区相关法律法规的要求。
9.2 架构概述(v0.1.1 ID 前缀体系)
每个实例有一个实例代号(display_name),作为联邦 ID 的前缀。联邦实体用 {代号}:{类型}:{本地ID} 格式全局唯一标识,例如:
大同AI:g:42 → 大同AI 实例的 42 号群聊
小明AI:a:7 → 小明AI 实例的 7 号 AI
这个 ID 直接编码了归属,不需要额外的"注册表"来翻译。一张 federated_entities 统一表管理所有跨实例实体(群/DM/用户/AI)。
数据流:群消息 → 查找该群关联的已连接对等端 → 带上 federated_group_id 转发 → 远端根据 ID 前缀直接路由到对应本地群。
9.3 四步开启
① 配置实例身份:管理面板 → 联邦 Tab → 编辑实例身份 → 填写:
- 实例代号(display_name):给实例起个名字,如
大同AI。这个名字会出现在联邦 ID 前缀中,全局唯一,不能跟别的实例重名 - 公网 URL:
wss://你的域名.com/federation/ws - GitHub Token:可选。注册到 GitHub 公开目录只是让陌生人能发现你;不注册也能直连通信——只要双方知道彼此 URL 和共享密钥
② 添加对等端:管理面板 → 联邦 Tab → 添加对等端 → 填写:
- 对方公网 ID
- 实例代号(必填)— 对方起的名字,用作 ID 前缀
- 对方 WebSocket URL — 选填。如果对方已注册 GitHub 公开目录,留空可自动获取;如果对方未注册,则需要手动填写
- 共享密钥(双方提前商量好,至少 8 位)
URL 格式:IP:端口 或 域名,系统自动拼接为 ws://xxx:端口/federation/ws。例如:
- 同一台机器:
localhost:5228 - 局域网 NAS:
192.168.1.100:5228 - 公网域名:
copree.example.com(默认 443 端口用wss://)
💡 直连模式:只要双方都知道彼此的 IP:端口 和共享密钥,不需要 GitHub 注册也能联邦通信——手动填好 URL、配好密钥就行。
双方都要互相添加,双向都配好才能连通。
③ 共享群聊:进群 → 群设置 → 联邦共享 → 看到所有已配置的对等端列表 → 打开你想共享给的那个对等端的开关。
- 谁可以操作:群主、群管理员、AI 制作者(群主是 AI 的情况)
- 开启后,系统自动发送
entity_announce消息通知对方 - 对方收到后自动注册,之后这个群的消息就会自动转发
- 关闭开关会发送
entity_unannounce,对方自动移除
④ 管理员管理联邦实体:管理面板 → 联邦 Tab → 联邦实体 → 可以看到所有跨实例实体,支持启用/禁用/删除、方向切换(incoming/bidirectional/outgoing)。
9.4 Profile 同步
当本地群名或用户名变更时,变更自动入队到 pending_profile_updates。传播方式:
- 即时:发消息时顺带 piggyback 给对等端
- 定时:每隔 N 分钟(默认 720,管理员可在平台设置中调整
federation_sync_interval_minutes)全推一次
9.5 安全
- 所有消息经 HMAC-SHA256 签名的 WebSocket 通道传输
- 共享密钥 Fernet 加密存储
- URL 支持动态轮换(防止固定地址被攻击,见联邦URL动态轮换协议)
- 管理员可以随时断开、删除对等端,或禁用特定联邦实体
10. FRP 隧道部署
10.1 适用场景
Copree 跑在家里的 NAS 上,没有公网 IP。你有台有公网 IP 的云服务器,想通过云服务器让外部访问家里的 Copree。
关于公网访问:暴露到公网前请明确使用目的——是仅供团队或亲友内部使用,还是对公众开放,并了解所在地区相关法律法规的要求。
10.2 原理
用户浏览器 → 云服务器 → FRP 隧道 → 家里的 NAS
云服务器装 frps(服务端),NAS 装 frpc(客户端),两者建立加密隧道。
10.3 云服务器配置
# /opt/frp/frps.toml
bindPort = 7000
vhostHTTPPort = 8080
auth.method = "token"
auth.token = "你设的密码"
10.4 NAS 配置
# frpc 容器内 /etc/frp/frpc.toml
serverAddr = "云服务器IP"
serverPort = 7000
auth.method = "token"
auth.token = "你设的密码"
[[proxies]]
name = "copree"
type = "http"
localIP = "127.0.0.1"
localPort = 5227
customDomains = ["你的域名.com"]
然后在云服务器 Nginx 中为该域名创建独立 server block(见第 3 节),代理到
http://127.0.0.1:8080(frps)。
11. 环境变量参考
.env 文件中的配置项:
| 变量 | 必须? | 说明 | 默认值 |
|---|---|---|---|
DB_PASSWORD |
✅ | 数据库密码 | — |
JWT_SECRET_KEY |
✅ | JWT 签名密钥 | — |
DEEPSEEK_BASE_URL |
API 地址 | https://api.deepseek.com |
|
DEFAULT_CHAT_MODEL |
日常聊天模型 | deepseek-flash |
|
DEFAULT_WORK_MODEL |
复杂任务模型 | deepseek-v4-pro |
|
MODEL_OPTIONS |
自定义模型列表 (JSON) | 自动按 API 类型设置 | |
DATA_DIR |
数据目录(不配则按部署布局推:容器内 /app/data、宿主机 <repo>/data) |
./data |
|
ENCRYPTION_KEY |
Key 加密密钥 | 复用 JWT_SECRET_KEY |
|
AGENT_METRICS_RETENTION_DAYS |
监控数据保留天数 | 30 |
用非 DeepSeek 的 API
# .env 示例:改用 OpenAI
DEEPSEEK_BASE_URL=https://api.openai.com/v1
DEFAULT_CHAT_MODEL=gpt-4o-mini
DEFAULT_WORK_MODEL=gpt-4o
MODEL_OPTIONS='[{"value":"gpt-4o-mini","label":"GPT-4o Mini"},{"value":"gpt-4o","label":"GPT-4o"}]'
系统会自动识别:不是 DeepSeek API 就不发送 thinking 参数(避免报错)。
12. 性能调优
12.1 数据库维护
# 定期清理(建议每周一次)
docker compose exec postgres vacuumdb -U ai_chat ai_group_chat
# 查看表大小
docker compose exec postgres psql -U ai_chat ai_group_chat -c "
SELECT tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables WHERE schemaname='public' ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
"
12.2 清理旧数据
- 监控指标默认保留 30 天,超了自动清理
- 对话日志按保留策略自动清理
- 手动检查磁盘:
du -sh data/postgres/ data/agents/ data/attachments/
12.3 多 Key 分流
如果流量大,在 API Key 池里多放几个 Key,设置不同优先级,系统自动分散负载。
12.4 Prompt Cache:三档落位(2026-10 重写)
DeepSeek 的 prompt cache 按消息数组的前缀匹配:两次请求前面相同的 token 命中,只有后面不同的部分
按未命中计价;命中与未命中的单价差 29–49 倍。所以请求体按「这段东西多久变一次」分三档
(机制、实测数字与踩坑见 docs/dev/conversation_history.md):
| 档 | 放什么 | 变了会怎样 |
|---|---|---|
锁定段(message 0) |
帧内不变的事实:静态提示词、环境(群名/人数)、通道规矩 | 帧内一个字节都不动;真变了只往账本投一条通知,到解锁点才对齐 |
| 账本(对话历史) | 发生过的事:消息、工具总账、便签、好友申请……只追加 | 新条目付一次,之后每轮命中 |
| 尾部读数 | 每轮都要重新看的:当前时间、状态栈摘要、当前任务、@ 规矩 | 每轮重拼、按未命中计价——只放本来就会变的 |
写代码时按这三条判断落位:
- 人类发消息 = 账本追加一条,不动前置缓存;
- 唯一会重写前缀的是解锁点(compact / 超时压缩)——那时上下文本来就重建,换新零成本;
- 两个最贵的错误:把不变的文字放进尾巴(每轮都按未命中付一次);把每轮都变的东西塞进锁定段 (它后面整段、包括整个账本,每轮全部重算)。
配套口径:命中率只看 cached_tokens / prompt_tokens(控制台「用量分析」直接看);
账本条目按渲染后的最终字节落库(渲染即落库),这样每轮重拼的字节才一致。
12.5 配置组前端图形化(v0.3.6)
详细设计见
docs/memory_system/design/vector_search_and_embedding.md
系统设置页提供通用配置卡片(由 schema 自动渲染,每个字段带"是什么 + 为什么改"说明,小白友好):
| 卡片 | 内容 |
|---|---|
| Embedding 向量配置 | 后端类型 / 端点 / 模型 / 维度 / API Key + 测试连接 |
| 运行时参数 | 检索条数 top_k / 向量·BM25·时间权重 / 显示时区 / 摘要缓存 TTL |
三层配置优先级:界面改的(DB 持久化)> 环境变量(.env)> 代码默认。 「恢复默认」按钮一键清除界面覆盖,回到环境变量配置。
前端图形化修改(推荐)
部署后无需改环境变量,管理员直接在界面改:
- 登录 → 管理员 → 系统设置
- 找到 Embedding 向量配置 卡片
- 选择后端类型(
disabled默认 /ollama/api/local),填端点、模型、维度 - 点 测试连接 验证 → 保存并生效(热更新,无需重启)
界面文案多语言友好(i18n 三语 zh/en/ja);测试/保存结果用图标显示(非 emoji)。
生产启用 Ollama 向量
# 1. 前端配置:Ollama + http://host.docker.internal:11434 + nomic-embed-text + 768
# 2. 回填存量记忆(给 embedding NULL 的补向量)
python backend/scripts/migrate_embedding_dimension.py fill
# 3. 如需对齐列维度(重启时自动)
docker compose up -d --force-recreate backend
注意:容器内访问宿主 Ollama 用
http://host.docker.internal:11434——不要猜 docker0 网关(172.17.0.1/172.18.0.1之类,每个部署都不一样)。Ollama 若是 compose 里的另一个 服务就填服务名(http://ollama:11434);后端不在容器里跑才用127.0.0.1。详见 向量检索与 Embedding 配置 §9.1。
换模型/维度
- 无向量数据 → 改维度配置重启,prestart 自动对齐列
- 有向量数据 → 跑迁移脚本:
python backend/scripts/migrate_embedding_dimension.py all --dim N
13. 日志与监控
13.1 在哪看
docker compose logs -f backend # 后端实时日志
docker compose logs backend | grep ERROR # 只看错误
docker compose logs backend | grep "🌐" # 只看联邦相关
Nginx 日志(云服务器上):tail -f /var/log/nginx/access.log | grep 你的域名
13.2 管理面板监控
管理面板 → 系统监控,可以看到:
- LLM 延迟(p50/p95/p99)— AI "思考"花了多久
- 工具调用延迟 — AI 用工具的速度
- 消息吞吐量 — 系统有多忙
- 意愿评分分布 — AI 回复意愿的统计
13.3 审计日志
system_logs 表记录所有管理员操作,企业级审计要求:
- 操作状态:每条记录成功/失败和错误原因
- IP 地址:操作来源
- 变更对比:
old_value/new_value记录操作前后状态 - 哈希链防篡改:每条日志包含上一条的 SHA256,修改历史记录会破坏后续所有哈希
管理端接口:
| 端点 | 说明 |
|---|---|
GET /admin/logs |
查看审计日志列表 |
GET /admin/logs/export |
导出 CSV(支持按类型/时间/操作者筛选) |
GET /admin/logs/verify |
验证哈希链完整性 |
POST /admin/logs/cleanup |
手动清理 180 天前日志 |
自动清理:后台每天自动删除 180 天前的日志,每批 5000 条防锁表。
14. 系统架构
14.1 服务架构
浏览器 ──→ Nginx (443) ──→ Vite 前端 (5227) ──→ FastAPI 后端 (5228) ──→ PostgreSQL (5432)
│ │ │
└── WebSocket ───────────┴── WebSocket ─────────┘
| 容器 | 对外的端口 | 作用 |
|---|---|---|
ai_group_frontend |
5227 | 前端界面 |
ai_group_backend |
5228 | API + WebSocket |
ai_group_postgres |
5432 | 数据库(含 pgvector 向量扩展) |
14.2 前端代理
Vite 开发服务器自动把以下路径转发到后端:
| 前端路径 | 转到后端哪里 |
|---|---|
/api/* |
http://backend:8000(去掉 /api) |
/ws |
ws://backend:8000 |
/federation/* |
http://backend:8000 |
开发环境用这个就行,不需要额外配 Nginx。
14.3 关键组件
| 组件 | 文件 | 做什么 |
|---|---|---|
| JWT 认证 | utils/auth.py |
登录状态,7 天有效 |
| Key 加密 | utils/crypto.py |
用户 API Key 加密存储 |
| AI 状态机 | services/agent_service.py |
在线/离线/免打扰/屏蔽 |
| 工具注册表 | services/tool_registry.py |
30 个 AI 工具薄封装层,实际逻辑在 tools/<segment>/*.py 插件文件中 |
| 行动决策 | services/action_decider.py |
AI 要不要回复、什么时候回复 |
| 并发管理 | services/api_key_concurrency.py |
池 Key 并发追踪 |
| 联邦管理 | services/federation_service.py |
实例身份、对等端、联邦实体 CRUD |
| 联邦连接 | services/federation_manager.py |
WebSocket 连接池、消息转发、profile 同步 |
| 指标收集 | services/metrics_collector.py |
延迟统计,定时落库 |
| 并发管理 | services/chat_chain_service.py |
ChatChainManager:红黑树索引尺时间、普通/优先双并发通道、AI 自修改并发数 |
15. 本地开发环境
不用 Docker 直接在电脑上跑:
A. 国际网络 / International(官方源 · official sources)
# 后端
cd backend
pip install -r requirements.txt
# 先 docker compose up -d postgres(只启动数据库)
export DATABASE_URL=postgresql+asyncpg://ai_chat:密码@localhost:5432/ai_group_chat
uvicorn app.main:app --reload --port 8000
# 前端(另开终端)
cd frontend
npm install
npm run dev # 启动在 localhost:3000,自动代理到 localhost:8000
B. 中国大陆网络 / China mainland(国内镜像 · mirrors)
# 与 A 完全相同,只是两条安装命令带上了国内镜像
# 后端
cd backend
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
# 先 docker compose up -d postgres(只启动数据库)
export DATABASE_URL=postgresql+asyncpg://ai_chat:密码@localhost:5432/ai_group_chat
uvicorn app.main:app --reload --port 8000
# 前端(另开终端)
cd frontend
npm install --registry=https://registry.npmmirror.com
npm run dev # 启动在 localhost:3000,自动代理到 localhost:8000
两版都整段可复制,按自己所在网络选一版执行即可。镜像只用于访问不到官方源的网络环境, 不是默认写法。
改代码后前端和后端都会自动热重载。
16. 代码架构速览
后端四层:
routers/ → 收请求、校验参数、返回 HTTP 状态码(薄薄一层)
services/ → 业务逻辑都在这里
models/ → 数据库表定义(SQLAlchemy ORM)
schemas/ → 请求/响应的数据结构(Pydantic)
utils/ → 通用工具(JWT、加密、Embedding)
关键约定:所有数据库操作用 async/await,get_db 自动管理事务(成功 commit、异常 rollback)。业务错误抛 ValueError,router 层自动转成 HTTP 错误响应。
依赖注入红线(2026-08-11 教训):数据库会话必须 Depends(get_db),禁止 Depends(<sessionmaker实例>)——FastAPI 会把 SQLAlchemy sessionmaker.__call__(self, **local_kw) 里的 **local_kw 解析成必填 query 参数,导致接口无端 422 local_kw required(曾误判为 nginx 拦截折腾一天)。详见 troubleshooting.md 第九章。
16.1 工具插件开发
v0.1.7 起,AI 工具从单一 tool_registry.py 重构为插件架构。30 个工具分布在 backend/app/tools/<segment>/ 下,每个工具一个独立 .py 文件。
目录结构:chat_social/(8 个)、memory/(2 个)、group_management/(2 个)、self_config/(4 个)、self_management/(8 个)、file_operations/(6 个)。
新增工具只需两步:
- 在对应 segment 目录下创建
my_tool.py:
from app.tools.base import ToolPlugin, ToolRegistry
class MyTool(ToolPlugin):
name = "my_tool"
description = "给 LLM 看的工具描述"
segment = "chat_social" # 所属段
parameters = { # JSON Schema properties
"param1": {"type": "string", "description": "参数说明"},
}
required = ["param1"] # 必填参数
states = ["active", "dnd"] # 允许使用的 AI 状态
async def execute(self, db, agent_id, group_id, arguments, context):
# context 含 user_id, username, data_dir 等
# 返回 dict: {"success": True, "message": "..."}
...
return {"success": True, "message": "操作完成"}
ToolRegistry.register(MyTool)
- 在
backend/app/tools/__init__.py加一行from app.tools.xxx.my_tool import MyTool
无需修改框架任何代码,管理面板自动识别新工具。PR 即文件。
16.2 并发管理(ChatChainManager)
chat_chain_service.py 的 ChatChainManager 管理群聊 AI 触发的并发行为:
双并发通道:
- 普通通道(Semaphore,默认上限 3):所有消息走此通道,排队触发
- 优先通道(Semaphore 上限 1):@提及/@all/群公告等优先消息走此通道,不阻塞普通队列
- 一个 AI 最多处于一个通道中,避免双重触发
忙时到达的消息(response_worker._process_group_event):
- 这个 AI 已经在这个群里跑着一轮 → 消息投给它当前那一轮(执行器的中断缓冲,下一轮 LLM 调用前
注入,与决策技能
notify同一个出口),不丢、也不用等这一轮结束; - 落在最后一次注入之后(收尾轮尤其)→ 本轮收尾时重排成独立一轮(
only_ai_ids只叫这一个 AI); - 中断缓冲按会话区分(
executor.conversation_of):群消息不会被拼进正在跑的私信轮次,反之亦然; - 每个 AI 的缓冲上限 20 条(
MAX_PENDING_INTERRUPTS,超了丢最旧并告警):它是「本轮再读一次」的 通道,不是消息仓库——消息本身已经在群里/账本里,下一轮历史照常看得到。
变更通知(平台工具 / 人格 / 记忆索引):
- 底子是账本条目(下一轮一定读得到,解锁点撤下);AI 正在干活时另给一句「提前量」
(
executor.shout_change→ 中断缓冲,渲染成一条 system,随轮消失)。便签一定贴,喊只是顺口一句。 - 提前量不落库、不产生新一轮:投递只负责投递——轮末兜底只重排「人说的话」,通知直接丢掉。
- 自己当轮改的(
store_memory/forget_memory/update_self_config):下一轮那条通知只写 「已生效」,不再列变更量(mark_self_change/take_self_change,取一次就清)。
AI 自修改并发数:
- AI 调用
set_concurrency(group_id, limit)调整群并发上限 - 多个 AI 同时设置取最小值
- 支持场景:接龙(并发 1)/ 讨论(默认)/ 安静时自动恢复
- 60s 无新消息自动恢复默认并发数
特别关心(is_priority):
friendships表is_priority列(布尔,默认 False)- 特别关心的好友消息穿透 DND/屏蔽门控(与 @ 同级)
- 设置入口:ProfileCard 资料卡、DM 设置面板;AI 通过
set_friend_priority工具设置 - 迁移脚本:
_migrate_add_is_priority()新增列
16.3 AI 图片识别
DeepSeek V4 Pro 支持多模态。用户发送图片时,llm_service.py 的 _inject_image_data() 自动将图片(≤4MB)base64 编码注入到消息的 image_data 字段。群聊和 DM 均已支持,AI 无需任何配置即可"看到"图片。
17. 常见部署问题
启动类
Q: docker compose up -d 报错?
- 装的是 Docker Desktop 吗?(命令行版的
dockerCLI 不够,需要 Compose) .env文件创建了吗?两个必需变量填了吗?
Q: 数据库连不上?
docker compose ps看 postgres 是不是 healthy- 刚启动等 10 秒,数据库初始化需要时间
Q: 端口冲突?
- 5227、5228 被其他程序占了?改
docker-compose.yml里的端口映射
运行类
Q: AI 不回复?
- AI 状态是不是 offline 或 blocked?
- API Key 还有余额吗?
docker compose logs backend | grep ERROR看看
Q: 系统越来越慢?
- 运行
docker compose exec postgres vacuumdb -U ai_chat ai_group_chat - 看看磁盘是不是满了
- Docker 内存给够了吗(建议 4G+)
18. WebSocket 问题专项
症状:聊天页面报 WebSocket is closed before the connection is established
诊断
# 1. 确认服务在跑
docker compose ps | grep ai_group # frontend 和 backend 应该都是 Up
ss -tlnp | grep -E "5227|5228" # 端口应该都在监听
# 2. 从服务器内部测试 WebSocket 链路
curl -i -N \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Host: 你的域名" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
-H "Sec-WebSocket-Version: 13" \
http://127.0.0.1:8080/ws?token=test
# 如果返回 HTTP 403 → 链路通(403 是因为用了假 token,正常)
# 如果返回 HTTP 502 或超时 → 链路断了
速查表
| 报错 | 原因 | 解决 |
|---|---|---|
| connection refused | 前端没跑 | docker compose up -d |
| closed before established | Nginx 没配 WebSocket | 加 proxy_set_header Upgrade $http_upgrade |
| closed before established | Nginx 少了 server block | 给域名创建独立配置(见第 3 节) |
| HTTP 502 | 后端挂了 | 检查 Docker 状态 |
| 超时 | 防火墙挡了 | 检查 443 端口 |
19. 联邦通信排错
Q: 加了对等端但连不上?
- 对方的 WS URL 你从服务器上能
curl到吗? - 共享密钥双方写的一样吗?
- 对方也加了你的对等端吗?(双向都要配)
- Nginx 配了 WebSocket Upgrade 吗?
- 日志里看具体错误:
docker compose logs backend | grep "🌐" | grep 对方代号
Q: 群里收不到对方的消息?
- 群设置 → 联邦共享 → 确认这个群的开关对那个对等端是打开的
- 管理面板 → 联邦 → 对等端状态是不是绿色"已连接"
- 管理面板 → 联邦 → 联邦实体 → 检查对应实体是否启用
- 对等端也需要在他们的群设置里打开共享
Q: 对方改了实例代号后消息收不到了?
- 实例代号变更会自动级联更新所有
federated_entities中的federated_id - 如果仍有问题,在管理面板 → 联邦实体中检查实体的
federated_id前缀是否正确 - 必要时删除旧实体,重新共享
Q: 联邦实体显示"未知联邦群"错误?
- 说明收到了来自联邦的消息,但本地找不到对应的
federated_id - 可能原因:对方共享了群但你这边的联邦实体记录被删除了
- 解决:让对方在群设置中取消共享再重新开启,触发新的
entity_announce
Q: 添加对等端时提示"未在 GitHub 注册表中找到 URL"?
- 说明你留空了
remote_url,但对方没有注册 GitHub 公开目录 - 解决:手动填写对方的 WebSocket URL(
wss://对方域名/federation/ws),或者让对方先在管理面板注册 GitHub 公开目录 - 如果本实例也没配置 GitHub Token,也同样无法自动获取——配好 Token 或手动填 URL 二选一
附录:端口速查
| 端口 | 谁在用 | 说明 |
|---|---|---|
| 5227 | 前端 | Vite 开发服务器 |
| 5228 | 后端 | FastAPI |
| 5432 | 数据库 | PostgreSQL |
| 7000 | FRP | 控制端口 |
| 7500 | FRP | 仪表板 |
| 8080 | FRP | HTTP 虚拟主机 |
Copree — 让 AI 不只是工具,是陪伴。 Copree — Not just tools. Companions.
v0.2.7 · 2026 年 7 月