Files
MoviePilot/docs/agent-execution.md
JontyLee f2918b9285 fix(scheduler): 让声明为不限时的协程任务预算真正生效
JobSpec.timeout_seconds 此前无法表达“显式不限时”:所有未声明预算的协程任务
一律补齐 SCHEDULER_JOB_TIMEOUT,订阅搜索队列因此被 3600 秒总时长判死,而它的
托管协程消费者无法靠取消收尾,只会把正常长轮次记成失败。现在收敛为三态语义
(正数为上限、0 为显式不限时、缺省继承默认),并修正 asyncio 超时异常 str()
为空导致的日志、系统通知与 SystemError 事件文本缺失。
2026-10-26 10:34:42 +08:00

64 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 智能助手执行优先策略
交互轮次从宿主 `MoviePilotAgent.process` 进入,到本轮推理及工具调用收口后结束。
网页、消息渠道、CLI 和兼容 API 的前台请求使用交互容量;自主定时任务与心跳保持后台归属,
即使配置了结果回传渠道也不会升级为交互轮次。
默认启用 `AGENT_INTERACTIVE_PRIORITY=true`。这是协作式资源隔离:正在运行的同步线程、
数据库事务和后台协程不会被抢占。主事件循环仍采用 asyncio 的协作调度,
因此同步代码阻塞事件循环、CPU 饱和、数据库锁竞争和模型网络延迟仍可能影响响应。
| 执行边界 | 交互容量 | 普通后台容量 |
| --- | --- | --- |
| `ThreadHelper.submit` | 独立 4 线程 | 保留现有 `CONF.threadpool` |
| 宿主 `run_in_threadpool` | 当前事件循环共享 4 个 anyio 令牌 | 独立后台 limiter,容量跟随 anyio 默认配置(通常 40) |
| `DatabaseWorker.run` | 独立 1 线程、运行及排队总计 8 项 | 保留 4 线程、总计 32 项默认值 |
| Agent 同步工具 | 各能力域独立的交互桶 | 保留原能力域限额,供自主任务使用 |
| APScheduler | 自主 Agent 使用 `agent` 执行组,2 线程 | 其他任务使用原 `default` 组 |
以上容量按需启动工作线程,关闭时沿用各自生命周期 owner 的封口和收敛合同。
取消不会提前归还仍在执行的同步工具、线程调用或数据库事务的容量。
聊天持久化的全局写入准入也区分交互和后台;同一会话的写锁及单会话上限继续共享,
以免并发覆盖历史。`DatabaseWorker.snapshot()` 返回普通组,`interactive_snapshot()` 返回保留组;
数据库队列和执行指标包含两组的增减量。
执行归属通过上下文复制传入线程。`moviepilot_api` 的本机 HTTP 调用携带本进程随机登记的
临时执行凭据,由 ASGI 中间件恢复;普通自填请求头或已经结束的轮次无法取得优先级。
用户令牌与权限检查照常执行。子进程、外部 MCP 服务和插件自建线程池不受本策略控制。
框架同步鉴权与同步端点保留 anyio 默认 limiter,宿主后台线程调用不再消耗该组令牌;
框架自身的 HTTP 请求之间仍共用默认配额,不能保证在任意 HTTP 洪峰下立即响应。
## 后台避让
交互活跃时,新进入调度执行入口的非关键自动任务最多等待
`AGENT_BACKGROUND_DEFER_SECONDS` 秒,默认 30 秒,设为 `0` 禁用等待。
轮次结束后等待任务继续执行;持续对话超过预算时也会继续,避免后台任务饥饿。
期间保留原任务 generation、重入保护和协程句柄,不重复注册或补投任务。
调度器明确使用合并过期触发、单实例和无限 misfire 宽限,避免有限避让产生的排队
被默认一秒宽限误判为过期丢弃;任务自己的重入保护继续生效。
调度服务列表以“等待交互结束”显示正在避让的任务,API 的 `execution_group` 返回实际执行组。
Outbox 补偿、安全检查、数据库维护、运行时维护和手动专用任务不避让。
交互主动触发的同步调用不会等待自己结束;业务调度函数执行时会撤销继承的交互归属。
已有主循环同步入口沿用原调用合同,不在事件循环中同步等待。
避让只作用于新进入执行入口的任务,不会暂停或重放已开始的业务操作。
APScheduler 3 的标准线程池没有任意数值 `priority` 的调度能力。
这里通过独立执行组和实际准入等待实现隔离,不向 `add_job` 传入无效的优先级参数。
独立调度执行组仅隔离触发线程,协程仍回投到同一主循环。
## 超时
- `AGENT_LLM_TIMEOUT`:单次根 Agent 模型请求总预算,默认 180 秒,覆盖流式与非流式生成
以及该请求的供应商重试;`0` 表示不限时。工具执行时间由原 `LLM_TOOL_TIMEOUT` 管理。
- `SCHEDULER_JOB_TIMEOUT`:协程调度任务的默认执行预算,默认 3600 秒,等待避让不计入执行时间;
`0` 表示不限时。显式 `JobSpec.timeout_seconds` 优先于此默认值:正数是总时长上限,`0`
表示该任务显式不限时,缺省才继承此默认值。
超时通过协程取消收口,不能杀死同步线程或撤销已经发生的外部副作用。
数据库和线程调用仍等待实际操作收尾后释放容量,因而收尾时间可能超过超时预算。
超时后应先查询操作状态,再决定是否重试写入。
关闭 `AGENT_INTERACTIVE_PRIORITY` 可让 Agent 使用普通执行容量并停用避让;两项超时配置独立生效。
这些配置由部署环境或 `app.env` 提供,修改后重启生效。