Commit 1a90350f5eb22beccce5f6d7bc16b28d6c3a4a2b
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>
Showing
2 changed files
with
52 additions
and
19 deletions
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)。 | ... | ... |