From 6647565ec4f1f42ca9dc6b5d2f7c52486386d392 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 21 Jun 2026 18:29:27 +0800 Subject: [PATCH] Refactor plugin cache handling and update related docs --- app/agent/__init__.py | 51 +++- app/agent/middleware/activity_log.py | 118 ++++++++- app/agent/middleware/jobs.py | 72 +----- app/agent/middleware/skills.py | 277 +++++++++++++++------ app/agent/prompt/__init__.py | 109 +++----- app/agent/runtime.py | 20 +- app/agent/tools/factory.py | 3 - app/agent/tools/impl/query_activity_log.py | 120 --------- app/agent/tools/tags.py | 1 + tests/test_agent_activity_log.py | 55 ++-- tests/test_agent_background_output.py | 259 +++++++++++++++++-- tests/test_agent_prompt_secrets.py | 48 +++- tests/test_agent_runtime.py | 7 +- tests/test_agent_skills_middleware.py | 124 +++++++-- 14 files changed, 808 insertions(+), 456 deletions(-) delete mode 100644 app/agent/tools/impl/query_activity_log.py diff --git a/app/agent/__init__.py b/app/agent/__init__.py index b292635e..5c3f0055 100644 --- a/app/agent/__init__.py +++ b/app/agent/__init__.py @@ -27,7 +27,10 @@ from langgraph.checkpoint.memory import InMemorySaver from app.agent.callback import StreamingHandler from app.agent.llm import LLMHelper from app.agent.memory import memory_manager -from app.agent.middleware.activity_log import ActivityLogMiddleware +from app.agent.middleware.activity_log import ( + ActivityLogMiddleware, + QUERY_ACTIVITY_LOG_TOOL_NAME, +) from app.agent.middleware.jobs import ( JobsMiddleware, filter_active_jobs, @@ -36,7 +39,7 @@ from app.agent.middleware.jobs import ( from app.agent.middleware.memory import MemoryMiddleware from app.agent.middleware.patch_tool_calls import PatchToolCallsMiddleware from app.agent.middleware.runtime_config import RuntimeConfigMiddleware -from app.agent.middleware.skills import SkillsMiddleware +from app.agent.middleware.skills import SKILL_TOOL_NAME, SkillsMiddleware from app.agent.middleware.subagents import ( SUBAGENT_CONTROL_TOOL_NAME, SUBAGENT_TASK_TOOL_NAME, @@ -972,6 +975,20 @@ class MoviePilotAgent: # 工具列表 tools = self._initialize_tools() + skills_middleware = SkillsMiddleware( + sources=[str(agent_runtime_manager.skills_dir)], + bundled_skills_dir=str(settings.ROOT_PATH / "skills"), + ) + skill_tools = list(getattr(skills_middleware, "tools", []) or []) + activity_log_middleware = None + activity_log_tools = [] + if self.has_message_context: + activity_log_middleware = ActivityLogMiddleware( + activity_dir=str(agent_runtime_manager.activity_dir), + ) + activity_log_tools = list( + getattr(activity_log_middleware, "tools", []) or [] + ) subagent_middlewares, subagent_task_tools = create_subagent_middlewares( model=non_streaming_model, tools=self._initialize_subagent_tools(), @@ -988,14 +1005,23 @@ class MoviePilotAgent: if getattr(tool, "name", None) in {SUBAGENT_TASK_TOOL_NAME, SUBAGENT_CONTROL_TOOL_NAME} ) + if skill_tools: + always_include_tools.extend( + tool.name + for tool in skill_tools + if getattr(tool, "name", None) == SKILL_TOOL_NAME + ) + if activity_log_tools: + always_include_tools.extend( + tool.name + for tool in activity_log_tools + if getattr(tool, "name", None) == QUERY_ACTIVITY_LOG_TOOL_NAME + ) # 中间件 middlewares = [ # Skills - SkillsMiddleware( - sources=[str(agent_runtime_manager.skills_dir)], - bundled_skills_dir=str(settings.ROOT_PATH / "skills"), - ), + skills_middleware, # Jobs 任务管理 JobsMiddleware( sources=[str(agent_runtime_manager.jobs_dir)], @@ -1019,9 +1045,7 @@ class MoviePilotAgent: if self.has_message_context: middlewares.insert( 4, - ActivityLogMiddleware( - activity_dir=str(agent_runtime_manager.activity_dir), - ), + activity_log_middleware, ) # 工具选择 @@ -1029,7 +1053,12 @@ class MoviePilotAgent: middlewares.append( ToolSelectorMiddleware( model=non_streaming_model, - selection_tools=[*tools, *subagent_task_tools], + selection_tools=[ + *tools, + *skill_tools, + *activity_log_tools, + *subagent_task_tools, + ], max_tools=max_tools, always_include=always_include_tools, ) @@ -1037,7 +1066,7 @@ class MoviePilotAgent: return create_agent( model=agent_model, - tools=tools, + tools=[*tools, *skill_tools, *activity_log_tools], system_prompt=system_prompt, middleware=middlewares, checkpointer=InMemorySaver(), diff --git a/app/agent/middleware/activity_log.py b/app/agent/middleware/activity_log.py index 6ca95a87..01698b7e 100644 --- a/app/agent/middleware/activity_log.py +++ b/app/agent/middleware/activity_log.py @@ -6,6 +6,7 @@ 并在每次 Agent 启动时注入轻量索引,完整日志由工具按需查询。 """ +import json import re from collections.abc import Awaitable, Callable from datetime import datetime, timedelta @@ -23,9 +24,12 @@ from langchain.agents.middleware.types import ( ResponseT, ) from langchain_core.messages import AIMessage, HumanMessage, ToolMessage +from langchain_core.tools import StructuredTool from langgraph.runtime import Runtime +from pydantic import BaseModel, Field from app.agent.middleware.utils import append_to_system_message +from app.agent.tools.tags import ToolTag from app.log import logger # 活动日志保留天数 @@ -48,6 +52,13 @@ MAX_LOG_FILE_SIZE = 256 * 1024 MAX_CONTEXT_FOR_SUMMARY = 4000 SUMMARY_SKIP_MARKER = "SKIP" +QUERY_ACTIVITY_LOG_TOOL_NAME = "query_activity_log" +QUERY_ACTIVITY_LOG_TOOL_DESCRIPTION = ( + "Query recent MoviePilot Agent activity logs on demand. Use this when the user asks what was done before, " + "asks to continue a previous task, or explicitly references recent agent activity. Supports keyword, date, " + "recent-day window, limit, and optional regex filters. If a keyword search returns no results, retry with " + "a shorter keyword, a larger days window, or no keyword to inspect recent entries." +) # LLM 总结的提示词 SUMMARY_PROMPT = """请判断以下 AI 助手与用户的对话是否值得写入 MoviePilot 活动日志。 @@ -71,6 +82,41 @@ SUMMARY_PROMPT = """请判断以下 AI 助手与用户的对话是否值得写 ACTIVITY_ENTRY_PATTERN = re.compile(r"^-\s+\*\*(?P