fix(monitor): 策略3-尾部匹配剥离bot回复

增强_strip_bot_reply_fragments函数,添加第三种剥离策略:

问题:OCR同屏捕获时,bot回复被拼接到用户消息末尾
例:"个登录插件是在哪个地方下载? 4001234S67 ]插件下载:见github..."

解决方案:
1. 检测问句后的尾部内容(≥15字符)
2. 提取纯中文部分,排除英文/数字干扰
3. 与数据库中已存储的bot回复计算相似度
4. 相似度≥85%时直接截断到问句结束

效果:
- 抵抗OCR噪声(标点/空格差异)
- 抵抗英文/数字干扰(只比较纯中文)
- 精确识别尾部污染并剥离

测试验证:
- 污染查询: '个登录插件是在哪个地方下载? 4001234S67 ]插件下载:见github...'
- 相似度: 100% (纯中文'插件下载见'完全匹配)
- 输出: '个登录插件是在哪个地方下载?'
This commit is contained in:
youpaishidifu
2026-08-16 15:27:52 +08:00
parent c75c771604
commit 27a2889028
+30 -1
View File
@@ -4714,11 +4714,16 @@ class MessageMonitor:
被 OCR 拼成一行后,filter4 因检测到 bot 旧回复文本而把整段(含用户新消息)
误判为残片整体丢弃。
两种策略:
三种策略:
1. 精确子串:某条 bot 旧回复 r 完整出现在 text 中 → 直接删除 r
2. 反向包含:OCR 把「用户新消息 + bot 旧回复」按逗号拼成一行,且 r 不是
text 的子串,但 text 中逗号分隔的片段可能是 r 的子串 → 逐片段判断,
剥离属于 bot 回复的片段,保留用户片段。
3. 尾部匹配:OCR 把 bot 旧回复拼在用户消息末尾(无分隔符或仅空格),
文本末尾子串是某条 bot 回复的子串 → 切掉尾部,保留前面用户消息。
例:"个登录插件是在哪个地方下载? 4001234S67 ]插件下载:见github..."
→ "个登录插件是在哪个地方下载?"
使用宽松匹配(≥15字符连续匹配),抵抗OCR噪声导致的微小差异。
⭐ OCR 噪声鲁棒性:bot 旧回复被 OCR 重新识别时,标点(,。)、全/半角、空格
常与原文不一致(如原文"Minecraft 生存服务器,拥有完善的领地系统"被识别成
@@ -4761,6 +4766,30 @@ class MessageMonitor:
if len(user_parts) < len(parts):
ts = ','.join(user_parts).strip(',').strip()
stripped_bot = True
# 策略3:尾部匹配——OCR把bot回复直接拼在用户消息末尾
# 使用纯中文相似度匹配,抵抗OCR噪声和英文/数字干扰
if not stripped_bot:
q_mark_pos = max(ts.find('?'), ts.find('?'))
if q_mark_pos >= 0 and q_mark_pos < len(ts) - 15:
after_q = ts[q_mark_pos + 1:]
after_chinese = re.sub(r'[^\u4e00-\u9fff]', '', after_q)
if len(after_chinese) >= 4: # 尾部有足够中文内容
for r in replied:
if len(r) < 10:
continue
r_chinese = re.sub(r'[^\u4e00-\u9fff]', '', r)
if len(r_chinese) < 4:
continue
# 计算纯中文相似度
sim = SequenceMatcher(None, after_chinese, r_chinese).ratio()
if sim >= 0.85:
ts = ts[:q_mark_pos + 1]
logger.debug(
f"[qq] 尾部匹配剥离bot回复: {ts!r} <- "
f"{ts + after_q[:40]!r} (中文相似度={sim:.0%})"
)
stripped_bot = True
break
if stripped_bot:
ts = re.sub(r'\s+', ' ', ts).strip().strip(',').strip()
return ts