Enhancing the Chatbot with Contextual Awareness

Posted on in programming

cover image for article

In our previous articles, we've built a basic chatbot using Python and the OpenAI API that can engage in simple conversations. However, you might have noticed that the chatbot sometimes forgets the context of the conversation, leading to disjointed or irrelevant responses. In this article, we'll focus on enhancing our chatbot's contextual awareness, enabling it to remember previous interactions and provide more coherent and meaningful responses. By the end of this tutorial, your chatbot will handle conversations more naturally, much like how humans do.

Why Context Matters in Conversations

Context is the backbone of any meaningful conversation. It allows participants to understand references, maintain the flow, and build upon previous statements.

The Role of Context in Chatbots

  • Improved Relevance: Contextual awareness helps the chatbot provide responses that are directly related to the user's previous inputs.
  • Enhanced User Experience: A context-aware chatbot feels more natural and engaging, improving user satisfaction.
  • Complex Tasks: For tasks like booking appointments or providing personalized recommendations, maintaining context is essential.

Challenges Without Context

  • Repetition: The chatbot may repeat information or ask the same questions multiple times.
  • Confusion: Without context, responses can become irrelevant or confusing.
  • User Frustration: Users may become frustrated if the chatbot doesn't "remember" past interactions.

Understanding Conversation History Management

To maintain context, we need to manage the conversation history effectively.

Techniques for Context Management

  1. Session-Based Storage: Keep track of the conversation within a session that resets when the session ends.
  2. Database Storage: Store conversation data in a database for long-term context retention.
  3. In-Memory Variables: Use data structures like lists or dictionaries to store context during the session.

Choosing the Right Approach

For our chatbot, we'll use in-memory variables to store the conversation history during the session. This approach is suitable for command-line applications and simplifies the implementation.

Modifying API Calls for Contextual Information

The OpenAI API allows us to include conversation history in the prompt, which helps the AI model generate contextually relevant responses.

Revisiting the Prompt Structure

Previously, we formatted the prompt by appending the user's input to the conversation history:

prompt_formatted = "\n".join(context) + "\nChatbot:"

Enhancing the Prompt with Context

We'll include more detailed context by keeping track of both user and chatbot messages.

context.append(f"User: {prompt}")
prompt_formatted = "\n".join(context) + "\nChatbot:"

Example Prompt Sent to OpenAI

User: Hello!
Chatbot: Hi there! How can I assist you today?
User: What's the weather like in New York?
Chatbot:

By providing the conversation history, the AI can generate a response that's relevant to the user's previous questions.

Implementing Contextual Awareness in Our Chatbot

Let's modify our existing chatbot_cli.py script to enhance contextual awareness.

Step 1: Adjust the generate_response Function

We'll improve the function to handle longer conversation histories and manage token limits.

def generate_response(prompt, context):
    # Limit context to maintain token limit
    if len(context) > 10:
        context = context[-10:]
    context.append(f"User: {prompt}")
    prompt_formatted = "\n".join(context) + "\nChatbot:"
    try:
        response = openai.Completion.create(
            engine='text-davinci-003',
            prompt=prompt_formatted,
            max_tokens=150,
            temperature=0.7,
            stop=["User:", "Chatbot:"],
        )
        answer = response.choices[0].text.strip()
        context.append(f"Chatbot: {answer}")
        return answer, context
    except openai.error.OpenAIError as e:
        print(f"An error occurred: {e}")
        return "I'm sorry, but I couldn't process that.", context

Explanation:

  • Context Limiting: We limit the context to the last 10 exchanges to prevent exceeding the token limit imposed by the API.
  • Error Handling: Improved error handling to provide user-friendly messages.

Step 2: Update the Chat Loop

We need to ensure the context is consistently passed between function calls.

def chat():
    print("Welcome to the Chatbot! Type 'help' for a list of commands.")
    context = []
    while True:
        user_input = input("You: ")
        if user_input.lower() in ['exit', 'quit']:
            print("Chatbot: Goodbye!")
            break
        if user_input.lower() == 'help':
            print("Available commands:\n - exit: Quit the chatbot\n - help: Show this help message")
            continue
        if not user_input.strip():
            print("Chatbot: Please say something so I can assist you.")
            continue
        response, context = generate_response(user_input, context)
        print(f"Chatbot: {response}")

Step 3: Testing the Enhanced Chatbot

Run the script and engage in a conversation that tests the chatbot's contextual awareness.

Sample Interaction:

You: Hello
Chatbot: Hi there! How can I help you today?
You: Can you remind me to call John tomorrow?
Chatbot: Sure, I'll remind you to call John tomorrow.
You: What time did we schedule the meeting?
Chatbot: The meeting is scheduled for 10 AM tomorrow.
You: Thanks
Chatbot: You're welcome! Is there anything else I can assist you with?

Observation:

  • The chatbot remembers that a meeting was scheduled, even though we didn't mention it explicitly in the last prompt.
  • This demonstrates improved contextual understanding.

Improving Response Relevance and Coherence

To further enhance the chatbot's responses, we can fine-tune the API parameters and the way we handle context.

Adjusting the API Parameters

  • Temperature: Lowering it slightly can make responses more focused.

    temperature=0.65
    
  • Max Tokens: Ensure it's sufficient for the chatbot to provide detailed responses.

    max_tokens=200
    

Using OpenAI's Chat Completion Endpoint

OpenAI has introduced a Chat Completion endpoint that's designed specifically for conversational applications.

Switching to the Chat Endpoint

First, install the latest OpenAI package:

pip install --upgrade openai

Updating the generate_response Function

def generate_response(prompt, context):
    context.append({"role": "user", "content": prompt})
    try:
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=context,
            temperature=0.7,
        )
        answer = response.choices[0].message['content'].strip()
        context.append({"role": "assistant", "content": answer})
        return answer, context
    except openai.error.OpenAIError as e:
        print(f"An error occurred: {e}")
        return "I'm sorry, but I couldn't process that.", context

Modifying the Context Initialization

In the chat function, initialize context as a list of dictionaries:

context = [{"role": "system", "content": "You are a helpful assistant."}]

Adjusting the Chat Loop

No changes are needed here since we're passing the context correctly.

Benefits of Using the Chat Endpoint

  • Better Context Handling: Designed for multi-turn conversations.
  • Structured Messages: Uses a message list with roles (system, user, assistant).
  • Improved Responses: Generally provides more coherent and contextually appropriate replies.

Managing Conversation Flow

To maintain a smooth conversation flow, consider implementing the following:

Implementing Conversation Reset

Allow the user to reset the conversation context.

Adding a Reset Command

In the chat loop:

if user_input.lower() == 'reset':
    context = [{"role": "system", "content": "You are a helpful assistant."}]
    print("Chatbot: The conversation has been reset.")
    continue

Updating the Help Message

print("Available commands:\n - exit: Quit the chatbot\n - help: Show this help message\n - reset: Reset the conversation context")

Limiting Context Size

To prevent context from growing indefinitely, limit the number of messages stored.

def generate_response(prompt, context):
    context.append({"role": "user", "content": prompt})
    # Keep the last 20 messages
    if len(context) > 20:
        context = [context[0]] + context[-19:]
    # Rest of the function...

Testing with Extended Conversations

Engage in longer conversations to test context retention.

Sample Interaction:

You: What's the capital of France?
Chatbot: The capital of France is Paris.
You: Can you tell me more about it?
Chatbot: Certainly! Paris is known for its art, gastronomy, and culture. It's home to landmarks like the Eiffel Tower and the Louvre Museum.
You: What's the currency used there?
Chatbot: The currency used in France is the Euro (€).
You: reset
Chatbot: The conversation has been reset.
You: Who is the president of the United States?
Chatbot: As of my last update in 2021, the president of the United States is Joe Biden.

Observation:

  • After resetting, the chatbot forgets previous context, as expected.
  • The chatbot maintains context within the limits we've set.

Ethical Considerations in Context Management

When building chatbots, it's essential to consider user privacy and data handling.

Privacy Concerns

  • Sensitive Information: Users may share personal or sensitive data.
  • Data Storage: Avoid storing conversation data unless necessary.

Best Practices

  • Inform Users: Let users know how their data is used.
  • Data Minimization: Store only what's necessary for the conversation.
  • Secure Handling: Ensure that any stored data is protected.

Recommended Tools and Accessories

To aid in developing and testing your chatbot, consider the following tools:

JSON Viewer Extensions

  • VS Code Extensions: Install JSON Viewer or JSON Tools to better visualize JSON data structures.

Debugging Tools

  • Loguru: A logging library that makes logging in Python (stupidly) simple.

    pip install loguru
    
  • Usage:

    from loguru import logger
    
    logger.debug("Debug message")
    

Recommended Reading

"Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow" by Aurélien Géron provides practical guidance on machine learning techniques.

Conclusion

By enhancing our chatbot with contextual awareness, we've significantly improved its ability to engage in meaningful and coherent conversations. We've learned how to manage conversation history, modify API calls to include context, and handle the conversation flow effectively. These improvements make our chatbot more user-friendly and functional.

In the next article, we'll explore customizing the chatbot's personality. We'll adjust its tone and style to make interactions even more engaging and suitable for specific applications. We'll also discuss ethical considerations in personality customization.

For more tutorials and insights on boosting your developer productivity, be sure to check out slaptijack.com.

Part 6 of the Building a Chatbot with Python and OpenAI series

Slaptijack's Koding Kraken