十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

screenpipe Pipe 执行可靠性架构解析:从内存态调度、边缘故障清单到 DB 持久化方案

screenpipe Pipe 执行可靠性架构解析:从内存态调度、边缘故障清单到 DB 持久化方案 screenpipe Pipe 执行可靠性架构解析从内存态调度、边缘故障清单到 DB 持久化方案【免费下载链接】screenpipeYC (S26) | Open Computer History | Record your screen continuously locally and provide context to your agents (Claude, Codex, Openclaw, Hermes, Runner...)项目地址: https://gitcode.com/GitHub_Trending/sc/screenpipe本篇技术指南围绕 screenpipe 的Pipe 执行可靠性规范Pipe Execution Reliability Spec展开梳理当前 Pipe 调度与执行架构的全部状态依赖、九大类边缘故障模型解析、电源事件、日志、进程管理、调度、配置、网络、聊天会话与 Tauri 生命周期的成因与修复要求并完整解读其提出的pipe_executions/pipe_scheduler_state两表持久化方案、执行状态机与 P0–P3 实施优先级。读者读完可以掌握 screenpipe 内部 Pipe 调度器的真实工作方式、所有已知可靠性短板以及仓库中已经落地的 SQLite 迁移、睡眠/唤醒监控、速率限制重试等实现的对应位置。一、当前架构一切状态都在内存里规范首先给出了当前实现的架构基线全部位于crates/screenpipe-core/src/pipes模块主要在 mod.rs所有 Pipe 状态都保存在进程内内存中典型结构是ArcMutexHashMap。规范中明确指出运行中的 Pipe 映射为running: ArcMutexHashMapString, ExecutionHandle对应 mod.rs。执行日志以 JSON 文件形式写入~/.screenpipe/pipes/{name}/logs/。一个全局信号量串行化所有 Pipe 执行Semaphore::new(1)在调度循环中即execution_semaphore Arc::new(tokio::sync::Semaphore::new(1))见 mod.rs。Pi Agent 子进程使用wait_with_output()且没有任何超时。对应实现位于 pi.rs即let output child.wait_with_output().await?;。PID 追踪存在缺陷spawn 时通过child.id()捕获了真实 PIDpi.rs但在 running map 中存入的是ExecutionHandle { pid: 0 }真实 PID 从未回写。注规范文件头部标注为Drifting2026-05-22 后未再全量核对并提示以crates/screenpipe-core/src/pipes为准可用bun scripts/check-doc-freshness.ts检查漂移程度。以下内容均以「规范描述 仓库现状对照」的方式呈现。单次 Pipe 运行会触及的状态依赖规范用一棵依赖树说明了「一次 Pipe 运行会触碰哪些可变状态」pipe.md (config prompt) ├── store.bin (AI preset → model provider) │ └── may be written by Tauri app concurrently ├── ~/.pi/agent/models.json (provider config, merged on every run) ├── ~/.pi/agent/auth.json (API keys, merged on every run) ├── pi binary (found via PATH/known locations) ├── screenpipe API at localhost:3030 (queried by pipe during execution) │ └── SQLite DB (OCR, audio, UI data the pipe reads) ├── LLM provider API (external network call) │ └── API key (from auth.json or SCREENPIPE_API_KEY env) ├── ./output/ directory (pipe writes results here) └── ./logs/ directory (execution logs written after completion)规范强调这些依赖中的每一个都可能在中途独立失败。这正是整份可靠性规范的存在理由——任何一环故障都不应让调度器崩溃或让用户面对无法理解的神秘失败。二、完整边缘案例清单Edge Case Inventory规范按九大主题A–I枚举了全部已知边缘场景每个场景都遵循「Current现状→ Required要求」的结构。以下完整继承并逐条展开。A. 模型与 Provider 解析Model Provider ResolutionA1. Pipe 运行时用户在 UI 切换模型现状preset 在排队时解析对应resolve_preset()调用点如 mod.rs运行中的执行不受影响。风险resolve_preset()读取store.bin时没有加锁。若 Tauri 恰好在调度器读取的瞬间写入会出现半读 → JSON 解析失败 → 返回None→静默回退到 pipe.md 默认值。要求原子化读取store.bin先读入缓冲区再解析或将 preset 也迁移到 SQLite。A2. 排队与执行之间模型被切换现状当前没有真正的队列preset 在executor.run()前才解析。但一旦引入 DB 队列Pipe 可能在队列中等待数分钟期间用户可能改了 preset。要求在排队时就把解析好的 model/provider 快照进执行记录行执行时使用快照而非重新实时解析。A3. Pipe 引用的 preset 被删除现状resolve_preset()返回None→ 静默回退到 pipe.md 的model:字段。用户以为在用 Claude Opus实际 Pipe 可能跑在 Haikupipe.md 默认值上。要求preset 不存在时直接失败给出明确错误Preset xyz no longer exists. Update pipe config or set a new default.。A4. 模型名拼写错误或 provider 上没有该模型现状Pi 子进程收到 LLM API 的错误stderr 被捕获但截断到 5KB记为泛化失败。要求从 stderr 解析常见 LLM API 错误model_not_found、invalid_api_key、rate_limited在 API 响应中输出结构化error_type让 UI 给出可操作的提示。A5. Provider 需要 API key 但 key 缺失/过期现状ensure_pi_config()会写入SCREENPIPE_API_KEY环境变量并合并 auth.json。但如果 screenpipe cloud token 过期Pi 收到 401stderr 显示 unauthorized用户看到的却是泛化失败。要求识别鉴权错误在执行错误中输出 API key expired 或 API key missing for provider X。A6. 自定义 Provider URL 变更或不可达现状Provider URL 来自 preset → models.json 合并。URL 不可达时Pi 会挂在 HTTP 超时上取决于 Pi 内部 HTTP 客户端可能是 30s–2min。要求这是通用超时问题的子集**执行超时提议 5 分钟**可以覆盖。A7. Ollama 模型未拉取 / 未运行现状providerollama 时若 Ollama 未运行或模型未拉取Pi 立即连接拒绝失败但错误埋在 stderr 里。要求预检pre-flight checkproviderollama 时先curl http://localhost:11434/api/tags确认可达再以结构化错误输出 Ollama not running 或 Model X not found in Ollama。A8.store.bin损坏 / 半写状态现状serde_json::from_str()返回Err→resolve_preset()返回None→ 静默回退。要求记录警告 store.bin is corrupted, using pipe defaults创建 bootstrap store.bin 时考虑原子写先写临时文件再 rename。A9. Provider 映射不完整现状只映射pi、native-ollama、openai、custom四种。任何其他 provider 字符串 →None→ 不传 provider 给 Pi → Pi 用自己的默认值。要求要么对未知 provider 显式失败要么原样透传给 Pi 自行处理。B. 计算机重启 / 睡眠 / 电源Computer Restart / Sleep / PowerB1. Pipe 运行时计算机重启现状进程被 OS 杀死内存态全部丢失没有任何执行记录Pipe 可能正写到一半输出文件 → 部分/损坏输出。要求DB 行statusrunning在重启后存续启动时检测孤儿行并标记为failederror interrupted by system restart检查部分输出文件并清理或标记不完整。B2. macOS 睡眠期间执行现状进程被 OS 挂起。唤醒后进程恢复但墙钟已跳变LLM API 连接大概率超时/关闭Pi 子进程可能收到 broken pipe 或 connection reset。要求唤醒后检查运行中执行是否存活PID 检查进程在睡眠期间死掉则标记 failed进程恢复但挂起唤醒后 60s 无输出则超时并 kill可考虑利用已有的sleep_monitor.rs检测睡眠/唤醒事件在睡眠前主动 kill 运行中的 Pipe。B3. macOS App Nap 节流 screenpipe现状App Nap 可能挂起 server 进程 → 调度器停摆 → Pipe 不按计划运行解除 Napping 后调度恢复但错过的窗口已消失。要求Tauri App 已通过NSProcessInfoactivity assertion 缓解但 CLIscreenpipe二进制没有此保护需在文档中说明 App Nap 可能影响调度可靠性。B4. 计算机时钟跳变NTP 同步、时区变化、夏令时现状调度器使用Utc::now()和Local::now()last_run是内存 HashMap。时钟向前跳 → 多个 Pipe 突然到期时钟向后跳 → 刚跑过的 Pipe 看起来没跑重复执行。要求间隔调度改用单调时钟last_run存 DB重启存活、单一事实来源catch-up 上限时钟跳变后若积压超过 3 次只补跑一次cron 调度仍用墙钟cron 的本义就是墙钟。B5. 笔记本合盖 → WiFi 断开 → LLM API 响应中途断连现状Pi 阻塞在 HTTP 读上最终 OS TCP 超时可能数分钟后 Pi 报错Pipe 失败。要求属于超时子集。5 分钟执行超时会在 TCP 超时前 kill 掉 Pipe但 LLM 的部分响应会丢失建议未来、较复杂Pi 支持部分工作 checkpoint。C. 日志与输出Logs OutputC1. 日志目录不存在现状std::fs::create_dir_all(log_dir)自动创建静默处理。状态OK。C2. 写日志时磁盘已满现状std::fs::write()返回 Err 被let _ 忽略mod.rs 附近日志丢失。要求至少用 tracing 打到 stderrDB 场景 SQLite 会返回 SQLITE_FULL需优雅处理、不崩溃。C3. 日志无限累积现状无轮转每次运行都在~/.screenpipe/pipes/{name}/logs/新建一个 JSON 文件数月后成千上万个小文件。要求DB 场景默认清理 30 天前的执行记录可配置启动时删除 7 天前的 JSON 日志内存缓存已封顶 50良好。C4. 日志中的敏感数据现状stdout/stderr 可能包含 OCR 文本、转录、个人数据以明文 JSON 文件存储。要求JSON 日志文件应设置受限权限0600DB 使用与主 screenpipe DB 相同的文件权限考虑日志脱敏选项剥离 API key、PII但复杂放 Phase 3。C5. 日志截断丢失关键调试信息现状stdout 截断到 10KB、stderr 到 5KB。Pi Agent 可能产生大量输出。要求DB 存完整输出SQLite TEXT 无实际限制API 分页获取输出GET /pipes/:id/executions/:exec_id/output?offset0limit10000仅对内存缓存与列表接口保留截断。C6. 崩溃/超时运行的输出文件现状Pipe 崩溃前可能已在./output/写入部分文件无清理下次运行可能追加或覆盖。要求每次执行写入带时间戳的子目录./output/{execution_id}/超时/崩溃时目录保留但标记不完整不自动删除——用户可能想要部分结果。C7. 并发日志写入现状手动运行与定时运行不会重叠semaphore1。但若信号量提高同一 Pipe 的两次运行可能写同一个日志目录。要求采用执行级输出目录见 C6日志写入 DB 行无文件冲突。C8. 无法将 Pipe 输出与 screenpipe 数据关联现状Pipe 带时间范围查询 screenpipe API输出写文件无法建立Pipe 看到了哪些帧/音频与它产出了什么之间的链接。要求未来在执行行中保存时间范围与查询参数实现展示本次 Pipe 运行基于哪些数据。D. 进程管理Process ManagementD1. PID 追踪错误现状child.id()在 pi.rs 捕获并放入AgentOutput.pid但ExecutionHandle { pid: 0 }在executor.run()之前就插入 running map对应 mod.rs 的running.insert(name.to_string(), handle)。真实 PID 只在run()返回后才可得——对取消操作来说太晚了。要求让AgentExecutor::run()通过回调/通道上报 PID或返回(PID, FutureAgentOutput)或拆分为spawn() → ExecutionHandle与wait(handle) → AgentOutputspawn 后立即把 PID 写入 DB 行。D2. Pi 派生子进程现状Pi 可能 spawn bash 命令、npm 进程等kill_process()只杀 Pi 自身 PID子进程成为孤儿。要求使用进程组cmd.process_group(0)Rust unstable或 Unixsetsid杀整个进程组kill(-pgid, SIGTERM)Windows 使用Job Objects分组子进程。D3. Kill 发送 SIGTERM 但进程无视现状只发一次kill -TERM无后续动作。若 Pi 卡在 syscall 里无视 SIGTERM进程永久运行。要求SIGTERM → 等 5s → SIGKILL无论 kill 是否成功都更新状态为cancelled或timed_out无论如何都释放信号量。D4. Pi 二进制在找到后又被移除现状is_available()在运行前检查find_pi_executable()见 pi.rs但检查与run()之间 Pi 可能被卸载bun uninstall、PATH 变化cmd.spawn()返回 Err。要求已处理——spawn()错误会传播到PipeRunLog { success: false }但错误信息应明确pi binary not found at {path}。D5. Pi 二进制版本不匹配现状无版本检查。Pi 通过bun add -g earendil-works/pi-coding-agent安装且未锁定版本自动更新可能破坏兼容性。要求在ensure_installed()中锁定 Pi 版本至少检查pi --version输出并在异常时告警。D6. 多个 screenpipe 实例同时运行 Pipe现状每个实例有独立的 PipeManager 和独立内存态两个实例可能跑同一 Pipe、spawn 重复 Pi 进程。要求基于 DB 的锁。执行开始前INSERT INTO pipe_executions带唯一约束或 advisory lock第二个实例得到冲突。E. 调度SchedulingE1. 排队期间调度被修改现状当前无队列调度器内联触发。引入队列后执行按旧调度排队用户改为 manual 或不同间隔。要求队列条目存储触发原因若 Pipe 被禁用或调度改为 manual出队挂起的运行。E2. 启用的 Pipe 其调度几小时前就到期现状last_run默认DateTime::UNIX_EPOCHmod.rs 附近可见unwrap_or(DateTime::UNIX_EPOCH)。因此启用 every 30m 的 Pipe 会立即触发一次运行。要求这其实是正确行为但应文档化可提供pipe.mdfrontmatter 的run_on_enable: false来抑制立即运行。E3. 两个定时触发快速连续发生现状调度器立即设置last_run然后 spawn 异步任务。若调度循环在 Pipe 启动前再次运行30s 粒度is_running检查防止重复。但存在竞态last_run.insert()先于running.insert()发生存在狭窄窗口使两次都通过运行检查。要求原子操作——在同一锁作用域内同时插入 running map 并更新 last_run。E4. 调度任务 panic现状tokio::spawn()的闭包 panic 后任务静默死亡Pipe 停止调度用户看不到任何错误。要求调度循环用catch_unwind包裹记录 panic重启调度器在 health 端点暴露。E5. 高频调度every 1m 慢 Pipe现状Pipe 跑 3 分钟信号量阻塞调度器每 30s 尝试都命中 already running 检查跳过完成后下个 tick 又跑实际上背靠背连续运行。要求基本可接受但浪费建议在 Pipe 持续慢于调度时记录警告pipe X takes avg 3min but is scheduled every 1min.。E6. Cron 表达式求值为过去时间现状should_run()比较now next_occurrence(last_run)。时钟向后跳时now可能早于下次出现时间 → Pipe 不运行。要求cron 基于墙钟此行为可接受文档化cron 调度依赖准确系统时钟。F. 配置与文件系统Config File SystemF1. Pipe 运行时 pipe.md 被修改现状配置在调度/运行时间加载执行已持有副本prompt 已渲染无冲突。但load_pipes()不会自动重读——Pipe 只在启动时加载一次mod.rs 区间只有显式 API 调用enable/disable/update才修改调度器用内存快照。要求文件 watcher 或每个调度 tick 重读或接受配置变更只能经 API 生效的现状并文档化。F2. pipe.md 的 YAML frontmatter 非法现状YAML 解析失败 →load_pipes()跳过该 Pipe无任何错误浮出。要求记录带 Pipe 名与解析错误的警告APIGET /pipes应包含带config_error字段的 Pipe。F3. Pipe 运行时其目录被删除现状Pi 子进程的 working_dir 指向 Pipe 目录若中途被删则文件操作失败Pi 可能崩溃。要求删除操作应先取消运行中的执行等待完成后再删除。F4. 输出目录权限现状./output/由 Pi 在执行期间创建若父目录权限错误则 Pi 失败。要求spawn Pi 前先ensure_dir_exists(pipe_dir.join(output))。F5. 从不信任 URL 安装 Pipe现状install_pipe()从 URL 拉取并写 pipe.md无校验、无沙箱。要求未来内容校验——pipe.md 必须有合法 frontmatter警告未知 agent不自动启用已安装的 Pipe。G. 网络与 API 依赖Network API DependenciesG1. Pipe 运行时 screenpipe API 尚未就绪现状Pipe prompt 中写死 Screenpipe API: http://localhost:3030Pi 查询之。若服务器仍在启动Pi 收到 connection refused失败。要求Pipe 调度器应仅在服务器开始监听后启动。目前调度器与服务器启动之间存在潜在竞态。G2. screenpipe API 端口不是 3030现状端口硬编码在render_prompt()中Screenpipe API: http://localhost:3030。用户跑在 3031 端口时 Pipe 查询错误端口。要求把实际服务器端口传给 PipeManager注入 prompt 模板。G3. LLM Provider 限流现状Pi 收到 429 后作为 stderr 返回记为失败。要求解析限流错误设置结构化error_type rate_limited考虑内置退避重试。G4. LLM 响应过大 / 畸形现状Pi 捕获全部 stdout若 LLM 产生垃圾输出编码问题、二进制输出stdout 可能巨大或不可解析。要求将 stdout 捕获限制在合理上限1MB?尽早检测二进制/非 UTF-8 内容。H. Pi 聊天会话交互式Pi Chat SessionsH1. 聊天会话进程追踪现状聊天通过 Tauri command spawn Pi进程由前端standalone-chat.tsx管理无服务端追踪。要求聊天会话与 Pipe 不同交互 vs 批处理保持分离但共享进程管理关注点PID、超时、kill考虑chat_sessions表或扩展pipe_executions加execution_type chat | pipe。H2. 聊天卡住 / 无响应现状用户无法知道 Pi 是在思考还是挂起无超时。要求心跳——Pi 无 stdout 达 60s 时 UI 显示 response may be delayed手动取消按钮真正 kill 进程需要可靠 PID服务端在独立 map 中追踪聊天 PID 并经 API 暴露。H3. 多个聊天会话现状多个聊天窗口会 spawn 多个 Pi 进程无协调。要求文档化一次一个聊天会话或追踪所有活跃聊天 PID 并在系统状态中展示。H4. 聊天与 Pipe 竞争资源现状聊天不使用 Pipe 信号量两者可同时 spawn Pi。要求决策聊天是否应尊重 Pipe 信号量大概率不应用户期望即时响应但需监控资源使用。I. Tauri App / 内嵌服务器Tauri App / Embedded ServerI1. Pipe 运行时 App 退出现状Tauri App 关闭会杀掉内嵌服务器内存态丢失Pi 进程是否被杀取决于进程组继承。要求优雅关闭——取消运行中的 Pipe最多等 5s强制 kill退出前向 DB 写最终状态。I2. Pipe 运行时 App 更新现状Updater 替换二进制重启后等同 B1计算机重启。要求更新前钩子取消运行中的 Pipe或 updater 等待 Pipe 完成带超时。I3. store.bin 被 Tauri 锁定现状resolve_preset()用std::fs::read_to_string()读 store.binTauri plugin-store 写同一文件无协调。要求Tauri plugin-store 内部用原子写写 tmp 再 rename但某些文件系统上 rename 期间读取仍可能看到部分内容修复JSON 解析失败时重试读取简单且覆盖该竞态。I4. 执行期间权限对话框阻塞现状macOS 可能弹出权限对话框录屏、麦克风阻塞线程。要求与 Pipe 无直接关系Pipe 查询 API 而非硬件。但若 screenpipe server 被权限对话框阻塞Pi 的 API 请求挂起 → 超时 kill。在有超时的情况下可接受。三、与仓库源码的对照验证哪些现状已经改变规范标注为 Drifting因此在阅读时需要结合仓库当前代码判断哪些条目已经落地。以下是可直接验证的事实1. 信号量与进程管理Semaphore::new(1)全局串行化执行在 mod.rs 仍然成立且还引入了event_semaphoremod.rs说明事件触发的执行也有独立信号量。进程组方面pi.rs 的注释确认子进程组与父进程共享 pidspawn 时用setsid()并在运行结束后调用reap_lingering_process_group(pid)清理残留孙进程pi.rskill_process_group()pi.rs实现了按进程组杀进程Windows 走taskkill /F /T /PID。这与 D2 的进程组 整组 kill要求方向一致。2. 限流重试已经落地规范 G3 建议内置退避重试而 pi.rs 中已存在is_rate_limit_error()、parse_rate_limit_reset_secs()与重试等待逻辑说明 Pi 执行器对 429 限流已有实际处理。3. 睡眠/唤醒监控已有完整实现规范 B2 提到的sleep_monitor.rs确实存在crates/screenpipe-engine/src/sleep_monitor.rs并已相当完善macOSCGSessionCopyCurrentDictionary每 2s 轮询锁屏 CFNotificationCenter事件驱动 NSWorkspacesleep/wake 通知WindowsPowerRegisterSuspendResumeNotification订阅挂起/恢复OpenInputDesktop每 5s 轮询锁屏并用时钟间隙检测唤醒Linux时钟间隙轮询推断挂起/恢复暴露recently_woke_from_sleep()、screen_is_locked()、system_is_suspended()等原子标志供捕获循环与调度侧停摆。 这为 B2 的检测睡眠/唤醒并主动处理提供了现成基础设施。4. 持久化表已经落地迁移文件即证据规范提议的pipe_executions与pipe_scheduler_state两表在 crates/screenpipe-db/src/migrations/20260213000000_create_pipe_executions.sql 中已按几乎相同的结构落地包含status默认queued、trigger_type、pid、model、provider、started_at/finished_at、stdout/stderr、exit_code、error_type、error_message、duration_ms以及同名的三个索引和pipe_scheduler_statepipe_name主键 last_run_at/last_success_at/consecutive_failures。此外 20260728000000_create_pipe_event_runs.sql 还追加了pipe_event_runs表用(pipe_name, event_name, event_key)主键 INSERT OR IGNORE实现事件触发运行的幂等性并为pipe_executions增加trigger_event/trigger_key列——这回应了规范 E1 中队列条目存储触发原因以及 C8 中记录数据上下文的方向。5. HTTP API 面crates/screenpipe-engine/src/pipes_api.rs 实现了 Pipe 管理 API列表、启停enable_pipe/stop_pipe、执行历史get_executions/get_execution、日志、安装与删除等其中trigger_type校验逻辑run_trigger_type只信任onboarding作为非手动触发scheduled会被降级为manual——与规范中触发原因可信性的主题一脉相承。四、提议 Schema两表分工的持久化方案规范给出了更新后的完整建表 SQL这是全文的核心交付物之一必须完整继承CREATE TABLE pipe_executions ( id INTEGER PRIMARY KEY AUTOINCREMENT, pipe_name TEXT NOT NULL, -- lifecycle status TEXT NOT NULL DEFAULT queued, -- queued → running → completed | failed | cancelled | timed_out queued_at TIMESTAMP NOT NULL DEFAULT (strftime(%Y-%m-%dT%H:%M:%fZ, now)), started_at TIMESTAMP, finished_at TIMESTAMP, -- process tracking pid INTEGER, -- OS process ID, set immediately after spawn -- execution context (snapshot at queue time — immutable after creation) agent TEXT NOT NULL DEFAULT pi, model TEXT NOT NULL, provider TEXT, preset_id TEXT, preset_snapshot TEXT, -- JSON: full preset config at queue time trigger TEXT NOT NULL, -- manual | scheduled | retry rendered_prompt TEXT, -- full prompt sent to agent (for debugging) api_port INTEGER, -- screenpipe port used in prompt -- output (updated during/after execution) stdout TEXT DEFAULT , stderr TEXT DEFAULT , exit_code INTEGER, error_type TEXT, -- structured: timeout | crash | rate_limited | -- model_not_found | auth_failed | network | -- cancelled | agent_not_found | NULL (success) error_message TEXT, -- human-readable error for UI -- metadata duration_ms INTEGER, retry_of INTEGER REFERENCES pipe_executions(id), retry_count INTEGER DEFAULT 0, -- data context (what screenpipe data window this execution covered) context_start TIMESTAMP, -- start of time range in rendered prompt context_end TIMESTAMP -- end of time range in rendered prompt ); CREATE INDEX idx_pe_name_status ON pipe_executions(pipe_name, status); CREATE INDEX idx_pe_running ON pipe_executions(status) WHERE status running; CREATE INDEX idx_pe_name_time ON pipe_executions(pipe_name, queued_at DESC); -- Scheduler state (persisted across restarts) CREATE TABLE pipe_scheduler_state ( pipe_name TEXT PRIMARY KEY, last_run_at TIMESTAMP, last_success_at TIMESTAMP, consecutive_failures INTEGER DEFAULT 0 );为什么是两张表pipe_executions是append-only每次运行一行pipe_scheduler_state是每 Pipe 一行、原地更新。分离的好处调度器只读一张小表来决定下一步跑什么执行历史查询扫描另一张独立表无需在每个调度 tick 上执行SELECT MAX(queued_at) FROM pipe_executions GROUP BY pipe_name。值得注意的设计点preset_snapshot/rendered_prompt/api_port直接回应 A2排队时快照、G2注入实际端口而非硬编码 3030与 C8记录数据上下文——执行记录不可变事后可完整复现这次运行到底用了什么配置、看了什么数据窗口。error_type枚举与 A4、A5、G3 的结构化错误要求一一对应timeout、crash、rate_limited、model_not_found、auth_failed、network、cancelled、agent_not_found成功则为 NULL。retry_of/retry_count支撑状态机中的自动重试P2 的 retry with backoff。部分索引idx_pe_name_status、idx_pe_running部分索引、idx_pe_name_time已在迁移文件中落地说明这套查询路径已被实际使用。五、执行状态机State Machine规范为每行执行定义了明确的生命周期┌─────────┐ queue │ queued │ ────────► │ │ └────┬────┘ │ semaphore acquired, spawn process ▼ ┌─────────┐ │ running │──── PID set in DB │ │──── stdout/stderr streaming to DB └────┬────┘ ╱ │ ╲ ╱ │ ╲ ▼ ▼ ▼ ┌──────┐ ┌──────┐ ┌──────────┐ │compl.│ │failed│ │timed_out │ └──────┘ └──┬───┘ └──────────┘ │ retry_count max? yes → new row (triggerretry, retry_ofid) no → stay failed At any point from queued or running: user cancel → cancelled server shutdown → failed (error_typeshutdown) sleep/crash → failed (error_typeinterrupted, detected on startup)关键语义queued → running的唯一入口是信号量获取成功 spawn 进程running期间持续做两件事PID 写入 DB对应 D1 的修复与stdout/stderr 流式写入 DB对应 P2failed状态下若retry_count max则新建一行triggerretry、retry_of原id否则保持 failed从queued或running随时可中断用户取消 →cancelled服务器关闭 →failederror_typeshutdown睡眠/崩溃 → 启动时检测为failederror_typeinterrupted。六、实施优先级P0 → P3规范以「阻塞关系」为主线排定了实施顺序这是团队对先修什么的明确决策PhaseWhatBlocksP0Execution timeout默认 5min 进程组 kill一切——没有它一个挂死的 Pipe 整个系统死机P0修复 PID 追踪executor 拆分为 spawnwait立即把 PID 写入 running map取消操作P0pipe_executionspipe_scheduler_state表重启恢复、可观测性、一切P0启动时孤儿恢复干净状态保证P1排队时快照 preset/model用户改设置后仍正确执行P1向 prompt 注入实际 API 端口而非硬编码 3030非默认端口的正确性P1结构化错误类型解析 stderr 中的常见失败UI 可操作的错误提示P1调度状态入 DBlast_run、consecutive_failures重启存活、防止重复运行P1优雅关闭App 退出时取消 Pipe干净退出P2执行期间流式写 stdout/stderr 到 DB实时可见性P2WebSocket 实时输出跟随用户实时观察 Pipe 运行P2带退避的重试处理瞬时 LLM 失败P2API 中的队列位置用户知道自己在哪P3执行级输出目录干净的部分输出处理P3日志清理30 天保留磁盘管理P3并发执行semaphore 1性能P3预检Ollama 可达、模型存在更好的 UX对照仓库现状可以确认的进展P0 的超时 进程组 kill进程组 kill 已有实现kill_process_group/reap_lingering_process_groupD3 提出的SIGTERM → 5s → SIGKILL级联在 pi.rs 中已有按 PID 杀进程组的代码路径handle.current_pid()kill_process_group(pid)。P0 的持久化表迁移文件 20260213000000_create_pipe_executions.sql 已落地pipe_scheduler_state也被调度循环实际使用mod.rs 在首次 tick 从 DB 加载last_run。P1 的调度状态入 DB同样已随迁移落地且advance_scheduler_last_runmod.rs将last_run_at持久化到pipe_scheduler_state。P3 的预检仍是未来项但 P0/P1 的多数基础设施已就位说明规范的整体演进路径与实际代码保持了一致方向。七、总结docs/PIPE_EXECUTION_SPEC.md是一份典型的「现状审计 目标架构」型可靠性规范它先如实记录内存态调度、无超时子进程、PID 追踪缺陷等现实约束再以九大类、数十个边缘场景枚举故障模式并给出可落地的修复要求最终收敛为两表持久化 Schema、状态机与分阶段实施计划。结合仓库源码可以看到规范中的核心结论已经部分兑现——pipe_executions/pipe_scheduler_state迁移、睡眠/唤醒监控、进程组 kill、限流重试等均已存在而执行超时默认 5 分钟排队时 preset 快照结构化 error_type执行级输出目录等仍是值得继续推进的方向。对于希望深入理解 screenpipe 调度器内部机制或为其贡献可靠性改进的开发者这份规范连同 mod.rs、pi.rs 与 pipes_api.rs 构成了完整的阅读路径。【免费下载链接】screenpipeYC (S26) | Open Computer History | Record your screen continuously locally and provide context to your agents (Claude, Codex, Openclaw, Hermes, Runner...)项目地址: https://gitcode.com/GitHub_Trending/sc/screenpipe创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表