prompt 加要求:第一行 `TITLE: <主题>` + `---` + 正文;backend parse 头两行,覆盖 recordings.title;summary 字段不含 title 行。 失败 fallback 不动 title。前端 sidebar/主视图自带 5s 轮询自动刷新。
This commit is contained in:
+44
-5
@@ -427,8 +427,8 @@ async fn process_recording(s: AppState, id: i64) {
|
||||
);
|
||||
}
|
||||
|
||||
// LLM:生成会议纪要
|
||||
let summary = match call_llm_summary(&s, &transcript).await {
|
||||
// LLM:生成会议纪要 + 标题
|
||||
let raw = match call_llm_summary(&s, &transcript).await {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
tracing::error!(%id, error = %e, "LLM failed");
|
||||
@@ -436,14 +436,50 @@ async fn process_recording(s: AppState, id: i64) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let (new_title, summary_body) = parse_title_from_summary(&raw);
|
||||
{
|
||||
let conn = s.db.lock().unwrap();
|
||||
if let Some(t) = new_title.as_deref() {
|
||||
let _ = conn.execute(
|
||||
"UPDATE recordings SET title = ?1, summary = ?2, status = 'done', error = NULL WHERE id = ?3",
|
||||
params![t, &summary_body, id],
|
||||
);
|
||||
} else {
|
||||
let _ = conn.execute(
|
||||
"UPDATE recordings SET summary = ?1, status = 'done', error = NULL WHERE id = ?2",
|
||||
params![&summary, id],
|
||||
params![&summary_body, id],
|
||||
);
|
||||
}
|
||||
tracing::info!(%id, "done");
|
||||
}
|
||||
tracing::info!(%id, title = ?new_title, "done");
|
||||
}
|
||||
|
||||
/// 从 LLM 输出剥离 `TITLE: ...\n---\n` 头部。
|
||||
/// 返回 (Option<title>, summary_body),title 失败时返回 None + 原文。
|
||||
fn parse_title_from_summary(raw: &str) -> (Option<String>, String) {
|
||||
let mut lines = raw.lines();
|
||||
let first = lines.next().unwrap_or("").trim();
|
||||
let Some(rest) = first
|
||||
.strip_prefix("TITLE:")
|
||||
.or_else(|| first.strip_prefix("Title:"))
|
||||
.or_else(|| first.strip_prefix("标题:"))
|
||||
.or_else(|| first.strip_prefix("标题:"))
|
||||
else {
|
||||
return (None, raw.to_string());
|
||||
};
|
||||
let title: String = rest.trim().chars().take(80).collect();
|
||||
if title.is_empty() {
|
||||
return (None, raw.to_string());
|
||||
}
|
||||
// 吃掉接下来的 `---` separator + 空行
|
||||
let body: String = lines
|
||||
.skip_while(|l| {
|
||||
let t = l.trim();
|
||||
t.is_empty() || t == "---" || t.starts_with("---")
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
(Some(title), body)
|
||||
}
|
||||
|
||||
fn set_status(s: &AppState, id: i64, status: &str, transcript: Option<&str>, error: Option<&str>) {
|
||||
@@ -501,8 +537,11 @@ async fn call_llm_summary(s: &AppState, transcript: &str) -> Result<String, Stri
|
||||
"model": s.llm_model,
|
||||
"messages": [
|
||||
{ "role": "system", "content":
|
||||
"你是一个会议纪要助手。根据语音转写整理一份结构化纪要(markdown 格式):\n\
|
||||
"你是一个会议纪要助手。根据语音转写输出:\n\
|
||||
\n\
|
||||
第一行:`TITLE: <8-20 字符的会议主题>`(不含日期/时间,提取核心议题)\n\
|
||||
第二行:`---`\n\
|
||||
之后是 markdown 纪要:\n\
|
||||
1. **概要**:1-2 句话总结\n\
|
||||
2. **关键讨论点**:bullet 列出\n\
|
||||
3. **决定 / 结论**\n\
|
||||
|
||||
Reference in New Issue
Block a user