diff --git a/app/src/main/assets/summarize/prompts.md b/app/src/main/assets/summarize/prompts.md
index 2dcb6a0..c391267 100644
--- a/app/src/main/assets/summarize/prompts.md
+++ b/app/src/main/assets/summarize/prompts.md
@@ -10,8 +10,8 @@
| 步骤 | temperature | maxNewTokens | 备注 |
|---|---|---|---|
-| P1 滚动笔记更新 | 0(Greedy) | 900 | 可复现;笔记要求确定性 |
-| P2 成稿 | 0.5 / topK 64 / topP 0.95 | 2500 | 低温采样,兼顾成稿自然度与可复现 |
+| P1 内容概括更新 | 0(Greedy) | 900 | 可复现;概括要求确定性 |
+| P2 最终输出 | 0.5 / topK 64 / topP 0.95 | 1600 | 低温采样,兼顾自然度与可复现 |
思考(thinking)关闭:App 渲染模板时不注入 `<|think|>` 标记;模型若仍输出 `<|channel>thought…` 块,App 侧正则剔除。
@@ -21,61 +21,56 @@
---
-## P1 — 滚动笔记更新
+## P1 — 内容概括更新
### SYSTEM(固定,勿动态改写)
```
-你是处理会议转写记录的笔记员。请把【新的断片】的内容合并进给定的【当前笔记】,只以JSON输出更新后的笔记。
+你是会议内容概括员。请把【新的断片】合并进【当前概括】,只以JSON输出更新后的概括。
严格要求:
-- 只输出符合指定schema的JSON对象;禁止说明文字、代码围栏、开场语。
-- schema的键名必须原样输出,不得翻译、转写或改写(程序按这些键校验)。
-- 笔记内容用中文书写;人名、组织名、产品名等专有名词保留原文写法。
-- 不得把输入笔记原样照抄:必须从【新的断片】中提取至少一条笔记尚未覆盖的信息并合并;断片确实没有新信息时,保留原笔记并只更新「前回末尾」。
+- 只输出JSON对象:{"概括":"..."};禁止说明文字、代码围栏、开场语。
+- 键名必须原样输出(只有这一个键),不得翻译或改写(程序按此键校验)。
+- 「概括」是一段连贯流畅的叙述性概括,覆盖会议至今的全部重要内容;是滚动重写,不是断片列表,不得照抄断片原文。
+- 用与会议相同的语言书写;人名、组织名、产品名等专有名词保留原文写法。
- 数字、金额、日期时间、期限、次数、单位按原文准确转记,不许模糊概括。
-- 不得删除否定、保留、反对的意图;「暂缓」「再议」「反对意见」本身就是重要信息。
-- 每个条目在「出典」字段给出依据发言的时间段(格式:"mm:ss-mm:ss",多个用";"分隔)。
-- 总计超过500字时,合并删除低价值的旧条目压缩;「行動項目」和「已定事項」最后才允许删。
-- 「前回末尾」用一句话写最近的议题,供下一个断片衔接上下文。
+- 不得遗漏否定、保留、反对的意图;决定事项与未决事项均要体现。
+- 概括控制在400字以内;超出时压缩合并旧内容,保留主干与最新进展。
- JSON字符串值内需要双引号时转义为\";换行用\n。
schema(键名禁止改动):
-{"已定事項":[{"内容":"","出典":""}],"討論中":[{"内容":"","出典":""}],"行動項目":[{"内容":"","担当者":"","期限":"","出典":""}],"人物":[{"名前":"","立場":""}],"前回末尾":""}
+{"概括":""}
```
### USER(每块渲染;`{}` 为占位符)
```
-【当前笔记】
+【当前概括】
{notes_json}
【新的断片】(时间戳为会议开始的 分:秒,[]内是说话人标签)
{chunk_text}
-请输出更新后的笔记JSON。
+请输出更新后的概括JSON。
```
---
-## P2 — 最终成稿
+## P2 — 最终输出
### SYSTEM(固定)
```
-你是会议的官方纪要撰写者。收到会议笔记(JSON)与术语表后,用指定成稿语言输出完整的Markdown会议纪要。
+你是会议概括撰写者。收到会议内容概括(JSON)与术语表后,用指定成稿语言输出内容概括。
严格要求:
-- 输出语言严格遵守指定的「成稿语言」,用该语言的书面体撰写。
-- 正文只用成稿语言的文字书写:中文稿不得出现日文假名;英文稿不得出现汉字与假名;日文稿除产品名外不得残留英文句子。
-- 人物、公司、品牌等专有名词可保留笔记/术语表中的原文写法(上述文字限制的例外)。
-- 章节标题必须依序为「议题与经过」「决定事项」「行动项」「未决与待议」;成稿语言非中文时翻译成该语言的等价标题;没有条目的章节明确写"(无)"。
-- 行动项用表格输出,列为 负责人 / 内容 / 期限 / 出处;负责人不明时写"待定"。
-- 每个条目末尾给出处,格式 `出处: mm:ss-mm:ss`;出处只允许使用笔记JSON中「出典」字段的值,不得编造。
-- 不得添加笔记中没有的事实、推测或补充说明。
-- 人名、产品名以术语表的正式写法统一(术语表没有的沿用笔记写法)。
-- 开头放置元信息:会议时间(如有)、发言人数、使用语言、转写总时长。
-- 只输出Markdown正文,不要用代码围栏包裹全文。
+- 输出两部分:先一段连贯的概括正文,空一行后给出要点。
+- 要点3~6条,每条单独一行、以「・」开头;不加小标题、不分类、不标时间、不写出处。
+- 概括正文目标200~400字,覆盖会议主干与结论;要点摘出最值得记住的3~6条。
+- 输出语言严格遵守「成稿语言」,用书面体:中文稿不得出现日文假名;英文稿不得出现汉字与假名;日文稿除专名外不得残留英文句子。
+- 人物、公司、品牌等专有名词可保留原文写法(上述限制的例外)。
+- 不得添加概括中没有的事实、推测或补充说明;人名、产品名以术语表的正式写法统一。
+- 只输出正文,不要Markdown标题、不要代码围栏。
```
### USER
@@ -83,15 +78,13 @@ schema(键名禁止改动):
```
【成稿语言】{language_name}
-【会议信息】开始: {meeting_start} / 发言人数: {n_speakers} / 总时长: {total_duration}
-
【术语表(正式写法列表;人名、产品名的错误写法以此统一)】
{glossary_lines}
-【会议笔记JSON】
+【会议内容概括JSON】
{notes_json}
-请根据以上内容输出会议纪要Markdown。
+请输出内容概括与要点。
```
---
@@ -108,7 +101,7 @@ schema(键名禁止改动):
- 目标 2500±500 字/块,按句子边界聚合,块间重叠 1 句;同一说话人轮次不拆开。
- 行格式:`(mm:ss-mm:ss)[话者名] 文本`,时间戳来自 SessionTranscript。
-- 首轮 `{notes_json}` = 空 schema(`{"已定事項":[],"討論中":[],"行動項目":[],"人物":[],"前回末尾":""}`)。
+- 首轮 `{notes_json}` = 空 schema(`{"概括":""}`)。
- 单块+笔记的总输入若超过 ~5000 字符,Kotlin 先把该块对半拆成两次 P1 调用(块长自适应,不截断内容)。
## 用 PC llama.cpp 复现(提示词调优用)
diff --git a/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt b/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt
index b39d6f5..1a0b143 100644
--- a/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt
+++ b/app/src/main/java/com/k2fsa/sherpa/onnx/MainActivity.kt
@@ -2832,7 +2832,12 @@ class MainActivity : AppCompatActivity() {
MeetingNotesIO.pathFor(SessionTranscript.dirOf(filesDir), baseName), baseName)
var resumeIndex = 0
var resumeNotes: String? = null
- if (saved != null && saved.entriesP1Done > 0 && saved.notes != SummarizePipeline.EMPTY_NOTES) {
+ if (saved != null && saved.entriesP1Done > 0 &&
+ saved.notes != SummarizePipeline.EMPTY_NOTES &&
+ saved.notes.contains("\"概括\"")
+ ) {
+ // schema 栅(2026-09-16):旧版五键笔记(已定事項…)视为不可续,
+ // 直接全量重跑,防旧 schema 喂进新提示词惹乱
resumeIndex = saved.entriesP1Done.coerceAtMost(entries.size)
resumeNotes = saved.notes
Log.i(TAG, "SUMMARIZE resume from notes at $resumeIndex/${entries.size}")
@@ -2874,7 +2879,7 @@ class MainActivity : AppCompatActivity() {
override fun onProgress(stage: String, done: Int, total: Int) {
runOnUiThread {
val sName = when (stage) {
- "滚动笔记" -> getString(R.string.sum_stage_notes)
+ "内容概括" -> getString(R.string.sum_stage_notes)
"成稿" -> getString(R.string.sum_stage_draft)
else -> stage
}
diff --git a/app/src/main/java/com/k2fsa/sherpa/onnx/SummarizePipeline.kt b/app/src/main/java/com/k2fsa/sherpa/onnx/SummarizePipeline.kt
index 8dbe06f..f954ad5 100644
--- a/app/src/main/java/com/k2fsa/sherpa/onnx/SummarizePipeline.kt
+++ b/app/src/main/java/com/k2fsa/sherpa/onnx/SummarizePipeline.kt
@@ -5,7 +5,7 @@ import android.util.Log
import org.json.JSONObject
// ============================================================================
-// SummarizePipeline — §5 摘要流水线(分块 → 滚动笔记 → 成稿)
+// SummarizePipeline — §5 摘要流水线(分块 → 内容概括滚动更新 → 概要输出;2026-09-16 简化)
//
// 提示词唯一事实源是 assets/summarize/prompts.md(用户可直接编辑/复制到 PC
// llama.cpp 调试)。解析失败时退回本文件内置的同源副本 —— 两份不一致以 md
@@ -87,42 +87,37 @@ object PromptsLoader {
return Prompts(p1s, p1u, p2s, p2u)
}
- // 与 prompts.md 同源的内置副本(md 丢失/被改坏时的兜底;2026-09-14 起中文)。
+ // 与 prompts.md 同源的内置副本(md 丢失/被改坏时的兜底;2026-09-16 起
+ // 简化为"内容概括"单项)。一致性单测逐字比对,改一处必须两处同改。
val BUILTIN = Prompts(
- p1System = "你是处理会议转写记录的笔记员。请把【新的断片】的内容合并进给定的【当前笔记】,只以JSON输出更新后的笔记。\n\n" +
+ p1System = "你是会议内容概括员。请把【新的断片】合并进【当前概括】,只以JSON输出更新后的概括。\n\n" +
"严格要求:\n" +
- "- 只输出符合指定schema的JSON对象;禁止说明文字、代码围栏、开场语。\n" +
- "- schema的键名必须原样输出,不得翻译、转写或改写(程序按这些键校验)。\n" +
- "- 笔记内容用中文书写;人名、组织名、产品名等专有名词保留原文写法。\n" +
- "- 不得把输入笔记原样照抄:必须从【新的断片】中提取至少一条笔记尚未覆盖的信息并合并;断片确实没有新信息时,保留原笔记并只更新「前回末尾」。\n" +
+ "- 只输出JSON对象:{\"概括\":\"...\"};禁止说明文字、代码围栏、开场语。\n" +
+ "- 键名必须原样输出(只有这一个键),不得翻译或改写(程序按此键校验)。\n" +
+ "- 「概括」是一段连贯流畅的叙述性概括,覆盖会议至今的全部重要内容;是滚动重写,不是断片列表,不得照抄断片原文。\n" +
+ "- 用与会议相同的语言书写;人名、组织名、产品名等专有名词保留原文写法。\n" +
"- 数字、金额、日期时间、期限、次数、单位按原文准确转记,不许模糊概括。\n" +
- "- 不得删除否定、保留、反对的意图;「暂缓」「再议」「反对意见」本身就是重要信息。\n" +
- "- 每个条目在「出典」字段给出依据发言的时间段(格式:\"mm:ss-mm:ss\",多个用\";\"分隔)。\n" +
- "- 总计超过500字时,合并删除低价值的旧条目压缩;「行動項目」和「已定事項」最后才允许删。\n" +
- "- 「前回末尾」用一句话写最近的议题,供下一个断片衔接上下文。\n" +
+ "- 不得遗漏否定、保留、反对的意图;决定事项与未决事项均要体现。\n" +
+ "- 概括控制在400字以内;超出时压缩合并旧内容,保留主干与最新进展。\n" +
"- JSON字符串值内需要双引号时转义为\\\";换行用\\n。\n\n" +
"schema(键名禁止改动):\n" +
- "{\"已定事項\":[{\"内容\":\"\",\"出典\":\"\"}],\"討論中\":[{\"内容\":\"\",\"出典\":\"\"}],\"行動項目\":[{\"内容\":\"\",\"担当者\":\"\",\"期限\":\"\",\"出典\":\"\"}],\"人物\":[{\"名前\":\"\",\"立場\":\"\"}],\"前回末尾\":\"\"}",
- p1UserTpl = "【当前笔记】\n{notes_json}\n\n" +
+ "{\"概括\":\"\"}",
+ p1UserTpl = "【当前概括】\n{notes_json}\n\n" +
"【新的断片】(时间戳为会议开始的 分:秒,[]内是说话人标签)\n{chunk_text}\n\n" +
- "请输出更新后的笔记JSON。",
- p2System = "你是会议的官方纪要撰写者。收到会议笔记(JSON)与术语表后,用指定成稿语言输出完整的Markdown会议纪要。\n\n" +
+ "请输出更新后的概括JSON。",
+ p2System = "你是会议概括撰写者。收到会议内容概括(JSON)与术语表后,用指定成稿语言输出内容概括。\n\n" +
"严格要求:\n" +
- "- 输出语言严格遵守指定的「成稿语言」,用该语言的书面体撰写。\n" +
- "- 正文只用成稿语言的文字书写:中文稿不得出现日文假名;英文稿不得出现汉字与假名;日文稿除产品名外不得残留英文句子。\n" +
- "- 人物、公司、品牌等专有名词可保留笔记/术语表中的原文写法(上述文字限制的例外)。\n" +
- "- 章节标题必须依序为「议题与经过」「决定事项」「行动项」「未决与待议」;成稿语言非中文时翻译成该语言的等价标题;没有条目的章节明确写\"(无)\"。\n" +
- "- 行动项用表格输出,列为 负责人 / 内容 / 期限 / 出处;负责人不明时写\"待定\"。\n" +
- "- 每个条目末尾给出处,格式 `出处: mm:ss-mm:ss`;出处只允许使用笔记JSON中「出典」字段的值,不得编造。\n" +
- "- 不得添加笔记中没有的事实、推测或补充说明。\n" +
- "- 人名、产品名以术语表的正式写法统一(术语表没有的沿用笔记写法)。\n" +
- "- 开头放置元信息:会议时间(如有)、发言人数、使用语言、转写总时长。\n" +
- "- 只输出Markdown正文,不要用代码围栏包裹全文。",
+ "- 输出两部分:先一段连贯的概括正文,空一行后给出要点。\n" +
+ "- 要点3~6条,每条单独一行、以「・」开头;不加小标题、不分类、不标时间、不写出处。\n" +
+ "- 概括正文目标200~400字,覆盖会议主干与结论;要点摘出最值得记住的3~6条。\n" +
+ "- 输出语言严格遵守「成稿语言」,用书面体:中文稿不得出现日文假名;英文稿不得出现汉字与假名;日文稿除专名外不得残留英文句子。\n" +
+ "- 人物、公司、品牌等专有名词可保留原文写法(上述限制的例外)。\n" +
+ "- 不得添加概括中没有的事实、推测或补充说明;人名、产品名以术语表的正式写法统一。\n" +
+ "- 只输出正文,不要Markdown标题、不要代码围栏。",
p2UserTpl = "【成稿语言】{language_name}\n\n" +
- "【会议信息】开始: {meeting_start} / 发言人数: {n_speakers} / 总时长: {total_duration}\n\n" +
"【术语表(正式写法列表;人名、产品名的错误写法以此统一)】\n{glossary_lines}\n\n" +
- "【会议笔记JSON】\n{notes_json}\n\n" +
- "请根据以上内容输出会议纪要Markdown。",
+ "【会议内容概括JSON】\n{notes_json}\n\n" +
+ "请输出内容概括与要点。",
)
}
@@ -141,8 +136,7 @@ class SummarizePipeline(
// 保证 ≤8192 ctx 永远够用(日语 1 字 ≈ 1 token 多一点)
private const val P1_MAX_PROMPT_CHARS = 6500
private const val NOTES_RESERVE = 1200 // 笔记+模板文案预留
- val EMPTY_NOTES =
- "{\"已定事項\":[],\"討論中\":[],\"行動項目\":[],\"人物\":[],\"前回末尾\":\"\"}"
+ val EMPTY_NOTES = "{\"概括\":\"\"}"
// 脚本占比判语种:假名出现即日语信号(日语里汉字占比也高,不能只看汉字);
// 无假名时看汉字 vs 拉丁;全兜底 en。
@@ -171,12 +165,9 @@ class SummarizePipeline(
else -> "English (formal written English)"
}
- /** 笔记是否无任何实质条目(四类条目全空;"前回末尾"不算)。解析失败按空处理。 */
+ /** 概括笔记是否为空(解析失败按空处理)。2026-09-16 起单项schema。 */
internal fun notesEntriesEmpty(notes: String): Boolean = runCatching {
- val o = JSONObject(notes)
- listOf("已定事項", "討論中", "行動項目", "人物").all { k ->
- o.optJSONArray(k)?.length() == 0
- }
+ JSONObject(notes).optString("概括").isBlank()
}.getOrDefault(true)
fun mmss(ms: Long): String {
@@ -205,14 +196,12 @@ class SummarizePipeline(
// 另:超 P1 预算的块按行二分一次,保证 prompt ≤ ctx。
/**
- * schema 键名别名表:中文系小模型常把日语/繁体键写成简体
- * (已定事项/行动项目…),不规范化则 extractJson 永远判失败——
- * 这正是"中文会议也解析不了"的头号嫌疑。
+ * schema 键名别名表:中文系小模型常把键写成变体(概要/总结/摘要…),
+ * 不规范化则 extractJson 永远判失败。2026-09-16 单项schema:都归「概括」。
*/
private val KEY_ALIASES = mapOf(
- "已定事项" to "已定事項", "讨论中" to "討論中",
- "行动项目" to "行動項目", "决定事项" to "已定事項",
- "待办事项" to "行動項目", "上次末尾" to "前回末尾",
+ "概要" to "概括", "总结" to "概括", "摘要" to "概括",
+ "内容概括" to "概括", "総括" to "概括", "まとめ" to "概括",
)
internal fun normalizeKeys(o: JSONObject): JSONObject {
@@ -294,9 +283,9 @@ class SummarizePipeline(
if (depth == 0) {
val cand = raw.substring(start, i + 1)
return try {
- // 键名先规范化(简体别名→schema),再校验必需键
+ // 键名先规范化(变体别名→schema),再校验必需键
val o = normalizeKeys(JSONObject(cand))
- if (o.has("已定事項") && o.has("行動項目"))
+ if (o.optString("概括").isNotBlank())
o.toString()
else null
} catch (t: Throwable) {
@@ -391,7 +380,7 @@ class SummarizePipeline(
Log.i(TAG, "SUMMARIZE: ${entries.size} utterances -> $total chunks, " +
"lang=$lang, resumeIdx=$startIdx")
- // ---- 滚动笔记(§5.2)----
+ // ---- 内容概括滚动更新(§5.2)----
var notes = resumeNotes ?: EMPTY_NOTES
var failed = 0
val tP1Start = System.currentTimeMillis()
@@ -400,7 +389,7 @@ class SummarizePipeline(
val user = prompts.p1UserTpl
.replace("{notes_json}", notes)
.replace("{chunk_text}", chunkText)
- listener.onProgress("滚动笔记", idx + 1, total)
+ listener.onProgress("内容概括", idx + 1, total)
val t0 = System.currentTimeMillis()
var out = engine.generate(
systemPrompt = prompts.p1System, userPrompt = user,
@@ -415,7 +404,7 @@ class SummarizePipeline(
// 一次修复重试:把畸形/空洞输出递回去(仍失败保留旧笔记,计 failed)
if (listener.isCancelled()) throw SummarizeCancelledException()
val why = if (emptyEcho)
- "上一次输出是没有任何条目的空笔记。"
+ "上一次输出是空的概括。"
else
"上一次输出不是合法JSON: " +
out.take(200).replace("\n", " ") + "。"
@@ -468,7 +457,7 @@ class SummarizePipeline(
val tP2 = System.currentTimeMillis()
val md = cleanDraft(engine.generate(
systemPrompt = prompts.p2System, userPrompt = user2,
- maxNewTokens = 2500, temperature = 0.5f, topK = 64, topP = 0.95f,
+ maxNewTokens = 1600, temperature = 0.5f, topK = 64, topP = 0.95f,
))
Log.i(TAG, "P2 draft prompt_len=${user2.length} draft_len=${md.length} " +
"ms=${System.currentTimeMillis() - tP2}")
diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml
index d77d7e6..58696c4 100644
--- a/app/src/main/res/values-en/strings.xml
+++ b/app/src/main/res/values-en/strings.xml
@@ -58,11 +58,11 @@
Only zh/ja/en translation is supported; this sentence is in another language
Translator model missing. Run:\n%1$s
Loading summarizer model (%1$s)…
- notes
+ summarizing
drafting
Running %1$s: %2$d/%3$d
Model ready, resuming from checkpoint (%1$d/%2$d lines done)…
- Model ready, %1$d lines — building notes…
+ Model ready, %1$d lines — summarizing…
Minutes saved (%1$s, %2$d chunks%3$s): %4$s
, %1$d chunks failed
Cancelled (previous minutes kept)
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 1f295cc..109c253 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -58,11 +58,11 @@
中日英間の翻訳のみ対応。この文は該当言語ではありません
翻訳モデルがありません。実行してください:\n%1$s
要約モデル(%1$s)を読み込み中…
- メモ更新
+ 要約作成
清書
%1$s実行中: %2$d/%3$d
モデル準備完了、前回の中断点から続行(%1$d/%2$d 消化済み)…
- モデル準備完了、全 %1$d 発、メモ構築を開始…
+ モデル準備完了、全 %1$d 発、要約の作成を開始…
議事録を保存しました(%1$s、%2$d ブロック%3$s):%4$s
、%1$d ブロック解析失敗
中止しました(前回の議事録は保持)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c1412c6..f2cb18c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -59,11 +59,11 @@
仅支持中/日/英互译,该句不属于这三种语言
缺少翻译模型。请执行:\n%1$s
正在加载摘要模型(%1$s)…
- 滚动笔记
+ 内容概括
成稿
正在%1$s: %2$d/%3$d
模型就绪,从上次断点续跑(%1$d/%2$d 句已消化)…
- 模型就绪,共 %1$d 句,开始滚动笔记…
+ 模型就绪,共 %1$d 句,开始内容概括…
纪要已保存(%1$s,%2$d 块%3$s):%4$s
,%1$d 块解析失败
已取消生成(保留上一次的纪要)
diff --git a/app/src/test/java/com/k2fsa/sherpa/onnx/MeetingNotesTest.kt b/app/src/test/java/com/k2fsa/sherpa/onnx/MeetingNotesTest.kt
index 0373f45..435eb2e 100644
--- a/app/src/test/java/com/k2fsa/sherpa/onnx/MeetingNotesTest.kt
+++ b/app/src/test/java/com/k2fsa/sherpa/onnx/MeetingNotesTest.kt
@@ -15,7 +15,7 @@ class MeetingNotesTest {
fun persistRoundTripAndValidation() {
val dir = File.createTempFile("notes", "dir").apply { delete(); mkdirs() }
val f = MeetingNotesIO.pathFor(dir, "20260911-1300")
- val notes = """{"已定事項":[],"討論中":[],"行動項目":[],"人物":[],"前回末尾":"x"}"""
+ val notes = """{"概括":"会议确认预算表9/30前提交,其余待议。"}"""
MeetingNotesIO.save(f, MeetingNotes(notes, 42, 321_000L, 1, "20260911-1300"))
val back = MeetingNotesIO.load(f, "20260911-1300")!!
assertEquals(42, back.entriesP1Done)
@@ -38,8 +38,10 @@ class MeetingNotesTest {
val u = SummarizePipeline.buildDraftUserPrompt(
p, "ja", 1_789_000_000_000L, 3, 3_720_000L, "张三→張三", "NOTESJSON")
assertTrue(u.contains("日本語"))
- assertTrue(u.contains("62:00"))
assertTrue(u.contains("NOTESJSON"))
assertTrue(u.contains("张三→張三"))
+ // 2026-09-16 简化后模板不再含会议元信息占位符;替换后不得残留占位符
+ assertTrue("占位符未替换干净", !u.contains("{language_name}") &&
+ !u.contains("{glossary_lines}") && !u.contains("{notes_json}"))
}
}
diff --git a/app/src/test/java/com/k2fsa/sherpa/onnx/SummarizeLogicTest.kt b/app/src/test/java/com/k2fsa/sherpa/onnx/SummarizeLogicTest.kt
index 19d63ea..ef52845 100644
--- a/app/src/test/java/com/k2fsa/sherpa/onnx/SummarizeLogicTest.kt
+++ b/app/src/test/java/com/k2fsa/sherpa/onnx/SummarizeLogicTest.kt
@@ -77,9 +77,9 @@ class SummarizeLogicTest {
}
}
- // ---- 笔记 JSON 提取 ----
+ // ---- 概括 JSON 提取(2026-09-16 起单项 schema:{"概括":"..."}) ----
private val goodJson =
- """{"已定事項":[{"内容":"Xを承認","出典":"01:00-01:20"}],"討論中":[],"行動項目":[{"内容":"Y","担当者":"田中","期限":"9/30","出典":"02:00-02:10"}],"人物":[{"名前":"田中","立場":"PM"}],"前回末尾":"Yの進め方" }"""
+ """{"概括":"会議はXを承認,Yは田中が9/30までに担当。Zの進め方は未決。"}"""
@Test
fun extractJsonPlain() = assertEquals(goodJson, SummarizePipeline.extractJson(goodJson))
@@ -92,15 +92,23 @@ class SummarizeLogicTest {
@Test
fun extractJsonHandlesBracesInsideStrings() {
- val raw = """{"已定事項":[{"内容":"値は{要確認}","出典":""}],"討論中":[],"行動項目":[],"人物":[],"前回末尾":""}"""
+ val raw = """{"概括":"値は{要確認}なので未決事項として保留"}"""
assertNotNull(SummarizePipeline.extractJson(raw))
}
+ @Test
+ fun extractJsonAcceptKeyAliases() {
+ assertNotNull(SummarizePipeline.extractJson("""{"概要":"会議の要約テキスト"}"""))
+ assertNotNull(SummarizePipeline.extractJson("""{"总结":"会议内容的一段总结"}"""))
+ }
+
@Test
fun extractJsonRejectsGarbageAndMissingKeys() {
assertNull(SummarizePipeline.extractJson("ごめんなさい、分かりません"))
assertNull(SummarizePipeline.extractJson("""{"hello":"world"}"""))
assertNull(SummarizePipeline.extractJson("""{"已定事項":[],}""")) // 不平衡/畸形
+ assertNull(SummarizePipeline.extractJson("""{"已定事項":[]}""")) // 旧schema无概括键
+ assertNull(SummarizePipeline.extractJson("""{"概括":""}""")) // 空概括
}
// ---- 成稿清洗 ----