How to make you own Cursor Agent using Python and Open AI (but free).

Have you ever wanted to build your own AI assistant that behaves like a mini operating system—thinking, planning, and executing commands? That’s exactly what this little agent does.
Let’s walk through the code that powers it.
Importing the essentials:
os
: For environment variables.json
: To handle communication with the LLM in structured format.requests
: For making HTTP requests (e.g., for weather).OpenAI
: To interact with the OpenAI-compatible endpoint (here, pointing to Google's Gemini viabase_url
).
Setting up the Client
We read the Gemini API key from environment variables and configure the OpenAI client to use Gemini’s API endpoint. This is flexible—you could also point it to OpenAI or a local model server.
Define Tooling Functions
You’ve got two tools:
run_command
: Executes shell commands.get_weather
: Callswttr.in
to fetch weather data for any city.
These tools are then described in a dictionary, like a tool registry:
System Prompt: Teaching the Agent How to Think
This is the core logic that explains to the agent how to:
Plan the next move,
Decide on which tool to use,
Call the tool via an action,
Wait for the result (observe),
And finally respond to the user.
This kind of multi-turn planning loop is what makes agents powerful and dynamic.
The Interaction Loop
We start with just the system message. Then we enter a loop:
This reads a prompt from the user and sends it to the model.
Then comes the cool part—the agent execution loop:
Step-by-Step Agent Loop
Just showing what the agent is thinking.
If it decides to call a tool, we run it, then send the result back to the LLM with the observe
step.
Once we reach the final step, it breaks the loop and waits for a new user query.
Now that the entire agent is set up and running, try creating a file named sum.py
and ask the AI to either write a function inside that file or prompt the user to check the weather for a specific location (any city of your choice).
The full code is provided below (make sure to follow the earlier steps for clarity).
Don’t forget to add your gemini api key in your .env file
# Automation
import os
import json
import requests
from openai import OpenAI
api_key = os.getenv("GEMINI_API_KEY")
client = OpenAI(
api_key=api_key,
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
)
def query_db(sql):
pass
def run_command(command):
# execute command
# return result
result = os.system(command=command)
return result
def get_weather(city: str):
# TODO: Perform an actual API call
print("🔨 Tool Called: get_weather", city)
url = f"https://wttr.in/{city}?format=%C+%t"
response = requests.get(url)
if response.status_code == 200:
return f"The weather in {city} is {response.text}"
return "Something went wrong"
available_tools = {
"get_weather": {
"fn": get_weather,
"description": "Takes a city name as an input and returns the current weather of that city.",
},
"run_command": {
"fn": run_command,
"description": "Takes a command as input to execute on sustem and returns output.",
},
}
system_prompt = f"""
You are an helpful AI Assistant who is specialized in resolving user query.
You work on start, plan, action, observe mode.
For the given user query and available tools, plan the step by step execution, based on the planning,
select the relevant tool from the available tool. and based on the tool selection you perform an action to call the tool
Wait for the observation and based on the observation from the tool call resolve the user query.
Rules:
- Follow the Output JSON Format.
- Always perform one step at a time and wait for next intput
- Carefully analyse the user query
Output JSON Format:
{{
"step": "string",
"content": "string",
"function": "The name of function if the step is action",
"input": "The input parameter for the function",
}}
Available Tools:
- get_weather: Takes a city name as an input and returns the current weather of that city.
- run_command: Takes a command as input to execute on sustem and returns output.
Example:
User Query: What is the weather of new york?
Output: {{ "step": "plan", "content": "The user is interested in weather data of new york" }}
Output: {{ "step": "plan", "content": "From the available tools I should call get_weather" }}
Output: {{ "step": "action", "function": "get_weather", "input": "new york" }}
Output: {{ "step": "observe", "output": "12 Degree Celcius" }}
Output: {{ "step": "output", "content": "The weather for new york seems to be 12 degrees." }}
"""
messages = [
{ 'role': 'system', 'content': system_prompt },
]
while True:
user_query = input('> ')
messages.append({ 'role': 'user', 'content': user_query })
while True:
response = client.chat.completions.create(
model='gemini-2.0-flash',
response_format={"type": "json_object"},
messages=messages,
)
parsed_output = json.loads(response.choices[0].message.content)
messages.append({ 'role': 'assistant', 'content': json.dumps(parsed_output) })
if parsed_output['step'] == 'plan':
print(f"🧠: {parsed_output.get('content')}")
continue
if parsed_output['step'] == 'action':
tool_name = parsed_output.get('function')
tool_input = parsed_output.get('input')
if available_tools.get(tool_name, False) != False:
output = available_tools[tool_name].get('fn')(tool_input)
messages.append({ 'role': 'assistant', 'content': json.dumps({ 'step': 'observe', 'output': output }) })
continue
if parsed_output['step'] == 'output':
print(f"🤖: {parsed_output.get('content')}")
break
Subscribe to my newsletter
Read articles from Karan Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
