修复关键词打分器自拦截:时间比较排除刚插入的自身记录

根因:
should_skip_message 先 record_message_score 落库(时间=now),
再 _should_skip_by_time 按 message_hash 反查最后一条 → 命中的是
刚插入的自己 → db_time=now → '当前<=DB+5分' 永远拦截。
新消息首次出现即被拦,5分钟窗口永远走不完。

例:'那服务器有哪些基本信息,L ~' score=2(命中共用词'服务'/'务器')
时间比较命中自己 id=54(14:40:15) → 拦截,消息丢失。

修复:
record_message_score 返回 lastrowid,
_should_skip_by_time 增加 exclude_id 参数,查询排除自身。

验证:
- 新消息首次出现 → 放行(不再自拦截)
- 同文本5分内重现 → 拦截(去重仍生效)
- 同文本5分后重现 → 放行(时间超窗)
- 真实库下游测3/3 全部符合预期
This commit is contained in:
youpaishidifu
2026-08-19 14:50:23 +08:00
parent c6b6a73970
commit fd147862c7
+29 -13
View File
@@ -174,7 +174,7 @@ class KeywordScorer:
conn = sqlite3.connect(self.db_path, check_same_thread=False)
try:
conn.execute("""
cursor = conn.execute("""
INSERT INTO keyword_scores
(platform, message_text, message_hash, score, matched_keywords, local_time, is_new_message)
VALUES (?, ?, ?, ?, ?, ?, ?)
@@ -183,6 +183,7 @@ class KeywordScorer:
conn.commit()
logger.debug(f"[keyword-scorer] 记录得分: platform={platform}, score={score}, "
f"keywords={matched_keywords[:5]}, is_new={is_new}")
return cursor.lastrowid
finally:
conn.close()
@@ -220,14 +221,16 @@ class KeywordScorer:
score, matched = self.score_against_history(keywords, platform)
# 记录本次得分(先记录用于后续检查)
self.record_message_score(platform, text, keywords, score, matched, is_new=True)
rec_id = self.record_message_score(platform, text, keywords, score, matched, is_new=True)
# 得分≤1:直接放行(新消息)
if score <= 1:
return False, score, matched
# 得分≥1:时间比较逻辑
should_skip = self._should_skip_by_time(text, platform, score, matched, ocr_time)
# ⭐ 排除刚插入的自身记录:否则同 hash 反查命中自己 → db_time=now → 永远拦截
should_skip = self._should_skip_by_time(text, platform, score, matched, ocr_time,
exclude_id=rec_id)
if should_skip:
logger.info(f"[keyword-scorer] 跳过消息(得分{score},重复): {text[:40]}... "
@@ -239,7 +242,7 @@ class KeywordScorer:
return should_skip, score, matched
def _should_skip_by_time(self, text: str, platform: str, score: int, matched: List[str],
ocr_time: Optional[str]) -> bool:
ocr_time: Optional[str], exclude_id: Optional[int] = None) -> bool:
"""时间比较逻辑:判断是否拦截重复消息
规则:
@@ -249,23 +252,36 @@ class KeywordScorer:
2. 判断:
- 当前时间 > DB落库时间 + 5分钟 → 放行
- 否则 → 拦截
exclude_id: 排除刚插入的当前消息记录id(防止自拦截)
"""
import hashlib
from datetime import timedelta
# 获取DB落库时间(同平台、同hash的最后一条记录)
# 获取DB落库时间(同平台、同hash的最后一条记录,排除自身)
message_hash = hashlib.md5(text.strip().encode('utf-8')).hexdigest()
conn = sqlite3.connect(self.db_path, check_same_thread=False)
try:
# 查询同hash的最新记录
cursor = conn.execute("""
SELECT local_time, score
FROM keyword_scores
WHERE platform = ?
AND message_hash = ?
ORDER BY id DESC
LIMIT 1
""", (platform, message_hash))
if exclude_id is not None:
cursor = conn.execute("""
SELECT local_time, score
FROM keyword_scores
WHERE platform = ?
AND message_hash = ?
AND id != ?
ORDER BY id DESC
LIMIT 1
""", (platform, message_hash, exclude_id))
else:
cursor = conn.execute("""
SELECT local_time, score
FROM keyword_scores
WHERE platform = ?
AND message_hash = ?
ORDER BY id DESC
LIMIT 1
""", (platform, message_hash))
row = cursor.fetchone()
if not row: