A beginner-friendly clone that lets your AI agent query the web from the command line
Welcome to another official UVF IT build guide! In this tutorial you’ll create a tiny command‑line tool that mimics the core of Agent‑Reach: take a natural‑language query, fetch top web results, and return a concise summary—all with pure Python and free resources.
We’ll scaffold a minimal project, use the public DuckDuckGo instant‑answer API for search, and expose a simple search.py CLI. In ~45 minutes you’ll have a working prototype you can extend for your own AI agents.
pip. The real Agent‑Reach uses paid browser‑automation services; our clone only fetches public instant‑answer data, so results are less rich.Create a fresh folder for your clone, set up an isolated Python virtual environment, and install the single external library we need—requests. This gives you a clean workspace and avoids polluting your system Python.
The virtual environment also makes the later steps reproducible for anyone who follows the guide. Once activated, pip install will pull the library into the .venv directory only.
mkdir agent_reach_clone
cd agent_reach_clone
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install requestsWe now wrap DuckDuckGo’s instant‑answer endpoint in a tiny helper function. The API returns JSON; we extract the Results array, then build a simple list of (title, url) tuples for the top results.
Returning a plain Python list keeps the helper reusable – later the CLI can format or further process the data however it likes.
search_util.py
import requests
from typing import List, Tuple
def duckduckgo_search(query: str, max_results: int = 5) -> List[Tuple[str, str]]:
"""Return a list of (title, url) pairs from DuckDuckGo Instant Answer.
Args:
query: Natural‑language search string.
max_results: Upper bound on how many results to return.
"""
endpoint = "https://api.duckduckgo.com/"
params = {"q": query, "format": "json", "no_redirect": 1, "no_html": 1}
resp = requests.get(endpoint, params=params, timeout=10)
resp.raise_for_status()
data = resp.json()
results = []
for item in data.get("Results", [])[:max_results]:
title = item.get("Text", "").strip()
url = item.get("FirstURL", "").strip()
if title and url:
results.append((title, url))
return resultsIn this step we turn the helper function into a real command‑line tool. The script will accept a search string, forward it to the DuckDuckGo instant answer API, and then format the top results for a human‑friendly display. Keeping the logic small makes it easy to call from other programs or from an AI agent that can execute shell commands.
We use the standard argparse module for argument parsing and requests for the HTTP call. After we receive the JSON payload we extract the title and URL of each result, number them, and print them to stdout. This keeps the output deterministic and easy to pipe into other tools.
search.py
#!/usr/bin/env python3
import argparse, json, sys
import requests
def fetch_results(query, max_results=5):
url = 'https://api.duckduckgo.com/'
params = {'q': query, 'format': 'json', 'no_redirect': 1, 'no_html': 1, 'skip_disambig': 1}
resp = requests.get(url, params=params, timeout=10)
resp.raise_for_status()
data = resp.json()
# DuckDuckGo returns related topics; we flatten them
results = []
for topic in data.get('RelatedTopics', []):
if 'Text' in topic and 'FirstURL' in topic:
results.append((topic['Text'], topic['FirstURL']))
elif 'Topics' in topic:
for sub in topic['Topics']:
if 'Text' in sub and 'FirstURL' in sub:
results.append((sub['Text'], sub['FirstURL']))
if len(results) >= max_results:
break
return results[:max_results]
def main():
parser = argparse.ArgumentParser(description='Simple web search CLI (DuckDuckGo)')
parser.add_argument('query', nargs='+', help='Search terms')
args = parser.parse_args()
query = ' '.join(args.query)
try:
hits = fetch_results(query)
except Exception as e:
sys.stderr.write(f'Error fetching results: {e}\n')
sys.exit(1)
if not hits:
print('No results found.')
return
for i, (title, url) in enumerate(hits, 1):
print(f"{i}. {title}\n {url}\n")
if __name__ == '__main__':
main()Now that the script is ready we make it executable and test it with a sample query. The shebang at the top lets the OS know to run it with Python, and chmod +x gives the file the right permissions. Running the CLI will display the first few hits directly in your terminal.
If you see numbered titles followed by URLs, the wrapper is working and you can start integrating it into larger workflows.
chmod +x search.py
./search.py "Python virtualenv"To be confident the clone behaves as expected, we check that the output contains at least one numbered line with a title and a URL. This simple grep confirms the formatting contract that downstream agents rely on.
From here you can enhance the tool: add a local cache file to avoid repeated API calls, increase max_results, or feed the JSON into a language model for summarisation. The modular design keeps those extensions straightforward.
./search.py "UVF IT" | grep -E "^[0-9]+\. " && echo "✅ Output looks good"| Check | Command / Why it matters |
|---|---|
Virtual environment created and requests installed | source .venv/bin/activate && pip list | grep requests |
search.py runs without error and prints results | ./search.py "Python virtualenv" |
| Output contains at least one numbered result line | ./search.py "UVF IT" | grep -E "^[0-9]+\. " |
You now have a functional web‑search CLI that any AI agent can call. Experiment by swapping DuckDuckGo for another free API, add a simple cache file, or feed the JSON into a language model for a natural‑language summary. Happy hacking!