From e9d9551e3d955d8d79e862ffd66f1a541556d353 Mon Sep 17 00:00:00 2001 From: youpaishidifu Date: Sun, 26 Jul 2026 13:38:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D2=E4=B8=AA=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=9A=E9=80=80=E5=9B=9E=E8=A3=81=E5=89=AA+=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E5=BA=93=E5=8F=AA=E5=9B=9E=E5=A4=8D=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E5=8F=A5\n\n1.=20=E8=A3=81=E5=89=AA=E9=80=80=E5=9B=9Ec9acd7e?= =?UTF-8?q?=E7=89=88(cap=5Fleft=3D0.25)\n=20=20=200.35=E5=8F=AA=E6=88=AA?= =?UTF-8?q?=E5=88=B0=E6=97=B6=E9=97=B4=E6=88=B3=EF=BC=8C=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E8=A2=AB=E5=88=87;0.25=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=96=87=E5=AD=97=E5=8C=BA=E5=9F=9F\n\n2.=20?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=E8=BF=94=E5=9B=9E=E5=85=A8=E6=96=87?= =?UTF-8?q?=E2=86=92=E5=8F=AA=E8=BF=94=E5=9B=9E=E5=8C=B9=E9=85=8D=E9=97=AE?= =?UTF-8?q?=E7=AD=94=E6=AE=B5\n=20=20=20=E5=8E=9F=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=9A=E5=90=8D=E5=AD=97.txt=E5=90=AB2=E4=B8=AAQ&A=EF=BC=8CT?= =?UTF-8?q?F-IDF=E6=95=B4=E6=96=87=E4=BB=B6=E5=8C=B9=E9=85=8D=E5=90=8E?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=85=A8=E6=96=87\n=20=20=20=E8=AF=A5?= =?UTF-8?q?=E6=96=B9=E6=A1=88=EF=BC=9Aknowledge=5Fbase.=5Fparse=5Fqa=5Fseg?= =?UTF-8?q?ments=E8=A7=A3=E6=9E=90=E6=96=87=E4=BB=B6=E4=B8=BA=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E9=97=AE=E7=AD=94=E6=AE=B5\n=20=20=20=E6=AF=8F?= =?UTF-8?q?=E6=AE=B5=E4=BD=9C=E4=B8=BA=E7=8B=AC=E7=AB=8Bdoc=EF=BC=8Cauto?= =?UTF-8?q?=5Freply.=5Fget=5Fanswer=E4=BC=98=E5=85=88=E8=BF=94=E5=9B=9Edoc?= =?UTF-8?q?.answer\n=20=20=20=E7=94=A8=E6=88=B7=E9=97=AE=E4=B8=80=E5=8F=A5?= =?UTF-8?q?=E8=AF=9D=EF=BC=8C=E5=8F=AA=E5=9B=9E=E5=A4=8D=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=AD=94=E5=8F=A5=EF=BC=8C=E4=B8=8D=E5=A1=9E=E5=85=A8=E9=83=A8?= =?UTF-8?q?=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- auto_reply.py | 6 +++-- knowledge_base.py | 67 +++++++++++++++++++++++++++++++++++++++++++---- monitor.py | 6 ++--- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/auto_reply.py b/auto_reply.py index 4516ced..a9977eb 100644 --- a/auto_reply.py +++ b/auto_reply.py @@ -65,8 +65,10 @@ class AutoReply: return {"platform": platform, "answer": answer, "sender": sender} def _get_answer(self, platform: str, msg: str, doc: dict, score: float) -> str or None: - """提取答案 + 防重复检查。""" - answer = doc["content"].strip() + """提取答案 + 防重复检查。 + 优先返回 doc['answer'](Q&A 段),兜底用整段 content。 + """ + answer = (doc.get("answer") or doc["content"]).strip() if not answer: return None diff --git a/knowledge_base.py b/knowledge_base.py index f7a61bc..d9d5f78 100644 --- a/knowledge_base.py +++ b/knowledge_base.py @@ -27,6 +27,50 @@ class KnowledgeBase: """jieba 分词""" return list(jieba.cut(text)) + @staticmethod + def _parse_qa_segments(content: str) -> list: + """解析 Q&A 格式文件为独立问答段。 + 格式:问题[N]:xxx\\n答:xxx + 返回 [{"question": str, "answer": str, "raw": str}] + """ + segments = [] + cur_q = [] + cur_a = [] + state = None # None, 'q', 'a' + + def flush(): + if not cur_q and not cur_a: + return + q = '\n'.join(cur_q).strip() + a = '\n'.join(cur_a).strip() + if q or a: + segments.append({ + "question": q, + "answer": a, + "raw": f"问题:{q}\n答:{a}", + }) + cur_q.clear() + cur_a.clear() + + for line in content.split('\n'): + qm = re.match(r'^问题\d*\s*[::]\s*(.*)', line) + am = re.match(r'^答\s*[::]\s*(.*)', line) + if qm: + flush() + state = 'q' + cur_q.append(qm.group(1)) + elif am: + state = 'a' + cur_a.append(am.group(1)) + elif state == 'q': + cur_q.append(line) + elif state == 'a': + cur_a.append(line) + # else: 文件头空白/注释,忽略 + + flush() + return segments + def set_path(self, path: str): self.root_path = path self.reload() @@ -45,11 +89,24 @@ class KnowledgeBase: try: content = fp.read_text("utf-8", errors="ignore") title = fp.stem - self.docs.append({ - "path": str(fp), - "title": title, - "content": content, - }) + # 解析 Q&A 段 + segments = self._parse_qa_segments(content) + if segments: + for seg in segments: + self.docs.append({ + "path": str(fp), + "title": title, + "content": seg["raw"], + "answer": seg["answer"], + "question": seg["question"], + }) + else: + # 非 Q&A 格式,整文件作为一段 + self.docs.append({ + "path": str(fp), + "title": title, + "content": content, + }) except Exception: continue diff --git a/monitor.py b/monitor.py index 7e32204..7c94494 100644 --- a/monitor.py +++ b/monitor.py @@ -570,9 +570,9 @@ class MessageMonitor: # 先切 QQ 到前台再截屏,避免 CMD 窗口遮挡→OCR 读到终端文字 self._ensure_foreground(hwnd) time.sleep(0.15) - # 左边界 35%,从消息面板左侧开始(联系人/会话列表 / 消息面板分界约 55%) - # 但消息正文在面板左侧,因此稍左移 35% 覆盖消息文字左侧边缘 - cap_left = int(left + win_w * 0.35) + # 左边界 25%,覆盖消息文字区域(他人消息在面板左侧) + # 从 c9acd7e 版本恢复:0.35 仅截到时间戳,0.25 才覆盖文字 + cap_left = int(left + win_w * 0.25) cap_top = top + max(40, int(win_h * 0.04)) # 标题栏下方 cap_right = int(left + win_w * 0.95) # 跳过滚动条 cap_bottom = bottom - max(100, int(win_h * 0.10)) # 输入框上方