CLAUDE.md 30.2 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

项目概述

本仓库(aipcrm/)的主线 = lifelog-recorder/ —— Pixel 8 Pro 上一个完全端上、零云端的 Local-First 个人记忆 + 人脉管理(Personal CRM)原生应用:24/7 录音 + 本地中文 ASR + 声纹/人脸识别 + 端上 LLM 纪要/对话 + 以「人」为中心的联系人档案 + 全相册语义索引。所有能力跑在设备上,不依赖任何 Mac/PC 服务。

子项目 路径 形态 状态
lifelog-recorder lifelog-recorder/ Pixel 原生 Android 应用(Kotlin + Compose) 主线 v0.8
phoneuse phoneuse/ Hermes Agent 的 Android MCP 工具层(Python/Termux + Kotlin APK 双实现) 并行实验,多数工具未验证
wechat_plain wechat_plain/ Pixel 上微信 bot 的 Xposed/A11y 模块(端上) 辅助
wechat-viewer wechat-viewer/ 设备上跑的 Flask 微信查看/学习 Web 应用(Python/Termux) 辅助(Hermes 侧)
lemonPCRM server/ + frontend/ + packages/ Node.js + Express 后端 + 静态前端 已废弃

[!IMPORTANT] 方向已转向纯端上 App 早期主线是 lemonPCRM(Mac 上 Node/Express + 知识图谱 + ACP 驱动 Claude CLI 的服务端 CRM)。 现已废弃:其 Personal CRM 能力已在 lifelog-recorder 里原生端上重实现(联系人档案、人脸/声纹识别、 AI 印象、RAG 对话、照片语义检索),不再依赖 Mac。server/ frontend/ packages/ 代码仅作历史留仓, 新功能不要往里加,也不要把 app 回链到它。App 与旧 lemonPCRM 的能力对比 / 仍存差距见下文 §lifelog-recorder「App 即 Personal CRM:与旧 lemonPCRM 的能力对比」。

wechat_plain(端上微信 bot,辅助)

Pixel 上微信发送/跟踪的落地,纯设备内运行(不依赖 Mac):

  • 发送消息(唯一可用链路):am broadcast com.hermes.wechat.SEND_MSG → HermesAccessibilityService(lsposed-wechat-sendmsg 模块内)→ 启动微信 LauncherUI → 点搜索按钮 → 搜索 nickname(如"火星人")→ 点结果 → bkk 容器内 EditText 输入文本 → 点 bql 发送按钮。纯 A11y UI 自动化,绕开所有反射 / hook 失败。
  • 已废弃的两条发送路径(命名误导,勿用):LSPosed 反射 API(HermesWeChatModule.sendTextMessageDirect,Send result: true 是误报,消息不进 message 表);Frida daemon(Frida 17 + 微信 8.0.72 注入大 bundle 触发反调试崩溃)。真正能推进 message 表的只有 A11y service。

[!IMPORTANT] A11y service 启用方式 enabled_accessibility_services 系统设置易被关掉,重启:

adb shell "su -c 'settings put secure enabled_accessibility_services com.hermes.wechat.xposed/com.hermes.wechat.xposed.accessibility.HermesAccessibilityService; settings put secure accessibility_enabled 1'"

详见 wechat_plain/ 内文档。

wechat-viewer(设备上的 Flask 微信查看/学习 Web 应用,辅助)

2026-07-15 从 Pixel 设备 ~/wechat-viewer(Termux home)并入本仓库。是设备上 Hermes Agent 侧长出来的东西,不是 lifelog-recorder 那条端上 App 脉络——两者没打通。设备侧详细领域词典见 wechat-viewer/CONTEXT.md,开发日志见 wechat-viewer/CHANGELOG.md。

跑在 Android/Termux 里的 Flask Web 应用(app.py ~135KB 单文件 + Jinja2 模板 + SQLite),局域网访问(设备 :5000)。核心能力:

  • 直读微信加密库:wechat_db.py 通过 su + sqlcipher 连 /data/data/com.tencent.mm/MicroMsg/<hash>/EnMicroMsg.db(PRAGMA key + cipher_compatibility=1 + cipher_page_size=1024),多账号自动发现。密钥存在仓库里的 .wechat_key_* dotfile(本仓库 push 到自建私有 GitLab,故随代码入库)。
  • 本地加工库:local_db.py 管一个独立 SQLite(circle.db,不入库)存 processed_messages / chat_summaries / articles / daily_reports / report_images / hermes_jobs / learn_queue / learning_results / reply_whitelist。
  • Skill 驱动、非写死逻辑:所有智能任务(群日报、文章摘要、6 步学习闭环、发图)都走 Hermes CLI 子进程 + skill(_spawn_hermes_job() 统一起,强制 -s <skill> -t terminal,web,file),改 skill 不必重启 Flask。
  • 发消息/发图复用别处链路:发图直调 wechat_hook_send(LSPosed hook),跟 §wechat_plain 的 A11y 发送是又一条独立通道。

[!IMPORTANT] 入库口径 = 只带源码,不带数据/密钥外的运行态 并入时排除了二进制 SQLite 库(circle.db/wechat.db/*.db)、日志(*.log)、static/(19M 头像图)、__pycache__——这些是数据/运行产物。设备上的仓库自身 remote 是 yibo/pixelfiles(旧、非最新),本仓库这份取自设备工作目录(含未提交改动),才是最新。别再从 pixelfiles 回拉覆盖。

phoneuse(Hermes Agent 的 Android MCP 工具层,并行实验)

完整架构见 phoneuse/ARCHITECTURE.md,Sprint 任务 + 真机审计记录见 phoneuse/TASKS.md,产品 vision 原始稿见 phoneuse/phoneuse.md。本节只给定位 + 别踩的坑,细节进文件看。

定位

把 Pixel 上所有 App 虚拟化成 Hermes Agent(独立 LLM agent runtime,跑在设备 Termux 里)可调用的数据源/能力源:App Runtime Access → Semantic Extraction → Memory Graph → Agent Planning,目标是替代"截图 + OCR + 点击"那种第一代 phone use。跟 lifelog-recorder 是不同的项目脉络,2026-06-23 才拍平合并进本仓库,目前两边没有打通。

[!IMPORTANT] 不是零 Mac lifelog-recorder 的"完全本地零云端"约束不适用于 phoneuse 的 Frida 层:frida_bridge.py 跑在 Mac 上,设备端 MCP 工具通过 adb reverse tcp:8767 tcp:8767 调用 Mac 上的 frida Python 库去 attach 设备进程。判断这部分能力时不要套用 lifelog-recorder 那套"零网络"假设。

架构(两套并行实现,未合并)

  • 实现 A(Python/Termux,主链路已跑通):notification_monitor.py(root dumpsys 轮询) → event_server.py(FastAPI+SQLite 队列,:8765) → android_mcp.py(FastMCP stdio,Hermes 直连),独立工具模块各自 register_xxx_tools(mcp):ui_control_tools/device_tools/telephony_tools/ocr_tools/frida_inspect_tools。
  • 实现 B(Kotlin APK,大量 stub):AgentAccessibilityService/AgentNotificationListenerService 实时事件 → Unix socket → MCP HTTP/SSE(:8080);frida-bridge 模块是 Frida REST client 骨架。

MCP 工具现状:不要假设"写了就能用"

android_mcp.py 注册 ~60 个工具。TASKS.md 2026-06-16 真机逐个审计结论:真可用 11 / 降级 1 / 失效 23,且明确写了"之前版本所有任务全标 [x] 是错的"。典型坑:EVENT_BUS_DIR 未定义连带 connector/persona/cloud 11 个工具 NameError;_run_sqlcipher 不支持参数化导致部分微信查询工具直接 datatype mismatch。改这块前先读 TASKS.md 的审计记录,别被勾选状态骗。

Frida 深度运行时层(Layer 5,"特种部队",只打高价值 App)

通用能力(不需要逆向任何具体 App):

  • frida_call(package, script) — 注入任意调用方自写的 JS。
  • inspect_foreground_intent(package) — 枚举目标进程里所有存活 Activity,按 hasWindowFocus() 识别前台的那个,dump 它的 Intent(action/data/component/extras)。
  • dump_live_bitmaps(package, max_export) — 枚举目标进程堆里所有存活 Bitmap,导出最大的 N 张为 base64 PNG。

字符串解密 / sendMessage() 之类具体方法 hook 是per-app 逆向,不通用——微信 8.x 重度混淆已经卡死过一次(TASKS.md S3-05/S3-06),明确排除在这层通用能力之外。

[!IMPORTANT] frida_bridge.py 版本锁定 + 已知匹配 bug Mac 端跑 frida_bridge.py 必须用 frida==17.9.10 + frida-tools==14.8.0(phoneuse/.venv,start_frida_bridge.sh 一键起 adb reverse + 起桥)——更新版本的 frida-tools 对 frida 有严格版本 range 依赖,配不上设备上跑的 frida-server 17.9.10 会直接报错。 现有进程匹配(req.package in p.name)按子串匹配 frida 报告的人类可读进程标签,不是包名——微信主进程标签是 "WeChat" 不含包名子串,会回退匹配到无 UI 的 :push 子进程,导致静默空结果(不报错,看起来像目标 App 没有 Activity/Bitmap)。inspect_foreground_intent/dump_live_bitmaps 已对空结果加诊断提示,根因(frida_bridge.py 本身)未修——影响面会牵连 frida_call 其它调用方,留作后续单独修。

已知阻塞

  • 真机连续性差:开发时常掉 USB,涉及真机的验证要先确认 adb devices 能看到设备。
  • 微信相关的 Frida/A11y 经验跟 wechat_plain 是分开积累的——wechat_plain 已验证过 Frida daemon 注入微信大 bundle 会触发反调试崩溃(见上文 §wechat_plain);phoneuse 走的是 USB attach 而非常驻 daemon,崩溃模式不一定一样,但动手前先看那段记录,别重新踩同一个坑。

Hermes Agent 运行时现状(手机 ~/.hermes/,据 2026-06-24 一次性设备备份)

备份本身不进 git(含真实 API key + 真实微信消息原文,已加进根 .gitignore 的 /hermes_backup_*/),看完建议删掉或挪出仓库。这里只摘录"对以后开发有用"的事实,原始内容不再保留引用路径。

phoneuse/android_mcp.py 的真实消费方是第三方框架 Hermes Agent(Nous Research 出品,非本项目自研),跑在手机 Termux 里,靠 ~/.hermes/config.yaml 的 mcp_servers.android-phone 一条配置连过来;驱动模型是 Kimi(model.default: kimi-for-coding,base_url: api.kimi.com/coding)。Hermes 自己又在 phoneuse 之外长出了一套 skills/plugins/cron,会直接调用 phoneuse 的 MCP 工具,值得记下来:

  • memory_graph.db 确认在线增长:备份快照里 1020 person / 1255 message / 518 vector,对比 TASKS.md 2026-06-16 审计时的 522/1205/510+——证明 ingest_wechat_to_memory/embed_memory_messages 这条链路在真实持续跑,不是一次性验证。
  • wechat-image-sender skill(2026-06-23 验证通过)补上了 phoneuse 自己没做的能力:send_wechat_message/mcp_android_phone_send_wechat_message 只能发文字,发图片靠这个 skill 的 am start -a android.intent.action.SEND -t image/png 唤出微信"选择聊天"→OCR 找联系人(拼音兜底)→固定坐标 (899, 2627) 点发送,针对 1344×2992 屏幕全程验证可用。以后要给 wechat_send.py 加发图能力,这条路子是现成验证过的,不用重新摸索。该 skill 更早的笔记(image_sending_limitations.md)里提过想用 Frida bridge 做但因为"不在 Termux 里跑"放弃——这正是这次新增的 frida_inspect_tools.py/start_frida_bridge.sh 解决的那条链路,以后两边可以接上看看。
  • wechat-group-digest skill:查 EnMicroMsg.db 群消息→提取公众号文章/讨论→Kimi 摘要→Pillow/GPT-Image-2 生成报纸风格信息图→调 mcp_android_phone_send_wechat_message 发文字摘要。绑了两个 cron job(每天 00:00 / 12:00),截至备份时两个都报错失败:cannot import name 'env_float' from 'utils'——hermes-agent 自身 Python 包版本问题,不是 phoneuse 代码的锅,升级 hermes-agent 应该能解,但别假设这两个 cron 在正常工作。
  • feishu-lark skill + hermes-feishu 插件:飞书机器人通道(身份 "pixel phone agent"),跟微信是两条独立的消息平台,互不影响。
  • VisualAgent cron daemon(每 5 分钟轮询 /sdcard/VisualAgent/commands/ 执行 screenshot/tap/swipe/input/start_app):跟 phoneuse 自己的 ui_control_tools.py 是两套独立的 UI 自动化通道,目前没有合并,谁先谁后没有约定。

[!NOTE] 备份不完整 备份自带的 manifest.json 列了 5 个 skill(含 screen-wake、android-visual-automation),但备份目录里实际只有 3 个(wechat-group-digest/wechat-image-sender/feishu-lark)——别假设这份备份是手机上 ~/.hermes/ 的完整镜像。

lifelog-recorder(Pixel 8 Pro 24/7 录音 + 本地中文 ASR + 本地 LLM,v0.8)

完整接手地图、踩坑清单、命令速查见 lifelog-recorder/HANDOFF.md。本节只勒住整个项目的架构骨架 + 关键决策来源,不重复细节。

项目目标

把 Pixel 8 Pro 当成永远开机的麦克风:7×24 后台录音 → VAD 切段 → 中文 ASR 转文字 → 入库 → 按时间轴显示和搜索 → LLM 总结成纪要。最终目的是把所有「我说过 / 别人对我说过」的内容沉淀成可搜索、可回放的个人记忆库,并在 app 内自成端上 Personal CRM(photolog):某段语音 → 声纹 → 联系人档案,照片 → 人脸 → 同一联系人。整套 CRM 跑在设备上,已取代旧的 Mac 端 lemonPCRM。

约束:完全本地 / 零云端依赖 / 月成本 0 元 / 隐私走 0 网络。

架构骨架

block-based diarization-first(2026-06-07 起,commit 07f3d22):弃掉「录一句·转一句·实时聚类」(短句 embedding 噪声大、实时滑动聚类污染 anchor → 声纹串人)。改为攒块 → 整块 diarize → 逐 turn 转写,从源头产出单说话人干净 embedding。没有实时字幕,文字在块关闭后出。

Foreground Service (foregroundServiceType=microphone, WAKE_LOCK partial)
  └ AudioRecord 16k mono PCM16  (VOICE_RECOGNITION source, no AGC/NS)
     └ Silero VAD ONNX (500ms 静音切段 / 30s 强切) ── 段不再直接入库
        └ BlockBuffer:speech-only PCM 攒「块」(filesDir/blocks/<ts>/audio.pcm + manifest)
           关块:静音≥5min 或 块≥10min ──Channel──> BlockProcessor (与采集解耦)
              └ DiarizationEngine: pyannote 分割 + campplus + 聚类 → turns[start,end,speaker]
                 └ 每 turn: paraformer 转写 + campplus 干净嵌入 + SpeakerClusterer.assign 全局认人
                    └ Room SQLite (turn = 时间轴一行: Segment/Transcript/Summary/SpeakerProfile/Chat*)
                       └ Compose 底部 4-tab (人物 / 录音 / 对话 / 设置);「录音」内含 4 顶部子 tab (录音 / 时间轴 / 声纹 / 纪要)
                          ├ SummaryWorker:按时间间隔切「会话」→ 本地 llama-swap(8080) Qwen3.5-2B 出纪要
                          ├ ChatScreen:本地 2B/4B(llama-swap 8080) + Hermes(8642→Kimi) 多端点对话
                          └ photolog/CRM 层 (PhotologScreen/PersonProfileScreen/PhotologViewModel + vision/)
                               人物 tab = 「我管理的人物」;通讯录(微信+手机两源合并)是池子(勾选纳入);相册人脸标注/绑声纹自动入库
                               相册 tab = 全相册自动索引(MediaStore 全量):Tier-0 人脸/时间秒级铺 + Tier-1 VL 场景描述充电补 + 搜图(关键词/语义)

声纹纠偏 L1+L2(SpeakerClusterer):L1 = reclusterAll 和 assign 都冻结人工命名 anchor 不漂移(止住「秦子琛吸宜博→质心漂→吞更多」雪球);L2 = 重命名/合并时 rebuildAnchor 用该人全部段均值重建质心(多 anchor,取代单段注册)。用户流程:听一段 → 命名/合并 → 「重新聚类」。阈值默认 0.45(设置页 0.35–0.65)。CAMPPlus 在短中文段上区分度有天花板,详见 memory lifelog-block-diarization-pipeline.md。

⚠️ 旧的周期 DiarizationWorker/DiarizationScheduler 已删除(diarize 现在在块关闭时当场做)。WorkManager 每 worker 有 10min 硬上限,是把 diarize 放进常驻前台服务而非 WM worker 的原因。

本地 LLM 栈(v0.6,零云端):Termux 自编 llama.cpp HEAD + llama-swap v223 监听 127.0.0.1:8080,按 model 名(2b/4b)热切换 Qwen3.5-{2B,4B}-Q4_K_M。纪要 worker + App 对话都打 8080;Magisk service.d 开机自启。完整部署/坑见 memory pixel-local-llm-stack.md + HANDOFF.md。

photolog / 人脉 CRM 层(v0.7 新增):在录音之外把 lifelog 长成端上 mini-CRM。PhotologScreen(人物 tab + 相册 tab)、PersonProfileScreen(联系人档案:tab 式 + 记录搜索 + 微信聊天/朋友圈/AI 印象 + 「🎤 录一段绑定 TA」当场录声纹)、PhotologViewModel 统管。两个人物池分离:「我管理的人物」(CRM 关心的人,人物 tab 显示)vs「通讯录全部联系人」(池子,选人页里勾选纳入)。池子有两个来源:微信(Mac 推的 enroll 数据)+ 手机系统通讯录(READ_CONTACTS → ContactsContract,contactRef phone:<id>,按手机号/姓名跨源自动合并);Person 还存手填 realName(真名)/resume(简历),档案页可编辑。vision/(FaceEngine/FaceMatcher/PhotoProcessor/PhotoScheduler/PhotoFaceWorker/PhotoVlWorker/PhotoVlState/GalleryScanner/VisionClient/EmbedClient/WeChatEnrollWorker)做相册扫描 + 人脸聚类 + 微信头像锚点入库;微信头像配方与「锚点 embedding 必须端上算」见 memory wechat-avatar-face-enrollment.md。声纹绑人 ↔ 人脸标注 ↔ 微信联系人三者打通到同一 Person。

全相册自动索引 / 分层管线(v0.8 新增):把手机整个相册自动跑成可搜索内容。核心矛盾:本地 VL(Qwen2.5-VL-3B)真机 ~30-40s/张,几千张全量 VL 按天计 → 分层。GalleryScanner 游标遍历 MediaStore 全量,只存 content:// uri 不拷文件(拷一份撑爆私有目录),mediaId 去重增量;photos 加 source/mediaId/scanState/captionEmbedding(Room v14)。Tier-0 PhotoFaceWorker(15min,battery-not-low):扫描 + ML Kit 人脸 + 512d emb + GPS,秒级全量铺好,立刻按人/时间搜;Tier-1 PhotoVlWorker(15min,+charging):Qwen-VL 场景描述 + caption 语义向量,优先级队列(有人脸>近期),慢慢补。scanState:0 待索引→1 人脸已铺→2 已出描述;imported 源(手动导入)走同管线,只是 decode 走 file path。搜图:关键词(caption+人名 LIKE,中文子串比 FTS4 靠谱)+ 语义(EmbedClient 打 llama-swap embed 模型出 query 向量 → 全库 caption 向量余弦 top-K;没配 embed 模型则优雅降级,只用关键词)。VL 控制 PhotoVlState(singleton,paused 持久化):相册页运行中显「暂停/停止」、空闲显「立即理解/继续」,正在 VL 的格子转圈,格子角标 待索引/待理解/已描述;暂停连充电时的周期 worker 也尊重。详见 HANDOFF.md §四 photolog 段。

技术栈:Kotlin 2.1 + Compose BOM 2024.12.01 + Room 2.6.1 + WorkManager 2.10 + OkHttp 4.12,AGP 8.7.3,minSdk 30 / targetSdk 35(Android 14 强制 foregroundServiceType 起步),arm64-v8a 单 ABI。

接口

内部组件接口(pipeline,详见 lifelog-recorder/HANDOFF.md §四):

上游 → 下游 数据形式
AudioCapture SharedFlow VAD FloatArray 30ms 帧 (512 samples)
Vad.acceptWaveform() poll Vad.front() / pop() SpeechSegment(start, samples: FloatArray)
BlockBuffer.append() sync filesystem filesDir/blocks/<startTs>/{audio.pcm, manifest.json}(speech-only PCM + run 偏移)
BlockBuffer.close() Channel BlockProcessor.process() 块目录(关块条件:静音≥5min 或 块≥10min)
DiarizationEngine.diarize() sync — List<Turn(startSec, endSec, speaker)>(pyannote 分轮)
ParaformerEngine.transcribeFloat() sync — OfflineRecognizerResult(text, tokens, timestamps, lang, emotion, event)(逐 turn)
SegmentWriter sync filesystem filesDir/segments/YYYY-MM-DD/HH-MM-SS-MS.opus(turn 切片)
Repository.segments/transcripts suspend Room DAOs in data/Daos.kt(turn = 一行)

外部接口:

  • ModelDownloader → https://hf-mirror.com/csukuangfj/sherpa-onnx-paraformer-zh-2023-09-14/(断点续传,fallback huggingface.co)
  • XfyunIatClient → wss://iat.xf-yun.com/v1(讯飞大模型,仅作云复核 fallback)
  • 未来 Summary Worker → http://127.0.0.1:8642/v1/chat/completions(hermes gateway 转发到 Kimi)

对外暴露的 IPC:RecorderService 仅做自启/暂停(startForegroundService / stopService),状态走 RecorderState (StateFlow) 推给 UI;没有 binder/广播接口。

历史沿革 / PoC 失败与成功记录

这一节是为什么选了 paraformer 而不是 whisper / 云 ASR 的完整溯源。后面 session 想换技术栈前必读。

Round 1 — 全云 ASR 路线(弃):最初想用「VAD 段 → 上传讯飞 / 百度 / Groq Whisper」流式 ASR。讯飞「中文识别大模型」WebSocket 客户端已经写完(asr/XfyunIatClient.kt,鉴权 + 三段帧协议 + 增量结果解析),但发现按次计费下 24/7 用量会冲到月 5000+ 元。「讯飞流式听写区间包 80 元/月 unlimited」是消费端「讯飞听见」App 的卡,不是开发者 API——这是用户和我都踩过的坑。

Round 2 — whisper.cpp on-device(弃):在 Pixel 8 Pro Termux 编了 whisper.cpp + NEON 优化,跑 95s 中文 TTS sample:

模型 线程 RTF 速度 中文质量
ggml-tiny.bin 4 0.26 3.8× realtime ❌ 输出乱码「战亡未来一年」,中文不可用
ggml-small-q5_1.bin (181 MB) 8 (最快) 1.66 0.60× realtime ✓ 转写正确
ggml-small.bin (fp16, 465 MB) 4 1.76 0.57× 同 q5_1(量化省的全被 ARM dequant 吃回)

结论:whisper-small 任何配置都 ,连续录会越积越多。tiny 够快但中文崩。on-device whisper 路线直接砍掉。

Round 3 — sherpa-onnx + 中文专门优化模型(采用):阿里 DAMO 的 paraformer-zh 跟 SenseVoice 都是 ONNX 部署、Android 友好。同样 95s sample:

模型 测试环境 RTF 速度 备注
paraformer-zh int8 (232 MB) t=4 proot Ubuntu 0.068 14.7× realtime 选定
paraformer-zh int8 t=8 proot 0.099 10.1× 8 线程反而慢,X3+A715 协调开销
SenseVoice int8 (228 MB) t=4 proot 0.092 10.9× 带情绪/事件 metadata,但慢
paraformer-zh int8 t=4 真机 Android app 0.048 20.94× realtime proot 摘掉后再快 43%
paraformer-zh int8 t=4 真实活语音(用户对手机说) — 20-32× 1-10s 段,~50-300ms 转写

结论:on-device paraformer 完胜。月成本 0 元,延迟

关键坑(接手必读)

  1. 不要把 .onnx 当 asset mmap:sherpa-onnx v1.13.2 native code 对 asset 压缩兼容差,silero VAD 直接 SIGSEGV。已加 androidResources.noCompress.add("onnx") 但保险起见在 RecorderService.runPipeline 里把 VAD 模型从 assets 拷到 filesDir 后再用 file path 加载。
  2. silero_vad.onnx 别从 snakers4 的 GitHub raw 拉:会被 LFS 重定向成 HTML。用 https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx。
  3. Tensor G3 paraformer 线程甜点 = 4,不是 8。
  4. AID_INET (3003) gotcha:跨 root + adb 调试 Termux 用户态时必须 su -G 3003 10288 -c "...",否则 DNS 全断。详见 ~/.claude/projects/-Users-yibo-Documents-aipcrm/memory/magisk-su-android-inet-group.md。
  5. keys.properties gitignored:讯飞凭证从这里读到 BuildConfig.XFYUN_*。模板 keys.properties.example。
  6. 232 MB paraformer 模型不进仓库:首次启动从 hf-mirror.com 下到 app.filesDir/models/paraformer-zh/。dev 加速可以预先 adb push 进去(HANDOFF.md §三)。

未来计划(按优先级)

完整待办、估时、技术路线见 lifelog-recorder/HANDOFF.md §五。

✅ v0.2–v0.9 已完成:Opus 编码 / BOOT 自启 + 电池白名单 / FTS4 中文搜索 / 段播放 / 声纹在线聚类 / 会话级纪要(按时间间隔切会话) / 底部 4-tab + 子 tab UI / App 内对话(本地 2B/4B + Hermes) / 本地 LLM 栈(llama-swap) / Room 迁移(去 destructive) / block-based diarization-first 录音管线(v0.7,攒块→整块分轮→逐 turn 转写) / 声纹纠偏 L1 冻结 anchor + L2 多 anchor 重建 / 设置页(SettingsScreen:纪要模型后端 / 聚类阈值 / llama-swap 端点 / 电池豁免) / photolog 人脉 CRM 层(人物档案 + 相册人脸 + 微信通讯录/朋友圈/AI 印象) / 存储 TTL(超期删 .opus,文字永留) / 全相册自动索引(v0.8:MediaStore 全量扫描 + Tier-0 人脸/Tier-1 VL 分层管线 + 关键词/语义搜图 + VL 暂停/停止控制,Room v14) / 微信抽取完全端上化(v0.8 P2 #6:SQLCipher key 端上推导 + 联系人/头像/聊天/朋友圈全不依赖 Mac) / Transcript→Person 提及关系(v0.9 P0 #5:词典反查 + MentionScanner 实时扫 + mentions 表 + 「被提到」tab) / 人↔人共现边(v0.9 P1 #1:RelationBuilder + relations 表 + 「相关」tab + CommunitiesScreen) / 待跟进 & 话题实体(FollowupExtractor + TopicExtractor,4B LLM 抽取)。

剩余摘要:

  • ⭐ 语义搜图落地(代码已完成:EmbedClient + SettingsScreen embed 字段;需在 Pixel llama-swap config.yaml 加 embed 模型才生效,中文 GGUF 如 bge-small-zh,参见 HANDOFF.md §七 调试陷阱)
  • ✅ 内部 Transcript 实体 → Person 表(已完成:crm/MentionExtractor.kt + MentionScanner 词典反查 + BlockProcessor 实时扫 + PersonProfileScreen 「被提到」tab + RelationBuilder 共现边)
  • ✅ 微信抽取端上化(已完成 2026-06-10:SQLCipher 端上解密 + 联系人/头像/朋友圈全端上,不依赖 Mac)

App 即 Personal CRM:与旧 lemonPCRM 的能力对比

App(photolog)已覆盖、并在「联系人」维度超过旧 lemonPCRM 的核心:

能力 旧 lemonPCRM(Mac/server) 现 app(端上)
联系人实体 Person 图节点 Person(融合 声纹/人脸/微信/手机 四模态,更丰富)
多源导入 微信 / LinkedIn / X / 会议 / 照片 微信 + 手机通讯录 + 手动 + 24/7 录音(独有)
每人聚合视图 社媒动态 / 纪要 / 文件 / 照片 聊天 / 朋友圈 / 照片 / 录音 / AI 印象 / 简历(PersonProfileScreen 四 tab)
AI Master Agent over 图谱(工具编排) RAG 对话 over 转写/纪要(LifelogRetriever)+ AI 印象
照片 人脸聚类 + CLIP 检索 人脸聚类 + 语义检索(端上 ML Kit + ArcFace + Qwen-VL + embed)
部署 Mac Node 服务(依赖 Claude CLI) 纯端上(更隐私 / 零成本 / 零网络)

仍存差距(旧 lemonPCRM 有、app 还没有):

  1. 人↔人关系图:app 只有 人↔模态(声纹/人脸/微信/手机)链接,没有 person↔person 边(KNOWS / 同事 / 同项目)。app 现在是「带富资料的扁平联系人表」,不是知识图谱。
  2. Organization / Project / Event 实体:app 只有 Person/Photo/Face,没有组织/项目/事件节点。
  3. 关系分析:旧有 关系路径(path) / 社群发现(communities) / 待跟进(followups);app 三者皆无。
  4. AI agent / 工具编排:app 是纯 RAG 对话,AI 不能对数据做动作(建人 / 连边);旧有 Master→Sub-Agent + 图谱工具(create_person/link_persons 等)。
  5. Transcript 内容 → 实体:app 只按声纹把录音连到 Person(谁说的),没有从转写文本抽人名/项目做关联(旧 lemonPCRM 的 MENTIONED_IN)——这正是下一步要补的。
  6. 微信抽取端上化:微信联系人/头像/聊天/朋友圈仍由 Mac 一次性抽取(解密 EnMicroMsg.db + 抠头像)推到 filesDir/wx_enroll/,再由 WeChatEnrollWorker 端上入库。手机系统通讯录已完全端上;微信抽取是「纯 App」最后一块拼图。

下一步:Transcript 实体 → 内部 Person 表(取代旧「回链 lemonPCRM」)

不再回链外部 server,改在 app 内部打通:从 Transcript.text / Summary 抽实体(人名/项目/地点,端上 LLM 或规则 NER)→ 与本地 Person 表匹配 → 建一张「提及」关系(新表 mentions(segmentId, personId, ...) 或 Person↔Segment 边)。让 PersonProfileScreen 除了现有「TA 自己说的」(声纹匹配段)之外,还能显示「TA 被哪些录音提到、说了什么」。这直接补差距 #5;同段被同时提到的人 → 共现边,又为差距 #1(人↔人关系图)打基础。

上面 6 项差距的可执行 TODO 清单(带优先级 P0–P3 + 做法)见 lifelog-recorder/HANDOFF.md §「端上 CRM 补齐 — TODO」。路线:P0 #5 → P1 #1 → P2 #6。

注意事项

  • 构建:cd lifelog-recorder && JAVA_HOME=/opt/homebrew/opt/openjdk@17 ./gradlew :app:assembleDebug,adb install -r ...。无测试框架。改 Room schema 必须写 Migration(AppDatabase 已去 destructive 改 fail-loud;24/7 录音库静默清掉是大事故,见 memory lifelog-room-migration-wipe.md)。
  • 改完即真机验证:app 是 24/7 录音的命脉,改 RecorderService / 管线后务必装机看 logcat(reconcile → / pipeline running / BlockProcessor)确认录音没断。
  • keys.properties / local.properties gitignored:讯飞凭证 + Hermes 端点 → BuildConfig;sdk.dir 换机要改。模板见 keys.properties.example。
  • server/ frontend/ packages/(旧 lemonPCRM)已废弃:不再维护、不要往里加功能、不要让 app 依赖它。

[!NOTE] 旧 lemonPCRM 的服务端架构(ACP 四层 / Master→Sub-Agent / GraphStore / 微信 server 链路)已从本文件移除。 如确需考古,查 git 历史里本文件的早期版本。