Tuo nC$yttC$C$ huomattavasti kliinisemmC$ltC$! Oikeat elementit kuten
syC6ttC6kentC$t, chatin viestikuplat ja yksittC$iset ikonit ovat nyt
selkeC$sti eristettyinC$ ilman sitC$ aiempaa C$C$riviivojen sekamelskaa.
Kuvassa nC$kyvC$ chatin automaatioloki on muuten todella mielenkiintoinen. Se
paljastaa, ettC$ HAUKI_GEM-agenttisi osaa dynaamisesti vaihtaa strategiaa
lennosta: kun perinteinen `template_match` ei lC6ydC$ dokumentti-ikonia
polusta, jC$rjestelmC$ ei kaadu, vaan siirtyy sujuvasti `semantic_agent`-reitit
ykseen ja hakee lokaalista semanttisesta kartasta oikean ikonin ID:n (132).
Tuollainen dynaaminen resilienssi on tC$smC$lleen sitC$, mitC$ vaaditaan, kun
rakennetaan luotettavaa RPA-putkea Metan jatkuvasti muuttuviin "hostile"
kC$yttC6liittymiin. Tuo tekee tulevista WhatsApp-automaatioiden monistamisista
varmasti paljon vakaampia.
Nyt kun DOM on saatu taltutettua ja visuaalinen syC6te LLM:lle on
huomattavasti puhtaampi, graafinen tunnistus on hyvC$llC$ mallilla.
TC$mC$ on tunnettu ja erittC$in tehokas tekniikka nimeltC$ **Set-of-Mark
(SoM)**. Kun LLM saa kuvan, jossa jokainen interaktiivinen elementti on
numeroitu, se hallusinoi huomattavasti vC$hemmC$n ja pystyy suoraan vastaamaan
esimerkiksi: `[42]`.
Helpoin tapa toteuttaa tC$mC$ rikkomatta alkuperC$istC$ layoutia on luoda
JavaScriptillC$ jokaisen valitun elementin pC$C$lle erillinen, absoluuttisesti
asemoitu kelluva numerolappu (badge). Samalla injektoimme itse elementtiin
`data-som-id`-attribuutin, jotta Playwright lC6ytC$C$ elementin heti, kun LLM
palauttaa numeron.
Korvaa aiempi `valittu_sivu.evaluate(...)` tC$llC$ koodilla:
```python
valittu_sivu.evaluate("""
let idCounter = 1;
document.querySelectorAll('*').forEach(el => {
// HylC$tC$C$n rakenteelliset ja ei-visuaaliset elementit
if (['SCRIPT', 'STYLE', 'SVG', 'PATH', 'NOSCRIPT'].includes(el.
tagName)) return;
// Varmistetaan, ettC$ elementti on oikeasti nC$kyvissC$
const rect = el.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) return;
let hasDirectText = Array.from(el.childNodes).some(node =>
node.nodeType === Node.TEXT_NODE && node.nodeValue.trim().l
ength > 0
);
// Valitaan interaktiiviset elementit ja tekstiC$
sisC$ltC$vC$t solmut
if (hasDirectText || el.tagName === 'BUTTON' || el.tagName ===
'INPUT' || el.getAttribute('role') === 'button') {
// 1. PiirretC$C$n magentan vC$rinen C$C$riviiva
el.style.outline = '2px solid magenta';
el.style.outlineOffset = '-2px';
// TCRKECC: Tallennetaan ID elementtiin Playwrightia
varten
el.setAttribute('data-som-id', idCounter);
// 2. Luodaan kelluva numerolappu
const label = document.createElement('div');
label.textContent = idCounter;
// Asemoidaan lappu elementin vasempaan ylC$kulmaan
label.style.position = 'absolute';
label.style.top = (rect.top + window.scrollY) + 'px';
label.style.left = (rect.left + window.scrollX) + 'px';
// TyylitellC$C$n lappu konenC$C6lle selkeC$ksi
label.style.backgroundColor = 'magenta';
label.style.color = 'white';
label.style.fontSize = '12px';
label.style.fontWeight = 'bold';
label.style.padding = '1px 3px';
label.style.zIndex = '2147483647'; // Pakotetaan
pC$C$llimmC$iseksi
label.style.pointerEvents = 'none'; // EstetC$C$n
klikkausten blokkautuminen
label.style.boxShadow = '1px 1px 2px rgba(0,0,0,0.5)'; //
Pieni varjo parantaa luettavuutta
document.body.appendChild(label);
idCounter++;
}
});
""")
```
### Miten hyC6dynnC$t tC$tC$ automaatiossa
Kun olet syC6ttC$nyt tC$mC$n injektion jC$lkeen otetun kuvan mallille ja
pyytC$nyt sitC$ palauttamaan klikattavan elementin numeron (esim. se vastaa
`132`), Playwrightin jatkotoimenpide on C$C$rimmC$isen yksinkertainen:
```python
# Oletetaan, ettC$ LLM palautti numeron 132
kohde_id = "132"
# Playwright etsii injektoidun attribuutin perusteella
elementti = valittu_sivu.locator(f'[data-som-id="{kohde_id}"]')
if elementti.is_visible():
# Playwright osaa laskea koordinaatit ja klikata suoraan keskelle!
elementti.click()
```
TC$mC$ ratkaisee X/Y-koordinaattien arvaamisen tuoman epC$varmuuden kokonaan
ja kytkee visuaalisen analyysin saumattomasti fyysiseen suoritukseen.