xiaoxixi d9ad58b84b feat(scheduler): unify scheduled task execution and delivery
Replace the dual task/monitor model, NO_REPLY string protocol, and Agent
self-delivery with a single Scheduled Run path: claim-time JobRun snapshots,
isolated Root/named Agent execution, exactly-once complete_scheduled_run
termination, and Scheduler-owned policy delivery through a persistent outbox.

- SQLite v11: drop job_kind/model/delete_after_run, add job_runs with
  status/outcome joint constraints and delivery lease columns; one-shot
  BEGIN IMMEDIATE migration with atomic rollback.
- Non-blocking JoinSet event loop with bounded run/delivery concurrency;
  terminal commit before any channel I/O; recover unfinished runs as unknown.
- ExecutionOrigin::Scheduled propagates to descendants, completion sink is
  top-level only, background delegation downgrades to foreground.
- Typed delivery receipts, fixed target_session_id, idempotent
  scheduled:<job_run_id> history insert.
- New cron_runs read-only tool; cron_add/update drop kind/model; WebUI and
  Health consume the same JobRun projection.
- Bump version to 1.22.0.
2026-08-21 14:59:02 +08:00

213 lines
15 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.

# PicoBot 配置说明
配置文件加载顺序:`~/.picobot/config.json` → 当前目录 `./config.json`
占位符 `<VAR_NAME>` 从启动环境替换。PicoBot 依次加载 `config.json` 同目录的 `.env``workspace_dir/.env`最后保留启动进程已有环境变量作为最高优先级workspace 层覆盖配置目录层。合并值也会进入进程环境,供 MCP 和工具子进程继承。workspace `.env` 不能修改用于定位自身的 `workspace_dir`
Gateway WebUI 的“配置”页可以编辑实际加载的配置文件。读取时 API Key、secret、password 和 token 会显示为 `********`保持掩码不变再保存会保留原值写入采用同目录临时文件替换。Gateway 会忽略可恢复的历史未知字段、类型不匹配字段和失效的非核心命名条目,在配置页按 JSON Pointer 显示诊断,但不会自动改写原始文件;“一键清除”由后端校验文件 revision 后原子删除已诊断项普通保存仍严格拒绝无效配置。JSON 损坏、不可用的 `default` Agent 链路和无法安全构造运行代的错误不会被忽略。运行配置保存或清理后可执行 `picobot reload`、发送 `/reload`,或由根交互 Agent 在用户明确要求时调用 `reload_config` 工具。Gateway 会先校验候选配置,停止接收新工作并等待交互 Turn、Scheduler job 和后台子 Agent 到达安全边界后切换;失败时继续使用旧配置。`GET /api/config/reload/status` 可查询 generation、相位与最近错误。`gateway.host``gateway.port``workspace_dir``gateway.session_db_path` 的有效路径必须通过完整重启变更。`USER.md``AGENTS.md` 的修改用于后续构建的 Agent 上下文。
## config.json 结构
```jsonc
{
"providers": {}, // LLM 提供商配置
"models": {}, // 模型配置
"agents": {}, // Provider/Model profile
"context_compaction": {}, // 上下文 reserve 预算与近期保留量
"agent_orchestration": {}, // 具名子 Agent Definition 与编排上限
"gateway": {}, // 网关配置
"client": {}, // 客户端配置
"channels": {}, // 渠道配置
"memory": {}, // 记忆系统配置
"workspace_dir": "", // 工作目录,默认 ~/.picobot/workspace
"mcp": {}, // MCP 服务器配置
"browser": {} // 可选浏览器自动化配置
}
```
完整示例见 `assets/config.example.json`
## providers 字段
| 字段 | 说明 |
|------|------|
| `type` | 提供商类型: `openai`(兼容 OpenAI API`anthropic` |
| `base_url` | API 端点地址 |
| `api_key` | API 密钥,支持 `<ENV_VAR>` 占位符 |
| `extra_headers` | 额外 HTTP 头 |
## models 字段
| 字段 | 说明 |
|------|------|
| `model_id` | 模型标识名称 |
| `temperature` | 采样温度,可选 |
| `max_tokens` | 最大输出 token 数,可选 |
| `token_limit` | 模型上下文窗口硬上限,可选;未配置时默认为 128000Agent 只能进一步收紧 |
| `input_type` | 模型支持的输入类型,如 `["text"]``["text", "image"]`,默认 `["text"]`. 纯内部使用,不会传递给 LLM API |
## agents 字段
| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `provider` | string | - | 提供商名称(对应 providers key |
| `model` | string | - | 模型名称(对应 models key |
| `max_tool_iterations` | int | 99 | 最大工具调用轮数 |
| `token_limit` | int | 使用模型上限 | 可选的 Agent 上限;有效窗口取 Agent 与模型(模型未配置时为 128000的最小值 |
## context_compaction 字段
自动压缩只使用 reserve 公式 `context_tokens > context_window - effective_reserve`。小窗口下 `effective_reserve = min(reserve_tokens, context_window / 2)`,近期原样保留量最多为有效阈值的一半。
| 字段 | 默认 | 说明 |
|------|------|------|
| `enabled` | true | 只控制 Turn 前自动压缩;不禁用 `/compact` 或 overflow 恢复 |
| `reserve_tokens` | 16384 | 为输出、工具迭代和估算误差预留的输入窗口 |
| `keep_recent_tokens` | 20000 | checkpoint 后尽量原样保留的近期历史 token |
## agent_orchestration 字段
子 Agent 编排是 PicoBot 的内在机制,始终启用、不可关闭;该配置块只控制定义目录与各类上限。`definitions_dir` 相对 `config.json` 所在目录解析,且不得通过绝对路径或 symlink 逃逸该受信任配置目录。Gateway 启动和热重载会严格校验全部 Markdown Definition任一无效 Provider profile、工具、Skill 或委托目标会拒绝整个候选运行代。
| 字段 | 默认 | 说明 |
|------|------|------|
| `definitions_dir` | agents | 第一层 `*.md` Definition 目录 |
| `max_tree_depth` | 4 | Root-relative 委托深度硬上限 |
| `max_runs_per_tree` | 16 | 单任务树 run 预算(树级原子计数强制) |
| `max_concurrent_runs` / `max_concurrent_runs_per_session` | 6 / 4 | background run 接纳配额global→session 顺序获取runner 持有至 terminal commitforeground 不占) |
| `max_concurrent_provider_steps` / `..._per_session` | 8 / 4 | Provider step 上限global→session |
| `max_concurrent_tool_steps` / `..._per_session` | 16 / 8 | 普通工具 step 上限global→session |
| `max_pending_inbox_events_per_session` | 128 | durable inbox 容量(条件更新,预留槽不可被信号挤占) |
| `inbox_event_ttl_hours` | 168 | inbox 事件 TTL已预留配置TTL 清理尚未实现) |
| `max_inbox_delivery_attempts` | 8 | inbox 最大投递次数,超限 dead-letter |
| `max_user_turn_burst_before_inbox` | 4 | 用户 Turn 公平调度阈值 |
| `max_inbox_wait_secs` | 30 | inbox 最大等待阈值 |
已实现:具名 foreground 与 backgroundRoot 单任务或批量)、内联 `provider`/`model``llm_profile`、工具集由定义文件 `tools` 决定、批量并发、父子委托边校验、durable inbox continuation空闲时完成即返回`emit_signal`queue/steer、run quota 与 step gate、内置 general-purpose 定义与 WebUI「子 Agent」管理页。未开放子 Agent 发起的 background、`idempotency_key` 工具入口。旧匿名 general 兼容路径已移除,委托必须指定具名 `target`
## gateway 字段
| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `host` | string | 127.0.0.1 | 监听地址 |
| `port` | int | 19876 | 监听端口 |
| `require_pairing` | bool | true | 是否要求 WebUI 与 CLI 设备先使用一次性代码配对 |
| `session_ttl_hours` | int | - | 兼容/预留字段;当前没有会话 TTL 清理循环 |
| `session_db_path` | string | - | SQLite 数据库路径,默认在配置目录 `data/` 下 |
| `cleanup_interval_minutes` | int | - | 兼容/预留字段;当前没有按此间隔运行的 session 清理任务 |
| `scheduler` | object | - | 调度器配置 |
### gateway.scheduler 字段
| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `enabled` | bool | true | 是否启动调度器并注册 cron 工具 |
| `poll_interval_secs` | int | 60 | 检查到期任务的轮询间隔 |
| `max_concurrent` | int | 1 | 同时执行的 Scheduled Run 上限,运行时限制在 1256投递使用独立有界并发 |
| `execution_timeout_secs` | int | 900 | 单个定时任务 Agent 执行的硬超时Job 执行租约额外覆盖关停宽限,投递由持久化 outbox 独立恢复 |
## memory 字段
| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `consolidation_provider` | string | 主 Agent provider | 当前记录在 MemoryManager 中供后续归并使用;压缩摘要仍使用 Session provider |
| `consolidation_model` | string | 主 Agent model | 当前记录在 MemoryManager 中供后续归并使用;压缩摘要仍使用 Session model |
| `recall_limit` | int | 5 | 预期的每轮知识召回上限;当前 worker 固定使用 5 |
| `idle_consolidation_minutes` | int | 10 | 预留的空闲归并阈值;当前无对应循环 |
| `timeline_retention_days` | int | 90 | 默认日常维护巡检删除超过该期限的 TimelineKnowledge 不受影响 |
| `max_failures_before_degrade` | int | 3 | 预留的归并失败阈值;当前无失败降级循环 |
注意:当前 worker 的 Knowledge 召回数量仍固定为 5idle consolidation 和失败降级循环尚未接入。Timeline 清理由默认启用的 `picobot-routine-maintenance` Scheduled Run 执行;该任务使用 `never` 策略,结构化结果只进入运行审计和 Health。
## channels.feishu 字段
| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `enabled` | bool | false | 是否启用 |
| `app_id` | string | - | 飞书应用 ID |
| `app_secret` | string | - | 飞书应用密钥 |
| `allow_from` | []string | ["*"] | 允许交互的用户列表 |
| `require_mention` | bool | true | 群聊中是否必须明确 @ 机器人;无法解析机器人身份时安全地忽略群消息 |
| `agent` | string | - | 使用的 agent 名称 |
| `media_dir` | string | ~/.picobot/media/feishu | 配置默认值Gateway 注册渠道时会覆盖为 `{workspace}/media/feishu` |
| `reaction_emoji` | string | "Typing" | 回复意向表达的表情 |
| `live_updates` | bool | false | 是否用单张卡片实时编辑活动 Turn关闭时只发送终态 |
| `live_update_interval_ms` | int | 500 | 卡片更新最小间隔,运行时限制在 2505000ms |
| `max_image_bytes` | int | 10485760 | 单个入站/出站图片的最大字节数 |
| `max_file_bytes` | int | 26214400 | 单个入站/出站文件、音频或视频的最大字节数 |
| `media_dir_max_bytes` | int | 536870912 | 飞书媒体目录容量上限;达到上限后拒绝新下载,不自动删除旧文件 |
| `request_timeout_secs` | int | 30 | 单次飞书 HTTP 请求及响应体读取的硬超时,运行时限制在 5120 秒 |
飞书属于外部渠道:无论是否开启实时卡片,都不会接收模型 reasoning工具只显示紧凑状态。渠道配置可通过 Gateway 配置重载生效。
## mcp 字段
| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `servers` | array | [] | MCP 服务器列表 |
| `tool_timeout_secs` | int | 180 | 工具调用超时秒数 |
MCP 服务器单条配置:
| 字段 | 说明 |
|------|------|
| `name` | 服务器名称 |
| `enabled` | 是否启用,默认 true关闭后启动/重载时不连接该服务器 |
| `transport` | 传输方式: `stdio``sse``streamable-http` |
| `command` | 启动命令stdio 模式) |
| `args` | 命令参数 |
| `env` | 子进程环境变量 |
| `url` | URLsse / streamable-http 模式) |
| `headers` | HTTP 传输额外请求头 |
| `tool_timeout_secs` | 单独的超时设置 |
| `tool_settings` | 按 MCP 原始工具名索引的本地执行属性声明;每项可设 `read_only``exclusive`,均默认 false |
`tool_settings` 示例:
```json
{
"tool_settings": {
"read_file": { "read_only": true },
"refresh_index": { "exclusive": true }
}
}
```
`可并发` 不需要也不能单独配置:它始终等于 `read_only && !exclusive`。未声明的 MCP 工具按可能有副作用且顺序执行处理。WebUI「工具 → MCP」展开服务器后可编辑这些复选框编辑先暂存可跨多个工具一次“保存并应用”并热重载离开 MCP 标签或刷新页面会丢弃未保存草稿。
## browser 字段
浏览器工具默认开启并注册 `browser``browser_profiles` 工具。缺少外部依赖不会阻止 Gateway 启动,但实际调用会返回安装错误,`picobot health` 会提前判定。上层由 PicoBot 管理浏览器生命周期与媒体,底层调用 agent-browser JSON CLI不再依赖 Fantoccini、ChromeDriver 或 WebDriver。
| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `enabled` | bool | true | 是否启用浏览器工具;关闭后不注册 `browser` |
| `command` | string | agent-browser | CLI 名称或绝对路径 |
| `headless` | bool | true | 是否无头运行 |
| `browser_executable_path` | string | - | 自定义 Chrome/Chromium 可执行文件路径 |
| `max_sessions` | int | 4 | 同时保留的普通 dialog 临时浏览器会话上限;持久身份不计入 |
| `idle_timeout_secs` | int | 3600 | 大于零;普通 dialog 临时浏览器的空闲退出及 Manager 回收时间;持久浏览器不使用该超时 |
| `command_timeout_secs` | int | 120 | 单次 CLI 调用硬超时 |
| `max_output_chars` | int | 50000 | 页面来源文本输出上限 |
| `content_boundaries` | bool | true | 启用 agent-browser 不可信页面边界元数据 |
| `allowed_domains` | []string | [] | 可选域名白名单;空数组表示不启用域名限制 |
| `allow_private_hosts` | bool | false | 是否允许回环、私网和本地域名 |
| `artifact_dir` | string | ~/.picobot/media/browser | 截图产物目录 |
| `persistence.profile_dir` | string | ~/.picobot/browser/profiles | 持久 ID、语义标签和 Profile 目录的受控根目录 |
持久化不是配置模式。`browser` 调用省略 `persistent_id` 时使用当前 dialog 的普通临时浏览器,并在达到 `idle_timeout_secs` 后自动退出长期工作需要保持浏览器进程或保留登录和站点状态时Agent 可自主调用 `browser_profiles(create,label=...)` 生成 `picobot-profile-<uuid>`,对应数据位于 `profile_dir/<id>`,然后在该工作的后续每个 action 中持续传入同一个 ID。持久浏览器禁用 daemon 空闲自动退出,只通过显式 `browser(close)` 或 Profile 删除关闭。没有默认 ID也不会按 dialog 自动选择持久身份。同一 ID 跨 dialog 共享底层 session 与串行锁,不同 ID 各自独立并可并发。`browser_profiles` 还支持 `set_label``list` 和使用精确 ID 的 `delete`;标签可重命名,但选择浏览器始终使用不可变 ID。
agent-browser 0.33.0 不允许持久 Profile 与 `allowed_domains` 同时使用。配置域名限制后普通临时浏览器仍可用创建或使用持久身份会被拒绝health 会提示该可选能力受限。Profile 包含登录凭据,应把目录视为敏感数据,不得提交到版本控制、跨用户共享或放在不受信任的网络文件系统中。
旧字段 `webdriver_url``chrome_path` 不再接受。推荐安装 `agent-browser@0.33.0` 后运行 `agent-browser install`Linux 可运行 `agent-browser install --with-deps`。使用前用 `picobot health` 检查 CLI、浏览器安装和真实 headless 启动环境。
### 浏览器依赖故障处置
| health / 工具错误 | 处置 |
|---|---|
| `agent-browser` 未找到 | 运行 `npm install -g agent-browser@0.33.0`,或 `cargo install agent-browser --version 0.33.0 --locked` |
| CLI 已安装但找不到 Chrome/Chromium | 运行 `agent-browser install`;已有浏览器则设置 `browser_executable_path``AGENT_BROWSER_EXECUTABLE_PATH` |
| Linux 缺少共享库/系统包 | 运行 `agent-browser install --with-deps`,然后再运行 `agent-browser doctor` |
| 安装状态不明确 | 先运行 `picobot health` 获取 PicoBot 视角的结果,再运行 `agent-browser doctor` 查看完整上游诊断 |
不得在 Agent 工具调用中自动安装或执行 `doctor --fix`;安装会修改系统且可能需要管理员权限,应把命令报告给用户,由用户确认后执行。临时不需要浏览器时可设置 `browser.enabled=false`,此时 health 不要求 agent-browser/Chrome。