Files
MoviePilot/docs/refactor/agent-tool-refactor-plan.md

38 KiB
Raw Blame History

MoviePilot Agent 工具体系重构计划

状态:COMPLETE — L1-L10 全部 VERIFIED,正式方案已提交推送并通过远端 CI

建立日期:2026-08-31

当前基线:v3@91e33277f3a05dc10b8984a2e612d8a32441c987

关联目标:本线程已建立的 Agent 工具体系重构 Goal

1. 战略目标

在不削弱 MoviePilot Agent 智能化、权限安全、会话交互、后台任务和失败恢复能力的前提下, 重构当前过大的 Agent 工具目录:

  • 用受控、结构化的 MoviePilot API 网关承载稳定业务能力
  • 用按领域 Skill 提供调用流程、参数语义和 API 操作范围
  • 用受控 Skill 脚本直接承载下载器、媒体服务器的低层第三方 API 操作
  • 迁移可以由 REST API 完整表达的业务工具,删除重复的 Agent Tool Schema
  • 补齐搜索会话、规则校验、安全设置和插件数据等 API parity
  • 保留依赖当前会话、渠道、宿主或流式状态的原生能力
  • 保持原有权限、确认、幂等、审计、结果裁剪和可恢复语义

最终目标不是建立一个可执行任意 URL 的万能 curl 工具,而是建立一个由操作白名单、身份透传、 策略分类和结果投影共同约束的 API 能力面。

下载器和媒体服务器采用混合边界:设备级查询、诊断和任务控制可以由 Skill 的受控脚本 读取本机配置后调用第三方自身 API;涉及 MoviePilot 媒体身份、路径映射、下载历史、订阅、 转移和跨服务器聚合的高层用例继续由 MoviePilot Application/API 承担。

2. 基线与当前实现事实

项目 初始基线 当前实现
工厂固定工具元组 82 个 12 个原生工具;默认再追加 send_local_file 与 moviepilot_api,共 14 个
工具目录默认行为 LLM_MAX_TOOLS=0,默认将完整目录绑定到主模型 默认完整目录已收敛为 14 个;按钮选择和语音发送仅在渠道能力满足时条件注入
默认固定目录体积 工具名称、描述和 JSON Schema 合计约 113 KB 实测 23,865 bytes,约下降 79%
API Skill skills/moviepilot-api/SKILL.md 通过任意 method/path 调用 moviepilot_api 只接受 203 个固定 operation ID;Skill 与 MCP oneOf 逐 operation 暴露精确英文参数合同
API 身份 API Token 映射为管理员级集成身份,不等于 Agent 当前用户/渠道身份 Web/渠道身份绑定到真实用户;HTTP/MCP 管理入口绑定持久化超级用户,不接受模型注入 Token
下载器/媒体服务器 依赖内置低层 Agent 工具或有限 REST operation downloader-operation 与 mediaserver-operation Skill 通过固定脚本调用已配置 provider API
MCP/HTTP 工具管理 存在旧业务工具与同名 first-wins 选择空间 与主 Agent 共用严格唯一新目录;重名直接以 TOOL_IDENTITY_AMBIGUOUS 失败
退役代码 旧实现仍位于 app/agent/tools/impl 77 个退役文件已直接删除,其中 72 个工具模块、5 个辅助模块
架构图 982 个宿主模块、8,430 条内部依赖边 919 个宿主模块、7,690 条内部依赖边,Application/Chain 具体 Adapter 直连仍为 0
工作区状态 基线提交 871632af,与 origin/v3 对齐,初始工作区干净 L1-L10 已完成并通过远端 CI;2026-09-01 继续修复集合总数在大结果截断预览中不可见的问题

3. 目标工具分层

3.1 第一批迁移:现有 API 基本覆盖的 48 个工具

领域 工具
媒体 search_media, search_person, search_person_credits, recognize_media, scrape_metadata, query_episode_schedule, query_media_detail
订阅 add_subscribe, update_subscribe, search_subscribe, query_subscribes, query_subscribe_shares, query_popular_subscribes, query_subscribe_history, delete_subscribe
下载/历史 add_download_tasks, query_download_tasks, delete_download_tasks, delete_download_history, delete_transfer_history, query_downloaders
站点 query_sites, update_site, query_site_userdata, test_site, update_site_cookie
推荐/媒体库/存储 get_recommendations, query_library_exists, query_library_latest, query_directory_settings, list_directory, query_transfer_history, transfer_file
调度/工作流 query_schedulers, run_scheduler, query_workflows, run_workflow
插件 query_installed_plugins, query_market_plugins, query_plugin_capabilities, query_plugin_config, update_plugin_config, reload_plugin, install_plugin, uninstall_plugin
命令/配置 list_slash_commands, query_custom_identifiers, update_custom_identifiers

3.2 第二批迁移:补齐 API parity 后的 16 个工具

  • 搜索会话:search_torrents, get_search_results
  • 下载器高级修改:update_download_tasks
  • 过滤规则和规则组:9 个查询、增加、更新、删除工具
  • 插件持久化数据:query_plugin_data
  • 安全系统设置:query_system_settings, update_system_settings
  • 斜杠命令执行:run_slash_command

3.3 原生能力最终保留

  • 外部能力:search_web, recognize_captcha, browse_webpage
  • 当前渠道消息:send_message, send_local_file;ask_user_choice 与 send_voice_message 按渠道能力条件注入
  • Agent 会话任务:agent_task 单工具 action=create/list/update/run/delete
  • Agent 人格状态:persona 单工具 action=list/switch/update
  • 宿主能力:execute_command, edit_file, apply_patch, write_file, read_file
  • 诊断能力:query_doctor_report

此外,渠道工具、Skill 工具、统一记忆(含活动记录)、子 Agent 和插件/MCP 动态工具继续按运行时条件注入;活动记录不再维护独立的活动日志中间件。

3.4 最终目录规模

  • 工厂固定工具元组由 82 个降为 12 个原生工具。
  • 默认运行目录再追加 send_local_file 与单一 moviepilot_api 网关,共 14 个固定工具。
  • 其中 13 个是原生能力,1 个是 203-operation 结构化 API 网关。
  • ask_user_choice、send_voice_message、Skill、渠道、子 Agent、插件和外部 MCP 工具仍按运行时条件注入,不计入 14 个默认固定工具。
  • 77 个退役实现/辅助文件直接删除,不保留 Agent 或 MCP 兼容副本。

3.5 下载器与媒体服务器第三方 API Skill

评估结论:可行,纳入正式目标,但采用“低层直连、高层保留”的边界。

能力 最终承载方式 原因
下载器实例发现、连接诊断、任务列表和详情 downloader-operation Skill + 受控脚本 属于 qBittorrent、Transmission、rTorrent 自身能力,适合按 provider 扩展
下载器暂停、恢复、限速、标签、分类、Tracker 和删除 downloader-operation Skill + 受控脚本 参数差异可在脚本适配层显式归一;删除文件仍需明确确认
媒体服务器实例、用户、媒体库、条目、最近入库和扫描诊断 mediaserver-operation Skill + 受控脚本 属于 Emby、Jellyfin、Plex 等服务自身查询面
媒体库刷新/扫描 mediaserver-operation Skill + 受控脚本 允许按 provider 能力执行,属于外部副作用并要求确认
下载提交、种子与站点 Cookie、保存目录选择 MoviePilot API 必须保留站点、下载选择、路径映射、历史和失败恢复语义
library.exists 精确去重 MoviePilot API 必须保留 canonical media_source/media_id、音乐匹配和多服务器聚合
下载/转移历史、订阅状态、自动转移 MoviePilot API 属于 MoviePilot 持久化与业务编排,不是第三方设备 API

Skill 脚本必须满足:

  • 从 MoviePilot 本机配置读取连接参数,模型参数中不得出现服务器 URL、用户名、密码或 Token
  • 只允许脚本声明的 provider、实例名和 action,禁止任意 URL、任意 HTTP method 和任意请求头
  • 优先复用现有 provider client/SDK 和配置解析,不复制 MoviePilot 高层 Chain 业务规则
  • 输出统一 JSON、默认分页和字段投影,错误中不得回显凭据
  • 只在新 Skill 和离线测试就绪后,从 moviepilot-api operation 目录移除重复低层操作
  • 不删除供前端和其它宿主调用的 REST 端点;删除的是 Agent operation 暴露和旧 Agent Tool 代码

能力面不局限于替换现有 operation。每个脚本都提供 capabilities 动作,按当前已配置实例和 provider 动态返回 namespaced action、参数约束、副作用等级及是否支持批量调用;Agent 只在加载 对应 Skill 后看到这部分领域知识。首批扩展目标包括:

  • 下载器:任务属性/文件/Tracker/Peer、队列与优先级、暂停/恢复、重校验、重新汇报、 限速、分类、标签、保存位置、顺序下载、会话统计、剩余空间和批量任务控制
  • 媒体服务器:服务器/用户/媒体库/条目查询、最近入库、搜索与详情、播放会话、观看状态、 媒体库扫描、单项元数据刷新,以及 provider 已稳定实现的合集/播放列表等扩展能力
  • provider 专属能力通过扩展注册表增加;禁止退化为任意对象方法调用、任意 URL 或任意 HTTP 请求

4. 阶段、叶子和依赖

执行规则:任何时刻只允许一个叶子处于 ACTIVE;每个叶子必须可独立验证、交付和回滚。

阶段/叶子 状态 依赖 退出条件
L1 基线与契约设计 VERIFIED 无 工具替代矩阵、调用路径、权限事实、输出合同、回归指标和排除项冻结
L2 受控 API 网关 VERIFIED L1 moviepilot_api 结构化工具、固定 API 路由、身份/策略透传、结果裁剪和统一新目录完成;不设置运行时切换开关
L3 API parity VERIFIED L2 搜索会话、下载高级修改、过滤规则、插件数据、系统设置和斜杠命令均有 typed API 与离线测试
L4 第一批迁移 VERIFIED L2 第一批工具从 Agent、MCP 和 HTTP 工具管理目录移除,能力统一由 API 网关提供
L5 第二批迁移 VERIFIED L3 parity 工具迁移完成,确认、脱敏、幂等和恢复语义进入统一策略
L6 原生工具收敛 VERIFIED L4,L5 Agent 任务和人格工具合并为 action 工具;宿主/会话/渠道原生能力边界固定
L7 第三方服务 Skill 化 VERIFIED L5 下载器和媒体服务器能力发现、受控脚本、Skill、策略和离线测试完成;重复低层 Agent API operation 已删除
L8 收口与交付 VERIFIED L6,L7 旧代码、文档与架构基线已收口;静态检查和锁定全量测试已完成
L9 全 API 面审计与最终交付 VERIFIED L8 375 个 OpenAPI 操作逐路由归属、203 个网关合同与 72 个退出工具映射均由测试锁定;全量测试、80% 覆盖率门禁、提交推送和远端 CI 终态均已完成
L10 查询 API 显式分页与数据库下推 VERIFIED L9 列表端点显式声明分页输入;数据库筛选在 LIMIT/OFFSET 前完成并使用同条件精确 COUNT;省略新增参数保持旧返回语义;响应 data 列表结构不变;外部原生分页未提供总数时不伪造;OpenAPI、MCP、Skill、审计、80% 覆盖率和远端 CI 同步完成

5. L2 受控 API 网关约束

5.1 模型可见合同

模型只看到一个结构化工具:moviepilot_api(operation_id, path_params, query, body)。

operation_id 来自审核过的操作注册表,模型不能传入任意 host、URL、认证头或 API Token。

5.2 身份和权限

  • 从当前 Agent 的 ToolPolicyContext 继承用户、会话、渠道、来源、后台/子 Agent 身份
  • API 网关不能把全局 API_TOKEN 当作当前用户身份
  • 策略按 operation_id 加参数分类,而不是只按工具名 moviepilot_api 分类
  • 读取、敏感读取、可恢复写入、破坏性写入和外部副作用分别处理
  • 保持现有确认、管理员门禁、敏感值脱敏、幂等和恢复模式

5.3 Skill 合同

Skill 负责判断何时使用某个领域能力、提供业务流程和参数示例、声明允许访问的 allowed-api-operations。宿主必须实际执行该操作范围;只在提示词里显示 allowed-tools 不算权限控制。

5.4 输出和失败合同

  • 统一 JSON envelope、分页、字段投影和长度上限
  • 结果进入模型前做敏感字段裁剪和错误归一化
  • 写操作带幂等键或版本前置条件
  • 超时和取消后明确外部状态未知,不能直接报告为已停止
  • API 网关不得复制业务规则;业务 owner 仍是现有 Application/Chain/API 能力

第三方服务 Skill 的脚本不属于 API 网关,也不得成为任意网络出口。脚本只消费审核过的 action,并使用 MoviePilot 已配置的具体服务实例访问其自身 API。

6. 验收指标

功能

  • 媒体搜索、识别、订阅、下载、站点管理、媒体库、插件和工作流任务成功率不低于基线
  • 搜索结果短引用、分页、筛选项和下载任务关联不回退
  • API 网关和 provider Skill 的替代路径保持原业务结果、错误、确认与恢复语义

安全

  • 普通用户、频道管理员、系统管理员、后台任务和子 Agent 权限边界保持一致
  • 未经确认不能执行敏感读取、删除、覆盖、安装和外部副作用
  • API Token、Cookie、密码和插件私有数据不进入 prompt、日志或错误摘要
  • 不允许模型通过参数绕过 operation allowlist、host 限制或身份绑定
  • 第三方服务 Skill 不接受连接地址或凭据参数,删除文件和媒体库扫描按外部副作用处理

运行质量

  • 主模型工具 Schema 体积显著下降
  • 工具选择误选率、平均调用次数、上下文 token、端到端延迟不劣于基线,优先取得改善
  • API 网关失败可观测、可重试、可对账;取消/超时场景可恢复
  • 所有测试零真实外部网络;相关测试使用边界 mock 或录制回放

7. 明确排除项

  • 不把 execute_command 当作 MoviePilot API 网关;仅允许领域 Skill 调用仓库内受控、可审计的固定脚本
  • 不暴露任意 REST path、任意 HTTP method 或任意外部 host
  • 不把浏览器、验证码、文件编辑、终端命令和渠道回调强行 API 化
  • MCP、HTTP 工具管理器和主 Agent 全部使用新 API 工具目录,不保留旧业务工具兼容入口
  • 不修改 app/plugins/** 运行时副本
  • 旧工具不作为回滚机制;回滚依赖 Git 版本和提交边界,正式运行时不保留兼容实现
  • 不用第三方直连替代下载提交、订阅、转移、历史和 canonical 媒体存在性等 MoviePilot 高层用例

8. 阶段更新日志

2026-08-31:建立计划

  • 创建 Codex Goal,目标为 Agent 工具体系的 API 化重构和安全收口
  • 基线同步到 v3@6ee47795629f
  • 确认 82 个固定工具、48 个第一批迁移候选、16 个 parity 候选和 18 个原生保留工具
  • 当前活动叶子为 L1;尚未修改生产代码

2026-08-31:L1 基线验证与 L2 启动

  • 已核对最新 v3 基线、工具工厂、Agent 编排、Skill 中间件、MCP 入口、API Token 鉴权和策略注册表
  • 已确认第一批 48 个 API 候选、第二批 16 个 parity 候选和原生保留边界;后续实测更正默认固定目录终态为 14 个
  • 已冻结 API 网关必须透传 ToolPolicyContext、禁止任意 URL、保留确认/幂等/恢复和结果裁剪的设计约束
  • 当前唯一活动叶子切换为 L2;下一步直接实现正式结构化 API 网关,不增加运行时切换

2026-08-31:L2.1 网关骨架完成

  • 新增 app/agent/policy/api.py,冻结第一批 48 个和 parity 16 个 operation ID、固定 API 路由与策略元数据
  • 新增 app/agent/tools/impl/api.py,提供 moviepilot_api(operation_id, path_params, query, body) 结构化合同
  • 工具工厂已切换为统一新目录:默认移除替代业务工具并注入单一网关,不新增运行时开关
  • 主 Agent、子 Agent、MCP 和 HTTP 工具管理器均走同一新目录;不保留旧业务工具对外或内部兼容入口
  • 策略注册表已按 operation ID 分类未知、敏感、可恢复写入、破坏性写入和外部副作用
  • 网关已从旧工具 owner 转发改为 operation ID 到固定 HTTP 方法/路径的 API 执行器,模型不能注入 URL、认证头或方法
  • 已删除临时 app/agent/tools/legacy.py,并移除 MCP 同名工具 first-wins 兼容选择;重复名称由严格目录冲突机制处理
  • Skill operation 范围改为默认拒绝:未加载声明 allowed-api-operations 的 Skill 时不能调用 moviepilot_api
  • 敏感系统设置确认入口已迁移到 config.system.get operation,策略回执对 SECRET 结果整体脱敏
  • 当前未完成:补齐 parity API 的真实业务端点、完成渠道用户到 API 用户身份映射、删除 64 个退出工具文件并迁移关联测试/Skill
  • 验证:网关、Skill、策略、数据端口与 MCP 专项 63 passed;新增代码 compileall 通过

2026-08-31:最终目录决策确认

  • 不增加 AGENT_API_GATEWAY_ENABLED、AGENT_COMPACT_TOOL_CATALOG_ENABLED 或其它运行时切换
  • 不考虑 MCP 旧工具兼容;MCP、HTTP 工具管理器和 Agent 统一采用新 API 目录
  • 旧业务工具代码的删除是本目标的必达项:先将行为迁移到 API operation handler,再删除对应 app/agent/tools/impl/* 文件、注册、测试和文档
  • 网关不得实例化旧工具类;临时 legacy owner 文件已经删除,后续不再恢复

2026-08-31:L2 完成并进入 L3

  • Web/WebAgent 数字用户 ID 才允许直接作为 API 身份;外部消息渠道即使用户 ID 为数字,也必须通过渠道绑定解析为有效 MoviePilot 用户
  • API 执行器使用宿主签发的当前用户令牌,并通过模型不可控请求头透传会话、渠道和来源;不读取全局 API_TOKEN
  • 系统设置元数据与脱敏、过滤规则校验与引用处理、插件管理投影已迁出 app/agent/tools/impl,进入对应 Application owner
  • 新增 typed API:统一系统设置、自定义识别词、下载任务高级修改、过滤规则/规则组 CRUD、插件运行能力/持久化数据、斜杠命令执行
  • parity operation 已切换到真实固定路由,不再使用 system/ruletest、插件页面或环境变量接口作为占位映射
  • L2 状态更新为 VERIFIED,当前唯一活动叶子为 L3;下一步补齐推荐、目录配置等剩余语义差异并开始删除 64 个退出工具文件
  • 验证:uv run --locked --no-sync python -m compileall -q app/agent app/application app/api/endpoints app/schemas 通过;Agent 网关/Skill/策略/数据端口/MCP 专项 63 passed

2026-08-31:纳入下载器与媒体服务器第三方 API Skill

  • 对照 database-operation Skill、下载器 provider client、媒体服务器 provider client 和现有 API operation 完成边界评估
  • 决定新增 downloader-operation 与 mediaserver-operation 两个领域 Skill,通过固定脚本读取本机配置并调用已配置第三方服务自身 API
  • 两个 Skill 不仅替代既有低层 operation,还提供动态 capabilities 清单和 provider 扩展注册表,使 Agent 可按需使用任务文件/Tracker/Peer/队列、播放会话、观看状态、扫描和元数据刷新等高级能力
  • 计划在 Skill 通过离线测试后移除 download.list、download.update、download.delete、downloaders.list 和 library.latest 等重复低层 Agent operation
  • 保留 download.add、library.exists、下载/转移历史和订阅等 MoviePilot 高层 operation,避免削弱媒体身份、路径映射、持久化和恢复能力
  • 不保留旧工具或 MCP 兼容入口;前端仍需使用的 REST 端点不在删除范围
  • 当前活动叶子仍为 L6 原生工具收敛;L7 已加入 Goal,待 action 工具收口后实施

2026-08-31:L3 至 L6 完成

  • 完成 59 个正式 operation:第一批 44 个、parity 15 个;模型不能注入 URL、method、认证头或 Token
  • 将过滤规则、系统设置、插件管理/数据、音乐投影、下载任务和命令执行迁入对应 Application owner
  • agent_task 合并 create/list/update/run/delete,persona 合并 list/switch/update;旧独立工具实现全部删除
  • 主 Agent、子 Agent、CLI、MCP 和 HTTP 工具管理器统一消费同一严格目录,不再注册旧工具名
  • 删除 77 个 app/agent/tools/impl 退役文件,其中 72 个工具模块和 5 个辅助模块
  • 阶段验证:网关、策略、原生 action、流式活动和关联回归 218 passed

2026-08-31:L7 第三方服务 Skill 化完成

  • 新增 downloader-operation Skill 与 mp-downloader.py 固定脚本,覆盖 qBittorrent、Transmission、rTorrent 的实例、能力发现、任务/文件/Tracker/Peer、队列、限速、标签、分类、位置和会话统计等动作
  • 新增 mediaserver-operation Skill 与 mp-mediaserver.py 固定脚本,覆盖 Emby、Jellyfin、Plex、ZSpace、Ugreen、TrimeMedia、Navidrome 的实例、媒体库、条目、活动、播放会话、扫描和元数据刷新等动作
  • 两个脚本只读取 MoviePilot 已配置实例,拒绝任意 URL/method/header/凭据参数,统一递归裁剪密码、Token、Cookie、API Key 和会话字段
  • 从 Agent operation 目录删除 download.list、download.update、download.delete、downloaders.list 和 library.latest;保留前端 REST 端点与 MoviePilot 高层 download.add、library.exists、订阅和历史能力
  • 阶段验证:provider Skill、网关移除项和内置 Skill 边界 33 passed

2026-08-31:L8 收口完成

  • CLI scheduler list/run 已改用 scheduler.list 与 scheduler.run operation,不再调用已删除工具名
  • 工具目录构造、Agent 图、HTTP/MCP direct 调用均强制唯一身份;同名插件/MCP 工具直接失败,不采用 first-wins
  • docs/mcp-api.md、docs/cli.md、命令规则、内置 Skill 文档和 Schema 导出清单已同步新合同
  • 宿主架构基线在 L8 首次更新为 917 个模块、7,671 条内部依赖边;L9 新增固定 API MCP 合同与外部服务工具 owner 后为 919 个模块、7,680 条内部依赖边,未新增 Application/Chain 到具体 Adapter 的直连
  • 受影响 Python 文件通过 Ruff、compileall 和 Pylint --errors-only;Schema 导出、架构基线与 git diff --check 均通过
  • 最终架构/OpenAPI/i18n/事件/package-root 回归 84 passed;Agent/MCP/CLI/Skill/架构大回归 1066 passed、插件与架构边界专项 124 passed、provider/gateway 专项 33 passed
  • 完整锁定测试 uv run --locked --no-sync python tests/run.py 完成:7554 passed、9 skipped、2 failed
  • 两个失败仅为 test_release_supply_chain.py::test_release_promotes_latest_only_after_both_versioned_images 与 test_resource_v3.py::test_v3_release_workflows_use_main_wiki_and_isolated_images;均已在未改动的 v3@6ee47795629f 干净 detached worktree 复现,属于本次重构前已有的发布工作流基线问题
  • Agent 工具重构范围内没有遗留失败;L1-L8 全部退出,正式方案不保留旧工具、MCP 兼容或运行时切换开关
  • 当前变更尚未提交或推送

2026-08-31:Skill/MCP 自描述合同与系统设置收口

  • 下载器 24 个 provider action 加 instances.list / capabilities.list 两个发现 action、媒体服务器 17 个 provider action 加同样两个发现 action、MoviePilot API 203 个 operation 均从运行时合同生成完整英文参数说明;外部 MCP tools/list 保留相同 oneOf、必填、类型、默认值、枚举和跨字段约束
  • 新增 admin-only database_operation MCP 工具,暴露 tables、schema、query、write 四个 action;内置 Agent 仍按需加载 database-operation Skill,不增加常驻业务工具
  • 数据库 Skill 已覆盖 27 张 ORM 表、alembic_version 和全部字段基线;每张表均说明用途、适合查询的场景和写入边界,运行时仍以 tables / schema 实际结果为准
  • Skill 文档生成器会对数据库说明与 ORM 元数据做双向缺项校验;新增、删除或改名表但未同步说明时生成直接失败,避免文档再次出现漏表或只有字段没有语义的问题
  • 系统设置继续由 config.system.get/update 统一承载,不恢复旧配置工具;Skill 不再复制易漂移的 Settings / SystemConfigKey 全量清单,而是要求先动态发现 definition(声明类型、当前形状、敏感性、允许操作、列表匹配字段和持久化位置),再按精确键更新
  • Settings 更新执行类型转换并持久化到 app.env;SystemConfigKey 通过配置服务写入数据库,保留插件 mutation 门禁、敏感值脱敏和配置变更事件
  • app/agent/policy/resources/api_mcp_schema.json 明确为 OpenAPI + 固定 operation 合同生成的 MCP tools/list 制品,禁止手工维护;所有 operation、模型和字段必须具备具体英文说明,抽象占位文本或中文说明会由测试拒绝
  • API 权限保持双层硬门禁:moviepilot_api 按 operation 的 required_role 在发起 HTTP 前拒绝非管理员,最终 FastAPI 端点继续用真实当前用户令牌执行 superuser/manage/user 级鉴权;下载器、媒体服务器、数据库外部工具整体为 admin-only
  • 异步边界复核:内置 Skill 脚本经 asyncio subprocess 执行;外部服务/数据库 MCP 工具把同步脚本放入 downloader、mediaserver、db 分域线程池;moviepilot_api 使用异步 HTTP,不阻塞 Agent event loop
  • 当前阶段进入最终验证:聚焦 Agent/Skill/MCP 回归、全量锁定测试、80% 固定覆盖率 CI、提交推送与远端终态确认

2026-08-31 至 2026-09-01:L9 全 API 面审计与真实调用验证

  • 从当前 FastAPI OpenAPI 生成 375 个 HTTP 操作的完整清单;每个路由明确归类为 gateway、provider-skill、consolidated、alternate-auth-duplicate、transport_or_identity、stream_or_binary 或 ui_presentation,生成器对任何未归类端点直接失败
  • moviepilot_api 扩展为 203 个稳定 operation,除 10 个音乐识别、探索、专辑、艺术家和缓存操作外,继续覆盖仪表盘、媒体发现与识别、站点全生命周期、订阅协作、存储维护、整理队列、工作流、种子缓存、数据库备份、规则与网络测试、插件运行管理、当前用户公开配置和使用统计;音乐支持艺术家到作品、作品到艺术家的双向浏览
  • 新增 tests/test_agent_api_surface_audit.py,逐项校验 OpenAPI 清单、固定路由、生成审计文档、MCP oneOf、英文字段说明和 Skill operation 章节完全一致
  • 将历史删除提交中的 72 个业务工具冻结为替代映射测试,逐项证明其 owner 是 203-operation API、下载器/媒体服务器 action 或统一 agent_task / persona 原生工具,并确认旧模块物理文件不存在
  • 实际执行数据库脚本 tables / schema / SELECT 1、下载器与媒体服务器 instances / capabilities,并直接运行三个结构化 service tool;本机未配置 provider 实例时返回空实例而不是配置读取错误
  • 通过临时本地 HTTP 服务实际执行 MoviePilotApiTool -> MoviePilotApiExecutor -> GET /api/v1/site/agent 完整链路,验证返回成功且普通用户投影不包含 cookie、API key、token 或 RSS 等认证字段
  • 新增内置 Agent 私有 read_skill 工具,绕过普通工具 64 KiB 结果裁剪并单独限制 SKILL.md 主体为 512 KiB;工具同时返回全部辅助文件相对路径,超过上限时明确标记截断,真实内置 Skill 加载测试确认 203 个 operation 均可见且 truncated=false
  • Skill 生成器同步 YAML allowed-api-operations 与正文目录,避免“文档有参数但运行时未授权”的双事实源漂移;MCP schema、Skill front matter、正文和注册表数量及集合完全一致
  • 为站点优先级、插件目录和工作流路径 ID 等原先不精确的输入补充类型模型或端点约束;固定路由占位符与 path schema 名称、required 状态由测试逐项校验
  • 受影响 Agent/Skill/MCP/OpenAPI/音乐/架构回归 247 passed;修复全量发现的工作流管理员门禁、插件分页默认值、模块命名治理、服务工具标签和 Schema 导出清单后,专项回归 40 passed
  • 完整锁定测试最终通过:7,614 passed、9 skipped;覆盖率按 CI 相同的 8 分片采集并合并,Application 81.88%、Domain 81.01%,通过固定 80% 门禁
  • 首次推送 6d4d7331d 的远端 Pylint 成功;Architecture Contract Gate 在 complexity v2 发现 plugin.py 从 999 行增长到 1007 行,合并三个短 import 后恢复到 999 行,本地两套复杂度门禁及 49 项相关回归均通过
  • 第二次推送 28298c485 的远端 Pylint 成功;Architecture Contract Gate 识别出两处 Ruff 导入排序债务下降尚未固化,以及新增 service tool/Agent 投影端点未显式处理 mypy 既有基础设施告警
  • 已对新增 Pydantic/ResponseAPIRouter 边界使用精确错误码抑制,补齐 service tool 的 ClassVar 与可选 selector 类型;未通过覆盖基线隐藏任何新增 mypy 错误
  • Ruff 低水位由 576 降至 574,mypy 低水位由 9,606 降至 9,605;mypy、复杂度 v1/v2、并发、异步阻塞、TaskRegistry owner、服务定位和启动性能全部本地通过
  • API/Skill/MCP/provider 专项 86 passed,架构与质量门禁专项 127 passed;生成器重建后工作区合同无额外差异
  • 第三次推送 e5722ed98 的远端 Pylint 成功;Unit Tests 的功能分片与覆盖率分片均通过,但 Architecture Contract Gate 在文档合同测试中发现优化清单仍写着旧的 9,606 / 576 量化指标
  • 已将 docs/refactor/optimization-checklist.md 同步为 9,605 / 574,本地 test_architecture_contract_baseline.py 与 test_architecture_baseline_cli.py 共 45 项通过,并确认 baseline.py --check-host 成功
  • 当前活动叶子保持 L9;下一步提交文档指标修复,等待所有远端工作流终态成功后更新为 COMPLETE

2026-08-31:最终提交与远端门禁完成

  • 文档合同修复提交 3106984dfc586aa171bbe9bd52307d19c6ef14f3 已推送到 origin/v3,本地 HEAD 与远端一致,工作区无未提交变更
  • Unit Tests 工作流 33421990027 已终态成功:Architecture Contract Gate、4 个功能分片、8 个覆盖率分片和 Coverage Report 全部通过
  • Pylint 工作流 33421989865 已终态成功;架构质量门禁、Ruff/mypy ratchet、复杂度、异步阻塞、Task owner、服务定位和启动性能检查均成功
  • 全量 Agent API 面审计、Skill/MCP 参数合同、下载器/媒体服务器/数据库真实只读调用、音乐双向浏览能力和固定 80% 覆盖率要求均已完成
  • 本次重构正式完成:旧 Agent 工具代码、旧 MCP 兼容路径和运行时切换开关均不保留;后续新增 API 必须同步注册表、Skill、MCP schema、审计清单和测试

2026-09-01:L10 查询 API 兼容分页与总数合同

  • 重新开启父目标并进入 L10;当前基线 91e33277f 与 origin/v3 对齐,初始工作区干净
  • 查询 API 按结果语义分为完整列表、原生分页或限量列表、结构化分页对象、统计或聚合对象;只有列表结果进入统一数量合同,统计、映射和时序对象不为形式统一而错误分页
  • 原完整列表新增的 page / count 必须都是可选参数;两者都省略时不切片,继续返回端点原先的完整列表。显式传入任一参数才启用分页,缺失的 page 按 1、缺失的 count 按 50 解释
  • REST 响应继续保持 Response.data 为原列表,禁止改成 {items,total} 等对象;总数和分页信息使用响应头及 Agent 网关附加元数据表达,避免破坏外部插件和既有客户端
  • 对完整列表可报告切片前精确总数;对已经由第三方来源原生分页或限量、且上游没有提供总数的结果,只报告当前返回数量,不强制增加总数,也禁止把当前页数量伪装成全局总数
  • 第一版由统一响应路由隐式注入分页参数并在序列化后切片;复核后确认这种实现虽然能生成查询参数,但端点签名不自描述,而且数据库列表仍会全量读取,因此不作为最终方案
  • 当前正式方案由每个列表端点显式声明 page / count;框架 Response 仅用于写响应头,不会出现在 OpenAPI、MCP 或 Skill 输入中。已有第三方原生 page、count、limit 或 max_results 的接口保留其显式参数语义;当原生限量参数默认值为 None 时,省略所有窗口参数继续返回完整列表
  • 用户、PassKey、活动订阅、Workflow、Site、站点用户数据和站点统计的筛选、稳定排序、LIMIT/OFFSET 与精确 COUNT 已下推到异步 SQLAlchemy 查询;状态、用户名、名称、触发类型、站点启用状态、站点 ID/域名和日期筛选均在分页前执行,避免空页和错误总数
  • 纯内存、配置、缓存、文件系统或运行时目录仍可在响应边界按显式 page/count 切片;第三方原生分页且不返回总数的接口只报告当前页数量,不伪造总数
  • REST 保持 data 原列表;X-Result-Count 报告本次返回数量,精确可知时增加 X-Total-Count,Agent 网关把这些响应头映射到附加 collection 对象。下载历史、订阅历史和插件目录已增加本地精确计数;外部媒体、音乐、推荐和搜索来源未提供总数时不输出 total_count
  • OpenAPI 与 api_mcp_schema.json 已重新生成,英文 skills/moviepilot-api/SKILL.md 已同步显式参数;生成器会识别插件目录的既有 max_results,不会错误声称其默认返回全量
  • 完整锁定测试已通过:4 个分片分别为 1620 passed, 3 skipped、1860 passed, 4 skipped、1917 passed、2248 passed, 2 skipped,合计 7645 passed, 9 skipped
  • 覆盖率按 CI 相同的 8 分片采集并合并,Application 81.72%、Domain 81.01%,通过固定 80% 门禁;Ruff/mypy ratchet、严格 mypy、复杂度 v1/v2、并发、异步阻塞、TaskRegistry owner、服务定位、事件策略和启动性能门禁均通过
  • 数据库 Oper 复用 literal_contains 新增 4 条内部依赖边,宿主快照从 7,684 更新为 7,688;没有新增 Application/Chain 到具体 Adapter 的直连或跨层依赖
  • 生成器幂等校验通过,api_mcp_schema.json 与 skills/moviepilot-api/SKILL.md 重建前后哈希一致;受影响 Python 文件 Pylint 10.00/10,关键回归 207 passed

2026-09-01:L10 提交推送与远端门禁完成

  • 实现提交 d512f528be351dabba0cf344c2e9d890bb93ea6f 已推送到 origin/v3,本地与远端提交一致
  • Unit Tests 工作流 33457912749 已终态成功:Architecture Contract Gate、4 个功能分片、8 个覆盖率分片和 Coverage Report 全部通过
  • Pylint 工作流 33457912735 已终态成功;变更文件质量检查与完整建议报告均成功
  • L10 正式完成:集合端点显式声明 page/count,数据库查询在 SQL 层完成筛选、稳定排序、分页与同条件精确计数;不传新增参数继续返回旧全量列表,外部来源没有总数时不伪造
  • MoviePilot Agent 工具体系重构父目标至此全部完成,后续查询端点必须继续维护 REST、OpenAPI、MCP、Skill 和数据库查询合同的一致性

2026-09-01:集合总数截断可见性修复

  • Web Agent 实际调用 subscription.list 时未传分页,完整订阅列表超过通用工具结果 64KB 上限;此前网关把 collection 追加在 data 之后,截断预览只保留开头条目,导致 Agent 看不到已经由 API 返回的精确 collection.total_count,继而错误回退到数据库统计
  • 网关现将 collection 放在 data 前面,使 X-Total-Count 投影的精确总数在大结果预览中仍优先可见;列表响应和 REST data 结构均未改变
  • 核心提示、MCP oneOf、moviepilot-api Skill 和文档统一要求:数量或摘要查询使用最小分页窗口;兼容分页传 page=1,count=1 并读取 collection.total_count,不得因为条目或工具预览截断而改查数据库
  • moviepilot-api Skill 版本提升到 24;新增超大订阅结果回归,验证截断预览中总数早于 data 出现
  • 完整锁定测试已通过:4 个分片分别为 1620 passed, 3 skipped、1860 passed, 4 skipped、1917 passed、2250 passed, 2 skipped,合计 7647 passed, 9 skipped;变更文件 Pylint 为 10.00/10,Ruff、生成物幂等与 git diff --check 均通过
  • 当前未托管后端使用的 API 身份与工作区 CLI 配置不一致,真实 CLI 调用被既有 API key 校验拒绝;本轮未擅自重启正在运行的服务,运行进程需在代码部署并重载后再做在线会话复验

2026-09-01:啰嗦模式 API 主要参数展示修复

  • 旧领域工具各自通过 get_tool_message() 展示目标和主要参数;收敛为 moviepilot_api 后,该方法只展示 operation_id,导致 Web Agent 与通知渠道在啰嗦模式下失去 path_params、query 和 body 的调用上下文
  • moviepilot_api 现按路径、查询、请求体顺序生成最多 320 字符的主要参数摘要;空参数桶不展示,工具执行与 API 输入结构保持不变
  • 参数摘要统一复用宿主递归脱敏器,设置凭据、密码、Cookie、Token 与 API key 等仅显示 ***,避免为了恢复可观察性而泄露敏感输入
  • 复用脱敏器新增 1 条 Agent 同域内部依赖边,宿主架构快照由 7,688 更新为 7,689;模块总数和跨层边界均未变化
  • 后续将 Skill、子 Agent 和活动记录等中间件统一接入运行时啰嗦模式判断,新增 app.agent.callback -> app.runtime.settings 依赖,宿主架构快照由 7,689 更新为 7,690;该方向符合 Agent 到 Runtime 的既定边界
  • 中间件 import 排序修复同时消除了 2 条 Ruff I001 历史诊断,低水位由 571 降至 569,并已同步质量指标文档
  • 新增 Web Agent 实际流式回调回归和设置凭据脱敏回归;扩展 Agent/API/流式专项 487 passed,完整锁定测试 4 个分片分别为 1620 passed, 3 skipped、1860 passed, 4 skipped、1917 passed、2253 passed, 2 skipped,合计 7650 passed, 9 skipped;Ruff、Pylint 10.00/10 和全量 mypy ratchet 均通过

本文件作为本次重构的持续记录,保留阶段状态、实际变更、验证结果、提交状态与已知基线边界。