提交 c632f219 authored 作者: 陈泽健's avatar 陈泽健

feat(keyword-matcher): jieba分词优化 + 评分归一化 (Phase 1&2)

Phase 1 - 关键词提取优化:
- 引入jieba分词替代暴力替换,解决语义丢失问题
- 精简修饰词列表,添加项目词汇白名单
- extract_value支持中文值提取

Phase 2 - 评分体系归一化:
- 精确匹配(1.0)高于包含匹配(0.3)
- click对INPUT减分更激进(0.1),fill优先匹配INPUT
- 归一化评分到[0,1]区间

新增测试脚本验证改动
Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 3f4fffa2
...@@ -11,3 +11,4 @@ websockets==12.0 ...@@ -11,3 +11,4 @@ websockets==12.0
requests==2.31.0 requests==2.31.0
paho-mqtt==2.1.0 paho-mqtt==2.1.0
openpyxl==3.1.2 openpyxl==3.1.2
jieba>=0.42.1
\ No newline at end of file
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
测试脚本:验证 Phase 1 关键词提取优化
测试项:
1. jieba分词替代暴力替换
2. 修饰词精简后不再空关键词
3. 中文值提取
"""
import sys
import os
import io
# 设置标准输出编码为UTF-8(解决Windows GBK编码问题)
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.services.keyword_matcher import extract_keywords, extract_value_from_description
def test_extract_keywords():
"""测试关键词提取"""
print("=" * 60)
print("测试关键词提取(jieba分词版本)")
print("=" * 60)
test_cases = [
# (输入, 期望包含的关键词)
("点击【新建会议】按钮", ["新建会议"]),
("点击展开", ["展开"]), # 之前返回空列表
("点击系统设置", ["系统设置"]), # 之前返回空列表
("输入会议名称:自动化新建会议", ["会议名称", "自动化", "新建会议"]),
("点击信息发布", ["信息发布"]),
("点击功能中心图标", ["功能中心"]),
("输入备注:这是一个测试备注", ["备注"]),
("点击确定按钮", ["确定"]),
("点击取消按钮", ["取消"]),
("输入用户名:admin@xty", ["用户名"]),
]
passed = 0
failed = 0
for desc, expected_keywords in test_cases:
result = extract_keywords(desc)
# 检查期望的关键词是否在结果中
missing = [kw for kw in expected_keywords if kw not in result]
if missing:
print(f"[FAIL] '{desc}'")
print(f" 期望包含: {expected_keywords}")
print(f" 实际结果: {result}")
print(f" 缺失: {missing}")
failed += 1
else:
print(f"[PASS] '{desc}' -> {result}")
passed += 1
print()
print(f"结果: 通过 {passed}/{passed + failed}")
return failed == 0
def test_extract_value():
"""测试值提取"""
print()
print("=" * 60)
print("测试值提取(支持中文值)")
print("=" * 60)
test_cases = [
# (输入, 期望值)
("输入用户名:admin@xty", "admin@xty"),
("输入账号:admin@xty", "admin@xty"),
("输入密码:Ubains@13579", "Ubains@13579"),
("输入参考价值:100", "100"),
("输入备注:这是一个测试备注", "这是一个测试备注"),
("输入会议名称:自动化测试会议", "自动化测试会议"),
("输入用户名 admin@xty", "admin@xty"),
("输入值'test_value'", "test_value"),
("输入值\"test_value\"", "test_value"),
]
passed = 0
failed = 0
for desc, expected_value in test_cases:
result = extract_value_from_description(desc)
if result == expected_value:
print(f"[PASS] '{desc}' -> '{result}'")
passed += 1
else:
print(f"[FAIL] '{desc}'")
print(f" 期望: '{expected_value}'")
print(f" 实际: '{result}'")
failed += 1
print()
print(f"结果: 通过 {passed}/{passed + failed}")
return failed == 0
def main():
"""运行所有测试"""
print("\n" + "=" * 60)
print("Phase 1 关键词提取优化 - 验证测试")
print("=" * 60)
all_passed = True
# 测试关键词提取
if not test_extract_keywords():
all_passed = False
# 测试值提取
if not test_extract_value():
all_passed = False
print()
print("=" * 60)
if all_passed:
print("[SUCCESS] 所有测试通过!Phase 1 验证成功")
else:
print("[WARNING] 部分测试失败,请检查")
print("=" * 60)
return 0 if all_passed else 1
if __name__ == "__main__":
sys.exit(main())
\ No newline at end of file
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
测试脚本:验证 Phase 2 评分体系归一化
测试项:
1. 精确匹配 vs 包含匹配 - 精确匹配应排序更高
2. 动作类型适配 - click优先匹配BUTTON,fill优先匹配INPUT
"""
import sys
import os
import io
# 设置标准输出编码为UTF-8
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.services.keyword_matcher import (
_calculate_match_score,
_apply_action_type_adjustment,
EXACT_MATCH_SCORE,
CONTAINS_MATCH_SCORE,
)
def test_match_score():
"""测试匹配分数计算"""
print("=" * 60)
print("测试匹配分数计算")
print("=" * 60)
test_cases = [
# (关键词, 目标文本, 期望精确匹配)
("确定", "确定", True),
("确定", "确定按钮", False),
("新建", "新建会议", False),
("新建会议", "新建会议", True),
("admin", "admin@xty", False),
("admin@xty", "admin@xty", True),
]
passed = 0
failed = 0
for keyword, target, expect_exact in test_cases:
score = _calculate_match_score(keyword, target)
is_exact = (score == EXACT_MATCH_SCORE)
if is_exact == expect_exact:
match_type = "精确" if is_exact else "包含"
print(f"[PASS] '{keyword}' vs '{target}' -> {match_type}匹配 (score={score:.2f})")
passed += 1
else:
print(f"[FAIL] '{keyword}' vs '{target}'")
print(f" 期望: {'精确' if expect_exact else '包含'}匹配")
print(f" 实际: score={score:.2f}")
failed += 1
print()
print(f"结果: 通过 {passed}/{passed + failed}")
return failed == 0
def test_action_type_adjustment():
"""测试动作类型适配"""
print()
print("=" * 60)
print("测试动作类型适配")
print("=" * 60)
test_cases = [
# (原分数, 标签, 动作, 期望结果描述)
(0.5, "BUTTON", "click", "应该加分"),
(0.5, "INPUT", "click", "应该减分"),
(0.5, "INPUT", "fill", "应该加分"),
(0.5, "BUTTON", "fill", "应该减分"),
(0.5, "SELECT", "select", "应该加分"),
]
passed = 0
failed = 0
for base_score, tag, action, expect_desc in test_cases:
adjusted = _apply_action_type_adjustment(base_score, tag, action, [])
if expect_desc == "应该加分" and adjusted > base_score:
print(f"[PASS] {tag}+{action}: {base_score:.2f} -> {adjusted:.2f} ({expect_desc})")
passed += 1
elif expect_desc == "应该减分" and adjusted < base_score:
print(f"[PASS] {tag}+{action}: {base_score:.2f} -> {adjusted:.2f} ({expect_desc})")
passed += 1
else:
print(f"[FAIL] {tag}+{action}: {base_score:.2f} -> {adjusted:.2f} ({expect_desc})")
failed += 1
print()
print(f"结果: 通过 {passed}/{passed + failed}")
return failed == 0
def test_exact_vs_contains_ranking():
"""测试精确匹配排名高于包含匹配"""
print()
print("=" * 60)
print("测试精确匹配排名高于包含匹配")
print("=" * 60)
# 模拟场景:关键词"确定"
# 元素A: text="确定" (精确匹配)
# 元素B: text="确定按钮" (包含匹配)
# 期望:A的分数高于B
score_exact = _calculate_match_score("确定", "确定") * 0.15 # text维度权重
score_contains = _calculate_match_score("确定", "确定按钮") * 0.15
if score_exact > score_contains:
print(f"[PASS] 精确匹配({score_exact:.4f}) > 包含匹配({score_contains:.4f})")
passed = True
else:
print(f"[FAIL] 精确匹配({score_exact:.4f}) <= 包含匹配({score_contains:.4f})")
passed = False
# 模拟场景:关键词"新建"
# 元素A: text="新建" (精确匹配)
# 元素B: text="新建会议" (包含匹配)
score_exact2 = _calculate_match_score("新建", "新建") * 0.15
score_contains2 = _calculate_match_score("新建", "新建会议") * 0.15
if score_exact2 > score_contains2:
print(f"[PASS] 精确匹配({score_exact2:.4f}) > 包含匹配({score_contains2:.4f})")
passed2 = True
else:
print(f"[FAIL] 精确匹配({score_exact2:.4f}) <= 包含匹配({score_contains2:.4f})")
passed2 = False
return passed and passed2
def main():
"""运行所有测试"""
print("\n" + "=" * 60)
print("Phase 2 评分体系归一化 - 验证测试")
print("=" * 60)
all_passed = True
if not test_match_score():
all_passed = False
if not test_action_type_adjustment():
all_passed = False
if not test_exact_vs_contains_ranking():
all_passed = False
print()
print("=" * 60)
if all_passed:
print("[SUCCESS] 所有测试通过!Phase 2 验证成功")
else:
print("[WARNING] 部分测试失败,请检查")
print("=" * 60)
return 0 if all_passed else 1
if __name__ == "__main__":
sys.exit(main())
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论