优化: 模型能力探测前缀匹配收紧 + 手动标注能力不被测试覆盖

This commit is contained in:
lxy
2026-08-08 14:19:38 +08:00
parent d19784a414
commit b408b2d7a1
2 changed files with 83 additions and 9 deletions
+69 -8
View File
@@ -4,7 +4,7 @@
//!
//! 多源探测,高优先源命中即返(短路):
//! 1. 内置预设表精确匹配(name 完全相等) → `ProbeSource::PresetTable`
//! 2. 内置预设表模糊匹配(子串包含) → `ProbeSource::PresetTable`
//! 2. 内置预设表前缀匹配(前缀 + 分隔符边界) → `ProbeSource::PresetTable`
//! 3. 模型名启发式推断(命名模式) → `ProbeSource::Heuristic`
//! 4. 默认值兜底(`ModelConfig::with_defaults`) → `ProbeSource::Default`
//!
@@ -26,7 +26,7 @@ use crate::model_probe_helpers::{heuristic_infer, presets};
///
/// 多源探测顺序(高优先源命中即返):
/// 1. 预设表精确匹配(`model_id` 完全相等,大小写敏感)
/// 2. 预设表模糊匹配(`model_id` 双向子串包含,大小写不敏感)
/// 2. 预设表前缀匹配(`model_id` 是入参前缀且后缀以分隔符开始,大小写不敏感)
/// 3. 启发式推断(模型名命名模式)
/// 4. 默认值兜底
///
@@ -38,14 +38,27 @@ pub fn probe(model_id: &str) -> ModelConfig {
return hit;
}
// 2. 预设表模糊匹配(双向子串包含,大小写不敏感)
// 多个候选命中时,选预设 model_id 最长者(最具体:glm-4v > glm-4)。
// 2. 预设表前缀匹配(入参名以预设名开头 + 后缀以分隔符开始,大小写不敏感)
// 收紧自原「双向子串包含」:子串匹配会把 glm-4.6v 误配给 glm-4v(继承其 vision),
// gpt-4o-mini 误配给 gpt-4o 等「张冠李戴」。前缀匹配保留合理继承
// (glm-4v-flash → glm-4v),同时要求后缀以分隔符(-/./_)或结尾开始,
// 防止 glm-4v2/glm-4vx 命中 glm-4v。
let needle = model_id.to_lowercase();
let fuzzy = presets()
.iter()
.filter(|m| {
let cand = m.model_id.to_lowercase();
!cand.is_empty() && (cand.contains(&needle) || needle.contains(&cand))
if cand.is_empty() || cand.len() > needle.len() {
return false;
}
if !needle.starts_with(&cand) {
return false;
}
// 前缀后须为分隔符或字符串结束,避免部分单词命中(glm-4v2 不算 glm-4v)
match needle[cand.len()..].chars().next() {
None => true, // 完全相等(精确匹配已覆盖,这里兜底)
Some(c) => matches!(c, '-' | '.' | '_'),
}
})
.max_by_key(|m| m.model_id.len());
@@ -157,20 +170,68 @@ mod tests {
assert_eq!(m.context_window, 1_048_576);
}
// ── 预设模糊匹配 ──
// ── 预设前缀匹配 ──
#[test]
fn probe_preset_fuzzy_match_glm4v_variant() {
// "glm-4v-x" 不在预设表精确命中,但 "glm-4v" 是其子串模糊命中
// "glm-4v-x" 不在预设表精确命中,但 "glm-4v" 是其前缀(后缀 "-x" 以分隔符开始)前缀命中
let m = probe("glm-4v-x");
assert_eq!(m.probe_source, Some(ProbeSource::PresetTable));
assert_eq!(m.model_id, "glm-4v-x", "模糊命中后 model_id 应用入参名");
assert_eq!(m.model_id, "glm-4v-x", "前缀命中后 model_id 应用入参名");
assert!(
m.modalities.contains(&Modality::Vision),
"应继承 glm-4v 的 vision 模态"
);
}
#[test]
fn probe_preset_prefix_match_glm4v_flash() {
// 合理继承:glm-4v-flash 以 glm-4v 为前缀 + 分隔符 -,命中并继承 vision
let m = probe("glm-4v-flash");
assert_eq!(m.probe_source, Some(ProbeSource::PresetTable));
assert!(m.modalities.contains(&Modality::Vision));
}
#[test]
fn probe_preset_prefix_match_gpt4o_mini() {
// 合理继承:gpt-4o-mini 以 gpt-4o 为前缀 + 分隔符 -
let m = probe("gpt-4o-mini");
assert_eq!(m.probe_source, Some(ProbeSource::PresetTable));
assert_eq!(m.model_id, "gpt-4o-mini");
}
#[test]
fn probe_preset_prefix_match_case_insensitive() {
// 前缀匹配大小写不敏感(精确匹配大小写敏感,前缀兜底)
let m = probe("GLM-4V-FLASH");
assert_eq!(m.probe_source, Some(ProbeSource::PresetTable));
assert!(m.modalities.contains(&Modality::Vision));
}
#[test]
fn probe_preset_prefix_reject_non_prefix_variant() {
// 张冠李戴防线:glm-4.6v 不以 glm-4v 为前缀(字符顺序不同),不继承 glm-4v 的 vision。
// 但它以 glm-4 为前缀(+ 分隔符 .)→ 命中 glm-4 预设(纯文本),合理。
let m = probe("glm-4.6v");
assert_eq!(m.probe_source, Some(ProbeSource::PresetTable));
assert_eq!(
m.model_id, "glm-4.6v",
"前缀命中后 model_id 应用入参名"
);
assert!(
!m.modalities.contains(&Modality::Vision),
"glm-4.6v 不应继承 glm-4v 的 vision(只继承 glm-4 纯文本): {:?}",
m.modalities
);
}
#[test]
fn probe_preset_prefix_reject_no_separator() {
// 分隔符边界:glm-4v2 前缀后是数字(非 -/./_),不算 glm-4v 的合法变体
let m = probe("glm-4v2");
assert_eq!(m.probe_source, Some(ProbeSource::Heuristic));
}
// ── 启发式:Vision ──
#[test]