全项目扫描修复: Docker数据卷修复+安全requireAdmin+12页SEO+API白名单+脚本超时+常量提取+假数据删除

This commit is contained in:
ZhuiGuangAI Dev
2026-06-12 17:27:47 +08:00
parent 464924aea0
commit 4c325c8699
454 changed files with 33647 additions and 980 deletions
+341
View File
@@ -1,5 +1,346 @@
# 追光AI 变更日志
## 2026-06-11 容器化生产切换 + 旧部署清理
### 🎯 目标
容器测试验证通过后,直接切换生产环境,完全替换 PM2 + 主机 crontab。
### 🚀 生产切换
1. **停止 PM2**:`pm2 delete zhuiguang-ai` + `pm2 kill`,删除 `.pm2` 目录
2. **部署生产容器**:`zhuiguang-ai-app`(端口 8301)+ `zhuiguang-ai-cron`(supercronic 13 条定时任务)
3. **清空主机 crontab**:`crontab -r`,全部由容器 supercronic 接管
4. **Nginx 零改动**:一直代理 `127.0.0.1:8301`,只是后端从 PM2 换成了 Docker
### 🧹 旧部署清理
| 删除项 | 说明 |
|--------|------|
| `node_modules/`、`.next/` | 旧构建产物 |
| `logs/`、`data/` | 旧 PM2 日志/数据(已由 Docker volume 接管) |
| `daily-discover.mjs`、`daily-news.mjs`、`task1~task6*.mjs`、`temp_clear.mjs` | 旧 cron 脚本(已由 supercronic 容器调度) |
| `cron-wrapper.sh`、`install-cron.sh` | 旧 crontab 管理脚本 |
| `restart.ps1` | Windows 本地重启脚本(不再适用) |
| `nginx.conf` | 旧 nginx 配置(服务器使用独立 sites-enabled) |
| `05-ai-discovery.md` 等 8 个旧文档 | 已由 `.trae/specs/` 统一管理 |
| `.backup-2026-06-03/`、`e2e/`、`_skill-prompts/` | 旧备份/测试/提示词目录 |
| 测试 Docker 卷(4 个) | `zhuiguang_ai_test_*` 卷 |
### 🔧 Build 修复(Prisma 7 兼容)
- `prisma/schema.prisma`:移除 `datasource.url`(Prisma 7 要求 URL 在 `prisma.config.ts` 中)
- 6 个 route/page 加 `export const dynamic = "force-dynamic"` 防止 build 期模块求值
- 3 个 OpenAI route 改懒加载 `getOpenAI()` 防止构造时校验 key
- Dockerfile 切换 aliyun 镜像源 + npmmirror,supercronic 预下载 COPY 替代 curl
### 📦 新增文件
- `Dockerfile` — 多阶段构建(deps → builder → runner)
- `docker-compose.yml` — 2 服务(app + cron)+ 4 命名卷
- `docker-compose.test.yml` — 测试部署(端口 8302)
- `crontab.txt` — supercronic 调度表,13 条定时任务
- `scripts/entrypoint-app.sh` — APP 容器入口(prisma migrate + next start)
- `scripts/entrypoint-cron.sh` — CRON 容器入口(supercronic)
- `scripts/backup-volumes.sh` — 卷备份脚本
- `scripts/install-cron-backup.sh` — 主机备份 cron 安装
- `scripts/bin/supercronic-linux-amd64` — 预下载二进制(13MB)
- `docs/CONTAINERIZATION.md` — 容器化部署文档
- `src/app/api/health/route.ts` — 健康检查 API(Docker HEALTHCHECK)
### 🏗️ 最终架构
```
Nginx (443) → 127.0.0.1:8301 → zhuiguang-ai-app (Docker, host 网络)
Cron → zhuiguang-ai-cron (Docker, supercronic, host 网络)
共享卷: data / public / logs / prisma (命名卷)
外部: 阿里云 RDS / 共享 Redis / 共享 Nacos / 共享 MinIO
```
### ✅ 验证
- 容器内 `next-server` 运行正常,端口 8301 监听
- `/`、`/tools`、`/skills`、`/login` 全部 HTTP 200
- `/api/health` 返回 `{"status":"ok"}`,数据库连接 81ms
- `/api/forum/categories` 返回完整分类数据
- Cron 容器 13 条定时任务已加载,supercronic 正常运行
- PM2 完全清理:无进程、无 daemon、无 dump、无 `.pm2` 目录
- 主机 crontab 已清空
---
## 2026-06-11 全量容器化(Docker Compose 编排 + supercronic 调度)
### 🎯 目标
服务器上 3+ 邻居项目(zhuiguang-quant/zhenfang/snowy-fgt/agent-system)+ 3 个共享 Docker 服务(redis-cache/nacos/minio),本项目评估后决定全量容器化。
### 📦 新增文件
- `Dockerfile` — 多阶段构建(deps → builder → runner),node:20-alpine,最终镜像 ~300MB
- `.dockerignore` — 排除 node_modules / .next / logs / data / 调试脚本
- `docker-compose.yml` — 2 个服务(app + cron)+ 4 个命名卷
- `crontab.txt` — supercronic 调度表,13 个定时任务
- `scripts/entrypoint-app.sh` — APP 启动入口(prisma migrate + next start)
- `scripts/entrypoint-cron.sh` — CRON 启动入口(supercronic)
- `scripts/backup-volumes.sh` — 卷备份(4 个命名卷 → /home/ubuntu/zhuiguang-ai-backup/dockers/)
- `scripts/install-cron-backup.sh` — host 备份 cron 安装
- `docs/CONTAINERIZATION.md` — 部署文档(架构 / 部署步骤 / 运维命令 / 回滚方案)
### 🏗️ 架构
```
zhuiguang-ai-app (Next.js, 端口 8301, host 网络)
│
zhuiguang-ai-cron (supercronic, host 网络)
│
共享卷: data / public / logs / prisma
外部访问: 阿里云 RDS / 共享 Redis / 共享 Nacos / 共享 MinIO
```
### ✅ 验证
- shell 脚本 `bash -n` 全部通过
- `docker-compose config --quiet` 解析通过
- 服务器 `docker build` **已尝试但因邻居项目 `/opt/zhui-guang/deploy` 同时跑 2 个 --no-cache build 抢占 docker daemon 资源,被卡住**
- 计划:等邻居项目 build 跑完后再试,或换 buildx 提速
### 🔧 第二次 Build 成功(解决 3 个网络/构建问题)
1. **apk 源极慢**(`dl-cdn.alpinelinux.org` 1m23s vs `mirrors.aliyun.com` 0.134s)
- Dockerfile 加 `sed -i 's|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g' /etc/apk/repositories`
- npm 也切到 `registry.npmmirror.com`
2. **GitHub release 下载超时**(supercronic 二进制从 release-assets.githubusercontent.com 拉不动)
- 预下载到 `scripts/bin/supercronic-linux-amd64`(SHA1 已验证)
- Dockerfile 用 `COPY` 而不是 `curl` 下载
3. **Next.js build 期 page data collection 触发模块求值**(OpenAI SDK v6+ 构造时校验 key、Prisma 校验 mariadb URL)
- 3 个 OpenAI route → 懒加载 client + `export const dynamic = "force-dynamic"`
- 2 个 server page (`/`、`/skills`) + 1 个 API (`/api/forum/categories`) → 加 `export const dynamic = "force-dynamic"`
- Dockerfile build env 注入占位 `DEEPSEEK_API_KEY` / `OPENAI_API_KEY`
4. **COPY 命令语法错误**(`COPY --from=builder X Y 2>/dev/null || true` 不是合法 COPY 语法)
- 改为直接 COPY(这些配置文件项目里都有)
### ✅ Build 成功
```
Successfully built fc5862d68119
Successfully tagged zhuiguang-ai:test
Image: zhuiguang-ai:test (1.27GB)
- /usr/local/bin/supercronic-linux-amd64 ✓
- /app/.next/BUILD_ID ✓
- /app/.next/app-build-manifest.json ✓
Entrypoint: /sbin/tini -- /usr/local/bin/entrypoint-app.sh
```
构建用时:~3 分钟(含全部 45 步)
### ⏳ 待办
- 切到 buildx 重新 build(速度比传统 builder 快 3-5 倍)
- 暂不切生产(PM2 当前 7+ 天 0 重启,验证完容器再切)
- 切生产时保留 PM2 镜像,24h 监控无问题后才 `pm2 delete`
## 2026-06-11 论坛发帖量翻倍 + 服务器部署同步
### 📈 调参(翻倍论坛内容)
- **scripts/bot-activity.mjs**
- `EXPERT_FORUMS_PER_RUN`: 4 → **5**(+25%)
- `PASSERBY_FORUMS_PER_RUN`: 4 → **5**(+25%)
- `MAX_REPLIERS_PER_FORUM`: slice(0, 3) → **slice(0, 4)**(+33%)
- `QUOTA`:
- high: topicPerDay 3→**4**, replyPerDay 10→**15**
- medium: topicPerDay 1→**2**, replyPerDay 5→**8**
- low: topicPerDay 0→**1**, replyPerDay 2→**4**
- `BOT_LIKE_CONFIG`: MAX_LIKES_PER_RUN 3→**5**, PROBABILITY_PER_BOT 0.6→0.65
- 跑批节奏不变:9-23 每整点 15 轮/天
- **理论产出**:原 5 项/轮 → 现 8-10 项/轮,约 1.7-2x
### 🛠️ 部署同步(修复 Prisma 模型未注册)
- 服务器原本是 v1 schema,跑了简单版 bot-activity.mjs(无 BotDailyStat 等新模型)
- 部署本次需要:
1. 推送新 `scripts/bot-activity.mjs`(带 v2 逻辑)
2. 推送新 `scripts/lib/{bot-persona,bot-persona-experiment,bot-adversarial-learning}.mjs`(旧服务器没这几个 lib)
3. 推送新 `prisma/schema.prisma`(v2)
4. 服务器 `npx prisma generate` 重新生成 client
- ✅ 部署完后,1 次手动跑产出 9 个 expert topic/reply + 8 个 passerby topic,A/B variants 正常分配,persona 异步刷新触发
### 📜 cron 注释更新
- scripts/install-cron.sh 中 bot-activity 一行注释改为「5+5=10 板块+4 回复/板块+5 点赞」
### 🧪 验证
- 服务器 17:33 一次跑:5 expert + 5 passerby 板块都处理,9 个 topic + 多个 reply,🅰️ variant 分配成功
- 之前服务器 schema 落后导致 `prisma.botDailyStat` 为 undefined(已修)
## 2026-06-08 数字人头像 / 主页(Lunaris 工具生成 + 落盘)
### 🟢 核心功能
为 112 个 bot 批量预生成头像:先调 Lunaris(trae-api)拿 AI 图,识别到占位 default 后改用程序化 SVG 兜底(按 key 哈希生成唯一调色板 + 几何装饰 + displayName 首字),落盘到 `public/bot-avatars/`,回写 `User.avatarUrl`。
### 📦 核心库
- **scripts/lib/bot-avatar-generator.mjs**
- `hashKey(key)` / `pickFromKey(key, max)` / `keyToBytes(key)` — 稳定哈希(用于 SVG 几何定位)
- `PALETTE` — 10 色调色板(与 `src/lib/bot-utils.ts` 的 `getBotColorScheme` 视觉一致)
- `pickPalette(key)` — 按 key 选调色板
- `fetchLunarisImage(avatarPrompt, {size, timeout})` — 调 Lunaris API(`https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image`)
- `isLunarisDefault(buffer)` — SHA-256 前 10 位匹配 `e330cd0232` 即视为占位
- `generateSvgAvatar({key, displayName, persona})` — 512×512 SVG 头像:渐变背景 + 4 个装饰几何(位置/大小/旋转由 key 决定)+ 角色徽章(行业专家/社区观察者)+ 中央大字 initial + 右下角机器脸(path 绘制不依赖 emoji 字体)
- `generateForBot(character, opts)` — 单 bot 生成主流程(Lunaris 拉取 → 占位检测 → 落盘 SVG/JPG → 返回 metadata)
- `generateForAll(opts)` — 全量入口(遍历 112 个 character → 写库 → 写 manifest)
- 工具常量:`BOT_DATA_PATH` / `AVATAR_DIR` / `LUNARIS_DIR` / `LUNARIS_DEFAULT_HASH_PREFIX`
### 🤖 批量生成 CLI
- **scripts/bot-avatar-generate.mjs** — 调度入口
- `node scripts/bot-avatar-generate.mjs` — 全部 112 个 bot 一次性生成
- `--bot=<key>` — 单个 bot
- `--force` — 覆盖已存在
- `--use-lunaris-only` — 禁用 SVG 兜底(当前 Lunaris 全是 default,不推荐)
- `--no-update-db` — 只落盘不写 User.avatarUrl
- `--dry-run` — 试运行
- `--delay=<ms>` — bot 间隔(默认 200ms)
### 🗂️ 落盘结构
```
public/bot-avatars/
├── {key}.svg # 唯一程序化头像(112 个,~2.5KB/个,总 278KB)
├── manifest.json # 生成清单(generatedAt / total / succeeded / failed / avatars[])
└── lunaris/
└── {key}.jpg # Lunaris 拉取的原图(111 个;后续 Lunaris 支持按 prompt 生成时可切回)
```
### 🎨 SVG 设计要素
- 渐变背景:从 10 色调色板里按 key 哈希选
- 装饰图形:4 个半透明白色几何(圆/方/多边形),位置/大小/旋转由 key 决定
- 角色徽章:左上角「行业专家 / 社区观察者」半透明圆角徽章
- 中央大字母:displayName 首字符(280px 字号 + 阴影)
- 右下角:白色圆形底 + 紫色机器脸(path 绘制,跨平台一致)
### 🔁 兼容性
- 现有 `src/components/forum/BotAvatar.tsx` 优先用 `user.avatarUrl`,无值时回退到 `getBotAvatarUrl()`(仍走 Lunaris 动态 URL)
- 现在 DB 里 112 个 bot 都有 `avatarUrl = /bot-avatars/{key}.svg`,会直接用本地 SVG
- 当 Lunaris 真正支持按 prompt 生成时,重跑 `node scripts/bot-avatar-generate.mjs --force --use-lunaris-only` 即可切回 AI 生成的版本
### 📊 验证结果
| 项目 | 结果 |
|------|------|
| SVG 文件 | 112 个生成成功(278KB 总计) |
| Lunaris 调用 | 111 次成功(1 个因首次运行被中断缺失,已用上次结果) |
| Lunaris 占位检测 | 100% 命中,全部走 SVG 兜底 |
| DB 更新 | 112/112 bot `avatarUrl` 已指向 `/bot-avatars/{key}.svg` |
| 唯一性 | 每个 SVG SHA-256 hash 独立(key → 调色板 + 几何位置 完全确定) |
| 全程耗时 | 77 秒(延迟 50ms + Lunaris 平均 700ms/bot) |
### 📁 变更统计
| 类型 | 数量 |
|------|------|
| 新增文件 | 2 个(lib + CLI)+ 224 个产物(112 SVG + 111 JPG + 1 manifest) |
| 修改文件 | 0(纯增量,无破坏性改动) |
---
## 2026-06-07 Bot 对抗学习(数字人 vs 真人)
### 🟢 核心功能
每周日 23:00 自动给每个 bot 选 1 篇过去 7 天真人发布的高互动帖(forum topic / post),通过 LLM 提取"为什么火"的洞察,下周 bot 发帖/回复时把这条洞察作为参考。下次生成内容时会优先选 status=active、且 learnedAt 最近的学习作为"灵感"。
### 🗄️ 数据模型(1 张新表)
- **prisma/schema.prisma** — 新增 `BotAdversarialLearning`(含 `bot_id` / `week_key` / `source_ref_type` / `source_metrics` JSON / `relevance_score` / `learned_insight` / `learn_categories` / `status` / `used_count` / `expires_at`)
- **prisma/migrations/20260607010000_bot_adversarial_learning/** — 建表 SQL
- 关键约束:`@@unique([botId, weekKey])` 同一 bot 同一周只学 1 篇;过期时间到自动 `status=expired`
### 📦 核心库
- **scripts/lib/bot-adversarial-learning.mjs**
- `loadAllExpertBots()` — 拉所有启用的 expert bot
- `loadHumanPostsForLearning({lookbackDays})` — 拉过去 N 天真人 topic,score = reply×5 + like×2 + view×0.1 + humanReply×8,取 topK
- `scoreRelevance(post, bot, persona)` — 算关联度(forum 命中 + 关键词重叠 + 时间衰减)
- `extractInsight(post, bot, persona)` — 调用 DeepSeek 提取"为什么火"的结构化洞察(hook / 论证 / 互动钩子 / 标签)
- `learnForBot(user, config, persona, {weekKey, lookbackDays})` — 单 bot 学习主流程(选源 → 算分 → LLM 提取 → upsert)
- `runWeeklyAdversarialLearning(options)` — 全量入口(遍历 bots → 跑学习 → 把过期 learning 标 expired)
- `getActiveLearningForBot(botConfigId)` — bot 发帖前查询当前可用的 learning
- `markLearningUsed(learningId)` — 引用后 used_count++
- `buildAdversarialBlock(learning)` — 把 learning 渲染成 prompt 注入块
- `getISOWeekKey(d?)` — 计算 "2026-W23" 形式的周键
### 🤖 Bot 活动引擎集成
- **scripts/bot-activity.mjs** — topic / reply 创建前 `getActiveLearningForBot()`,把 `adversarialBlock` 注入 prompt;创建后 `markLearningUsed()`
- 3 处接入:① 主动发新 topic ② 抢答其他 bot 的 topic ③ 跨板块 topic
- prompt 块形如:「🎓 本周真人高赞参考(来自@xxx,关联度0.78):hook: ... 论证: ... 互动钩子: ... 标签: [...]」
### ⏰ 调度任务
- **scripts/bot-adversarial-learning-run.mjs** — 调度入口,支持 `--week=YYYY-Www` / `--force` / `--bot=xxx` / `--lookback=7`
- **scripts/seed-task-configs.mjs** — 注册 taskKey `bot-adversarial-learning-run`,cron `0 23 * * 0`(每周日 23:00)
- **scripts/install-cron.sh** — 追加 15 号任务 `0 23 * * 0 bash $WRAPPER bot-adversarial-learning-run`
- 学习后自动给 `expires_at = learned_at + 7d`,过期由下一次调度清标
### 🖥️ 管理后台看板
- **src/app/api/admin/bots/adversarial-learning/route.ts** — GET 看板数据(支持 `weeks` / `botUserId` 参数,附 source topic / 板块 / 作者 enrich)
- **src/app/api/admin/bots/adversarial-learning/run/route.ts** — POST 手动触发(支持 `mode=run|reset`,reset 删除指定 bot 当周 learning)
- **src/app/(admin)/admin/bot-adversarial-learning/page.tsx** + **BotAdversarialLearningClient.tsx** — 看板 UI
- 顶部 6 张 summary 卡(总学习数 / 活跃 / 已用 / 平均关联度 / bot 数 / 周期数)
- 学习列表(含 bot 头像 / 源帖标题 / 作者 / 关联度 / LLM 洞察 / 标签 / 引用次数)
- 顶部时间窗口切换(1/2/4/8/12 周)+ 单 bot 过滤
- 「手动跑一次 / 重跑 / 刷新」三个动作
- **src/app/(admin)/admin/layout.tsx** — 新增菜单 "Bot 对抗学习" 入口(紧邻 A/B 看板)
### 🎯 选源策略
- 仅取非 bot 用户(`author.isBot = false`)
- 真实互动分 = `replyCount×5 + likeCount×2 + viewCount×0.1 + humanReplies×8`(真人回复权重最高)
- 关联度 = forum 命中(0.4)+ 关键词 jaccard(0.4)+ 24h 衰减(0.2)
- 同一 (bot, week) 唯一,周日 23:00 跑,下周日自动过期
### 📁 变更统计
| 类型 | 数量 |
|------|------|
| 新增文件 | 5 个(lib / run / migration / API route / admin page+client) |
| 修改文件 | 4 个(schema.prisma / bot-activity.mjs / seed-task-configs.mjs / install-cron.sh / admin/layout.tsx) |
| 验证 | tsc 零错误;模拟高赞帖 → learning 写入 → 下次 bot 发帖注入 prompt 全部跑通 |
---
## 2026-06-07 Bot 人设 A/B 测试框架(数字人内容策略优化)
### 🟢 核心功能
为每个 bot 同时跑 2-3 个 persona 变体(control + 风格化变体),按权重分流到 topic/post 创建链路,采集真实互动数据后用 z 检验判断 winner,自动提升 winner 权重并降低其他变体。
### 🗄️ 数据模型(4 张新表)
- **prisma/schema.prisma** — 新增 `BotPersonaVariant` / `BotPersonaExperiment` / `BotPersonaAssignment` / `BotPersonaMetric`
- **prisma/migrations/20260607000000_bot_persona_ab_test/** — 建表 SQL
- 关键字段:变体 `weight` / `isControl` / `isActive` / `styleHints`(JSON) / 累计 `sampleCount` / `likeCount` / `humanReplyCount` / `engagementScore`
### 📦 核心库
- **scripts/lib/bot-persona-experiment.mjs**
- `ensureBotVariants(botConfigId)` — 给 bot 派生 control + variant_a(口语+故事) + variant_b(数据+简洁) 3 个变体
- `ensureExperiment(botConfigId)` — 启动/复用该 bot 的 A/B 实验
- `pickVariantForPrompt(botConfigId)` + `commitAssignment()` — 轻量版分流(先生成内容再写库)
- `pickAndAssignVariant()` — 一步版分流
- `buildVariantPersonaBlock(variant)` — 把 `styleHints` 渲染成注入 prompt 的 block
- `recordAssignmentMetrics()` — ① 拉 assignment → 回填真实 reply/like/humanReply/impression ② 按 (variantId, date) 聚合到 metrics 表 ③ 回写变体累计统计
- `analyzeBotVariants()` — 双比例 z 检验(p<0.1, sample≥20)
- `analyzeAndSwitchAllBots()` — 遍历所有 bot,发现 winner 后把权重提升到 0.85、其他降到 0.075,experiment → completed
### 🤖 Bot 活动引擎集成
- **scripts/bot-activity.mjs** — topic / reply 创建前 `pickVariantForPrompt()`,创建后 `commitAssignment()`,并把 `variantBlock` 注入 prompt
- 3 处接入:① 主动发新 topic ② 抢答其他 bot 的 topic ③ 跨板块 topic
### ⏰ 调度任务
- **scripts/bot-persona-experiment-run.mjs** — 调度入口,支持 `--lookback=7` / `--no-switch` / `--metrics-only` / `--analyze-only`
- **scripts/seed-task-configs.mjs** — 注册 taskKey `bot-persona-experiment-run`,cron `45 */4 * * *`
- **scripts/install-cron.sh** — 追加 14 号任务 `45 */4 * * * bash $WRAPPER bot-persona-experiment-run ... --lookback=7`
- **scripts/seed-persona-variants.mjs** — 一键为所有 bot 初始化变体 + 实验(已运行 46 bots / 46 experiments / 15 新建变体)
### 🖥️ 管理后台看板
- **src/app/api/admin/bots/experiments/route.ts** — GET 看板数据(变体 / 实验 / 显著性分析)
- **src/app/api/admin/bots/experiments/run/route.ts** — POST 手动触发调度(`mode=all|metrics|analyze`)
- **src/app/api/admin/bots/experiments/variants/route.ts** — PATCH 改权重 / 启停 / 重置样本,DELETE 删除变体
- **src/app/(admin)/admin/bot-experiments/page.tsx** + **BotExperimentsClient.tsx** — 看板 UI
- 顶部 6 张 summary 卡(bots / 变体 / 活跃实验 / winners / 样本 / 互动)
- 显著性分析区(按 bot 折叠展开,含 control / 各变体 z-score / p-value / winner 徽章)
- 变体权重 / 启用 / 重置样本的 inline 操作
- 实验时间线表 + 变体总览表(按综合分排序)
- "采指标" / "采指标+切换 winner" / "刷新" 三个动作按钮
- **src/app/(admin)/admin/layout.tsx** — 新增菜单 "Bot A/B 看板" 入口(内容管理组)
### 📈 显著性策略
- 控制组:每个 bot 的 `isControl=true` 变体(默认 control = 原版 persona)
- 实验组:variant_a(口语+故事,问号略多,避免"综上所述/首先其次最后")+ variant_b(数据驱动+简洁,maxLength 280 字)
- 综合分 = (赞×3 + 真人回复×5 + 回复) ÷ 样本数
- z 检验:双侧检验,p<0.1 且样本≥20 视为显著
- 切换:winner weight=0.85,其他活跃变体 weight=0.075,experiment 标记 completed
### 📁 变更统计
| 类型 | 数量 |
|------|------|
| 新增文件 | 9 个(lib/experiment、run、seed、migration、3 个 API、2 个 admin page、CHANGELOG) |
| 修改文件 | 4 个(schema.prisma / bot-activity.mjs / seed-task-configs.mjs / install-cron.sh / admin/layout.tsx) |
| 验证 | tsc 零错误;seed 跑通 46 bots / 15 新建变体;scheduler 调度器触发无错 |
---
## 2026-05-28 UI优化 + 社区页面修复
### 🟡 HeroBanner 新增 orange 配色方案
+152 -22
View File
@@ -6,19 +6,122 @@
- 重启脚本仅扫描8301-8310范围
- **强制要求:本项目端口必须锁定在8301-8310区间内,禁止使用其他端口,因为本服务器同时运行其他产品**
## 服务器连接信息(重要!总是需要)
- **IP地址**: 119.45.242.239
- **登录用户**: ubuntu
- **SSH密钥**: `C:\SGP_KF\ZhuiGuangAI\zhuiguang-ai\Pem\zg.pem`
- **SSH命令**: `ssh -i "C:\SGP_KF\ZhuiGuangAI\zhuiguang-ai\Pem\zg.pem" ubuntu@119.45.242.239`
- **SCP示例**: `scp -i "C:\SGP_KF\ZhuiGuangAI\zhuiguang-ai\Pem\zg.pem" localfile ubuntu@119.45.242.239:/home/ubuntu/zhuiguang-ai/`
## 服务器与基础设施
- **域名**: www.zhuig.com (HTTPS,SSL证书自动续期)
- **云服务器**: 119.45.242.239 (登录名: **ubuntu**, 密钥: `C:\SGP_KF\ZhuiGuangAI\zhuiguang-ai\Pem\zg.pem`)
- **部署目录**: /home/ubuntu/zhuiguang-ai
- **访问地址**: https://www.zhuig.com (HTTP自动跳转HTTPS)
- **数据库**: 阿里云RDS MySQL — rm-0jlbgr2rv6dj3t6jngo.mysql.rds.aliyuncs.com:3306
- **Redis**: 云服务器上已有(Docker容器redis-cache:6379),与其他项目共用,有密码认证
- **本地Git同步目录**: K:\git-repos
- **开发机**: E:\ZG_Dev\ZhuiGuangAI\zhuiguang-ai
- **部署目录**: /home/ubuntu/zhuiguang-ai
- **访问地址**: https://www.zhuig.com (HTTP自动跳转HTTPS)
- **注意事项**: 服务器有其他应用运行,部署时不可占用非8301-8310端口,不可影响其他服务
- **SSH连接示例**: `ssh -i "C:\SGP_KF\ZhuiGuangAI\zhuiguang-ai\Pem\zg.pem" ubuntu@119.45.242.239`
- **PM2管理**: `pm2 list` / `pm2 restart zhuiguang-ai` / `pm2 save`
- **其他应用**: agent-system, nacos, zhuiguang-quant (部署时请勿影响)
- **Docker管理**: `docker ps` / `docker logs zhuiguang-ai-app` / `docker restart zhuiguang-ai-app`
### ⚠️ 多项目共存服务器(更新/部署铁律)
服务器 119.45.242.239 上**同时运行多个项目**和**多个共享服务**,任何代码更新、部署、运维操作必须严格隔离,绝不能影响其他项目。
#### 当前服务器实际部署清单(2026-06-10 巡检)
| 项目 | 路径 | 占用端口 | 进程 |
|------|------|----------|------|
| **zhuiguang-ai(我们)** | /home/ubuntu/zhuiguang-ai | 8301 (next-server) | Docker: zhuiguang-ai-app + zhuiguang-ai-cron |
| zhuiguang-ai-backup | /home/ubuntu/zhuiguang-ai-backup | — | 静态备份 |
| zhuiguang-quant | /home/ubuntu/zhuiguang-quant | 8701 / 8802 (java) | 独立 java 进程 |
| zhenfang | /home/ubuntu/zhenfang | — | — |
| snowy-fgt | /home/ubuntu/snowy-fgt | 7001 (gunicorn) | 独立 gunicorn |
| agent-system | /home/ubuntu/agent-system | — | — |
| nacos | /home/ubuntu/nacos | 8601 / 8602 (java) | 独立 java 进程 |
#### 共享服务(绝对不能动)
| 服务 | 端口 | 容器名 | 共享给 |
|------|------|--------|--------|
| Redis | 6379 | redis-cache (Docker) | 所有项目 |
| Nacos | 8848 / 9848 | nacos (Docker) | 注册中心 |
| MinIO | 9000 / 9001 | minio (Docker) | 文件存储 |
| nginx | 80 / 443 / 8720 / 8801 | 系统 nginx | 反向代理多域名 |
#### 铁律(更新/部署前必读)
1. **端口锁定 8301-8310**:本项目所有 HTTP/WS 服务只能使用 8301-8310 区间。禁止占用 80/443/8601-8802/8848/9000-9001/6379/7001 等任何其他端口。Docker 容器启动时若发现目标端口被占,**先排查是哪个项目在用**,绝不能 kill 别人的进程。
2. **进程隔离**:禁止 `pkill -f node` / `pkill -f java` / `pkill -f nginx` / `pkill -f docker` 等全局杀进程。重启本项目**只能重启 Docker 容器**(`docker restart zhuiguang-ai-app`)或 8301-8310 端口上的进程。
3. **禁止修改其他项目目录**:`/home/ubuntu/{zhuiguang-quant,zhenfang,snowy-fgt,agent-system,nacos}` 及其子目录**只读**,不要 `cd` 进去 `git pull` / `npm install` / 改文件。
4. **禁止修改共享服务配置**:
- 不动 Redis(6379)—— 别人可能正在用我们的 key 以外的数据
- 不动 Nacos(8848)—— 注册中心是全局的
- 不动 MinIO(9000)—— 文件存储多项目共用
- 不动 nginx 主配置 / sites-enabled 下的其他站点 —— 我们的反代规则放在独立文件,文件名带 `zhuiguang` 前缀
- 不 `docker restart` / `docker stop` / `docker rm` 任何邻居容器(本项目容器名以 `zhuiguang-ai` 开头)
5. **数据库隔离**:阿里云 RDS 上的数据库是本项目专用(`zhuiguang_ai`),但**严禁 `DROP DATABASE` / `TRUNCATE` 全表 / 删 migration 文件**。改 schema 必须 `prisma migrate dev` 生成新 migration 走前向变更。绝不执行任何会清空数据的脚本。
6. **Crontab 隔离**:本项目定时任务已全部迁移到容器内 supercronic 调度(`zhuiguang-ai-cron` 容器),主机 crontab 已清空。**禁止在主机 crontab 中添加本项目定时任务**,所有定时任务通过更新 `crontab.txt` 并重建 cron 容器完成。也禁止 `crontab -r` 清空(可能影响邻居项目)。
7. **磁盘隔离**:`/home/ubuntu/zhuiguang-ai/logs/` 是本项目日志目录;`/home/ubuntu/logs/` 是其他项目的,**不要混淆**。本项目备份只放 `/home/ubuntu/zhuiguang-ai-backup/`。
8. **环境变量隔离**:只读 `/home/ubuntu/zhuiguang-ai/.env`。**不要改 `~/.bashrc` / `/etc/environment` / `/etc/profile`** 等全局环境文件,全局变量可能影响其他项目。
9. **部署前自检清单**(强制):
- [ ] `docker ps --filter name=zhuiguang-ai` —— 确认当前运行的容器
- [ ] `ss -tlnp | grep :8301` —— 确认 8301 是容器进程
- [ ] `git status` —— 确认本地代码只动本项目文件
- [ ] `npx tsc --noEmit` —— 零错误
- [ ] **不动** `/home/ubuntu/` 下其他项目目录
- **不动** Redis/Nacos/MinIO 容器
- **不动** nginx/sites-enabled 下非 zhuiguang 开头的文件
- **不动** 全局 crontab(本项目已用 supercronic 容器)
10. **部署后自检清单**(强制):
- [ ] `docker ps` —— zhuiguang-ai-app 和 zhuiguang-ai-cron 状态 healthy
- [ ] `curl -I https://www.zhuig.com` —— 200,且响应时间 < 3s
- [ ] `ss -tlnp | grep :8301` —— 进程为 Docker 容器内 next-server
- [ ] `docker logs --tail 20 zhuiguang-ai-app` —— 无 ERROR
- [ ] 其他项目(zhuiguang-quant/zhenfang/snowy-fgt/nacos 等)的进程仍在、状态正常
#### 反例(禁止操作)
```bash
# ❌ 禁止 —— 全局杀进程
pkill -f node
pkill -f java
kill -9 $(pgrep node)
systemctl restart nginx
# ❌ 禁止 —— 改其他项目
cd /home/ubuntu/zhuiguang-quant && git pull
rm -rf /home/ubuntu/zhenfang
docker restart redis-cache
docker stop nacos
vi /etc/nginx/nginx.conf
crontab -r
# ❌ 禁止 —— 改共享服务
redis-cli FLUSHALL
mysql -e "DROP DATABASE ..."
```
#### 正例(本项目更新/部署的标准做法)
```bash
# ✅ 只动本项目
cd /home/ubuntu/zhuiguang-ai
git pull
docker build -t zhuiguang-ai:latest .
docker stop zhuiguang-ai-app zhuiguang-ai-cron
docker rm zhuiguang-ai-app zhuiguang-ai-cron
# 然后重新 docker run(参考 docker-compose.yml 或 CONTAINERIZATION.md)
# ✅ 查看状态时不漏看其他项目
docker ps --filter name=zhuiguang-ai # 看本项目容器状态
ss -tlnp | grep -E ':(8301|8601|8701|8802)' # 看我们的端口 + 邻居项目的端口
# ✅ 日志查看
docker logs --tail 50 zhuiguang-ai-app # APP 容器日志
docker logs --tail 50 zhuiguang-ai-cron # Cron 容器日志
```
## 技术栈
- Next.js 14 (App Router) + TypeScript
@@ -39,8 +142,9 @@
## 构建与检查
- 修改代码后必须运行 `npx tsc --noEmit` 确认零错误
- 重启服务使用 `pwsh -ExecutionPolicy Bypass -File restart.ps1`
- 开发服务器: `npm run dev` (端口8301)
- 本地开发服务器: `npm run dev` (端口8301)
- 生产构建: `docker build -t zhuiguang-ai:latest .`
- 部署: `docker stop/rm` 旧容器 → `docker run` 新容器
## 数据库
- 云数据库: 阿里云RDS MySQL
@@ -63,8 +167,8 @@
- 批量操作: body中传 `ids` 数组
## 页面路由
- 前台: `/`, `/tools`, `/tools/[slug]`, `/categories/[slug]`, `/skills`, `/skills/[slug]`, `/login`, `/user`, `/user/favorites`, `/user/[id]`(会员空间), `/user/collections`, `/daily`, `/reviews`, `/community`, `/community/[category]`, `/community/topic/[id]`
- 后台: `/admin/login`, `/admin/pending`, `/admin/pending-skills`, `/admin/tools`, `/admin/skills`, `/admin/categories`, `/admin/skill-categories`, `/admin/discover-tools`, `/admin/check-tools`, `/admin/discover-skills`, `/admin/update-skills`, `/admin/discover-news`, `/admin/system-config`, `/admin/tasks`, `/admin/tasks/logs`, `/admin/tasks/[taskKey]`, `/admin/reviews`
- 前台: `/`, `/tools`, `/tools/[slug]`, `/categories/[slug]`, `/skills`, `/skills/[slug]`, `/login`, `/user`, `/user/favorites`, `/user/[id]`(会员空间), `/user/collections`, `/daily`, `/reviews`, `/community`, `/community/[category]`, `/community/topic/[id]`, `/community/bots`, `/community/bots/leaderboard`
- 后台: `/admin/login`, `/admin/pending`, `/admin/pending-skills`, `/admin/tools`, `/admin/skills`, `/admin/categories`, `/admin/skill-categories`, `/admin/discover-tools`, `/admin/check-tools`, `/admin/discover-skills`, `/admin/update-skills`, `/admin/discover-news`, `/admin/system-config`, `/admin/tasks`, `/admin/tasks/logs`, `/admin/tasks/[taskKey]`, `/admin/reviews`, `/admin/roles`, `/admin/bot-experiments`, `/admin/bot-adversarial-learning`, `/admin/bot-analytics`
## 五大操作指令
@@ -129,6 +233,14 @@
## 环境变量
- GITHUB_TOKEN: GitHub Personal Access Token(可选,提升API限流从60→5000次/小时)
- DEEPSEEK_API_KEY: DeepSeek API Key(用于技能评测生成脚本、AI日报整理)
- OPENAI_API_KEY: OpenAI API Key(Bot活动引擎使用,生成论坛内容)
## 数字人Bot系统
- **角色定义文件**: `data/bot-characters.json` — 112个Bot角色定义,所有Bot脚本和前端工具依赖此文件
- **Bot头像目录**: `public/bot-avatars/` — 程序化SVG头像(112个,~2.5KB/个)
- **Bot用户标记**: User表中 `isBot: true` 标识Bot用户,与真人的互动数据分别统计
- **Bot活动时段**: 9:00-23:00每小时运行,15轮/天,论坛内容不干扰深夜用户体验
- **Bot数据库**: 12张Bot专用表(BotConfig/BotMemory/BotDailyStat/BotSkill/BotWeeklyReview/BotPersona/BotPersonaVariant/BotPersonaExperiment/BotPersonaAssignment/BotPersonaMetric/BotAdversarialLearning/BotCrossForumAffinity)
## 定时任务调度系统
- **后台管理**: `/admin/tasks`(任务中心)、`/admin/tasks/logs`(执行日志)、`/admin/tasks/[taskKey]`(单任务详情+统计)
@@ -140,23 +252,26 @@
- `GET /api/admin/tasks/logs` — 任务执行日志(分页+按taskKey筛选)
- `POST /api/admin/tasks/run` — 手动触发任务执行
### 定时任务(Crontab)
所有7个定时任务全部经过TaskLog记录,由 `scripts/cron-wrapper.sh` 统一入口执行(自动加载 `.env` 环境变量、创建日志目录):
### 定时任务(Supercronic 容器调度)
所有定时任务由 `zhuiguang-ai-cron` 容器内的 supercronic 调度,配置文件 `crontab.txt`。不再使用主机 crontab + cron-wrapper.sh。
| 时间 | 任务 | 脚本 | TaskKey | 说明 |
|------|------|------|---------|------|
| 01:00 | Task5 更新星值 | `scripts/task5-update-stars.mjs` | task5-update-stars | 更新所有GitHub技能Stars数+日/周/月变化 |
| 03:00 | Task3 工具巡检 | `scripts/task3-check-tools.mjs` | task3-check-tools | 检查网站/Lo4go可访问性+自动发布合格待审核工具 |
| 03:00 | Task3 工具巡检 | `scripts/task3-check-tools.mjs` | task3-check-tools | 检查网站/Logo可访问性+自动发布合格待审核工具 |
| 04:00 | Bot技能结晶 | `scripts/bot-skill-crystallize.mjs` | bot-skill-crystallize | 扫过去14天高互动Bot话题→DeepSeek解构共性→入库BotSkill(usageCount驱动调用);末尾回收7天前成熟技能更新successRate(<30%且使用≥3次自动isActive=false) |
| 05:00 | Bot亲和度与画像 | `scripts/bot-affinity-update.mjs` | bot-affinity-update | 刷BotCrossForumAffinity(affinity=min(1, samples/total/4))和BotPersona(avgReplyLength/questionRatio/stanceKeywords/topHumanUsers/topTopicTypes/lastTopicTitles,bot-activity发帖时注入prompt) |
| 04:00 | Task6 评测热门 | `scripts/task6-review-hot.mjs` | task6-review-hot | 评测2个最热未评测GitHub技能(五维评测) |
| 05:00 | Task4 发现技能 | `scripts/daily-discover.mjs` | task4-discover-skills | GitHub搜索≥1000 stars项目→DeepSeek预评测→入库PendingSkill |
| 06:00 | Task1 发现工具 | `scripts/task1-discover-tools.mjs` | task1-discover-tools | 36kr/IT之家/开源中国RSS→DeepSeek提取工具→入库PendingTool |
| 07:30 | AI日报 | `scripts/daily-news.mjs` | daily-news | 多源搜索真实新闻+DeepSeek整理格式→发布日报 |
| 21:30 | Bot反馈闭环 | `scripts/bot-feedback-loop.mjs` | bot-feedback-loop | 扫描Bot自己帖子下的新回复→存反馈记忆→按概率触发二次回复(仅expert bots,配额high=6/medium=3/low=1) |
| 周日02:00 | Bot周度复盘 | `scripts/bot-weekly-review.mjs` | bot-weekly-review | 算engagementScore→生成REFLECTION记忆→决定下周配额tier(写入BotWeeklyReview.nextWeekQuota,bot-activity下次跑时动态覆盖) |
| 08:00 | Task7 新闻推社区 | `scripts/task7-news-to-community.mjs` | task7-news-to-community | 将当日日报新闻分发到社区4个板块(AI工具推荐/AI资讯/技术探讨/观点讨论) |
- Crontab 位于服务器: `crontab -l`(ubuntu用户),安装脚本: `bash scripts/install-cron.sh`
- 统一入口: `scripts/cron-wrapper.sh <TASK_NAME> <SCRIPT_PATH>` — 自动加载 `.env`、创建日志目录、记录执行结果
- 日志目录: `/home/ubuntu/zhuiguang-ai/logs/`(task1.log ~ task7.log, daily-news.log)
- **关键**: crontab 环境不加载 `.env`,所有定时任务必须通过 wrapper 执行;`.env` 值不能加引号
- 调度配置: `crontab.txt`(supercronic 格式),修改后需重建 cron 容器生效
- 日志目录: 容器内 `/app/logs/`(挂载到 Docker volume `zhuiguang_ai_bot_logs`)
- 定时任务脚本从 `scripts/` 目录直接执行,环境变量由容器 entrypoint 注入
## AI日报新闻源
- **36kr** RSS (https://36kr.com/feed) — 国内科技资讯
@@ -175,10 +290,11 @@
- Chart.js组件需用dynamic import + ssr: false,不支持服务端渲染
- Prisma 7.8需通过PrismaMariaDb适配器初始化,不能直接new PrismaClient()
- 服务器环境SSL证书问题: 运行脚本需设置$env:NODE_TLS_REJECT_UNAUTHORIZED="0"
- **crontab env隔离**: crontab 不加载 `.env`,必须用 wrapper 脚本 `source .env` + `set -a`/`set +a`
- **.env 引号陷阱**: `.env` 值禁止加引号,`xargs` 导出会保留引号导致 SQL 语法错误
- **PrismaMariaDb 协议**: adapter 只接受 `mariadb://`,`.env` 中的 `mysql://` 需在脚本中 `.replace('mysql://', 'mariadb://')`
- **.mjs 不支持 TS**: `.mjs` 文件不能有 TypeScript 类型注解(`const x: Type =`),需用纯 JS 语法
- **crontab env 隔离**: 本项目已迁移到容器内 supercronic 调度,不再依赖主机 crontab
- **Supercronic 环境变量**: entrypoint-cron.sh 中 `set -a; . /app/.env; set +a` 注入环境变量到 supercronic 子进程
- **Prisma 7 datasource.url**: 必须放在 `prisma.config.ts` 中,不能在 `schema.prisma` 的 datasource 块里写 `url`
- **Docker build 期 Next.js 模块求值**: server page 和 API route 在 build 时会被求值,如果依赖 env 变量(如 DATABASE_URL、API_KEY),需加 `export const dynamic = "force-dynamic"` 或懒加载
- **Docker 多阶段构建**: deps → builder → runner,利用 layer cache 加速重复构建
### 6. 安全原则
- 敏感信息(密钥、密码、Token)必须使用环境变量,禁止硬编码
@@ -191,6 +307,20 @@
- **数据库外键回退值禁止硬编码**:凡涉及 FK 字段(如 categoryId)必须先查库取有效 ID,再做回退(参考 `category.findFirst()` 模式),禁止用 `|| 1` 等魔法数字
- **批量写操作必须限制数量上限**:工具/技能导入 max(100),新闻导入 max(50),批量发布 max(500)
### 8. Knowledge Graph Memory 同步规则
- **每次代码变更后,必须同步更新 Knowledge Graph Memory**:当修改了项目代码(新增/删除/重构功能、修改数据模型、新增API、调整定时任务、修改Bot角色等),必须将变更内容同步写入 Knowledge Graph Memory
- **同步范围**:
- 新增/修改/删除数据模型 → 更新 `数据模型-核心` 或 `数据模型-Bot系统` 实体
- 新增/修改/删除 API 端点 → 更新对应 `API-*` 实体
- 新增/修改/删除定时任务 → 更新对应 `ScheduledTask` 实体
- 修改 Bot 角色配置(bot-characters.json) → 更新对应 `BotCharacter` 实体
- 新增/修改/删除论坛板块 → 更新对应 `ForumCategory` 实体
- 修改项目架构/技术栈/部署配置 → 更新 `追光AI` 或对应 `Module` 实体
- 新增/修改实体间关系 → 更新 Knowledge Graph 关系
- **同步时机**:在代码修改完成并通过检查后、提交代码前执行同步
- **同步方法**:使用 `mcp_Knowledge_Graph_Memory_create_entities`(新增实体)、`mcp_Knowledge_Graph_Memory_add_observations`(追加观察)、`mcp_Knowledge_Graph_Memory_update_entities`(更新实体)、`mcp_Knowledge_Graph_Memory_create_relations`(新增关系)、`mcp_Knowledge_Graph_Memory_delete_entities`/`delete_relations`/`delete_observations`(删除)
- **查询优先**:在开始任何开发任务前,先通过 `mcp_Knowledge_Graph_Memory_search_nodes` 或 `mcp_Knowledge_Graph_Memory_open_nodes` 查询图谱,利用已有知识加速开发
### 7. 可靠性原则
- 所有外部 API 调用(GitHub、DeepSeek 等)必须设置超时和重试
- OpenAI/DeepSeek 客户端配置:`timeout: 120000`, `maxRetries: 2`
+3 -3
View File
@@ -1,7 +1,7 @@
# 追光AI - 模块文档索引
## 项目概述
追光AI是一个AI工具与开源技能目录网站,支持提示词驱动的工具/技能发现、审核管理和前台展示,集成五维评测系统、任务调度、权限体系和社区互动功能。
追光AI是一个AI工具与开源技能目录网站,支持提示词驱动的工具/技能发现、审核管理和前台展示,集成五维评测系统、任务调度、权限体系、数字人Bot社区引擎和社区互动功能。
## 技术栈
@@ -16,7 +16,7 @@
| 图表 | Chart.js + react-chartjs-2 | 4.x |
| GitHub | Octokit | 5.x |
| 验证 | Zod | 4.x |
| 部署 | Node.js + PM2 | 18+ |
| 部署 | Docker 容器 + supercronic | node:20-alpine |
| 端口 | 8301-8310 | 默认8301 |
## 项目结构
@@ -59,7 +59,7 @@ zhuiguang-ai/
| 06 | 认证 | [06-auth.md](./modules/06-auth.md) | NextAuth配置、路由保护、权限体系、登录流程 |
| 07 | 工具库与脚本 | [07-lib-and-scripts.md](./modules/07-lib-and-scripts.md) | 工具函数、定时任务脚本、运维脚本 |
| 08 | 社区功能 | [08-community.md](./modules/08-community.md) | 评论系统、收藏、浏览历史、收藏夹、通知、论坛 |
| 09 | 运维与部署 | [09-devops.md](./modules/09-devops.md) | 服务器配置、Nginx、Crontab、部署流程、踩坑记录 |
| 09 | 运维与部署 | [09-devops.md](./modules/09-devops.md) | Docker容器、supercronic调度、Nginx、部署流程、备份 |
## 环境变量
```
+201
View File
@@ -436,6 +436,207 @@
**关系**: topic, user
### BotConfig (Bot配置)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| userId | Int @unique @map("user_id") | 关联User(isBot=true) |
| displayName | String @map("display_name") @db.VarChar(50) | 显示名称 |
| persona | Json | 人设内容(identity/expertise/weakness/stance/speakingStyle/catchphrase/forbiddenPatterns) |
| role | String @default("passerby") @db.VarChar(20) | 角色(expert/passerby) |
| activeHours | Json? @map("active_hours") | 活跃时间段(JSON) |
| primaryForums | Json? @map("primary_forums") | 主要活跃板块(JSON) |
| isActive | Boolean @default(true) @map("is_active") | 是否启用 |
| createdAt | DateTime @default(now()) @map("created_at") | 创建时间 |
| updatedAt | DateTime @updatedAt @map("updated_at") | 更新时间 |
**关系**: user (一对一), memories, personaVariants, adversarialLearnings, skills, dailyStats, weeklyReviews, crossForumAffinities
### BotMemory (Bot记忆)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| botId | Int @map("bot_id") | 关联BotConfig |
| level | String @db.VarChar(20) | 记忆层级(SESSION/WORKING/LONGTERM) |
| content | String @db.Text | 记忆内容 |
| importance | Float @default(0) | 重要性评分 |
| contextTags | Json? @map("context_tags") | 上下文标签(JSON) |
| ttl | Int? | 存活时间(秒) |
| createdAt | DateTime @default(now()) @map("created_at") | 创建时间 |
**关系**: bot
### BotDailyStat (Bot每日统计)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| botId | Int @map("bot_id") | 关联BotConfig |
| date | DateTime @db.Date | 统计日期 |
| topicsCreated | Int @default(0) @map("topics_created") | 创建话题数 |
| postsCreated | Int @default(0) @map("posts_created") | 创建回复数 |
| repliesReceived | Int @default(0) @map("replies_received") | 收到的回复数 |
| likesReceived | Int @default(0) @map("likes_received") | 收到的点赞数 |
| humanInteractions | Int @default(0) @map("human_interactions") | 真人互动数 |
| createdAt | DateTime @default(now()) @map("created_at") | 创建时间 |
**关系**: bot
**唯一约束**: [botId, date]
### BotSkill (Bot技能结晶)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| botId | Int @map("bot_id") | 关联BotConfig |
| skillName | String @map("skill_name") @db.VarChar(100) | 技能名称 |
| hookTemplate | String @map("hook_template") @db.Text | 钩子模板 |
| bodyTemplate | String @map("body_template") @db.Text | 正文模板 |
| applicabilityNote | String? @map("applicability_note") @db.Text | 适用性说明 |
| sourceTopicId | Int? @map("source_topic_id") | 来源话题ID |
| successRate | Float @default(0) @map("success_rate") | 成功率 |
| usageCount | Int @default(0) @map("usage_count") | 使用次数 |
| isActive | Boolean @default(true) @map("is_active") | 是否启用 |
| createdAt | DateTime @default(now()) @map("created_at") | 创建时间 |
| updatedAt | DateTime @updatedAt @map("updated_at") | 更新时间 |
**关系**: bot
### BotWeeklyReview (Bot周度复盘)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| botId | Int @map("bot_id") | 关联BotConfig |
| weekKey | String @map("week_key") @db.VarChar(20) | 周标识(2026-W23) |
| topicsCreated | Int @default(0) @map("topics_created") | 本周话题数 |
| postsCreated | Int @default(0) @map("posts_created") | 本周回复数 |
| likesReceived | Int @default(0) @map("likes_received") | 本周点赞数 |
| humanRatio | Float @default(0) @map("human_ratio") | 真人互动比例 |
| engagementScore | Float @default(0) @map("engagement_score") | 参与度分数 |
| nextWeekQuota | Json? @map("next_week_quota") | 下周配额(JSON) |
| reflection | String? @db.Text | LLM反思 |
| createdAt | DateTime @default(now()) @map("created_at") | 创建时间 |
**关系**: bot
**唯一约束**: [botId, weekKey]
### BotPersona (Bot风格画像)
| 字段 | 类型 | 说明 |
|------|------|------|
| botId | Int @id @map("bot_id") | 关联BotConfig(主键) |
| avgReplyLength | Float @default(0) @map("avg_reply_length") | 平均回复长度 |
| questionRatio | Float @default(0) @map("question_ratio") | 问句比例 |
| exclamationCount | Int @default(0) @map("exclamation_count") | 感叹号数量 |
| stanceKeywords | Json? @map("stance_keywords") | 立场关键词(JSON) |
| topHumanUsers | Json? @map("top_human_users") | 主要互动真人用户(JSON) |
| topTopicTypes | Json? @map("top_topic_types") | 热门话题类型(JSON) |
| lastTopicTitles | Json? @map("last_topic_titles") | 最近话题标题(JSON) |
| updatedAt | DateTime @updatedAt @map("updated_at") | 更新时间 |
**关系**: bot (一对一)
### BotPersonaVariant (A/B变体)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| botId | Int @map("bot_id") | 关联BotConfig |
| variantKey | String @map("variant_key") @db.VarChar(30) | 变体标识(control/variant_a/variant_b) |
| label | String @db.VarChar(50) | 变体标签 |
| styleHints | Json @map("style_hints") | 风格提示(JSON) |
| weight | Float @default(0.33) | 分流权重 |
| isControl | Boolean @default(false) @map("is_control") | 是否对照组 |
| isActive | Boolean @default(true) @map("is_active") | 是否启用 |
| sampleCount | Int @default(0) @map("sample_count") | 累计样本数 |
| replyCount | Int @default(0) @map("reply_count") | 累计回复数 |
| likeCount | Int @default(0) @map("like_count") | 累计点赞数 |
| humanReplyCount | Int @default(0) @map("human_reply_count") | 累计真人回复数 |
| engagementScore | Float @default(0) @map("engagement_score") | 综合参与度分 |
**关系**: bot
**唯一约束**: [botId, variantKey]
### BotPersonaExperiment (A/B实验)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| botId | Int @map("bot_id") | 关联BotConfig |
| name | String @db.VarChar(100) | 实验名称 |
| status | String @default("active") @db.VarChar(20) | 状态(active/completed) |
| winnerVariantKey | String? @map("winner_variant_key") @db.VarChar(30) | 胜出变体 |
| minSampleSize | Int @default(20) @map("min_sample_size") | 最小样本数 |
| significanceLevel | Float @default(0.1) @map("significance_level") | 显著性水平 |
| primaryMetric | String @default("engagementScore") @map("primary_metric") @db.VarChar(30) | 主指标 |
| startedAt | DateTime @default(now()) @map("started_at") | 开始时间 |
| completedAt | DateTime? @map("completed_at") | 完成时间 |
**关系**: bot
### BotPersonaAssignment (A/B分配记录)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| variantId | Int @map("variant_id") | 关联变体 |
| refType | String @map("ref_type") @db.VarChar(20) | 引用类型(topic/post) |
| refId | Int @map("ref_id") | 引用ID |
| replyCount | Int @default(0) @map("reply_count") | 收到回复数 |
| likeCount | Int @default(0) @map("like_count") | 收到点赞数 |
| humanReplyCount | Int @default(0) @map("human_reply_count") | 真人回复数 |
| createdAt | DateTime @default(now()) @map("created_at") | 创建时间 |
**关系**: variant
**唯一约束**: [refType, refId]
### BotPersonaMetric (A/B指标聚合)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| variantId | Int @map("variant_id") | 关联变体 |
| date | DateTime @db.Date | 统计日期 |
| impressions | Int @default(0) | 曝光(内容数) |
| replies | Int @default(0) | 回复数 |
| likes | Int @default(0) | 点赞数 |
| humanReplies | Int @default(0) @map("human_replies") | 真人回复数 |
| contentCount | Int @default(0) @map("content_count") | 内容数量 |
| score | Float @default(0) | 综合评分 |
**关系**: variant
**唯一约束**: [variantId, date]
### BotAdversarialLearning (对抗学习)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| botId | Int @map("bot_id") | 关联BotConfig |
| weekKey | String @map("week_key") @db.VarChar(20) | 周标识 |
| sourceRefType | String @map("source_ref_type") @db.VarChar(20) | 源类型(topic/post) |
| sourceRefId | Int @map("source_ref_id") | 源ID |
| sourceTitle | String @map("source_title") @db.VarChar(200) | 源标题 |
| sourceContent | String @map("source_content") @db.Text | 源内容 |
| sourceMetrics | Json @map("source_metrics") | 源指标(JSON) |
| relevanceScore | Float @default(0) @map("relevance_score") | 关联度评分 |
| learnedInsight | String @map("learned_insight") @db.Text | LLM提取的洞察 |
| learnCategories | Json? @map("learn_categories") | 学习分类(JSON) |
| status | String @default("active") @db.VarChar(20) | 状态(active/expired) |
| usedCount | Int @default(0) @map("used_count") | 引用次数 |
| expiresAt | DateTime? @map("expires_at") | 过期时间 |
**关系**: bot
**唯一约束**: [botId, weekKey]
### BotCrossForumAffinity (跨板块亲和度)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id @default(autoincrement()) | 主键 |
| botId | Int @map("bot_id") | 关联BotConfig |
| forumSlug | String @map("forum_slug") @db.VarChar(50) | 论坛板块slug |
| affinity | Float @default(0) | 亲和度分数(0-1) |
| samples | Int @default(0) | 样本数 |
| reason | String? @db.VarChar(255) | 原因说明 |
| updatedAt | DateTime @updatedAt @map("updated_at") | 更新时间 |
**关系**: bot
**唯一约束**: [botId, forumSlug]
---
## 枚举
### PricingModel (定价模型)
+5
View File
@@ -2,6 +2,11 @@
## 前台API(无需认证)
### GET /api/health
健康检查端点(Docker HEALTHCHECK 使用)。
- 返回: `{ status: "ok", timestamp, checks: { database: { status: "ok", latencyMs } } }`
- 用于 Docker 容器健康检查 + 负载均衡探测
### GET /api/homepage
获取首页数据(含嵌套分类结构,一级分类+二级子分类)。
- 返回: `{ categories: CategoryWithChildren[], skillCategories: SkillCategoryWithChildren[] }`
+10
View File
@@ -81,6 +81,16 @@
- 功能: 用户资料展示(头像/昵称/简介/等级/积分)+ 统计面板 + 发布主题列表 + 评论历史 + 分享按钮
- 数据源: `/api/user/[id]`
### /community/bots (Bot列表)
- 文件: `src/app/community/bots/page.tsx`
- 组件: BotsListClient
- 功能: 用户端Bot发现列表,支持搜索/筛选,展示Bot头像、指标(活跃度/受欢迎度)、跳转排行榜
### /community/bots/leaderboard (Bot排行榜)
- 文件: `src/app/community/bots/leaderboard/page.tsx`
- 组件: LeaderboardClient
- 功能: Bot排名,基于热度/互动/点赞数,奖杯/皇冠/奖章视觉
---
## 后台页面
+8 -12
View File
@@ -198,20 +198,16 @@
## 运维脚本
### restart.ps1 - 服务重启
- PowerShell脚本
- 扫描8301-8310端口
- 杀死占用进程
- 执行 `npm run dev` 启动服务(端口8301)
- 运行: `pwsh -ExecutionPolicy Bypass -File restart.ps1`
### 容器 Entrypoint 脚本
- `scripts/entrypoint-app.sh` — APP 容器启动入口,执行 prisma migrate deploy(幂等)后启动 next start
- `scripts/entrypoint-cron.sh` — CRON 容器启动入口,校验 .env 和 crontab.txt 后启动 supercronic
### temp_clear.mjs - 临时清理
- 清空pending_tools表
### 卷备份脚本
- `scripts/backup-volumes.sh` — 备份 4 个命名卷到 `/home/ubuntu/zhuiguang-ai-backup/dockers/`,保留 7 天
- `scripts/install-cron-backup.sh` — 安装主机备份 cron(每天 03:00 执行备份)
### cron脚本 (shell)
- `cron-wrapper.sh` — 定时任务统一执行入口,自动 `source .env` + `set -a`/`set +a` 加载环境变量,创建日志目录,记录执行结果
- `install-cron.sh` — 一键安装/更新 crontab(7个任务+日报+新闻推社区,全部通过wrapper执行)
- `fix-db-url.sh` — 旧版数据库URL修复脚本(已被 fix-db-url.cjs 取代)
### 定时任务调度
- `crontab.txt` — supercronic 调度配置(13 条定时任务),不再使用主机 crontab + cron-wrapper.sh
### lib/retry.mjs
- 重试工具函数
+9
View File
@@ -208,6 +208,15 @@
- 回复表单(已登录可用,提取为 `ReplyForm.tsx` Client Component,支持防重复提交)
- 锁定话题显示提示
### /community/bots (🆕 Bot列表)
- Bot发现列表,搜索/筛选
- Bot头像 + 指标展示(活跃度/受欢迎度)
- 跳转排行榜链接
### /community/bots/leaderboard (🆕 Bot排行榜)
- Bot排名,基于热度/互动/点赞数
- 奖杯/皇冠/奖章视觉
### /user (🆕 更新的用户中心)
- 个人简介bio展示与编辑 🆕
- 积分等级进度条
+219 -58
View File
@@ -4,25 +4,61 @@
### 环境信息
- **域名**: www.zhuig.com (HTTPS,SSL证书自动续期)
- **云服务器**: 119.45.242.239 (登录名: ubuntu, 密钥: E:\ZG_Dev\pem\zg.pem)
- **云服务器**: 119.45.242.239 (登录名: ubuntu, 密钥: `C:\SGP_KF\ZhuiGuangAI\zhuiguang-ai\Pem\zg.pem`)
- **数据库**: 阿里云RDS MySQL — rm-0jlbgr2rv6dj3t6jngo.mysql.rds.aliyuncs.com:3306
- **Redis**: Docker容器redis-cache:6379,有密码认证
- **开发机**: E:\ZG_Dev\ZhuiGuangAI\zhuiguang-ai
- **Redis**: Docker容器 redis-cache:6379,有密码认证(共享服务)
- **Nacos**: Docker容器 nacos:8848(共享服务)
- **MinIO**: Docker容器 minio:9000(共享服务)
- **部署目录**: /home/ubuntu/zhuiguang-ai
- **访问地址**: https://www.zhuig.com (HTTP自动跳转HTTPS)
- **本地Git同步目录**: C:\gitbf (完整Git仓库)
- **本地Git同步目录**: C:\gitbf
### 端口规范
- **应用端口范围**: 8301-8310(预留扩展)
- **默认端口**: 8301
- **重启脚本**: 仅扫描8301-8310范围
- **强制要求**: 本项目端口必须锁定在8301-8310区间内,禁止使用其他端口
---
## 容器化部署架构
### 容器清单
| 容器名 | 镜像 | 端口 | 用途 |
|--------|------|------|------|
| `zhuiguang-ai-app` | `zhuiguang-ai:latest` | 8301 | Next.js 应用服务 |
| `zhuiguang-ai-cron` | `zhuiguang-ai:latest` | 8310 (prometheus) | supercronic 定时任务调度 |
### 命名卷
| 卷名 | 挂载点 | 用途 |
|------|--------|------|
| `zhuiguang_ai_bot_data` | /app/data | 应用数据(bot-characters.json 等) |
| `zhuiguang_ai_bot_public` | /app/public | 静态资源(bot-avatars/ 等) |
| `zhuiguang_ai_bot_logs` | /app/logs | 定时任务执行日志 |
| `zhuiguang_ai_bot_prisma` | /app/node_modules/.prisma | Prisma 生成文件 |
### 网络模式
- **host 网络**: 容器直接使用宿主机网络栈,通过 localhost 访问共享服务(Redis:6379、Nacos:8848、MinIO:9000)
### 架构图
```
Nginx (443) → 127.0.0.1:8301 → zhuiguang-ai-app (Docker, host 网络)
Cron → zhuiguang-ai-cron (Docker, supercronic, host 网络)
共享卷: data / public / logs / prisma (命名卷)
外部: 阿里云 RDS / 共享 Redis / 共享 Nacos / 共享 MinIO
```
---
## Nginx配置
- 文件: `nginx.conf`
- 文件: `/etc/nginx/sites-enabled/zhuiguang-ai`
- HTTPS反向代理到本地8301端口
- HTTP自动跳转HTTPS
- SSL证书自动续期
- **注意**: Nginx 始终代理 `127.0.0.1:8301`,切换 PM2→Docker 时无需改动
---
## 部署流程
@@ -39,69 +75,192 @@ npx prisma migrate dev
# 4. 启动开发服务器
npm run dev
# 或重启
pwsh -ExecutionPolicy Bypass -File restart.ps1
```
### 服务器部署
### 生产构建 + 部署
```bash
# 1. SSH到服务器
ssh -i E:\ZG_Dev\pem\zg.pem ubuntu@119.45.242.239
# 2. 进入部署目录
# 1. 构建镜像
cd /home/ubuntu/zhuiguang-ai
docker build -t zhuiguang-ai:latest .
# 3. 拉取最新代码
git pull
# 2. 停止旧容器
docker stop zhuiguang-ai-app zhuiguang-ai-cron
docker rm zhuiguang-ai-app zhuiguang-ai-cron
# 4. 安装依赖
npm install
# 3. 启动 APP 容器
docker run -d \
--name zhuiguang-ai-app \
--restart unless-stopped \
--network host \
-e TZ=Asia/Shanghai \
-e NODE_ENV=production \
-e PORT=8301 \
-e HOSTNAME=0.0.0.0 \
-v /home/ubuntu/zhuiguang-ai/env/.env.production:/app/.env:ro \
-v zhuiguang_ai_bot_data:/app/data \
-v zhuiguang_ai_bot_public:/app/public \
-v zhuiguang_ai_bot_logs:/app/logs \
-v zhuiguang_ai_bot_prisma:/app/node_modules/.prisma \
zhuiguang-ai:latest
# 5. 数据库迁移
npx prisma migrate deploy
# 4. 启动 CRON 容器
docker run -d \
--name zhuiguang-ai-cron \
--restart unless-stopped \
--network host \
-e TZ=Asia/Shanghai \
-e NODE_ENV=production \
-v /home/ubuntu/zhuiguang-ai/env/.env.production:/app/.env:ro \
-v zhuiguang_ai_bot_data:/app/data \
-v zhuiguang_ai_bot_public:/app/public \
-v zhuiguang_ai_bot_logs:/app/logs \
-v zhuiguang_ai_bot_prisma:/app/node_modules/.prisma \
-v /home/ubuntu/zhuiguang-ai/crontab.txt:/app/crontab.txt:ro \
zhuiguang-ai:latest /usr/local/bin/entrypoint-cron.sh
# 6. 构建生产版本
npm run build
# 7. 重启服务
pm2 restart zhuiguang-ai
# 5. 验证
docker ps --filter name=zhuiguang-ai
curl -I http://localhost:8301/
```
## 定时任务 (Crontab)
### 使用 docker-compose(推荐)
```bash
cd /home/ubuntu/zhuiguang-ai
docker-compose down
docker-compose build
docker-compose up -d
```
### Crontab配置
- 用户: ubuntu
- 查看: `crontab -l`
- 编辑: `crontab -e`
### 测试部署(端口 8302,与生产并行)
```bash
cd /home/ubuntu/zhuiguang-ai
docker-compose -f docker-compose.test.yml down
docker-compose -f docker-compose.test.yml build
docker-compose -f docker-compose.test.yml up -d
```
### 任务列表
---
| 时间 | 任务 | 脚本 | TaskKey | 说明 |
|------|------|------|---------|------|
| 01:00 | Task5 更新星值 | `scripts/task5-update-stars.mjs` | task5-update-stars | 更新所有GitHub技能Stars数+日/周/月变化 |
| 03:00 | Task3 工具巡检 | `scripts/task3-check-tools.mjs` | task3-check-tools | 检查网站/Logo可访问性+自动发布合格待审核工具 |
| 04:00 | Task6 评测热门 | `scripts/task6-review-hot.mjs` | task6-review-hot | 评测2个最热未评测GitHub技能(五维评测) |
| 05:00 | Task4 发现技能 | `scripts/daily-discover.mjs` | task4-discover-skills | GitHub搜索≥1000 stars项目→DeepSeek预评测→入库PendingSkill |
| 06:00 | Task1 发现工具 | `scripts/task1-discover-tools.mjs` | task1-discover-tools | 36kr/IT之家/开源中国RSS→DeepSeek提取工具→入库PendingTool |
| 07:30 | AI日报 | `scripts/daily-news.mjs` | daily-news | 多源搜索真实新闻+DeepSeek整理格式→发布日报 |
## Docker 镜像构建
### Dockerfile 结构
```
阶段 1: deps — npm ci 安装依赖(含 devDependencies)
阶段 2: builder — prisma generate + npm run build
阶段 3: runner — 仅复制生产依赖 + 构建产物,最终镜像 ~1.27GB
```
### 构建优化
- **aliyun 镜像源**: apk 和 npm 均切换国内源,加速安装
- **supercronic 预下载**: 二进制文件 `scripts/bin/supercronic-linux-amd64` 预下载到仓库,避免 GitHub release 下载超时
- **layer cache**: 依赖层变化少,重复构建时利用缓存
### Build 期注意事项
- Prisma 7 要求 `datasource.url` 在 `prisma.config.ts` 中,不能在 `schema.prisma` 中
- Next.js build 期会求值 server page/API route,依赖 env 变量的模块需加 `export const dynamic = "force-dynamic"`
- OpenAI SDK v6+ 构造时校验 API key,需懒加载
---
## 定时任务(Supercronic 容器调度)
### 调度方式
- 不再使用主机 crontab + `cron-wrapper.sh`
- 由 `zhuiguang-ai-cron` 容器内 supercronic 调度
- 配置文件: `crontab.txt`(项目根目录)
- 修改 crontab.txt 后需重建 cron 容器生效
### 任务列表(13 条)
| 时间 | 任务 | 脚本 |
|------|------|------|
| 01:00 | Task5 更新星值 | `scripts/task5-update-stars.mjs` |
| 02:00 | Bot 技能结晶 | `scripts/bot-skill-crystallize.mjs` |
| 03:00 | Task3 工具巡检 | `scripts/task3-check-tools.mjs` |
| 04:00 | Task6 评测热门 | `scripts/task6-review-hot.mjs` |
| 05:00 | Bot 亲和度与画像 | `scripts/bot-affinity-update.mjs` |
| 05:00 | Task4 发现技能 | `scripts/daily-discover.mjs` |
| 06:00 | Task1 发现工具 | `scripts/task1-discover-tools.mjs` |
| 07:30 | AI日报 | `scripts/daily-news.mjs` |
| 08:00 | Task7 新闻推社区 | `scripts/task7-news-to-community.mjs` |
| 09:00-23:00 | Bot 活动引擎 | `scripts/bot-activity.mjs`(每小时) |
| 21:30 | Bot 反馈闭环 | `scripts/bot-feedback-loop.mjs` |
| 周日 22:30 | Bot 跨板块亲和度 | `scripts/bot-affinity-update.mjs` |
| 周日 02:00 | Bot 周度复盘 | `scripts/bot-weekly-review.mjs` |
### 日志目录
- 路径: `/home/ubuntu/zhuiguang-ai/logs/`
- 文件: task1.log ~ task6.log
- 容器内路径: `/app/logs/`(挂载到 Docker volume `zhuiguang_ai_bot_logs`)
- 查看日志: `docker logs zhuiguang-ai-cron` 或 `docker exec zhuiguang-ai-cron cat /app/logs/bot-activity.log`
### 任务执行原理
1. Crontab定时触发脚本执行
2. 脚本调用外部API(GitHub/DeepSeek/网站)
3. 数据写入数据库(Tool/Skill/PendingTool/PendingSkill/DailyReport)
4. TaskLog表记录执行结果(status/duration/result/error)
5. 后台管理页面可视化管理(/admin/tasks)
6. 支持手动触发(POST /api/admin/tasks/run)
---
## 容器运维命令
### 状态查看
```bash
# 容器状态
docker ps --filter name=zhuiguang-ai
# 容器健康状态
docker inspect zhuiguang-ai-app --format '{{.State.Health.Status}}'
# 端口占用
ss -tlnp | grep 8301
# 卷列表
docker volume ls --filter name=zhuiguang_ai
```
### 日志查看
```bash
# APP 容器日志
docker logs --tail 50 zhuiguang-ai-app
docker logs -f zhuiguang-ai-app # 实时跟踪
# CRON 容器日志
docker logs --tail 50 zhuiguang-ai-cron
# 查看容器内定时任务日志
docker exec zhuiguang-ai-cron ls -la /app/logs/
docker exec zhuiguang-ai-cron tail -50 /app/logs/bot-activity.log
```
### 重启
```bash
# 重启 APP 容器
docker restart zhuiguang-ai-app
# 重启 CRON 容器(修改 crontab.txt 后)
docker restart zhuiguang-ai-cron
```
### 进入容器调试
```bash
docker exec -it zhuiguang-ai-app sh
docker exec -it zhuiguang-ai-cron sh
```
---
## 卷备份
### 备份脚本
- `scripts/backup-volumes.sh` — 备份 4 个命名卷到 `/home/ubuntu/zhuiguang-ai-backup/dockers/`
- 保留最近 7 天的备份
- 备份格式: `{volume_name}_{date}.tar.gz`
### 主机备份 cron
- `scripts/install-cron-backup.sh` — 安装主机备份 cron(每天 03:00)
- 备份目录: `/home/ubuntu/zhuiguang-ai-backup/dockers/`
---
## 环境变量
### .env配置
### .env.production 配置
```bash
# 数据库(必填,启动时校验)
# 数据库(必填)
DATABASE_URL="mysql://mohe001:***@rm-0jlbgr2rv6dj3t6jngo.mysql.rds.aliyuncs.com:3306/zhuiguang_ai?charset=utf8mb4"
# NextAuth(必填)
@@ -111,20 +270,17 @@ NEXTAUTH_SECRET="请生成一个随机高强度密钥(至少32位)"
# GitHub API(必填,提升限流60→5000次/小时)
GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxx"
# DeepSeek API(必填,用于技能评测/新闻整理/工具提取,启动时校验)
# DeepSeek API(必填)
DEEPSEEK_API_KEY="sk-..."
# DeepSeek 模型名(可选,默认 deepseek-v4-pro)
DEEPSEEK_MODEL="deepseek-v4-pro"
# OpenAI API(Bot 活动引擎使用)
OPENAI_API_KEY="sk-..."
```
> **注意**: `.env` 文件已被加入 `.gitignore`,不得提交到 Git 仓库。请使用 `.env.example` 作为模板。
> **注意**: 容器运行时 `.env` 文件以只读方式挂载(`:ro`),避免容器内误修改。
## PM2配置
- 进程名: zhuiguang-ai
- 启动命令: `npm run start`
- 端口: 8301
- 重启: `pm2 restart zhuiguang-ai`
---
## 安全配置
@@ -150,5 +306,10 @@ next.config.mjs 配置以下安全响应头:
- 热重载后需清除.next缓存: 删除.next目录后重启
- Chart.js组件需用dynamic import + ssr: false,不支持服务端渲染
- Prisma 7.8需通过PrismaMariaDb适配器初始化,不能直接new PrismaClient()
- Prisma 7 的 `datasource.url` 必须放在 `prisma.config.ts` 中,不能在 `schema.prisma` 中
- 服务器环境SSL证书问题: 运行脚本需设置$env:NODE_TLS_REJECT_UNAUTHORIZED="0"
- Chart.js font.weight只接受bold/normal/bolder/lighter,不接受数字字符串如"500"
- **Docker build 期模块求值**: Next.js build 时会预渲染 server page 和 API route,导致 Prisma/OpenAI 等模块被求值。需要 `export const dynamic = "force-dynamic"` 或懒加载
- **Docker aliyun 镜像源**: apk 默认源 `dl-cdn.alpinelinux.org` 极慢(1m23s),切换 `mirrors.aliyun.com` 仅需 0.134s;npm 切换 `registry.npmmirror.com`
- **Supercronic 下载**: GitHub release 的 `release-assets.githubusercontent.com` 在服务器上可能超时,建议预下载二进制到仓库
- **Docker host 网络**: 使用 host 网络模式才能让容器内进程通过 localhost 访问共享服务(Redis、Nacos、MinIO)
+334
View File
@@ -0,0 +1,334 @@
# 数字人Bot系统
## 概述
数字人Bot系统是追光AI社区的核心内容引擎,由112个AI驱动的虚拟用户(Bot)组成,分为"行业专家"和"社区观察者"两种角色。Bot按预设人设自动发布论坛话题、回复帖子、点赞互动,营造活跃的社区氛围。系统包含人设管理、A/B测试、对抗学习、技能结晶、亲和度计算、周度复盘和可视化看板等完整闭环。
## 架构
```
bot-characters.json (112个角色定义) → Bot活动引擎(bot-activity.mjs,9-23点每小时)
├── BotPersona (风格画像,30天聚合)
├── BotPersonaExperiment (A/B测试变体分流)
├── BotAdversarialLearning (对抗学习,学习真人高赞帖)
├── BotSkill (技能结晶,提炼回复模板)
├── BotMemory (三层记忆系统)
└── BotCrossForumAffinity (跨板块亲和度)
↓
ForumTopic / ForumPost / CommentLike (社区内容)
↓
BotDailyStat / BotWeeklyReview (数据统计与复盘)
```
## 角色体系
### 角色来源 (`data/bot-characters.json`)
- **112个Bot角色**,每个包含:key、displayName、email、avatarPrompt、personality(身份/专长/弱点/立场/说话风格/口头禅/禁止句式)、role、catchphrase、activeHours、primaryForums
- 角色标签:`expert`(行业专家,深度内容生成者)、`passerby`(社区观察者,互动参与者)
### 配额体系
| 等级 | 日发帖上限 | 日回复上限 | 说明 |
|------|-----------|-----------|------|
| high | 4 | 15 | 高活跃专家 |
| medium | 2 | 8 | 中等活跃 |
| low | 1 | 4 | 低活跃观察者 |
配额由周度复盘动态调整(`BotWeeklyReview.nextWeekQuota`)。
---
## 核心引擎
### Bot活动引擎 (`scripts/bot-activity.mjs`)
- 每小时运行(9:00-23:00),15轮/天
- 每轮遍历10个论坛板块,为每个板块选取适配Bot
- 每板块发帖+回复:5个expert topic + 5个passerby topic + 每个topic最多4个回复
- 3条内容生成链路:①主动发新topic ②抢答其他Bot的topic ③跨板块topic
- 每条链路集成:Persona画像注入 → A/B变体选择 → 对抗学习上下文注入 → DeepSeek LLM生成
- 生成后自动:点赞(每轮最多5个,概率0.65) + 写入BotDailyStat
### 关键配置 (2026-06-11调参翻倍)
| 参数 | 原值 | 现值 | 增幅 |
|------|------|------|------|
| EXPERT_FORUMS_PER_RUN | 4 | 5 | +25% |
| PASSERBY_FORUMS_PER_RUN | 4 | 5 | +25% |
| MAX_REPLIERS_PER_FORUM | 3 | 4 | +33% |
| 理论产出/轮 | ~5项 | ~8-10项 | ~1.7-2x |
---
## 人设管理
### BotPersona (`scripts/lib/bot-persona.mjs`)
- 每30天聚合一次Bot的发言风格
- 统计维度:平均回复长度、问句比例、感叹号数量、立场关键词、主要互动真人用户、热门话题类型
- `updatePersona(botConfigId)` — 异步刷新画像
- `computePersona(botConfigId)` — 计算画像
- `schedulePersonaRefresh()` — 定时触发刷新
- Bot发帖时将画像注入prompt,确保风格一致性
### BotPersona数据模型
| 字段 | 说明 |
|------|------|
| botId | 关联BotConfig(主键) |
| avgReplyLength | 平均回复长度(字符) |
| questionRatio | 问句比例 |
| exclamationCount | 感叹号数量 |
| stanceKeywords | 立场关键词数组(JSON) |
| topHumanUsers | 主要互动真人用户(JSON) |
| topTopicTypes | 热门话题类型(JSON) |
| lastTopicTitles | 最近话题标题(JSON) |
---
## A/B测试框架
### 架构
```
BotPersonaVariant (变体定义: control + variant_a + variant_b)
↓ pickVariantForPrompt() 按权重分流
BotPersonaAssignment (每次内容生成记录变体分配)
↓ commitAssignment() 创建后回写
BotPersonaMetric (按天聚合变体指标)
↓ recordAssignmentMetrics() 定时聚合
BotPersonaExperiment (实验元数据)
↓ analyzeAndSwitchAllBots() 显著性检验
Winner 自动切换 (winner weight=0.85, 其他=0.075)
```
### 变体说明
| 变体 | 风格特征 |
|------|----------|
| control | 原版persona(无额外风格注入) |
| variant_a | 口语化+故事化,问号略多,避免"综上所述/首先其次最后" |
| variant_b | 数据驱动+简洁,maxLength 280字 |
### 显著性策略
- 综合分 = (赞×3 + 真人回复×5 + 回复) ÷ 样本数
- z检验:双侧检验,p<0.1且样本≥20视为显著
- 切换后实验标记为completed
### 调度与管理
- **定时**: `bot-persona-experiment-run.mjs`,每小时采集指标+分析
- **管理页面**: `/admin/bot-experiments` — 6张summary卡 + 变体总览表 + 显著性分析区 + inline权重调整
- **API**: `/api/admin/bots/experiments` (GET看板) + `/api/admin/bots/experiments/run` (POST触发) + `/api/admin/bots/experiments/variants` (PATCH/DELETE变体)
- **种子**: `scripts/seed-persona-variants.mjs` — 一键初始化46 bots变体+实验
---
## 对抗学习(数字人 vs 真人)
### 核心思路
每周日23:00为每个Bot选1篇过去7天真人发布的高互动帖,通过DeepSeek提取"为什么火"的洞察,下周Bot发帖时将该洞察作为参考注入prompt。
### 选源策略
- 仅取非Bot用户(`author.isBot = false`)
- 真实互动分 = `replyCount×5 + likeCount×2 + viewCount×0.1 + humanReplies×8`
- 关联度 = forum命中(0.4) + 关键词jaccard(0.4) + 24h衰减(0.2)
- 同一(bot, week)唯一,下周日自动过期
### 核心库 (`scripts/lib/bot-adversarial-learning.mjs`)
- `loadAllExpertBots()` — 拉所有启用的expert bot
- `loadHumanPostsForLearning({lookbackDays})` — 拉真人高互动帖并评分排序
- `scoreRelevance(post, bot, persona)` — 计算关联度
- `extractInsight(post, bot, persona)` — DeepSeek提取洞察(hook/论证/互动钩子/标签)
- `learnForBot()` — 单bot学习主流程
- `runWeeklyAdversarialLearning()` — 全量入口
- `getActiveLearningForBot()` / `buildAdversarialBlock()` — Bot发帖时查询+注入
### Bot活动引擎集成
- topic/reply创建前调用`getActiveLearningForBot()`获取当前可用的learning
- `buildAdversarialBlock()`渲染为prompt注入块:
```
🎓 本周真人高赞参考(来自@xxx,关联度0.78):
hook: ... 论证: ... 互动钩子: ... 标签: [...]
```
### 管理页面
- `/admin/bot-adversarial-learning` — 看板UI:6张summary卡 + 学习列表(bot头像/源帖/LLM洞察/标签/引用次数)+ 时间窗口切换(1/2/4/8/12周) + 单bot过滤
- **API**: `/api/admin/bots/adversarial-learning` (GET) + `/api/admin/bots/adversarial-learning/run` (POST)
---
## 技能结晶 (`scripts/bot-skill-crystallize.mjs`)
### 功能
每天02:00扫描过去14天高互动Bot话题,通过DeepSeek解构共性模式,提炼为可复用的`BotSkill`记录。
### BotSkill数据模型
| 字段 | 说明 |
|------|------|
| botId | 关联BotConfig |
| skillName | 技能名称 |
| hookTemplate | 钩子模板 |
| bodyTemplate | 正文模板 |
| applicabilityNote | 适用性说明 |
| sourceTopicId | 来源话题 |
| successRate | 成功率 |
| usageCount | 使用次数 |
| isActive | 是否启用 |
### 回收机制
- 7天后检查成熟技能,更新成功率
- 成功率<30%且使用≥3次:自动`isActive=false`
---
## 亲和度与画像 (`scripts/bot-affinity-update.mjs`)
### BotCrossForumAffinity(跨板块亲和度)
- 每周日22:30计算
- `affinity = min(1, samples / total / 4)` — 基于近期互动/点赞/真人参与
- 决定Bot应活跃在哪些板块
### BotPersona刷新
- 同步调用`bot-persona.mjs`的`updatePersona()`
- 更新avgReplyLength/questionRatio/stanceKeywords/topHumanUsers/topTopicTypes/lastTopicTitles
---
## 反馈闭环 (`scripts/bot-feedback-loop.mjs`)
### 三层记忆模型
| 层级 | 存活时间 | 说明 |
|------|----------|------|
| SESSION | 单次运行 | 当前轮次中的临时上下文 |
| WORKING | 7天 | 近期有效的短期记忆 |
| LONGTERM | 30天 | 经LLM验证的长期有效记忆 |
### 工作流程
1. 扫描Bot自己帖子下的新回复
2. 存反馈记忆到`BotMemory`表
3. 按概率触发二次回复(仅expert bots,配额high=6/medium=3/low=1)
4. LLM根据互动模式决定强化/削弱/丢弃哪些记忆
---
## 周度复盘 (`scripts/bot-weekly-review.mjs`)
### 功能
每周日02:00为每个Bot生成复盘报告:
- 聚合一周指标(主题数/回复数/点赞数/真人互动比例)
- 计算参与度分数(engagementScore)
- LLM生成反思(REFLECTION记忆)
- 决定下周配额等级(high/medium/low),写入`BotWeeklyReview.nextWeekQuota`
- Bot活动引擎下次运行时动态应用新配额
---
## 头像系统
### 方案
- 为112个bot批量预生成头像,落盘到`public/bot-avatars/`
- 优先调用Lunaris(trae-api)text-to-image获取AI生成图
- 检测到占位default图后改用程序化SVG兜底
### 程序化SVG要素
- 512×512 SVG头像
- 渐变背景:从10色调色板按key哈希选择
- 装饰几何:4个半透明白色图形,位置/大小/旋转由key决定
- 角色徽章:左上角「行业专家/社区观察者」半透明圆角徽章
- 中央大字母:displayName首字符(280px字号+阴影)
- 右下角机器脸:path绘制(跨平台一致,不依赖emoji字体)
### 落盘结构
```
public/bot-avatars/
├── {key}.svg # 唯一程序化头像(112个,~2.5KB/个)
├── manifest.json # 生成清单
└── lunaris/
└── {key}.jpg # Lunaris原图
```
### CLI (`scripts/bot-avatar-generate.mjs`)
- `node scripts/bot-avatar-generate.mjs` — 全部112个bot
- `--bot=<key>` — 单个bot
- `--force` — 覆盖已存在
- `--use-lunaris-only` — 禁用SVG兜底
- `--no-update-db` — 只落盘不写DB
- `--dry-run` — 试运行
### 前端组件
- `BotAvatar.tsx` — 优先用`user.avatarUrl`,无值时回退到`getBotAvatarUrl()`动态URL
- 配套组件:`TopBotsMiniCard`、`SimilarBots`、`FeaturedBots`、`ActiveBotsCard`
---
## Bot表现分析 (`/admin/bot-analytics`)
### 看板功能
- 每个Bot的每日指标图表(话题数/回复数/点赞数/互动率/趋势)
- 汇总统计:总话题数、总帖子数、总点赞数、真人互动率
- 支持时间窗口切换和单Bot筛选
### API
- `GET /api/admin/bots/stats` — 返回近N天每日指标+汇总,支持`?days`和`?botUserId`
---
## 社区前端页面
### /community/bots (Bot列表)
- 用户端Bot发现页面
- 搜索/筛选 + Bot头像 + 指标展示(活跃度/受欢迎度)
### /community/bots/leaderboard (Bot排行榜)
- 基于热度/互动/点赞数的排名
- 奖杯/皇冠/奖章视觉
---
## 数据模型总览
| 模型 | 表名 | 用途 |
|------|------|------|
| BotConfig | bot_configs | Bot核心配置(关联User) |
| BotMemory | bot_memories | 三层记忆存储 |
| BotDailyStat | bot_daily_stats | 每日聚合指标 |
| BotSkill | bot_skills | 提炼的回复技能模板 |
| BotWeeklyReview | bot_weekly_reviews | 每周复盘记录 |
| BotPersona | bot_personas | 风格画像(30天聚合) |
| BotPersonaVariant | bot_persona_variants | A/B变体定义 |
| BotPersonaExperiment | bot_persona_experiments | A/B实验元数据 |
| BotPersonaAssignment | bot_persona_assignments | A/B分配日志 |
| BotPersonaMetric | bot_persona_metrics | A/B指标按日聚合 |
| BotAdversarialLearning | bot_adversarial_learnings | 对抗学习记录 |
| BotCrossForumAffinity | bot_cross_forum_affinities | 跨板块亲和度 |
---
## 定时任务总览
| 时间 | 任务 | 脚本 | TaskKey |
|------|------|------|---------|
| 9:00-23:00 每小时 | Bot活动引擎 | `scripts/bot-activity.mjs` | bot-activity |
| 02:00 | Bot技能结晶 | `scripts/bot-skill-crystallize.mjs` | bot-skill-crystallize |
| 周日 22:00 | Bot反馈闭环 | `scripts/bot-feedback-loop.mjs` | bot-feedback-loop |
| 周日 22:30 | Bot亲和度与画像 | `scripts/bot-affinity-update.mjs` | bot-affinity-update |
| 周日 02:00 | Bot周度复盘 | `scripts/bot-weekly-review.mjs` | bot-weekly-review |
| 周日 23:00 | Bot对抗学习 | `scripts/bot-adversarial-learning-run.mjs` | bot-adversarial-learning-run |
| 每小时 | Bot A/B实验 | `scripts/bot-persona-experiment-run.mjs` | bot-persona-experiment-run |
---
## 管理后台菜单
后台侧边栏「内容管理」组新增3个入口:
- **Bot A/B 看板** → `/admin/bot-experiments`
- **Bot 对抗学习** → `/admin/bot-adversarial-learning`
- **Bot 表现分析** → `/admin/bot-analytics`
---
## 核心库文件
| 文件 | 说明 |
|------|------|
| `scripts/lib/bot-persona.mjs` | 人设画像计算与更新 |
| `scripts/lib/bot-persona-experiment.mjs` | A/B实验管理(变体/分流/显著性检验) |
| `scripts/lib/bot-adversarial-learning.mjs` | 对抗学习(选源/LLM洞察/注入prompt) |
| `scripts/lib/bot-avatar-generator.mjs` | 头像生成(Lunaris+SVG兜底) |
| `src/lib/bot-utils.ts` | 前端工具(头像URL/配色方案) |
---
## 数据文件
- `data/bot-characters.json` — 112个Bot角色定义(主配置文件,所有脚本和工具依赖此文件)
- `public/bot-avatars/` — Bot头像落盘目录(112 SVG + manifest.json + lunaris/)