Depender de un único proveedor de búsqueda para su LLM local es un único punto de falla. Cuando ese proveedor deja de funcionar o alcanza límites de tarifas, su agente pierde completamente la conexión a Internet. Este tutorial crea una pila alternativa de búsqueda que prueba primero Scavio ($0,005/consulta), recurre a Brave Search ($0,005/consulta) y luego Tavily ($0,008/crédito) si ambos no están disponibles. La pila normaliza las respuestas en un formato común para que su LLM vea datos consistentes independientemente del proveedor que respondió.
Requisitos previos
- Python 3.9+ instalado
- solicita biblioteca instalada
- Claves API para Scavio, Brave Search y Tavily
- Una configuración de LLM local (Ollama, llama.cpp o vLLM)
Guia paso a paso
Paso 1: Definir la interfaz del proveedor
Cree una interfaz común que implemente cada proveedor de búsqueda. Todos los proveedores devuelven el mismo formato de resultado normalizado.
import os, requests, time
from typing import Optional
SCAVIO_KEY = os.environ.get('SCAVIO_API_KEY', '')
BRAVE_KEY = os.environ.get('BRAVE_API_KEY', '')
TAVILY_KEY = os.environ.get('TAVILY_API_KEY', '')
def normalize_result(title: str, url: str, snippet: str, source: str) -> dict:
return {'title': title, 'url': url, 'snippet': snippet, 'source': source}Paso 2: Implementar cada proveedor de búsqueda
Escriba una función de búsqueda para cada proveedor que devuelva resultados normalizados. Cada función maneja sus propios errores y devuelve Ninguno en caso de falla.
def search_scavio(query: str, num: int = 5) -> Optional[list]:
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': num}, timeout=10)
resp.raise_for_status()
return [normalize_result(r['title'], r['link'], r.get('snippet', ''), 'scavio')
for r in resp.json().get('organic_results', [])]
except Exception as e:
print(f'Scavio failed: {e}')
return None
def search_brave(query: str, num: int = 5) -> Optional[list]:
try:
resp = requests.get('https://api.search.brave.com/res/v1/web/search',
headers={'X-Subscription-Token': BRAVE_KEY, 'Accept': 'application/json'},
params={'q': query, 'count': num}, timeout=10)
resp.raise_for_status()
return [normalize_result(r['title'], r['url'], r.get('description', ''), 'brave')
for r in resp.json().get('web', {}).get('results', [])]
except Exception as e:
print(f'Brave failed: {e}')
return None
def search_tavily(query: str, num: int = 5) -> Optional[list]:
try:
resp = requests.post('https://api.tavily.com/search',
json={'api_key': TAVILY_KEY, 'query': query, 'max_results': num}, timeout=10)
resp.raise_for_status()
return [normalize_result(r['title'], r['url'], r.get('content', ''), 'tavily')
for r in resp.json().get('results', [])]
except Exception as e:
print(f'Tavily failed: {e}')
return NonePaso 3: Construya la pila alternativa
Proveedores de cadena en orden de prioridad. El primer proveedor que devuelva resultados gana. Registre qué proveedor atendió la solicitud.
PROVIDERS = [
('scavio', search_scavio, 0.005),
('brave', search_brave, 0.005),
('tavily', search_tavily, 0.008),
]
def search_with_fallback(query: str, num: int = 5) -> dict:
for name, fn, cost in PROVIDERS:
results = fn(query, num)
if results is not None:
return {'results': results, 'provider': name, 'cost': cost, 'query': query}
return {'results': [], 'provider': 'none', 'cost': 0, 'query': query}
# Test the fallback
result = search_with_fallback('best python web frameworks 2026')
print(f'Provider: {result["provider"]} (${result["cost"]}/query)')
for r in result['results'][:3]:
print(f' {r["title"]}')
print(f' {r["url"]}')Paso 4: Integre con su LLM local
Utilice la búsqueda alternativa como herramienta para su LLM local. El LLM nunca necesita saber qué proveedor respondió.
def format_for_llm(search_result: dict) -> str:
lines = [f'Web search results for: {search_result["query"]}\n']
for i, r in enumerate(search_result['results'], 1):
lines.append(f'[{i}] {r["title"]}')
lines.append(f' URL: {r["url"]}')
lines.append(f' {r["snippet"]}')
lines.append('')
return '\n'.join(lines)
result = search_with_fallback('latest AI frameworks 2026')
formatted = format_for_llm(result)
print(formatted)
print(f'\n--- Served by: {result["provider"]} at ${result["cost"]}/query ---')Ejemplo en Python
import os, requests
from typing import Optional
SCAVIO_KEY = os.environ.get('SCAVIO_API_KEY', '')
BRAVE_KEY = os.environ.get('BRAVE_API_KEY', '')
def search_scavio(query, num=5):
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': num}, timeout=10)
resp.raise_for_status()
return [{'title': r['title'], 'url': r['link'], 'snippet': r.get('snippet', '')}
for r in resp.json().get('organic_results', [])]
except: return None
def search_brave(query, num=5):
try:
resp = requests.get('https://api.search.brave.com/res/v1/web/search',
headers={'X-Subscription-Token': BRAVE_KEY},
params={'q': query, 'count': num}, timeout=10)
resp.raise_for_status()
return [{'title': r['title'], 'url': r['url'], 'snippet': r.get('description', '')}
for r in resp.json().get('web', {}).get('results', [])]
except: return None
def search(query):
for name, fn in [('scavio', search_scavio), ('brave', search_brave)]:
results = fn(query)
if results:
print(f'Served by {name}')
return results
return []
for r in search('python web frameworks 2026')[:3]:
print(f' {r["title"]}')Ejemplo en JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
const BRAVE_KEY = process.env.BRAVE_API_KEY;
async function searchScavio(query) {
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: 5 })
});
const data = await resp.json();
return (data.organic_results || []).map(r => ({ title: r.title, url: r.link, snippet: r.snippet || '' }));
} catch { return null; }
}
async function searchBrave(query) {
try {
const resp = await fetch(`https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=5`, {
headers: { 'X-Subscription-Token': BRAVE_KEY, Accept: 'application/json' }
});
const data = await resp.json();
return (data.web?.results || []).map(r => ({ title: r.title, url: r.url, snippet: r.description || '' }));
} catch { return null; }
}
async function search(query) {
for (const [name, fn] of [['scavio', searchScavio], ['brave', searchBrave]]) {
const results = await fn(query);
if (results) { console.log(`Served by ${name}`); return results; }
}
return [];
}
search('AI frameworks 2026').then(r => r.slice(0, 3).forEach(x => console.log(x.title)));Salida esperada
Provider: scavio ($0.005/query)
FastAPI 1.0: The Modern Python Web Framework
https://fastapi.tiangolo.com/release-notes/
Django 6.0 Features and Migration Guide
https://docs.djangoproject.com/en/6.0/
--- Served by: scavio at $0.005/query ---