Resumen
Local LLM agents necesita web search pero single-provider setups break cuando el API es down o rate-limited. Este pipeline wraps Scavio as el primary search proveedor con configurable fallback logic: if el primary call fails o times out, it retries once, entonces falls back to un cached resultado o un secondary proveedor. Designed for on-demand usar inside LangChain, LlamaIndex, o raw function-calling agents. Cada search costs one credit ($0.005), y el failover agrega zero cost unless you configurar un paid secondary proveedor.
Desencadenador
On search solicitud de local LLM agent
Programación
On-demand
Pasos del flujo de trabajo
Recibir Search Consulta
Accept el consulta de busqueda y optional parametros (plataforma, pais) de el LLM agent.
Attempt Primary Search
Call Scavio search API con un 10-segundo timeout. Analizar el respuesta y verificar for valid resultados.
Retry on Failure
Si el primary call fails o returns empty, retry once con exponential backoff.
Verificar Local Cache
Si ambos attempts fail, verificar un local SQLite cache for un reciente resultado for el same consulta.
Return Structured Resultados
Format el resultados en un estandar esquema el LLM agent puede analizar: titulo, fragmento, URL, fuente.
Implementacion en Python
import requests, os, json, time, sqlite3
from pathlib import Path
API_KEY = os.environ["SCAVIO_API_KEY"]
SH = {"x-api-key": API_KEY, "Content-Type": "application/json"}
CACHE_DB = "search_cache.db"
def init_cache():
conn = sqlite3.connect(CACHE_DB)
conn.execute("CREATE TABLE IF NOT EXISTS cache (query TEXT PRIMARY KEY, result TEXT, ts REAL)")
conn.commit()
return conn
def scavio_search(query: str, platform: str = "google", retries: int = 1) -> dict | None:
for attempt in range(retries + 1):
try:
resp = requests.post(
"https://api.scavio.dev/api/v1/search",
headers=SH,
json={"query": query, "platform": platform},
timeout=10,
)
resp.raise_for_status()
data = resp.json()
if data.get("organic"):
return data
except Exception as e:
print(f"Attempt {attempt + 1} failed: {e}")
if attempt < retries:
time.sleep(2 ** attempt)
return None
def cached_search(conn: sqlite3.Connection, query: str, max_age: float = 86400) -> dict | None:
row = conn.execute("SELECT result, ts FROM cache WHERE query = ?", (query,)).fetchone()
if row and (time.time() - row[1]) < max_age:
return json.loads(row[0])
return None
def save_cache(conn: sqlite3.Connection, query: str, result: dict):
conn.execute("INSERT OR REPLACE INTO cache VALUES (?, ?, ?)", (query, json.dumps(result), time.time()))
conn.commit()
def resilient_search(query: str, platform: str = "google") -> list:
conn = init_cache()
result = scavio_search(query, platform, retries=1)
if result:
save_cache(conn, query, result)
else:
result = cached_search(conn, query)
if result:
print("Using cached result")
else:
return []
return [
{"title": r.get("title", ""), "snippet": r.get("snippet", ""), "url": r.get("url", ""), "source": platform}
for r in result.get("organic", [])[:5]
]
results = resilient_search("best python web framework 2026")
for r in results:
print(f" {r['title']} - {r['url']}")Implementacion en JavaScript
const SH = {'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json'};
const fs = await import('fs');
const CACHE_FILE = 'search_cache.json';
let cache = {};
try { cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')); } catch {}
async function scavioSearch(query, platform='google', retries=1) {
for (let attempt = 0; attempt <= retries; attempt++) {
try {
const r = await fetch('https://api.scavio.dev/api/v1/search', {method:'POST', headers:SH, body:JSON.stringify({query, platform}), signal:AbortSignal.timeout(10000)});
const data = await r.json();
if (data.organic && data.organic.length > 0) return data;
} catch (e) {
console.log('Attempt '+(attempt+1)+' failed: '+e.message);
if (attempt < retries) await new Promise(r => setTimeout(r, 2**attempt * 1000));
}
}
return null;
}
function cachedSearch(query, maxAge=86400) {
const entry = cache[query];
if (entry && (Date.now()/1000 - entry.ts) < maxAge) return entry.result;
return null;
}
function saveCache(query, result) {
cache[query] = {result, ts: Date.now()/1000};
fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
}
async function resilientSearch(query, platform='google') {
let result = await scavioSearch(query, platform, 1);
if (result) {
saveCache(query, result);
} else {
result = cachedSearch(query);
if (result) console.log('Using cached result');
else return [];
}
return (result.organic || []).slice(0,5).map(r => ({title:r.title||'', snippet:r.snippet||'', url:r.url||'', source:platform}));
}
const results = await resilientSearch('best python web framework 2026');
results.forEach(r => console.log(' '+r.title+' - '+r.url));Plataformas utilizadas
Búsqueda web con grafo de conocimiento, PAA y resúmenes de IA