“Everything You Do Is Being Recorded”,这句话放到今天,不是一句网络段子,而是一句工具级的描述。操作系统会记录活动历史,浏览器会保留访问记录,输入法会保存输入习惯,AI 工具会把你的提问和文件内容一起收进对话上下文。对这些记录保持敏感,不是为了陷入监控焦虑,而是开发者必须理解的一种基础机制:谁在记录、记录了什么、存在哪里、能不能删。
这篇文章会从三个层面展开:先拆解操作系统、浏览器和 AI 工具各自记录了什么;再给一套可以自己运行在本地、把活动日志可视化自管自控的轻量级方案;最后讲清理、脱敏和合规边界。无论你是做安全审计、想统计自己的时间都花在了哪里,还是纯粹想搞清楚设备上那些行为数据到底流向哪里,这篇都可以作为一次本地实践的开始。
1. 行为记录能力速览
先明确一个基本判断:行为记录本身不是坏事,坏事是记录不被用户感知、不被用户控制。下面把常见的记录层面和典型内容整理成一张表,方便对照排查。
| 记录层面 | 典型记录内容 | 常见存储位置 | 用户可控程度 |
|---|---|---|---|
| 操作系统层 | 应用使用时间、前台窗口、登录日志、最近文件 | 系统日志库、事件查看器 | 中,可部分关闭 |
| 浏览器层 | 历史记录、Cookie、扩展读取的页面数据 | 浏览器用户目录 | 中,可清理或隐私模式 |
| 输入法与剪贴板 | 输入习惯、复制内容 | 输入法服务端、系统剪贴板 | 低,存在明文字段风险 |
| AI 工具 | 提问内容、上传文件、生成结果 | 服务商云端或本地缓存 | 低,依赖隐私政策与本地化设置 |
| 自建记录系统 | 应用使用事件、窗口标题、时间戳 | 自己控制的本地数据库 | 高,可删可改可迁移 |
从开发者角度看,最有价值的动作不是把记录全关掉,而是把记录能力纳入自己的掌控范围:知道哪些数据在产生、存在哪、以什么形式存在、能不能被第三方读取。
这里先给出一套自建行为记录系统的能力速览。后面的章节会带你把最小版本跑起来。
| 能力项 | 说明 |
|---|---|
| 记录范围 | 本机应用使用事件,不采集键盘明文,不监听聊天内容 |
| 存储方式 | 本地 SQLite 数据库,默认不联网 |
| 查询方式 | Web 页面与简单 JSON API |
| 部署条件 | Python 3、一台电脑、能安装依赖 |
| 适合场景 | 个人时间统计、本地审计、活动日志分析 |
2. 系统与应用的记录机制拆解
聊行为记录,先要知道现状。
2.1 操作系统层:系统一直在记账
Windows 有活动历史记录,会记录你打开过哪些应用、访问过哪些文件;任务管理器里的“应用历史记录”标签页,能直接看到每个应用的使用时间。Linux 这一侧更直接,shell 会写~/.bash_history,systemd 日志会记下服务启动和登录事件,last命令能列出最近登录记录。macOS 的“屏幕使用时间”和统一日志(unified log)也在做类似的事。
这些机制的设计初衷是系统审计和用户体验,但它们默认产生的数据量非常大。如果你从来没有主动查看过这些记录,大概率你的设备里已经躺着很多“行为痕迹”。系统层记录的特点是写入位置固定,普通用户不一定知道怎么清理,企业管理员则可以通过组策略或 MDM 强制开启审计。
2.2 浏览器与应用层:记录被当成默认能力
浏览器是所有本机行为记录里最直观的一层。历史记录、Cookie、indexedDB、localStorage,任何一个普通用户打开开发者工具都能看到一堆数据被写进浏览器存储。第三方扩展拿到的权限更大,一个能读取所有站点数据的扩展,实际上可以知道你一天里打开过哪些网页、在页面上输入过什么。
应用层的情况类似。即时通讯软件会保留聊天记录,文档编辑器会保留最近打开的文件列表,截图工具会保存历史截图。记录本身是为了使用便利,但一旦应用内部保留的日志被意外上传或泄露,风险就会放大。
2.3 AI 工具层:上下文机制决定了它必须“记住”
AI 工具的行为记录跟前两者不太一样。大模型的对话能力依赖上下文窗口,它需要把你的提问、上传的文件片段、系统提示词一起放进上下文,才能生成回答。这意味着用户输入的内容会在推理过程中被读取和计算,也可能会被服务商按隐私政策保存一段时间。
如果你在输入框里贴入一段源代码、一份合同或者一张带个人信息的截图,这些内容本质上已经进入了对应该工具的数据链路。了解这一点之后,哪些内容适合粘贴、哪些必须在本地模型里处理、哪些需要先脱敏,应该成为一种习惯。
3. 环境准备与前置条件
自建行为记录系统不挑机器,重点是把环境拆干净,避免把日志写进系统目录造成权限混乱。
3.1 系统与硬件要求
操作系统可以是 Windows、Linux 或 macOS。考虑到后续还要显示 Web 界面,建议内存保持在 4GB 以上,磁盘剩余空间在 20GB 以上。采集器和 Web 服务都跑在同一台机器时,占用会很低。
3.2 Python 环境
建议使用 Python 3.9 及以上版本。为了避免污染系统 Python,先建一个虚拟环境。
python -m venv activity-env source activity-env/bin/activate # Windows 下使用 activity-env\Scripts\activate安装依赖:
pip install flask3.3 磁盘与日志轮转规划
行为记录最大的问题是数据会持续增长。频率越高、保留越久,磁盘占用越大。提前规划一个数据目录,把数据库和未来可能生成的导出文件都放进去,建议结构如下:
activity-system/ ├── app.py # 主程序 ├── collector.py # 事件采集示例 ├── requirements.txt # 依赖清单 └── data/ # 数据目录,也可以挂载到 Docker 卷 └── activity.db # SQLite 数据库3.4 权限边界
这套系统只记录本机、本用户的应用使用事件,不监听网络流量,不记录键盘明文。在正式写到采集逻辑之前,先明确一条边界:如果代码逻辑里需要读取密码、验证码、聊天内容这类数据,说明范围已经越界,应当直接删除该功能,而不是试图给它做脱敏。
4. 自建本地活动记录与审计面板
下面给出一个最小可行的行为记录系统。它包含三个部分:数据库初始化、事件写入、Web 查询接口。所有代码都是通用示例,可以直接复制到本地项目里跑通。
4.1 初始化数据库
新建collector.py,先创建一张基础的事件表。
import sqlite3 from datetime import datetime DB_PATH = "data/activity.db" def init_db(): conn = sqlite3.connect(DB_PATH) conn.execute(""" CREATE TABLE IF NOT EXISTS events ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_type TEXT NOT NULL, app_name TEXT, title TEXT, detail TEXT, created_at TEXT NOT NULL ) """) conn.commit() conn.close() def record_event(event_type, app_name=None, title=None, detail=None): conn = sqlite3.connect(DB_PATH) conn.execute( "INSERT INTO events (event_type, app_name, title, detail, created_at) VALUES (?, ?, ?, ?, ?)", (event_type, app_name, title, detail, datetime.now().isoformat()) ) conn.commit() conn.close() if __name__ == "__main__": init_db() record_event( "manual_test", app_name="demo", title="Everything You Do Is Being Recorded", detail="这个事件用于验证本地记录链路" ) print("event recorded")这段代码只是演示事件入库逻辑。实际部署时,采集事件的方式会因操作系统不同而变化,比如 Windows 上用窗口句柄获取前台程序名,Linux 上用wmctrl或xprop,macOS 上用 AppleScript。核心思路一致:采集到事件后调用record_event写入数据库。
4.2 启动 Web 查询接口
新建app.py,用 Flask 暴露一个查询接口。
from flask import Flask, jsonify, request import sqlite3 app = Flask(__name__) DB_PATH = "data/activity.db" def query_events(limit=50): conn = sqlite3.connect(DB_PATH) rows = conn.execute( "SELECT * FROM events ORDER BY id DESC LIMIT ?", (limit,) ).fetchall() conn.close() return [ { "id": r[0], "event_type": r[1], "app_name": r[2], "title": r[3], "detail": r[4], "created_at": r[5], } for r in rows ] @app.route("/api/events") def events(): limit = request.args.get("limit", default=50, type=int) return jsonify({"events": query_events(limit)}) if __name__ == "__main__": app.run(host="127.0.0.1", port=5000)启动方式:
python app.py启动后访问http://127.0.0.1:5000/api/events,可以看到刚写入的测试事件。
4.3 curl 验证 API
接口能跑通,说明这套系统已经具备最基本的查询能力。用 curl 验证一下:
curl "http://127.0.0.1:5000/api/events?limit=20"返回结果类似:
{ "events": [ { "id": 1, "event_type": "manual_test", "app_name": "demo", "title": "Everything You Do Is Being Recorded", "detail": "这个事件用于验证本地记录链路", "created_at": "2025-01-01T10:00:00.000000" } ] }到这里,一个能记录、能查询的最小系统已经跑通。后续要接批量任务或数据导出,可以直接复用这张事件表。
4.4 Docker 部署模板
如果你不想在宿主机上装 Python 依赖,可以用 Docker 跑。下面是一份通用配置,实际使用时需要把项目路径替换成你本机的绝对路径。
version: "3" services: activity-web: build: . ports: - "5000:5000" volumes: - ./data:/app/data restart: unless-stopped对应的 Dockerfile 可以保持最小体积:
FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . VOLUME /app/data EXPOSE 5000 CMD ["python", "app.py"]这个模板适用于任何把 SQLite 文件放在工作目录下的 Python Web 服务。数据目录通过volumes挂载出来,重装容器也不用担心数据库丢失。
5. 功能测试与效果验证
自建系统最容易出现的问题是采集端正常、展示端报错,或者反过来。下面给出一套通用验证流程,照着走一遍能很快定位问题。
5.1 验证事件写入
先运行一次采集脚本,确认没有报错。
python collector.py如果看不到event recorded输出,优先检查data/目录是否存在、SQLite 是否已经创建了表。Windows 上尤其要注意路径分隔符,建议统一使用相对路径并保证当前工作目录正确。
5.2 验证 API 查询
Web 服务启动后,直接在浏览器打开接口地址,确认能返回 JSON。不能返回时,按顺序检查四件事:
- Flask 是否启动成功,终端有没有报错。
- 端口是否被占用,换一个端口再试。
- 数据库路径是否和采集端一致。
- 是否有请求触发了异常但被 Flask 默认错误页挡住。
5.3 判断成功的标准
一个行为记录系统判断是否成功,不看“能跑”,而看“连续跑一段时间后数据可用”。建议用以下标准:
| 验证点 | 通过标准 |
|---|---|
| 事件写入 | 数据库里能看到新时间戳 |
| 数据持续时间 | 至少记录 24 小时 |
| 查询稳定性 | 页面上连续刷新不报错 |
| 数据隔离 | 日志目录只在本地,外部无法访问 |
| 停止与恢复 | 进程中断重启后数据库仍可查询 |
5.4 扩展:自动采集前台窗口
如果要自动记录前台应用,可以写一个系统相关的采集函数。下面是通用结构,具体命令需要按平台替换:
def get_active_window(): # Windows 示例:使用 pywin32 获取 GetForegroundWindow # Linux 示例:使用 wmctrl -l 解析 # macOS 示例:使用 AppleScript 获取 frontmost app return "current_app_name"调用侧保持不变:
record_event("app_switch", app_name=get_active_window(), title="窗口标题")重点在于采集函数只返回应用名和窗口标题,不读取窗口内部内容。一旦采集插件开始抓取验证码、密码框、聊天消息,就必须停止使用。
6. 开源现成工具快速落地
如果你不想从零写采集器,也可以直接用现成的开源行为记录工具。这个领域最典型的项目是 ActivityWatch,它主打本地优先、开源、数据留在本机,主打个人时间追踪和自动记录应用使用情况。
使用思路通常是:安装官方客户端或者用 Docker 启动,本地服务会把应用使用数据写入数据库,然后在浏览器打开 Web 面板查看时间线、应用分类和网页访问统计。选择这类工具时应该关注几条原则:
- 必须开源,能查看代码,确认数据流向。
- 必须本地优先,默认不向第三方服务器上传。
- 数据格式可导出,最好直接落 SQLite 或 JSON。
- 项目活跃度正常,至少最近一年还有发布记录。
反向的提示也要给:不要下载来路不明的“录屏监控工具”或“键盘记录器”。这类工具里经常混入恶意代码,轻则后台挖矿,重则窃取账号密码。自建行为记录系统的意义是把数据留在自己手里,而不是交给另一个黑盒。
7. 资源占用与数据清理
行为记录系统长期运行时,需要持续观察三个资源指标:CPU、内存、磁盘。
7.1 CPU 与内存
采集端如果用轮询方式获取前台窗口,CPU 占用主要来自轮询间隔。间隔越短,数据越实时,但占用越高。建议轮询间隔设置在 2 到 5 秒,对现代 CPU 几乎没有影响。Web 服务端在只有个人访问时,内存占用可以控制在很低水平。如果页面变慢,先查是不是数据库增长过大导致查询变慢,再考虑加索引。
7.2 磁盘增长与保留策略
SQLite 数据库的大小取决于事件频率和 detail 字段的长度。如果每秒记一条事件,一年大约会产生 3000 多万条记录,磁盘占用会明显上升。建议在采集端加数量限制:
def trim_events(max_rows=500000): conn = sqlite3.connect(DB_PATH) conn.execute(""" DELETE FROM events WHERE id NOT IN ( SELECT id FROM events ORDER BY id DESC LIMIT ? ) """, (max_rows,)) conn.commit() conn.close()这个函数可以在每天定时任务里执行,保留最近 50 万条事件,避免日志无限膨胀。
7.3 清理与归档
如果数据有长期保存价值,可以按周导出 JSON 或 CSV,然后清理主表。导出时要注意脱敏:窗口标题、文件名、URL 都可能包含个人信息,发布或上传之前先过滤一遍。
8. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 采集脚本报数据库锁错误 | SQLite 同时被多个进程写入 | 查看是否有脚本重复启动 | 用单进程采集,或写入加锁 |
| Web 页面打不开 | 端口被占用或服务未启动 | 检查日志和端口监听 | 更换端口或重启服务 |
| 接口返回空数据 | 数据库路径不一致 | 对比采集端和 Web 端的 DB_PATH | 统一路径 |
| 数据量增长过快 | 轮询间隔太短或采集事件类型过多 | 查看数据库总行数 | 增加轮询间隔,减少事件类型 |
| 进程重启后数据丢失 | Docker 卷未挂载 | 检查 docker-compose volumes 配置 | 挂载宿主目录 |
| 页面加载越来越慢 | 数据表缺少索引 | 查看查询耗时 | 给 created_at 加索引 |
| 磁盘被日志占满 | 没有清理策略 | 查看数据库文件大小 | 添加 trim 和归档 |
| 依赖安装失败 | Python 版本或网络问题 | 查看 pip 报错 | 换镜像源或升级 Python |
9. 隐私保护、开源协议与合规边界
本地记录系统的价值是让你获得数据控制权,而不是替你规避法律和伦理问题。以下几点必须明确。
第一,记录行为的对象边界。自己电脑上的个人行为记录,属于个人对设备的合理管理。把同样的工具装到别人电脑上,未经授权持续记录他人应用使用习惯,就可能触碰隐私和安全红线。公司电脑上通常有统一合规审计政策,个人额外部署记录工具前,需要确认是否符合公司规定。
第二,任何与账号密码、验证码、聊天内容、人脸、声音相关的数据,都不应该进入这个事件表。不是技术不允许,而是风险不可控。一旦数据库文件泄露,明文保存的敏感字段会让损失成倍放大。
第三,内容发布前的脱敏。如果后续要把活动记录导出成报告或截图分享,必须先检查窗口标题、文件名、URL 中是否包含姓名、手机号、邮箱、Token 等信息。工业界常见的做法是用正则或规则引擎先打码,再人工复核。
第四,开源协议与依赖合规。如果自己写采集器,要检查依赖库是不是商用友好协议;如果直接用开源工具,要看它的代码有没有内置遥测上报,有的话要在配置里关闭。
10. 总结与下一步
“Everything You Do Is Being Recorded”描述的不是某个软件的 bug,而是现代系统的默认工程特性。真正值得关注的不是“有没有记录”,而是“记录是否可感知、可管理、可清理”。这篇文章给出的最小版本已经能完成事件写入、本地存储和 API 查询,你可以先跑通这套链路,再决定要不要接入真实的前台窗口采集。
如果你第一次尝试,建议按这个顺序验证:先把数据库和 Flask 跑通,再确认数据只存在本地目录,最后接入窗口标题采集,观察一天的数据。最容易踩的坑是采集端和 Web 端路径不一致导致查不到数据,以及数据库无限膨胀导致页面变慢。
后续可以扩展的方向包括:把日报自动整理成 Markdown、用 SQL 做每周使用时间统计、接入本地大模型做行为摘要、给数据库加 SQLCipher 加密。数据在自己手里,这些扩展才真正可控。