Fix OCR self-feed loop: bold md detect, repeat-char garbage, SequenceMatcher reply fragment catch
- _is_markdown_document: add bold **text** detection before len<10 gate - Garbage filter: 5+ consecutive same char detect (LLLLL/11111) - OCR filter + Poll filter: SequenceMatcher longest common CJK substring >=12 chars vs replied_texts. Catches TTTT八X1r以...工单]、**使用**权 fragments that exact/substring/fuzzy match miss due to OCR noise prefix.
This commit is contained in:
+59
-3
@@ -1315,6 +1315,33 @@ class MessageMonitor:
|
||||
logger.debug(f"[{platform}] OCR 过滤: md纯标题碎片 line={line!r}")
|
||||
continue
|
||||
|
||||
# —— OCR 幻觉乱码检测:连续重复字符(LLL / 111 / ===) ——
|
||||
# EasyOCR 对头像/特殊 UI 输出如 ":山LeLyeeuCL...ggsegU凶>债券管理"
|
||||
# 普通消息不含连续 5+ 相同字符
|
||||
if re.search(r'([A-Za-z0-9])\1{4,}', line):
|
||||
logger.debug(f"[{platform}] OCR 过滤: 幻觉乱码(重复) line={line!r}")
|
||||
continue
|
||||
|
||||
# —— 回复内容片段检测:OCR 捕获 bot 自己刚发的回复 ——
|
||||
# 用 SequenceMatcher 找 OCR 行 CJK 与 replied 文本 CJK 的最长公共子串
|
||||
if self._replied_texts and len(line) > 15:
|
||||
import difflib
|
||||
cjk_line = ''.join(c for c in line if '\u4e00' <= c <= '\u9fff')
|
||||
if len(cjk_line) >= 12:
|
||||
is_match = False
|
||||
for r in self._replied_texts:
|
||||
r_cjk = ''.join(c for c in r if '\u4e00' <= c <= '\u9fff')
|
||||
if not r_cjk:
|
||||
continue
|
||||
sm = difflib.SequenceMatcher(None, cjk_line, r_cjk)
|
||||
longest = sm.find_longest_match(0, len(cjk_line), 0, len(r_cjk))
|
||||
if longest.size >= 12:
|
||||
is_match = True
|
||||
break
|
||||
if is_match:
|
||||
logger.debug(f"[{platform}] OCR 过滤: 回复内容片段 line={line[:60]!r}")
|
||||
continue
|
||||
|
||||
texts.append(line)
|
||||
logger.debug(f"[{platform}] OCR -> {len(ocr_lines)} raw, {len(texts)} filtered, "
|
||||
f"last=\"{texts[-1][:50] if texts else ''}\"")
|
||||
@@ -1684,6 +1711,9 @@ class MessageMonitor:
|
||||
# OCR 退化 md 链接缺括号:短文本也检测
|
||||
if re.search(r'\[.+?\]\(', text):
|
||||
return True
|
||||
# **加粗**语法:短文本也检测(bot md 回复标配,用户日常不用)
|
||||
if re.search(r'\*\*.+?\*\*', text):
|
||||
return True
|
||||
if len(text) < 10:
|
||||
return False
|
||||
# ##直接跟汉字(OCR 丢失 ##后空格时常见:##文档导航)
|
||||
@@ -2384,9 +2414,35 @@ class MessageMonitor:
|
||||
continue
|
||||
# _replied_texts 子串包含:OCR 提取的 md 表格细胞值(如"依赖插件可选/必选")
|
||||
# 是 bot 回复表的一部分,不是用户提问。长度>5才检查避免误杀短消息。
|
||||
if len(ts) > 5 and any(ts in r for r in replied):
|
||||
logger.debug(f"[{platform}] 回复子串包含跳过({len(ts)}c): {ts[:50]}")
|
||||
continue
|
||||
# OCR 常把 ,转 , 等标点差异,做双向子串检查(原始+归一化标点)
|
||||
if len(ts) > 5:
|
||||
if any(ts in r for r in replied):
|
||||
logger.debug(f"[{platform}] 回复子串包含跳过({len(ts)}c): {ts[:50]}")
|
||||
continue
|
||||
# 归一化标点后重试(OCR 噪声:,→, 。→. !→! ?→?)
|
||||
norm_ts = ts.replace(',', ',').replace('.', '。').replace('!', '!').replace('?', '?')
|
||||
if norm_ts != ts and any(norm_ts in r for r in replied):
|
||||
logger.debug(f"[{platform}] 回复子串包含跳过(归一化,{len(ts)}c): {ts[:50]}")
|
||||
continue
|
||||
# _replied_texts 长公共子串检测:OCR 截取的碎片开头/结尾有噪声,精确/子串匹配失效
|
||||
# 用 SequenceMatcher 找 CJK 字符的最长公共子串
|
||||
if len(ts) > 15:
|
||||
cjk_ts = ''.join(c for c in ts if '\u4e00' <= c <= '\u9fff')
|
||||
if len(cjk_ts) >= 12:
|
||||
import difflib
|
||||
matched_r = False
|
||||
for r in replied:
|
||||
r_cjk = ''.join(c for c in r if '\u4e00' <= c <= '\u9fff')
|
||||
if not r_cjk:
|
||||
continue
|
||||
sm = difflib.SequenceMatcher(None, cjk_ts, r_cjk)
|
||||
longest = sm.find_longest_match(0, len(cjk_ts), 0, len(r_cjk))
|
||||
if longest.size >= 12:
|
||||
matched_r = True
|
||||
break
|
||||
if matched_r:
|
||||
logger.debug(f"[{platform}] 回复公共子串跳过: {ts[:50]}")
|
||||
continue
|
||||
if ts in seen:
|
||||
continue
|
||||
# 模糊匹配:OCR 输出每次微小差异(勖彐碎片、行首数字、尾部截断)
|
||||
|
||||
Reference in New Issue
Block a user