Multi-Agent Semantic Core Automation with Ensemble Voting
This script automates semantic data collection: input a mask, retrieve frequency data, export to a table. It replaces the tedious Wordstat → Excel workflow. Leverages two data sources: the free Bukvarix API for broad synonym coverage (with a monthly lag) and the paid XMLRiver for real-time data via proxy to Yandex XML (no CAPTCHA required).
XMLRiver supports three frequency types:
- Basic:
apartment repair→ 45,661 - Exact:
"apartment repair"→ 12,340 - Refined:
[!apartment !repair]→ 8,912
Queries run in parallel across 10 threads with retry logic. Competitiveness heuristic uses the formula: score = (word_count * 1000) / (frequency + 1). Results are color-coded:
- 🟢 EASY (>50)
- 🟡 MEDIUM (10–50)
- 🔴 HARD (<10)
Optimized Keyword Processing Pipeline
A direct approach fails on 3,000+ keywords due to duplicate SERP queries. The pipeline filters step-by-step:
- Collection: Bukvarix or XMLRiver → ~3,000 keywords.
- Regex Shield: removes noise (job listings, Avito fragments, incomplete terms).
- Fuzzy Dedup: pymorphy2 lemmatization + rapidfuzz (token_sort_ratio ≥82%, grouped by first word) → ~1,500–2,000 unique.
- SERP Collection: 10 threads per unique keyword.
- Clustering: NLP + SERP Veto.
- Intent & Metrics.
The 82% threshold was empirically tuned: removes 30–40% duplicates (morphology), without merging distinct phrases like "apartment repair price" and "apartment repair cost".
Clustering with SERP Veto
SentenceTransformers (paraphrase-multilingual-MiniLM-L12-v2) generate embeddings. NLP challenge: semantically similar terms (e.g., "apartment repair Moscow" vs. "apartment repair Voronezh") don’t compete.
SERP Veto: If TOP-10 Yandex results have <2 overlapping URLs, they’re treated as separate clusters:
overlap = len(urls_core.intersection(urls_cand))
if urls_core and urls_cand and overlap < 2:
continue # different clusters
Modes:
- NLP Only: Fast, based on embeddings.
- SERP Only: Accurate, slow, URL-based.
- Hybrid: NLP + Veto — optimal balance.
Geographic isolation: Keywords from different cities aren’t clustered together.
Multi-Agent AI Filtering with Ensemble Voting
LLM (DeepSeek) classifies: suitable / irrelevant / minus / check. Challenges: instability (38% stable across 3 runs), lack of niche context.
PlannerAgent
Generates niche-specific plans using few-shot prompting, trap detection, and geo-filtering.
Example:
- SUITABLE: "apartment repair turnkey Moscow"
- IRRELEVANT: "job apartment repair"
Token Optimization
ID numbering: model returns only IDs, not text. Batch size of 20 keys → ~80 tokens vs. ~400 previously.
Ensemble Voting
Three parallel runs (temperature=0), majority vote (threshold=2/3). Tie → ArbiterAgent.
def single_vote(_):
response = ai_client.call(system_prompt, user_message, temperature=0)
return ai_client.parse_json(response)
with ThreadPoolExecutor(max_workers=votes) as vote_pool:
vote_results = list(vote_pool.map(single_vote, range(votes)))
counts = Counter(votes_for_keyword)
threshold = votes // 2 + 1
Result: stability ~85%. Cost: $0.3 for 3,000 keywords.
Paranoid Mode: Whitelist (exact token matches, not substrings) for brands — bypassing AI entirely.
Additional Modules
SERP Module: Parses organic results, related queries, ads, and neural responses.
AI Assistant: Chat interface with pandas.query() over the dataframe.
Key Takeaways
- Pipeline reduces SERP requests from 3,000 to 1,500 via Fuzzy Dedup (rapidfuzz + pymorphy2).
- Hybrid clustering combines NLP speed with SERP Veto accuracy.
- Ensemble voting (3x) boosts AI filtering stability to 85%.
- Token savings: ID numbering + batching → $0.3 per full run.
- Processing time: 20–30 minutes vs. 3–4 hours manually.
Tech Stack: Python 3.11, DeepSeek API, XMLRiver, SentenceTransformers, rapidfuzz, pymorphy2, pandas, customtkinter.
— Editorial Team
No comments yet.