fix: finalize agent architecture ratchets

This commit is contained in:
jxxghp
2026-09-01 01:46:22 +08:00
parent 28298c485c
commit e5722ed987
8 changed files with 32 additions and 34 deletions

View File

@@ -17,7 +17,7 @@ from app.agent.tools.tags import ToolTag
from app.runtime.settings import get_runtime_setting
class DownloaderOperationInput(BaseModel):
class DownloaderOperationInput(BaseModel): # type: ignore[misc]
"""下载器操作工具的运行时输入模型。"""
client: Optional[str] = Field(
@@ -35,7 +35,7 @@ class DownloaderOperationInput(BaseModel):
model_config = ConfigDict(extra="forbid")
class MediaServerOperationInput(BaseModel):
class MediaServerOperationInput(BaseModel): # type: ignore[misc]
"""媒体服务器操作工具的运行时输入模型。"""
server: Optional[str] = Field(
@@ -53,7 +53,7 @@ class MediaServerOperationInput(BaseModel):
model_config = ConfigDict(extra="forbid")
class DatabaseOperationInput(BaseModel):
class DatabaseOperationInput(BaseModel): # type: ignore[misc]
"""数据库操作工具的运行时输入模型。"""
action: str = Field(
@@ -300,7 +300,7 @@ def _parse_script_payload(stdout: str) -> dict[str, Any]:
def _run_service_script(
*,
relative_script: str,
selector_flag: str,
selector_flag: Optional[str],
selector_value: Optional[str],
action: str,
arguments: Dict[str, Any],
@@ -317,7 +317,7 @@ def _run_service_script(
"--arguments",
json.dumps(arguments, ensure_ascii=False, separators=(",", ":")),
]
if selector_value:
if selector_value and selector_flag:
command.extend([selector_flag, selector_value])
completed = subprocess.run(
command,
@@ -386,12 +386,12 @@ class DownloaderOperationTool(_ServiceOperationTool):
)
tags: list[str] = [ToolTag.Download]
args_schema: Type[BaseModel] = DownloaderOperationInput
_relative_script = "skills/downloader-operation/scripts/mp-downloader.py"
_selector_name = "client"
_selector_flag = "--client"
_blocking_bucket = "downloader"
_relative_script: ClassVar[str] = "skills/downloader-operation/scripts/mp-downloader.py"
_selector_name: ClassVar[Optional[str]] = "client"
_selector_flag: ClassVar[Optional[str]] = "--client"
_blocking_bucket: ClassVar[str] = "downloader"
async def run(
async def run( # type: ignore[override]
self,
action: str,
arguments: Optional[Dict[str, Any]] = None,
@@ -417,12 +417,12 @@ class MediaServerOperationTool(_ServiceOperationTool):
)
tags: list[str] = [ToolTag.Media]
args_schema: Type[BaseModel] = MediaServerOperationInput
_relative_script = "skills/mediaserver-operation/scripts/mp-mediaserver.py"
_selector_name = "server"
_selector_flag = "--server"
_blocking_bucket = "mediaserver"
_relative_script: ClassVar[str] = "skills/mediaserver-operation/scripts/mp-mediaserver.py"
_selector_name: ClassVar[Optional[str]] = "server"
_selector_flag: ClassVar[Optional[str]] = "--server"
_blocking_bucket: ClassVar[str] = "mediaserver"
async def run(
async def run( # type: ignore[override]
self,
action: str,
arguments: Optional[Dict[str, Any]] = None,
@@ -523,10 +523,10 @@ class DatabaseOperationTool(_ServiceOperationTool):
)
tags: list[str] = [ToolTag.Admin]
args_schema: Type[BaseModel] = DatabaseOperationInput
_relative_script = "skills/database-operation/scripts/mp-db.py"
_selector_name = None
_selector_flag = None
_blocking_bucket = "db"
_relative_script: ClassVar[str] = "skills/database-operation/scripts/mp-db.py"
_selector_name: ClassVar[Optional[str]] = None
_selector_flag: ClassVar[Optional[str]] = None
_blocking_bucket: ClassVar[str] = "db"
def get_mcp_input_schema(self) -> dict[str, Any]:
"""返回数据库 action 的完整 MCP JSON Schema。"""
@@ -539,7 +539,7 @@ class DatabaseOperationTool(_ServiceOperationTool):
title=self.name,
)
async def run(
async def run( # type: ignore[override]
self,
action: str,
arguments: Optional[Dict[str, Any]] = None,

View File

@@ -130,7 +130,7 @@ async def read_sites(
return await query.list_ordered()
@router.get(
@router.get( # type: ignore[misc]
"/agent",
summary="查询 Agent 可用站点",
response_model=List[_SchemaJsonObject],

View File

@@ -146,7 +146,7 @@ def _list_files(
return file_list or []
@router.post(
@router.post( # type: ignore[misc]
"/agent/list",
summary="查询 Agent 可用目录和文件",
response_model=List[_SchemaFileItem],

View File

@@ -43,7 +43,7 @@ async def list_workflows(
return await query.list()
@router.get(
@router.get( # type: ignore[misc]
"/agent",
summary="查询 Agent 可用工作流",
response_model=List[_SchemaJsonObject],

View File

@@ -60,7 +60,7 @@ class Site(BaseModel):
model_config = ConfigDict(from_attributes=True)
class SitePriorityUpdate(BaseModel):
class SitePriorityUpdate(BaseModel): # type: ignore[misc]
"""站点批量优先级更新项。"""
id: int = Field(..., description="Persistent site ID returned by site.list.")

View File

@@ -1,6 +1,6 @@
# MoviePilot Agent 工具体系重构计划
> 状态:IN PROGRESS — 本地验收已通过,提交推送与远端 CI 收口
> 状态:IN PROGRESS — 全量能力与本地门禁已通过,等待最终修复提交和远端 CI 终态
>
> 建立日期:2026-08-31
>
@@ -41,7 +41,7 @@
| MCP/HTTP 工具管理 | 存在旧业务工具与同名 first-wins 选择空间 | 与主 Agent 共用严格唯一新目录;重名直接以 TOOL_IDENTITY_AMBIGUOUS 失败 |
| 退役代码 | 旧实现仍位于 app/agent/tools/impl | 77 个退役文件已直接删除,其中 72 个工具模块、5 个辅助模块 |
| 架构图 | 982 个宿主模块、8,430 条内部依赖边 | 919 个宿主模块、7,680 条内部依赖边,Application/Chain 具体 Adapter 直连仍为 0 |
| 工作区状态 | 基线提交 871632af,与 origin/v3 对齐,初始工作区干净 | 生产改动、生成合同、静态检查、全量测试和固定 80% 覆盖率门禁已完成;提交、推送和远端 CI 尚待完成 |
| 工作区状态 | 基线提交 871632af,与 origin/v3 对齐,初始工作区干净 | 主体提交 `6d4d7331d` 与复杂度修复 `28298c485` 已推送;生产改动、生成合同、静态检查、全量测试和固定 80% 覆盖率门禁已完成,正在提交 Ruff/mypy 低水位收口并等待远端 CI 终态 |
## 3. 目标工具分层
@@ -314,6 +314,10 @@ action,并使用 MoviePilot 已配置的具体服务实例访问其自身 API
- 受影响 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 项相关回归均通过
- 当前活动叶子保持 L9;下一步完成提交前差异审查、提交推送并检查远端 CI 终态
- 第二次推送 `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;生成器重建后工作区合同无额外差异
- 当前活动叶子保持 L9;下一步提交本轮低水位修复,等待所有远端工作流终态成功后更新为 COMPLETE
本文件作为本次重构的持续记录,保留阶段状态、实际变更、验证结果、提交状态与已知基线边界。

View File

@@ -519,7 +519,7 @@
"no-any-return": 5,
"no-untyped-call": 1,
"no-untyped-def": 2,
"type-arg": 11
"type-arg": 10
},
"app/api/endpoints/recommend.py": {
"attr-defined": 2,

View File

@@ -579,9 +579,6 @@
"app/schemas/response.py": {
"I001": 1
},
"app/schemas/site.py": {
"I001": 1
},
"app/schemas/subscribe.py": {
"I001": 1
},
@@ -996,9 +993,6 @@
"tests/test_site_adapter_collector.py": {
"I001": 1
},
"tests/test_skill_scripts_security.py": {
"I001": 1
},
"tests/test_skills_command.py": {
"E402": 6,
"I001": 1