From c6b6a73970bdd806096e4fb233db177bf1e1b34c Mon Sep 17 00:00:00 2001 From: youpaishidifu Date: Wed, 19 Aug 2026 14:20:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D[=E7=9B=B8=E5=85=B3=E6=A3=80?= =?UTF-8?q?=E6=9F=A5]=EF=BC=9A=E7=BA=AF=E5=81=9C=E7=94=A8=E8=AF=8D?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=A8=A1=E7=B3=8A=E5=AD=90=E4=B8=B2=E5=9B=9E?= =?UTF-8?q?=E9=80=80=EF=BC=8C=E5=AE=B9=E5=BF=8DOCR/=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E5=99=AA=E5=A3=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 问题:查询"关于我们儿"匹配到index.md"关于我们"(匹配度8.66)却未通过内容相关性检查,跳过回复。 - 根因:q_mean为空(关于/我们均为停用词)落入纯停用词分支,整串精确子串匹配"关于我们儿" in full失败即拒,多一个"儿"字符无法容忍。 - 修复:精确匹配失败后增加模糊子串回退——按stripped长度从大到小在full中查找连续片段,命中长度≥max(3, len*0.75)放行。 - 测试:单测4/4(案例+回归),真实KB端到端:index 8.66→True,beibao 2.24/sdf1 0.60→False。 --- auto_reply.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/auto_reply.py b/auto_reply.py index 5842e67..2aac84a 100644 --- a/auto_reply.py +++ b/auto_reply.py @@ -142,7 +142,38 @@ class AutoReply: if len(stripped) >= 2: full = (doc.get("heading", "") + " " + doc.get("title", "") + " " + (doc.get("content", "") or doc.get("body", ""))).lower() - return stripped in full + if stripped in full: + return True + # ⭐ 模糊子串回退(2026-08-19):OCR/输入噪声导致 1~2 字符差异时 + # 仍应放行。例:查询"关于我们儿" vs 文档标题"关于我们"(多一个"儿")。 + # 纯停用词查询(q_mean 为空)整串匹配失败后,降级为 LCS 相似度检查—— + # 只要 stripped 的绝大部分字符能连续出现在 full 中即视为相关。 + # 复用子串回退思路,但此分支无 meaningful token 可依赖,直接查 LCS。 + _win = 0 + _src = stripped + _tgt = full + # LCS 长度 ≥ min(len(_src), len(_tgt)) * 0.75 且 ≥ 3 时放行 + # 用窗口滑动近似 LCS:以 stripped 的连续片段(len 从大到小)在 full 中查找 + for _qlen in range(len(_src), 2, -1): + for _start in range(len(_src) - _qlen + 1): + _phrase = _src[_start:_start + _qlen] + if _phrase in _tgt: + _win = _qlen + break + if _win: + break + if _win >= max(3, int(len(_src) * 0.75)): + logger.debug( + f"纯停用词查询模糊子串回退放行: query={query[:30]!r} " + f"stripped={stripped[:30]!r} win_len={_win}/{len(_src)} " + f"doc_heading={(doc.get('heading') or '')[:30]!r}" + ) + return True + logger.debug( + f"纯停用词查询无共同子串拒绝: query={query[:30]!r} " + f"stripped={stripped[:30]!r} win_len={_win}/{len(_src)} " + f"doc_heading={(doc.get('heading') or '')[:30]!r}" + ) return False _link = re.compile(r'\[([^\]]*)\]\([^\)]+\)')