Tutorial

How to Scope Agent Data Access for Better Results

Narrow your AI agent's search scope to specific platforms and result types for higher quality answers and lower costs. Practical scoping patterns.

Giving an AI agent access to the entire web produces noisy results. The agent wastes tokens on irrelevant data and hallucination rates increase. Scoping which platforms and result types the agent can access improves answer quality, reduces latency, and cuts costs. This tutorial builds a scoped search layer that restricts agent data access based on task type. A product comparison agent only sees Amazon and Walmart. A sentiment agent only sees Reddit. A news agent only sees Google News.

Prerequisites

  • Python 3.9+ installed
  • requests library installed
  • A Scavio API key from scavio.dev
  • Basic understanding of AI agent tool calling

Walkthrough

Step 1: Define scope profiles for different agent tasks

Create predefined scope configurations that restrict which platforms and result types an agent can access. Each profile is tuned for a specific use case.

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'}

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"]})')

Step 2: Build the scoped search function

A search function that enforces the scope profile. It refuses to search platforms outside the allowed list and applies any query restrictions.

Python
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]}')

Step 3: Create scope-aware tool definitions for the agent

Generate tool definitions dynamically based on the active scope. The agent only sees tools for its assigned scope, preventing out-of-scope searches.

Python
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])

Step 4: Add scope validation and cost tracking

Validate that every search request stays within scope. Track per-scope costs to identify which access patterns cost the most.

Python
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')

Step 5: Implement dynamic scope escalation

Sometimes an agent needs broader scope. Add a controlled escalation mechanism that logs scope changes and requires explicit approval.

Python
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}')

Python Example

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')

JavaScript Example

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`);
  }
})();

Expected Output

JSON
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)

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.9+ installed. requests library installed. A Scavio API key from scavio.dev. Basic understanding of AI agent tool calling. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Narrow your AI agent's search scope to specific platforms and result types for higher quality answers and lower costs. Practical scoping patterns.