Se fier à un seul fournisseur de recherche signifie que votre agent échoue lorsque ce fournisseur subit des temps d'arrêt, des limites de débit ou une couverture insuffisante pour un type de requête spécifique. Ce tutoriel construit un agent de recherche multi-moteur qui cascade à travers les fournisseurs (Scavio, Brave, SearXNG) et sélectionne le meilleur ensemble de résultats. L'agent essaie d'abord le fournisseur le moins cher et ne recourt aux alternatives qu'en cas de besoin, minimisant ainsi les coûts.
Prérequis
- Python 3.9+ installé
- bibliothèque requests installée
- Une clé API Scavio depuis scavio.dev
- Optionnel : clé API Brave pour le secours
Parcours
Étape 1: Définir la cascade de moteurs de recherche
Configurez plusieurs fournisseurs de recherche par ordre de priorité. Chacun a une fonction de recherche, un coût et un seuil de qualité pour accepter les résultats.
import requests, os, time
SCAVIO_KEY = os.environ.get('SCAVIO_API_KEY', '')
BRAVE_KEY = os.environ.get('BRAVE_API_KEY', '')
def search_scavio(query: str, count: int = 10) -> list:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us', 'num_results': count},
timeout=10)
if resp.status_code != 200:
return []
return [{'title': r['title'], 'link': r['link'], 'snippet': r.get('snippet', '')}
for r in resp.json().get('organic_results', [])]
def search_brave(query: str, count: int = 10) -> list:
if not BRAVE_KEY:
return []
resp = requests.get('https://api.search.brave.com/res/v1/web/search',
headers={'X-Subscription-Token': BRAVE_KEY},
params={'q': query, 'count': count}, timeout=10)
if resp.status_code != 200:
return []
return [{'title': r['title'], 'link': r['url'], 'snippet': r.get('description', '')}
for r in resp.json().get('web', {}).get('results', [])]
ENGINES = [
{'name': 'scavio', 'fn': search_scavio, 'cost': 0.005, 'min_results': 3},
{'name': 'brave', 'fn': search_brave, 'cost': 0.005, 'min_results': 3},
]
print(f'Configured {len(ENGINES)} search engines in cascade')
for e in ENGINES:
print(f' {e["name"]}: ${e["cost"]}/query, min {e["min_results"]} results')Étape 2: Construire la fonction de recherche en cascade
Essayez chaque moteur dans l'ordre. Acceptez les résultats s'ils atteignent le seuil de qualité. Suivez quel moteur a réussi et le coût total.
def cascade_search(query: str, count: int = 10) -> dict:
"""Search with cascading fallback across engines."""
attempts = []
for engine in ENGINES:
start = time.time()
try:
results = engine['fn'](query, count)
latency = int((time.time() - start) * 1000)
attempts.append({
'engine': engine['name'], 'results': len(results),
'latency_ms': latency, 'cost': engine['cost']
})
if len(results) >= engine['min_results']:
total_cost = sum(a['cost'] for a in attempts)
return {
'results': results,
'engine': engine['name'],
'attempts': attempts,
'total_cost': total_cost
}
except Exception as e:
attempts.append({
'engine': engine['name'], 'error': str(e),
'cost': engine['cost']
})
# All engines failed or returned poor results
total_cost = sum(a['cost'] for a in attempts)
best = max(attempts, key=lambda a: a.get('results', 0))
return {
'results': [],
'engine': 'none',
'attempts': attempts,
'total_cost': total_cost,
'note': 'All engines returned insufficient results'
}
result = cascade_search('best python web framework 2026')
print(f'Engine: {result["engine"]}')
print(f'Results: {len(result["results"])}')
print(f'Cost: ${result["total_cost"]:.3f}')
for a in result['attempts']:
print(f' {a["engine"]}: {a.get("results", "error")} results, {a.get("latency_ms", "N/A")}ms')Étape 3: Ajouter une sélection intelligente du moteur en fonction du type de requête
Acheminez différents types de requêtes vers le meilleur moteur. Les requêtes techniques peuvent mieux fonctionner sur un moteur tandis que les requêtes d'actualités fonctionnent mieux sur un autre.
def smart_search(query: str, count: int = 10) -> dict:
"""Route query to best engine based on query type."""
query_lower = query.lower()
# Classify query type
if any(w in query_lower for w in ['news', 'latest', 'today', 'just', 'announced']):
query_type = 'news'
elif any(w in query_lower for w in ['site:', 'filetype:', 'inurl:']):
query_type = 'advanced'
elif any(w in query_lower for w in ['how to', 'tutorial', 'guide', 'example']):
query_type = 'tutorial'
else:
query_type = 'general'
# Reorder engines based on query type
# For most queries, default cascade works fine
# For advanced queries, Scavio handles operators better
result = cascade_search(query, count)
result['query_type'] = query_type
return result
# Test different query types
test_queries = [
'latest AI news today',
'site:github.com python search api',
'how to build a search agent',
]
for q in test_queries:
r = smart_search(q)
print(f'[{r["query_type"]}] {q[:40]} -> {r["engine"]} ({len(r["results"])} results, ${r["total_cost"]:.3f})')Exemple Python
import requests, os, time
SCAVIO_KEY = os.environ.get('SCAVIO_API_KEY', '')
BRAVE_KEY = os.environ.get('BRAVE_API_KEY', '')
def search(query, count=10):
# Try Scavio first
try:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us', 'num_results': count}, timeout=10)
results = resp.json().get('organic_results', [])
if len(results) >= 3:
return {'results': results, 'engine': 'scavio', 'cost': 0.005}
except Exception:
pass
# Fallback to Brave
if BRAVE_KEY:
try:
resp = requests.get('https://api.search.brave.com/res/v1/web/search',
headers={'X-Subscription-Token': BRAVE_KEY},
params={'q': query, 'count': count}, timeout=10)
results = resp.json().get('web', {}).get('results', [])
return {'results': results, 'engine': 'brave', 'cost': 0.010}
except Exception:
pass
return {'results': [], 'engine': 'none', 'cost': 0}
r = search('python web framework 2026')
print(f'{r["engine"]}: {len(r["results"])} results, ${r["cost"]}')Exemple JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
const BRAVE_KEY = process.env.BRAVE_API_KEY;
async function search(query, count = 10) {
try {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: 'us', num_results: count })
});
const results = (await resp.json()).organic_results || [];
if (results.length >= 3) return { results, engine: 'scavio', cost: 0.005 };
} catch {}
if (BRAVE_KEY) {
try {
const resp = await fetch(`https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=${count}`, {
headers: { 'X-Subscription-Token': BRAVE_KEY }
});
const results = (await resp.json()).web?.results || [];
return { results, engine: 'brave', cost: 0.01 };
} catch {}
}
return { results: [], engine: 'none', cost: 0 };
}
search('python framework 2026').then(r => console.log(`${r.engine}: ${r.results.length} results`));Sortie attendue
Configured 2 search engines in cascade
scavio: $0.005/query, min 3 results
brave: $0.005/query, min 3 results
Engine: scavio
Results: 10
Cost: $0.005
scavio: 10 results, 245ms
[news] latest AI news today -> scavio (10 results, $0.005)
[advanced] site:github.com python search api -> scavio (8 results, $0.005)
[tutorial] how to build a search agent -> scavio (10 results, $0.005)