Lesson Introduction

Welcome! Today, we’ll explore a core feature of AI assistants: the interactive chat loop. If you’ve used a chatbot or virtual assistant, you’ve experienced this loop — an ongoing conversation between you and an AI.

Our goal is to build a simple chat loop using Python and the OpenAI API. By the end, you’ll know how to collect user input, send it to the AI, receive a response, and keep the conversation going. This is a key step toward building advanced AI agents.

Understanding the Chat Loop Structure

What is a chat loop? Think of texting a friend: you send a message, get a reply, and respond again. In programming, a chat loop lets a user and an AI assistant interact repeatedly.

In Python, a while loop keeps the conversation running until the user wants to stop:

while True:
    user_input = input("You: ")
    if user_input.lower() == "exit":
        print("Goodbye!")
        break
    print("Assistant: I received your message:", user_input)

Sample output if you type "Hello" and then "exit":

You: Hello
Assistant: I received your message: Hello
You: exit
Goodbye!

This basic loop asks for input and prints a response, stopping only when "exit" is typed. In a real assistant, we’ll send the input to an AI model and show its reply.

Building and Managing Message History

Why track message history? For context. If you ask, “What is AI?” and then, “Can you give an example?” the assistant needs to remember the earlier question.

We keep a list of messages, each a dictionary with a role ("user", "assistant", or "system") and content (the text):

history = []
user_input = "What is AI?"
assistant_response = "AI stands for Artificial Intelligence, which is the simulation of human intelligence by machines."
history.append({"role": "user", "content": user_input})
history.append({"role": "assistant", "content": assistant_response})
print(history)
[{'role': 'user', 'content': 'What is AI?'},
 {'role': 'assistant', 'content': 'AI stands for Artificial Intelligence, which is the simulation of human intelligence by machines.'}]

To guide the assistant’s behavior, add a system message at the start:

system_instruction = "You are a helpful assistant."
messages = [{"role": "system", "content": system_instruction}]
messages.extend(history)
print(messages)
[{'role': 'system', 'content': 'You are a helpful assistant.'},
 {'role': 'user', 'content': 'What is AI?'},
 {'role': 'assistant', 'content': 'AI stands for Artificial Intelligence, which is the simulation of human intelligence by machines.'}]
Building and Managing Message History: Using Functions

Usually, you include only the most recent exchanges to save memory and reduce API costs. Here’s a function to build the message list:

def build_messages(user_input, history=None):
    system_instruction = "You are a helpful assistant."
    messages = [{"role": "system", "content": system_instruction}]
    if history:
        for u, a in history:
            messages.append({"role": "user", "content": u})
            messages.append({"role": "assistant", "content": a})
    messages.append({"role": "user", "content": user_input})
    return messages

# Example usage:
history = [("What is AI?", "AI stands for Artificial Intelligence.")]
user_input = "Give me an example."
print(build_messages(user_input, history))
[{'role': 'system', 'content': 'You are a helpful assistant.'},
 {'role': 'user', 'content': 'What is AI?'},
 {'role': 'assistant', 'content': 'AI stands for Artificial Intelligence.'},
 {'role': 'user', 'content': 'Give me an example.'}]

This prepares the messages for the API.

Making API Calls in the Loop

With our messages ready, we use the OpenAI API’s chat completions endpoint to get a response.

Here’s a function to send messages and get a reply:

from openai import OpenAI

client = OpenAI()

def get_completion(messages, model="gpt-4o", max_tokens=50, temperature=0.7):
    completion = client.chat.completions.create(
        model=model,
        messages=messages,
        max_tokens=max_tokens,
        temperature=temperature
    )
    return completion.choices[0].message.content.strip()

# Example usage (output will vary depending on the model's response):
messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "What is AI?"}
]
response = get_completion(messages)
print(response)

Sample output:

AI stands for Artificial Intelligence, which refers to machines designed to perform tasks that typically require human intelligence.

Key parameters:

  • model: AI model to use (e.g., "gpt-4o").
  • messages: List of system, user, and assistant messages.
  • max_tokens: Maximum length of the reply.
  • temperature: Controls randomness (higher = more creative).

For shorter answers, set max_tokens=30. For more creative replies, try temperature=1.0.

Making API Calls in the Loop: Full Chat Example

Here’s how to use this in a chat loop:

history = []
while True:
    user_input = input("You: ")
    if user_input.lower() == "exit":
        print("Goodbye!")
        break
    messages = build_messages(user_input, history[-1:])
    response = get_completion(messages)
    print("Assistant:", response)
    history.append((user_input, response))

Sample output if you type "What is AI?" and then "exit":

You: What is AI?
Assistant: AI stands for Artificial Intelligence, which refers to machines designed to perform tasks that typically require human intelligence.
You: exit
Goodbye!

This keeps the conversation going, sending each new input and recent history to the AI.

The build_messages function prepares the list of messages to send to the API, combining the system instruction, recent user and assistant exchanges from the history, and the latest user input. This gives the AI enough context to generate a relevant response.

For more context, you could include the entire chat history in each request. However, this can quickly exceed the model’s token limit, especially in long conversations. To avoid this, it often makes sense to include only the most recent few turns (for example, the last 1–3 exchanges) when building the messages list. This balances providing enough context for coherent replies while staying within token constraints.

Handling User Input and Exiting the Loop

A good assistant handles user input well:

  • Recognizes "exit" or "quit" to end the chat.
  • Ignores empty input.
  • Handles errors, such as missing API keys.

Here’s how to add these features:

while True:
    user_input = input("You: ").strip()
    if user_input.lower() in ["quit", "exit"]:
        print("Assistant: Goodbye! It was nice chatting with you.")
        break
    if not user_input:
        print("Please provide a valid input.")
        continue
    # ... (rest of the chat logic)

Sample output if you press Enter, then type "exit":

You: 
Please provide a valid input.
You: exit
Assistant: Goodbye! It was nice chatting with you.

For errors (such as missing API keys), catch and display a helpful message:

try:
    # Chat loop code here
except ValueError as ve:
    print(f"Configuration Error: {ve}")
    print("Please set your OPENAI_API_KEY environment variable.")
except Exception as e:
    print(f"Unexpected error: {e}")

Sample output if an error occurs:

Configuration Error: <error message>
Please set your OPENAI_API_KEY environment variable.

This makes your assistant more robust.

Lesson Summary and Practice Introduction

You’ve learned how to build a simple chat loop for an AI assistant using Python and the OpenAI API. We covered:

  • The structure of a chat loop.
  • Managing message history for context.
  • Sending messages to the API and getting responses.
  • Handling user input and errors.

Next, you’ll practice building and testing your own chat loop, reinforcing these concepts and preparing for more advanced AI agent features. Let’s get started!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal