screenpipe:把AI记忆刻进本地的Rust实践
screenpipe用Rust重写「数字记忆」基础设施:事件驱动捕获(5–10% CPU)、SQLite FTS5语义搜索、MCP协议直连Claude、OS级权限隔离的Pipe插件系统。不上传、不依赖云、不妥协隐私——所有AI能力运行在你MacBook的内存里。

嘿,各位Java老战友,我是周小码——一个被Spring Boot自动配置折磨到怀疑人生的后端老兵,最近却在Rust项目里喝到了一口清冽的山泉。今天聊的不是JVM调优,也不是K8s YAML写到手抽筋,而是这个让我凌晨三点关掉IDEA、打开屏幕录像反复验证的项目:screenpipe。
痛点不是“录不到”,是“录了也找不到”
我们每天产生的数字痕迹,比自己记得的多十倍:Zoom会议里一闪而过的报错截图、IntelliJ控制台滚屏消失的堆栈、Slack里同事发的那条关键配置命令……它们真实存在,却像沉入深海的碎片——没有索引、没有上下文、没有时间锚点。传统录屏工具只负责“存”,不解决“找”;云笔记要手动复制粘贴,漏掉音频、窗口状态、光标位置这些隐性上下文;而所谓“AI助手”,往往只是把你的数据喂给远端大模型,再把结果吐回来——你既不知道它记了什么,也不知道它忘了什么。
screenpipe 不是又一个录屏软件,它是你电脑的「海马体」——那个负责把短期记忆转成长期记忆的大脑结构。它不靠云、不求人,就在你本地默默运行,把你的每一次窗口切换、每一段Zoom会议、每一行敲下的代码、甚至你自言自语的吐槽,都变成可搜索、可编程、可AI理解的「数字记忆」。
架构即哲学:三层乐高,全部本地化
它的架构不是炫技堆叠,而是对「本地优先」的工程具象:
-
底层采集层(Rust + Tauri):不走ffmpeg全帧抓取,而是监听系统事件(
AXUIElement,CGWindowListCopyWindowInfo,uinput等),仅在窗口切换、按键释放、鼠标悬停、滚动停止等「认知锚点时刻」触发快照+Accessibility Tree抓取。README实测CPU占用稳定在5–10%,MacBook M2 Air跑满24小时无风扇狂转。 -
中间存储层(SQLite + FTS5):所有数据落地为本地SQLite DB,启用WAL模式+PRAGMA mmap_size=268435456(256MB内存映射)+零拷贝BLOB读取。FTS5全文检索不仅支持
MATCH 'error AND stacktrace',更通过rank=bm25(10.0, 5.0)加权标题与OCR文本,并预留fts5vocab表供后续接入Sentence-BERT做向量相似度补全——但注意:默认不启向量库,纯FTS5已足够应对90%知识检索场景。 -
上层交互层(MCP Server + Pipes):HTTP API是入口,但真正灵魂是MCP(Model Context Protocol)服务。它不是RESTful,而是基于JSON-RPC 2.0定义的上下文感知协议,让Claude/Cursor等AI客户端能以
{"method":"search_context","params":{"q":"NullPointerException in UserService","time_window":"last_2h"}}方式精准提问。而所有回答背后,都是本地SQLite的JOIN查询+Whisper本地ASR结果拼接——没有token外泄,没有请求日志上传。
核心代码解析:从CLI到Pipe,全是硬核细节
先看最狠的一行安装命令:
bash
npx screenpipe@latest record
这行命令背后藏着三重设计:
npx绕过Rust环境依赖,实际执行的是预编译的Tauri二进制(macOS:.app, Windows:.exe, Linux: ELF);record子命令直接调用Rust主函数cli::run_record(),跳过GUI初始化,启动纯后台采集;- 所有配置(如OCR开关、采样间隔、窗口白名单)由
~/.screenpipe/config.json驱动,支持热重载——改完配置不用重启进程。
再看最关键的搜索API,它暴露的不只是关键词匹配:
bash
GET http://localhost:3030/search?q=meeting+notes&content_type=ocr&limit=10
这个/search端点实际路由到Rust的axum handler:
rust
// src/api/search.rs
async fn handle_search(
State(db): State<DbPool>,
Query(params): Query<SearchQuery>,
) -> Result<Json<Vec<SearchResult>>, StatusCode> {
let mut query = format!("SELECT * FROM segments WHERE content MATCH ?");
// 关键:根据content_type动态JOIN不同表
if params.content_type == "ocr" {
query.push_str(" AND segments.id IN (SELECT segment_id FROM ocr_results)");
}
// 时间范围用SQLite内置datetime处理,非应用层过滤
if let Some(t) = ¶ms.start_time {
query.push_str(&format!(" AND datetime(timestamp) >= datetime('{}')", t));
}
// 使用FTS5 bm25排序,而非简单ORDER BY
query.push_str(" ORDER BY rank MATCH ?");
let rows = sqlx::query(&query)
.bind(¶ms.q)
.bind(¶ms.q)
.fetch_all(&*db)
.await?;
Ok(Json(rows.into_iter().map(|r| r.into()).collect()))
}
最后是Pipe插件系统的灵魂——pipe.md文件:
markdown
---
## 权限声明:OS级硬隔离,非AI自觉守规矩
permissions:
- read: ["chrome", "vscode"]
- ocr: ["chrome"]
- audio: false
- write: false
---
当检测到VS Code中出现"ERROR"字样时:
1. 截取当前编辑器窗口内容
2. 提取其中堆栈跟踪(正则匹配java.lang.*Exception)
3. 将错误摘要和时间戳写入Obsidian vault的"errors/"目录
4. 向飞书机器人发送告警(需提前配置webhook)
这个YAML前言会被Rust解析器pipe::parse_permissions()校验:若Pipe试图读取微信窗口,内核级accessibility_api::is_allowed()直接返回Err(PermissionDenied),根本不会进入OCR流程——权限不是运行时检查,是编译期+OS API双锁死。
实战演示:5分钟构建你的AI站会助理
假设你每天10:00开站会,录音+共享屏幕。用screenpipe自动化:
- 创建
standup.pipe.md:
markdown
---
permissions:
- audio: true
- ocr: ["zoom"]
- write: true
---
当检测到Zoom会议开始且麦克风开启时:
- 录制音频流(Whisper.cpp本地转录)
- 每30秒抓取Zoom窗口OCR文本(识别共享PPT中的Action Items)
- 将转录文本+OCR结果合并,用Ollama llama3生成会议纪要
- 保存为`daily/standup_20260320.md`并推送到Git
- 启动监听:
bash
screenpipe pipe watch --file ./standup.pipe.md
- 查看结果(毫秒级响应):
javascript
import { pipe } from "@screenpipe/js";
const results = await pipe.queryScreenpipe({
q: "assign to me",
contentType: "all",
limit: 5,
startTime: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
});
// 返回结构包含:timestamp, window_title, ocr_text, audio_transcript, embedding_vector(可选)
踩坑指南:现实骨感,但可控
- Apple Intelligence仅限M系列芯片:因为依赖
AVFoundation的硬件加速编码,Intel Mac需降级使用ffmpeg软编,CPU占用升至15–20%; - Linux需源码编译:官方未提供预编译二进制,但README的
cargo build --release --features linux步骤清晰,关键是安装libx11-dev libxtst-dev libasound2-dev等头文件; - Whisper模型首次加载慢:默认下载
ggml-base.en.bin约170MB,建议提前screenpipe model download --name base.en; - SQLite WAL冲突:多进程写入时可能触发
database is locked,解决方案是PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL;已内置,无需手动干预。
个人评价:这不是工具,是新契约
作为Java人,我酸了——我们还在为Spring Cloud Gateway的路由配置掉头发时,screenpipe已经用Tauri(Rust+TS)把桌面应用、CLI、HTTP服务、MCP服务器全塞进一个二进制里。它没用gRPC,没上Kafka,却靠SQLite WAL模式+内存映射+零拷贝IO实现了毫秒级搜索响应。
值不值得学?必须。不是学它怎么录屏,而是学它如何用Rust重写「信任契约」:把隐私承诺变成编译期约束,把AI能力封装成Markdown语法,把复杂系统拆解成可插拔、可审计、可离职交接的Pipe文件。这,才是下一代开发者该有的基础设施思维。
P.S. 我刚试完,它真的记住了我刚才吐槽这段话时皱的眉——虽然还没学会给我递杯咖啡 😅