轻小说 AI 总结启动

文档编号 11

11 - 轻小说 AI 总结启动

背景

轻小说查询详情页原本支持登录用户手动编辑 novel_ai_summary.intro。本次新增管理员/编辑权限下的 AI 启动入口:在“AI总结”标题区点击 AI 按钮,输入 wenku8 全本 TXT 下载链接后,由服务器下载 TXT、写入缓存、后台启动 opencode,并写入临时启动提示。


架构

novel-search.php  (详情页 AI 按钮 + dialog + EventSource)
       │
       ├─ GET /novel-ai-summary-api.php?action=token
       │       └─ Typecho 登录态 + editor 权限 + CSRF token
       │
       ├─ GET /novel-ai-summary-api.php?action=models
       │       └─ opencode models → 30 分钟 JSON 缓存 → 默认选中模型
       │
       └─ GET /novel-ai-summary-api.php?action=start&bookid=&url=&model=&_=
               │  SSE 进度:校验 → 下载 → 写缓存 → 检查启动环境 → 启动 CLI → 写提示
               │
               ├─ inc/wenku8-txt.php
               │       └─ wenku8 链接解析、TXT 下载、GBK/Big5/UTF-8 转换
               │
               ├─ usr/cache/ai_summary_cache/{小说名}.txt
               ├─ opencode run -m <model> "/novel-summary <txt> <bookid>"
               └─ novel_ai_summary.intro = "yyyy-mm-dd hh:mm:ss 启动AI总结"

新增 / 修改文件

文件说明
novel-ai-summary-api.php新增 AI 总结启动 API:token + models + SSE start
usr/themes/classic-22/inc/wenku8-txt.php新增 wenku8 TXT 解析/下载/转码共享模块
usr/themes/classic-22/novel-search.phpAI 总结标题区新增 AI 按钮和启动弹窗
epub-convert-api.php改用 inc/wenku8-txt.php,移除重复 TXT 下载逻辑

关键实现说明

权限与安全

  • AI 启动入口仅对 editor 及以上权限显示;后端再次硬校验,未登录返回 401,权限不足返回 403。
  • 前端先请求 action=token 获取 Typecho Widget\Security token;action=start 必须携带 _ 参数并通过 hash_equals() 校验。
  • 前端和后端都只接受 https://dl.wenku8.com/down.php?...&id=<bookid> 全本链接;分卷 packtxt.php 不允许用于 AI 总结。
  • 后端强制校验链接中的 id 与当前详情页传入的 bookid 一致,避免误启动其他小说。
  • 服务器命令使用 escapeshellarg() 分别转义 bin、model、prompt、log path;用户输入不会直接拼入 shell。

模型选择

  • 弹窗打开时请求 GET /novel-ai-summary-api.php?action=models,仅 editor 及以上权限可访问。
  • 后端复用 AI_SUMMARY_OPENCODE_BINAI_SUMMARY_OPENCODE_CWD 执行 opencode models,并将可用模型列表缓存到 AI_SUMMARY_CACHE_DIR/.opencode-models.json
  • 模型缓存 TTL 固定为 1800 秒;30 分钟内重复打开弹窗优先读取缓存,避免频繁执行 CLI。
  • 接口返回 modelsdefault_modelselected_model;若 AI_SUMMARY_OPENCODE_MODEL 存在于模型列表中,则 selected_model 为配置模型,否则为第一项。
  • 前端加载模型期间禁用模型下拉框和启动按钮;加载失败或列表为空时显示错误并保持启动按钮禁用。

下载与缓存

  • 缓存目录通过 AI_SUMMARY_CACHE_DIR 配置,建议指向 {TYPECHO_ROOT}/usr/cache/ai_summary_cache/;未配置时后端有默认值。
  • 文件名来自 novels.title,会移除 / \ : * ? " < > | 和控制字符,并限制长度;为空时降级为 bookid-<id>.txt
  • TXT 下载会校验实际读取字节数;随后先写 .tmp.<random>,成功后 rename() 到正式文件,避免 opencode 读取半文件。
  • TXT 下载上限复用共享模块默认 30 MB。

opencode 启动

  • AI_SUMMARY_OPENCODE_MODEL 作为默认模型配置:弹窗默认选中它;旧调用未传 model 时也会回退使用它。
  • 可选配置:AI_SUMMARY_OPENCODE_BIN(默认 opencode)、AI_SUMMARY_OPENCODE_CWDAI_SUMMARY_TASK_LOCK_TTL
  • 后端在 TXT 写入缓存后再检查 novel_ai_summary 表、opencode 配置和可执行文件,再启动后台命令,通过 & echo $! 获取后台 pid;接口不等待总结完成。
  • action=start 接受可选 model 参数;前端传入用户选择的模型,后端校验为 provider/model 形态后用于 opencode run -m
  • 启动 prompt 会同时传入 TXT 绝对路径和 bookid,供 novel-summary skill 最终写回 novel_ai_summary
  • 成功发起 CLI 后才写入临时 intro:Y-m-d H:i:s 启动AI总结

并发控制

  • 同一本书启动时会写 .ai-summary-<bookid>.lock
  • 默认 10 分钟内再次启动同一本书会被拒绝,避免重复任务互相覆盖。
  • 如果启动失败,lock 会立即删除;启动成功后 lock 保留到 TTL 过期。

配置示例

以下配置写入 config.inc.php,不要写入 Git:

define('AI_SUMMARY_OPENCODE_MODEL', 'your-model-name');
define('AI_SUMMARY_CACHE_DIR', __TYPECHO_ROOT_DIR__ . '/usr/cache/ai_summary_cache/');
define('AI_SUMMARY_OPENCODE_BIN', 'opencode');
// 可选:define('AI_SUMMARY_OPENCODE_CWD', __TYPECHO_ROOT_DIR__);
// 可选:define('AI_SUMMARY_TASK_LOCK_TTL', 600);

服务器需确保 Web 进程对缓存目录有写权限,并能执行 opencode


注意事项

  1. novel_ai_summary 表仍按 09 号文档维护,站点代码不自动建表。
  2. opencode 生成最终总结后会自行写库;本站只负责启动任务和写入启动提示。
  3. 如果前端显示“模型列表获取失败”或“模型列表为空”,需检查 Web 进程是否能执行 opencode models,以及 AI_SUMMARY_OPENCODE_CWD 下的 opencode 配置是否完整。
  4. 如果错误提示以“TXT 已缓存”开头,说明下载与落盘已成功,需检查提示中的后续配置、表结构或执行权限问题。