8.3 KiB
Agent 复杂任务执行与恢复
MoviePilot Agent 通过模型、Skills、工具和会话状态共同完成任务。本次增强关注长任务的连续性:明确目标和检查结果,按需补充工具,并在执行中断后保留已知事实。
任务计划
复杂任务可以使用内部 update_plan 工具保存目标、步骤、状态、检查依据和阻塞原因。例如“排查下载后未入库”可以依次检查下载状态、整理队列、目标目录和媒体库。
- 简单查询不需要创建计划。
- 步骤支持
pending、in_progress、completed、blocked;完成和阻塞状态需要填写依据。 - 计划单独保存在会话图状态中,每次模型请求都能读取;压缩旧消息不会删除计划。
- 计划快照随聊天消息保存,在会话图重建后恢复。用户询问进度时继续当前目标,明确开始新任务时替换计划。
- 计划是模型记录的工作状态,不代表工具执行结果或写操作授权。仍需通过工具核实实际结果。
按需发现工具
设置 LLM_MAX_TOOLS > 0 时,Agent 首轮仍只筛选一批相关工具。后续读取 Skill 或发现新问题后,可以通过内部 search_tools(search, limit) 按名称、说明或标签搜索当前会话工具目录,并在下一次模型调用获得匹配工具的完整参数定义。
每次最多返回 8 个匹配工具,并保留最近 16 个额外工具候选;再次发现会刷新候选优先级。匹配支持工具精确名称和有限中英文能力别名,例如“整理”对应 transfer。没有匹配结果时不会展开全部工具。
额外工具的参数定义共享最多 4096 tokens、且不超过已知模型窗口 10% 的预算,总输入保留 15% 余量。搜索先报告候选,下一次模型调用根据统一预算明确报告实际启用和未启用原因。初选、常驻和供应商工具保持可用。发现状态仅作用于当前用户请求,新的用户请求重新筛选。LLM_MAX_TOOLS 约束首轮筛选数量;发现不会安装工具、连接新的 MCP 服务或扩大执行权限。
update_plan、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,并保留指定工作目录。
写工具的持久执行记录
生产 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 的最终请求预算和上下文压缩机制。长调查可以压缩旧消息;最新单条工具事务或固定提示本身超过模型窗口时仍会明确失败,不会静默删除关键内容。
验证范围
任务评测与离线轨迹验收的区别、场景及命令见 Agent 任务评测。固定响应单测不代表真实模型完成率。
自动化测试使用脚本模型和真实 LangGraph 执行图,覆盖工具发现和预算、计划恢复、结果状态、分页续读、持久认领和只读核验、执行中断以及连续两轮子代理委派。SQLite 测试覆盖并发认领、token 隔离、迁移和清理。测试不调用付费模型或外部服务。
这些机制提高执行可靠性;真实任务的理解、规划质量和成功率仍取决于所选模型、上下文窗口、工具权限及外部服务状态。模型能力需要用真实业务场景单独评估。