Les pipelines RAG standards récupèrent du contexte depuis un stockage vectoriel, mais ce contexte devient obsolète dès que vos documents sont indexés. Pour les requêtes sur les prix, les événements récents ou les classements actuels, un contexte obsolète conduit à des réponses fausses avec assurance. L'augmentation SERP corrige cela en ajoutant un chemin de récupération parallèle : lorsque la requête semble sensible au temps, récupérez des résultats de recherche en direct et fusionnez-les avec les résultats vectoriels avant de les transmettre au LLM. Ce tutoriel montre comment ajouter l'augmentation SERP à n'importe quel pipeline RAG existant. Chaque appel de recherche coûte $0.005 via l'API Scavio.
Prérequis
- Python 3.9+ installé
- Un pipeline RAG existant (n'importe quel stockage vectoriel)
- bibliothèque requests installée
- Une clé API Scavio depuis scavio.dev
Parcours
Étape 1: Détecter les requêtes sensibles au temps
Construisez un classifieur qui détermine si une requête a besoin de données en direct. Les requêtes concernant les prix, les dates, les versions ou les événements actuels doivent déclencher l'augmentation SERP.
import re
TIME_SIGNALS = [
r'\b202[4-9]\b', r'\blatest\b', r'\bcurrent\b', r'\bprice\b',
r'\bpricing\b', r'\btoday\b', r'\brecent\b', r'\bnew\b',
r'\bversion\b', r'\brelease\b', r'\bupdate\b'
]
def needs_live_data(query: str) -> bool:
query_lower = query.lower()
return any(re.search(p, query_lower) for p in TIME_SIGNALS)
# Examples:
for q in ['What is a transformer?', 'Latest Python version 2026', 'Semrush pricing today']:
print(f'{q}: live_data={needs_live_data(q)}')Étape 2: Construire la fonction de récupération SERP
Créez un récupérateur qui renvoie des documents dans le même format que votre stockage vectoriel, afin qu'ils puissent être fusionnés de manière transparente.
import requests, os
API_KEY = os.environ['SCAVIO_API_KEY']
def serp_retrieve(query: str, k: int = 5) -> list:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us'})
resp.raise_for_status()
results = resp.json().get('organic_results', [])[:k]
return [{
'content': f'{r["title"]}\n{r.get("snippet", "")}',
'source': r['link'],
'retriever': 'serp',
'position': r['position']
} for r in results]Étape 3: Fusionner les résultats vectoriels et SERP
Combinez les résultats des deux récupérateurs, en dédupliquant par URL et en donnant aux résultats SERP un boost de fraîcheur dans le classement.
def merge_results(vector_docs: list, serp_docs: list) -> list:
seen_urls = set()
merged = []
# SERP results first (fresh data priority)
for doc in serp_docs:
url = doc.get('source', '')
if url not in seen_urls:
seen_urls.add(url)
merged.append(doc)
# Then vector results
for doc in vector_docs:
url = doc.get('source', '')
if url not in seen_urls:
seen_urls.add(url)
merged.append(doc)
return merged[:10] # cap at 10 context docs
# Example:
vector_results = [{'content': 'Old pricing data...', 'source': 'https://example.com/old', 'retriever': 'vector'}]
serp_results = serp_retrieve('Semrush pricing 2026')
merged = merge_results(vector_results, serp_results)
for doc in merged:
print(f'[{doc["retriever"]}] {doc["content"][:60]}...')Étape 4: Intégrer dans votre pipeline RAG
Enveloppez la logique d'augmentation dans votre étape de récupération existante. Appelez l'API SERP uniquement lorsque la requête est sensible au temps pour minimiser les coûts.
def augmented_retrieve(query: str, vector_store) -> list:
# Always get vector results
vector_docs = vector_store.similarity_search(query, k=5)
vector_formatted = [{'content': d.page_content, 'source': d.metadata.get('source', ''),
'retriever': 'vector'} for d in vector_docs]
# Conditionally add SERP results
if needs_live_data(query):
serp_docs = serp_retrieve(query, k=5)
return merge_results(vector_formatted, serp_docs)
return vector_formatted
# Usage in your chain:
# docs = augmented_retrieve(user_query, my_vector_store)
# context = '\n\n'.join(d['content'] for d in docs)
# answer = llm(f'Context:\n{context}\n\nQuestion: {user_query}')Exemple Python
import os, re, requests
API_KEY = os.environ['SCAVIO_API_KEY']
TIME_SIGNALS = [r'\b202[4-9]\b', r'\blatest\b', r'\bprice\b', r'\bcurrent\b']
def needs_live_data(query: str) -> bool:
return any(re.search(p, query.lower()) for p in TIME_SIGNALS)
def serp_retrieve(query: str, k: int = 5) -> list:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us'})
return [{'content': f'{r["title"]}\n{r.get("snippet", "")}',
'source': r['link'], 'retriever': 'serp'}
for r in resp.json().get('organic_results', [])[:k]]
def augmented_rag(query: str, vector_docs: list) -> list:
if needs_live_data(query):
serp_docs = serp_retrieve(query)
return serp_docs + vector_docs
return vector_docs
query = 'Semrush pricing 2026'
result = augmented_rag(query, [{'content': 'old data', 'retriever': 'vector'}])
for r in result:
print(f'[{r["retriever"]}] {r["content"][:60]}')Exemple JavaScript
const API_KEY = process.env.SCAVIO_API_KEY;
const TIME_SIGNALS = [/\b202[4-9]\b/i, /\blatest\b/i, /\bprice\b/i, /\bcurrent\b/i];
function needsLiveData(query) {
return TIME_SIGNALS.some(p => p.test(query));
}
async function serpRetrieve(query, k = 5) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: 'us' })
});
const data = await resp.json();
return (data.organic_results || []).slice(0, k)
.map(r => ({ content: `${r.title}\n${r.snippet || ''}`, source: r.link, retriever: 'serp' }));
}
async function augmentedRag(query, vectorDocs) {
if (needsLiveData(query)) {
const serp = await serpRetrieve(query);
return [...serp, ...vectorDocs];
}
return vectorDocs;
}
augmentedRag('Semrush pricing 2026', [{ content: 'old', retriever: 'vector' }])
.then(docs => docs.forEach(d => console.log(`[${d.retriever}] ${d.content.slice(0, 60)}`)));Sortie attendue
What is a transformer?: live_data=False
Latest Python version 2026: live_data=True
Semrush pricing today: live_data=True
[serp] Semrush Pricing Plans 2026 - Complete Breakdown...
[serp] Semrush Review: Is the $139.95/mo Pro Plan Worth It?...
[serp] Semrush vs Ahrefs Pricing Comparison (May 2026)...
[vector] old data