-
24/7 录音 .opus 只增不减(实测 ~32MB 攒几天),加保留期清理: - StorageManager.prune:按天目录名删除早于 N 天的 segments/yyyy-MM-dd/(不碰 DB, Segment 行保留→文字永留;audioPath 仍指向已删文件,播放对缺失文件优雅 no-op; 不动在途 blocks/)。+ audioUsageBytes 统计占用。 - StorageTtlWorker + StorageScheduler:每天一次 periodic 清理(电池不低时),LifelogApp 注册 - 设置页「存储」段:录音占用显示 + 保留时长下拉(永久/7/14/30/60/90 天)+ 立即清理按钮 - 默认「永久保留」(ttl=0 不删):自动删录音是破坏性的,由用户主动选保留期才生效 真机验证:占用读数 31.9MB 与 du 一致;UI 渲染正常。未跑真实删除(默认关闭)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
贪心单遍聚类会把同一人切成多簇:早期噪声段一旦另起簇,后续同人段按「最近质心」 各自归簇,再不会并回(实测主说话人被拆成 说话人11+12+29,质心却差 0.65+)。 - 新增 SpeakerClusterer.mergeOverSplit:重聚类后做 agglomerative 合并——反复把质心 余弦 ≥ mergeThreshold 的两簇并掉,质心用两簇全部成员段重算(比存的封顶滑动均值 更准,能暴露被掩盖的过切:实测 说话人11↔12 成员重算余弦 0.749)。 约束:不并两个人工命名簇;一人工一自动并入人工保留名字;都自动并入成员多的。 合并后刷新被并段的 speakerId 与相似度。 - reclusterAll / clearAllAndRecluster 收尾调用合并;mergeThreshold 默认 0.55,设置页可调 - 真机实测:32→29 簇,主说话人统一为单簇(580 段),残留仅 4 对 0.50–0.52 小簇待人工 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
档案页「🔗 合并重复」→ 独立选人子页(搜索+全部人物列表,含微信池) → 确认 → 并入当前人物: - 人脸归属改派、声纹/人脸锚点/头像/昵称-备注-微信号/手机号/真名/简历/AI 印象 缺啥补啥 - contactRef 优先保留 wechat: 引用,合并后微信聊天/朋友圈/AI 印象不丢 - 被合并的那条删除;当前人物存活并置 managed - DAO: FaceDao.reassignPerson + PersonDao @Update - 选人改用整页子页(MergePersonPickerScreen),不再塞进 AlertDialog(之前被布局挤没、看不到搜索/列表/按钮) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
之前「📱手机通讯录」一点就全量导进池子,改为跟微信导入一致的列表+多选体验: - 新增 PhoneContactPickerScreen:READ_CONTACTS 后读系统联系人成内存列表(不入库), 按拼音 A–Z 分组+搜索,每行显示 人名+手机号 带复选框,支持全选/取消全选 - 勾选「导入选中」才入库,直接进「我管理的人物」(managed=true,不用再去池子勾第二遍) - VM: importPhoneContacts(全量) 拆成 loadPhoneContacts(读列表)+importSelectedPhoneContacts(导选中) - 保留按手机号/姓名跨源自动合并去重 - 「通讯录」池页的手机入口改为跳转多选页(onOpenPhonePicker) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
- Person 加 realName/resume,档案页可编辑(PersonProfileScreen);DB 迁移 12→13 - 通讯录池新增手机系统通讯录来源(READ_CONTACTS / ContactsContract,contactRef phone:<id>), 按手机号/姓名跨源自动合并去重(PersonDao getByPhone/getByExactName/setPhone/updatePhoneInfo/updateProfile) - PhotologScreen/PhotologViewModel 支持两源池子 - AndroidManifest 加 READ_CONTACTS 权限;同步 CLAUDE.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
录音 tab: - 录音中显示「⏱ 已录制 mm:ss」秒表(RecorderState 记 startedAt/accum,暂停冻结、继续累加) - 「最近转写」换成「录音记录」列表:按 audioPath 聚合录音块,每行显示起止/时长/转写状态/字数/人数/段数,点击跳时间轴定位该块 声纹: - 短段不认声纹:assign/reclusterAll 加最短识别时长闸门(默认 3s,设置页 1–8s 可调),止住短段量产伪说话人 - 新增 clearAllAndRecluster + 「声纹」tab「清空重识别」按钮:删全部声纹+清归属后从零重聚类 录音分段: - BlockBuffer 静音切段间隔由编译期常量改为构造参数,设置页「分段间隔」1–15min 可调(下次服务启动生效) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
- UI 从 5-tab 改为底部 4-tab(人物/录音/对话/设置)+录音内 4 子 tab - 新增 photolog 人脉 CRM 层说明(两人物池/vision 人脸/微信头像锚点) - 设置页从 TODO 移到已完成;保留未建的存储 TTL/用量/暂停时段 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
删掉底部「纪要」菜单,把它的声纹/纪要两个子页并入「录音」。 录音菜单顶部子 tab 从 2 个(录音/时间轴)扩到 4 个(录音/时间轴/声纹/纪要)。 底部导航 6→5:人物/录音/对话/相册/设置。 跨子页联动保留:声纹点人→时间轴按 TA 筛选;纪要点时间戳→跳时间轴对应段。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
某些块的音频会让 sherpa-onnx 在 native 层 SIGSEGV(实测重处理历史时一个 12.8s 正常音频块触发,原因在 native 不可控),把整个进程(含 UI)带崩——表现为「一打开 录音页就闪退」,且重启后 listPending 又排到同一毒块,死循环崩。 native SIGSEGV 无法 try/catch,改用文件标记防御:处理前写 .attempt;若启动时标记 仍在(= 上次崩在这块、没走到删目录),把该块移到 blocks_quarantine/ 跳过。一块最多 崩一次即被隔离,其余正常处理,app 不再死循环。隔离块留存可后续排查。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
档案页声纹行加「🎤 录一段」:对手机说 ~6s → CAMPPlus 192d 声纹 → 新建 SpeakerProfile 绑到 TA(原「绑定」改名「选已有」走旧的选择已有声纹)。 关键:24/7 RecorderService 占麦,录前 pause 释放 AudioRecord、录后 resume, 避免双 AudioRecord 抢麦。VoiceEnroll 状态机驱动对话框(录音倒计时/提取中/完成/错误)。 绑定后自动 setManaged + touch。真机验证:pause→capture→embed(dim=192)→resume 全链路通。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
reprocess-history.sh:把所有旧逐句 .opus 按静音5min/10min封顶合并成「块」 (ffmpeg 解码→blocks/<ts>/{audio.pcm,manifest}),清空旧 segments/transcripts/ speaker_profiles,推回设备。重启 RecorderService 后由端上本地模型(pyannote+ paraformer+campplus)经现有 block 崩溃恢复管线整体重跑声纹+文字——识别全程不碰云/Claude, 脚本只做机械重打包+清库。自动备份原 DB。 实测:1078 段 → 89 块。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> -
人物 tab 只显示 managed=true 的人;4357 个微信联系人退为「池子」。 新增「微信通讯录」选人页:全部联系人 A–Z + 搜索 + 勾选 → setManaged。 标人脸 / 绑声纹 / 手动建人自动置 managed=true。observeManaged 驱动列表。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
录音文件改造:BlockProcessor 不再把块切成逐句小 .opus,整块 speech 存成「一个 完整录音」.opus;每个 turn 只记在块内的起始偏移(audioStartMs)+时长,共享块文件。 省空间(不重复存),且真正有「完整录音文件」可回放。 时间轴 UI: - 时间段块头加 ▶:playSequence 把该段涉及的各块完整录音依次播。 - 每条 turn 加 ▶:seek 到块内偏移、只播该 turn 时长(SegmentPlayer 支持 startMs/durMs)。 - 旧逐句段 audioStartMs=0、audioPath=自身文件,照常整段播放,兼容。 Schema:Segment +audioStartMs(MIGRATION_10_11)。注:本提交的 Entities/AppDatabase 同时含并行开发中的 persons.managed(MIGRATION_11_12,version 12)——两者在同一 schema 文件内经 version 号耦合,无法拆分提交,故一并带上。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
档案页从一长条下拉改成:固定资料头 + 搜索框 + 顶部 TabRow 切换。 - 固定头部:头像 + 名字 + 微信号/备注/手机号 + 声纹绑定(绑定/改绑)+ AI 印象 - 搜索框:在当前 tab 的记录里实时过滤(caption/转写/聊天/朋友圈文本子串) - TabRow:照片 / 语音 / 聊天 / 朋友圈(各带数量),切到哪个就显示哪个列表 - 照片:3 列网格,点开看大图 - 语音:TA 说过的录音段,点击播放 - 聊天:微信气泡;朋友圈:动态列表 实测:火星人档案页头部固定,「照片1/语音54/聊天1/朋友圈10」tab 切换正常。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
- 「人物」从相册子页提升为**独立一级 tab,放第一位**(默认打开);PhotologTab 拆成 ContactsTab(通讯录+档案) 与 AlbumTab(照片),共用同一 PhotologViewModel(Activity 域) - 通讯录排序:点过/绑过声纹的联系人 lastAccess 置顶到「★ 最近联系人」区, 其余仍按拼音 A–Z(Person.lastAccess 字段 + 迁移 v9→v10;点开档案/绑声纹时 touch) 注:本提交还顺带含时间轴「按 5min 静音切时间段分组」的并行改动(同文件耦合,已编译通过)。 实测:默认进人物页(共 4357),点联系人后回列表,顶部出现「最近联系人」;迁移 v10 零数据损失。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
更新录音管线描述(07f3d22 的架构改动): - §一/概述:一句话管线改为「攒块→整块 pyannote 分轮→逐 turn 转写+认人」,标注无实时字幕 - §四 架构链路:RecorderService 攒块 + BlockBuffer/BlockProcessor + Channel 解耦 + 崩溃 listPending; 补块边界参数(SILENCE_MS=5min / MAX_MS=10min);标注旧 DiarizationWorker 已删 + WM 10min 上限 - 文件地图:加 BlockBuffer/BlockProcessor/DiarizationEngine;SpeakerClusterer 标 L1/L2 - 接口表:VAD→BlockBuffer→BlockProcessor→DiarizationEngine→逐 turn 入库 - 声纹纠偏 L1 冻结 anchor + L2 多 anchor 重建;阈值默认 0.45 - changelog 加 v0.7;多 anchor 待办标完成;logcat tag 加 BlockBuffer/BlockProcessor - 版本号 v0.6 → v0.7 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
档案页扩成多源聚合(里程碑 2/4/5;3 短信因本机无数据暂缓)。 - data:WechatMessage / Moment 两表 + persons.impression 字段 + 迁移 v6→v7→v9; 对应 DAO observeByTalker / observeByUser / updateImpression - WeChatEnrollWorker 增 Pass1.5/1.6:导入 messages.json(聊天) + moments.json(朋友圈) (clear+重灌幂等,按 wxid 关联 Person.contactRef) - PersonProfileScreen:加「微信聊天」气泡 + 「朋友圈」列表两段 - M5 AI 印象:PhotologViewModel.enrichImpression 汇总 TA 朋友圈 → 本地 4B(8080) 生成一句话画像 + 标签(爱好/性格),存 persons.impression,档案页展示 + 按钮触发 - server/services/wechat/extract-wechat-chat-moments.py:Mac 侧提取脚本 (EnMicroMsg message 表解密 + SnsMicroMsg 明文 SnsInfo protobuf 文本近似提取) 实测:真机导入聊天 40 条 + 朋友圈 78 条,按 wxid 关联到联系人(火星人 10 条朋友圈)。 M3 短信:研究清楚(mmssms.db 明文 + contacts2 手机号源),但本机 SMS=0、通讯录=0 (纯微信用户),无数据暂缓,机制在有数据设备上可直接复用。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
弃掉「录一句、转一句、实时聚类」——短句(0.3-30s) embedding 噪声大,实时滑动 聚类把噪声平均进 anchor,是声纹串人的根因。改为: 录音端(RecorderService):VAD 只负责存语音 + 检测块边界,删掉实时 transcribe/ embed/clusterer.assign。块关闭条件:静音≥5min 或 块时长≥10min(长会议保活)。 - BlockBuffer:speech-only PCM 追加 + manifest(run 偏移→真实时间);崩溃重启 listPending 续处理。 处理端(BlockProcessor):整块 pyannote 分轮 → 每 turn 切片 paraformer 转写 + CAMPPlus 嵌入 + 全局 anchor 匹配 → 写 turn 行(turn = 时间轴一行)。pyannote 缺失则降级整块单 turn。块处理与采集解耦(Channel 顺序消费),不阻塞录音。 embedding 从源头是单说话人干净切片,不再被实时逐句污染。SpeakerClusterer.assign 也加 L1 冻结(命名 anchor 不漂移)。废弃旧的周期 DiarizationWorker(块创建即 diarize),删 DiarizationScheduler/Worker;纪要按钮不再触发它。 实测(95s 真实会议块):13 段语音 → 24 个干净不重叠 turn,逐 turn 带说话人+文字。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> -
把「人物」做成微信通讯录样式 + 联系人档案页(以人为中心聚合的地基)。 - data:Person 加微信资料字段(nickname/remark/alias/pinyin/pyInitial/phone) + 迁移 v5→v6; PersonDao observe(id) / updateContactInfo;SegmentDao observeBySpeaker(声纹→录音) - WeChatEnrollWorker 重写两段:Pass1 给**全部真人联系人**upsert 资料(通讯录全员可见), Pass2 仅对有脸头像建 faceEmbedding 锚点。幂等 - ContactList:搜索 + 拼音 A–Z 粘性分组 + 头像 + 人脸/声纹标,点进档案页 - PersonProfileScreen:头像 + 微信名/备注/微信号 + 声纹绑定 + 关联照片列表 + 录音列表 (录音 = segments.speakerId=person.voiceSpeakerId,绑声纹后出现) - 标注对话框改可滚动/搜索/固定底部按钮(联系人多时不再卡死) - extract 脚本补全字段(nickname/remark/alias/pinyin) 实测:4357 真人联系人入通讯录(1339 人脸锚点),A–Z 分组 + 头像 + 档案页正常。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
单段注册的 anchor 太弱:实测「宜博」「秦子琛」两个单段 anchor 余弦只隔 0.53, 高度混淆,导致段被错派。L2 把 anchor 从「单段」升级为「该人名下全部段的均值」: - SpeakerClusterer.rebuildAnchor(repo, speakerId):取该说话人所有带 embedding 段 的均值、L2 归一化写回质心(sampleCount 同步)。 - SegmentDao.embeddingsForSpeaker(speakerId):拉该人名下段 embedding。 - 触发点:重命名(命名即确认身份)、合并(成员集变大)后各 rebuild 一次。 配合 L1 冻结不漂移:anchor 干净(多段均值)且稳定(不被相邻说话人污染), 用户「听一段→命名/合并→重新聚类」即可把历史漂移搞坏的 anchor 一次性纠正。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
reclusterAll 原本对命中的簇一律滑动均值精修质心。问题:人工命名的 anchor 吸入相邻说话人的段后,质心会朝其漂移、再吸走更多 → 雪球式吞并(实测「秦子琛」 把「宜博」从 ~125 段啃到只剩 56)。 改为:命中人工命名 anchor 时只归属、**冻结质心不漂移**(autoIndexOf==null 判定); 自动簇仍滑动均值。anchor 始终保持用户标定方向,不被相邻说话人污染。 实测确认生效:冻结后「秦子琛」anchor 不再吞并,各簇内聚 0.76-0.82 干净分离。 注:此改动也暴露出历史漂移已把存量 anchor 的 embedding 平均坏了(连本该代表的 主说话人都匹配不上),需 L2 多 anchor 重注册给出干净 anchor。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
底部导航 7 → 5:录音 / 对话 / 纪要 / 相册 / 设置。 - 「录音」页顶部 TabRow 切 录音 / 时间轴 - 「纪要」页顶部 TabRow 切 声纹 / 纪要 - 跨页跳转保留:声纹点某人 / 纪要点要点 → 自动切到「录音」页的时间轴子 tab Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
整场会议不再截断在前 ~140 段:按 3min speech 分块独立 diarize(块内说话人 index 不跨块,身份交全局 reclusterAll),断点 cursor 持久化到 prefs,下轮从 上次中断的段续跑而非重头。 修卡死真 bug:WorkManager 对每个 worker 有 10min 硬上限,原来 8min 大 chunk 一次 diarize 逼近上限被系统 kill、cursor 写不回去 → 永远卡在同一段。改成 3min chunk + 5min 预算,一轮跑完多块、多次写 cursor,绝不撞红线。实测 473 段 真实会议 4 轮自动跑完(142→232→336→424→done)。 SettingsScreen 阈值下拉补 0.35/0.40(0.4 过合并、0.55 过分裂,甜点 ~0.45)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
把微信联系人头像建成人脸锚点,照片处理时自动认出"这是谁",标注时直接带名字。 - data:Person 加 faceEmbedding 锚点字段 + 迁移 v4→v5(ALTER 加 BLOB 列, DDL 对过 Room;真机迁移零数据损失);PersonDao anchors/getByContactRef - FaceMatcher:gallery 统一 = 人物锚点(头像) + 已标注脸样本;match() 共享余弦匹配; PhotoProcessor 改用统一 gallery - WeChatEnrollWorker:读 filesDir/wx_enroll/(enroll.json + 头像) → 对每张头像跑 **端上 FaceEngine**(必须同引擎才能跟照片人脸比),恰 1 脸就建 Person(name, contactRef=wechat:wxid, avatarPath, faceEmbedding)。幂等、单轮预算 8min、retry 续 - UI:人物页「从微信导入联系人」按钮;人物卡片标注「微信/声纹」来源 - server/services/wechat/extract-wechat-avatars.py:Mac 侧提取脚本 (sqlcipher 解密 EnMicroMsg.db → rcontact⨝img_flag 筛真人 → 下载头像) 实测:4338 真人联系人,头像有脸覆盖率 41%(1796/4337),端上 90s 建 284 锚点(继续后台跑)。 头像/姓名属敏感 PII,存 _wx_enroll/ 已 gitignore。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
- 新增「相册」tab(照片 / 人物 两子页) - 照片:Coil 网格 + 人脸数徽标;「+导入照片」走系统相册选择器,导入瞬时完成、 后台识别(显示「待处理 N」) - 照片详情:大图 + 时间/GPS + Qwen-VL 场景描述 + 人脸列表,点脸 → TagFaceDialog 标注(选已有人物或新建),自动回填同一人到其它照片 - 人物:人 → TA 的照片,可重命名;预留「已绑声纹」展示(融合声纹下一步) - PhotologViewModel:导入登记 + 标注/回填,随 Activity 存活 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
以「人」为中心融合身份的相册基础(声纹那套的人脸版): - data:persons/photos/faces 三表 + 增量迁移 v3→v4(DDL 对过 Room,真机零数据损失) - vision/FaceEngine:ML Kit 检测+5点 → 相似变换对齐 → TFLite MobileFaceNet 512维 embedding(同人 0.976/异人 0.127,embed 44ms)。**不用 onnxruntime-android**—— 它和 sherpa 自带 libonnxruntime.so(自定义符号版本)无法共存,会弄坏音频 - vision/VisionClient:经 llama-swap 8080 的 vl 端点(Qwen2.5-VL-3B)出场景描述,只描场景不报人名 - vision/PhotoProcessor:人脸+embedding+VL+EXIF → 更新 photos 行 + 写 faces + 匹配 Person - vision/PhotoWorker + PhotoScheduler:导入只瞬时登记,重活交后台 worker 定时/闲时处理 (15min periodic + 导入触发 runNow,单轮 7min 预算,防并发),不阻塞导入 - vision/FaceMatcher:标注一张脸 → 回填其它照片同一人(同声纹回填) - ImageUtil:EXIF 时间/GPS、按方向解码、降 512px 喂 VL、embedding 打包 - 模型 w600k_mbf.tflite(6.5MB) 进 assets(实测还原 onnx cos=1.0) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
解决 v1 的说话人爆炸(98)+ 内存崩溃: - 不再每会话贪心建全局说话人。改为:diarization 给每段存"干净"的 dominant-turn embedding(覆盖实时的混合 embedding),run 末尾跑一次 SpeakerClusterer.reclusterAll 做全局聚类(本就全局 + 保留人工命名锚点)。 - 修 OutOfMemoryError:拼接会话音频原用 ArrayList<Float>(每 float 装箱 16B, 6min 会话 ~92MB 垃圾)→ 改 List<FloatArray> 一次性 copy;AudioDecoder 的 ArrayList<Short> → ByteArrayOutputStream。MAX_CONCAT 18→10min 留余量。 注:Daos.SegmentDao.updateEmbedding 这一行(diarization 需要)未在此 commit, 随 photolog WIP 一起;提交 photolog 时带上即可。 实测:无 OOM;但说话人仍 89——因单轮 7min 预算只 diarize 了 3/14 会话, 其余仍是脏 embedding。periodic 多跑几轮全部 diarize 后才会真正收缩。 有声书测试数据本就对声纹是地狱级,真实多人数据 + 阈值微调是正路。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
逐 VAD 段单 embedding 的实时方案无法处理「一段几秒内多人说话」(混合 embedding=噪声) + 在线贪心聚类过度合并。改为会话级批处理重跑(与纪要 worker 同构): - DiarizationEngine:sherpa-onnx OfflineSpeakerDiarization(pyannote 分割检测 说话人变更点+重叠 → CAMPPlus embedding → FastClustering 自动聚类)。 - AudioDecoder:.opus(Opus-in-Ogg) → 16k mono PCM(MediaCodec,含 48k→16k 重采样 + 下混)。 - DiarizationWorker:每个已结束/未 diarize 会话 → 解码拼接 → diarize → 局部说话人 桥接全局 SpeakerProfile(复用 SpeakerClusterer)→ 段按重叠最大 turn 重标。 AtomicBoolean 互斥 + 7min 预算 + 进程内 idempotency(prefs)。 - DiarizationScheduler:periodic 15min(UPDATE) + 「现在跑一次」一并触发。 - ModelDownloader.ensurePyannoteModel:首启下 pyannote-seg(~6MB)。 实测端到端通:56 段会话 → 44 turns、检测出多说话人(核心能力达成)。 已知待调(非 bug): - 测试数据是有声书(一人配几十角色),对声纹是地狱级输入 → 全局说话人偏多(98)。 需在真实多人录音上验证;threshold 走设置页可调。 - 全局身份桥接现为贪心 match-or-create,易累积;v1.1 应改全局重聚类。 - 段内多说话人 v1 先归主导 turn,不拆分+重 ASR(v2)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
- HermesAccessibilityService: 折叠态封面屏(1080×2364)下 A11y 节点 bounds 全 0、 ACTION_CLICK/SET_TEXT 失效,改用固定坐标 dispatchGesture 全流程(搜索图标 → 设剪贴板长按粘贴 → 首条结果 → IME 剪贴板候选插入 → 绿色发送按钮), 删 ~750 行旧反射/节点路径(-749/+74)。 - setup_termux_boot.sh: frida-server 路径挪到 Termux 家目录(~/hermes-wechat/bin), 弃用 /data/local/tmp(Termux 路径约定)。 - server/services/wechat/wechat-contacts.{json,md}: 从 EnMicroMsg.db rcontact 导出 5423 联系人(发送时确定真实 wxid 的权威来源)。 - wechat_plain/scripts/frida-daemon/: Frida 调试脚本(已废弃发送路径,留作参考)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> -
- LifelogRetriever:「lifelog 模式」开关下,发送前本地检索纪要 + 转写拼成 system context 注入(本地 2B/4B 和 Hermes 通用,不落库)。中文用 bigram + 重叠打分召回(FTS4 unicode61 对中文短语不命中),命中多者优先、抑制单 bigram 噪声 - Daos:加 timelineList / searchTextLike / summaries.recentList 三个 suspend 检索方法 - ChatViewModel:会话状态搬进 VM(随 Activity 存活)。修复切 tab 回来: 流式请求被取消(viewModelScope 不取消) / 变成新会话 / lifelog 开关重置—— currentConvId / selectedTargetId / lifelogMode 均持久化到 prefs,重启也记住 - 键盘 inset:Activity setDecorFitsSystemWindows(false) + manifest adjustResize + Scaffold imePadding,键盘弹出时整个界面(含底部 tab)抬到键盘上方、可见 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
只有结果 list 看不出 worker 有没有正常跑(periodic 经常 0 篇——会话没结束)。 加一条持久化执行记录,确认「几点几分跑了、几个会话→几篇」。 - SummaryRunLog:singleton + lifelog_prefs(JSON) 存最近 50 次执行; StateFlow 供 UI 实时刷新,prefs 保证重启不丢。 - SummaryWorker:每次 doWork 跑完(含 0 段早退)记一行 Run(ts/made/skipped/ total/secs/backend/trigger);trigger 区分 auto(periodic)/manual。 - SummaryScheduler.runNow:标 trigger=manual。 - SummariesTab:折叠「执行记录(N)· 上次 HH
ss → M篇」,展开看历次。
验证:手动触发 → prefs 记 {made:0,skipped:9,total:9,trigger:manual} → UI 正常显示。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> -
新增第 6 个 tab「设置」(SettingsScreen.kt),把散落各处的配置收进一页: - 纪要:会话间隔 / 自动跑周期(改完即重排 WorkManager) / 会话最短语音 / 后端模型(2B/4B/Hermes) - 声纹:聚类阈值(0.45–0.65) - 本地模型端点:baseUrl / 2B / 4B model 名(ChatConfig) - 录音/电池:白名单状态 + 申请 配套把写死的参数改成可配: - SummaryWorker:KEY_PERIOD_MIN / KEY_MIN_SPEECH_SEC,min-speech 从 prefs 读 - SummaryScheduler:周期从 prefs 读(默认 15,硬下限 15) - SpeakerClusterer:thresholdFromPrefs(),RecorderService 的 live 聚类器 + SpeakersTab 的 reclusterAll 都用 prefs 阈值 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
- §六 加最易混淆点:纪要 WorkManager periodic 每 15min(Android 硬下限, 改间隔须 UPDATE 策略)自动扫已结束会话;UI「会话间隔 10min」是切分规则, 与跑纪要频率无关。附 502 并发坑的解法(AtomicBoolean + 7min 预算 + 不 retry)。 - 文件清单:SummaryScheduler/Worker 描述同步(互斥/预算/15min)+ 补 SummaryProgress。 - 纪要后端接线改为「UI 可选 2B/4B/Hermes,经 ChatConfig 解析」。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
之前 periodic 是 1 小时一次,体感像「不自动跑」。注意 UI 里的「10 分钟」 是会话切分间隔(多长静音算另一场),不是跑纪要频率,两者无关。 - 周期 1h → 15min(WorkManager periodic 硬下限,做不到正好 10min)。 - ExistingPeriodicWorkPolicy KEEP → UPDATE:否则旧 1h 排程不被新间隔覆盖。 - worker 已自带并发互斥 + 7min 时间预算,15min 频繁触发不会堆积。 验证:jobscheduler 显示 SummaryWorker 下次 ~14min 后触发。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
实测:periodic + one-shot + RETRY 死循环并发打同一个 2b 上游 → llama-swap proxy context-canceled → 502,纪要永远生成不出来(console 卡「运行中」)。 - 进程内 AtomicBoolean 互斥:同时只跑一个 worker body(enqueueUniqueWork 只能去重同名 one-shot,挡不住 periodic)。 - 单轮时间预算 7min:超了停、剩余会话下轮继续,避开 WorkManager ~10min 执行上限被中途 kill(→ context canceled → 502)。 - 失败不再 Result.retry():未处理会话靠下一轮 periodic / 手动重跑捡, 不再 hammering。 验证:单 worker 跑 56 段会话,llama-swap 返 200,本地 2B 纪要落库。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
- SummaryWorker: 后端从 SharedPreferences 读 ChatTarget.id(local-2b/4b/hermes), 经 ChatConfig 解析端点/model(与对话共用配置),不再写死 2B。 - SummaryProgress:新增 worker→UI 进度 singleton(同进程 StateFlow), worker 每步发 line(开始/会话生成中/✅完成 Ns/❌失败/跳过统计)。 - SummariesTab:加「纪要模型」下拉(2B/4B/Hermes,存 prefs)+ 进度 console (滚动框,最新在上)+「现在跑一次」运行中转圈禁用。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
- HermesClient: 加 chat_template_kwargs.enable_thinking=false —— 纪要走本地 Qwen3.5,不需要 <think>,关掉省 token/发热(Kimi 等不认会忽略)。 - SpeakerClusterer: MATCH_THRESHOLD 0.5 → 0.55 —— 0.5 偏松过度合并 (4B 测试「说话人4」吃了 23 段),收紧分得更细。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
- 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>
-
- ChatClient: 本地 Qwen 目标加 chat_template_kwargs.enable_thinking=false (Hermes/Kimi 不认会忽略,故只对 LOCAL 加);修 parseDelta —— delta.content 为 JSON null(思考阶段/role-only 首块)时跳过,不再因 org.json.optString 返回字面 "null" 污染回复开头。 - SummaryWorker: 纪要后端解耦到本地 llama-swap(LLM_BASE_URL=8080 / LLM_MODEL="2b"), 独立于 keys.properties——那个留给聊天的 Hermes 选项(8642 网关 → Kimi)。 - AppDatabase: 删掉 fallbackToDestructiveMigration 改 fail-loud(已有 MIGRATION_2_3; 以后改 schema 不写迁移直接抛异常,绝不再静默清掉真实录音)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-
- SummaryScheduler.runNow 改 enqueueUniqueWork(KEEP):在跑/排队时重复触发直接忽略, 避免并发跑出重复纪要 - SummaryWorker:单篇喂 LLM 的转写条数上限 150,超长会话均匀抽样(覆盖整段、时间戳 保真),防止 prompt 撑爆上下文 / 端侧 prefill 过慢 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>