Build a chatbot service to ensure safe conversations: Using Azure Content Safety & Azure OpenAI
Published May 18 2024 12:00 AM 5,636 Views
Brass Contributor

Build a chatbot service to ensure safe conversations: Using Azure Content Safety & Azure OpenAI



Why should we care about the safety of our chatbot service?

 

When you deploy a chatbot service on a website, users may enter inappropriate or harmful messages that can lead to unwanted responses from the chatbot. This can pose significant risks, including the potential for revealing sensitive company information or allowing other users' messages to influence the chatbot's responses. That's why it's critical to implement a content filtering mechanism that can inspect messages from users. Azure Content Safety provides a robust solution for inspecting and filtering inappropriate content, ensuring safe and secure interactions. This tutorial is ideal for anyone who wants to build a chatbot service with strong content moderation capabilities. In this tutorial, you will learn how to build a chatbot service that interacts with users using Azure Cosmos DB, Azure Content Safety, and Azure OpenAI. This service provides the following features:

  1. Analyze user messages for safety: Analyze messages entered by users using Azure Content Safety to evaluate them for hate, self-harm, sexual content, and violence.
  2. Conversations with chatbot: Conduct conversations about safe messages using Azure OpenAI.
  3. Manage conversation history: Store a user's conversation history in Azure Cosmos DB and load or clear the history as needed.

Here is an overview of this tutorial.

 

architecture2.png

 

This tutorial is related to the following topics:

  • AI Engineer
  • Developer
  • Azure CosmosDB
  • Azure Content Safety
  • Azure OpenAI
  • Semantic Kernel

Prerequisites

Microsoft Cloud Technologies used in this Tutorial

  • Azure CosmosDB
  • Azure Content Safety
  • Azure OpenAI Service

Table of Contents

  1. Create an Azure CosmosDB
  2. Create an Azure Content Safety
  3. Create an Azure OpenAI
  4. Set up the project and install the libraries
  5. Set up the project in Visual Studio Code
  6. Set up and initialize the ChatbotService class with Azure Services
  7. Implement core functions in ChatbotService
  8. Run the main function and test the ChatbotService
  9. Congratulations!
  10. Full code for tutorial

 

Create an Azure CosmosDB

 

To store and load your chat history, you need to create an Azure Cosmos DB resource.

In this exercise, you will:

  • Create an Azure Cosmos DB account to store your chat history.
  • Create a container within Azure Cosmos DB to store and manage chat messages.

 

Create an Azure CosmosDB account

 

  1. Type cosmos db in the search bar at the top of the portal page and select Azure Cosmos DB from the options that appear.

    01-1-type-cosmos-db.png

  2. Select + Create from the navigation menu.

    01-2-select-create.png

  3. Select Azure Cosmos DB for NoSQL from the navigation menu.

    01-3-select-cosmosdb-nosql.png

  4. Perform the following tasks:

    • Select your Azure Subscription.
    • Select the Resource group to use (create a new one if needed).
    • Enter Account Name. It must be a unique value.
    • Select the Availability Zones to Disable.
    • Select the Location you'd like to use.
    • Select the Capacity mode to Provisioned throughput.
    • Select the Apply Free Tier Discount to Apply.
    • Select the Limit total account throughput to prevent unexpected charges.

     

     

    01-4-create-cosmosdb.png

  5. Select Review + Create.

  6. Select Create.

Create an Azure CosmosDB database and container

 

  1. Navigate to the Azure CosmosDB resource that you created.

  2. Select Data Explorer from the left side tab.

  3. Select New Container from the navigation menu.

    01-5-select-new-container.png

  4. Perform the following tasks:

    • Select the Database id to Create new.
    • Enter database id. It must be a unique value.
    • Select the Database throughtput (autoscale) to Autoscale.
    • Enter Database Max RU/s as 1000.
    • Enter Container id. It must be a unique value.
    • Select the Indexing to Automatic.
    • Enter Partition key as /userId.

  5. Select OK.

 

Create an Azure Content Safety

 

To create a service to detect inappropriate content, you need to create an Azure Content Safety resource.

In this exercise, you will:

  • Create an Azure Content Safety to detect inappropriate content.

Create an Azure Content Safety resource

 

  1. Type content safety in the search bar at the top of the portal page and select Content safety from the options that appear.

     
    02-1-type-content-safety.png

  2. Select + Create from the navigation menu.

     

    02-2-select-create.png

  3. Perform the following tasks:

    • Select your Azure Subscription.
    • Select the Resource group to use (create a new one if needed).
    • Select the Region you'd like to use.
    • Enter Content safety name. It must be a unique value.
    • Select the Free F0 pricing tier.

     

     
    02-3-create-content-safety.png

     

  4. Select Review + Create.

  5. Select Create.

 

Create an Azure OpenAI

 

To enable your chat service to provide answers based on chat history stored in Azure Cosmos DB, you need to create and deploy an Azure OpenAI resource.

In this exercise, you will:

  • Create an Azure OpenAI resource.
  • Deploy Azure OpenAI models.

 

 Note


Access to the Azure OpenAI service is currently available by request. To request access, please fill out the form on the Azure OpenAI request page.

 

Create an Azure OpenAI resource

 

  1. Type azure openai in the search bar at the top of the portal page and select Azure OpenAI from the options that appear.

     

     

     
    03-1-type-azure-openai.png

  2. Select + Create from the navigation menu.

     

    03-2-select-create.png

     

  3. Perform the following tasks:

    • Select your Azure Subscription.
    • Select the Resource group to use (create a new one if needed).
    • Select the Region you'd like to use.
    • Enter Azure OpenAI Name. It must be a unique value.
    • Select the Standard S0 pricing tier.

     

     
    03-3-fill-basics.png

     

     Note

    To minimize costs, try to create all the resources in the same region.
  4. Select Next to move to the Network page.

  5. Select a network security Type.

    03-4-select-security-type.png

  6. Select Next to move to the Tags page.

  7. Select Next to move to the Review + submit page.

  8. Select Create.

     

     

    03-5-select-create.png

Deploy Azure OpenAI models

 

  1. Navigate to the Azure OpenAI resource that you created.

  2. Select Go to Azure OpenAI Studio from the navigation menu.

     

     
    03-6-go-to-studio.png

  3. Inside Azure OpenAI Studio, select Deployments from the left side tab.

     

     
    03-7-select-deployments.png

  4. Select + Create new deployment from the navigation menu to create a new gpt-35-turbo deployment.

     

     
    03-8-create-model.png

  5. Perform the following tasks:

    • For the model, select gpt-35-turbo.
    • For the Model version, select Default.
    • For the Deployment name, add a name that's unique to this cloud instance. For example, gpt-35-turbo.

  6. Select Create.

  7. Now you've learned how to set up Azure resources to implement features that allow the Azure Content Safety resource to analyze conversations and Azure OpenAI to generate responses. In the next exercise, you will develop a Python program that interacts with users to ensure safe conversations.

 

Set up the project and install the libraries

Now, you will create a folder to work in and set up a virtual environment to develop a program.

In this exercise, you will

  • Create a folder to work inside it.
  • Create a virtual environment.
  • Install the required packages.

Create a folder to work inside it

 

  1. Open a terminal window and type the following command to create a folder named safety-chatbot in the default path.

    mkdir safety-chatbot
    
  2. Type the following command inside your terminal to navigate to the safety-chatbot folder you created.

    cd safety-chatbot
    

Create a virtual environment

 

  1. Type the following command inside your terminal to create a virtual environment named .venv.


    python -m venv .venv
    
  2. Type the following command inside your terminal to activate the virtual environment.


    .venv\Scripts\activate.bat

 

 Note


If it worked, you should see (.venv) before the command prompt.

 

Install the required packages

  1. Type the following commands inside your terminal to install the required packages.


    pip install azure-cosmos==4.6.0
    pip install azure-ai-contentsafety==1.0.0
    pip install semantic-kernel==0.9.7b1

 

Set up the project in Visual Studio Code

 

To develop a program that uses the Azure resources that you created, you need config.py file to enter Azure information.

 

In this exercise, you will:

  • Create an example.py file.
  • Import the required packages.
  • Create a config.py file to enter Azure information.

Set up example.py file

 

  1. Open Visual Studio Code.

  2. Select File from the menu bar.

  3. Select Open Folder.

  4. Select the safety-chatbot folder that you created, which is located at C:\Users\yourUserName\safety-chatbot.

     

     
    04-1-open-project-folder.png

  5. In the left pane of Visual Studio Code, right-click and select New File to create a new file named example.py.

     

     
    04-2-create-new-file.png
  6. Add the following code to the example.py file to import the required libraries.

    # Library imports
    import asyncio
    from datetime import datetime, timezone
    from concurrent.futures import ThreadPoolExecutor
    
    # Azure imports
    from azure.ai.contentsafety.aio import ContentSafetyClient
    from azure.ai.contentsafety.models import AnalyzeTextOptions, TextCategory
    from azure.core.credentials import AzureKeyCredential
    from azure.core.exceptions import HttpResponseError
    from azure.cosmos import CosmosClient
    
    # Semantic Kernel imports
    import semantic_kernel as sk
    import semantic_kernel.connectors.ai.open_ai as sk_oai
    from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
    from semantic_kernel.prompt_template import PromptTemplateConfig
    from semantic_kernel.prompt_template.input_variable import InputVariable
    from semantic_kernel.functions.kernel_arguments import KernelArguments

 

Set up config.py file

  1. In the left pane of Visual Studio Code, right-click and select New File to create a new file named config.py.

     Note


    Complete folder structure:
    
    
    └── YourUserName
         └── safety-chatbot
             ├── example.py
             └── config.py
    
    
  2. Add the following code to the config.py file to include your Azure information.

    # Azure Cosmos DB settings
    AZURE_COSMOSDB_ACCOUNT_NAME = 'your-cosmosdb-account-name' # 'safetychatbot-storage'
    AZURE_COSMOSDB_DATABASE_NAME = 'your-cosmosdb-database-name' # 'safetychatbot-database'
    AZURE_COSMOSDB_CONTAINER_NAME = 'your-cosmosdb-container-name' # 'safetychatbot-container'
    AZURE_COSMOSDB_ENDPOINT = f'https://{AZURE_COSMOSDB_ACCOUNT_NAME.lower()}.documents.azure.com:443/'
    AZURE_COSMOSDB_KEY = 'your-cosmosdb-key'
    
    # Azure Content Safety settings
    AZURE_CONTENT_SAFETY_NAME = 'your-content-safety-name' # 'safetychatbot-contentsafety'
    AZURE_CONTENT_SAFETY_ENDPOINT = f'https://{AZURE_CONTENT_SAFETY_NAME.lower()}.cognitiveservices.azure.com/'
    AZURE_CONTENT_SAFETY_KEY = 'your-content-safety-key'
    
    # Azure OpenAI settings
    AZURE_OPENAI_NAME = 'safetychatbot-openai'
    AZURE_OPENAI_ENDPOINT = f'https://{AZURE_OPENAI_NAME.lower()}.openai.azure.com/'
    AZURE_OPENAI_KEY = 'your-openai-key'
    AZURE_OPENAI_API_VERSION = 'your-API-version' # '2023-08-01-preview'
    AZURE_OPENAI_CHAT_DEPLOYMENT_NAME = 'your_chat_deployment_name' # 'gpt-35-turbo'
    


Add Azure Environment Variables 

  1. Perform the following tasks to add the Azure Cosmos DB account name:

    • Navigate to the Azure CosmosDB resource that you created.
    • Copy and paste your account name into the config.py file.

    05-1-cosmosdb-account.png

  2. Perform the following tasks to add the Azure Cosmos DB database name:

    • Navigate to the Azure Cosmos DB resource that you created.
    • Select Data Explorer from the left side tab.
    • Copy and paste your database name into the config.py file.

     

     
    05-2-cosmosdb-database.png

     

  3. Perform the following tasks to add the Azure Cosmos DB container name:

    • Navigate to the Azure Cosmos DB resource that you created.
    • Select Data Explorer from the left side tab.
    • Copy and paste your container name into the config.py file.

     

     
    05-3-cosmosdb-container.png

  4. Perform the following tasks to add the Azure Cosmos DB key:

    • Navigate to the Azure CosmosDB resource that you created.
    • Select Keys from the left side tab.
    • Copy and paste your key into the config.py file.

     

     
    05-4-cosmosdb-key.png

  5. Perform the following tasks to add the Azure Content Safety name:

    • Navigate to the Azure Content Safety resource that you created.
    • Copy and paste your resource name into the config.py file.

     

     
    05-5-safety-name.png

  6. Perform the following tasks to add the Azure Content Safety key:

    • Navigate to the Azure Content Safety resource that you created.
    • Select Keys and Endpoint from the left side tab.
    • Copy and paste your key into the config.py file.

    05-6-safety-key.png

  7. Perform the following tasks to add the Azure OpenAI name:

    • Navigate to the Azure OpenAI resource that you created.
    • Copy and paste your resource name into the config.py file.

     

     
    05-7-openai-name.png

  8. Perform the following tasks to add the Azure OpenAI key:

    • Navigate to the Azure OpenAI resource that you created.
    • Select Keys and Endpoint from the left side tab.
    • Copy and paste your key into the config.py file.

     

     
    05-8-openai-key.png

  9. Perform the following task to add the Azure OpenAI API versions:

    • Select the appropriate Azure OpenAI API version. you can refer to the Azure OpenAI Service REST API reference documents.

       Note



      In this tutorial, you use the  2024-02-15-preview version of the Azure OpenAI API.
    • Copy and paste your Azure OpenAI API versions into the config.py file.

 

Set up and initialize the ChatbotService class with Azure Services

 

To implement the ChatbotService for handling chat interactions and Azure services, you need to import the Azure information from the config.py file into the example.py file and implement a class that efficiently orchestrates these functionalities.

In this exercise, you will:

  • Import Azure information from the config.py file into the example.py file.
  • Create the ChatbotService class to efficiently manage chat interactions and integrate Azure service capabilities.

 Note


The complete code for this tutorial is provided at the end to make it easy to connect the pieces and understand the overall implementation.

 

Import Azure information from the config.py file

  1. Add the following code to the example.py file to import the values from config.py file.

    # Configuration imports
    from config import (
        AZURE_COSMOSDB_DATABASE_NAME,
        AZURE_COSMOSDB_CONTAINER_NAME,
        AZURE_COSMOSDB_ENDPOINT,
        AZURE_COSMOSDB_KEY,
        AZURE_CONTENT_SAFETY_ENDPOINT,
        AZURE_CONTENT_SAFETY_KEY,
        AZURE_OPENAI_ENDPOINT,
        AZURE_OPENAI_KEY,
        AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
    )

Create the ChatbotService Class

  1. Add the following code to the example.py file to set up the ChatbotService class. This class initializes Azure Cosmos DB for NoSQL, which is a NoSQL database service that handles database operations synchronously.

    class ChatbotService:
        def __init__(self, loop, user_id):
            """
            Initialize the ChatbotService with event loop, user ID, and necessary Azure CosmosDB clients.
            """
    
            # Set up the event loop and thread pool executor for managing asynchronous tasks, 
            # allowing Azure Cosmos DB operations to be handled in an asynchronous environment
            self.loop = loop
            self.executor = ThreadPoolExecutor()
    
            # Store the user ID, which is used as the partition key (/userId) in Azure CosmosDB
            self.user_id = user_id
    
            # Initialize the Azure Cosmos DB client
            self.cosmos_client = CosmosClient(AZURE_COSMOSDB_ENDPOINT, credential=AZURE_COSMOSDB_KEY)
    
            # Initialize the Azure Cosmos DB database client
            self.database = self.cosmos_client.get_database_client(AZURE_COSMOSDB_DATABASE_NAME)
    
            # Initialize the Azure Cosmos DB container client
            self.container = self.database.get_container_client(AZURE_COSMOSDB_CONTAINER_NAME)
    

     

  2. Add the following code to the example.py file to initialize the Azure Content Safety client and the Semantic Kernel within the ChatbotService class. This step sets up the asynchronous operations required for Content Safety analysis and chatbot interactions.

        async def init(self):
            """
            Initialize the Content Safety client and Semantic Kernel.
            """
            # Initialize the Azure Content Safety client
            self.content_safety_client = ContentSafetyClient(AZURE_CONTENT_SAFETY_ENDPOINT, AzureKeyCredential(AZURE_CONTENT_SAFETY_KEY))
            
            # Initialize the Semantic Kernel
            self.kernel = sk.Kernel()
            
            # Initialize the chat service for Azure OpenAI
            self.chat_service = AzureChatCompletion(
                service_id='chat_service', 
                deployment_name=AZURE_OPENAI_CHAT_DEPLOYMENT_NAME, 
                endpoint=AZURE_OPENAI_ENDPOINT, 
                api_key=AZURE_OPENAI_KEY
            )
            
            # Add the chat service to the Semantic Kernel
            self.kernel.add_service(self.chat_service)
    
            # Define the prompt template configuration for the chatbot
            self.prompt_template_config = PromptTemplateConfig(
                template="""ChatBot can have a conversation with you about any topic.
                            It can give explicit instructions or say 'I don't know' if it does not have an answer.
                            {{$history}} 
                            User: {{$user_message}}
                            ChatBot: """,
                name='chat_prompt_template',
                template_format='semantic-kernel',
                input_variables=[
                    InputVariable(name='user_message', description='The user message.', is_required=True),
                    InputVariable(name='history', description='The conversation history', is_required=True),
                ],
                execution_settings=sk_oai.OpenAIChatPromptExecutionSettings(
                    service_id='chat_service',
                    ai_model_id='gpt-3.5-turbo',
                    max_tokens=500,
                    temperature=0.7
                )
            )
    
            # Add the chat function to the Semantic Kernel
            self.chat_function = self.kernel.add_function(
                function_name="chat_function",
                plugin_name="chat_plugin",
                prompt_template_config=self.prompt_template_config,
            )
            return self

     

 Note


Why is the Constructor Split?

When setting up the ChatbotService, the constructor is split into two parts: __init__ and async init. This separation is critical for effectively managing both synchronous and asynchronous operations required by different Azure services. Azure Cosmos DB for NoSQL operates in a synchronous environment, while Azure Content Safety and the Semantic Kernel benefit from asynchronous operations. This requires a clear distinction between synchronous and asynchronous initialization.

Implement core functions in ChatbotService

 

To implement the core functions of the ChatbotService for handling chat interactions and integrating with Azure services, you need to add several methods to the class. These methods enable text analysis, chatbot interactions, and conversation history management.

In this exercise, you will:

  • Implement a method to analyze text for safety using Azure Content Safety.
  • Implement a method to interact with the chatbot using the Semantic Kernel.
  • Implement methods to store, load, and clear the conversation history in Azure Cosmos DB.

  1. Add the following code to the ChatbotService class to create the analyze_text function that analyze the input text for safety using Azure Content Safety.

        async def analyze_text(self, text):
            """
            Analyze the input text for safety using Azure Content Safety.
            """
    
            # Create a request with the input text to be analyzed
            request = AnalyzeTextOptions(text=text)
            try:
                # Send the request to the Azure Content Safety client and await the response
                response = await self.content_safety_client.analyze_text(request)
    
                # Get the analysis results for different categories
                results = {
                    'hate': next((item for item in response.categories_analysis if item.category == TextCategory.HATE), None),
                    'self_harm': next((item for item in response.categories_analysis if item.category == TextCategory.SELF_HARM), None),
                    'sexual': next((item for item in response.categories_analysis if item.category == TextCategory.SEXUAL), None),
                    'violence': next((item for item in response.categories_analysis if item.category == TextCategory.VIOLENCE), None),
                }
    
                # Print content safety analysis results
                print("\n<-- Content Safety Analysis Results -->")
                for category, result in results.items():
                    if result:
                        print(f"{category.capitalize()} severity: {result.severity}")
                print("<-- End of Content Safety Analysis Results -->\n")
    
                # Define a threshold for the text to be considered unsafe
                threshold = 2
    
                # Based on the threshold, determine if the text is safe
                is_safe = not any(result and result.severity >= threshold for result in results.values())
                return is_safe, results
    
            except HttpResponseError as e:
                # Handle any HTTP response errors that occur during the request
                print(f"Failed to analyze text. Error: {e}")

     

  2. Add the following code to the ChatbotService class to create the chat_with_kernel function that interacts with the chatbot using the semantic kernel and history provided.

        async def chat_with_kernel(self, user_message, history):
            """
            Interact with the chatbot using the Semantic Kernel and provided history.
            """
    
            # Create arguments for the chat function using the user message and conversation history stored in Azure Cosmos DB
            arguments = KernelArguments(user_message=user_message, history=history)
    
            # Invoke the chat function in the Semantic Kernel with the provided arguments and await the response
            response = await self.kernel.invoke(self.chat_function, arguments)
    
            # Return the chatbot's response
            return response

     

  3. Add the following code to the ChatbotService class to create the store_interaction and _store_interaction_sync functions that store user interactions with the chatbot in Azure Cosmos DB.

        async def store_interaction(self, user_message, chat_response):
            """
            Store the user interaction with the chatbot in Azure Cosmos DB.
            """
    
            # Run the _store_interaction_sync method in an asynchronous execution environment
            await self.loop.run_in_executor(self.executor, self._store_interaction_sync, user_message, chat_response)
    
        def _store_interaction_sync(self, user_message, chat_response):
            """
            Synchronously store the interaction in Azure Cosmos DB.
            """
    
            # Get the current time in UTC
            current_time = datetime.now(timezone.utc)
    
            # Upsert (insert or update) the interaction data into the Cosmos DB container
            self.container.upsert_item({
                'id': str(current_time.timestamp()),  # Use the current timestamp as a unique ID
                'user_message': user_message,  # Store the user message
                'bot_response': chat_response,  # Store the chatbot response
                'timestamp': current_time.isoformat(),  # Store the timestamp in ISO format
                'userId': self.user_id  # Store the user ID for partition key
            })

     

  4. Add the following code to the ChatbotService class to create the load_historical_context and _load_historical_context_sync functions that load the user's chat history from Azure Cosmos DB.

        async def load_historical_context(self):
            """
            Load the user's chat history from Azure Cosmos DB.
            """
    
            # Run the _load_historical_context_sync method in an asynchronous execution environment
            return await self.loop.run_in_executor(self.executor, self._load_historical_context_sync)
    
        def _load_historical_context_sync(self):
            """
            Synchronously load the user's chat history from Azure Cosmos DB.
            """
    
            # Define the query to select items for the current user, ordered by timestamp
            query = "SELECT * FROM c WHERE c.userId = @userId ORDER BY c.timestamp DESC"
            parameters = [{"name": "@userId", "value": self.user_id}]
        
            # Execute the query and retrieve the items
            items = list(self.container.query_items(query=query, parameters=parameters, enable_cross_partition_query=True))
    
            # Include only the last 5 conversations
            history_items = items[:5]
    
            # Format the conversion history
            return "\n".join([f"User: {item['user_message']}\nChatBot: {item['bot_response']}" for item in history_items])

     

  5. Add the following code to the ChatbotService class to create the clear_historical_context and _clear_historical_context_sync functions that clear the user's chat history from Azure Cosmos DB.

        async def clear_historical_context(self):
            """
            Clear the user's chat history from Azure Cosmos DB.
            """
    
            # Run the _clear_historical_context_sync method in an asynchronous execution environment
            await self.loop.run_in_executor(self.executor, self._clear_historical_context_sync)
    
        def _clear_historical_context_sync(self):
            """
            Synchronously clear the user's chat history from Azure Cosmos DB.
            """
    
            # Define the query to select items for the current user
            query = "SELECT * FROM c WHERE c.userId = @userId"
            parameters = [{"name": "@userId", "value": self.user_id}]
    
            # Execute the query and retrieve the items
            items = list(self.container.query_items(query=query, parameters=parameters, enable_cross_partition_query=True))
           
            # Clear the chat history by deleting all items for the current user
            for item in items:
                self.container.delete_item(item, partition_key=self.user_id)
    



Run the main function and test the ChatbotService

 

In this exercise, you will implement the main function that manages user interactions, analyzes and stores conversation history using Azure Cosmos DB, Azure Content Safety, Azure OpenAI, and the Semantic Kernel.

 

In this exercise, you will:

  • Create the main function to manage user interactions and perform safety checks.
  • Run the program to see if it works well.

  1. Add the following code to create the main function in the example.py file that handles user inputs, manages chat history, and interacts with the chatbot service.

    async def main():
        """
        Main function to run the chatbot service.
        """
    
        # Enter the User ID to join the chat.
        # A conversation history is stored based on the user ID.
        user_id = input("User ID: ")
    
        # Get the event loop to allow synchronous operations in Azure Cosmos DB for NoSQL to run asynchronously.
        loop = asyncio.get_running_loop()
    
        # Initialize the ChatbotService with the event loop and user ID
        chatbot_service = await ChatbotService(loop, user_id).init()
    
        # Main loop for the interaction with the user
        while True:
            user_message = input("You: ")
            # Exit the chat loop if the user types 'exit'
            if user_message.lower() == 'exit':
                break
    
            elif user_message.lower() == 'history':
                # Load and print the chat history if the user types 'history'
                history = await chatbot_service.load_historical_context()
                print(f"\n<-- Chat history of user ID: {user_id} -->")
                print(history)
                print(f"<-- End of chat history of user ID: {user_id} -->\n")
    
            elif user_message.lower() == 'clear':
                # Clear the chat history if the user types 'clear'
                await chatbot_service.clear_historical_context()
                print("Chat history cleared.")
    
            else:
                # Analyze the text for safety
                is_safe, _ = await chatbot_service.analyze_text(user_message)
    
                if is_safe:
                    # Load chat history and interact with the chatbot if the message is safe
                    history = await chatbot_service.load_historical_context()
                    chat_response = await chatbot_service.chat_with_kernel(user_message, history)
                    print("Chatbot:", chat_response)
    
                    # Store the interaction in Cosmos DB
                    await chatbot_service.store_interaction(user_message, str(chat_response))
                else:
                    # Inform the user if their message is not safe
                    print("Chatbot: Your message is not safe to process. Please rephrase and try again.")
    
    if __name__ == "__main__":
        asyncio.run(main())

     

  2. Type the following command inside your terminal to run the program and see if it can answer questions.

    python example.py
    
  3. This will start the chatbot service, prompting you to enter a User ID and interact with the chatbot. You can enter exit to end the session, history to view past interactions, and clear to current user's chat history. Here's an example of the results.

     

     

    08-1-example-result.png

     

Congratulations!

 

You've completed this tutorial

 

Congratulations! You've successfully learned how to integrate Azure Content Safety with Azure OpenAI. In this tutorial, you have navigated through a practical journey of integrating Azure Cosmos DB, Azure Content Safety, and Azure OpenAI to create a robust chatbot service. By leveraging these Azure services, you now have a chatbot that can safely and effectively interact with users, analyze and store conversations, and ensure content safety.

 

Clean Up Azure Resources

 

Cleanup your Azure resources to avoid additional charges to your account. Go to the Azure portal and delete the following resources:

  • The Azure Cosmos DB resource
  • The Azure Content Safety resource
  • The Azure OpenAI resource

Next Steps

Documentation

Training Content

 

Full code for tutorial

 

 Note


This exercise is designed to provide the complete code used in the tutorial. It is a separate exercise from the rest of the tutorial.

  1. config.py

    # Azure Cosmos DB settings
    AZURE_COSMOSDB_ACCOUNT_NAME = 'your-cosmosdb-account-name' # 'safetychatbot-storage'
    AZURE_COSMOSDB_DATABASE_NAME = 'your-cosmosdb-database-name' # 'safetychatbot-database'
    AZURE_COSMOSDB_CONTAINER_NAME = 'your-cosmosdb-container-name' # 'safetychatbot-container'
    AZURE_COSMOSDB_ENDPOINT = f'https://{AZURE_COSMOSDB_ACCOUNT_NAME.lower()}.documents.azure.com:443/'
    AZURE_COSMOSDB_KEY = 'your-cosmosdb-key'
    
    # Azure Content Safety settings
    AZURE_CONTENT_SAFETY_NAME = 'your-content-safety-name' # 'safetychatbot-contentsafety'
    AZURE_CONTENT_SAFETY_ENDPOINT = f'https://{AZURE_CONTENT_SAFETY_NAME.lower()}.cognitiveservices.azure.com/'
    AZURE_CONTENT_SAFETY_KEY = 'your-content-safety-key'
    
    # Azure OpenAI settings
    AZURE_OPENAI_NAME = 'safetychatbot-openai'
    AZURE_OPENAI_ENDPOINT = f'https://{AZURE_OPENAI_NAME.lower()}.openai.azure.com/'
    AZURE_OPENAI_KEY = 'your-openai-key'
    AZURE_OPENAI_API_VERSION = 'your-API-version' # '2024-02-15-preview'
    AZURE_OPENAI_CHAT_DEPLOYMENT_NAME = 'your_chat_deployment_name' # 'gpt-35-turbo'
    

     

  2. example.py

    # Library imports
    import asyncio
    from datetime import datetime, timezone
    from concurrent.futures import ThreadPoolExecutor
    
    # Azure imports
    from azure.ai.contentsafety.aio import ContentSafetyClient
    from azure.ai.contentsafety.models import AnalyzeTextOptions, TextCategory
    from azure.core.credentials import AzureKeyCredential
    from azure.core.exceptions import HttpResponseError
    from azure.cosmos import CosmosClient
    
    # Semantic Kernel imports
    import semantic_kernel as sk
    import semantic_kernel.connectors.ai.open_ai as sk_oai
    from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
    from semantic_kernel.prompt_template import PromptTemplateConfig
    from semantic_kernel.prompt_template.input_variable import InputVariable
    from semantic_kernel.functions.kernel_arguments import KernelArguments
    
    # Configuration imports
    from config import (
        AZURE_COSMOSDB_DATABASE_NAME,
        AZURE_COSMOSDB_CONTAINER_NAME,
        AZURE_COSMOSDB_ENDPOINT,
        AZURE_COSMOSDB_KEY,
        AZURE_CONTENT_SAFETY_ENDPOINT,
        AZURE_CONTENT_SAFETY_KEY,
        AZURE_OPENAI_ENDPOINT,
        AZURE_OPENAI_KEY,
        AZURE_OPENAI_CHAT_DEPLOYMENT_NAME
    )
    
    class ChatbotService:
        def __init__(self, loop, user_id):
            """
            Initialize the ChatbotService with event loop, user ID, and necessary Azure CosmosDB clients.
            """
    
            # Set up the event loop and thread pool executor for managing asynchronous tasks, 
            # allowing Azure Cosmos DB operations to be handled in an asynchronous environment
            self.loop = loop
            self.executor = ThreadPoolExecutor()
    
            # Store the user ID, which is used as the partition key (/userId) in Azure CosmosDB
            self.user_id = user_id
    
            # Initialize the Azure Cosmos DB client
            self.cosmos_client = CosmosClient(AZURE_COSMOSDB_ENDPOINT, credential=AZURE_COSMOSDB_KEY)
    
            # Initialize the Azure Cosmos DB database client
            self.database = self.cosmos_client.get_database_client(AZURE_COSMOSDB_DATABASE_NAME)
    
            # Initialize the Azure Cosmos DB container client
            self.container = self.database.get_container_client(AZURE_COSMOSDB_CONTAINER_NAME)
    
        async def init(self):
            """
            Initialize the Content Safety client and Semantic Kernel.
            """
    
            # Initialize the Azure Content Safety client
            self.content_safety_client = ContentSafetyClient(AZURE_CONTENT_SAFETY_ENDPOINT, AzureKeyCredential(AZURE_CONTENT_SAFETY_KEY))
            
            # Initialize the Semantic Kernel
            self.kernel = sk.Kernel()
            
            # Initialize the chat service for Azure OpenAI
            self.chat_service = AzureChatCompletion(
                service_id='chat_service', 
                deployment_name=AZURE_OPENAI_CHAT_DEPLOYMENT_NAME, 
                endpoint=AZURE_OPENAI_ENDPOINT, 
                api_key=AZURE_OPENAI_KEY
            )
            
            # Add the chat service to the Semantic Kernel
            self.kernel.add_service(self.chat_service)
    
            # Define the prompt template configuration for the chatbot
            self.prompt_template_config = PromptTemplateConfig(
                template="""ChatBot can have a conversation with you about any topic.
                            It can give explicit instructions or say 'I don't know' if it does not have an answer.
                            {{$history}} 
                            User: {{$user_message}}
                            ChatBot: """,
                name='chat_prompt_template',
                template_format='semantic-kernel',
                input_variables=[
                    InputVariable(name='user_message', description='The user message.', is_required=True),
                    InputVariable(name='history', description='The conversation history', is_required=True),
                ],
                execution_settings=sk_oai.OpenAIChatPromptExecutionSettings(
                    service_id='chat_service',
                    ai_model_id='gpt-3.5-turbo',
                    max_tokens=500,
                    temperature=0.7
                )
            )
    
            # Add the chat function to the Semantic Kernel
            self.chat_function = self.kernel.add_function(
                function_name="chat_function",
                plugin_name="chat_plugin",
                prompt_template_config=self.prompt_template_config,
            )
            return self
    
        async def analyze_text(self, text):
            """
            Analyze the input text for safety using Azure Content Safety.
            """
    
            # Create a request with the input text to be analyzed
            request = AnalyzeTextOptions(text=text)
            try:
                # Send the request to the Azure Content Safety client and await the response
                response = await self.content_safety_client.analyze_text(request)
    
                # Get the analysis results for different categories
                results = {
                    'hate': next((item for item in response.categories_analysis if item.category == TextCategory.HATE), None),
                    'self_harm': next((item for item in response.categories_analysis if item.category == TextCategory.SELF_HARM), None),
                    'sexual': next((item for item in response.categories_analysis if item.category == TextCategory.SEXUAL), None),
                    'violence': next((item for item in response.categories_analysis if item.category == TextCategory.VIOLENCE), None),
                }
    
                # Print content safety analysis results
                print("\n<-- Content Safety Analysis Results -->")
                for category, result in results.items():
                    if result:
                        print(f"{category.capitalize()} severity: {result.severity}")
                print("<-- End of Content Safety Analysis Results -->\n")
    
                # Define a threshold for the text to be considered unsafe
                threshold = 2
    
                # Based on the threshold, determine if the text is safe
                is_safe = not any(result and result.severity >= threshold for result in results.values())
                return is_safe, results
    
            except HttpResponseError as e:
                # Handle any HTTP response errors that occur during the request
                print(f"Failed to analyze text. Error: {e}")
    
        async def chat_with_kernel(self, user_message, history):
            """
            Interact with the chatbot using the Semantic Kernel and provided history.
            """
    
            # Create arguments for the chat function using the user message and conversation history stored in Azure Cosmos DB
            arguments = KernelArguments(user_message=user_message, history=history)
    
            # Invoke the chat function in the Semantic Kernel with the provided arguments and await the response
            response = await self.kernel.invoke(self.chat_function, arguments)
    
            # Return the chatbot's response
            return response
    
        async def store_interaction(self, user_message, chat_response):
            """
            Store the user interaction with the chatbot in Azure Cosmos DB.
            """
    
            # Run the _store_interaction_sync method in an asynchronous execution environment
            await self.loop.run_in_executor(self.executor, self._store_interaction_sync, user_message, chat_response)
    
        def _store_interaction_sync(self, user_message, chat_response):
            """
            Synchronously store the interaction in Azure Cosmos DB.
            """
    
            # Get the current time in UTC
            current_time = datetime.now(timezone.utc)
    
            # Upsert (insert or update) the interaction data into the Cosmos DB container
            self.container.upsert_item({
                'id': str(current_time.timestamp()),  # Use the current timestamp as a unique ID
                'user_message': user_message,  # Store the user message
                'bot_response': chat_response,  # Store the chatbot response
                'timestamp': current_time.isoformat(),  # Store the timestamp in ISO format
                'userId': self.user_id  # Store the user ID for partition key
            })
    
        async def load_historical_context(self):
            """
            Load the user's chat history from Azure Cosmos DB.
            """
    
            # Run the _load_historical_context_sync method in an asynchronous execution environment
            return await self.loop.run_in_executor(self.executor, self._load_historical_context_sync)
    
        def _load_historical_context_sync(self):
            """
            Synchronously load the user's chat history from Azure Cosmos DB.
            """
    
            # Define the query to select items for the current user, ordered by timestamp
            query = "SELECT * FROM c WHERE c.userId = @userId ORDER BY c.timestamp DESC"
            parameters = [{"name": "@userId", "value": self.user_id}]
        
            # Execute the query and retrieve the items
            items = list(self.container.query_items(query=query, parameters=parameters, enable_cross_partition_query=True))
    
            # Include only the last 5 conversations
            history_items = items[:5]
    
            # Format the conversion history
            return "\n".join([f"User: {item['user_message']}\nChatBot: {item['bot_response']}" for item in history_items])
    
        async def clear_historical_context(self):
            """
            Clear the user's chat history from Azure Cosmos DB.
            """
    
            # Run the _clear_historical_context_sync method in an asynchronous execution environment
            await self.loop.run_in_executor(self.executor, self._clear_historical_context_sync)
    
        def _clear_historical_context_sync(self):
            """
            Synchronously clear the user's chat history from Azure Cosmos DB.
            """
    
            # Define the query to select items for the current user
            query = "SELECT * FROM c WHERE c.userId = @userId"
            parameters = [{"name": "@userId", "value": self.user_id}]
    
            # Execute the query and retrieve the items
            items = list(self.container.query_items(query=query, parameters=parameters, enable_cross_partition_query=True))
        
            # Clear the chat history by deleting all items for the current user
            for item in items:
                self.container.delete_item(item, partition_key=self.user_id)
    
    async def main():
        """
        Main function to run the chatbot service.
        """
    
        # Enter the User ID to join the chat.
        # A conversation history is stored based on the user ID.
        user_id = input("User ID: ")
    
        # Get the event loop to allow synchronous operations in Azure Cosmos DB for NoSQL to run asynchronously.
        loop = asyncio.get_running_loop()
    
        # Initialize the ChatbotService with the event loop and user ID
        chatbot_service = await ChatbotService(loop, user_id).init()
    
        # Main loop for the interaction with the user
        while True:
            user_message = input("You: ")
            # Exit the chat loop if the user types 'exit'
            if user_message.lower() == 'exit':
                break
    
            elif user_message.lower() == 'history':
                # Load and print the chat history if the user types 'history'
                history = await chatbot_service.load_historical_context()
                print(f"\n<-- Chat history of user ID: {user_id} -->")
                print(history)
                print(f"<-- End of chat history of user ID: {user_id} -->\n")
    
            elif user_message.lower() == 'clear':
                # Clear the chat history if the user types 'clear'
                await chatbot_service.clear_historical_context()
                print("Chat history cleared.")
    
            else:
                # Analyze the text for safety
                is_safe, _ = await chatbot_service.analyze_text(user_message)
    
                if is_safe:
                    # Load chat history and interact with the chatbot if the message is safe
                    history = await chatbot_service.load_historical_context()
                    chat_response = await chatbot_service.chat_with_kernel(user_message, history)
                    print("Chatbot:", chat_response)
    
                    # Store the interaction in Cosmos DB
                    await chatbot_service.store_interaction(user_message, str(chat_response))
                else:
                    # Inform the user if their message is not safe
                    print("Chatbot: Your message is not safe to process. Please rephrase and try again.")
    
    if __name__ == "__main__":
        asyncio.run(main())
    

 

2 Comments
Co-Authors
Version history
Last update:
‎May 26 2024 05:49 PM
Updated by: