
你可能也遇到过这种场景刚才还刷到过一个很关键的技术帖子等想回去翻的时候就是找不到开会时有人提到一个数据你记得之前看到过但死活想不起来在哪看到的或者说你想复盘自己一天的工作却完全不记得下午两三点那会儿到底在忙什么。这种“想不起来”的问题本质上不是记性差而是信息进来的时候没有建立索引。很多所谓的碎片化阅读真正浪费的不是你读的那几分钟而是你读完就忘、下次想用却找不到的那一次搜索。我最近就在做一个叫“hindsight”的小项目想解决的就是这件事。hindsight 这个词本身是“后见之明”的意思心理学里也有个著名的“后见之明偏差”说的是事后觉得自己早就知道。把项目起名叫 hindsight我其实想表达的是让电脑帮你记住那些你当时没在意、事后才觉得重要的信息让你每次回溯都有一种“原来这里我看过”的确定感。这篇文章就把这个项目的完整思路、技术方案和实操过程拆开来讲包括每一层模块怎么选型、核心代码怎么写、会遇到哪些坑、以及我做了哪些隐私保护取舍。如果你也想过“能不能给自己的电脑装一个外部记忆库”这篇应该能帮到你。1. 我为什么要做 hindsight以及它到底解决什么问题1.1 先想清楚你的信息为什么总是“找不到”我们每天接触到的信息量其实远超自己能用语言描述出来的部分。你读了一篇文章、看了一段视频、浏览了一个网页、甚至只是瞟了一眼微信群里的某张截图这些信息都进入了大脑但绝大多数没有被“编码”成可以检索的记忆。人类记忆的机制很有意思它在编码阶段就会做筛选和压缩。你当时觉得不重要的东西大脑直接就丢掉了不会给你事后检索的机会。但问题在于你事后是否觉得某条信息重要和你当时看到它时是否觉得重要其实经常不是一回事。大多数信息都是“事后才显得有价值”的等你想用的时候记忆早就没了。所以我想要的这个系统不应该依赖人的主动记录因为人做不到每次都主动记录。它应该像一个后台进程不动声色地把你接触过的信息全部留存下来建立一个可检索、可回溯的外部记忆库。简单说就是给电脑装一个自动运行的“记忆存档器”。1.2 hindsight 的核心思路用外挂记忆弥补大脑的截断一句话概括这个项目的思路持续截屏光学识别向量化存储自然语言检索。听起来不复杂但每个环节其实都有讲究。持续截屏解决的是“获取信息”的问题光学字符识别OCR解决的是“把图像变成文字”的问题向量化存储解决的是“让文字可以被语义检索”的问题自然语言检索解决的是“你想不起来关键词但记得大概意思”的问题。这四个环节串起来就是一个最小可用的个人记忆系统。你不需要给每条信息手动打标签也不需要维护什么复杂的分类目录你只需要在键盘上敲一句“我上周好像看过一篇讲 RAG 优化的文章”系统就能帮你把相关的屏幕截图翻出来。这就好像给大脑加了一块外置硬盘还是带全文检索那种。虽然不能真的增强你的记忆但它能帮你找到那些已经被大脑丢弃的信息痕迹。1.3 为什么我不直接用现成的记录工具市面上其实有不少可以替代的成品工具比如截图笔记软件、浏览器历史记录、甚至像 Rewind 这样的自动回放工具。但我自己动手做是因为几个现有方案都不太合手。浏览器历史记录只能管浏览器里的内容你打开本地文档、看视频、用聊天软件它都管不到截图笔记软件靠的是手动截图你忘了截它就什么都没有而我自己恰恰就是老忘重放类工具又太“重”了它会把所有操作都录下来隐私风险比较高而且检索能力一般你想找一条几个月前的信息缩略图翻起来非常痛苦。自己做一个 hindsight好处是可以完全按照自己的使用习惯来定制。我可以控制哪些目录不记录、哪些应用不参与、数据存在本地、检索逻辑自己调。这种可控感是成品工具给不了的。1.4 这个项目适合谁来参考如果你是想做一个能长期用的效率工具这个项目很适合拿来改造成你自己的版本。如果你正在学习 AI 应用开发、想练手 RAG检索增强生成操作链路那 hindsight 就是一个非常典型的落地场景从数据采集、离线处理、向量检索到生成式回答整条链路都覆盖了。它的代码量其实不大核心部分几百行就能跑起来但对理解“信息处理管道”这件事非常有帮助。不像很多教程里用现成 PDF 文档做 RAG 演示hindsight 的数据是从真实场景里实时采集的噪音更多、格式更乱、清洗起来更真实你做完以后对检索系统的理解会比看十篇理论文章都深刻。2. 架构设计与技术选型我是怎么权衡的2.1 整条数据管道的四个核心环节先看整体流程。hindsight 的核心链路是屏幕画面采集 - OCR 提取文字 - 文本清洗和切分 - 向量化 - 写入检索库 - 查询时做语义匹配 - 可选地交给大模型生成答案。这条链路里每个环节的输入输出都是明确定义的。屏幕画面是一张 PNG 或者 JPEG 图像OCR 环节把它变成一段带坐标的文字文本清洗把无关的符号、排版噪声去掉切分逻辑把长文本切成适合检索的小段向量化把每段文本变成一个高维数组检索时把你的查询转成同样的数组然后做相似度搜索。这里面最容易被低估的其实是“切分”这个环节。很多人以为做个人记忆系统只要把 OCR 出来的文字整段存进去就行了。但实际上一屏截图里的文字内容往往混杂着窗口标题、页签名称、正文内容、按钮文案等等。如果整段存检索效果会非常差。等会儿在实操部分我会详细讲怎么切。2.2 截图模块的选型逻辑与跨平台差异截图是整个系统的数据入口。它最核心的要求不是清晰而是“稳定、不打断你”。在 macOS 上screencapture命令就够用在 Linux 上可以用scrot或者gnome-screenshotWindows 上可以用 PowerShell 配合 .NET 方法或者用 Python 的mss库mss是跨平台的性能也不错支持指定显示器区域。我自己是在 macOS 上用的screencapture -x-x参数表示不播放快门声音这样截屏的时候不会有任何感知。采集频率设置在 5 秒左右一次这个频率能保证大多数操作都有记录又不会产生太多重复内容。如果调成 1 秒一次可能一分钟就攒好几十张几乎一样的图全是无效冗余存储和 OCR 的压力都会上去。这里有个取舍要讲清楚截全屏还是截当前窗口。我一开始是全屏截后来发现多个显示器的时候全屏截图会捕获到一堆无关区域比如另外一个显示器上开着的视频画面OCR 出来全是噪声。后改成只截主屏的当前活动窗口信息密度高很多。如果你的使用场景比较固定建议优先考虑活动窗口截图。2.3 OCR 选型Tesseract 够用但你要会调参数说到 OCR中文场景下最容易踩坑的是编码和语言包问题。Tesseract 是老牌开源 OCR胜在免费、离线、部署简单但它在中文识别上需要你额外下载中文语言包而且默认参数对中文的识别效果只能说一般。我在项目里用的是 Tesseract 5 的 LSTM 引擎配合--psm 3模式这个模式是自动版面分析适合截屏这种整页内容。如果识别率不理想还可以试试--psm 6它把整页当作一个文本块处理在内容比较规整的网页截图上效果更好。如果你对中文识别精度要求更高可以换成 PaddleOCR它在中文上的表现确实比 Tesseract 好不少尤其是手写体和带背景的截图。代价是依赖更重PaddleOCR 需要安装 PaddlePaddle 框架部署体积大很多。我做这个项目的时候先用了 Tesseract后面再考虑要不要切换。2.4 向量存储与嵌入模型的选择判断OCR 出来的文字变成向量之后放在哪里、用什么模型嵌入是决定查询效果的关键。嵌入模型方面我选了开源的中文向量模型。OpenAI 的 embedding 接口效果确实好但这是纯本地项目每屏截图都去调远程接口不但会积累 API 费用还有隐私问题。你要记录的是自己的一切屏幕内容这本身就是非常私密的数据我坚持一切都用本地模型处理。从 embedding 模型输出的向量维度这一块本地开源的小模型一般在 768 到 1024 维之间检索精度和性能的平衡点也在这一区间。至于存储端我早期用的是 Chroma 这类向量数据库后来发现对纯本地单机场景有点杀鸡用牛刀直接换成 SQLite numpy 余弦相似度计算几十万条文本向量检索全表也就是几十毫秒级别完全够用而且备份、迁移都非常简单。2.5 隐私和合规设计为什么一切都要留在本地隐私这块我的原则很简单数据不出本机。所有 OCR、向量化、存储、检索都在本地完成。截屏图片在 OCR 完成后会做压缩处理只保留 JPEG 缩略图原文文本和向量存在本地数据库未来如果接大模型做摘要我也是打算用本地跑的小模型比如 Ollama 加载 Qwen 或者 Llama 的量化版。这里提一个很多人会忽略的点OCR 出来的文本里可能包含密码、验证码、聊天隐私这类敏感内容。我在设计里加了一层脱敏策略用正则把可能是密码、Token、手机号、邮箱的片段在存储前替换成占位符。这个操作不能说绝对安全但至少让数据即使被误看到也不会直接泄露完整敏感信息。工具本身是一个提高效率的东西但前提是它不能成为新的隐私风险点。把自己的完整屏幕内容毫无保护地扔在云端那等于把家门钥匙放在脚垫底下还是写着“钥匙在这”那种。3. 实操过程从零搭一套可用的 hindsight3.1 环境准备和依赖安装我先说下我的运行环境macOS Python 3.11。各平台的安装命令不太一样但依赖的核心库就几个。# macOS brew install tesseract tesseract-lang # Ubuntu/Debian sudo apt install tesseract-ocr tesseract-ocr-chi-sim # Python 依赖 pip install pillow pytesseract numpy openai-chatbot我还额外用了sqlite-vec这个扩展来做向量索引它比我自己手写 numpy 全表扫描要规范一些还支持 SQL 查询管理数据方便很多。如果你不想引入这个依赖直接用 numpy 算余弦相似度也行数据量不大的时候性能差距可以忽略。需要说明的是pytesseract只是 Python 调用 Tesseract 的封装真正的识别引擎还是系统里装的那个 Tesseract 二进制文件。所以装完 tesseract 之后要先在命令行里执行一下tesseract --version验证是否装好再检查一下语言包是否存在。3.2 核心代码自动截屏 OCR 文本提取先看第一段截屏和 OCR 的主流程。import time import subprocess import os from datetime import datetime from PIL import Image import pytesseract SCREENSHOT_DIR ~/hindsight/captures INTERVAL 5 # 秒 def capture_screen(): timestamp datetime.now().strftime(%Y%m%d_%H%M%S) path os.path.expanduser(f{SCREENSHOT_DIR}/{timestamp}.png) # macOS 截屏命令-x 表示无声截屏 subprocess.run([screencapture, -x, path], checkTrue) return path def ocr_image(path): # 中文 英文识别 text pytesseract.image_to_string( Image.open(path), langchi_simeng, config--psm 3 ) return text.strip() def main(): while True: try: img_path capture_screen() text ocr_image(img_path) if text: print(f[{img_path}] OCR 文字长度: {len(text)}) # 下一步写入存储见 3.3 else: # 没识别出文字删掉截图不留垃圾 os.remove(img_path) except Exception as e: print(f处理失败: {e}) time.sleep(INTERVAL) if __name__ __main__: main()这段代码看起来简单但有几个细节值得说。一是空文本处理如果 OCR 一个字都没识别出来大概率是截到了桌面壁纸或者纯色界面这种图直接删除不进入存储链路省存储也省向量化的开销。二是异常处理OCR 偶尔会崩溃尤其是跑久了之后一定不能让主循环因为一次异常就整体退出。三是INTERVAL 5这个间隔我测试过 3 秒、5 秒、10 秒三档5 秒是最平衡的3 秒产生的重复图片太多10 秒容易漏掉一些短暂停留的弹窗信息。3.3 数据入库文本切分、向量化和存储OCR 出来的长文本不能直接整篇入库。一屏内容里可能包含了正在编辑的文档、聊天窗口、状态栏时间这些信息的“主题”完全不同。如果把整篇文本作为一个向量存进去检索时它的语义会被稀释掉。我的做法是按段落切分再用滑动窗口合并短段落。具体逻辑是把 OCR 出来的文本按空行分成若干块如果一块的长度超过 200 字再按句号或逗号二次切分如果相邻两块都很短加起来不超过 150 字就合并成一块因为它们大概率在描述同一件事。import re import sqlite_vec import sqlite3 from sentence_transformers import SentenceTransformer # 加载本地中文向量模型 model SentenceTransformer(shibing624/text2vec-base-chinese) def split_text(text, max_len200, min_merge150): blocks [b.strip() for b in re.split(r\n\s*\n, text) if b.strip()] merged [] for block in blocks: if len(block) max_len: # 长文本按句子切分 sentences re.split(r(?[。]), block) merged.extend([s.strip() for s in sentences if s.strip()]) else: if merged and len(merged[-1]) len(block) min_merge: merged[-1] block else: merged.append(block) return merged def store_text(timestamp, text): conn sqlite3.connect(hindsight.db) conn.enable_load_extension(True) sqlite_vec.load(conn) conn.execute(CREATE TABLE IF NOT EXISTS memory (id INTEGER PRIMARY KEY, time TEXT, text TEXT, vec BLOB)) segments split_text(text) for seg in segments: vec model.encode(seg).astype(float32).tobytes() conn.execute(INSERT INTO memory (time, text, vec) VALUES (?, ?, ?), (timestamp, seg, vec)) conn.commit() conn.close()这一段代码的关键点是split_text函数里的合并逻辑。很多人做 RAG 切分文本只会用固定长度硬切结果就是语义片段被从中劈开检索时精确度很差。这里的段落合并逻辑虽然朴素但对“截屏文本”这种天然带段落结构的输入效果远好于固定窗口切分。模型为什么用 text2vec-base-chinese因为它是专门针对中文语义匹配训练的小模型维度是 768模型文件只有几百 MB在 CPU 上 encode 一段文字只要几十毫秒对个人项目来说非常合适。做个人记忆系统嵌入模型用通用型还是中文专用型差距在“你搜一个意思但它没命中原文关键词”这种场景下体现得最明显。中文专用模型对这种泛化问题处理得更好。3.4 查询入口语义检索 大模型生成回答存储做好了查询就是把这个过程反过来。你输入一个自然语言问题先转成向量然后在库里找最相似的文本片段再把这些片段拼成上下文交给本地大模型生成回答。def query(memory_db, question, top_k5): conn sqlite3.connect(memory_db) conn.enable_load_extension(True) sqlite_vec.load(conn) q_vec model.encode(question).astype(float32).tobytes() rows conn.execute( SELECT text, time, distance FROM memory WHERE vec MATCH ? ORDER BY distance LIMIT ?, (q_vec, top_k) ).fetchall() return rows执行查询后你会拿到top_k条历史记录每条带原始文本、截图时间戳、相似度距离。如果你只是想知道“我之前看到过什么东西”那直接看这些片段就够了。但如果你希望它像 ChatGPT 一样直接给你一个总结性回答就需要把这些片段拼进一个 prompt 里再调用本地 LLM。ollama pull qwen2.5:7b ollama run qwen2.5:7b 以下是从你的历史记忆库中检索到的相关片段... 请根据这些片段回答用户的问题。大模型这一步是可选的它最大的价值是从多个碎片信息里帮你拼出前后关联来。比如你搜“我们什么时候讨论过数据库选型”检索片段可能分别来自三个不同日子的截屏大模型可以把它们整理成“本周三会议上你提到想用 PostgreSQL原因是……”。3.5 实测效果与性能数据我跑了大概一周每天工作 8 小时看看整体数据情况。表一是采集规模的数据方便你对这个量级有概念指标数据平均每天截屏数量5秒间隔约 2400 张OCR 后识别出文字的占比约 35%每天实际入库的文本片段数约 600 段一周数据库体积约 1.2 GB包含向量和缩略图单次语义检索耗时CPU120 ms 左右平均每日 OCR 计算耗时并行处理后约 25 分钟这个体积如果用纯 JSON 存原始文本其实只有几十 MB大头都在向量化数据和截图缩略图。考虑到现代硬盘容量这个成本完全可接受。但这里要提醒一句OCR 每天消耗 25 分钟 CPU 时间对笔记本来说是个不小的热量和耗电负担。我后来优化成“只在插电时运行”电池模式下自动暂停这样能显著延长笔记本续航。还有一个优化方向是内容去重——如果两张截屏的 OCR 文本完全一样比如你停在一个页面看了很久就不再入库只更新时间戳。这个优化能再省掉大约 40% 的无效存储。4. 常见问题与排查技巧4.1 OCR 识别出来的中文是乱码或者空白这个是我被问得最多的问题。如果你安装 Tesseract 后直接跑中文 OCR很可能输出一堆乱码或者干脆什么都没识别出来。原因基本只有一个语言包没装对。你要确认三件事。第一tesseract --list-langs命令的输出里要有chi_sim。第二pytesseract调用时lang参数要写成chi_sim不是chinese也不是chn。第三确认你的 Tesseract 是 5.x 版本4.x 的老版本 LSTM 模型对中文支持要差不少。如果语言包没问题但识别率还是低试试把截屏区域放大两倍再做识别Tesseract 对字号太小的文字识别率会断崖式下降。PIL 里加一行img img.resize((img.width * 2, img.height * 2), Image.LANCZOS)就能看到明显改善。4.2 向量检索结果不准确搜出来的东西不相关如果你搜“我们上周讨论的数据库方案”出来的结果却是“数据库备份脚本报错”之类的别急着怪向量模型。问题多半出在文本切分的粒度上。OCR 文本天然带排版噪声比如左侧文件列表、底部状态栏这些内容会污染整个片段的语义。我的建议是先在切分前做一次针对性过滤。比如你的工作环境里经常出现“Chrome 书签栏”“Terminal — 80x24”这类固定文本可以维护一个停用词表OCR 之后先把这些模式匹配掉。另一个技巧是提高相似度阈值。sqlite-vec返回的 distance 是越小越相似你可以根据实测数据画一个分布看看相关结果和不相关结果的 distance 分界线在哪然后把阈值设在那里。我自己的场景里0.5 以下的结果基本可用0.65 以上的基本就是噪声了。4.3 磁盘空间被截图塞满怎么控制体积截图文件如果以 PNG 格式保留一张 1440x900 的屏幕截图大概是 2 MB 到 4 MB一天就能攒出近 2 万张图硬盘根本撑不住。我的处理方案是分三级原始 PNG 只在 OCR 完成后保留 24 小时超过 24 小时自动转成 JPEG 缩略图质量 60%宽度 800px再超过 7 天直接删除图片只保留文字片段和向量。毕竟这个系统的核心价值在文字检索截图只是辅助你查看上下文。数据库里那条time字段就是干这个用的定期跑一个清理脚本按时间删除旧图片就行。4.4 隐私与密码泄露风险是否要脱敏最后郑重提一下安全问题。hindsight 记录的是你屏幕上的所有内容包括你可能在网页表单里输入的密码、聊天软件里发的验证码、文本编辑器里临时粘贴的 Token。如果不做任何处理这些信息长期躺在数据库里一旦数据库文件被别人拿到等于所有密码裸奔。我的做法是在 store_text 之前加一道脱敏正则匹配常见的敏感模式SENSITIVE_PATTERNS [ (r[A-Za-z0-9._%-][A-Za-z0-9.-]\.[A-Za-z]{2,}, EMAIL), (r\b\d{11}\b, PHONE), (r(?i)\b(api[_-]?key|token|secret|password)\b.{0,20}, CREDENTIAL), ] def desensitize(text): for pattern, placeholder in SENSITIVE_PATTERNS: text re.sub(pattern, placeholder, text, flagsre.IGNORECASE) return text这个脱敏方案不是百分百安全但能过滤掉大部分直接暴露的敏感信息。你再怎么设置密码也架不住手滑最好的方法是不给明文密码留在索引里的机会。我在实际使用中还发现一个更隐性的风险点hindsight 里存的不只是你的信息还有你屏幕上看得到的别人的信息。比如同事发的文档、网页上展示的陌生人隐私这些数据同样敏感。所以如果你要把这个项目分享给别人用记得把脱敏模块做成默认开启的别让用户觉得“我本地的东西没事”。4.5 性能与耗电笔记本用户必须考虑的优化hindsight 持续跑 OCR 和向量化CPU 占用会稳定在 20%~30%。对台式机无所谓但如果你用笔记本键盘会发烫风扇会起飞电池会掉得肉眼可见。我的解决方案是加一个电源状态监听只有在接电源时才执行 OCR电池模式下降级为只截屏等插电后再统一补处理。这个逻辑在 macOS 上可以通过pmset -g batt解析电池状态实现Linux 上读/sys/class/power_supply/AC/online就行。另外OCR 和向量化可以并行跑。截图是串行采集处理是并行消费。用 Python 的concurrent.futures.ThreadPoolExecutor开 4 个线程处理速度能提升 3 倍左右。实测下来 OCR 的数字识别确实快不少Tesseract 本身会释放 GIL线程并行有效果。5. 从背后的想法到可用的工具我踩过最有价值的几个坎这项目做到最后我发现重要的其实不是截图和 OCR而是“你如何面对自己每天产生的信息”。之前我一直觉得碎片信息自己会沉淀想看的时候翻一翻就找得到。事实证明不会。自己做的 hindsight 虽然在技术上还有很多粗糙的地方但确实能帮我找回不少“脑子里只剩个模糊印象”的旧信息。依赖本地端到端跑模型全部用开源确保数据闭合在自己可控的范围内。坦白讲初期性能不高但调了几天之后逐渐发现这种模式的不可替代性——系统和系统之间的顺畅感是云服务给不了的。有一个很容易被忽略、但实际很影响体验的小细节时间线浏览界面。检索只是一个入口有时候你连自己“要找什么”都说不上来只是想来翻一翻“这周都干了什么”。所以我在查询入口旁边加了一个按时间倒序排列的浏览视图每行显示“某年某月某日某时 文本片段摘要”。这个功能让我用起来舒服很多也让这个工具真正具有“回看”的自然体验。我把整个项目的代码量控制在了一千行左右后续打算再加三个东西一是把前端从终端命令改成 Web 界面方便手机上也能查二是在录入数据时就做增量摘要这样长时间区间回看的时候可以直接读摘要不拖一堆原始文本三是增加一个“主动遗忘机制”允许用户对某段时间的数据做永久删除而不是把数据存在那里永远不碰。如果你也想复刻一个这样的系统我的建议是第一版千万不要贪全只要能解决“截屏 OCR 存文本 关键词搜索”这四件事就已经能带来明显的效率提升。向量检索和大模型回答是后面慢慢加都来得及的优化项。就个人体验来看所有碎片化阅读习惯的症结不在“使用时长”而在于我们只在意“读过了”的即时感受从来没有为“顺藤摸瓜”留下后路。hindsight 至少能让你想要那个后路的时候它还在那里。