Donner à un agent IA un accès complet au web génère des résultats bruyants. L'agent gaspille des tokens sur des données non pertinentes et les taux d'hallucination augmentent. Limiter les plateformes et types de résultats auxquels l'agent peut accéder améliore la qualité des réponses, réduit la latence et diminue les coûts. Ce tutoriel construit une couche de recherche limitée qui restreint l'accès aux données de l'agent en fonction du type de tâche. Un agent de comparaison de produits ne voit que Amazon et Walmart. Un agent d'analyse des sentiments ne voit que Reddit. Un agent d'actualités ne voit que Google News.
Prérequis
- Python 3.9+ installé
- bibliothèque requests installée
- Une clé API Scavio depuis scavio.dev
- Compréhension de base de l'appel d'outils d'agent IA
Parcours
Étape 1: Définir des profils de périmètre pour différentes tâches d'agent
Créer des configurations de périmètre prédéfinies qui limitent les plateformes et types de résultats accessibles par un agent. Chaque profil est adapté à un cas d'utilisation spécifique.
import os, requests
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
URL = 'https://api.scavio.dev/api/v1/search'
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
SCOPE_PROFILES = {
'product_research': {
'platforms': ['amazon', 'walmart'],
'max_results': 5,
'description': 'Product pricing and availability'
},
'sentiment': {
'platforms': ['reddit'],
'max_results': 10,
'description': 'Community opinions and discussions'
},
'news': {
'platforms': ['google'],
'query_suffix': ' news 2026',
'max_results': 5,
'description': 'Current news and events'
},
'technical': {
'platforms': ['google'],
'site_restrict': ['stackoverflow.com', 'github.com', 'docs.python.org'],
'max_results': 5,
'description': 'Technical documentation and Q&A'
},
'video': {
'platforms': ['youtube'],
'max_results': 5,
'description': 'Video tutorials and reviews'
}
}
for name, profile in SCOPE_PROFILES.items():
print(f'{name}: {profile["description"]} ({profile["platforms"]})')Étape 2: Construire la fonction de recherche limitée
Une fonction de recherche qui applique le profil de périmètre. Elle refuse de chercher sur des plateformes hors de la liste autorisée et applique les restrictions de requête.
def scoped_search(query: str, scope: str, override_num: int = None) -> list:
"""Search within a defined scope profile."""
profile = SCOPE_PROFILES.get(scope)
if not profile:
return [{'error': f'Unknown scope: {scope}. Available: {list(SCOPE_PROFILES.keys())}'}]
num = override_num or profile['max_results']
all_results = []
for platform in profile['platforms']:
q = query
# Apply query suffix if defined
if 'query_suffix' in profile:
q += profile['query_suffix']
# Apply site restrictions
if 'site_restrict' in profile:
for site in profile['site_restrict']:
site_q = f'site:{site} {q}'
resp = requests.post(URL, headers=H,
json={'query': site_q, 'country_code': 'us', 'num_results': 2})
for r in resp.json().get('organic_results', []):
all_results.append({'title': r['title'], 'url': r['link'],
'snippet': r.get('snippet', ''), 'platform': platform, 'scope': scope})
continue
# Platform-specific site prefix
site_map = {'amazon': 'amazon.com', 'youtube': 'youtube.com',
'walmart': 'walmart.com', 'reddit': 'reddit.com'}
if platform in site_map:
q = f'site:{site_map[platform]} {q}'
resp = requests.post(URL, headers=H,
json={'query': q, 'country_code': 'us', 'num_results': num})
for r in resp.json().get('organic_results', []):
all_results.append({'title': r['title'], 'url': r['link'],
'snippet': r.get('snippet', ''), 'platform': platform, 'scope': scope})
return all_results[:num]
results = scoped_search('noise cancelling headphones', scope='product_research')
print(f'Product scope: {len(results)} results')
for r in results:
print(f' [{r["platform"]}] {r["title"][:60]}')Étape 3: Créer des définitions d'outils sensibles au périmètre pour l'agent
Générer dynamiquement des définitions d'outils en fonction du périmètre actif. L'agent ne voit que les outils de son périmètre assigné, empêchant les recherches hors périmètre.
def make_scoped_tool(scope: str) -> dict:
"""Generate a tool definition locked to a specific scope."""
profile = SCOPE_PROFILES[scope]
return {
'type': 'function',
'function': {
'name': f'search_{scope}',
'description': f'Search for {profile["description"]}. '
f'Platforms: {", ".join(profile["platforms"])}. '
f'Max {profile["max_results"]} results.',
'parameters': {
'type': 'object',
'properties': {
'query': {'type': 'string', 'description': 'Search query'}
},
'required': ['query']
}
}
}
def make_agent_tools(scopes: list[str]) -> list:
"""Create tool set for an agent with specific scopes."""
return [make_scoped_tool(s) for s in scopes]
# Product comparison agent: only Amazon + Walmart
product_tools = make_agent_tools(['product_research'])
print('Product agent tools:', [t['function']['name'] for t in product_tools])
# Research agent: news + technical + sentiment
research_tools = make_agent_tools(['news', 'technical', 'sentiment'])
print('Research agent tools:', [t['function']['name'] for t in research_tools])Étape 4: Ajouter la validation du périmètre et le suivi des coûts
Valider que chaque demande de recherche reste dans le périmètre. Suivre les coûts par périmètre pour identifier les modèles d'accès les plus coûteux.
class ScopedSearchManager:
def __init__(self, allowed_scopes: list[str]):
self.allowed_scopes = set(allowed_scopes)
self.usage = {s: {'calls': 0, 'results': 0} for s in allowed_scopes}
def search(self, query: str, scope: str) -> list:
if scope not in self.allowed_scopes:
raise PermissionError(
f'Scope "{scope}" not allowed. Allowed: {self.allowed_scopes}')
results = scoped_search(query, scope)
self.usage[scope]['calls'] += 1
self.usage[scope]['results'] += len(results)
return results
def report(self) -> str:
lines = ['Scope Usage Report:', '-' * 40]
total_cost = 0
for scope, stats in self.usage.items():
cost = stats['calls'] * 0.005
total_cost += cost
lines.append(f'{scope}: {stats["calls"]} calls, '
f'{stats["results"]} results, ${cost:.3f}')
lines.append(f'Total cost: ${total_cost:.3f}')
return '\n'.join(lines)
# Product comparison agent: restricted scope
agent = ScopedSearchManager(['product_research', 'sentiment'])
results = agent.search('Sony WH-1000XM6', 'product_research')
results = agent.search('Sony WH-1000XM6 review', 'sentiment')
print(agent.report())
# This would raise PermissionError:
# agent.search('sony stock price', 'news')Étape 5: Implémenter l'escalade dynamique du périmètre
Parfois, un agent a besoin d'un périmètre plus large. Ajouter un mécanisme d'escalade contrôlé qui enregistre les changements de périmètre et nécessite une approbation explicite.
class EscalatableScopedSearch(ScopedSearchManager):
def __init__(self, allowed_scopes, escalation_scopes=None):
super().__init__(allowed_scopes)
self.escalation_scopes = set(escalation_scopes or [])
self.escalation_log = []
def request_escalation(self, scope: str, reason: str) -> bool:
"""Request access to a scope outside the default set."""
if scope not in self.escalation_scopes:
print(f'Escalation denied: {scope} not in escalation list')
return False
self.allowed_scopes.add(scope)
self.usage[scope] = {'calls': 0, 'results': 0}
self.escalation_log.append({'scope': scope, 'reason': reason})
print(f'Escalation approved: {scope} ({reason})')
return True
# Agent starts with product scope only
agent = EscalatableScopedSearch(
allowed_scopes=['product_research'],
escalation_scopes=['sentiment', 'news']
)
# Normal search works
results = agent.search('laptop 2026', 'product_research')
print(f'Product search: {len(results)} results')
# Escalate to sentiment when needed
agent.request_escalation('sentiment', 'User asked about community opinions')
results = agent.search('laptop 2026 reddit opinions', 'sentiment')
print(f'Sentiment search: {len(results)} results')
print(f'Escalations: {agent.escalation_log}')Exemple Python
import os, requests
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
URL = 'https://api.scavio.dev/api/v1/search'
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
SCOPES = {
'product': {'sites': ['amazon.com', 'walmart.com'], 'num': 5},
'opinion': {'sites': ['reddit.com'], 'num': 8},
'tech': {'sites': ['stackoverflow.com', 'github.com'], 'num': 5},
}
def scoped_search(query, scope='product'):
cfg = SCOPES[scope]
results = []
for site in cfg['sites']:
resp = requests.post(URL, headers=H,
json={'query': f'site:{site} {query}', 'country_code': 'us', 'num_results': 3})
results.extend(resp.json().get('organic_results', []))
return [{'title': r['title'], 'url': r['link']} for r in results[:cfg['num']]]
for scope in ['product', 'opinion']:
results = scoped_search('mechanical keyboard', scope)
print(f'{scope}: {len(results)} results')Exemple JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
const URL = 'https://api.scavio.dev/api/v1/search';
const SCOPES = {
product: { sites: ['amazon.com', 'walmart.com'], num: 5 },
opinion: { sites: ['reddit.com'], num: 8 },
tech: { sites: ['stackoverflow.com', 'github.com'], num: 5 },
};
async function scopedSearch(query, scope = 'product') {
const cfg = SCOPES[scope];
const results = [];
for (const site of cfg.sites) {
const resp = await fetch(URL, {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query: `site:${site} ${query}`, country_code: 'us', num_results: 3 })
});
results.push(...((await resp.json()).organic_results || []));
}
return results.slice(0, cfg.num).map(r => ({ title: r.title, url: r.link }));
}
(async () => {
for (const scope of ['product', 'opinion']) {
const results = await scopedSearch('mechanical keyboard', scope);
console.log(`${scope}: ${results.length} results`);
}
})();Sortie attendue
Product scope: 5 results
[amazon] Sony WH-1000XM6 Wireless Noise Cancelling Headphones
[walmart] Sony WH-1000XM6 - Best Price Guarantee
Scope Usage Report:
----------------------------------------
product_research: 1 calls, 5 results, $0.005
sentiment: 1 calls, 8 results, $0.005
Total cost: $0.010
Escalation approved: sentiment (User asked about community opinions)Tutoriels associés
- Comment ajouter des données en direct multiplateformes à n'importe quel agent IA
- Comment limiter les serveurs MCP par projet pour réduire l'inflation des tokens
- Comment auditer l'utilisation des jetons MCP et les coûts de description d'outils
- Comment benchmarker les API de recherche pour la qualité RAG