Local-first screen memory that writes plain Markdown your AI agent can read.
Project description
LifeTrace
你的屏幕记忆,写成一份你能看懂的 Markdown。
LifeTrace 在后台安静地记录你屏幕上出现过的文字,按天存成本地 Markdown。 然后你已经在用的 AI —— Claude Code、Codex、Cursor —— 就能直接读它。
你 → 屏幕 → LifeTrace → ~/.lifetrace/memory/2026-07-31.md → 你的 Agent
没有云端,没有账号,没有订阅。
一句话装好
在任何 AI 编程助手里说这一句,它会自己装完并接好:
uvx lifetrace init
Windows 上这条命令不下载任何模型——OCR 用的是系统自带的
Windows.Media.Ocr,中文识别开箱即用。装完大约 60 MB,十几秒结束。
装完之后可以直接问你的 Agent:
我今天上午在干嘛? 上周那个 postgres 超时的报错,我是在哪看到的? 帮我写今天的日报。
为什么不是 MCP server
因为不需要。记忆本来就是磁盘上的 Markdown,而 Claude Code、Codex、Cursor 本来就会读文件、会 grep。
所以 LifeTrace 只装两样东西:一个写 Markdown 的后台进程,和一份告诉 Agent
去哪读的 SKILL.md。没有常驻端口,没有协议,不用为每个客户端写适配——
任何能读文件的 Agent 都天然支持,包括以后才出现的那些。
lifetrace skill install 会把说明书写进:
| Agent | 路径 |
|---|---|
| Claude Code | ~/.claude/skills/lifetrace/SKILL.md |
| Codex | ~/.codex/skills/lifetrace/SKILL.md + ~/.codex/AGENTS.md |
| Cursor | ~/.cursor/skills/lifetrace/SKILL.md |
| 通用 | ~/.agents/skills/lifetrace/SKILL.md |
记录哪些东西
全部在本机完成,不联网、不调任何按量计费的接口。
| 数据源 | 记什么 | 默认 |
|---|---|---|
| 屏幕文字 | 前台窗口所在屏幕的全部可见文字,本机 OCR | 开 |
| 应用与窗口 | 进程名、窗口标题 | 开 |
| 停留与离开 | 每个应用待了多久、什么时候离开了电脑 | 开 |
| 浏览器网址 | UI Automation 读地址栏拿到真实 URL | 开 |
| 缩略图 | 800px 宽 JPEG,约 25 KB | 开 |
| 剪贴板 | 复制过的文本 | 开 |
| 麦克风 | 你说的话,本机识别 | 需下载模型 |
| 扬声器 | 电脑放出来的声音——会议里对方的发言、视频、语音消息 | 需下载模型 |
「停留与离开」补的是 OCR 覆盖不到的那部分:看视频、开会、盯着一张图,屏幕上没有新文字,但那段时间确实花掉了。有了它,「今天上午干了什么」才答得完整。
剪贴板命中密钥特征(OpenAI / GitHub / AWS / Anthropic key、JWT、私钥、password:、银行卡号、URL 里的账号密码)的内容会被整条丢弃而不是截断——半个密钥仍然是密钥。
语音
识别用 SenseVoice + Silero VAD,全程在本机跑,音频一个字节都不出网,也没有任何按量计费。
pip install 'lifetrace[audio]' # 约 100 MB 依赖
lifetrace audio install # 约 230 MB 模型,只下一次,之后完全离线
lifetrace audio on
麦克风和扬声器分开记录:mic_pc 是你说的,speaker_pc 是别人说的。会议记录里这个区分就是全部意义所在。
扬声器那路的信号处理不是随便写的——按设备原生 48 kHz 采集后自己抗混叠降采样(让 WASAPI 代劳会把字准从 97.7% 打到 50.3%),再过 200 Hz 两级高通和 AGC(Windows 的低音增强会让 VAD 把 40 秒当成一句话,然后识别器只吐一个句号)。
它长什么样
数据就是这个样子,没有任何私有格式:
# 2026-07-31 感知记录
## 09:14 | ocr_proactive | Cursor
class CaptureDaemon:
def _tick(self) -> None:
window = get_active_window()
## 10:05 | ocr_proactive | 微信
张伟:LifeTrace 那个定价定了吗
我:先按 29.9 试水
## 10:31 | app_switch | 微信
在 微信 停留 26 分钟(10:05 - 10:31)
## 12:10 | app_switch | 离开
离开电脑 1 小时 30 分钟(12:10 - 13:40)
用 Obsidian 打开、丢进 git、rm -rf 删掉——都随你。
这个格式不是随便定的:它和 UniCone(甜筒)的 server/memory/raw_L0/ 逐字节一致。LifeTrace 本来就是从那个仓库里拆出来的,所以从 LifeTrace 升级到 UniCone 是一次目录拷贝,不是数据迁移。
命令
给人用的:
lifetrace init # 装好、接上 Agent、设开机自启、开始记录、打开界面
lifetrace ui # 打开界面
lifetrace status # 看状态和占用
lifetrace stop # 停止
lifetrace doctor # 自检:OCR、截图、窗口、托盘、自启、Agent 接入
lifetrace autostart enable # 开机自启(也可以在托盘和设置页里切)
lifetrace autostart disable
lifetrace audio install # 下载语音模型
lifetrace audio on / off # 开关语音记录
lifetrace audio status
lifetrace audio remove # 删掉模型,腾回 230 MB
给 Agent 用的(都往 stdout 打 Markdown):
lifetrace recall # 今天
lifetrace recall --day yesterday
lifetrace recall --since 14:00 --until 18:00
lifetrace recall --app Chrome
lifetrace search "postgres timeout" # 默认搜最近 30 天
lifetrace search "报销" --days 90
lifetrace dates
lifetrace apps --day today
界面
lifetrace ui 打开一个本地网页,只做两件事:回看和设置。
- 时间线 —— 按天回看,带缩略图,可以一键切到原始 Markdown
- 搜索 —— 全本地关键词搜索,命中高亮
- 设置 —— 采集间隔、隐私黑名单、存储与保留期、开机自启、Agent 接入状态
界面只绑 127.0.0.1,没有对外端口,所以也没有鉴权。
托盘
跑起来之后系统托盘会有一个圆点:绿色在记录,灰色已暂停。一个后台录屏的软件如果不告诉你它此刻在不在录,是不该被信任的,所以状态永远看得见,暂停永远是一次点击。
右键菜单:打开界面、暂停/恢复、开机自启、退出。
隐私
- 全部在本机。没有任何数据出网。
- 截图只在内存里过一遍 OCR,原图默认不落盘(缩略图可关)。
- 黑名单里的应用连截图都不会发生,密码管理器和无痕窗口默认已在名单里。
- 超过保留期的记忆自动删除,默认 14 天。
- 所有数据在一个文件夹里:
~/.lifetrace/,删掉就干净了。
配置
~/.lifetrace/config.json,或者直接在设置页里改(改完即时生效,不用重启)。
| 项 | 默认 | 说明 |
|---|---|---|
interval_seconds |
12 | 采集间隔;画面没变会自动跳过 |
ocr_language |
zh-Hans-CN |
系统 OCR 语言 |
blocked_apps |
见 config.py |
永不记录的进程名 |
blocked_title_keywords |
密码管理器 / 无痕 | 标题命中就跳过 |
save_frames |
true |
是否保留缩略图 |
retention_days |
14 | 超期自动删除 |
min_new_lines |
2 | 至少几行新内容才值得记 |
merge_window_minutes |
10 | 同一应用多久内合并成一段 |
port |
17893 | 本地界面端口 |
环境变量 LTRACE_HOME 可以换数据目录,LTRACE_PORT 可以换端口。
和 UniCone(甜筒)共存
LifeTrace 是从 UniCone / FreeU_Agent 里拆出来单独交付的,所以大概率会和它装在同一台机器上,甚至同时运行。两边的边界写在 src/lifetrace/namespace.py 里,并且有 tests/test_namespace.py 把它钉住——这些不是风格检查,每一条都对应一个会真把某一边搞坏的冲突。
环境变量前缀是 LTRACE_ 而不是 LIFETRACE_。 这条最容易踩:UniCone 的后端用 Dynaconf,envvar_prefix="LIFETRACE",也就是说任何 LIFETRACE_* 变量都会被它当成配置覆盖项吞掉。LIFETRACE_HOME 在那边会变成设置 home 配置键。
端口 17893,占用时向后顺延 10 个。17893-17902 这个窗口刻意避开了 UniCone 的全部区间(3001、3100-3149、4100-4149、5100-5149、8000-8002、8081、8100-8249、9000-9001、9100-9249、9876、10100-10249、15432、16379),也压在 19000 以下——因为它的悬浮窗触发器从 19274 开始无上限递增。
数据目录 ~/.lifetrace/,不碰 %APPDATA%\UniCone-Agent\ 和 Documents\UniCone\。注册表自启键名 LifeTrace,UniCone 用的是 UniCone-Agent。不注册任何全局快捷键,避开它的 Alt+Q/W/E 和 Ctrl+Shift+I。
技术栈对齐
除非有必须分叉的理由,实现路径跟着 FreeU_Agent 走,避免同一件事维护两套:
| 做法 | |
|---|---|
| 事件模型 | PerceptionEvent / SourceType / Modality 字段与其 server/perception/models.py 完全一致 |
| 落盘 | raw_L0/{date}.md、# {date} 感知记录、## HH:MM | source | app、无 front matter |
| 变化检测 | 同一套 dHash 16×16 + Hamming≤3,空白帧 std<5 |
| OCR | 同样是 WinRT 优先、RapidOCR 兜底 |
| 日志 | loguru 双 sink、{date}-{seq}.log、保留 7 天 / 错误 30 天 |
| ruff | 同一份 select 规则集 |
刻意分叉的地方只有三处,都是因为交付方式不同:
- 不用 numpy。 WinRT OCR 要的本来就是 BGRA,而
mss直接产出 BGRA,所以截图到 OCR 这条热路径一次转换都不需要。这是安装能做到 1 秒 / 60 MB 的原因。 - Python
>=3.10而非锁死 3.12。uvx要在用户已有的解释器上跑,钉死小版本会直接让一句话安装失败。 - 配置用 JSON + pydantic,不用 Dynaconf。 设置页需要往回写配置,而这里不需要 Dynaconf 的多层合并;更重要的是它的环境变量前缀正好是上面那个冲突源。
已知的上游 bug:FreeU_Agent 的 _normalize_winrt_spacing 用的是 0.7 比例阈值,中英数字混排时会失效(实测影响 11% 的中文 OCR 行)。这里已经修成逐字符判断,那份修复应该回流到上游,而不是让两边长期不一致。
平台
| 状态 | |
|---|---|
| Windows 10/11 | 完整支持,OCR 用系统自带的 Windows.Media.Ocr,零下载 |
| macOS 12+ | 完整支持,OCR 用系统自带的 Vision,同样零下载。首次要手动授予屏幕录制权限;扬声器转写需另装 BlackHole,因为 macOS 没有系统级音频回环 |
| Linux | 采集可用;需要 pip install 'lifetrace[ocr]',窗口标题依赖 xprop,无空闲检测和剪贴板 |
macOS 支持是在 Windows 上写的、未经真机验证。装好后先跑 uv run python scripts/check_macos.py,它会逐项告诉你哪些真的能用。
开发
uv sync # 后端依赖
pnpm --dir webui install # 前端依赖
pnpm --dir webui build # 构建前端进 wheel
uv run lifetrace start # 起服务(含采集)
pnpm --dir webui dev # 前端热更新,代理到 17893
uv run python scripts/smoke_capture.py # 真机采集冒烟测试
uv run python scripts/seed_demo.py # 造演示数据看 UI
uv run python scripts/check_agent_call.py # 按 Agent 的方式验证 CLI 输出
uv run python scripts/analyze_noise.py # 跑过一段时间后,量化 OCR 噪音并对比清洗前后
uv run pytest
uv run ruff check .
验证一句话安装真能跑(uvx 就是 uv tool run):
uv build --wheel
uv tool run --isolated --from dist/lifetrace-0.1.0-py3-none-any.whl lifetrace doctor
两个反复踩到的坑
中文输出编码。 Agent 用 subprocess 调 CLI 时,中文 Windows 的管道默认是 GBK,所有中文会变成乱码。用 PowerShell 测会误判,因为它自己的重定向也会制造一模一样的乱码。要验证只能抓子进程的原始字节——scripts/check_agent_call.py 就是干这个的。
无控制台环境。 开机自启走的是 pythonw.exe,此时 sys.stdout 和 sys.stderr 都是 None,任何 print 都会让进程静默死掉。_bootstrap.ensure_streams() 必须在 loguru 导入之前跑,因为 loguru 在导入时就绑定了 sys.stderr。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lifetrace-0.2.2-py3-none-any.whl.
File metadata
- Download URL: lifetrace-0.2.2-py3-none-any.whl
- Upload date:
- Size: 181.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f3e3f26ef55456d059055fcaa3f3ca9fd00d80b7eb9c4270fb4d4e51a64d9b7e
|
|
| MD5 |
939ba928a8548693834323bcfdf9b6ae
|
|
| BLAKE2b-256 |
c5a323d459a201e0d5d576a864f8ea7fd70ec6da43cb257e59c6644b3f164756
|
Provenance
The following attestation bundles were made for lifetrace-0.2.2-py3-none-any.whl:
Publisher:
release.yml on FreeU-group/lifetrace
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lifetrace-0.2.2-py3-none-any.whl -
Subject digest:
f3e3f26ef55456d059055fcaa3f3ca9fd00d80b7eb9c4270fb4d4e51a64d9b7e - Sigstore transparency entry: 2339074227
- Sigstore integration time:
-
Permalink:
FreeU-group/lifetrace@e14e75d167b1e220a3b6fc643ae2e5316a9098f2 -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/FreeU-group
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e14e75d167b1e220a3b6fc643ae2e5316a9098f2 -
Trigger Event:
workflow_dispatch
-
Statement type: