修复bot多段回复与表格全文回调:强化相关性+表格只取上下文

- Fix A: auto_reply._get_answer 含表格正文不再返回整段section,改为
  提取表格+紧邻上文(最多2行),丢弃表格后无关图文,截断到800字符
- Fix A: 清理答案中的HTML <br> 标签,避免原形显示
- Fix B: 强化 _has_content_relevance,要求查询与文档共享有区分度核心词
  (去停用词),动作类查询额外要求正文命中,防牛头不对马嘴
- Fix B: gui._search_and_log 从 top_k 结果中选取首个通过相关性校验的文档
  发送,不再永远取 results[0],保证一条消息只回复一段精准内容
This commit is contained in:
youpaishidifu
2026-08-10 17:59:18 +08:00
parent 18347a9f7d
commit 9ab2da404f
2 changed files with 153 additions and 102 deletions
+29 -22
View File
@@ -940,8 +940,6 @@ class MsgBotApp(ctk.CTk):
lines = [f"[{platform.upper()}] {msg}"]
if results:
answer_title = results[0][0]["title"]
answer_score = results[0][1]
lines.append(f" 🔍 找到 {len(results)} 条相关结果:")
for i, (doc, score) in enumerate(results):
@@ -963,32 +961,41 @@ class MsgBotApp(ctk.CTk):
if i == 0:
lines.append(f" 完整内容片段: {content[:200].replace(chr(10), ' ')}...")
# 诊断日志:记录自动回复触发条件
# 从 top_k 结果中选取【第一个通过内容相关性校验】的文档发送,
# 避免永远只取 results[0] 导致"牛头不对马嘴"的错误文档被发送。
# 一条消息只回复一段(选中最相关且语义匹配的那一条),不触发 N 段回复。
chosen = None
if self.ar.enabled:
for idx, (doc, sc) in enumerate(results):
if sc < self.ar.min_score:
continue
rel = self.ar._has_content_relevance(cleaned_msg, doc, sc)
logger.debug(
f"[{platform}] 相关性候选[{idx}]: title={doc.get('title', '')!r} "
f"score={sc:.2f} relevance={rel}"
)
if rel:
chosen = (doc, sc)
break
if not self.ar.enabled:
lines.append(" ⚠ 自动回复已关闭,不触发回复")
elif answer_score < self.ar.min_score:
lines.append(f" ⚠ 匹配度 {answer_score:.2f} < 阈值 {self.ar.min_score},跳过")
elif chosen is None:
lines.append(" ⚠ 所有结果均未通过内容相关性/阈值检查,跳过回复")
else:
# 检查内容相关性
has_relevance = self.ar._has_content_relevance(msg, results[0][0], answer_score)
# 详细调试日志:记录内容相关性检查结果
logger.debug(f"[{platform}] 内容相关性检查: msg={msg!r}, has_relevance={has_relevance}")
logger.debug(f"[{platform}] 内容相关性检查: doc标题={results[0][0].get('title', '')!r}")
logger.debug(f"[{platform}] 内容相关性检查: doc heading={results[0][0].get('heading', '')[:50]!r}")
if not has_relevance:
heading = results[0][0].get("heading", "")[:40]
lines.append(f" ⚠ 内容相关性检查失败 (heading={heading!r}),跳过")
else:
lines.append(" ✅ 匹配通过,准备发送自动回复")
lines.append(" ✅ 匹配通过,准备发送自动回复")
# 自动回复:队列到主线程执行(daemon 线程不能可靠 SetForegroundWindow)
reply_data = self.ar.get_reply_data(platform, msg, results[0][0], results[0][1])
if reply_data:
self._reply_queue.put(reply_data)
if chosen is not None:
reply_data = self.ar.get_reply_data(
platform, msg, chosen[0], chosen[1], cleaned_msg=cleaned_msg
)
if reply_data:
self._reply_queue.put(reply_data)
else:
lines.append(" ⚠ get_reply_data 返回 None(重复命中或内部检查失败)")
else:
lines.append(" ⚠ get_reply_data 返回 None(重复命中或内部检查失败)")
reply_data = None
else:
lines.append(" ❌ 知识库中未找到相关答案")