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 模块(端上) | 辅助 |
| 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/ 内文档。
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.md2026-06-16 审计时的 522/1205/510+——证明ingest_wechat_to_memory/embed_memory_messages这条链路在真实持续跑,不是一次性验证。wechat-image-senderskill(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-digestskill:查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-larkskill +hermes-feishu插件:飞书机器人通道(身份 "pixel phone agent"),跟微信是两条独立的消息平台,互不影响。VisualAgentcron 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 元,延迟
关键坑(接手必读)
- 不要把 .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 加载。 - silero_vad.onnx 别从 snakers4 的 GitHub raw 拉:会被 LFS 重定向成 HTML。用
https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx。 - Tensor G3 paraformer 线程甜点 = 4,不是 8。
- 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。 keys.propertiesgitignored:讯飞凭证从这里读到BuildConfig.XFYUN_*。模板keys.properties.example。- 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+SettingsScreenembed 字段;需在 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 还没有):
- 人↔人关系图:app 只有 人↔模态(声纹/人脸/微信/手机)链接,没有 person↔person 边(KNOWS / 同事 / 同项目)。app 现在是「带富资料的扁平联系人表」,不是知识图谱。
- Organization / Project / Event 实体:app 只有
Person/Photo/Face,没有组织/项目/事件节点。 - 关系分析:旧有 关系路径(
path) / 社群发现(communities) / 待跟进(followups);app 三者皆无。 - AI agent / 工具编排:app 是纯 RAG 对话,AI 不能对数据做动作(建人 / 连边);旧有 Master→Sub-Agent + 图谱工具(
create_person/link_persons等)。 - Transcript 内容 → 实体:app 只按声纹把录音连到
Person(谁说的),没有从转写文本抽人名/项目做关联(旧 lemonPCRM 的MENTIONED_IN)——这正是下一步要补的。 - 微信抽取端上化:微信联系人/头像/聊天/朋友圈仍由 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 录音库静默清掉是大事故,见 memorylifelog-room-migration-wipe.md)。 - 改完即真机验证:app 是 24/7 录音的命脉,改
RecorderService/ 管线后务必装机看 logcat(reconcile →/pipeline running/BlockProcessor)确认录音没断。 keys.properties/local.propertiesgitignored:讯飞凭证 + Hermes 端点 →BuildConfig;sdk.dir换机要改。模板见keys.properties.example。server/frontend/packages/(旧 lemonPCRM)已废弃:不再维护、不要往里加功能、不要让 app 依赖它。
[!NOTE] 旧 lemonPCRM 的服务端架构(ACP 四层 / Master→Sub-Agent / GraphStore / 微信 server 链路)已从本文件移除。 如确需考古,查 git 历史里本文件的早期版本。