fix: 修复消息拼接/丢弃问题——同轮择优改为逐条处理所有候选消息 (Task #1642)

- 旧逻辑:_candidates列表按质量分排序,只选1条最好的处理,其余全部丢弃
- 新逻辑:for循环逐条处理所有候选消息,每条独立查重、入库、KB搜索和回复
- 修复用户报告:OCR一次读到4条消息后只有1条被处理,其余3条丢失
- 用户原话:不要把消息都拼接成一条,不然一个拦截剩余的全部丢了
This commit is contained in:
youpaishidifu
2026-08-30 18:46:26 +08:00
parent 3bb8c50969
commit bbc00f2c8b
+57 -61
View File
@@ -10135,74 +10135,70 @@ class MessageMonitor:
_diff = 86400 # 无时间戳,视为很旧
_candidates.append((score, item, cleaned_item, _diff))
# ⭐ 同轮择优:只选质量分最高的那条消息触发回复
# ⭐ 同轮逐条处理:每条候选消息独立处理和回复,不再丢弃
if _candidates:
# 按质量分降序排序,再按时间戳差值升序(越新鲜越好)
_candidates.sort(key=lambda x: (x[0], -x[3]), reverse=True)
_best_score, _best_raw, _best_cleaned, _best_diff = _candidates[0]
# 如果有多条同分,优先选时间戳最新的(差值最小)
_top_candidates = [c for c in _candidates if c[0] == _best_score]
_top_candidates.sort(key=lambda x: x[3]) # 按时间戳差值升序
_best_score, _best_raw, _best_cleaned, _best_diff = _top_candidates[0]
logger.info(
f"[{platform}] 同轮择优: 选中({_best_score:.1f}分) {_best_cleaned[:50]!r}, "
f"候选{len(_candidates)}条"
f"[{platform}] 同轮候选{len(_candidates)}条,逐条处理"
)
if len(_candidates) > 1:
for _sc, _raw, _cl, _diff in _candidates[1:]:
logger.debug(f"[{platform}] 同轮择优: 丢弃({_sc:.1f}分) {_cl[:50]!r}")
# ⭐ 添加到最近处理列表
self._recent_processed_messages.append((_best_cleaned, now))
# ⭐ 添加到最近用户查询列表(用于跨轮语义去重)
self._add_to_recent_user_queries(_best_cleaned)
# ⭐ Task #1556: 记录关键词得分到数据库
if self._keyword_scorer:
try:
keywords = self._keyword_scorer.extract_keywords(_best_cleaned)
# 新消息得分为0(未匹配历史回复),记录为已处理
self._keyword_scorer.record_message_score(
platform, _best_cleaned, keywords, 0, [], is_new=True
)
except Exception as e:
logger.debug(f"[{platform}] 关键词记录失败: {e}")
self._last_new_msg_time[platform] = time.time()
self._last_msg[platform] = _best_cleaned
engine_tag = self._last_chosen_engine.get(platform, "")
logger.info(f"[{platform}] 新消息[{engine_tag}]: {_best_raw[:60]}")
# ⭐ Task #1572: 记录OCR消息到暂存表(用于后续查重)
if self._msg_history:
# ⭐ 先进行数据库查重:清理后计算相似度,重复率≥50%就跳过入库和回复
if self._msg_history._is_similar_to_history(platform, _best_cleaned, threshold=0.5):
logger.info(f"[{platform}] 轮询消息去重: 与历史消息相似,跳过入库和回复: {_best_cleaned[:60]!r}")
# 虽然不入库,但仍需标记为已处理,防止重复处理
self._last_new_msg_time[platform] = time.time()
# 不回复,直接返回
for _idx, (_score, _raw, _cleaned, _diff) in enumerate(_candidates):
# ⚠️ 急停检查:处理每条消息前检查
if self._stop_event.is_set() or not self._running:
return
try:
self._msg_history.record_ocr_message(platform, _best_cleaned)
except Exception as e:
logger.debug(f"[{platform}] OCR暂存记录失败: {e}")
# ⚠️ 急停检查:回复前最后检查,防止急停后仍然发送
if self._stop_event.is_set() or not self._running:
return
if self._on_message:
self._on_message(platform, _best_cleaned)
if platform == "qq":
if stale == 3:
logger.debug(f"[QQ] 冻结 #{stale},强制刷新")
self._force_uia_refresh(platform)
elif stale == 6:
logger.debug(f"[QQ] 冻结 #{stale},重建窗口引用")
self._cached_hwnd.pop(platform, None)
self._cached_pid.pop(platform, None)
else:
if stale == 5:
logger.debug(f"[{platform}] 冻结 #{stale},尝试强制刷新")
self._force_uia_refresh(platform)
elif stale == 10:
logger.debug(f"[{platform}] 冻结 #{stale},重建窗口引用")
logger.info(
f"[{platform}] 处理第{_idx+1}/{len(_candidates)}条({_score:.1f}分): {_cleaned[:50]!r}"
)
# ⭐ 添加到最近处理列表
self._recent_processed_messages.append((_cleaned, now))
# ⭐ 添加到最近用户查询列表(用于跨轮语义去重)
self._add_to_recent_user_queries(_cleaned)
# ⭐ Task #1556: 记录关键词得分到数据库
if self._keyword_scorer:
try:
keywords = self._keyword_scorer.extract_keywords(_cleaned)
self._keyword_scorer.record_message_score(
platform, _cleaned, keywords, 0, [], is_new=True
)
except Exception as e:
logger.debug(f"[{platform}] 关键词记录失败: {e}")
self._last_new_msg_time[platform] = time.time()
self._last_msg[platform] = _cleaned
engine_tag = self._last_chosen_engine.get(platform, "")
logger.info(f"[{platform}] 新消息[{engine_tag}]: {_raw[:60]}")
# ⭐ Task #1572: 记录OCR消息到暂存表(用于后续查重)
if self._msg_history:
# ⭐ 数据库查重:重复率≥50%就跳过该条消息(continue继续处理下一条)
if self._msg_history._is_similar_to_history(platform, _cleaned, threshold=0.5):
logger.info(f"[{platform}] 轮询消息去重: 与历史消息相似,跳过: {_cleaned[:60]!r}")
self._last_new_msg_time[platform] = time.time()
continue # 跳过该条,继续处理下一条
try:
self._msg_history.record_ocr_message(platform, _cleaned)
except Exception as e:
logger.debug(f"[{platform}] OCR暂存记录失败: {e}")
# ⚠️ 急停检查:回复前最后检查,防止急停后仍然发送
if self._stop_event.is_set() or not self._running:
return
if self._on_message:
self._on_message(platform, _cleaned)
if platform == "qq":
if stale == 3:
logger.debug(f"[QQ] 冻结 #{stale},强制刷新")
self._force_uia_refresh(platform)
elif stale == 6:
logger.debug(f"[QQ] 冻结 #{stale},重建窗口引用")
self._cached_hwnd.pop(platform, None)
self._cached_pid.pop(platform, None)
else:
if stale == 5:
logger.debug(f"[{platform}] 冻结 #{stale},尝试强制刷新")
self._force_uia_refresh(platform)
elif stale == 10:
logger.debug(f"[{platform}] 冻结 #{stale},重建窗口引用")
self._cached_hwnd.pop(platform, None)
self._cached_pid.pop(platform, None)