Commit 1a90350f5eb22beccce5f6d7bc16b28d6c3a4a2b

Authored by yibo
1 parent b4757640

docs(lifelog): 文档同步到 v0.6 — 声纹聚类 / 5-tab / 会话纪要 / 对话 / 本地 LLM 栈

- CLAUDE.md:版本表 v0.1→v0.6;架构骨架补 SpeakerClusterer 在线聚类、
  5-tab UI、会话级 SummaryWorker、ChatScreen 多端点;新增「本地 LLM 栈」段
  (llama.cpp HEAD + llama-swap 8080 跑 Qwen3.5 2b/4b);未来计划标注已完成项。
- HANDOFF.md:§一 现状改写;§二 文件清单补 chat/ summary/ SpeakerClusterer/
  HermesClient,AppDatabase 改 v3+迁移;§五 加 v0.6 已完成项;
  新增「§六 本地 LLM 栈」接手要点(部署/接线/thinking 坑/G3 实测速度)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CLAUDE.md
... ... @@ -10,7 +10,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
10 10 |---|---|---|---|
11 11 | **lemonPCRM** | `server/` + `frontend/` + `packages/` | Node.js + Express 后端 + 静态前端 | 主线 |
12 12 | **wechat_plain** | `wechat_plain/` | Android Xposed/A11y 模块 + Pixel 部署脚本 | 主线 |
13   -| **lifelog-recorder** | `lifelog-recorder/` | Pixel 原生 Android 应用(Kotlin + Compose) | v0.1 (2026-06) |
  13 +| **lifelog-recorder** | `lifelog-recorder/` | Pixel 原生 Android 应用(Kotlin + Compose) | v0.6 (2026-06) |
14 14  
15 15 **lemonPCRM** — lemonAI 出品的 Local-First AI 个人人脉管理系统(Personal CRM)。以人为中心、以对话为链接,通过知识图谱管理微信、LinkedIn、X 等多平台社交关系,将每个联系人与其社媒动态、会议纪要、文件、照片全面关联。
16 16  
... ... @@ -163,7 +163,7 @@ Launcher (ProcessLauncher spawn / NoneLauncher 无操作)
163 163  
164 164 详见 `server/services/wechat/README.md`。
165 165  
166   -## lifelog-recorder(Pixel 8 Pro 24/7 录音 + 本地中文 ASR,v0.1)
  166 +## lifelog-recorder(Pixel 8 Pro 24/7 录音 + 本地中文 ASR + 本地 LLM,v0.6)
167 167  
168 168 > 完整接手地图、踩坑清单、命令速查见 `lifelog-recorder/HANDOFF.md`。本节只勒住整个项目的**架构骨架 + 关键决策来源**,不重复细节。
169 169  
... ... @@ -179,12 +179,16 @@ Launcher (ProcessLauncher spawn / NoneLauncher 无操作)
179 179 Foreground Service (foregroundServiceType=microphone, WAKE_LOCK partial)
180 180 └ AudioRecord 16k mono PCM16 (VOICE_RECOGNITION source, no AGC/NS)
181 181 └ Silero VAD ONNX (sherpa-onnx 包装,500ms 静音切 / 30s 强切)
182   - └ paraformer-zh int8 ONNX (sherpa-onnx JNI, 4 threads, 25× realtime)
183   - └ Room SQLite (Segment / Transcript / Summary)
184   - └ Compose LazyColumn 时间轴 UI (倒序,按 startTs)
185   - ↓ (待办) hermes gateway 8642 → Kimi → 纪要 Worker
  182 + ├ paraformer-zh int8 ONNX (sherpa-onnx JNI, 4 threads, ~20× realtime)
  183 + └ 3dspeaker campplus 192-d 嵌入 → SpeakerClusterer 在线聚类(自动「说话人N」,方案B)
  184 + └ Room SQLite v3 (Segment/Transcript/Summary/SpeakerProfile/Chat*; addMigrations, 已去 destructive)
  185 + └ Compose 5-tab UI (录音 / 对话 / 时间轴 / 声纹 / 纪要)
  186 + ├ SummaryWorker:按时间间隔切「会话」→ 本地 llama-swap(8080) Qwen3.5-2B 出纪要
  187 + └ ChatScreen:本地 2B/4B(llama-swap 8080) + Hermes(8642→Kimi) 多端点对话
186 188 ```
187 189  
  190 +**本地 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。
  191 +
188 192 技术栈: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。
189 193  
190 194 ### 接口
... ... @@ -245,13 +249,15 @@ Foreground Service (foregroundServiceType=microphone, WAKE_LOCK partial)
245 249  
246 250 ### 未来计划(按优先级)
247 251  
248   -完整待办、估时、技术路线见 `lifelog-recorder/HANDOFF.md` §五。摘要:
  252 +完整待办、估时、技术路线见 `lifelog-recorder/HANDOFF.md` §五。
  253 +
  254 +✅ **v0.2–v0.6 已完成**:Opus 编码 / BOOT 自启 + 电池白名单 / FTS4 中文搜索 / 段播放 / **声纹在线聚类(方案B 自动 diarization)** / **会话级纪要(按时间间隔切会话)** / **5-tab UI** / **App 内对话(本地 2B/4B + Hermes)** / **本地 LLM 栈(llama-swap)** / **Room 迁移(去 destructive)**。
249 255  
250   -- 🔥 **Opus 编码**(WAV 24/7 跑 ~3 GB/天,必须)
251   -- 🔥 **自启动 + 电池白名单**(BOOT_COMPLETED + REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,让 v1 真能脱离 dev 环境跑)
252   -- ⭐ **声纹识别 / speaker diarization**(用户语音里提了 3 次,sherpa-onnx `Speaker.kt` 现成可用)
253   -- ⭐ **FTS5 中文搜索**(Room + ICU tokenizer)
254   -- ⭐ **LLM 纪要 Worker**(cron + idle 触发 → hermes gateway → Kimi)
  256 +剩余摘要:
  257 +- ⭐ **存储 TTL**(删超 N 天 .opus,文字永留)
  258 +- ⭐ **设置页**(存储用量 / 模型切换 / 暂停时段)
  259 +- ⭐ **多 anchor 平均**(连选多段标同一人提高匹配率)
  260 +- ⭐ **回链 lemonPCRM**(Transcript 实体 → GraphStore `MENTIONED_IN`,v2)
255 261  
256 262 ### 与 lemonPCRM 的关系
257 263  
... ...
lifelog-recorder/HANDOFF.md
... ... @@ -19,9 +19,9 @@
19 19  
20 20 ## §一 这是什么
21 21  
22   -24/7 后台录音 → VAD 切段 → 本地 paraformer 中文 ASR → 入库 → 时间轴显示 → (未来)LLM 纪要。
  22 +24/7 后台录音 → VAD 切段 → 本地 paraformer 中文 ASR → 在线声纹聚类(自动「说话人N」)→ 入库 → 5-tab UI(录音/对话/时间轴/声纹/纪要)→ 按时间间隔切「会话」→ 本地 Qwen3.5 LLM 出纪要 + App 内对话。
23 23  
24   -**完全本地,零云端依赖**(讯飞客户端只作云复核 fallback)。
  24 +**完全本地,零云端依赖**(讯飞客户端只作云复核 fallback;纪要/对话走本机 llama-swap,Hermes→Kimi 仅作对话里一个可选端点)。v0.6(2026-06-04)起接了本地 LLM 栈,详见 §六。
25 25  
26 26 ### 实测性能(Pixel 8 Pro / Tensor G3 / Android 16 BP2A)
27 27  
... ... @@ -40,7 +40,14 @@ APK 大小: 62 MB(含 34 MB sherpa-onnx .so + 0.6 MB silero VAD + 3 MB samp
40 40 ```
41 41 app/src/main/kotlin/com/lemon/lifelog/
42 42 ├── LifelogApp.kt — Application,建 NotificationChannel
43   -├── MainActivity.kt — Compose UI,时间轴 + 搜索 + 声纹 enroll dialog
  43 +├── MainActivity.kt — Compose 5-tab 壳 + 时间轴/声纹/纪要 tab + 全 CRUD/批量/筛选
  44 +├── ChatScreen.kt — 对话 tab(ChatGPT 式:会话列表 + 流式气泡 + 端点切换)
  45 +├── chat/
  46 +│ ├── ChatConfig.kt — 聊天端点配置(本地 8080/2b/4b 存 SharedPreferences;Hermes 走 BuildConfig)
  47 +│ └── ChatClient.kt — OpenAI 兼容流式客户端(本地目标传 enable_thinking=false)
  48 +├── summary/
  49 +│ ├── SummaryWorker.kt — ★ 会话级纪要:按时间间隔切会话 → 本地 llama-swap 2B
  50 +│ └── SummaryScheduler.kt — WorkManager periodic + enqueueUniqueWork(KEEP) 去重
44 51 ├── service/
45 52 │ ├── RecorderService.kt — 前台服务,pipeline 主体 ←★ 改 ASR 流程改这里
46 53 │ ├── RecorderState.kt — StateFlow 把服务状态推到 UI
... ... @@ -51,14 +58,16 @@ app/src/main/kotlin/com/lemon/lifelog/
51 58 │ └── SegmentPlayer.kt — MediaPlayer 包装,Opus 段原音播放(系统原生支持)
52 59 ├── asr/
53 60 │ ├── ParaformerEngine.kt — sherpa-onnx paraformer 包装 ←★ 主 ASR
54   -│ ├── SpeakerEmbedder.kt — sherpa-onnx 3dspeaker campplus 192-d 嵌入 + cosineSim
  61 +│ ├── SpeakerEmbedder.kt — sherpa-onnx 3dspeaker campplus 192-d 嵌入 + cosineSim + l2normalize
  62 +│ ├── SpeakerClusterer.kt — ★ 方案B 在线聚类:自动发现「说话人N」+ reclusterAll 离线重算
55 63 │ ├── SpeakerMatcher.kt — 回填工具:用当前 profile 集重算所有段的 speakerId
  64 +│ ├── HermesClient.kt — OpenAI 兼容(纪要 worker 用,指本地 llama-swap;readTimeout 600s)
56 65 │ ├── ModelDownloader.kt — 首次启动下模型(paraformer 232 MB + speaker 28 MB)
57 66 │ └── XfyunIatClient.kt — 讯飞大模型 WS 客户端,仅 fallback / 云复核用
58 67 └── data/
59   - ├── Entities.kt — Segment / Transcript / Summary / SpeakerProfile / TranscriptFts
60   - ├── Daos.kt — observeTimeline + searchTimeline (FTS4) + SpeakerDao
61   - ├── AppDatabase.kt — Room v2,destructive migration(v0.3 schema 大改)
  68 + ├── Entities.kt — Segment / Transcript / Summary / SpeakerProfile / TranscriptFts / ChatConversation / ChatMessage
  69 + ├── Daos.kt — observeTimeline + searchTimeline(FTS4) + Speaker/Summary/ChatDao + 范围查询
  70 + ├── AppDatabase.kt — Room v3,addMigrations(MIGRATION_2_3),★ 已删 fallbackToDestructiveMigration(改 schema 不写迁移会崩,绝不静默清库)
62 71 └── Repository.kt
63 72 ```
64 73  
... ... @@ -159,6 +168,7 @@ VAD 参数(在 `RecorderService.runPipeline` 里调):
159 168 - v0.3 (2026-06):声纹识别(3dspeaker campplus)/ FTS4 中文搜索
160 169 - v0.4 (2026-06):段播放 (MediaPlayer Opus) / bottom sheet 操作菜单 / enroll 后自动回填老段
161 170 - v0.5 (2026-06):hermes api_server 端点 + SummaryWorker + 纪要 UI section
  171 +- v0.6 (2026-06-04):**声纹在线聚类**(SpeakerClusterer,自动「说话人N」+ reclusterAll)/ **5-tab UI**(录音/对话/时间轴/声纹/纪要,时间轴全 CRUD + 批量删导出 + 按说话人/时间/未匹配筛选 + 排序)/ **真暂停**(停采集留模型)/ **会话级纪要**(按时间间隔切会话、可调阈值、要点带 [HH:mm:ss] 点击跳回时间轴、折叠头显示说话人名单)/ **App 内对话**(本地 2B/4B + Hermes 多端点流式)/ **本地 LLM 栈**(llama.cpp HEAD + llama-swap 8080,纪要+对话全本地,见 §六)/ **Room v3 迁移**(去 destructive 改 fail-loud)
162 172  
163 173 剩下:
164 174  
... ... @@ -221,6 +231,23 @@ skills:
221 231 用 hermes 自己的 config 模块改最稳(`load_config`/`save_config`,保留其他配置)。改完重启 gateway。
222 232 **实测:prompt 12,500 → 399 token(-97%),纪要正常生成。**
223 233  
  234 +### §六 本地 LLM 栈(llama-swap,v0.6 — 纪要 + 对话的真后端)
  235 +
  236 +零云端推理。**完整部署/坑清单见 memory `pixel-local-llm-stack.md`**,这里只列接手要点:
  237 +
  238 +```
  239 +~/llama.cpp/ — 自编 HEAD(gh-proxy.com clone 绕墙;server-tools 缺 spawn.h → 手写 Bionic 兼容头丢 $PREFIX/include)
  240 +~/llama-swap/llama-swap — v223 静态 arm64 binary,监听 127.0.0.1:8080
  241 +~/llama-swap/config.yaml — model "2b"/"4b" → llama-server + Qwen3.5-{2B,4B}-Q4_K_M.gguf,ttl 300 热切换
  242 +~/models/Qwen3.5-{2B,4B}-Q4_K_M.gguf — hf-mirror.com/unsloth 下,2.74GB / 1.28GB
  243 +~/start-llama.sh — launcher(起 llama-swap),被 Magisk service.d 调用
  244 +```
  245 +
  246 +- **App 接线**:`ChatConfig` 本地默认 `8080`/`2b`/`4b`(SharedPreferences 可改);`keys.properties` = `8642`(对话的 Hermes 选项 → Kimi);`SummaryWorker.LLM_BASE_URL/LLM_MODEL` = `8080`/`2b`(纪要,已解耦 keys.properties)。
  247 +- **Qwen3.5 默认开思考**:纪要 + 本地对话都要传 `chat_template_kwargs:{enable_thinking:false}` 否则 token 被 `<think>` 吞、流式开头吐字面 `"null"`(org.json.optString 坑,ChatClient.parseDelta 已用 `isNull` 防)。
  248 +- **G3 实测**(Q4,热降频):2B ~12 t/s 生成 / 65 prefill(一篇纪要 ~48s);4B ~4 / 24(~2.6min)。
  249 +- 启动器手测:`su -G 3003 10302 -c 'bash ~/start-llama.sh'`,再 `curl 127.0.0.1:8080/v1/models`。
  250 +
224 251 ### 自启(Magisk service.d)
225 252  
226 253 Termux:Boot 未装(且无匹配签名 APK);已 root,用 Magisk service.d 更稳(不需开 Termux app)。
... ...