|
1 | | -//! 笔记废话出口过滤管线(REQ-082 / v0.6.0 M1;v0.7.5 净化接线)。 |
| 1 | +//! 笔记废话出口过滤管线(REQ-082 / v0.6.0 M1;v0.7.5 净化接线;v0.12.0 M1 正文源多态)。 |
2 | 2 | //! |
| 3 | +//! @ai-context: v0.12.0(ADR-021):filter_note 升级为正文源分派入口—— |
| 4 | +//! detect_body_source 判定 (segments, ocr_blocks) 的正文来源: |
| 5 | +//! Transcript→既有口语过滤链(视频,零改动);OcrDirect→ |
| 6 | +//! note_filter_ocr 精简链(图文,OCR 文本直接入 markdown 正文); |
| 7 | +//! Empty→标题仅。分派后重建路径(refresh_screen_points/ |
| 8 | +//! apply_ai_decisions/structure)按 body_source 分流,防覆盖。 |
3 | 9 | //! @ai-context: 过滤链(纯规则,本地优先): |
4 | 10 | //! ① UI 垃圾特征兜底(复用 REQ-083 黑名单同表) |
5 | 11 | //! ② 低置信丢弃(confidence <0.6) |
|
22 | 28 | //! 的短文本不是"纯符号"碎片);confidence=None(字幕段)跳过 |
23 | 29 | //! 低置信规则;净化阈值集中 purify_config(REQ-173 JSON 可校准)。 |
24 | 30 |
|
| 31 | +use crate::note_body_source::{detect_body_source, BodySource}; |
25 | 32 | use crate::purify_config::PurifyConfig; |
26 | 33 | use crate::symbol_normalize::SymbolNormalizeConfig; |
27 | 34 | use crate::types::{SessionOcrBlock, SessionScreen, SessionSegment, TranscriptSegment}; |
@@ -165,14 +172,45 @@ pub struct NoteFilterResult { |
165 | 172 | /// 会话转笔记"诚实降级;serde skip:markdown 已含该行,前端无需重复字段) |
166 | 173 | #[serde(skip)] |
167 | 174 | pub(crate) warning: Option<String>, |
| 175 | + /// v0.12.0(ADR-021):正文来源(serde skip——内部透传:refresh_screen_points/ |
| 176 | + /// apply_ai_decisions/structure 按来源分派重建,不序列化给前端) |
| 177 | + #[serde(skip)] |
| 178 | + pub(crate) body_source: BodySource, |
| 179 | + /// v0.12.0(ADR-021):OCR 直接正文净化文本序列(OcrDirect 分支产物; |
| 180 | + /// serde skip——markdown 已含正文,前端无需重复字段) |
| 181 | + #[serde(skip)] |
| 182 | + pub(crate) ocr_body: Vec<String>, |
168 | 183 | } |
169 | 184 |
|
170 | | -/// 笔记过滤(纯函数):转写段 + OCR 块 → 过滤后笔记。 |
| 185 | +/// 笔记过滤(纯函数,正文源分派入口):转写段 + OCR 块 → 过滤后笔记。 |
| 186 | +/// |
| 187 | +/// @ai-context: v0.12.0(ADR-021):detect_body_source 三路分派—— |
| 188 | +/// Transcript→filter_note_transcript(既有口语过滤链,零改动); |
| 189 | +/// OcrDirect→filter_note_from_ocr(图文 OCR 精简链,文本入正文); |
| 190 | +/// Empty→标题仅。视频会话路径行为逐字节不变(回归护栏)。 |
| 191 | +pub fn filter_note( |
| 192 | + title: &str, |
| 193 | + segments: &[SessionSegment], |
| 194 | + ocr_blocks: &[SessionOcrBlock], |
| 195 | + ui_junk: &UiJunkList, |
| 196 | + env: &PurifyEnv, |
| 197 | +) -> NoteFilterResult { |
| 198 | + match detect_body_source(segments, ocr_blocks) { |
| 199 | + BodySource::Transcript => { |
| 200 | + filter_note_transcript(title, segments, ocr_blocks, ui_junk, env) |
| 201 | + } |
| 202 | + BodySource::OcrDirect => crate::note_filter_ocr::filter_note_from_ocr(title, ocr_blocks, env), |
| 203 | + BodySource::Empty => filter_note_empty(title), |
| 204 | + } |
| 205 | +} |
| 206 | + |
| 207 | +/// 转写段正文过滤链(视频会话——既有路径,v0.12.0 提取自原 filter_note 主体, |
| 208 | +/// 逻辑零改动;OcrDirect/Empty 分派见 filter_note)。 |
171 | 209 | /// |
172 | 210 | /// @ai-context: 转写段按过滤链处理(见模块头);OCR 画面要点先经 |
173 | 211 | /// screens::filter_usable_blocks(v0.7.5:低分 0.7/单字符/边缘 |
174 | 212 | /// 条带/视频页 UI 共现/错字纠错)排除,再屏构建与精确去重。 |
175 | | -pub fn filter_note( |
| 213 | +fn filter_note_transcript( |
176 | 214 | title: &str, |
177 | 215 | segments: &[SessionSegment], |
178 | 216 | ocr_blocks: &[SessionOcrBlock], |
@@ -329,6 +367,29 @@ pub fn filter_note( |
329 | 367 | merged, |
330 | 368 | purify: config.clone(), |
331 | 369 | warning: None, |
| 370 | + body_source: BodySource::Transcript, |
| 371 | + ocr_body: Vec::new(), |
| 372 | + } |
| 373 | +} |
| 374 | + |
| 375 | +/// 空正文过滤链(纯函数):无转写段且无可用 OCR 块 → 标题仅 markdown。 |
| 376 | +/// |
| 377 | +/// @ai-context: 不 panic 契约(图文会话无内容时转笔记的诚实降级——标题 |
| 378 | +/// 即全部;与 v0.11.7 可行性契约一致)。 |
| 379 | +fn filter_note_empty(title: &str) -> NoteFilterResult { |
| 380 | + NoteFilterResult { |
| 381 | + title: title.to_string(), |
| 382 | + markdown: format!("# {}", title), |
| 383 | + kept: Vec::new(), |
| 384 | + ocr_points: Vec::new(), |
| 385 | + ocr_screens: Vec::new(), |
| 386 | + stats: FilterStats::default(), |
| 387 | + filtered: Vec::new(), |
| 388 | + merged: Vec::new(), |
| 389 | + purify: PurifyConfig::default(), |
| 390 | + warning: None, |
| 391 | + body_source: BodySource::Empty, |
| 392 | + ocr_body: Vec::new(), |
332 | 393 | } |
333 | 394 | } |
334 | 395 |
|
@@ -479,13 +540,23 @@ pub(crate) fn apply_session_warning(result: &mut NoteFilterResult, status: &str) |
479 | 540 | /// 净化配置随 result 透传(段落阈值/锚点与预览口径一致)。 |
480 | 541 | pub fn refresh_screen_points(result: &mut NoteFilterResult) { |
481 | 542 | result.ocr_points = render_screen_points(&result.ocr_screens); |
482 | | - result.markdown = rebuild_markdown( |
483 | | - &result.title, |
484 | | - &result.kept, |
485 | | - &[], |
486 | | - &result.purify, |
487 | | - result.warning.as_deref(), |
488 | | - ); |
| 543 | + // v0.12.0(ADR-021):按正文源分派重建——OcrDirect 走 OCR 正文重建 |
| 544 | + // (kept 为空,走 rebuild_markdown 会把 OCR 正文覆盖成标题仅) |
| 545 | + result.markdown = match result.body_source { |
| 546 | + BodySource::OcrDirect => crate::note_filter_ocr::rebuild_ocr_markdown( |
| 547 | + &result.title, |
| 548 | + &result.ocr_body, |
| 549 | + &result.purify, |
| 550 | + result.warning.as_deref(), |
| 551 | + ), |
| 552 | + _ => rebuild_markdown( |
| 553 | + &result.title, |
| 554 | + &result.kept, |
| 555 | + &[], |
| 556 | + &result.purify, |
| 557 | + result.warning.as_deref(), |
| 558 | + ), |
| 559 | + }; |
489 | 560 | } |
490 | 561 |
|
491 | 562 | /// 组装 Markdown(标题 + 讲述内容;段落切分复用 concat 口径; |
|
0 commit comments