From a7af231852e2719142eabee6289e5c9b1e2f9ddf Mon Sep 17 00:00:00 2001 From: yibo Date: Thu, 4 Jun 2026 16:51:44 +0800 Subject: [PATCH] feat(lifelog/summary): 纪要去重 + 超长会话抽样 --- lifelog-recorder/app/src/main/kotlin/com/lemon/lifelog/summary/SummaryScheduler.kt | 9 ++++++++- lifelog-recorder/app/src/main/kotlin/com/lemon/lifelog/summary/SummaryWorker.kt | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lifelog-recorder/app/src/main/kotlin/com/lemon/lifelog/summary/SummaryScheduler.kt b/lifelog-recorder/app/src/main/kotlin/com/lemon/lifelog/summary/SummaryScheduler.kt index e163a17..0712cd2 100644 --- a/lifelog-recorder/app/src/main/kotlin/com/lemon/lifelog/summary/SummaryScheduler.kt +++ b/lifelog-recorder/app/src/main/kotlin/com/lemon/lifelog/summary/SummaryScheduler.kt @@ -4,6 +4,7 @@ import android.content.Context import androidx.work.Constraints import androidx.work.Data import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder @@ -40,6 +41,12 @@ object SummaryScheduler { .setInputData(data) .addTag(ONE_SHOT_TAG) .build() - WorkManager.getInstance(ctx).enqueue(request) + // 唯一作业 + KEEP:若已有一次在跑/排队(含 RETRY),重复触发直接忽略, + // 避免并发跑出重复纪要。 + WorkManager.getInstance(ctx).enqueueUniqueWork( + ONE_SHOT_TAG, + ExistingWorkPolicy.KEEP, + request, + ) } } diff --git a/lifelog-recorder/app/src/main/kotlin/com/lemon/lifelog/summary/SummaryWorker.kt b/lifelog-recorder/app/src/main/kotlin/com/lemon/lifelog/summary/SummaryWorker.kt index b8ed3b9..c632c49 100644 --- a/lifelog-recorder/app/src/main/kotlin/com/lemon/lifelog/summary/SummaryWorker.kt +++ b/lifelog-recorder/app/src/main/kotlin/com/lemon/lifelog/summary/SummaryWorker.kt @@ -37,6 +37,10 @@ class SummaryWorker( const val DEFAULT_GAP_MIN = 10 const val MIN_SESSION_SPEECH_MS = 30_000L // 会话至少 30s 语音 + /** 单篇纪要喂给 LLM 的最大转写条数;超长会话均匀抽样到这个数, + * 避免 prompt 撑爆上下文 / prefill 过慢(端侧热降频下尤其重要)。 */ + const val MAX_SEGMENTS_PER_SUMMARY = 150 + const val INPUT_NOW_TS = "now_ts" // 覆盖 now(测试 / 手动) const val INPUT_GAP_MS = "gap_ms" // 覆盖会话间隔(手动跑用当前设置) @@ -132,11 +136,22 @@ class SummaryWorker( start: Long, end: Long, ): String? { + // 超长会话均匀抽样,保证 prompt 有界(覆盖整段时间而非只取头部)。 + val segs = if (session.size > MAX_SEGMENTS_PER_SUMMARY) { + val step = session.size.toDouble() / MAX_SEGMENTS_PER_SUMMARY + (0 until MAX_SEGMENTS_PER_SUMMARY).map { session[(it * step).toInt()] } + } else { + session + } + val sb = StringBuilder() sb.appendLine("以下是 ${rangeFmt.format(Date(start))} – ${rangeFmt.format(Date(end))} 一次连续录音的语音转写片段(按时间顺序)。") + if (segs.size < session.size) { + sb.appendLine("(会话较长,共 ${session.size} 段,已均匀抽样 ${segs.size} 段,时间戳仍为真实值。)") + } sb.appendLine() var hasText = false - for (seg in session) { + for (seg in segs) { val transcript = repo.transcripts.get(seg.id)?.text ?: continue if (transcript.isBlank()) continue hasText = true -- libgit2 0.21.4