screenpipe 健康检查与故障诊断实战:从进程探测到视觉/音频流水线的完整排查手册
【免费下载链接】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 是一款持续在本地录制屏幕画面与音频、并把它们转化为可检索上下文(供 Claude、Codex 等 Agent 使用)的开源工具。本文基于仓库内的screenpipe-health技能文档,系统讲解如何用一套可复制的命令检查 screenpipe 的进程状态、API 健康度、磁盘占用、数据库完整性与音频/视觉流水线指标,并结合仓库源码(Rust 实现与测试)说明每个健康信号背后的判定逻辑。读完本文,你将掌握从"进程是否存活"到"音频为什么没有转写"的端到端排查能力。
快速健康检查:五步确认核心运行状态
1. 检查 screenpipe 是否在运行
# 列出所有 screenpipe 相关进程 pgrep -fl screenpipe # 探测本地 HTTP API 是否响应(默认端口 3030) curl -s http://localhost:3030/health | head -100pgrep -fl会同时输出进程 PID 与完整命令行;curl /health是判断服务是否可用的黄金标准。端口 3030 是 screenpipe 服务端的默认监听端口,从源码看,健康检查端点同时提供了 HTTP 与 WebSocket 两种形态(/health与/ws/health),见 crates/screenpipe-engine/src/server.rs 中的路由注册。
2. 检查录制状态
# 提取健康响应中的视觉与音频状态字段 curl -s http://localhost:3030/health | jq '.frame_status, .audio_status' 2>/dev/null || curl -s http://localhost:3030/health如果系统没有安装jq,命令会自动回退到直接打印完整 JSON。frame_status描述屏幕录制流水线状态,audio_status描述音频流水线状态。
3. 检查磁盘占用
# screenpipe 数据目录总大小 du -sh ~/.screenpipe/ # 数据库文件大小 ls -lh ~/.screenpipe/db.sqlite* 2>/dev/null # 视频/音频缓存目录大小 du -sh ~/.screenpipe/data/ 2>/dev/nullscreenpipe 默认把所有数据存放在~/.screenpipe/:db.sqlite(以及 WAL 模式下的db.sqlite-wal、db.sqlite-shm)是核心 SQLite 数据库,data/目录存放录制的媒体缓存。db.sqlite*通配符可以一并看到 WAL 与 SHM 文件的大小。
4. 检查今日错误日志
# 今日日志中最后 10 条 error 记录 grep -i "error" ~/.screenpipe/screenpipe.$(date +%Y-%m-%d).log 2>/dev/null | tail -10screenpipe 的日志按日期滚动命名(screenpipe.YYYY-MM-DD.log),$(date +%Y-%m-%d)让命令始终命中今天的日志文件。
5. 综合状态报告(CLI 方式)
除了直接 curl,screenpipe 还内置了screenpipe status命令,它会在不干扰运行中录制器的情况下,探测/health、汇总数据库统计与存储占用,输出一行式的总览(● recording normally/▲ needs attention/○ not running),其实现位于 crates/screenpipe-engine/src/cli/status.rs。该命令特别注意到:当守护进程正在运行时,不会用第二个进程去打开 SQLite 数据库(macOS 上使用 unix-excl VFS 的 WAL 索引冲突可能毒化写入端),而是直接从健康响应中读取新鲜度字段——这正是值得学习的设计取舍。
详细诊断:深入到进程、API、流水线与数据库
进程信息与内存占用
# 详细进程信息 ps aux | grep -i screenpipe | grep -v grep # 内存占用汇总(单位 MB) ps aux | grep -i screenpipe | grep -v grep | awk '{sum+=$6} END {print "Total Memory: " sum/1024 " MB"}' # 区分是桌面应用还是 CLI pgrep -fl "screenpipe-app" && echo "Desktop app running" pgrep -fl "screenpipe$" && echo "CLI running"桌面应用进程名是screenpipe-app,纯 CLI 录制进程则精确匹配screenpipe$(防止误匹配到其他带 screenpipe 前缀的进程)。
API 端点总览:健康诊断工具箱
以下是健康排查最常用的一组端点,全部注册在 crates/screenpipe-engine/src/server.rs 中:
# 健康端点(含视觉 + 音频流水线统计) curl -s http://localhost:3030/health # 搜索端点(测试查询) curl -s "http://localhost:3030/search?limit=1" | head -50 # 列出音频设备 curl -s http://localhost:3030/audio/list # 列出显示器 curl -s http://localhost:3030/vision/list # 视觉流水线原始计数器 curl -s http://localhost:3030/vision/metrics # 音频流水线原始计数器 curl -s http://localhost:3030/audio/metrics/audio/list返回设备名列表并标记默认输入/输出设备(见 crates/screenpipe-engine/src/routes/audio.rs)。/vision/list返回显示器列表(id、stable_id、名称、分辨率、是否主屏),见 crates/screenpipe-engine/src/routes/health.rs 中的api_list_monitors。/vision/metrics与/audio/metrics返回流水线各阶段的原始计数器快照,源码注释明确说明其用途是"监控仪表盘与本地开发基准测试",见 crates/screenpipe-engine/src/routes/health.rs。
音频流水线诊断:转写是否真的在工作
/audio/metrics的字段与音频流水线的四个阶段一一对应:采集(capture)、VAD(语音活动检测)、转写(transcription)、数据库写入(DB),完整定义见 crates/screenpipe-audio/src/metrics.rs。下面这段 Python 脚本会把原始计数器翻译成人类可读的诊断结论:
# 快速音频流水线健康检查 —— 转写真的在工作吗? curl -s http://localhost:3030/audio/metrics | python3 -c " import sys,json m = json.load(sys.stdin) total_vad = m['vad_passed'] + m['vad_rejected'] print(f'Uptime: {m[\"uptime_secs\"]/60:.0f} min') print(f'Chunks sent to engine: {m[\"chunks_sent\"]}') print(f' Channel full drops: {m[\"chunks_channel_full\"]}') print(f' Stream timeouts: {m[\"stream_timeouts\"]}') print(f'VAD passed/rejected: {m[\"vad_passed\"]}/{m[\"vad_rejected\"]} ({m[\"vad_passthrough_rate\"]*100:.0f}% passthrough)') print(f' Avg speech ratio: {m[\"avg_speech_ratio\"]:.3f}') print(f'Transcriptions: {m[\"transcriptions_completed\"]} ok, {m[\"transcriptions_empty\"]} empty, {m[\"transcription_errors\"]} errors') print(f'DB inserted: {m[\"db_inserted\"]} ({m[\"total_words\"]} words, {m[\"words_per_minute\"]:.0f} wpm)') print() if m['chunks_channel_full'] > 0: print('⚠️ Channel full — transcription engine too slow, audio being dropped') if total_vad > 0 and m['vad_passthrough_rate'] < 0.1: print('⚠️ Very low VAD passthrough — may be dropping real speech') if m['transcription_errors'] > 0: print('⚠️ Transcription errors detected') if m['chunks_sent'] > 0 and m['db_inserted'] == 0: print('🔴 Chunks sent but nothing stored — pipeline is broken') if m['chunks_sent'] == 0 and m['uptime_secs'] > 120: print('🔴 No chunks sent after 2min — audio capture not working') "各计数器在源码中的真实语义(crates/screenpipe-audio/src/metrics.rs):
| 字段 | 含义 | 阶段 |
|---|---|---|
chunks_sent | 发送到转写通道的音频块数 | 采集 |
chunks_channel_full | 因转写通道已满而被丢弃的块数 | 采集 |
stream_timeouts | 设备流超时次数(>30 秒无音频数据) | 采集 |
chunks_lagged | 因消费者落后于广播通道而跳过的缓冲(CPU 争用下的静默丢失) | 采集 |
vad_passed/vad_rejected | 通过 / 被 VAD 拒绝的块数(阈值基于 speech_ratio) | VAD |
vad_passthrough_rate | vad_passed / (vad_passed + vad_rejected),0.0 表示全被拒绝 | VAD(派生) |
transcriptions_completed/transcriptions_empty/transcription_errors | 引擎成功返回 / 返回空串 / 出错 | 转写 |
db_inserted/total_words/words_per_minute | 成功入库数 / 累计词数 / 每分钟词数 | 数据库 |
一个关键细节:last_db_write_ts只在真正插入"去重后"的转写时更新,而last_transcription_attempt_ts在每次转写尝试时都会推进——哪怕 VAD 把全部音频判为静音。这两个时间戳的差异正是/health区分"无话可说"与"流水线卡死"的依据。
从/health读取的音频流水线汇总视图:
# 从 /health 读取音频流水线摘要 curl -s http://localhost:3030/health | python3 -c " import sys,json h = json.load(sys.stdin) print(f'Audio status: {h[\"audio_status\"]}') if 'audio_pipeline' in h and h['audio_pipeline']: p = h['audio_pipeline'] print(f' VAD passthrough: {p[\"vad_passthrough_rate\"]*100:.0f}%') print(f' Words/min: {p[\"words_per_minute\"]:.0f}') print(f' DB inserted: {p[\"db_inserted\"]}') "数据库健康检查
# 数据库完整性检查 sqlite3 ~/.screenpipe/db.sqlite "PRAGMA integrity_check;" 2>/dev/null # 数据库大小与各表行数 sqlite3 ~/.screenpipe/db.sqlite "SELECT name, (SELECT COUNT(*) FROM main WHERE name=t.name) FROM sqlite_master t WHERE type='table';" 2>/dev/null # 最近 24 小时的帧数 sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) as frames_today FROM frames WHERE timestamp > datetime('now', '-1 day');" 2>/dev/nullPRAGMA integrity_check是 SQLite 官方推荐的完整性校验手段,正常输出为ok。注意:当 screenpipe 正在运行(WAL 模式下)时,从外部用 sqlite3 打开数据库可能遇到锁或读到不一致的视图,此时更稳妥的方式是改用screenpipe status命令或/health接口获取新鲜度信息。
macOS 权限检查
屏幕录制与麦克风权限是 macOS 上最常出问题的环节。TCC(Transparency, Consent, and Control)数据库记录了应用的权限授权情况:
# 检查屏幕录制权限 sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db "SELECT client,allowed FROM access WHERE service='kTCCServiceScreenCapture';" 2>/dev/null | grep -i screenpipe # 检查麦克风权限 sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db "SELECT client,allowed FROM access WHERE service='kTCCServiceMicrophone';" 2>/dev/null | grep -i screenpipe # 或通过系统设置确认 echo "Check System Preferences > Privacy & Security > Screen Recording and Microphone for screenpipe permissions"直接读取 TCC.db 需要本机相应权限且依赖系统版本,命令末尾的2>/dev/null用于在无权限时静默失败。日常更推荐走系统设置界面确认。
常见问题与修复
问题:screenpipe 没有运行
# 启动 CLI 录制 screenpipe # 或启动桌面应用 open /Applications/screenpipe.app问题:没有捕获到屏幕帧
- 在系统设置中检查屏幕录制权限(macOS)。
- 检查日志中的权限相关错误:
grep -i "permission\|denied\|cg\|capture" ~/.screenpipe/screenpipe.$(date +%Y-%m-%d).log | tail -20问题:没有音频转写
- 检查麦克风权限。
- 检查音频流水线指标,用脚本定位卡点:
# 音频到底有没有被捕获? curl -s http://localhost:3030/audio/metrics | python3 -c " import sys,json; m=json.load(sys.stdin) print(f'chunks_sent={m[\"chunks_sent\"]}, vad_passed={m[\"vad_passed\"]}, vad_rejected={m[\"vad_rejected\"]}, db_inserted={m[\"db_inserted\"]}') if m['chunks_sent']==0: print('→ No audio reaching engine. Check device/permissions.') elif m['vad_passed']==0: print('→ VAD rejecting everything. Check mic input level or lower vad_sensitivity.') elif m['db_inserted']==0: print('→ Transcription failing. Check engine config or logs.') "这条脚本的三段式判断与音频流水线的阶段划分完全对应:chunks_sent=0说明采集层就没拿到数据(设备/权限问题);vad_passed=0说明 VAD 把所有音频都判为静音(输入电平过低或vad_sensitivity阈值不合适);只有到达db_inserted=0这一步才指向转写引擎本身。
- 检查音频设备选择与转写引擎日志:
curl -s http://localhost:3030/audio/list grep -i "audio\|device\|whisper" ~/.screenpipe/screenpipe.$(date +%Y-%m-%d).log | tail -20问题:CPU / 内存占用过高
# 查看当前占用 top -l 1 -s 0 | grep -i screenpipe # 在日志中查找内存泄漏或 OOM 痕迹 grep -i "memory\|oom" ~/.screenpipe/screenpipe.$(date +%Y-%m-%d).log问题:数据库被锁定
# 查看哪些进程持有数据库文件 fuser ~/.screenpipe/db.sqlite 2>/dev/null # 检查是否启动了多个 screenpipe 进程 pgrep -c screenpipe数据库被锁通常意味着存在多个 screenpipe 实例同时打开数据库,或某个异常进程未释放句柄。fuser能列出持有该文件的进程 PID,pgrep -c统计 screenpipe 进程数,确认是否存在重复实例。
源码级解读:/health如何判定健康状态
了解命令之后,再深入一层:/health返回的每个字段背后都有完整的判定逻辑,实现于 crates/screenpipe-engine/src/routes/health.rs。
响应结构:一次调用拿到全部诊断信息
HealthCheckResponse结构体(同文件定义)包含约 30 个字段,核心字段如下:
| 字段 | 说明 |
|---|---|
status/status_code | 总健康状态:healthy(200)/degraded(503)/unhealthy |
frame_status | 视觉流水线状态:ok/disabled/stale/not_started等 |
vision_reason | 视觉状态的机器可读原因(见下节) |
audio_status | 音频状态:ok/disabled/stale/active_no_data/no_input_device/waiting_for_meeting等 |
audio_capture_mode | 实际生效的采集模式:always/meetings-only/disabled |
capture_status | 结构化音频捕获状态(status + severity + reason),供会议/实时笔记 UI 使用 |
last_frame_timestamp/last_audio_timestamp | 最近一次写入时间戳 |
pipeline/audio_pipeline | 视觉与音频流水线的详细计数快照 |
recording_coverage | 录制覆盖率(近期活跃输入期间健康屏幕捕获的占比) |
pool_stats | SQLite 读写连接池的 size / idle 数 |
write_queue_degraded等 | 写队列降级、连续致命批次数、连接池重开次数等可靠性信号 |
vision_db_write_stalled/audio_db_write_stalled | 捕获循环存活但数据库写入停止(连接池耗尽或锁争用) |
drm_content_paused/schedule_paused | DRM 内容暂停 / 工作时间表暂停 |
值得注意的两个工程设计细节:
- 1 秒缓存 + 2 秒预算:多个 WebSocket 客户端和 HTTP 轮询可能每秒调用
/health数十次,而响应内容只有约 1 秒才发生有意义变化,因此端点实现了 1 秒 TTL 缓存和 single-flight 门控(HEALTH_CACHE_TTL_SECS = 1);同时整个计算被限制在 2 秒预算内(HEALTH_RESPONSE_BUDGET),超时则返回上次缓存的快照,避免健康检查拖垮调用方(包括 launchd 看门狗)。 - 写队列健康信号:
write_queue_consecutive_fatal(连续致命写入批次数)与write_pool_reopens(进程内重开连接池清除毒化连接的次数)等字段,让运维人员能直接看到数据库写入路径的可靠性状况。
frame_status与vision_reason:从"出问题"到"为什么出问题"
frame_status的历史教训是:它会把"screenpipe 自己关掉了像素录制"和"操作系统拒绝了屏幕捕获"折叠成同一个值,导致应用曾把已经授权过的用户引导去系统设置重新授权(源码注释引用了 issue #5808 的修复背景)。为此引入了vision_reason,用稳定的机器可读枚举区分九种状态:
ok— 正常录制disabled_by_setting— 设置中关闭了视觉(--disable-vision/disableVision)no_displays_expected— 所有选中显示器都被暂停、休眠或不活跃screenshots_disabled_by_config— 关闭了截图,仅捕获屏幕上的无障碍文本screenshots_disabled_by_power_profile— 低电量 / 低功耗模式暂停截图,恢复后自动继续permission_denied— 操作系统拒绝屏幕捕获(唯一应该给出权限指导的原因)capture_stalled— 有权限且预期录制,但帧停止到达not_started— 尚未产生第一帧ocr_unavailable— Linux 上找不到 Tesseract OCR 二进制,截图被存储但没有可搜索的文本
判定顺序是经过刻意设计的:先检查所有"故意关闭"的状态(用户自己关了像素就不是故障,绝不该收到权限指导),最后才轮到真正的故障(permission_denied/capture_stalled/not_started/ocr_unavailable)。对应逻辑见classify_vision_reason_with_ocr函数。
视觉停滞分类:不靠猜,靠计数器
当视觉流水线停滞时,/health会依据计数器把原因分为三类(VisionStallCause枚举,见 crates/screenpipe-engine/src/routes/health.rs):
SilentLoss(静默丢失):捕获仍在尝试,但帧没有到达写入端——capture_attempts在增长、frames_db_written与dedup_skips都平坦,说明帧在"尝试"与"写入"之间蒸发。CapturePaused(捕获暂停):捕获尝试完全停止——TCC 权限被撤销、显示器休眠或 ScreenCaptureKit 守护进程卡死。如果捕获后端最近发生过回退(如 ScreenCaptureKit 回退到 CoreGraphics),/health会直接点名"主后端卡死而非屏幕静止"。DbWritesNotLanding(数据库写入未落地):捕获到达了写入端,但写入端是问题所在——表现为丢帧数增长、平均 DB 延迟超过阈值或写连接池完全饱和。
这个分类的触发前提也很讲究:只有当last_db_write_ts真正过期(超过 60 秒新鲜度阈值)时才会触发,而record_dedup_skip与record_corrupt_skip都会推进该时间戳,因此"静态屏幕(空闲用户)"不会误报为停滞——源码注释直言,旧版本"idle user, not a pipeline stall"的措辞"每一次打印出来都是错的"。
音频状态机:为什么"没有麦克风"不是故障
classify_audio_status函数展示了音频健康判定的完整状态机:disabled→(屏幕锁定时视为ok)→meeting_detector_unavailable→waiting_for_meeting→no_input_device(音频开启但没有可捕获的麦克风,预期空闲而非失败,且不会让桌面端误触发停滞通知)→not_started→active_no_data(看门狗最近触发过且同一设备未恢复)→ok→stale。特别地,macOS 合盖(clamshell)模式下内置麦克风仍会被枚举但只能输出全零缓冲,健康判定会将其视为不可用(input_device_is_available函数),而外接麦克风仍然照常判定。
报告输出格式
健康检查完成后,一份合格的状态报告应遵循以下结构(原文档规定的输出规范):
- 先给出总体状态(healthy / unhealthy);
- 列出发现的所有问题;
- 给出具体的错误信息(尽量引用
/health中的message、vision_reason、capture_status.reason等结构化字段,而非笼统描述); - 针对每个问题给出修复建议(权限、设备、阈值或重启等可操作步骤)。
这套"总体状态 → 问题清单 → 证据 → 修复建议"的格式,与/health响应本身的设计哲学一脉相承:先给结论,再给原因,最后给可执行的动作。无论你是通过脚本定期巡检、接入监控仪表盘,还是为 Agent 提供健康上下文,都可以直接复用它。
小结
screenpipe 的健康排查可以总结为一条清晰的链路:进程 → API → 磁盘 → 日志 → 流水线指标 → 数据库 → 权限。本文提供的命令覆盖了前五层,而/health端点及其背后的 health.rs 实现则把最后几层压缩成了可机器消费的结构化信号——vision_reason区分"故意关闭"与"真故障",VisionStallCause用计数器而非猜测定位停滞环节,音频状态机把"没有麦克风"和"流水线卡死"分开处理。下次遇到 screenpipe 不工作,从pgrep -fl screenpipe和curl -s localhost:3030/health开始,沿着本文的步骤逐层深入即可。
【免费下载链接】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),仅供参考