fix(monitor): 策略4改为贪婪匹配,不管bot回复在哪都摘掉
原策略4是头部剥离(只处理bot回复在消息前面的情况), 用户反馈:OCR不确定哪天会把小帽子扣在哪,需要用贪婪匹配兜底。 新策略4: 1. 归一化文本(去空格标点),抵抗OCR噪声 2. 遍历已知bot回复,检查是否包含在文本中 3. 如果包含,定位并移除匹配部分 4. 保留剩余的用户消息 覆盖场景: - 头部:'官方下载 bbsmc分流 那我知道公告信息' → '那我知道公告信息' - 尾部:'那个登录插件在哪 见github' → '那个登录插件在哪' - 中部:'你好 [√] GitHub(稳定版) 请问怎么下载' → '你好 请问怎么下载' commit a3f7c2e (2026-08-16)
This commit is contained in:
+51
-37
@@ -4794,44 +4794,58 @@ class MessageMonitor:
|
||||
)
|
||||
stripped_bot = True
|
||||
break
|
||||
# 策略4:头部剥离——OCR把bot回复拼在用户消息前面("小帽子"问题)
|
||||
# 例:"官方下载 bbsmc分流 那我知道咱们服务器有哪些公告信息呀?"
|
||||
# → "那我知道咱们服务器有哪些公告信息呀?"
|
||||
# 修复:定位中文疑问词(那/请问/想知道等),剥离问句前的bot回复头部
|
||||
# 策略4:贪婪匹配剥离——不管bot回复拼在哪(头/尾/中),都摘掉
|
||||
# 例:
|
||||
# "官方下载 bbsmc分流 那我知道公告信息" → "那我知道公告信息"
|
||||
# "问问那个登录插件在哪儿能下载 见github" → "问问那个登录插件在哪儿能下载"
|
||||
# "[√] GitHub(稳定版) 那个登录插件在哪?" → "那个登录插件在哪?"
|
||||
if not stripped_bot:
|
||||
# 常见中文疑问词/短语,用于定位用户消息的起始位置
|
||||
_question_markers = [
|
||||
'那我知道', '那请问', '请问', '想知道', '想问',
|
||||
'怎么', '哪里', '什么', '哪些', '哪个',
|
||||
]
|
||||
_q_start = len(ts)
|
||||
for _marker in _question_markers:
|
||||
_pos = ts.find(_marker)
|
||||
if _pos >= 0 and _pos < _q_start:
|
||||
_q_start = _pos
|
||||
# 如果没找到疑问词,回退到找问号
|
||||
if _q_start == len(ts):
|
||||
_q_mark = max(ts.find('?'), ts.find('?'))
|
||||
if _q_mark >= 0:
|
||||
_q_start = _q_mark
|
||||
|
||||
_head = ts[:_q_start].strip()
|
||||
_tail = ts[_q_start:]
|
||||
if _head and len(_head) >= 3 and _tail:
|
||||
# 检查头部是否匹配已知的bot回复
|
||||
for _r in replied:
|
||||
if len(_r) < 5:
|
||||
continue
|
||||
_head_norm = re.sub(r'[\s\W_]+', '', _head)
|
||||
_r_norm = re.sub(r'[\s\W_]+', '', _r)
|
||||
if _head_norm and _r_norm and (_head_norm == _r_norm or _head_norm in _r_norm):
|
||||
ts = _tail
|
||||
stripped_bot = True
|
||||
logger.debug(
|
||||
f"[qq] 头部匹配剥离bot回复: {ts!r} <- "
|
||||
f"{_head!r} (匹配bot reply: {_r[:30]!r})"
|
||||
)
|
||||
break
|
||||
for _r in replied:
|
||||
if len(_r) < 5:
|
||||
continue
|
||||
# 归一化比较:去空格标点,抵抗OCR噪声
|
||||
_r_norm = self._norm_frag(_r)
|
||||
if not _r_norm:
|
||||
continue
|
||||
_ts_norm = self._norm_frag(ts)
|
||||
if not _ts_norm:
|
||||
continue
|
||||
# 贪婪匹配:如果文本包含bot回复,移除匹配部分
|
||||
if _r_norm in _ts_norm:
|
||||
# 定位匹配位置
|
||||
_r_idx = _ts_norm.find(_r_norm)
|
||||
if _r_idx >= 0:
|
||||
# 计算原始文本中的位置(考虑归一化可能改变字符数)
|
||||
# 使用简化的近似定位:从开头跳过非匹配部分
|
||||
_skip_count = 0
|
||||
_norm_count = 0
|
||||
for _i, _c in enumerate(ts):
|
||||
if _norm_count == _r_idx:
|
||||
_skip_count = _i
|
||||
break
|
||||
if re.sub(r'[\s\W_]+', '', _c):
|
||||
_norm_count += 1
|
||||
# 计算bot回复的归一化长度
|
||||
_r_norm_len = len(_r_norm)
|
||||
_end_norm = _r_idx + _r_norm_len
|
||||
_end_count = 0
|
||||
_end_pos = len(ts)
|
||||
for _i, _c in enumerate(ts[_skip_count:]):
|
||||
if re.sub(r'[\s\W_]+', '', _c):
|
||||
_end_count += 1
|
||||
if _end_count == _r_norm_len:
|
||||
_end_pos = _skip_count + _i + 1
|
||||
break
|
||||
# 移除匹配的bot回复部分
|
||||
_cleaned = ts[:_skip_count] + ts[_end_pos:]
|
||||
if _cleaned.strip():
|
||||
logger.debug(
|
||||
f"[qq] 贪婪匹配剥离bot回复: {ts!r} -> { _cleaned!r} "
|
||||
f"(移除: {ts[_skip_count:_end_pos]!r})"
|
||||
)
|
||||
ts = _cleaned.strip()
|
||||
stripped_bot = True
|
||||
break
|
||||
if stripped_bot:
|
||||
ts = re.sub(r'\s+', ' ', ts).strip().strip(',').strip()
|
||||
return ts
|
||||
|
||||
Reference in New Issue
Block a user