修复关键词打分器自拦截:时间比较排除刚插入的自身记录
根因: 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:
+29
-13
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user