36 KiB
Agent 复杂任务执行与恢复
MoviePilot Agent 通过模型、Skills、工具和会话状态共同完成任务。本次增强关注长任务的连续性:明确目标和检查结果,按需补充工具,并在执行中断后保留已知事实。能力对齐路线与每轮真实运行验收见 Agent 与 Codex Harness 对齐路线。
提示词与任务边界
核心提示词同时支持媒体管理、运行诊断、插件开发和 Bug 修复。媒体查询、订阅和整理优先走业务工具;用户要求编写插件或修改源码时,进入代码工作流:读取项目规范和现有改动、确认宿主代际与接口、实施最小修改、运行相关验证。插件 Skill 按实际 V3/V2 宿主选择源码目录、索引、依赖清单和开发指南。
用户明确要求的动作、对象和范围可以直接执行并验证,已有授权不重复询问;目标不清楚或需要扩大范围时才补充确认。代码修改本身不代表授权安装、重载、重启或发布,受自动重载监视的源码也要考虑即时运行影响。宿主明确要求的权限与确认检查仍必须满足。
完成标准对应用户请求:订阅创建并核实不需要等待未来更新,下载提交不代表已经入库,整理排队不代表整理完成,定时任务建好不代表未来执行成功。代码报告区分本地修改、测试、运行验证和发布。进度按有意义的发现或阶段结果更新,子代理结果由主 Agent 综合后说明。
Skill 目录仅提供发现信息,具体操作契约通过 read_skill 按需读取;操作白名单仍由宿主校验。人设负责表达风格,不能覆盖渠道格式、用户范围或宿主权限。网页、种子描述、文件名、NFO、日志、记忆和子代理报告都只是证据,不能授予写操作权限。
反馈 Issue 时,预览脚本返回完整脱敏预览和“确认提交 / 修改内容 / 取消”选项。支持交互的渠道通过 ask_user_choice 一次展示预览与按钮;点击确认后直接提交,不再要求手工重复确认。不支持按钮时使用文字确认,修改草稿后须重新预览并确认。GitHub 错误报告、功能请求模板与反馈脚本均支持 CLI 运行环境。
这些规则借鉴了 OpenAI 的 Codex 提示词指南中的持续执行与验证、Anthropic 的上下文工程实践中的按需加载,以及 Hermes 的提示词分层。具体授权和完成标准以 MoviePilot 的媒体业务与宿主契约为准。
任务计划
复杂任务可以使用内部 update_plan 工具保存目标、步骤、状态、检查依据和阻塞原因。例如“排查下载后未入库”可以依次检查下载状态、整理队列、目标目录和媒体库。
- 简单查询不需要创建计划。
- 步骤支持
pending、in_progress、completed、blocked;完成和阻塞状态需要填写依据。 - 计划单独保存在会话图状态中,每次模型请求都能读取;压缩旧消息不会删除计划。
- 计划快照随聊天消息保存,在会话图重建后恢复。用户询问进度时继续当前目标,明确开始新任务时替换计划。
- 计划是模型记录的工作状态,不代表工具执行结果或写操作授权。仍需通过工具核实实际结果。
代码修改与 Pull Request
用户明确要求提交 PR 时,使用 skills/submit-pull-request/SKILL.md。该流程把 Git
clone 作为代码修改入口:先通过 GitHub API 复用或创建当前 Token 用户的 Fork,
再从上游基线创建隔离工作分支和本地 clone;后续 Agent 的修改与测试都必须发生在
该 clone 中。没有本地 Git 仓库时不是降级为文件上传,而是先执行 clone。
流程在提交前生成真实 Git 工作树的变更预览,并要求用户明确确认;确认后才执行本地
git add、git commit、git push,最后通过 GitHub API 创建或复用 PR。提交脚本会
重新校验上游基线、Fork parent、origin remote、文件摘要和推送后的 Commit SHA,
不执行强制推送、合并或自动关闭 Issue。Token 只从运行时 GitHub 设置读取,不出现在
命令行、预览和日志中;网络中断可以复用同一 payload 重试。
运行中补充消息
Web 会话 ID 只用于定位,不能授予访问权限。已有记录按持久化 user_id 授权;
只有无 ID 的旧记录才按当前登录用户名兼容匹配。新会话按稳定用户 ID 分区,
包括上传附件后尚未保存历史的会话,账号改名不会改变身份,回收用户名不会继承他人历史。
越权续聊返回流式错误,越权上传返回 404,展示历史写回前再次检查归属。
Web 管理员可以续聊其他渠道的历史,保留原会话归属,但工具执行人仍是当前登录管理员;
后台或协议入口的管理员工具上下文不自动授予跨用户历史读取权,缓存复用也重新检查显式授权。
WebAgent 和消息渠道在 Agent 正在运行时仍可提交文本和附件。消息渠道只有同一用户、渠道配置、聊天位置和权限的输入才会追加到当前任务;其它来源保留独立排队处理。宿主为每个会话生成消息 ID,先把消息放入有界 steering inbox;下一次模型调用前由中间件把它作为真实 HumanMessage 写入图状态,WebAgent 同时通过当前 SSE 报告 queued、applied。提交和运行收尾共享原子边界,收尾竞态中已接受的消息会在同一 worker 内继续处理,停止会话不会继续派发新的工具动作。补充消息不会启动第二张 Agent 图,也不会替换当前输出回调。
补充消息实际加入上下文后,后续回复会另起一条消息。WebAgent 在该边界插入用户气泡并创建新的助手气泡;支持消息编辑的渠道会收口旧回复,再以新消息承接后续正文和工具进度,回复目标也切换到本次补充消息。尚未加入上下文时,当前回复继续更新。队列已满或运行已收尾时,输入回退到普通会话队列,不会静默丢弃。
稳定偏好与历史证据回忆
稳定记忆位于 config/agent/memory:MEMORY.md 保存公共规则,当前用户的环境事实位于 memory/users/<user-key>/MEMORY.md,跨任务偏好位于同目录 USER.md。这些稳定文件默认注入上下文,公共记忆先于用户记忆;其它主题 Markdown 通过 search_memory 按需检索,支持 primary、topic、all、字面关键词、文件路径、条数和显式正则。
session_search 按 Hermes 的四种形态返回实际消息,不额外调用模型摘要:query 发现相关会话,session_id 阅读会话,附带 around_message_id 围绕锚点滚动,无参数浏览最近会话。默认搜索 user/assistant,显式 role_filter="tool" 搜完整工具原文。默认 BM25 相关性排序,支持短语、AND/OR/NOT、前缀、会话开始时间区间、newest/oldest 和已看会话排除;最多 300 条候选经来源降级、会话谱系去重后返回默认 3、最多 10 个会话。adaptive 只为首命中展开前后各 5 条和首尾各 3 条,其余保留精确锚点,避免大量无关历史占满上下文。
消息库独立放在 config/agent/runtime/history/users/<user-key>/state.db,使用 SQLite FTS5、WAL、短事务与有界宿主 worker;不依赖业务主库的 SQLite/PostgreSQL 类型。按用户分别存库,工具不能指定用户、profile 或文件路径。宿主在上下文压缩前、模型回复后、工具结果截断前采集完整脱敏文字,工具调用关联单独保存,图像/音频二进制及合成压缩摘要不入库。正文保留与检索展示上限分开:工具标准索引只取 8192 字符,原文不因此裁剪;阅读默认首 20/尾 10 条,每条 2000 字符,锚点窗口每条 4000 字符。
标准词索引采用 external-content FTS5;中文/子串使用 Hermes 原版可选 cjk_unicode61 tokenizer 和 trigram。短词、显式工具全文、尚未就绪的索引按 Hermes 路由回退 LIKE,并通过 SQLite VM deadline 限制扫描;超时明确返回未完成,不伪报没有匹配。索引增量回填使用高水位/进度与一致触发器,结果暴露索引状态。见 历史检索与 Hermes 对标 中的路由、安装与验收说明。
旧主库只作为一次性恢复快照来源,后台分批搬迁;搬迁完成后检索不再读取主库。旧证据标记 legacy_snapshot,无法恢复升级前已被丢弃的消息,时间不能冒充原始事件时间。独立库复用 DATA_CLEANUP_ENABLE 与 DATA_CLEANUP_AGENT_CHAT_DAYS,在用户历史后台维护时按自身活动时间分批清理,零天关闭自动删除;不通过主库外键驱动。用户显式删除会话会同时删除独立证据并保存删除墓碑,防止在途写入或旧快照搬迁复活。跨库不假称原子事务,先删除证据,主库失败时安全重试。
压缩后的原始消息仍可查,当前活动上下文中的重复消息则排除。已废弃逐轮活动摘要生成、清理、检索入口和每任务强制首工具检索;旧活动 Markdown 原地保留,不再读取或续写。检索仅在历史与当前任务相关时使用;历史证据不能替代实时业务核验,也不授予新的写权限。
用户明确要求记住的跨任务偏好通过 memory(target="user") 写入当前用户的 USER.md,环境事实使用 target="memory";任务类别的操作方法和特定偏好写入个人技能。旧用户 MEMORY.md 原文保留,不自动搬迁或裁剪;只有系统管理员可以写入全局公共记忆。一次性请求、临时状态和凭据不应写入记忆。检索到的文件内容只是上下文,不能覆盖系统或用户指令。
Agent 人格是全局运行时配置,不按用户隔离;所有用户共享当前激活人格。普通用户可以查询人格列表,但只有系统管理员可以切换激活人格或修改人格定义。
按需发现工具
设置 LLM_MAX_TOOLS > 0 时,Agent 首轮仍只筛选一批相关工具。后续读取 Skill 或发现新问题后,可以通过内部 search_tools(search, limit) 按名称、说明或标签搜索当前会话工具目录,并在下一次模型调用获得匹配工具的完整参数定义。
每次最多返回 8 个匹配工具,并保留最近 16 个额外工具候选;再次发现会刷新候选优先级。匹配支持工具精确名称和有限中英文能力别名,例如“整理”对应 transfer。没有匹配结果时不会展开全部工具。
额外工具的参数定义共享最多 4096 tokens、且不超过已知模型窗口 10% 的预算,总输入保留 15% 余量。搜索先报告候选,下一次模型调用根据统一预算明确报告实际启用和未启用原因。初选、常驻和供应商工具保持可用。发现状态仅作用于当前用户请求,新的用户请求重新筛选。LLM_MAX_TOOLS 约束首轮筛选数量;发现不会安装工具、连接新的 MCP 服务或扩大执行权限。
update_plan、search_memory、session_search、memory、skills_list、skill_view、skill_manage、search_tools、read_tool_result 和 get_tool_execution 是 Agent 内部会话能力,不通过外部 MCP 或 moviepilot tool 发布。
工具结果与大结果续读
宿主统一区分 succeeded(成功)、failed(失败)、pending(任务未完成)、unknown(实际结果未知)。明确的业务失败、MCP isError 和普通异常会以失败消息返回模型;异步任务已提交不等于任务完成。普通业务对象中的错误说明或下载器状态不会被当成整个工具的失败或执行状态。
超长结果在当前会话图中临时保存,预览带 result_id 和 next_offset。Agent 使用 read_tool_result 按 Unicode 字符位置续读,无需重新执行原工具。每条完整结果最多 1 MiB,每个图最多 8 条、合计 4 MiB,15 分钟后过期;容量超限、图重建和进程退出也会使编号失效。无法保存的结果明确要求缩小查询范围。不同图线程不能互读,管理员结果在降权后不能续读。结果原文只短期驻留内存,不归档到磁盘或日志。
execute_command(action="run") 返回结构化 JSON:exit_code、timed_out、execution_outcome 和 status 表示实际执行结果,output 保存输出预览,output_file 指向超长输出的临时归档。仅正常退出且退出码为 0 时成功;非零退出或已停止的超时命令为失败,无法确认进程结束时为未知,不能凭“有输出”判断成功。超时和取消不会撤销命令已经产生的外部副作用。取消继续向外传播,同时回收输出读取任务、关闭归档文件;run 与 start 都支持 env,并采用同一工作目录与解释器策略。
项目安装目录存在 venv,或 Docker 的 VENV_PATH 环境存在时,execute_command 的一次性命令、pipe 和 PTY 会把该环境的 bin(Windows 为 Scripts)放在子进程 PATH 最前,设置 VIRTUAL_ENV,并让裸 python/python3 使用项目专用入口 moviepilot-python;入口不存在时回退到环境中的标准 Python。执行 Python 直接使用 python -m ...,尤其是下载器、SMB、媒体服务器等需要系统权限的操作。安装依赖优先使用项目 uv pip ... 或绑定环境中的 python -m pip ...,不要改用系统解释器或 sudo pip。
run、后台 pipe 和 PTY 共用 cwd、shell、login 解析。省略 cwd 使用 MoviePilot 根目录,相对路径也相对该目录,并支持 ~;错误目录在进程启动前拒绝。POSIX 默认使用配置的 SHELL(未配置时 /bin/sh)且不启动登录模式,显式 shell 可选择已安装解释器,login=true/false 控制其支持的登录行为。登录启动文件可能改变目录或环境;PTY 本身不再隐式开启登录模式。Windows 未显式指定时保留 Git Bash → PowerShell 7 → cmd 的选择顺序与 UTF-8 策略;不支持的解释器/登录组合明确失败。回包的 shell、login 说明实际策略,默认解释器不可用不会阻断其它 Agent 业务能力。
管道会话可用 write(input_text="末段输入", close_stdin=true) 在交付末段后关闭输入,或用空输入显式关闭;输出仍通过 read/wait 读取,stdin_closed 表示输入已关闭。普通空 write 不发送 EOF;输入关闭后不能再追加数据,重复空关闭可确认已有状态。run 的 stdin 始终为 EOF。PTY 的输入和输出共用端点,因此拒绝 close_stdin=true,也不会先写入附带输入或关闭输出;PTY 控制字节的行为取决于终端模式,pipe 中 \u0003、\u0004 只是普通字节。
终端句柄绑定宿主创建的用户及任务作用域,知道 session_id 不代表可以操作它。对话正常多轮和图重建保留同一作用域;每次定时执行按真实 run_id 单独持有终端,执行结束、失败或取消后回收。清空对话、空闲回收或同会话更换用户时封闭旧作用域;只清理它拥有的进程,未收敛时保留记录供重试。停止当前交互推理仍保留已交付的后台终端,后续对话可继续读取或显式终止。
父任务使用 task 或 subagent_task 的 terminal_sessions=[{"session_id":"term_…","actions":["read","wait"]}] 显式分享指定终端给该子任务。批次及管道分别在每个任务定义里声明;提示词中提到句柄不会授权,兄弟任务也不会继承。子任务保有独立作用域,结束时撤销分享但不终止父进程;当前只读子代理策略继续限制动作。宿主控制授权与工具角色权限分别校验,共享不能扩大角色权限。
缺少、已关闭或无权访问的终端均返回 terminal_access_denied,不回显其他任务的命令和输出。进程重启后不恢复旧句柄,也不会自动重跑命令。内部 MoviePilotToolsManager 每个实例独立持有作用域,通过 close() 收口;直接调用命令工具必须由宿主绑定真实上下文,工具 JSON 不能声明或覆盖身份。
interrupt 仅发送一次 POSIX SIGINT 或支持的 Windows CTRL_BREAK_EVENT,不等待升级强杀,也不把会话标记为主动终止;实际命令可以处理信号后继续运行,也可能自行退出。回包含实际 signal 和 signal_sent。不具备该控制能力时明确失败;kill 仍负责终止并在需要时强杀,但未知名称、无效编号和没有真实映射的信号会在任何状态修改前拒绝。两种输入控制均不增加只读子代理权限。
后台命令 start 默认最多等待 250ms 的首次输出,可用 yield_time_ms=0 立即返回。后续 read/wait/write/interrupt/kill 用返回的 output_until_seq 和 output_until_offset 一起续读:前者是完整交付的最后一个分片,后者是下一分片中已交付的 UTF-8 字节位置。首次传 since_offset=0 开启分片内分页;不传 offset 时保持完整分片模式,小页装不下一个分片会明确提示调整限额。last_seq 只表示已经产生的输出,不能用于跳过未读内容。
wait 在有未读输出时立即返回,等待中产生新输出或读取结束也会唤醒;timeout_ms=0 只查询,不终止命令。进程退出后仍可能有尾部输出,获取完整日志应继续到游标读完且 output_complete=true;退出码缺失时仍为 unknown。缓冲保留窗口之外的缺口由 output_lost=true 明确标记。若 start/write/interrupt/kill 已执行动作但返回 output_error,会保留会话 ID 和未消费游标;只需调整 max_bytes 后用 read 获取输出,不应重新执行动作。首次 start 尚未交付会话 ID 时被取消会回收该进程,取消已有会话的 wait 则保留进程。
终端分页还受最终 JSON 字符预算约束:必要时减少本页正文并重新计算游标,避免 JSON 转义后又被通用工具预览截断。过长的命令、目录和错误回显会带显式截断标记;会话内部保留完整命令。
工具图片与模型视觉
browse_webpage(action="screenshot") 和 view_image(url=...|file_path=...|image_data=...) 在 Agent 中返回真实图像块和有限来源说明。图像在通用文本截断之前处理,最终请求按完整工具回复批次把 ToolMessage 中的 image_url 块组装成带 TOOL_OBSERVATION_MARKER 的临时 HumanMessage(content=[text, image_url, ...]),再发送给支持图片的模型;原始用户消息、工具调用 ID 和授权不变。视觉中间件只修改出站副本,不把临时图片观察写回持久会话历史。view_image 的远程 URL 只允许通过公网安全校验的 HTTP(S) 地址,本地路径继续遵守 Agent 文件访问边界,image_data 支持纯 Base64、data URL 和原始字节。
每次模型请求都检查实际模型资料和 LLM_SUPPORT_IMAGE_INPUT 开关。总开关关闭时不投递工具图片;开启时优先采用实际提供商、模型及端点对应的缓存模型目录,目录能力未知时再参考依赖包的静态档案,两者均未知时沿用用户开关。目录明确声明支持图片时,依赖包中的旧“不支持”档案不会再剥离图像;目录明确为纯文本时仍保留保护。已知纯文本模型会得到明确的“未接收工具图片”说明。服务明确拒绝图片时,只对该次模型调用做一次文字回退,随后本轮沿用文字观察,不重复执行图片工具或其他工具;认证、限流等错误保持原来的错误语义。临时观察不会写入会话历史。
浏览器截图最多约 150 KiB 原始 JPEG,view_image 图片最多约 768 KiB,实际输入还受 1 MiB data URL 和像素上限约束;超限或数据无效时明确失败。运行图可保留完整图片,持久化与中断恢复仅保存来源及图像未保留说明,日志不记录图片数据。恢复后的旧图片不能用于声称已看到视觉细节,需要重新调用图片工具获取当前内容。视觉 token 预算是估算,实际消耗以模型供应商的 usage 为准。
用户请求登录时,图形验证码支持 OCR 与模型视觉两条路径。依赖浏览器会话、blob/canvas 或每次请求会换图的验证码,优先用 browse_webpage(action="screenshot", selector="已观察到的验证码选择器") 截取当前显示元素,避免独立下载改变验证码;不传 selector 时仍截取当前视口。元素选择器必须唯一,截图失败不会退回其他元素。recognize_captcha 使用配置的 OCR 服务,识别失败或服务异常会保留 success=false 并返回视觉识别的恢复指引。Agent 按浏览器 Skill 在同一会话和标签页中尝试看图,再决定刷新或手工输入;view_image(url=...) 不继承浏览器 Cookie,只用于可独立读取的图片。图片输入关闭、模型拒绝图片或图像不可读时不得猜测。OCR 与视觉合计最多提交三次,每次均以网站实际响应验证结果。
写工具的持久执行记录
生产 Agent 在副作用之前向 agentinvocation 表提交原子认领。记录只包含用户、会话、工具身份、参数指纹、执行 token、状态和固定宿主文案,不保存原始参数或工具输出。无法认领时不执行写入;相同调用 ID 携带不同参数时拒绝执行。
- 同一用户请求内,参数规范化后完全相同的 MoviePilot API 写调用共用一个执行身份,防止模型并行或重复调用;明确的新用户请求可再次执行已完成操作。
- 普通工具使用原始工具调用 ID 去重,不把重复执行同一条诊断命令误合并为同一业务意图。
- 上一轮相同 API 参数仍为运行中或未知时,新请求先核验旧记录,不盲目发起第二次写入。已确认提交的异步请求保存为 pending:同一执行身份不重复提交,新的明确用户意图可以再次提交,pending 不代表外部任务已完成。
get_tool_execution可凭宿主返回的invocation_id查询当前用户、当前会话的最近观测状态。 - 当前自动核验支持非敏感设置完整替换:通过
config.system.get只读确认目标值,匹配后收口为成功并跳过重复写入。敏感、未匹配、列表追加及其他无法确定的副作用保持未知,不允许模型自行把它改成成功或“未执行”。 - 写入请求发出后的传输中断、超时和响应正文不可用保留未知;调用前参数错误和明确 HTTP 拒绝仍为失败。
- 冷启动将未收口的 running 转为 unknown,并更换 token。旧执行者不能覆盖新恢复状态,也不会仅因时间过去而自动重放。
成功、失败和已确认提交的回执随会话删除回收;没有聊天记录的后台同类回执按共享会话保留期清理。pending 是已确认提交的历史,实际异步任务由原任务系统管理,不是待执行队列。DATA_CLEANUP_ENABLE 和保留期 0 的禁用语义保持一致,running/unknown 恢复状态不会按时间删除。这些机制不构成任意外部系统的“恰好一次”事务保证。
异常和取消后的继续
对已启用聊天持久化的会话,执行异常或用户取消时,Agent 在释放会话图之前尝试保存脱敏恢复快照。已收到的工具回执保留;未收到结果的调用明确标记为“结果未知”,不能当成未执行或已成功。
用户随后说“继续”时,Agent 可以读取已有结果和计划,从剩余工作继续。下载、修改配置、删除文件等操作如缺少回执,应先只读核验实际状态,再决定是否重试。取消仍会终止当前轮次,不会自动重放工具。
聊天快照保存采用有界等待。进程强制退出、持久化失败,或工具在中断后才完成的外部副作用,不能保证被聊天快照记录;写工具通过上面的持久执行记录保留恢复边界。
子代理连续工作
默认子代理在宿主执行边界强制只读。多功能 API 网关按具体 operation_id 判断副作用,继承管理员身份也不能修改、删除、触发下载或服务操作;敏感设置原值读取同样不开放。终端仅允许读取/等待已有输出;浏览器仅允许观察已有页面,不允许导航、输入、点击、脚本、关闭或修改 Cookie。外部 MCP 的兼容 Read 标签没有动作级保证,需交由主代理处理。正常业务查询和声明只读的本地工具仍可使用。
一轮结束会回收未完成的子任务,正常收敛后同一会话下一轮仍可委派。会话关闭时永久停止接收子任务;无法在等待上限内收敛的任务仍由原 owner 持有并封闭新提交。
子代理沿用主 Agent 的最终请求预算和上下文压缩机制。长调查可以压缩旧消息;最新单条工具事务或固定提示本身超过模型窗口时仍会明确失败,不会静默删除关键内容。
后台学习与个人技能
对齐 Hermes 的回复后复盘:已认证交互会话每累计10个用户轮次触发记忆复盘,每累计10次带工具调用的模型迭代触发技能复盘,调用 skill_manage 重置技能计数。最终回复交付并保存成功后才启动;取消、失败、定时任务和内部后台任务不启动。新前台输入先取消旧复盘,最多等待2秒;超时的任务仍有 owner,后续写入由取消令牌封闭。
复盘使用相同模型、推理配置、最终系统提示词和工具 schema,完整复制消息,首次请求保留原缓存前缀。执行白名单单独限制到技能维护和必要的读取;只有记忆触发时才能执行 memory。终端、业务 API、普通文件写入和外部 MCP 均不能在复盘执行。当前存在供应商原生工具的请求无法由本地白名单约束,因此跳过自动复盘。每次最多16轮,累计输入预算为模型窗口的75%、最高600000 token;未知窗口使用120000。后续上下文压缩独立于主图,其模型用量也累计归属父会话;后台文本不覆盖主回复或归档。
个人技能在 agent/runtime/learning/users/<user-key>/skills,独立于全局市场技能。skills_list 枚举目录,skill_view 按需读取全文和支持文件,skill_manage 最多20个有序操作共用锁与错误回滚边界。支持创建、局部补丁、完整重写、支持文件增删和独占批次删除;补丁沿用 Hermes 的九级匹配、歧义拒绝、Unicode/缩进和转义保护。后台修改已有文件必须在本次复盘重新读取该文件,读后内容变化也须重读。普通 I/O 失败回滚文件,不能视为跨文件断电事务保证。后台删除必须提供已存在的合并目标,并将完整包和来源保留到个人库 .archive;前台明确删除仍是硬删除。固定技能均不可删除。
公共、市场和外部技能只读;手工或前台创建的技能默认属于用户,后台只能维护自己创建且未固定的个人技能。来源、用户身份和所有权由宿主写入,不接受模型参数。个人技能不能声明 API/工具授权,也不能覆盖公共同名技能。复盘优先修正当前加载的同类技能,其次已有类别或主题支持文件,最后才创建新的类别技能;不保存逐次任务流水,也不将未解决的失败包装成可靠方法。稳定事实、跨任务偏好和操作方法各有一个归属,不重复保存。
后台复盘是宿主启动的独立模式,允许在上述范围内维护记忆和个人技能,不受前台“只有用户要求编写技能才修改”的限制。复盘没有更新数量要求:没有新的、持久且有证据的信息时不写入就是有效结果。记忆、技能和联合复盘分别遵守启用范围,不续做业务任务、不向用户发送消息,也不把未经验证的修复方案保存为可靠经验。
memory 按完整条目维护两份文件,默认预算 MEMORY.md 2200字符、USER.md 1375字符。old_text 仅定位条目,replace 的 content 替换整条。批次按最终大小校验,任何操作失败均不写入;禁止批次清空整库,明确删除最后一条需单次 remove。读取失败和磁盘格式漂移不会被当作空文件覆盖;注入模式在写入时拒绝,在加载快照时屏蔽但保留磁盘原文。会话内提示词使用首次加载的冻结快照,写入从下一会话生效。
后台新增稳定记忆可直接保存;替换或删除整批保存为待确认提案,固定原条目防止确认时误改新内容。用户在聊天输入以下命令即可管理,不经模型审批:
/memory pending:列出提案;/memory pending <ID>查看该提案的完整原条目和提议操作。/memory approve <ID>或/memory discard <ID>:应用或丢弃一项提案;原条目已变化则拒绝应用并保留提案。/skills restore <名称>:恢复后台合并归档的完整技能,拒绝覆盖现有或公共同名项。/skills adopt <名称>:允许后台维护当前用户的一项个人技能。/skills pin <名称>或/skills unpin <名称>:禁止或恢复个人技能的自动维护;公共技能不能接管。
这些命令限定到当前用户;工具参数和历史消息不能代替用户输入。旧活动摘要文件保持原地,不参与学习检索。实现参考固定 Hermes 提交 f42f579cf8bac4918ac9599bece71618afadd846,派生匹配器、威胁模式和复盘提示词保留 MIT 许可;具体对应关系见 派生实现来源。
执行纠偏与停止
工具循环按 Hermes 固定源码的阈值和恢复规则判断:同参数失败2次提示、5次后阻止继续重放;同一工具不同参数失败3次提示、8次可停止,但终端诊断等允许连续失败的工具不会因此被中断。相同只读结果2次提示、5次后阻止重放;连续相同调用3次提示,并检测最近64条中的2–4步重复循环。成功修改后重试属于新实验,改变参数或真实结果也会重新计数。
Web、兼容 API 和受父任务监督的子代理默认只警告,渠道网关与后台任务默认强制停止;网页搜索与子任务启动分别使用每轮50次的预算,按 Hermes 语义在批次派发前检查,已放行的同一并行批次可以越过该阈值。终端与子任务的明确 read/wait/status/list 动作允许正常轮询,不把所有同名工具动作一概豁免。每个真实图线程独立计数,按模型发出顺序处理并行回执,以宿主每次图运行的身份重置。检测复用已有模型包装与策略节点,不额外消耗递归步数,也不提高原有执行上限。
相同长结果从第二次起可在模型上下文中显示为前次结果引用,工具仍然实际执行,原始回执仍会归档。结果变化恢复完整正文,压缩移除原引用后也重新给出完整结果;超长结果存根附带本次 read_tool_result 引用,避免指向已淘汰缓存。未知副作用和等待状态保持显式,不能因循环停止变成成功。宿主生成的停止说明会进入流式回复,定时任务将其记录为未成功,本轮不会触发成功后的学习。
短回复以“接下来我会…”或 Hermes 的英文尾部动作结束却没有调用工具时,最多追加两次原版继续提示;这是相同规则的中文本地化。Codex Responses 入口额外采用原版工作区确认和极短异常碎片规则;普通数字、文件名和符合用户语言的短答案不触发。合成提示不作为用户消息归档或计入学习轮次,耗尽后明确标记部分完成。moviepilot_turn_status 记录 blocked/waiting/unknown/partial;这些是宿主停止状态,不是业务完成证明。
Python 只读工具编排
管理员会话提供 execute_code(code, reset=false),默认直接执行,不增加用户确认步骤。它适合三次以上带分页、分支、过滤或聚合的读取;单次读取或需要模型逐项判断的结果仍使用普通工具。先用 read_skill 加载对应业务合同,再在 Python 中调用当前已启用的 helper:
from moviepilot_tools import moviepilot_api
pages = [moviepilot_api("subscription.list", query={"page": page, "count": 100})
for page in range(1, 4)]
print(pages)
业务返回结构由已加载 Skill 定义,应先核对成功状态、分页终止条件及结果字段,再计算摘要;不要假设每个端点的列表包裹层相同。helper 返回解析后的 JSON,工具原文在进入模型预览截断前交给 Python,模型上下文只接收脚本打印的结果。read_file、search_web 也可在本轮实际启用时导入;签名从真实工具 schema 生成,可用签名写进当前模型请求并计入压缩预算。json_parse、shell_quote、retry 为本地辅助函数。
每次 RPC 自动核对当前用户、会话代次、启用工具交集、50次调用预算及 API 的精确副作用分类,再进入相同的策略、Skill 范围、工具执行与回执链。只开放真实内置工具对象;同名插件、MCP 标签和脚本声明不能扩展范围。API 仅允许 SAFE_READ 操作以及当前身份可读的普通或私有业务结果,例如管理员的整理队列;PRIVATE 不等于凭据。写入、删除、刷新、SECRET/UNKNOWN 结果、show_secrets=true 和未加载的 Skill 操作都会被拒绝。单次完整 RPC 结果上限16MB,超过时明确要求分页,不以不完整正文进行聚合。脚本忽略工具错误或以退出码0结束,也会保留最多5条 tool_errors 并令 success=false。
这是真实的持久 Python 子进程。变量、导入和数据跨 cell 保留;键包含宿主 owner 对象、启用工具集合、解释器和工作目录,不能跨用户、旧会话代次或不同工具集合误用状态。解释器使用宿主当前 Python(保留虚拟环境),工作目录沿用 Agent 的项目根目录。普通脚本异常保留已执行的赋值;reset=true、sys.exit、300秒超时、取消或 owner 关闭销毁内核。结果的 kernel.reused、execution_count、state_reset、ended/state_lost 描述实际状态,不能默认上次变量仍在。
stdout 内联50KB、stderr 10KB;超长 stdout 先脱敏,再把最多5MB正文存入当前用户的 agent/runtime/code/users/<user-key>/output/,可用 read_file(file_path=..., start_line=..., end_line=...) 续读。直接文件描述符输出和超出捕获上限的部分会明确标记不完整;spill 不保证包含未捕获正文。输出文件独立于内核目录,因此内核退出后仍可读取,七天后清理。空闲内核采用默认4个的 LRU 和1800秒过期;正在运行或排队的 cell 不被容量淘汰,因此高并发时可暂时超过4个。后台回收不依赖下一次调用,宿主 TaskRegistry 管理任务,文件操作使用现有 Agent 有界线程池。
内核启动时继承宿主部署环境,包括 CONFIG_DIR、MOVIEPILOT_ROOT、DB_TYPE 和数据库连接凭据,使配置读取和内核中启动的脚本与 shell 通道一致;不继承宿主 PYTHONPATH,并固定 UTF-8 输出。环境变量不会自动输出给模型。环境或 app.env 变更后,应重置内核以重新加载配置。
任意 Python 与管理员 execute_command 一样具备宿主进程权限,只读 RPC 不是操作系统沙箱。因此普通用户、只读子代理和没有宿主 cell 上下文的外部 HTTP/MCP 直接调用不能使用这项能力;它不通过外部 tools/list 发布。POSIX 使用私有 Unix socket、随机令牌和父死亡管道,Windows 使用回环 TCP、随机令牌和父进程对象句柄。每个 cell 重新绑定当前上下文,结束时撤销并取消遗留 RPC,不把持久 socket 当作永久身份。当前只实现本地主机内核,没有远程终端后端。
验证范围
任务评测与离线轨迹验收的区别、场景及命令见 Agent 任务评测。固定响应单测不代表真实模型完成率。
自动化测试使用脚本模型和真实 LangGraph 执行图,覆盖工具发现和预算、计划恢复、结果状态、分页续读、持久认领和只读核验、执行中断以及连续两轮子代理委派。SQLite 测试覆盖并发认领、token 隔离、迁移和清理。测试不调用付费模型或外部服务。
这些机制提高执行可靠性;真实任务的理解、规划质量和成功率仍取决于所选模型、上下文窗口、工具权限及外部服务状态。模型能力需要用真实业务场景单独评估。