修复短句碎片和模糊重复跳过的误报:允许包含括号的短消息,问句不视为模糊重复
This commit is contained in:
@@ -0,0 +1,96 @@
|
|||||||
|
1:2026-08-08 21:24:25,201 [msgbot] INFO: 消息问答助手启动中...
|
||||||
|
10:2026-08-08 21:25:12,874 [msgbot.monitor] WARNING: [mem-watch] RSS=645MB threads=37 gdi=118 hash=0 seen=0 garbage={} easyocr_alive=False
|
||||||
|
11:2026-08-08 21:25:12,874 [msgbot.monitor] INFO: [mem-watch] 内存看门狗已启动(每30s记录 RSS/线程/GDI)
|
||||||
|
14:2026-08-08 21:25:12,877 [msgbot.monitor] INFO: 消息监控已启动
|
||||||
|
18:2026-08-08 21:25:16,225 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程已启动,等待初始化握手...
|
||||||
|
26:2026-08-08 21:25:18,993 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程存活但握手未完成,等待就绪(≤60s)...
|
||||||
|
27:2026-08-08 21:25:24,892 [msgbot.monitor] WARNING: [wechat] _ensure_hwnd 超时(12s),跳过本轮
|
||||||
|
28:2026-08-08 21:25:28,993 [msgbot.monitor] WARNING: [qq] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
47:2026-08-08 21:25:31,697 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程存活但握手未完成,等待就绪(≤60s)...
|
||||||
|
48:2026-08-08 21:25:31,757 [msgbot.monitor] INFO: [ocr] EasyOCR 子进程就绪
|
||||||
|
49:2026-08-08 21:25:31,760 [msgbot.monitor] DEBUG: [ocr] 已截断崩溃日志,清除旧条目
|
||||||
|
66:2026-08-08 21:25:40,183 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[0]: '哪儿能下到登录插件啊?'
|
||||||
|
67:2026-08-08 21:25:40,184 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[1]: '等级'
|
||||||
|
68:2026-08-08 21:25:40,184 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[2]: '评'
|
||||||
|
69:2026-08-08 21:25:40,184 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[3]: '151'
|
||||||
|
70:2026-08-08 21:25:40,185 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[4]: '成'
|
||||||
|
71:2026-08-08 21:25:40,185 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[5]: '52'
|
||||||
|
72:2026-08-08 21:25:40,185 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[6]: '玩家'
|
||||||
|
73:2026-08-08 21:25:40,185 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[7]: '53'
|
||||||
|
74:2026-08-08 21:25:40,185 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[8]: '成'
|
||||||
|
75:2026-08-08 21:25:40,186 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[9]: '54'
|
||||||
|
76:2026-08-08 21:25:40,186 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[10]: '成功'
|
||||||
|
77:2026-08-08 21:25:40,186 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[11]: '玩家应尊重他人的合法权益;'
|
||||||
|
78:2026-08-08 21:25:40,186 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[12]: '不得进行任何形式'
|
||||||
|
79:2026-08-08 21:25:40,186 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[13]: '17:42:23'
|
||||||
|
80:2026-08-08 21:25:40,186 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[14]: 'PHP的用户端有哪两种登录模式?'
|
||||||
|
81:2026-08-08 21:25:40,187 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[15]: '20;22:50'
|
||||||
|
82:2026-08-08 21:25:40,187 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[16]: '应该去哪儿下载登录插件?'
|
||||||
|
83:2026-08-08 21:25:40,187 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[17]: '玩家应尊重他人的合法权益;'
|
||||||
|
84:2026-08-08 21:25:40,187 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[18]: '不得进行任何形式'
|
||||||
|
85:2026-08-08 21:25:40,187 [msgbot.monitor] INFO: [ocr] [easyocr/原始] line[19]: '['
|
||||||
|
86:2026-08-08 21:25:40,188 [msgbot.monitor] DEBUG: [ocr] 引擎对比: win=30.2(行1,字符231) vs easy=45.0(行20,字符135) → 选easyocr(分数差14.8, 胜率100%, 1轮)
|
||||||
|
107:2026-08-08 21:25:44,211 [msgbot.monitor] WARNING: [mem-watch] RSS=723MB threads=46 gdi=119 hash=2 seen=0 garbage={'qq': 0} easyocr_alive=True
|
||||||
|
108:2026-08-08 21:25:48,540 [msgbot.monitor] WARNING: [wechat] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
174:2026-08-08 21:25:52,422 [msgbot.monitor] WARNING: [qq] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
189:2026-08-08 21:25:56,529 [msgbot.monitor] DEBUG: [ocr] EasyOCR 连续3次超时,杀子进程重来
|
||||||
|
190:2026-08-08 21:25:57,176 [msgbot.monitor] DEBUG: [ocr] 杀 EasyOCR 子进程(连续超时): terminate 后正常退出
|
||||||
|
191:2026-08-08 21:25:57,176 [msgbot.monitor] WARNING: [ocr] 杀 EasyOCR 子进程(连续超时) 诊断: worker死前状态=busy(模式CPU, 上次img长度76816, 更新于2026-08-08T21:25:47.528199,距现在9.0s) | 崩溃日志=2026-08-08 21:25:29.204440 [easyocr_worker] 启动 (pid=28888)
|
||||||
|
253:2026-08-08 21:25:59,490 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程已启动,等待初始化握手...
|
||||||
|
271:2026-08-08 21:26:08,548 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程存活但握手未完成,等待就绪(≤60s)...
|
||||||
|
272:2026-08-08 21:26:09,474 [msgbot.monitor] WARNING: [qq] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
295:2026-08-08 21:26:10,273 [msgbot.monitor] INFO: [ocr] EasyOCR 子进程就绪
|
||||||
|
296:2026-08-08 21:26:10,275 [msgbot.monitor] DEBUG: [ocr] 已截断崩溃日志,清除旧条目
|
||||||
|
303:2026-08-08 21:26:14,307 [msgbot.monitor] WARNING: [mem-watch] RSS=723MB threads=41 gdi=119 hash=2 seen=0 garbage={'qq': 0, 'wechat': 0} easyocr_alive=True
|
||||||
|
304:2026-08-08 21:26:18,574 [msgbot.monitor] WARNING: [wechat] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
352:2026-08-08 21:26:21,766 [msgbot.monitor] WARNING: [qq] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
379:2026-08-08 21:26:26,781 [msgbot.monitor] DEBUG: [ocr] EasyOCR 锁竞争超时(15s),另一线程正在处理中,本次跳过(正常行为)
|
||||||
|
380:2026-08-08 21:26:26,905 [msgbot.monitor] DEBUG: [ocr] EasyOCR 连续3次超时,杀子进程重来
|
||||||
|
381:2026-08-08 21:26:27,541 [msgbot.monitor] DEBUG: [ocr] 杀 EasyOCR 子进程(连续超时): terminate 后正常退出
|
||||||
|
382:2026-08-08 21:26:27,541 [msgbot.monitor] WARNING: [ocr] 杀 EasyOCR 子进程(连续超时) 诊断: worker死前状态=busy(模式CPU, 上次img长度76816, 更新于2026-08-08T21:26:17.894109,距现在9.0s)
|
||||||
|
443:2026-08-08 21:26:29,920 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程已启动,等待初始化握手...
|
||||||
|
459:2026-08-08 21:26:30,933 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程存活但握手未完成,等待就绪(≤60s)...
|
||||||
|
460:2026-08-08 21:26:39,939 [msgbot.monitor] WARNING: [qq] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
481:2026-08-08 21:26:40,705 [msgbot.monitor] INFO: [ocr] EasyOCR 子进程就绪
|
||||||
|
482:2026-08-08 21:26:40,706 [msgbot.monitor] DEBUG: [ocr] 已截断崩溃日志,清除旧条目
|
||||||
|
483:2026-08-08 21:26:40,967 [msgbot.monitor] WARNING: [wechat] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
538:2026-08-08 21:26:44,328 [msgbot.monitor] WARNING: [mem-watch] RSS=711MB threads=37 gdi=119 hash=2 seen=0 garbage={'qq': 0, 'wechat': 0} easyocr_alive=True
|
||||||
|
546:2026-08-08 21:26:52,236 [msgbot.monitor] WARNING: [qq] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
572:2026-08-08 21:26:54,711 [msgbot.monitor] WARNING: [wechat] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
613:2026-08-08 21:26:57,217 [msgbot.monitor] DEBUG: [ocr] EasyOCR 锁竞争超时(15s),另一线程正在处理中,本次跳过(正常行为)
|
||||||
|
615:2026-08-08 21:26:57,466 [msgbot.monitor] DEBUG: [ocr] EasyOCR 连续3次超时,杀子进程重来
|
||||||
|
623:2026-08-08 21:26:58,134 [msgbot.monitor] DEBUG: [ocr] 杀 EasyOCR 子进程(连续超时): terminate 后正常退出
|
||||||
|
624:2026-08-08 21:26:58,135 [msgbot.monitor] WARNING: [ocr] 杀 EasyOCR 子进程(连续超时) 诊断: worker死前状态=busy(模式CPU, 上次img长度76816, 更新于2026-08-08T21:26:48.430053,距现在9.0s) | 崩溃日志=2026-08-08 21:26:38.455250 [easyocr_worker] 启动 (pid=3968)
|
||||||
|
688:2026-08-08 21:27:00,523 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程已启动,等待初始化握手...
|
||||||
|
705:2026-08-08 21:27:01,544 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程存活但握手未完成,等待就绪(≤60s)...
|
||||||
|
706:2026-08-08 21:27:10,528 [msgbot.monitor] WARNING: [qq] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
727:2026-08-08 21:27:11,245 [msgbot.monitor] INFO: [ocr] EasyOCR 子进程就绪
|
||||||
|
728:2026-08-08 21:27:11,246 [msgbot.monitor] DEBUG: [ocr] 已截断崩溃日志,清除旧条目
|
||||||
|
729:2026-08-08 21:27:11,593 [msgbot.monitor] WARNING: [wechat] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
775:2026-08-08 21:27:14,355 [msgbot.monitor] WARNING: [mem-watch] RSS=713MB threads=37 gdi=119 hash=2 seen=0 garbage={'qq': 0, 'wechat': 0} easyocr_alive=True
|
||||||
|
784:2026-08-08 21:27:22,853 [msgbot.monitor] WARNING: [qq] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
810:2026-08-08 21:27:25,161 [msgbot.monitor] WARNING: [wechat] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
855:2026-08-08 21:27:27,839 [msgbot.monitor] DEBUG: [ocr] EasyOCR 锁竞争超时(15s),另一线程正在处理中,本次跳过(正常行为)
|
||||||
|
866:2026-08-08 21:27:27,982 [msgbot.monitor] DEBUG: [ocr] EasyOCR 连续3次超时,杀子进程重来
|
||||||
|
874:2026-08-08 21:27:28,633 [msgbot.monitor] DEBUG: [ocr] 杀 EasyOCR 子进程(连续超时): terminate 后正常退出
|
||||||
|
875:2026-08-08 21:27:28,633 [msgbot.monitor] WARNING: [ocr] 杀 EasyOCR 子进程(连续超时) 诊断: worker死前状态=busy(模式CPU, 上次img长度76816, 更新于2026-08-08T21:27:18.949094,距现在9.0s)
|
||||||
|
899:2026-08-08 21:27:28,909 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程已启动,等待初始化握手...
|
||||||
|
908:2026-08-08 21:27:31,079 [msgbot.monitor] WARNING: [wechat] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
912:2026-08-08 21:27:31,089 [msgbot.monitor] WARNING: [qq] Windows OCR 超时(10.0s)线程存活,跳过
|
||||||
|
913:2026-08-08 21:27:31,089 [msgbot.monitor] WARNING: [qq] EasyOCR 超时(10.0s)线程存活,跳过
|
||||||
|
914:2026-08-08 21:27:31,090 [msgbot.monitor] DEBUG: [ocr] EasyOCR 第1次启动失败,冷却(-1786195651s)
|
||||||
|
916:2026-08-08 21:27:34,032 [msgbot.monitor] ERROR: [ocr] EasyOCR worker 启动即崩溃:
|
||||||
|
917:2026-08-08 21:27:34,032 [msgbot.monitor] WARNING: [ocr] 检测到静默原生崩溃(无日志), 累计1次,下次可能强制 CPU
|
||||||
|
918:2026-08-08 21:27:34,032 [msgbot.monitor] DEBUG: [ocr] EasyOCR 冷却中(30s),跳过
|
||||||
|
919:2026-08-08 21:27:34,063 [msgbot.monitor] DEBUG: [ocr] 杀 EasyOCR 子进程(stop): terminate 后正常退出
|
||||||
|
920:2026-08-08 21:27:34,063 [msgbot.monitor] WARNING: [ocr] 杀 EasyOCR 子进程(stop) 诊断: worker死前状态=busy(模式CPU, 上次img长度76816, 更新于2026-08-08T21:27:18.949094,距现在14.6s)
|
||||||
|
921:2026-08-08 21:27:34,149 [msgbot.monitor] INFO: 消息监控已停止
|
||||||
|
923:2026-08-08 21:27:34,178 [msgbot.monitor] WARNING: [mem-watch] RSS=683MB threads=30 gdi=119 hash=0 seen=0 garbage={} easyocr_alive=False
|
||||||
|
924:2026-08-08 21:27:34,178 [msgbot.monitor] INFO: [mem-watch] 内存看门狗已启动(每30s记录 RSS/线程/GDI)
|
||||||
|
927:2026-08-08 21:27:34,180 [msgbot.monitor] INFO: 消息监控已启动
|
||||||
|
931:2026-08-08 21:27:35,220 [msgbot.monitor] DEBUG: [ocr] EasyOCR 子进程已启动,等待初始化握手...
|
||||||
|
937:2026-08-08 21:27:39,510 [msgbot.monitor] ERROR: [ocr] EasyOCR worker 启动即崩溃:
|
||||||
|
938:2026-08-08 21:27:39,511 [msgbot.monitor] WARNING: [ocr] 检测到静默原生崩溃(无日志), 累计1次,下次可能强制 CPU
|
||||||
|
939:2026-08-08 21:27:39,539 [msgbot.monitor] DEBUG: [ocr] 杀 EasyOCR 子进程(stop): terminate 后正常退出
|
||||||
|
940:2026-08-08 21:27:39,539 [msgbot.monitor] WARNING: [ocr] 杀 EasyOCR 子进程(stop) 诊断: worker死前状态=busy(模式CPU, 上次img长度76816, 更新于2026-08-08T21:27:18.949094,距现在20.0s)
|
||||||
|
941:2026-08-08 21:27:39,636 [msgbot.monitor] INFO: 消息监控已停止
|
||||||
@@ -346,8 +346,8 @@ def _clean_query_for_kb(msg: str) -> str:
|
|||||||
t = msg.strip()
|
t = msg.strip()
|
||||||
# 1. 移除QQ群头衔格式: [头衔]用户名 / 【头衔】用户名
|
# 1. 移除QQ群头衔格式: [头衔]用户名 / 【头衔】用户名
|
||||||
t = re.sub(r'[\[【][^】\]]+[】\]]\s*(?:[A-Za-z0-9_\u4e00-\u9fff]{2,30})\s*[,,]?\s*', '', t)
|
t = re.sub(r'[\[【][^】\]]+[】\]]\s*(?:[A-Za-z0-9_\u4e00-\u9fff]{2,30})\s*[,,]?\s*', '', t)
|
||||||
# 2. 移除@提及: @username
|
# 2. 移除@提及: @username (仅后接空白/串尾时移除,防贪吃无空格的问句)
|
||||||
t = re.sub(r'@[A-Za-z0-9_\u4e00-\u9fff]{2,30}\s*', '', t)
|
t = re.sub(r'@[A-Za-z0-9_\u4e00-\u9fff]{2,20}(?=\s|$)', '', t)
|
||||||
# 3. 移除时间戳: HH:MM:SS / HH:MM / 日期格式
|
# 3. 移除时间戳: HH:MM:SS / HH:MM / 日期格式
|
||||||
t = re.sub(r'\d{1,2}[::]\d{2}(?:[::]\d{2})?\s*', '', t)
|
t = re.sub(r'\d{1,2}[::]\d{2}(?:[::]\d{2})?\s*', '', t)
|
||||||
t = re.sub(r'\d{4}[//\-]\d{1,2}[//\-]\d{1,2}\s*', '', t)
|
t = re.sub(r'\d{4}[//\-]\d{1,2}[//\-]\d{1,2}\s*', '', t)
|
||||||
@@ -360,6 +360,8 @@ def _clean_query_for_kb(msg: str) -> str:
|
|||||||
t = t.replace(nick, '')
|
t = t.replace(nick, '')
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
# 4a. 清理孤立@前缀(昵称移除后@username变成@残余)
|
||||||
|
t = re.sub(r'^@\s*', '', t)
|
||||||
# 5. 清理残留标点/空格
|
# 5. 清理残留标点/空格
|
||||||
t = re.sub(r'[,,、\s]+', ' ', t).strip(',,、 \t')
|
t = re.sub(r'[,,、\s]+', ' ', t).strip(',,、 \t')
|
||||||
return t
|
return t
|
||||||
|
|||||||
+19
-6
@@ -3819,7 +3819,8 @@ class MessageMonitor:
|
|||||||
has_q = any(w in line for w in _q_words)
|
has_q = any(w in line for w in _q_words)
|
||||||
# ⭐ 2026-08-11: 规则4剥离")昵称 "前缀的行(如"关于我们")是真实用户消息,
|
# ⭐ 2026-08-11: 规则4剥离")昵称 "前缀的行(如"关于我们")是真实用户消息,
|
||||||
# 豁免短句检查——否则规则4放行+前缀剥离全白做,消息仍被此规则丢弃。
|
# 豁免短句检查——否则规则4放行+前缀剥离全白做,消息仍被此规则丢弃。
|
||||||
if not has_q and line not in _ui_prefix_stripped:
|
# ⭐ 2026-08-12: 包含括号的消息(如"端0(基岩版)")可能是有效短消息,放行
|
||||||
|
if not has_q and line not in _ui_prefix_stripped and not re.search(r'[()()]', line):
|
||||||
logger.debug(f"[{platform}] OCR 过滤: 短句碎片(≤8字无问词) line={line!r}")
|
logger.debug(f"[{platform}] OCR 过滤: 短句碎片(≤8字无问词) line={line!r}")
|
||||||
continue
|
continue
|
||||||
# ★ 以特殊符号(~、`等)开头 → OCR把"一键"识别为"~键"等碎片
|
# ★ 以特殊符号(~、`等)开头 → OCR把"一键"识别为"~键"等碎片
|
||||||
@@ -6685,10 +6686,20 @@ class MessageMonitor:
|
|||||||
# 4a. 全文本签名快速跳过 — OCR 输出与上轮完全相同 → 无变化,跳过
|
# 4a. 全文本签名快速跳过 — OCR 输出与上轮完全相同 → 无变化,跳过
|
||||||
sig = "||".join([t.strip() for t in texts if t.strip()])
|
sig = "||".join([t.strip() for t in texts if t.strip()])
|
||||||
if sig == self._last_sig.get(platform, ""):
|
if sig == self._last_sig.get(platform, ""):
|
||||||
stale = self._stale_count.get(platform, 0) + 1
|
# 检查是否有未回复的消息(防止未回复消息被永久跳过)
|
||||||
self._stale_count[platform] = stale
|
unreplied_texts = [t for t in texts if t.strip() and t.strip() not in self._replied_texts]
|
||||||
logger.debug(f"[{platform}] Poll: 签名相同(#{stale})静默跳过: {sig[:60]!r}")
|
if not unreplied_texts:
|
||||||
return
|
# 所有消息均已回复,正常跳过
|
||||||
|
stale = self._stale_count.get(platform, 0) + 1
|
||||||
|
self._stale_count[platform] = stale
|
||||||
|
logger.debug(f"[{platform}] Poll: 签名相同(#{stale})静默跳过: {sig[:60]!r}")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# 有未回复消息,继续处理(不跳过)
|
||||||
|
logger.info(f"[{platform}] Poll: 签名相同但有{len(unreplied_texts)}条未回复消息,继续处理: {[t[:30] for t in unreplied_texts]}")
|
||||||
|
# 仍然更新 stale 计数器,但不 return
|
||||||
|
stale = self._stale_count.get(platform, 0) + 1
|
||||||
|
self._stale_count[platform] = stale
|
||||||
|
|
||||||
# 4. 用全局 _seen_messages 去重(跨平台、跨轮次防回环)
|
# 4. 用全局 _seen_messages 去重(跨平台、跨轮次防回环)
|
||||||
# 精确匹配 + 模糊匹配(OCR 噪声导致精确匹配失效)
|
# 精确匹配 + 模糊匹配(OCR 噪声导致精确匹配失效)
|
||||||
@@ -6972,7 +6983,9 @@ class MessageMonitor:
|
|||||||
continue
|
continue
|
||||||
# 模糊匹配:OCR 输出每次微小差异(勖彐碎片、行首数字、尾部截断)
|
# 模糊匹配:OCR 输出每次微小差异(勖彐碎片、行首数字、尾部截断)
|
||||||
# 精确匹配命中不了 → 用 difflib 相似度去重
|
# 精确匹配命中不了 → 用 difflib 相似度去重
|
||||||
if self._fuzzy_duplicate(ts, seen, threshold=0.80):
|
# ⭐ 2026-08-12: 包含问号的问句(如"PHP用户端有哪两种登录模式?")不视为模糊重复
|
||||||
|
# 用户可能重复问相同问题,允许再次处理
|
||||||
|
if not re.search(r'[??]', ts) and self._fuzzy_duplicate(ts, seen, threshold=0.80):
|
||||||
logger.debug(f"[{platform}] 模糊重复跳过: {ts[:50]}")
|
logger.debug(f"[{platform}] 模糊重复跳过: {ts[:50]}")
|
||||||
continue
|
continue
|
||||||
# 超长文本(>200字符)跳过——大概率是堆积历史或 bot 自己的回复,不是单条新消息
|
# 超长文本(>200字符)跳过——大概率是堆积历史或 bot 自己的回复,不是单条新消息
|
||||||
|
|||||||
+192
@@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
实时监控msgbot日志 - 诊断两个问题:
|
||||||
|
1. 知识库匹配错误(牛头不对马嘴)
|
||||||
|
2. OCR连续性问题(需要表情包/图片清屏才能触发OCR)
|
||||||
|
|
||||||
|
使用方法:
|
||||||
|
1. 先启动msgbot
|
||||||
|
2. 运行此脚本开始监控
|
||||||
|
3. 在QQ/微信中发送消息测试
|
||||||
|
4. 实时查看分析结果
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
class LogAnalyzer:
|
||||||
|
def __init__(self):
|
||||||
|
self.kb_matches = []
|
||||||
|
self.ocr_events = []
|
||||||
|
self.pixel_stats = []
|
||||||
|
self.replies = []
|
||||||
|
|
||||||
|
def analyze_line(self, line: str):
|
||||||
|
"""分析单行日志"""
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 分析KB匹配
|
||||||
|
if "KB搜索输入" in line:
|
||||||
|
self.kb_matches.append({
|
||||||
|
'time': datetime.now(),
|
||||||
|
'type': 'search_input',
|
||||||
|
'content': line
|
||||||
|
})
|
||||||
|
|
||||||
|
# 分析KB结果
|
||||||
|
elif "KB结果" in line and "标题" in line:
|
||||||
|
self.kb_matches.append({
|
||||||
|
'time': datetime.now(),
|
||||||
|
'type': 'search_result',
|
||||||
|
'content': line
|
||||||
|
})
|
||||||
|
|
||||||
|
# 分析内容相关性
|
||||||
|
elif "内容相关性检查" in line:
|
||||||
|
self.kb_matches.append({
|
||||||
|
'time': datetime.now(),
|
||||||
|
'type': 'relevance_check',
|
||||||
|
'content': line
|
||||||
|
})
|
||||||
|
|
||||||
|
# 分析像素检测
|
||||||
|
elif "像素统计" in line or "像素检测" in line:
|
||||||
|
self.pixel_stats.append({
|
||||||
|
'time': datetime.now(),
|
||||||
|
'content': line
|
||||||
|
})
|
||||||
|
|
||||||
|
# 分析OCR事件
|
||||||
|
elif "OCR" in line and ("跳过" in line or "触发" in line or "输出" in line):
|
||||||
|
self.ocr_events.append({
|
||||||
|
'time': datetime.now(),
|
||||||
|
'content': line
|
||||||
|
})
|
||||||
|
|
||||||
|
# 分析回复
|
||||||
|
elif "新消息" in line and ("回复" in line or "发送" in line):
|
||||||
|
self.replies.append({
|
||||||
|
'time': datetime.now(),
|
||||||
|
'content': line
|
||||||
|
})
|
||||||
|
|
||||||
|
def print_summary(self):
|
||||||
|
"""打印分析摘要"""
|
||||||
|
print("\n" + "="*80)
|
||||||
|
print(f"分析摘要 - {datetime.now().strftime('%H:%M:%S')}")
|
||||||
|
print("="*80)
|
||||||
|
|
||||||
|
# KB匹配分析
|
||||||
|
print(f"\n【知识库匹配】共 {len(self.kb_matches)} 条记录")
|
||||||
|
if self.kb_matches:
|
||||||
|
print(" 最近5条:")
|
||||||
|
for item in self.kb_matches[-5:]:
|
||||||
|
print(f" [{item['time'].strftime('%H:%M:%S')}] {item['content'][:80]}")
|
||||||
|
|
||||||
|
# 像素检测分析
|
||||||
|
print(f"\n【像素检测】共 {len(self.pixel_stats)} 条记录")
|
||||||
|
if self.pixel_stats:
|
||||||
|
print(" 最近5条:")
|
||||||
|
for item in self.pixel_stats[-5:]:
|
||||||
|
print(f" [{item['time'].strftime('%H:%M:%S')}] {item['content'][:80]}")
|
||||||
|
|
||||||
|
# OCR事件分析
|
||||||
|
print(f"\n【OCR事件】共 {len(self.ocr_events)} 条记录")
|
||||||
|
if self.ocr_events:
|
||||||
|
print(" 最近5条:")
|
||||||
|
for item in self.ocr_events[-5:]:
|
||||||
|
print(f" [{item['time'].strftime('%H:%M:%S')}] {item['content'][:80]}")
|
||||||
|
|
||||||
|
# 回复分析
|
||||||
|
print(f"\n【回复记录】共 {len(self.replies)} 条记录")
|
||||||
|
if self.replies:
|
||||||
|
print(" 最近5条:")
|
||||||
|
for item in self.replies[-5:]:
|
||||||
|
print(f" [{item['time'].strftime('%H:%M:%S')}] {item['content'][:80]}")
|
||||||
|
|
||||||
|
print("="*80)
|
||||||
|
|
||||||
|
def monitor_log_file(log_file: str):
|
||||||
|
"""监控日志文件"""
|
||||||
|
print(f"开始监控日志文件: {log_file}")
|
||||||
|
print("按 Ctrl+C 停止监控")
|
||||||
|
print("-"*80)
|
||||||
|
|
||||||
|
analyzer = LogAnalyzer()
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
# 跳到文件末尾
|
||||||
|
f.seek(0, 2)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
line = f.readline()
|
||||||
|
if not line:
|
||||||
|
time.sleep(0.1)
|
||||||
|
continue
|
||||||
|
|
||||||
|
analyzer.analyze_line(line)
|
||||||
|
print(line.rstrip())
|
||||||
|
|
||||||
|
# 每30秒打印一次摘要
|
||||||
|
if int(time.time()) % 30 == 0:
|
||||||
|
analyzer.print_summary()
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n监控已停止")
|
||||||
|
analyzer.print_summary()
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"日志文件不存在: {log_file}")
|
||||||
|
print("请先启动msgbot,然后运行此脚本")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
# 查找最新的日志文件
|
||||||
|
log_patterns = [
|
||||||
|
"msgbot.log",
|
||||||
|
"monitor.log",
|
||||||
|
"debug.log",
|
||||||
|
"*.log"
|
||||||
|
]
|
||||||
|
|
||||||
|
log_files = []
|
||||||
|
for pattern in log_patterns:
|
||||||
|
log_files.extend(list(Path(".").glob(pattern)))
|
||||||
|
|
||||||
|
if not log_files:
|
||||||
|
print("未找到日志文件,请先启动msgbot")
|
||||||
|
print("然后运行此脚本开始监控")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("找到以下日志文件:")
|
||||||
|
for i, f in enumerate(log_files, 1):
|
||||||
|
print(f" {i}. {f.name}")
|
||||||
|
|
||||||
|
print("\n监控功能说明:")
|
||||||
|
print("1. 实时显示日志内容")
|
||||||
|
print("2. 自动分析KB匹配过程")
|
||||||
|
print("3. 跟踪像素检测结果")
|
||||||
|
print("4. 记录OCR事件")
|
||||||
|
print("5. 监控回复发送")
|
||||||
|
|
||||||
|
# 选择要监控的文件
|
||||||
|
if len(log_files) == 1:
|
||||||
|
monitor_log_file(str(log_files[0]))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
choice = int(input("\n请选择要监控的日志文件编号: ")) - 1
|
||||||
|
if 0 <= choice < len(log_files):
|
||||||
|
monitor_log_file(str(log_files[choice]))
|
||||||
|
else:
|
||||||
|
print("无效的选择")
|
||||||
|
except ValueError:
|
||||||
|
print("请输入有效的数字")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user