diff --git a/app/agent/__init__.py b/app/agent/__init__.py index 5b411428..06ec498c 100644 --- a/app/agent/__init__.py +++ b/app/agent/__init__.py @@ -534,7 +534,7 @@ class MoviePilotAgent: @property def is_background(self) -> bool: """ - 是否为后台任务模式(无渠道信息,如定时唤醒) + 是否为无需回传捕获内容的后台任务模式。 """ return (not self.channel or not self.source) and not callable(self.output_callback) @@ -555,6 +555,13 @@ class MoviePilotAgent: """ return self.session_id.startswith(HEARTBEAT_SESSION_PREFIX) + @property + def has_message_context(self) -> bool: + """ + 是否具备真实消息渠道上下文。 + """ + return bool(self.channel and self.source) + async def _is_system_admin_context(self) -> bool: """ 判断当前 Agent 会话是否应按系统管理员上下文运行工具。 @@ -1042,7 +1049,7 @@ class MoviePilotAgent: UsageMiddleware(on_usage=self._record_usage), ] - if not self.is_heartbeat_session: + if self.has_message_context: middlewares.insert( 4, ActivityLogMiddleware( diff --git a/app/agent/middleware/activity_log.py b/app/agent/middleware/activity_log.py index 32bd7382..4a5f5fca 100644 --- a/app/agent/middleware/activity_log.py +++ b/app/agent/middleware/activity_log.py @@ -3,13 +3,14 @@ 按日期存储在 CONFIG_PATH/agent/activity/YYYY-MM-DD.md 中, 每次 Agent 执行完毕后自动调用 LLM 对本轮对话生成简洁的活动摘要, -并在每次 Agent 启动时加载近几天的活动日志注入系统提示词。 +并在每次 Agent 启动时注入轻量索引,完整日志由工具按需查询。 """ import re from collections.abc import Awaitable, Callable from datetime import datetime, timedelta -from typing import Annotated, Any, NotRequired, TypedDict +from pathlib import Path +from typing import Annotated, Any, NotRequired, Optional, TypedDict from anyio import Path as AsyncPath from langchain.agents.middleware.types import ( @@ -30,29 +31,210 @@ from app.log import logger # 活动日志保留天数 DEFAULT_RETENTION_DAYS = 7 -# 注入系统提示词时加载的天数 +# 注入系统提示词时索引的天数 PROMPT_LOAD_DAYS = 3 +# 工具默认查询的天数 +DEFAULT_QUERY_DAYS = 7 + +# 工具单次返回的最大条数 +DEFAULT_QUERY_LIMIT = 20 +MAX_QUERY_LIMIT = 50 + # 每日日志文件最大大小 (256KB) MAX_LOG_FILE_SIZE = 256 * 1024 # 提取本轮对话上下文的最大字符数(避免过长的对话消耗太多 token) MAX_CONTEXT_FOR_SUMMARY = 4000 +TRIVIAL_USER_TEXT_PATTERN = re.compile( + r"^\s*(你好|您好|hi|hello|hey|谢谢|谢了|多谢|ok|好的|收到|嗯|嗯嗯|是的|对|可以|行|好)\s*[。.!!]?\s*$", + re.IGNORECASE, +) + +SUMMARY_SKIP_MARKER = "SKIP" + # LLM 总结的提示词 -SUMMARY_PROMPT = """请根据以下 AI 助手与用户的对话记录,生成一条简洁的活动摘要(中文,一句话,不超过80字)。 -摘要应包含:用户的需求是什么、助手做了什么、结果如何。 -只输出摘要内容,不要加任何前缀、标点序号或解释。 +SUMMARY_PROMPT = """请判断以下 AI 助手与用户的对话是否值得写入 MoviePilot 活动日志。 + +如果本轮只是问候、寒暄、感谢、确认、闲聊、没有实际任务、没有工具动作、任务没有推进、纯粹的格式纠正或无意义空转,请只输出:SKIP + +如果值得记录,请输出一条中文单行活动摘要,要求: +- 40 到 160 个汉字左右,信息密度高,不要写成泛泛一句话。 +- 只输出摘要正文,不要标题、编号、Markdown、JSON 或解释。 +- 尽量包含:用户目标、关键对象(影片/剧集/站点/路径/任务/设置)、助手采取的关键动作或工具、结果状态、失败原因或下一步。 +- 如果有明确 ID、路径、站点名、任务状态、成功/失败数量,请保留关键值。 +- 不要记录 API Key、Cookie、Token、密码等敏感信息;如出现请写成“敏感信息已省略”。 + +推荐格式示例: +用户要求整理 `/downloads/Show`,助手识别为《示例剧》TMDB 12345,并提交 transfer_file 整理,结果成功。 +用户排查下载失败,助手查询 qBittorrent 任务和站点状态,发现 tracker 超时,建议更换站点或重试。 对话记录: {conversation}""" +ACTIVITY_ENTRY_PATTERN = re.compile(r"^-\s+\*\*(?P