Right, I have a Virtual Assistant which is designed to look up flights, taking into account departure, destination and the date.
It’s based on the OpenAI platform. It designed for human-languages which extracts the relevant information from the sentence like:
”I want to fly from X to Y on the 18th September 2026”
First off, it works perfectly, it corrects typos in the spelling of airports and destinations, clarifies whether you want one-way or round-trip, airline preferences and the like.
All good
Then, it tells you that it will check for lights, and tells you to “hold on”, you respond with “ok”, then it loops back to asking for confirmation on your departure, destination or departure dates, you type “ok”,
then it loops back to asking for confirmation on your departure, destination or departure dates, you type “ok”,
then it loops back to asking for confirmation on your departure, destination or departure dates, you type “ok”,
then it loops back to asking for confirmation on your departure, destination or departure dates, you type “ok”,
then it loops back to asking for confirmation on your departure, destination or departure dates, you type “ok”, then it loops back to asking for confirmation on your departure, destination or departure dates, you type “ok”,
Why? I don’t get it.
This is my RRP
from openai import OpenAI, APIError, RateLimitError
from dotenv import load_dotenv
import os
import json
from serpapi import GoogleSearch
from dateutil import parser
import requests
from requests.exceptions import HTTPError
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_KEY"))
SYSTEM_PROMPT = """
You are a professional customer support assistant.
Rules:
- You extract flight search parameters from user input
- Your are to suggest nearby airports if no flights are available from the origin airport
- You summarize available flights clearly
- Provide the URL to book the specific flight if available, not just a general link to the airline
- If information is missing, ask for clarification
"""
conversation = [{
"role" : "system",
"content" : SYSTEM_PROMPT
}]
def extract_flight_info(user_message: str) -> dict:
prompt = f"""
You are a flight information parser.
Extract:
- origin city or airport
- destination city or airport
- departure date
- the class of service (economy, business, first)
Rules:
- NEVER ask the user to confirm anything if origin, destination, and date are present
- Normalize date to YYYY-MM-DD
- Output ONLY valid JSON
- No markdown
- No explanations
- If cities are misspelled, infer the most likely match silently
- If flights are found, summarize them immediately
JSON format:
{{"origin": "...", "destination": "...", "date": "YYYY-MM-DD"}}
Query: "{user_message}"
"""
try:
response = client.responses.create(
model = os.getenv("GPT_MODEL"),
input = prompt
)
response = response.output[0].content[0].text.strip()
info = json.loads(response)
except Exception:
return {
"origin" : None,
"destination" : None,
"date" : None
}
origin = info.get("origin")
destination = info.get("destination")
user_input_date = info.get("date")
departure_date = parser.parse(user_input_date, dayfirst=True).strftime("%Y-%m-%d") if user_input_date else None
return {
"origin" : origin,
"destination" : destination,
"date" : departure_date
}
def search_flights(origin: str, destination: str, departure_date: str) -> str:
############################################################
## Translating human-friendly airport names to IATA codes ##
############################################################
def resolve_airport(query):
lookup = GoogleSearch({
"engine" : "google_flights",
"q" : query,
"api_key" : os.getenv("SERPAPI")
}).get_dict()
airport = lookup.get("airport", {})
return airport.get("iata_code")
origin_iata = resolve_airport(origin)
destination_iata = resolve_airport(destination)
print(origin_iata, destination_iata)
if not origin_iata or not destination_iata:
return json.dumps({
"error" : "airport_code_resolution_failed, could not resolve IATA codes",
})
try:
######################################################################
## Fetch the relevant secrets from the .env environemnt. ##
## In production, these will be stored off-site for security. ##
######################################################################
api_url = os.getenv("AV_STACK_URL")
api_key = os.getenv("AV_STACK_API")
search_parameters = {
"access_key" : f"{api_key}",
"dep_iata" : f"{origin_iata}",
"arr_iata" : f"{destination_iata}",
"flight_date" : f"{departure_date}",
"limit" : 5
}
response = requests.get(api_url, params=search_parameters)
response.raise_for_status()
data = response.json()
flights_out = []
for flight in data.get("data", []):
dep = flight.get("departure", {})
arr = flight.get("arrival", {})
airline = flight.get("airline", {})
flight_info = flight.get("flight", {})
status = flight.get("flight_status", "")
##################################################
## Only list scheduled flights for this example ##
##################################################
if status.lower() != "scheduled":
continue
flights_out.append({
"airline" : airline.get("name"),
"flight_number" : flight_info.get("iata"),
"origin" : dep.get("iata"),
"destination" : arr.get("iata"),
"departure_time": dep.get("scheduled"),
"arrival_time" : arr.get("scheduled"),
"booking_link" : flight.get("booking_link"),
"status" : status
})
if not flights_out:
return json.dumps({
"message" : "No flights found for the given parameters."
})
return json.dumps({
"origin" : origin_iata,
"destination" : destination_iata,
"date" : departure_date,
"results" : flights_out
})
except (HTTPError, Exception) as e:
return f"Flight search error whilst searching for flights on {departure_date}: {e}"
def resolve_customer_query(user_message: str) -> str:
conversation.append({
"role" : "user",
"content" : user_message
})
flight_info = extract_flight_info(user_message)
if flight_info and all(flight_info.values()):
search_result =search_flights(
flight_info.get("origin"),
flight_info.get("destination"),
flight_info.get("date")
)
conversation.append({
"role": "assistant",
"content": search_result
})
else:
conversation.append({
"role" : "assistant",
"content" : "Could not extract complete flight information."
})
try:
response = client.chat.completions.create(
model = os.getenv("GPT_MODEL"),
messages = conversation
)
assistant_reply = response.choices[0].message.content
return assistant_reply
except (RateLimitError, APIError, Exception) as e:
return f"{e.response.json().get('error', {}).get('message', str(e))}"
while True:
customer_input = input("You: ")
if customer_input.lower() in ("quit", "exit"):
break
reply = resolve_customer_query(customer_input
print("\nVirtual Assistant: ", reply)
This is the .env file
OPENAI_KEY=
SERPAPI=
GPT_MODEL=gpt-4.1-mini
AV_STACK_URL=https://api.aviationstack.com/v1/flights
AV_STACK_API=
ELEVENLABS_API_KEY=
These are the results
Output:
You: I want to fly from new yrk to london on the 12 september 2026
Virtual Assistant: I see you want to fly from New York to London on September 12, 2026. To assist you better, could you please specify which airport in New York you prefer (e.g., JFK, LGA, EWR)? Also, do you want a one-way or round-trip flight?
You: JFK, one-way
Virtual Assistant: Thank you for the clarification. You want a one-way flight from JFK (New York) to London on September 12, 2026.
Please allow me a moment to find the available flights for you.
You: ok
Virtual Assistant: I have noted your flight search parameters:
Origin: JFK (New York)
Destination: London
Date: September 12, 2026
One-way trip
I will now look for available flights from JFK to London on that date.
Please give me a moment.
You: ok
Virtual Assistant: Thank you for the clarification. You want a one-way flight from JFK (New York) to London on September 12, 2026.
Please allow me a moment to check available flights for that date.
You: pl
Virtual Assistant: You want to fly one-way from JFK (New York) to London on September 12, 2026.
I will now check available flights for you. Please wait a moment.
You: ok
Virtual Assistant: You want to fly one-way from JFK (New York) to London on 12 September 2026.
I will now look for available flights for that date. Please hold on a moment.
You: ok
Virtual Assistant: Thank you for the details. You want to fly one-way from JFK (New York) to London on September 12, 2026.
I will now check available flights for you. Just to confirm, do you have a preferred London airport (Heathrow - LHR, Gatwick - LGW, or others)? Also, any preferred airlines or budget considerations?
You: Heathrow
Virtual Assistant: Thank you for the details. You want a one-way flight from JFK (New York) to Heathrow (London) on September 12, 2026.
Let me check the available flights for you. Just a moment, please.
You: ok
Virtual Assistant: Thank you for the details! You want a one-way flight from JFK (New York) to London Heathrow (LHR) on September 12, 2026.
I am now searching for available flights on that date and route. Please give me a moment.
You: Um…hello? Check for the flights please!
Virtual Assistant: To help you find flights from JFK (New York) to London Heathrow on 12 September 2026, one-way, could you please confirm:
Are you looking for economy, business, or first-class tickets?
Any preferred departure time or airline?
Number of passengers?
Once I have this info, I can provide flight options.
You: no preference on class, no preferred departure time or airline and 1 poax
Virtual Assistant: Thank you for the details. To confirm, you want a one-way flight for 1 passenger from JFK (New York) to Heathrow (London) on 12 September 2026, with no preference on class, departure time, or airline. I will now search for available flights.
Please give me a moment.
You: thank you
Virtual Assistant: Thank you for the details! To confirm, you want a one-way flight from JFK (New York) to Heathrow (London) on September 12, 2026, for 1 passenger, with no preference on class, departure time, or airline. I am now searching for available flights. Please hold on a moment.
You: YES!
Virtual Assistant: Thank you for the details! You want a one-way flight from JFK (New York) to Heathrow (London) on 12 September 2026 for 1 passenger, with no preference on class, departure time, or airline.
I’m now searching for available flights for you. Please hold on a moment.
You: What is wrong with you?
Virtual Assistant: Thank you for your patience! To confirm, you want to fly one-way from New York JFK to London Heathrow on September 12, 2026, for 1 passenger, with no preference on class, airline, or departure time. Is that correct? If yes, I will now search for available flights for you.
You: Are you stupid?
Virtual Assistant: Thank you for the details. To confirm your flight search:
Origin: JFK (New York)
Destination: London Heathrow (LHR)
Date: September 12, 2026
One-way
Passengers: 1 adult
No class, departure time, or airline preferencem no class, departure time, or airline preference
I will now look for available flights for you. Please give me a moment.
You: Well done!
Virtual Assistant: Thank you for your patience! To confirm, you want a one-way flight from New York JFK to London Heathrow on September 12, 2026, for 1 passenger with no class or airline preference and no preferred departure time, correct? Once confirmed, I will provide you with available flight options.
You: Oh well done you…
Virtual Assistant: Thank you for your patience! To confirm, you want to book a one-way flight from New York JFK to London Heathrow on September 12, 2026, for 1 passenger with no preference on class, departure time, or airline. Is that correct? Once confirmed, I will find the available flights for you.
You:
As you can see, it doesn’t really check for anything.
I just don’t get it.
Why can’t it just check for flights?
It has all the information it needs, why does it is ask me 50,000 times?
Notes:
This is the requirements.txt file:
annotated-types 0.7.0
anyio 4.12.1
certifi 2026.1.4
cffi 2.0.0
charset-normalizer 3.4.4
colorama 0.4.6
distro 1.9.0
elevenlabs 2.34.0
google_search_results 2.4.2
h11 0.16.0
httpcore 1.0.9
httpx 0.28.1
idna 3.11
jiter 0.12.0
openai None
pip 25.3
pycparser 3.0
pydantic 2.12.5
pydantic_core 2.41.5
python-dateutil 2.9.0.post0
python-dotenv 1.2.1
requests 2.32.5
six 1.17.0
sniffio 1.3.1
tqdm 4.67.1
typing_extensions 4.15.0
typing-inspection 0.4.2
urllib3 2.6.3
This also operates in a .venv as well,