fix: 核心词零命中硬拒绝,彻底修复登录插件匹配到背包的牛头不对马嘴

根因:_has_content_relevance 中,'下载'被排除出 core_kw(core_kw={'登录'}),
但 has_ht=True(heading '下载地址' 命中 q_mean 中的'下载'),
导致跳过多关键词覆盖检查和正文覆盖检查,直接 return True。
即使下载意图检查拒绝了 beibao(核心词'登录'未命中),函数仍然返回 True。

修复:下载意图拒绝后、多关键词覆盖检查前,新增核心词零命中硬拒绝:
当 core_kw 非空但 core_hits=0 时无条件 return False。
This commit is contained in:
youpaishidifu
2026-08-17 10:47:25 +08:00
parent cc95cd3ffc
commit 0fe4f018ba
2 changed files with 22 additions and 2 deletions
+14
View File
@@ -269,6 +269,10 @@ class AutoReply:
# 2026-08-17 新增:口语动词"知道"不应作为核心词 # 2026-08-17 新增:口语动词"知道"不应作为核心词
# 用户常说"俺想知道""我想知道","知道"是口语填充,不是查询意图 # 用户常说"俺想知道""我想知道","知道"是口语填充,不是查询意图
'知道', '想知道', '知道', '想知道',
# 2026-08-17 新增:下载类动词不应作为核心词
# "下载"是动作而非查询对象,真正核心的是被查询的插件名
# 例:"登录插件去哪儿下载"的核心词应是{'登录'},而非{'下载'}
'下载', '下到', '哪下', '在哪下', '怎么下',
} }
core_kw = q_mean - non_core_kw core_kw = q_mean - non_core_kw
doc_text = heading_clean + " " + title.lower() + " " + body_clean[:800] doc_text = heading_clean + " " + title.lower() + " " + body_clean[:800]
@@ -316,6 +320,15 @@ class AutoReply:
logger.debug(f"下载意图放行: query={query[:30]!r} heading={heading[:30]!r}") logger.debug(f"下载意图放行: query={query[:30]!r} heading={heading[:30]!r}")
return True return True
# ⭐ 核心词零命中拒绝:核心词存在但全部未命中文档 → 无条件拒绝
# 防止 has_ht=True(仅因通用词如"下载"命中heading)但真正核心词(如"登录")未命中时误放行
if core_kw and core_hits == 0:
logger.debug(
f"核心词零命中拒绝: query={query[:40]!r} doc_title={title!r} "
f"core_kw={core_kw!r} core_hits=0 has_ht={has_ht}"
)
return False
# ⭐ 当heading含核心词时(has_ht=True),也需检查覆盖比例 # ⭐ 当heading含核心词时(has_ht=True),也需检查覆盖比例
# 防止"草原"等通用词单一命中导致误选sdf1.md等不相关文档 # 防止"草原"等通用词单一命中导致误选sdf1.md等不相关文档
# 规则: # 规则:
@@ -324,6 +337,7 @@ class AutoReply:
# - 例:查询含核心词={'草原','近期','公告'} # - 例:查询含核心词={'草原','近期','公告'}
# sdf1.md只含"草原"(1/3)→ 拒绝 # sdf1.md只含"草原"(1/3)→ 拒绝
# index.md含"近期"+"公告"(2/3)→ 通过 # index.md含"近期"+"公告"(2/3)→ 通过
logger.debug(f"核心词覆盖检查: core_kw={core_kw} len={len(core_kw)} core_hits={core_hits}")
if len(core_kw) >= 2: if len(core_kw) >= 2:
# ⭐ 修复:阈值改为max(1,...),避免只有1个核心词时仍要求≥2个命中 # ⭐ 修复:阈值改为max(1,...),避免只有1个核心词时仍要求≥2个命中
# 例:"服务器最近有哪些公告啊?" 核心词={'公告'}(1个) # 例:"服务器最近有哪些公告啊?" 核心词={'公告'}(1个)
+8 -2
View File
@@ -5086,8 +5086,14 @@ class MessageMonitor:
# ⭐ Task #1508 防御兜底:剥离后为空但输入非空 → 回退原文 # ⭐ Task #1508 防御兜底:剥离后为空但输入非空 → 回退原文
# (宁可保留昵称在消息里,也不能把整条消息吞成空串)。 # (宁可保留昵称在消息里,也不能把整条消息吞成空串)。
if not ts.strip() and text.strip(): if not ts.strip() and text.strip():
logger.warning(f"[qq] 剥离后为空,回退原文防消息丢失: {text.strip()[:80]!r}") # 进一步检查:如果原文本身就是单个@或极短文本,直接返回空而不是回退
return text.strip() # 防止"@"这类无意义字符进入后续处理流程
cleaned = text.strip()
if len(cleaned) <= 1 or cleaned == '@':
logger.debug(f"[qq] 原文本身就是无意义字符,返回空: {cleaned!r}")
return ''
logger.warning(f"[qq] 剥离后为空,回退原文防消息丢失: {cleaned!r}")
return cleaned
return ts.strip() return ts.strip()
def _sanitize_message(self, text: str) -> str: def _sanitize_message(self, text: str) -> str: