Ověření publikací AI-agentu na X: od 83 falešných úspěchů k 94% skutečných
AI-agent pro automatizované komentáře na X ukazoval 83 úspěšných publikací, ale skutečných odpovědí bylo pouze 16. Rozdíl vznikl kvůli neúplné kontrole: kliknutí na tlačítko Reply negarantuje zobrazení obsahu na platformě. Spam filtry X tiše mažou příspěvky bez upozornění.
Mechanismus publikace přes prohlížeč
Agent používá Puppeteer CDP pro ovládání skutečného prohlížeče. Otevře vlákno, vloží text a klikne na Reply. První verze funkce kontrolovala pouze DOM událost:
const button = await page.$('[data-testid="tweetButton"]');
if (button) {
const disabled = await button.evaluate(el =>
el.getAttribute('aria-disabled') === 'true'
);
if (!disabled) {
await button.click();
return true; // <-- problém s předčasným úspěchem
}
}
return false;
Vrátit true zaznamenalo úspěch na úrovni DOM, bez kontroly platformy. Databáze hromadila záznamy, dashboard zelenal.
X neoznamuje odmítnutí: pole se vyčistí, URL se nemění. Omezení rychlosti nebo pravidla obsahu fungují tiše, nechávají prohlížeč v iluzi úspěchu.
Problémy s ID příspěvků
Pro sledování agent extrahoval ID z page.url() po kliknutí:
const tweetId = page.url().split('/status/')[1];
X nepřesměrovává na nový příspěvek – URL zůstává rodičovské. Všech 83 záznamů dostalo stejné parent_tweet_id, maskující problém jako platná data.
Objevení proběhlo manuálně přes analytiku X: 83 vs 16 skutečných odpovědí.
Kořen problému: rozpor mezi DOM a platformou
DOM: button.click() → success ✓
Browser: event fired → success ✓
X Platform: content dropped → ??? (ticho)
Database: recorded as "posted" → success ✓
Dashboard: 83 engagements → výborně ✓
Reality: 16 actual posts
Monitorování bylo omezené na prohlížeč. Platformní filtry – černá skříňka.
Řešení: post-ověření s přenačtením
Nová funkce verifyReplyPosted čeká na render, přenačte stránku a skenuje DOM na přítomnost textu:
export async function verifyReplyPosted(
page: Page,
replyText: string,
parentTweetId: string,
): Promise<{ verified: boolean; replyTweetId: string | null }> {
await new Promise(r => setTimeout(r, 3000));
await page.reload({ waitUntil: "domcontentloaded" });
await new Promise(r => setTimeout(r, 5000));
await page.evaluate("window.scrollBy(0, 1000)");
await new Promise(r => setTimeout(r, 2000));
const result = await page.evaluate(`
(function() {
var snippet = '${safe}'; // první 30 znaků
var articles = document.querySelectorAll(
'article[data-testid="tweet"]'
);
for (var i = 0; i < articles.length; i++) {
var textEl = articles[i].querySelector(
'[data-testid="tweetText"]'
);
if (!textEl) continue;
var text = (textEl.innerText || '').slice(0, 60);
if (text.indexOf(snippet.slice(0, 30)) !== -1) {
var links = articles[i].querySelectorAll(
'a[href*="/status/"]'
);
return { verified: true, replyTweetId: realId };
}
}
return { verified: false, replyTweetId: null };
})()
`);
return result;
}
- Přenačtení zajišťuje aktuální vlákno bez cache.
- Hledání podle
article[data-testid="tweet"]a[data-testid="tweetText"]. - ID se extrahuje z
hrefodkazů uvnitř article. - Pouze
verified: truese zapisuje do databáze.
Univerzalita přístupu
Problém není unikátní pro X. Platformy (sociální sítě, formuláře, platby, nasazení) tiše odmítají obsah.
Klíčové principy ověření:
- Imitovat uživatele: přenačtení + scroll pro lazy loading.
- Hledat marker (úryvek textu) v cílovém DOM.
- Extrahovat identifikátory z reálných elementů, ne z URL/událostí.
- Zrušit záznam při absenci důkazu.
- Logovat selhání v reálném čase.
Co je důležité
- DOM události ≠ platformní úspěch: kliknutí negarantuje publikaci.
- Tiché odstranění: spam filtry fungují bez upozornění pro ztížení obcházení.
- Ověření přes DOM skenování: přenačtení + hledání textu/ID.
- Metriky po opravě: 94% ověřená úspěšnost, selhání viditelná okamžitě.
- Aplikace: jakýkoli automatizovaný posting/odeslání vyžaduje user-like kontrolu.
Po implementaci agent zaznamenává skutečné publikace, dashboard odráží pravdu. Přístup se škáluje na jiné platformy s analogickými filtry.
— Editorial Team
Zatím žádné komentáře.