"""经验条目的判重与归类。桌面端(backend)和中心服务端(server)共用同一套规则,避免两边判重结果不一致。 判重按内容而不是按 key:模型每次给同一条知识起的名字都不一样, 「班级下拉框」和「班级选择控件」实际是一条。阈值来自真实草稿对的标定。 """ from __future__ import annotations import difflib import re LEARNED_CATEGORIES = {"control_tip", "path", "rule", "note"} # 只有控件技巧和入口路径能自动生效:它们描述「控件怎么点」,错了顶多多花几步; # 系统规则和其他类描述「系统有什么约束」,一条错的会让 Agent 以后一直绕开正确操作,必须人审。 AUTO_VERIFY_CATEGORIES = {"control_tip", "path"} CATEGORY_LABELS = { "control_tip": "控件技巧", "path": "入口路径", "rule": "系统规则", "note": "其他", } def similarity(a: str, b: str) -> float: na = re.sub(r"\s+", "", a or "") nb = re.sub(r"\s+", "", b or "") if not na or not nb: return 0.0 return difflib.SequenceMatcher(None, na, nb).ratio() def same_knowledge(a_key: str, a_value: str, b_key: str, b_value: str) -> bool: """内容相似度 >= 0.55,或 key 相似度 >= 0.7 且内容 >= 0.45,视为同一条知识。""" vs = similarity(a_value, b_value) if vs >= 0.55: return True return similarity(a_key, b_key) >= 0.7 and vs >= 0.45 def pick_category(*cats: str) -> str: """合并两条草稿时取更具体的类别,note 让位。""" for c in cats: if c in LEARNED_CATEGORIES and c != "note": return c return "note" def normalise_category(value: str | None) -> str: category = str(value or "note").strip() return category if category in LEARNED_CATEGORIES else "note"