从算法到智能体:我的聚类研究如何塑造我的自动化逻辑

发布: (2025年12月13日 GMT+8 07:02)
9 min read
原文: Dev.to

Source: Dev.to

Cover image for From Algorithms to Agents: How My Research in Clustering Shapes My Automation Logic

关键洞见

  • 研究塑造思考 – 学术原理影响我处理自动化问题的方式。
  • 噪声 = 不稳定 – 用于过滤空间噪声的同一思维模型也适用于测试的稳定性。
  • 效率至关重要 – 算法思维驱动等待策略和元素选择的优化。
  • 模式识别 – “在混沌中寻找秩序”的心态适用于自愈框架。

“只要点这里,输入那个,检查这个。”

如果你以这种方式思考测试自动化,那你只是在搭建一座纸牌屋。

大多数自动化工程师关注动作——点击什么,输入什么,断言什么。把自动化仅仅当作一连串动作会导致脚本脆弱,一旦 UI 改动了一个类名,脚本就会崩溃。

我并不把自动化看作脚本编写。我把它视为数据问题。

为什么?因为在我用 Python 构建自动化框架之前,我和合著者 Hrishav 正在研究空间数据库中的算法效率。那篇 2012 年发表的研究——并没有教会我可以直接复制粘贴到 Selenium 的具体技巧,却从根本上塑造了我对复杂数据问题的思考方式

基础:TDCT 算法

2012 年,我和 Hrishav 合著了一篇题为《一种基于多边形方法的大规模空间数据密度聚类技术》(TDCT)的研究论文。

我们解决的问题

如何在庞大且混乱的数据集中找到有意义的模式(簇)——而不被噪声淹没

当时已有的算法如 DBSCAN 还算不错,但它们在以下方面表现不佳:

  • 任意形状 – 真实世界的数据并不形成整齐的圆形。
  • 计算成本 – 对每个点与所有其他点进行扫描无法扩展。
  • 噪声敏感 – 异常值会扭曲簇的边界。

我们的解决方案:三角形密度

我们没有使用 DBSCAN 那样的圆形邻域,而是将数据点映射到三角形多边形中。这使我们能够:

  • 以比径向扫描更高的效率计算密度。
  • 检测任意非凸形状的簇。
  • 在不破坏核心簇的前提下隔离噪声点。

关键洞见

通过改变问题的几何形状(圆 → 三角形),我们降低了计算复杂度,同时提升了簇检测的准确性。这项合作工作为我至今处理复杂数据问题奠定了基础。

桥梁:这对质量工程有什么意义

“Dhiraj,空间聚类和 Selenium 有什么关系?”

不是代码——是思维方式

研究思维方式自动化应用
噪声掩盖真实模式不稳定的测试掩盖真实缺陷
暴力扫描无法扩展线性轮询和硬性等待无法扩展
几何形状决定效率框架的结构决定其韧性
区分稳定核心与噪声区分可靠的元素属性与动态属性

关键在于问题的框定

当我遇到复杂的自动化挑战时,我不会立刻想“我需要哪个 Selenium 命令?”而是先问:

  • 这里的数据结构是什么?(DOM 是一棵树;测试结果是时间序列数据。)
  • 噪声与信号的界限在哪里?(哪些元素属性是稳定的?哪些失败是真正的 bug?)
  • 如何降低复杂度?(我能像 TDCT 那样优化问题的“几何形状”吗?)

这种由多年算法研究训练出的思维模型,影响着我做出的每一个框架决策。

应用思维方式:实用示例

示例 1:带回退逻辑的多属性元素定位

暴力方式(单点故障):

# If ID changes, everything breaks
element = driver.find_element(By.ID, "checkout-btn-v3")

算法式方式(按可靠性对多个属性进行聚类):

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException

def find_element_with_fallback(driver, strategies: list[tuple]) -> WebElement:
    """
    Analyze multiple 'data points' (attributes) ordered by reliability.
    Returns the first element that becomes clickable.
    """
    for strategy, locator in strategies:
        try:
            element = WebDriverWait(driver, 2).until(
                EC.element_to_be_clickable((strategy, locator))
            )
            return element
        except TimeoutException:
            continue
    raise NoSuchElementException("All strategies exhausted")

checkout_strategies = [
    (By.CSS_SELECTOR, "[data-testid='checkout']"),               # Most stable: test IDs
    (By.CSS_SELECTOR, "button[aria-label='Checkout']"),         # Accessibility attrs
    (By.XPATH, "//button[contains(text(), 'Checkout')]"),        # Text content
    (By.CSS_SELECTOR, ".cart-section button.primary"),         # Structural fallback
]

checkout_btn = find_element_with_fallback(driver, checkout_strategies)

第一个策略充当“密度核心”。如果它失效,我们会优雅地回退到仍然有效但稍逊的“邻居”。

示例 2:自愈元素定位

静态方式(脆弱、对噪声敏感):

driver.find_element(By.ID, "submit-btn-v3")  # Breaks when ID changes

自适应方式(类似聚类的韧性):

def self_heal_find(driver, primary_locator, fallback_locators):
    """
    Try the primary locator; if it fails, evaluate fallback locators
    based on attribute stability and similarity to known anchors.
    """
    try:
        return driver.find_element(*primary_locator)
    except NoSuchElementException:
        for loc in fallback_locators:
            candidates = driver.find_elements(*loc)
            if candidates:
                # Simple heuristic: pick the element with the most stable attributes
                return max(candidates, key=lambda e: evaluate_stability(e))
    raise NoSuchElementException("Element could not be healed")

def evaluate_stability(element):
    # Placeholder for a real scoring function (e.g., based on data‑testids,
    # ARIA labels, relative position to stable anchors, etc.)
    score = 0
    if element.get_attribute("data-testid"):
        score += 3
    if element.get_attribute("aria-label"):
        score += 2
    # Add more heuristics as needed
    return score

primary = (By.ID, "submit-btn-v3")
fallbacks = [
    (By.CSS_SELECTOR, "[data-testid='submit']"),
    (By.XPATH, "//button[contains(text(),'Submit')]"),
    (By.CSS_SELECTOR, "button.primary")
]

submit_btn = self_heal_find(driver, primary, fallbacks)

该实现并非 TDCT 的字面复制,但思路相同:评估多个数据点并选取最可靠的组合。

体现此哲学的工具

当我创建 Lumos ShadowDOMVisual Guard 等包时,并没有有意识地实现聚类算法。然而设计决策仍然呼应了相同的原则:

  • 高效遍历 Shadow DOM → 在暴力遍历之前先了解结构
  • 使用 SSIM 的视觉回归 → 用数学模型(而非像素逐个比较)找出有意义的差异。
  • 框架自愈 → 将元素属性视为具有不同可靠性的“数据点”。

研究并没有给我可直接复制粘贴的解决方案,而是提供了一种视角:把自动化看作数据问题,而非脚本问题。

结论:自动化不只是代码——它是逻辑

无论是多年以前发表的 TDCT 算法,还是我今天构建的 自动化工具和库,目标始终如一:

在混沌中建立秩序。

DOM 本身是混沌的。当以研究驱动的思维方式来对待测试自动化时,能够施加结构、降低不稳定性,并实现优雅的扩展。

Back to Blog

相关文章

阅读更多 »