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

fix(smart-locate): 修复智能定位三个问题

1. 关键词提取:去除特殊符号【】《》等
2. 元素匹配:点击操作优先匹配按钮,避免误匹配输入框
3. 登录步骤:优先使用已验证的登录模板选择器

问题原因:
- 步骤"点击【功能中心】"被误匹配到搜索输入框
- 登录相关步骤没有使用模板选择器
- 特殊符号影响关键词匹配准确度
Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 11ddf7e9
...@@ -48,9 +48,10 @@ def extract_keywords(description: str) -> List[str]: ...@@ -48,9 +48,10 @@ def extract_keywords(description: str) -> List[str]:
从自然语言描述中提取关键词 从自然语言描述中提取关键词
策略: 策略:
1. 去除动作词和元素类型词 1. 去除特殊符号(【】《》等)
2. 提取剩余的关键词(名词、修饰词等) 2. 去除动作词和元素类型词
3. 按 2-gram 分词获取更精确的关键词 3. 提取剩余的关键词(名词、修饰词等)
4. 按 2-gram 分词获取更精确的关键词
Args: Args:
description (str): 步骤描述,如 "输入用户名 admin@xty" description (str): 步骤描述,如 "输入用户名 admin@xty"
...@@ -63,9 +64,16 @@ def extract_keywords(description: str) -> List[str]: ...@@ -63,9 +64,16 @@ def extract_keywords(description: str) -> List[str]:
["登录"] ["登录"]
>>> extract_keywords("输入用户名 admin@xty") >>> extract_keywords("输入用户名 admin@xty")
["用户名", "admin", "admin@xty"] ["用户名", "admin", "admin@xty"]
>>> extract_keywords("点击【功能中心】展开")
["功能中心"]
""" """
# 去除动作词 # 去除特殊符号
cleaned = description cleaned = description
special_chars = ['【', '】', '《', '》', '「', '」', '『', '』', '〔', '〕', '〈', '〉']
for ch in special_chars:
cleaned = cleaned.replace(ch, '')
# 去除动作词
for word in sorted(ACTION_WORDS + ELEMENT_WORDS, key=len, reverse=True): for word in sorted(ACTION_WORDS + ELEMENT_WORDS, key=len, reverse=True):
cleaned = cleaned.replace(word, "") cleaned = cleaned.replace(word, "")
cleaned = cleaned.strip() cleaned = cleaned.strip()
...@@ -84,7 +92,8 @@ def extract_keywords(description: str) -> List[str]: ...@@ -84,7 +92,8 @@ def extract_keywords(description: str) -> List[str]:
keywords.append(sub) keywords.append(sub)
# 单字关键词(去掉停用词) # 单字关键词(去掉停用词)
stopwords = {'的', '在', '是', '和', '有', '等', '中', '为', '了', '与', '或', '、', ',', '。', ' '} stopwords = {'的', '在', '是', '和', '有', '等', '中', '为', '了', '与', '或', '、', ',', '。', ' ',
'展开', '分类', '添加', '操作', '查看', '是否', '正确', '新增', '条目', '列表', '数据'}
for ch in cleaned: for ch in cleaned:
if ch.strip() and ch not in stopwords: if ch.strip() and ch not in stopwords:
keywords.append(ch) keywords.append(ch)
...@@ -292,13 +301,26 @@ def match_element_by_keywords( ...@@ -292,13 +301,26 @@ def match_element_by_keywords(
'priority': 6 'priority': 6
}) })
# 7. 动作类型加分 # 7. 动作类型加分/减分
if action == 'fill' and tag in ['INPUT', 'TEXTAREA']: if action == 'fill' and tag in ['INPUT', 'TEXTAREA']:
score += 0.15 score += 0.20
elif action == 'click' and tag in ['BUTTON', 'A']: elif action == 'click' and tag in ['BUTTON', 'A']:
score += 0.15 score += 0.25
elif action == 'select' and tag == 'SELECT': elif action == 'select' and tag == 'SELECT':
score += 0.15 score += 0.20
# ⚠️ 关键修复:点击操作不应该匹配输入框(除非关键词明确包含输入相关)
if action == 'click' and tag in ['INPUT', 'TEXTAREA']:
# 只有关键词明确包含输入相关词汇时才保留输入框
input_keywords = ['输入框', '输入', '框', '填写', 'input']
if not any(kw in input_keywords for kw in keywords):
score *= 0.3 # 大幅降低分数,让按钮优先
# 填充操作不应该匹配按钮(除非关键词明确包含按钮相关)
if action == 'fill' and tag in ['BUTTON', 'A']:
fill_keywords = ['按钮', '点击', 'click']
if not any(kw in fill_keywords for kw in keywords):
score *= 0.3 # 大幅降低分数
# 8. 类型匹配(password, email, tel 等) # 8. 类型匹配(password, email, tel 等)
if el_type: if el_type:
......
...@@ -257,6 +257,19 @@ class SmartLocateService: ...@@ -257,6 +257,19 @@ class SmartLocateService:
result['message'] = '导航步骤无需定位' result['message'] = '导航步骤无需定位'
return result return result
# ⚠️ 新增:登录相关步骤优先使用已验证的选择器
login_selectors = self._get_login_selector(name)
if login_selectors:
result['success'] = True
result['selectors'] = {
'primary': login_selectors,
'candidates': [{'type': 'css', 'value': login_selectors, 'confidence': 0.95, 'priority': 1}]
}
result['params']['selector'] = login_selectors
result['message'] = f'登录模板选择器: {login_selectors}'
logger.info(f"步骤 {order} 使用登录模板选择器: {login_selectors}")
return result
# 提取关键词 # 提取关键词
keywords = extract_keywords(name) keywords = extract_keywords(name)
logger.debug(f"提取关键词: {keywords}") logger.debug(f"提取关键词: {keywords}")
...@@ -376,6 +389,40 @@ class SmartLocateService: ...@@ -376,6 +389,40 @@ class SmartLocateService:
logger.warning(f"执行步骤验证失败: {e}") logger.warning(f"执行步骤验证失败: {e}")
return False return False
def _get_login_selector(self, step_name: str) -> Optional[str]:
"""
获取登录相关步骤的选择器(使用已验证的登录模板)
Args:
step_name (str): 步骤名称
Returns:
Optional[str]: 选择器,如果不是登录步骤返回 None
"""
# 登录相关关键词映射到选择器
login_selector_map = {
# 输入账号
'账号': 'input[placeholder*="手机号"]',
'用户名': 'input[placeholder*="手机号"]',
'手机号': 'input[placeholder*="手机号"]',
# 输入密码
'密码': 'input[type="password"]',
# 输入验证码
'验证码': 'input[placeholder*="图"]',
# 勾选协议
'协议': '.el-checkbox',
'勾选': '.el-checkbox',
# 点击登录
'登录': 'button:has-text("登录")',
}
# 检查步骤名称是否包含登录关键词
for keyword, selector in login_selector_map.items():
if keyword in step_name:
return selector
return None
# ==================== 工厂函数 ==================== # ==================== 工厂函数 ====================
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论