feat(v1): T-R3 采纳 llmrouter 词边界与失败安全——难度英文标记整词命中 + HF 分类器两级回落

- difficulty:标记表编译拆分——英文标记改词边界正则(修真实误判:'int' 子串
  命中 'print'/'point'、'log' 命中 'logic'、'list' 命中 'listen'),中文标记
  保持子串语义;命中行为对合法用例不变(整词出现照常计数)
- classifier:HuggingFaceClassifier 推理期异常回落内置 RuleClassifier(单次
  推理异常不打垮路由);build_classifier 的 hf 分支构造失败(ML 依赖缺失/
  模型加载失败)打印提示并回落规则分类器(外置规则照常合并)
- 新增 tests/test_failsafe.py 5 项;全量 43 passed(38+5)
This commit is contained in:
tzt
2026-09-19 09:27:15 +08:00
parent 0dad895aac
commit 06700a5aa3
3 changed files with 110 additions and 17 deletions
+29 -2
View File
@@ -25,16 +25,43 @@ _MEDIUM_MARKERS = [
"translate", "fix", "explain", "describe", "list",
]
def _compile_markers(markers):
"""标记表编译:英文标记用词边界正则,中文标记保持子串匹配。
T-R3(采纳 llmrouter 词边界思想,super_hard 先于 hard 的同理):
纯子串匹配会让 "int" 误命中 "print"/"point""log" 误命中 "logic"
"list" 误命中 "listen"——英文必须整词命中才计数。
"""
substr, words = [], []
for m in markers:
(words if m.isascii() else substr).append(m)
pattern = re.compile(r"\b(?:%s)\b" % "|".join(re.escape(w) for w in words)) \
if words else None
return substr, pattern
_HARD_SUBSTR, _HARD_RE = _compile_markers(_HARD_MARKERS)
_MEDIUM_SUBSTR, _MEDIUM_RE = _compile_markers(_MEDIUM_MARKERS)
# 预编译正则(模块级一次,避免每次调用走 re 内部缓存查找)
_RE_CODE_EXPR = re.compile(r"\b(def|class|function|import)\b")
_RE_ARITH_EXPR = re.compile(r"[0-9]+\s*[+\-*/^=]\s*[0-9xya-z]")
def _hit_count(substr, pattern, q: str) -> int:
"""中文子串命中数 + 英文整词命中数。"""
n = sum(1 for m in substr if m in q)
if pattern is not None:
n += len(pattern.findall(q))
return n
def estimate_difficulty(query: str) -> Tuple[str, float]:
"""返回 (difficulty, score)score 属于 [0,1]。"""
q = query.lower()
hard_hits = sum(1 for m in _HARD_MARKERS if m in q)
medium_hits = sum(1 for m in _MEDIUM_MARKERS if m in q)
hard_hits = _hit_count(_HARD_SUBSTR, _HARD_RE, q)
medium_hits = _hit_count(_MEDIUM_SUBSTR, _MEDIUM_RE, q)
length = len(query)
score = 0.0