Tutorial

How to Build AI Agent Without Frameworks

Build a working AI agent in plain Python with no LangChain, no CrewAI. Just requests, a search tool, and an LLM API call.

Most agent tutorials start with LangChain or CrewAI, but you do not need a framework to build a useful agent. This tutorial builds a working search-and-answer agent in plain Python using only requests, an LLM API, and Scavio search. You control every line of code and understand every decision. Total dependencies: requests.

Prerequisites

  • Python 3.8+
  • requests library
  • A Scavio API key from scavio.dev
  • An LLM API key (OpenAI, Anthropic, or similar)

Walkthrough

Step 1: Define the search tool

Create a simple function the agent can call to search the web.

Python
import os, requests, json

API_KEY = os.environ['SCAVIO_API_KEY']
SH = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}

def search(query, n=5):
    """Search the web. Returns list of {title, link, snippet}."""
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SH, json={'query': query, 'country_code': 'us', 'num_results': n}).json()
    return [{'title': r.get('title', ''), 'link': r.get('link', ''),
             'snippet': r.get('snippet', '')} for r in data.get('organic_results', [])[:n]]

def extract(url):
    """Extract page content from URL."""
    data = requests.post('https://api.scavio.dev/api/v1/extract',
        headers=SH, json={'url': url}).json()
    return data.get('content', '')[:2000]

TOOLS = {
    'search': {'fn': search, 'desc': 'Search the web for information'},
    'extract': {'fn': extract, 'desc': 'Extract content from a URL'},
}
print(f'Tools available: {list(TOOLS.keys())}')
results = search('python 3.13 new features')
print(f'Test search: {len(results)} results')

Step 2: Build the agent loop

Create a think-act-observe loop that calls tools until the task is done.

Python
def agent(task, max_steps=5):
    """Plain Python agent: think -> act -> observe -> repeat."""
    memory = [f'Task: {task}']
    cost = 0
    for step in range(1, max_steps + 1):
        print(f'\n--- Step {step} ---')
        # Think: decide what to do based on memory
        context = '\n'.join(memory[-5:])  # Keep last 5 items
        # Simple heuristic: search if no results yet, extract if we have URLs
        if step == 1:
            action = 'search'
            args = {'query': task}
        elif any('http' in m for m in memory) and step == 2:
            # Extract from first URL found
            for m in memory:
                if 'http' in m:
                    url = m.split('http')[1].split()[0]
                    url = 'http' + url
                    action = 'extract'
                    args = {'url': url}
                    break
        else:
            action = 'done'
            args = {}
        if action == 'done':
            print('Agent: Task complete.')
            break
        # Act
        print(f'Action: {action}({json.dumps(args)[:60]})')
        tool = TOOLS.get(action)
        if tool:
            result = tool['fn'](**args)
            cost += 0.005
            observation = json.dumps(result)[:500] if isinstance(result, list) else str(result)[:500]
            memory.append(f'Action: {action} -> {observation[:200]}')
            print(f'Observation: {observation[:100]}...')
        else:
            print(f'Unknown action: {action}')
    print(f'\nTotal cost: ${cost:.3f} ({step} steps)')
    return memory

memory = agent('What are the top Python web frameworks in 2026?')

Step 3: Add structured output and error handling

Make the agent produce a clean final answer with sources.

Python
def robust_agent(task, max_steps=5):
    """Agent with error handling and structured output."""
    results_cache = []
    cost = 0
    errors = 0
    # Step 1: Search
    try:
        search_results = search(task)
        cost += 0.005
        results_cache.extend(search_results)
        print(f'Searched: {len(search_results)} results')
    except Exception as e:
        print(f'Search failed: {e}')
        errors += 1
    # Step 2: Extract top result if available
    if results_cache and results_cache[0].get('link'):
        try:
            content = extract(results_cache[0]['link'])
            cost += 0.005
            print(f'Extracted: {len(content)} chars from {results_cache[0]["link"][:40]}')
        except Exception as e:
            content = ''
            print(f'Extract failed: {e}')
            errors += 1
    else:
        content = ''
    # Step 3: Compile answer
    answer = {
        'task': task,
        'sources': [{'title': r['title'], 'url': r['link']} for r in results_cache[:5]],
        'summary': content[:300] if content else 'See sources below.',
        'cost': cost,
        'errors': errors
    }
    print(f'\n=== Agent Result ===')
    print(f'Sources: {len(answer["sources"])}')
    print(f'Summary: {answer["summary"][:100]}...')
    print(f'Cost: ${cost:.3f} | Errors: {errors}')
    print(f'\nNo frameworks needed. Just Python and an API.')
    return answer

robust_agent('best search api for ai agents 2026')

Python Example

Python
import os, requests
SH = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}

def agent(task):
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SH, json={'query': task, 'country_code': 'us'}).json()
    results = data.get('organic_results', [])[:5]
    print(f'Task: {task}')
    for r in results:
        print(f'  [{r.get("title", "")}]({r.get("link", "")})')
    print(f'Cost: $0.005 | No framework needed')

agent('best python web framework 2026')

JavaScript Example

JavaScript
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
async function agent(task) {
  const data = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST', headers: SH,
    body: JSON.stringify({ query: task, country_code: 'us' })
  }).then(r => r.json());
  console.log(`Task: ${task}`);
  (data.organic_results || []).slice(0, 5).forEach(r => console.log(`  ${r.title}`));
  console.log('Cost: $0.005 | No framework needed');
}
await agent('best python web framework 2026');

Expected Output

JSON
Tools available: ['search', 'extract']
Test search: 5 results

--- Step 1 ---
Action: search({"query": "What are the top Python web frameworks in 2026"})
Observation: [{"title": "Top Python Web Frameworks 2026", "link": "https://...

--- Step 2 ---
Action: extract({"url": "https://realpython.com/top-python-frameworks-2026"})
Observation: FastAPI continues to lead for API development...

--- Step 3 ---
Agent: Task complete.

Total cost: $0.010 (3 steps)

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.8+. requests library. A Scavio API key from scavio.dev. An LLM API key (OpenAI, Anthropic, or similar). 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

Build a working AI agent in plain Python with no LangChain, no CrewAI. Just requests, a search tool, and an LLM API call.