修复知识库短查询不匹配+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:
youpaishidifu
2026-07-25 13:56:09 +08:00
parent 95b4c8a136
commit cb170d1160
2 changed files with 31 additions and 9 deletions
+13 -2
View File
@@ -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
View File
@@ -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