修复关键词打分器:时间比较逻辑 + OCR时间源选择
1. 修复 _should_skip_by_time 方法调用参数缺失 text 的问题 2. 实现三源时间比较逻辑: - DB落库时间:历史记录的最后处理时间 - OCR时间:从消息文本提取的时间戳(如'22:54:16') - 系统时间:当前机器时间 3. 时间源选择规则: - |OCR时间 - 系统时间| ≤ 10秒 → 使用OCR时间(更准确) - 否则 → 使用系统时间 4. 重复判断规则: - 当前时间 > DB落库时间 + 5分钟 → 放行(新消息) - 否则 → 拦截(重复消息) 5. monitor.py 前置 OCR 时间戳提取,传递给打分器 6. 修复 threshold 默认值从 2 改为 1,确保 ≥1 分进入时间比较 参考:Task #1558, #1559
This commit is contained in:
+77
-31
@@ -187,20 +187,28 @@ class KeywordScorer:
|
||||
conn.close()
|
||||
|
||||
def should_skip_message(self, text: str, platform: str,
|
||||
ocr_time: Optional[str] = None,
|
||||
threshold: int = 2) -> Tuple[bool, int, List[str]]:
|
||||
"""判断是否应该跳过该消息(基于关键词得分 + 时间感知)
|
||||
|
||||
⭐ Task #1558: 新逻辑
|
||||
- 得分≤1 → 直接放行(新消息)
|
||||
- 得分≥2 → 检查最近一次打分时间:
|
||||
* 最近打分在5分钟内 → 丢弃(同一用户重复)
|
||||
* 最近打分超过5分钟 → 放行(不同用户的新提问)
|
||||
这样避免一刀切≥2分就丢弃,因为不同用户可能提出相同问题。
|
||||
流程:
|
||||
1. 得分≤1 → 直接放行(新消息)
|
||||
2. 得分≥1 → 比较三个时间源:
|
||||
- DB落库时间(历史最后一次处理时间)
|
||||
- OCR识别时间(来自时间戳)
|
||||
- 系统当前时间
|
||||
3. 时间源选择:
|
||||
- 若OCR时间与系统时间差 ≤ ±10秒 → 用OCR时间(OCR时间更准)
|
||||
- 否则 → 用系统时间
|
||||
4. 判断:
|
||||
- 若当前时间 > DB落库时间 + 5分钟 → 放行(5分钟前已处理过)
|
||||
- 否则 → 拦截(重复消息)
|
||||
|
||||
Args:
|
||||
text: 消息文本
|
||||
platform: 平台标识
|
||||
threshold: 得分阈值,≥threshold 则检查时间
|
||||
ocr_time: OCR识别到的时间戳(如'22:54:16'),可选
|
||||
threshold: 得分阈值
|
||||
|
||||
Returns:
|
||||
(是否跳过, 得分, 匹配关键词)
|
||||
@@ -218,47 +226,85 @@ class KeywordScorer:
|
||||
if score <= 1:
|
||||
return False, score, matched
|
||||
|
||||
# 得分≥2:检查最近一次同类消息的打分时间
|
||||
should_skip = self._should_skip_by_recent_score(platform, score, matched)
|
||||
# 得分≥1:时间比较逻辑
|
||||
should_skip = self._should_skip_by_time(text, platform, score, matched, ocr_time)
|
||||
|
||||
if should_skip:
|
||||
logger.info(f"[keyword-scorer] 跳过消息(得分{score}≥{threshold},5分钟内): {text[:40]}... "
|
||||
logger.info(f"[keyword-scorer] 跳过消息(得分{score},重复): {text[:40]}... "
|
||||
f"匹配关键词: {matched[:5]}")
|
||||
else:
|
||||
logger.info(f"[keyword-scorer] 放行消息(得分{score}≥{threshold},超过5分钟或无记录): "
|
||||
f"{text[:40]}... 匹配关键词: {matched[:5]}")
|
||||
logger.info(f"[keyword-scorer] 放行消息(得分{score},时间超时): {text[:40]}... "
|
||||
f"匹配关键词: {matched[:5]}")
|
||||
|
||||
return should_skip, score, matched
|
||||
|
||||
def _should_skip_by_recent_score(self, platform: str, score: int, matched: List[str]) -> bool:
|
||||
"""检查最近一次同类消息的打分时间,超过5分钟则放行"""
|
||||
def _should_skip_by_time(self, text: str, platform: str, score: int, matched: List[str],
|
||||
ocr_time: Optional[str]) -> bool:
|
||||
"""时间比较逻辑:判断是否拦截重复消息
|
||||
|
||||
规则:
|
||||
1. 时间源选择:
|
||||
- 若OCR时间与系统时间差 ≤ ±10秒 → 用OCR时间(OCR时间更准)
|
||||
- 否则 → 用系统时间
|
||||
2. 判断:
|
||||
- 当前时间 > DB落库时间 + 5分钟 → 放行
|
||||
- 否则 → 拦截
|
||||
"""
|
||||
import hashlib
|
||||
from datetime import timedelta
|
||||
|
||||
# 获取DB落库时间(同平台、同hash的最后一条记录)
|
||||
message_hash = hashlib.md5(text.strip().encode('utf-8')).hexdigest()
|
||||
conn = sqlite3.connect(self.db_path, check_same_thread=False)
|
||||
try:
|
||||
from datetime import timedelta
|
||||
cutoff_time = (datetime.now() - timedelta(seconds=300)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# 查询最近300秒内得分≥1的消息(不限score阈值,只看时间)
|
||||
# 查询同hash的最新记录
|
||||
cursor = conn.execute("""
|
||||
SELECT local_time, score
|
||||
FROM keyword_scores
|
||||
WHERE platform = ?
|
||||
AND score >= 1
|
||||
AND matched_keywords != ''
|
||||
AND local_time > ?
|
||||
AND message_hash = ?
|
||||
ORDER BY id DESC
|
||||
LIMIT 3
|
||||
""", (platform, cutoff_time))
|
||||
LIMIT 1
|
||||
""", (platform, message_hash))
|
||||
row = cursor.fetchone()
|
||||
|
||||
recent = cursor.fetchall()
|
||||
if not recent:
|
||||
# 无近期记录 → 放行(可能是新用户的相同提问)
|
||||
logger.debug(f"[keyword-scorer] 无近期记录,放行: score={score}, matched={matched[:3]}")
|
||||
if not row:
|
||||
# 无历史同hash记录 → 放行
|
||||
logger.debug(f"[keyword-scorer] 无历史同hash记录,放行: hash={message_hash[:8]}")
|
||||
return False
|
||||
|
||||
# 有近期记录 → 丢弃(同一用户重复提问)
|
||||
logger.debug(f"[keyword-scorer] 发现近期记录,跳过: count={len(recent)}, "
|
||||
f"最近时间={recent[0][0]}, score={recent[0][1]}")
|
||||
return True
|
||||
db_time_str = row[0]
|
||||
db_time = datetime.strptime(db_time_str, "%Y-%m-%d %H:%M:%S")
|
||||
logger.debug(f"[keyword-scorer] DB落库时间: {db_time_str}, score={row[1]}")
|
||||
|
||||
# 确定当前时间(OCR时间 vs 系统时间)
|
||||
now = datetime.now()
|
||||
current_time = now
|
||||
|
||||
if ocr_time:
|
||||
# 解析OCR时间(格式如'22:54:16')
|
||||
try:
|
||||
ocr_dt = datetime.strptime(ocr_time, "%H:%M:%S")
|
||||
# 同一天
|
||||
ocr_dt = ocr_dt.replace(year=now.year, month=now.month, day=now.day)
|
||||
# 时间差检查
|
||||
time_diff = abs((now - ocr_dt).total_seconds())
|
||||
if time_diff <= 10:
|
||||
current_time = ocr_dt
|
||||
logger.debug(f"[keyword-scorer] 使用OCR时间(差值{time_diff:.0f}秒≤10秒)")
|
||||
else:
|
||||
logger.debug(f"[keyword-scorer] OCR时间差值{time_diff:.0f}秒>10秒,使用系统时间")
|
||||
except ValueError:
|
||||
logger.debug(f"[keyword-scorer] OCR时间解析失败'{ocr_time}',使用系统时间")
|
||||
|
||||
# 判断:当前时间 > DB落库时间 + 5分钟 → 放行
|
||||
cutoff_time = db_time + timedelta(minutes=5)
|
||||
if current_time > cutoff_time:
|
||||
logger.debug(f"[keyword-scorer] 当前时间{current_time.strftime('%H:%M:%S')} > DB+5分{cutoff_time.strftime('%H:%M:%S')},放行")
|
||||
return False
|
||||
else:
|
||||
logger.debug(f"[keyword-scorer] 当前时间{current_time.strftime('%H:%M:%S')} <= DB+5分{cutoff_time.strftime('%H:%M:%S')},拦截")
|
||||
return True
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
Reference in New Issue
Block a user