修复OCR回复牛头不对马嘴+残留消息+EasyOCR崩溃捕获+360误报
1. knowledge_base.py: 检索排序惩罚标题-only/短正文段,降低heading bonus,奖励正文命中,避免纯标题/免责声明段排第一 2. auto_reply.py: 动作类查询要求正文命中才放行;拒绝纯标题/免责声明答案 3. monitor.py: 调试截图改存%LOCALAPPDATA%/windowmsgbot/ocr_debug避开源码目录(360); 收紧残留消息fresh gate(600s硬门); 启动即崩溃/请求崩溃时读取easyocr_crash.log写入主日志 4. easyocr_worker.py: 加faulthandler+sys.excepthook全局捕获,崩溃traceback落盘%LOCALAPPDATA%/windowmsgbot/easyocr_crash.log
This commit is contained in:
+31
-5
@@ -80,8 +80,10 @@ class AutoReply:
|
||||
"""查询词与匹配文档至少有一个共同2字+词,防偶然匹配。
|
||||
短查询(≤2个2字+词)直接放行,不做严格检查。
|
||||
检查顺序:heading → title → content/body。
|
||||
动作/配置类查询额外要求正文含核心词,不能只命中标题。
|
||||
高匹配度(≥10.0):如果heading+title+body都无共同词,直接拒绝。
|
||||
"""
|
||||
import re
|
||||
q_tokens = set(t for t in jieba.cut(query) if len(t) >= 2)
|
||||
if len(q_tokens) <= 2:
|
||||
return True # 短查询不拦截
|
||||
@@ -89,20 +91,35 @@ class AutoReply:
|
||||
# 1. 检查 heading + title
|
||||
doc_text = doc.get("heading", "") + " " + doc.get("title", "")
|
||||
d_tokens = set(t for t in jieba.cut(doc_text) if len(t) >= 2)
|
||||
if q_tokens & d_tokens:
|
||||
return True
|
||||
has_heading_hit = bool(q_tokens & d_tokens)
|
||||
|
||||
# 2. 检查 content/body(TF-IDF匹配的段落正文)
|
||||
body = doc.get("content", "") or doc.get("body", "")
|
||||
b_tokens = set()
|
||||
has_body_hit = False
|
||||
if body:
|
||||
# 截断前500字防超长文档
|
||||
b_tokens = set(t for t in jieba.cut(body[:500]) if len(t) >= 2)
|
||||
if q_tokens & b_tokens:
|
||||
return True
|
||||
has_body_hit = bool(q_tokens & b_tokens)
|
||||
|
||||
# 动作/配置类查询:不能只命中标题,正文必须也含核心词
|
||||
# 否则"# Sdf1系列登录插件使用说明"会因标题含"登录/插件/使用"被误放
|
||||
action_keywords = {"怎么", "如何", "使用", "配置", "修改", "设置", "安装", "用法"}
|
||||
if action_keywords & q_tokens:
|
||||
core_q_tokens = q_tokens - {"插件", "使用", "怎么", "如何", "吗", "呢", "吧"}
|
||||
if core_q_tokens and not (core_q_tokens & b_tokens):
|
||||
logger.debug(
|
||||
f"动作查询正文相关性不足: query={query[:40]!r} "
|
||||
f"heading_hit={has_heading_hit} body_hit={has_body_hit}"
|
||||
)
|
||||
return False
|
||||
|
||||
if has_heading_hit or has_body_hit:
|
||||
return True
|
||||
|
||||
# 3. 高匹配度但无共同词:可能是TF-IDF误匹配,拒绝
|
||||
# 用户问"PVP击杀播报",KB返回"json格式说明"(匹配度9.53)→ 应拒绝
|
||||
if score >= 10.0 and not (q_tokens & d_tokens) and not (q_tokens & (b_tokens if body else set())):
|
||||
if score >= 10.0 and not has_heading_hit and not has_body_hit:
|
||||
return False
|
||||
|
||||
return False
|
||||
@@ -128,6 +145,15 @@ class AutoReply:
|
||||
if not answer:
|
||||
return None
|
||||
|
||||
# 拒绝把纯标题/免责声明当作答案发送
|
||||
import re
|
||||
if answer.startswith("# ") and len(answer) < 60:
|
||||
logger.debug(f"[{platform}] 答案为纯标题,跳过: {answer[:60]!r}")
|
||||
return None
|
||||
if re.search(r'特别说明|免责声明|本插件.*自行修改|仅供参考|版权所有', answer):
|
||||
logger.debug(f"[{platform}] 答案为免责声明/通用说明,跳过: {answer[:60]!r}")
|
||||
return None
|
||||
|
||||
# 防重复指纹
|
||||
fp = hash(f"{platform}:{msg}:{answer[:80]}")
|
||||
replied_set = self._replied.setdefault(platform, set())
|
||||
|
||||
Reference in New Issue
Block a user