管理与开发者手册

源自 docs/guides/管理与开发者手册.md

面向管理员和开发者。 从部署到精通,由浅入深。 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

# 章节 难度
10 FRP 隧道部署 ⭐⭐⭐
11 环境变量参考 ⭐⭐
12 性能调优 ⭐⭐⭐
13 日志与监控 ⭐⭐

🛠️ 开发:深入内部 / Under the Hood

# 章节 难度
14 系统架构 ⭐⭐
15 本地开发环境 ⭐⭐⭐
16 代码架构速览 ⭐⭐⭐

❓ 排错:遇到问题来这里 / Troubleshooting

💡 刚接手?从第 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

  1. AI 有自己的独立 Key?→ 用那个
  2. 用户之前绑定了某个池 Key?→ 直接用
  3. 都没有?→ 自动选负载最低、没被限流的 Key 并绑定
  4. 池 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) 帧内不变的事实:静态提示词、环境(群名/人数)、通道规矩 帧内一个字节都不动;真变了只往账本投一条通知,到解锁点才对齐
账本(对话历史) 发生过的事:消息、工具总账、便签、好友申请……只追加 新条目付一次,之后每轮命中
尾部读数 每轮都要重新看的:当前时间、状态栈摘要、当前任务、@ 规矩 每轮重拼、按未命中计价——只放本来就会变的

写代码时按这三条判断落位:

  1. 人类发消息 = 账本追加一条,不动前置缓存;
  2. 唯一会重写前缀的是解锁点(compact / 超时压缩)——那时上下文本来就重建,换新零成本;
  3. 两个最贵的错误:把不变的文字放进尾巴(每轮都按未命中付一次);把每轮都变的东西塞进锁定段 (它后面整段、包括整个账本,每轮全部重算)。

配套口径:命中率只看 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)> 代码默认。 「恢复默认」按钮一键清除界面覆盖,回到环境变量配置。

前端图形化修改(推荐)

部署后无需改环境变量,管理员直接在界面改:

  1. 登录 → 管理员 → 系统设置
  2. 找到 Embedding 向量配置 卡片
  3. 选择后端类型(disabled 默认 / ollama / api / local),填端点、模型、维度
  4. 点 测试连接 验证 → 保存并生效(热更新,无需重启)

界面文案多语言友好(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 个)。

新增工具只需两步:

  1. 在对应 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)
  1. 在 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 吗?(命令行版的 docker CLI 不够,需要 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: 加了对等端但连不上?

  1. 对方的 WS URL 你从服务器上能 curl 到吗?
  2. 共享密钥双方写的一样吗?
  3. 对方也加了你的对等端吗?(双向都要配)
  4. Nginx 配了 WebSocket Upgrade 吗?
  5. 日志里看具体错误: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 月