修复知识库短查询不匹配+QQ消息抓不到
问题1: 你叫啥名字→分词["你","叫","啥","名字"] → len(t)>=2过滤只剩["名字"]→len(tokens)<=1直接返回空 修复: 放宽过滤 len(tokens)<=1→len(tokens)==0且len>=2 添加子串匹配回退: TF-IDF无结果时检查查询是否包含在文档中 问题2: QQ消息抓不到 _read_via_clipboard 加重试机制(首次空自动重试1次) _read_win32_texts 降低文本长度过滤 4→2 字 QQ路径加 _click_message_area 确保焦点在消息区
This commit is contained in:
+13
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple, Optional
|
||||
|
||||
@@ -73,9 +74,9 @@ class KnowledgeBase:
|
||||
if not self.is_ready or not query.strip():
|
||||
return []
|
||||
|
||||
# 短文本(< 4 个字或 <= 2 个有效词)不做搜索,避免噪音
|
||||
# 短文本:至少 2 个字且至少有 1 个有效词才搜索
|
||||
tokens = [t for t in jieba.cut(query.strip()) if len(t) >= 2]
|
||||
if len(query.strip()) < 4 or len(tokens) <= 1:
|
||||
if len(query.strip()) < 2 or len(tokens) == 0:
|
||||
return []
|
||||
|
||||
query_vec = self._vectorizer.transform([query])
|
||||
@@ -88,6 +89,16 @@ class KnowledgeBase:
|
||||
if score < min_score:
|
||||
continue
|
||||
results.append((self.docs[idx], score))
|
||||
if results:
|
||||
return results
|
||||
|
||||
# Fallback: 子串匹配(查询包含在文档标题或内容中)
|
||||
q = query.strip()
|
||||
for doc in self.docs:
|
||||
if q in doc["content"] or q in doc["title"]:
|
||||
logger = logging.getLogger("msgbot.kb")
|
||||
logger.debug(f"子串匹配: \"{q}\" → {doc['title']}")
|
||||
return [(doc, min_score)]
|
||||
return results
|
||||
|
||||
def get_doc_count(self) -> int:
|
||||
|
||||
+18
-7
@@ -280,7 +280,7 @@ class MessageMonitor:
|
||||
pass
|
||||
|
||||
def _read_win32_texts(self, platform: str) -> list:
|
||||
"""Win32 API 直接读窗口文本(绕过 UIA 缓存)。"""
|
||||
"""Win32 API 读窗口文本,递归枚举(depth 不限)。"""
|
||||
hwnd = self._cached_hwnd.get(platform)
|
||||
if not hwnd:
|
||||
return []
|
||||
@@ -289,7 +289,7 @@ class MessageMonitor:
|
||||
def enum_callback(child_hwnd, _):
|
||||
try:
|
||||
text = win32gui.GetWindowText(child_hwnd)
|
||||
if text and len(text.strip()) >= 4:
|
||||
if text and len(text.strip()) >= 2:
|
||||
texts.append(text.strip())
|
||||
except Exception:
|
||||
pass
|
||||
@@ -297,7 +297,7 @@ class MessageMonitor:
|
||||
|
||||
try:
|
||||
main_text = win32gui.GetWindowText(hwnd)
|
||||
if main_text and len(main_text.strip()) >= 4:
|
||||
if main_text and len(main_text.strip()) >= 2:
|
||||
texts.append(main_text.strip())
|
||||
win32gui.EnumChildWindows(hwnd, enum_callback, None)
|
||||
except Exception:
|
||||
@@ -308,7 +308,19 @@ class MessageMonitor:
|
||||
"""剪贴板法:聚焦窗口→Ctrl+A→Ctrl+C→读剪贴板(适用 Electron 应用)。
|
||||
|
||||
稳妥可靠但会切焦点+清用户剪贴板(自动恢复原内容)。
|
||||
自动重试 1 次若首次结果为空。
|
||||
"""
|
||||
texts = self._read_via_clipboard_once(platform)
|
||||
if texts:
|
||||
return texts
|
||||
# 重试一次:再次 SetForegroundWindow + SendKeys
|
||||
logger.debug(f"[{platform}] 剪贴板首次空,重试")
|
||||
time.sleep(0.5)
|
||||
texts = self._read_via_clipboard_once(platform)
|
||||
return texts
|
||||
|
||||
def _read_via_clipboard_once(self, platform: str) -> list:
|
||||
"""剪贴板法单次执行。"""
|
||||
hwnd = self._cached_hwnd.get(platform)
|
||||
if not hwnd or not win32gui.IsWindow(hwnd):
|
||||
return []
|
||||
@@ -359,8 +371,6 @@ class MessageMonitor:
|
||||
|
||||
return texts
|
||||
|
||||
# ── 自动回复相关 ──────────────────────────────────────────
|
||||
|
||||
def get_raw_clipboard(self, platform: str) -> str:
|
||||
"""获取最近一次剪贴板读取的原始文本。"""
|
||||
return self._raw_clipboard.get(platform, "")
|
||||
@@ -512,8 +522,9 @@ class MessageMonitor:
|
||||
def _read_messages(self, window, platform: str = "") -> list:
|
||||
"""每轮重新定位 ListControl。"""
|
||||
if platform == "qq":
|
||||
# QQ NT/TIM Electron: UIA 不可遍历,唯一可靠是 SetForegroundWindow + SendKeys(Ctrl+A/C)
|
||||
# 降频到 20s 一次,减少焦点切换次数;切回原前台由 sendkeys 后自动恢复
|
||||
# QQ NT/TIM Electron: UIA 不可遍历,剪贴板法+Win32回退
|
||||
# 先 SendMessage 点击消息区确保焦点位置
|
||||
self._click_message_area(platform)
|
||||
texts = self._read_via_clipboard(platform)
|
||||
if texts:
|
||||
return texts
|
||||
|
||||
Reference in New Issue
Block a user