Chaining Multiple Prompts together using ChatGPT for Better Task Execution

Here using ChatGPT we will see how to split complex tasks into a series of simpler subtasks by chaining multiple prompts together which can help provide better results than trying to perform a task using just one prompt
natural-language-processing
deep-learning
openai
prompt-engineering
Author

Pranath Fernando

Published

June 22, 2023

1 Introduction

Large language models such as ChatGPT can generate text responses based on a given prompt or input. Writing prompts allow users to guide the language model’s output by providing a specific context or topic for the response. This feature has many practical applications, such as generating creative writing prompts, assisting in content creation, and even aiding in customer service chatbots.

In earlier articles i’ve looked at how you can use ChatGPT to solve some of these tasks with simple prompts. But in many use cases, what is required is not just one prompt but a sequence of prompts where we need to also consider the outputs at each stage, before providing a final output - for example with a customer service chatbot.

In this article, we will see using ChatGPT how to split complex tasks into a series of simpler subtasks by chaining multiple prompts together, which can help provide better results than trying to perform a task using just one prompt

2 Setup

2.1 Load the API key and relevant Python libaries.

First we need to load certain python libs and connect the OpenAi api.

The OpenAi api library needs to be configured with an account’s secret key, which is available on the website.

You can either set it as the OPENAI_API_KEY environment variable before using the library: !export OPENAI_API_KEY='sk-...'

Or, set openai.api_key to its value:

import openai
openai.api_key = "sk-..."
import os
import openai
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file

openai.api_key  = os.environ['OPENAI_API_KEY']
# Define helper function
def get_completion_from_messages(messages, 
                                 model="gpt-3.5-turbo", 
                                 temperature=0, 
                                 max_tokens=500):
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=temperature, 
        max_tokens=max_tokens, 
    )
    return response.choices[0].message["content"]

3 Implementing a complex task with multiple prompts

So why break up a task into several prompts when you can do it with just one question and chain of reasoning, as we discovered in my previous article ? We’ve already demonstrated that language models, especially more sophisticated models like GPT-4, are excellent at following complicated instructions. Let’s compare chain of reasoning and chaining several prompts using two analogies to show why we would do this.

The first analogy to contrast the two is the distinction between preparing a complicated dish all at once versus in phases. Trying to create a sophisticated meal all at once while managing different materials, cooking methods, and timings can be like using one long, convoluted instruction. Keeping track of everything and ensuring that each ingredient is cooked to perfection might be difficult. Chaining prompts, on the other hand, is similar to preparing a meal in stages, where you concentrate on one component at a time and make sure that each is cooked properly before moving on to the next.

With this method, the task’s complexity is broken down, making it simpler to manage and lowering the possibility of mistakes. However, for a relatively straightforward recipe, this strategy can be overkill and unnecessary.

A slightly better analogy for the same thing is the difference between reading spaghetti code with everything in one long file and a simple modular program.

Ambiguity and intricate relationships between distinct portions of the logic can make spaghetti code problematic and challenging to debug. A challenging one-step assignment given to a language model can have the same effect. When you have a workflow that allows you to preserve the state of the system at each given time and execute different actions based on the existing state, chaining prompts is a potent tactic. Following the categorization of an incoming customer query to indicate whether it is an account question or a product question, the current state would then be the classification, for example. And then based on the state you might do something different.

The system is easy to maintain, the model is given all the information it needs to complete a task, and the likelihood of errors is decreased because each subtask only contains the instructions necessary for one state of the task. Since longer prompts with more tokens cost more to operate and in some situations it may not be required to outline every step, this strategy can also result in cheaper costs. This method also makes it simpler to test which processes might be failing more frequently or to involve a person at a particular phase.

To sum up - because this was a lengthy explanation, it may be preferable to keep track of the state externally and then add extra instructions as needed rather than describing an entire complex workflow in dozens of bullet points or several paragraphs in one prompt as in the previous article.

How complex is a problem, and why? In general, a problem is considered complicated if there are numerous different instructions, all of which may be applicable in any given circumstance. In these situations, it may be challenging for the model to reason about what to do. Additionally, as you work with and utilise these models more, you’ll develop an understanding for when to apply this method instead of the prior one.

It also enables the model to use other tools as necessary at specific stages of the workflow, which is another advantage I haven’t yet mentioned. For instance, it might decide to do something that couldn’t be done with a single prompt, like search a knowledge base, access an API, or seek something up in a product catalogue. Let’s now move on to an illustration.

4 Extract relevant product and category names

We’ll use the same example as in my previous article, where we want to respond to a customer’s query regarding a particular product, but this time we’ll utilise more goods and also divide the steps down into a number of separate prompts. So we’ll continue to use the delimiter from the earlier articles.

Let’s read through our system message.

“You will be provided with customer service queries. The customer service query will be delimited with four hashtag characters.”

“Output a Python list of objects where each object has the following format.”

Category, which is one of these predefined fields, or products. And this is a list of products that must be found in the allowed products below.

“Where the categories and products must be found in the customer service query. If a product is mentioned, it must be associated with the correct category in the allowed products list below. If no products or categories are found, output an empty list.”.

So now we have our allowed list of products and we have the categories and then the products within those categories.

And our final instruction is:

“Only output the list of objects with nothing else.”

So next we have our user message. And so this message is:

“tell me about the smarts pro phone and the fotosnap camera, the DSLR one.”.

Also:

“tell me about your TVs.”

So we have questions concerning two specific items as well as this broad category of televisions. Additionally, the list of permitted products includes both of these items. We also have a section for television. After that, we format the user and system messages before adding them to the messages array. The model then provides the completion for us. As you can see, we have a list of objects for our output, with categories and goods for each object.

We have the “SmartX ProPhone” and the “Fotosnap DSLR Camera”. And then in the final object, we actually only have a category because we didn’t mention any specific TVs. And so the benefit of outputting this structured response is that we can then read it into a list in Python, which is very nice.

delimiter = "####"
system_message = f"""
You will be provided with customer service queries. \
The customer service query will be delimited with \
{delimiter} characters.
Output a python list of objects, where each object has \
the following format:
    'category': <one of Computers and Laptops, \
    Smartphones and Accessories, \
    Televisions and Home Theater Systems, \
    Gaming Consoles and Accessories, 
    Audio Equipment, Cameras and Camcorders>,
OR
    'products': <a list of products that must \
    be found in the allowed products below>

Where the categories and products must be found in \
the customer service query.
If a product is mentioned, it must be associated with \
the correct category in the allowed products list below.
If no products or categories are found, output an \
empty list.

Allowed products: 

Computers and Laptops category:
TechPro Ultrabook
BlueWave Gaming Laptop
PowerLite Convertible
TechPro Desktop
BlueWave Chromebook

Smartphones and Accessories category:
SmartX ProPhone
MobiTech PowerCase
SmartX MiniPhone
MobiTech Wireless Charger
SmartX EarBuds

Televisions and Home Theater Systems category:
CineView 4K TV
SoundMax Home Theater
CineView 8K TV
SoundMax Soundbar
CineView OLED TV

Gaming Consoles and Accessories category:
GameSphere X
ProGamer Controller
GameSphere Y
ProGamer Racing Wheel
GameSphere VR Headset

Audio Equipment category:
AudioPhonic Noise-Canceling Headphones
WaveSound Bluetooth Speaker
AudioPhonic True Wireless Earbuds
WaveSound Soundbar
AudioPhonic Turntable

Cameras and Camcorders category:
FotoSnap DSLR Camera
ActionCam 4K
FotoSnap Mirrorless Camera
ZoomMaster Camcorder
FotoSnap Instant Camera

Only output the list of objects, with nothing else.
"""
user_message_1 = f"""
 tell me about the smartx pro phone and \
 the fotosnap camera, the dslr one. \
 Also tell me about your tvs """
messages =  [  
{'role':'system', 
 'content': system_message},    
{'role':'user', 
 'content': f"{delimiter}{user_message_1}{delimiter}"},  
] 
category_and_product_response_1 = get_completion_from_messages(messages)
print(category_and_product_response_1)
[
    {'category': 'Smartphones and Accessories', 'products': ['SmartX ProPhone']},
    {'category': 'Cameras and Camcorders', 'products': ['FotoSnap DSLR Camera']},
    {'category': 'Televisions and Home Theater Systems'}
]

So let’s try another example. So our second user message is:

“my router isn’t working”.

And if you notice in the list, we don’t actually have any routers. And then, let’s format this correctly and get the completion.

user_message_2 = f"""
my router isn't working"""
messages =  [  
{'role':'system',
 'content': system_message},    
{'role':'user',
 'content': f"{delimiter}{user_message_2}{delimiter}"},  
] 
response = get_completion_from_messages(messages)
print(response)
[]

As you can see, the output in this instance is an empty list. So that we can better respond to the customer’s question, now that we have this process to identify the category and items, we want to load some information about any found products and categories into the prompt. Because of this, when this prompt has run, the state of our workflow is either that products have been listed or they haven’t, in which case we wouldn’t try to search up anything because there isn’t anything to look up.

Additionally, if I were to actually include this into a system, I might use category names, such as “computers and laptops” or something like that, to avoid any strangeness with spaces and other special characters, but for the time being, this should work.

5 Retrieve information for extracted products and categories

We now want to search for some information about the items the user indicated, namely about this phone, this camera, and about TVs in general. To get this information, we need some sort of product catalogue. So, this is the product information that I just pasted in. As you can see, we have a lot of things in stock at our store, all of them are phoney and were produced by GPT-4. So, there are a few various fields for each product, including name, category, brand, guarantee, and so forth.

# product information
products = {
    "TechPro Ultrabook": {
        "name": "TechPro Ultrabook",
        "category": "Computers and Laptops",
        "brand": "TechPro",
        "model_number": "TP-UB100",
        "warranty": "1 year",
        "rating": 4.5,
        "features": ["13.3-inch display", "8GB RAM", "256GB SSD", "Intel Core i5 processor"],
        "description": "A sleek and lightweight ultrabook for everyday use.",
        "price": 799.99
    },
    "BlueWave Gaming Laptop": {
        "name": "BlueWave Gaming Laptop",
        "category": "Computers and Laptops",
        "brand": "BlueWave",
        "model_number": "BW-GL200",
        "warranty": "2 years",
        "rating": 4.7,
        "features": ["15.6-inch display", "16GB RAM", "512GB SSD", "NVIDIA GeForce RTX 3060"],
        "description": "A high-performance gaming laptop for an immersive experience.",
        "price": 1199.99
    },
    "PowerLite Convertible": {
        "name": "PowerLite Convertible",
        "category": "Computers and Laptops",
        "brand": "PowerLite",
        "model_number": "PL-CV300",
        "warranty": "1 year",
        "rating": 4.3,
        "features": ["14-inch touchscreen", "8GB RAM", "256GB SSD", "360-degree hinge"],
        "description": "A versatile convertible laptop with a responsive touchscreen.",
        "price": 699.99
    },
    "TechPro Desktop": {
        "name": "TechPro Desktop",
        "category": "Computers and Laptops",
        "brand": "TechPro",
        "model_number": "TP-DT500",
        "warranty": "1 year",
        "rating": 4.4,
        "features": ["Intel Core i7 processor", "16GB RAM", "1TB HDD", "NVIDIA GeForce GTX 1660"],
        "description": "A powerful desktop computer for work and play.",
        "price": 999.99
    },
    "BlueWave Chromebook": {
        "name": "BlueWave Chromebook",
        "category": "Computers and Laptops",
        "brand": "BlueWave",
        "model_number": "BW-CB100",
        "warranty": "1 year",
        "rating": 4.1,
        "features": ["11.6-inch display", "4GB RAM", "32GB eMMC", "Chrome OS"],
        "description": "A compact and affordable Chromebook for everyday tasks.",
        "price": 249.99
    },
    "SmartX ProPhone": {
        "name": "SmartX ProPhone",
        "category": "Smartphones and Accessories",
        "brand": "SmartX",
        "model_number": "SX-PP10",
        "warranty": "1 year",
        "rating": 4.6,
        "features": ["6.1-inch display", "128GB storage", "12MP dual camera", "5G"],
        "description": "A powerful smartphone with advanced camera features.",
        "price": 899.99
    },
    "MobiTech PowerCase": {
        "name": "MobiTech PowerCase",
        "category": "Smartphones and Accessories",
        "brand": "MobiTech",
        "model_number": "MT-PC20",
        "warranty": "1 year",
        "rating": 4.3,
        "features": ["5000mAh battery", "Wireless charging", "Compatible with SmartX ProPhone"],
        "description": "A protective case with built-in battery for extended usage.",
        "price": 59.99
    },
    "SmartX MiniPhone": {
        "name": "SmartX MiniPhone",
        "category": "Smartphones and Accessories",
        "brand": "SmartX",
        "model_number": "SX-MP5",
        "warranty": "1 year",
        "rating": 4.2,
        "features": ["4.7-inch display", "64GB storage", "8MP camera", "4G"],
        "description": "A compact and affordable smartphone for basic tasks.",
        "price": 399.99
    },
    "MobiTech Wireless Charger": {
        "name": "MobiTech Wireless Charger",
        "category": "Smartphones and Accessories",
        "brand": "MobiTech",
        "model_number": "MT-WC10",
        "warranty": "1 year",
        "rating": 4.5,
        "features": ["10W fast charging", "Qi-compatible", "LED indicator", "Compact design"],
        "description": "A convenient wireless charger for a clutter-free workspace.",
        "price": 29.99
    },
    "SmartX EarBuds": {
        "name": "SmartX EarBuds",
        "category": "Smartphones and Accessories",
        "brand": "SmartX",
        "model_number": "SX-EB20",
        "warranty": "1 year",
        "rating": 4.4,
        "features": ["True wireless", "Bluetooth 5.0", "Touch controls", "24-hour battery life"],
        "description": "Experience true wireless freedom with these comfortable earbuds.",
        "price": 99.99
    },

    "CineView 4K TV": {
        "name": "CineView 4K TV",
        "category": "Televisions and Home Theater Systems",
        "brand": "CineView",
        "model_number": "CV-4K55",
        "warranty": "2 years",
        "rating": 4.8,
        "features": ["55-inch display", "4K resolution", "HDR", "Smart TV"],
        "description": "A stunning 4K TV with vibrant colors and smart features.",
        "price": 599.99
    },
    "SoundMax Home Theater": {
        "name": "SoundMax Home Theater",
        "category": "Televisions and Home Theater Systems",
        "brand": "SoundMax",
        "model_number": "SM-HT100",
        "warranty": "1 year",
        "rating": 4.4,
        "features": ["5.1 channel", "1000W output", "Wireless subwoofer", "Bluetooth"],
        "description": "A powerful home theater system for an immersive audio experience.",
        "price": 399.99
    },
    "CineView 8K TV": {
        "name": "CineView 8K TV",
        "category": "Televisions and Home Theater Systems",
        "brand": "CineView",
        "model_number": "CV-8K65",
        "warranty": "2 years",
        "rating": 4.9,
        "features": ["65-inch display", "8K resolution", "HDR", "Smart TV"],
        "description": "Experience the future of television with this stunning 8K TV.",
        "price": 2999.99
    },
    "SoundMax Soundbar": {
        "name": "SoundMax Soundbar",
        "category": "Televisions and Home Theater Systems",
        "brand": "SoundMax",
        "model_number": "SM-SB50",
        "warranty": "1 year",
        "rating": 4.3,
        "features": ["2.1 channel", "300W output", "Wireless subwoofer", "Bluetooth"],
        "description": "Upgrade your TV's audio with this sleek and powerful soundbar.",
        "price": 199.99
    },
    "CineView OLED TV": {
        "name": "CineView OLED TV",
        "category": "Televisions and Home Theater Systems",
        "brand": "CineView",
        "model_number": "CV-OLED55",
        "warranty": "2 years",
        "rating": 4.7,
        "features": ["55-inch display", "4K resolution", "HDR", "Smart TV"],
        "description": "Experience true blacks and vibrant colors with this OLED TV.",
        "price": 1499.99
    },

    "GameSphere X": {
        "name": "GameSphere X",
        "category": "Gaming Consoles and Accessories",
        "brand": "GameSphere",
        "model_number": "GS-X",
        "warranty": "1 year",
        "rating": 4.9,
        "features": ["4K gaming", "1TB storage", "Backward compatibility", "Online multiplayer"],
        "description": "A next-generation gaming console for the ultimate gaming experience.",
        "price": 499.99
    },
    "ProGamer Controller": {
        "name": "ProGamer Controller",
        "category": "Gaming Consoles and Accessories",
        "brand": "ProGamer",
        "model_number": "PG-C100",
        "warranty": "1 year",
        "rating": 4.2,
        "features": ["Ergonomic design", "Customizable buttons", "Wireless", "Rechargeable battery"],
        "description": "A high-quality gaming controller for precision and comfort.",
        "price": 59.99
    },
    "GameSphere Y": {
        "name": "GameSphere Y",
        "category": "Gaming Consoles and Accessories",
        "brand": "GameSphere",
        "model_number": "GS-Y",
        "warranty": "1 year",
        "rating": 4.8,
        "features": ["4K gaming", "500GB storage", "Backward compatibility", "Online multiplayer"],
        "description": "A compact gaming console with powerful performance.",
        "price": 399.99
    },
    "ProGamer Racing Wheel": {
        "name": "ProGamer Racing Wheel",
        "category": "Gaming Consoles and Accessories",
        "brand": "ProGamer",
        "model_number": "PG-RW200",
        "warranty": "1 year",
        "rating": 4.5,
        "features": ["Force feedback", "Adjustable pedals", "Paddle shifters", "Compatible with GameSphere X"],
        "description": "Enhance your racing games with this realistic racing wheel.",
        "price": 249.99
    },
    "GameSphere VR Headset": {
        "name": "GameSphere VR Headset",
        "category": "Gaming Consoles and Accessories",
        "brand": "GameSphere",
        "model_number": "GS-VR",
        "warranty": "1 year",
        "rating": 4.6,
        "features": ["Immersive VR experience", "Built-in headphones", "Adjustable headband", "Compatible with GameSphere X"],
        "description": "Step into the world of virtual reality with this comfortable VR headset.",
        "price": 299.99
    },

    "AudioPhonic Noise-Canceling Headphones": {
        "name": "AudioPhonic Noise-Canceling Headphones",
        "category": "Audio Equipment",
        "brand": "AudioPhonic",
        "model_number": "AP-NC100",
        "warranty": "1 year",
        "rating": 4.6,
        "features": ["Active noise-canceling", "Bluetooth", "20-hour battery life", "Comfortable fit"],
        "description": "Experience immersive sound with these noise-canceling headphones.",
        "price": 199.99
    },
    "WaveSound Bluetooth Speaker": {
        "name": "WaveSound Bluetooth Speaker",
        "category": "Audio Equipment",
        "brand": "WaveSound",
        "model_number": "WS-BS50",
        "warranty": "1 year",
        "rating": 4.5,
        "features": ["Portable", "10-hour battery life", "Water-resistant", "Built-in microphone"],
        "description": "A compact and versatile Bluetooth speaker for music on the go.",
        "price": 49.99
    },
    "AudioPhonic True Wireless Earbuds": {
        "name": "AudioPhonic True Wireless Earbuds",
        "category": "Audio Equipment",
        "brand": "AudioPhonic",
        "model_number": "AP-TW20",
        "warranty": "1 year",
        "rating": 4.4,
        "features": ["True wireless", "Bluetooth 5.0", "Touch controls", "18-hour battery life"],
        "description": "Enjoy music without wires with these comfortable true wireless earbuds.",
        "price": 79.99
    },
    "WaveSound Soundbar": {
        "name": "WaveSound Soundbar",
        "category": "Audio Equipment",
        "brand": "WaveSound",
        "model_number": "WS-SB40",
        "warranty": "1 year",
        "rating": 4.3,
        "features": ["2.0 channel", "80W output", "Bluetooth", "Wall-mountable"],
        "description": "Upgrade your TV's audio with this slim and powerful soundbar.",
        "price": 99.99
    },
    "AudioPhonic Turntable": {
        "name": "AudioPhonic Turntable",
        "category": "Audio Equipment",
        "brand": "AudioPhonic",
        "model_number": "AP-TT10",
        "warranty": "1 year",
        "rating": 4.2,
        "features": ["3-speed", "Built-in speakers", "Bluetooth", "USB recording"],
        "description": "Rediscover your vinyl collection with this modern turntable.",
        "price": 149.99
    },

    "FotoSnap DSLR Camera": {
        "name": "FotoSnap DSLR Camera",
        "category": "Cameras and Camcorders",
        "brand": "FotoSnap",
        "model_number": "FS-DSLR200",
        "warranty": "1 year",
        "rating": 4.7,
        "features": ["24.2MP sensor", "1080p video", "3-inch LCD", "Interchangeable lenses"],
        "description": "Capture stunning photos and videos with this versatile DSLR camera.",
        "price": 599.99
    },
    "ActionCam 4K": {
        "name": "ActionCam 4K",
        "category": "Cameras and Camcorders",
        "brand": "ActionCam",
        "model_number": "AC-4K",
        "warranty": "1 year",
        "rating": 4.4,
        "features": ["4K video", "Waterproof", "Image stabilization", "Wi-Fi"],
        "description": "Record your adventures with this rugged and compact 4K action camera.",
        "price": 299.99
    },
    "FotoSnap Mirrorless Camera": {
        "name": "FotoSnap Mirrorless Camera",
        "category": "Cameras and Camcorders",
        "brand": "FotoSnap",
        "model_number": "FS-ML100",
        "warranty": "1 year",
        "rating": 4.6,
        "features": ["20.1MP sensor", "4K video", "3-inch touchscreen", "Interchangeable lenses"],
        "description": "A compact and lightweight mirrorless camera with advanced features.",
        "price": 799.99
    },
    "ZoomMaster Camcorder": {
        "name": "ZoomMaster Camcorder",
        "category": "Cameras and Camcorders",
        "brand": "ZoomMaster",
        "model_number": "ZM-CM50",
        "warranty": "1 year",
        "rating": 4.3,
        "features": ["1080p video", "30x optical zoom", "3-inch LCD", "Image stabilization"],
        "description": "Capture life's moments with this easy-to-use camcorder.",
        "price": 249.99
    },
    "FotoSnap Instant Camera": {
        "name": "FotoSnap Instant Camera",
        "category": "Cameras and Camcorders",
        "brand": "FotoSnap",
        "model_number": "FS-IC10",
        "warranty": "1 year",
        "rating": 4.1,
        "features": ["Instant prints", "Built-in flash", "Selfie mirror", "Battery-powered"],
        "description": "Create instant memories with this fun and portable instant camera.",
        "price": 69.99
    }
}

And so the products is just a dictionary from product name to this object that contains the information about the product. Notice that each product has a category. So remember, we want to look up information about the products that the user asks about, so we need to define some helper functions to allow us to look up product information by product name.

So let’s create a function, “get_product_by_name”. When we enter a name, a product dictionary will be returned, and we’ll get the value for the item with the name as the key as well as none as a fallback. In order to obtain all of the products for a particular category, we also want to develop another helper function. For instance, we would want to load all of the information about all of the different TVs when the user inquires about the TVs we have.

So “get_products_by_category” input the category name string. And to accomplish this, we need to iterate through every item in the products dictionary, determining whether the category matches the input category for each one and returning the result if it does. So, let’s proceed as follows. In order to access the category, which is contained in the value, we must first access the values before looping through each product. If the product category matches our input category, we will then return this item.

def get_product_by_name(name):
    return products.get(name, None)

def get_products_by_category(category):
    return [product for product in products.values() if product["category"] == category]

So first we have a product called the “TechPro Ultrabook”. So let’s get the product information by name.

print(get_product_by_name("TechPro Ultrabook"))
{'name': 'TechPro Ultrabook', 'category': 'Computers and Laptops', 'brand': 'TechPro', 'model_number': 'TP-UB100', 'warranty': '1 year', 'rating': 4.5, 'features': ['13.3-inch display', '8GB RAM', '256GB SSD', 'Intel Core i5 processor'], 'description': 'A sleek and lightweight ultrabook for everyday use.', 'price': 799.99}

So here you can see we’ve just fetched all of the product information. And let’s do an example to get all of the products for a category. So let’s get all of the products in the computers and laptops category.

print(get_products_by_category("Computers and Laptops"))
[{'name': 'TechPro Ultrabook', 'category': 'Computers and Laptops', 'brand': 'TechPro', 'model_number': 'TP-UB100', 'warranty': '1 year', 'rating': 4.5, 'features': ['13.3-inch display', '8GB RAM', '256GB SSD', 'Intel Core i5 processor'], 'description': 'A sleek and lightweight ultrabook for everyday use.', 'price': 799.99}, {'name': 'BlueWave Gaming Laptop', 'category': 'Computers and Laptops', 'brand': 'BlueWave', 'model_number': 'BW-GL200', 'warranty': '2 years', 'rating': 4.7, 'features': ['15.6-inch display', '16GB RAM', '512GB SSD', 'NVIDIA GeForce RTX 3060'], 'description': 'A high-performance gaming laptop for an immersive experience.', 'price': 1199.99}, {'name': 'PowerLite Convertible', 'category': 'Computers and Laptops', 'brand': 'PowerLite', 'model_number': 'PL-CV300', 'warranty': '1 year', 'rating': 4.3, 'features': ['14-inch touchscreen', '8GB RAM', '256GB SSD', '360-degree hinge'], 'description': 'A versatile convertible laptop with a responsive touchscreen.', 'price': 699.99}, {'name': 'TechPro Desktop', 'category': 'Computers and Laptops', 'brand': 'TechPro', 'model_number': 'TP-DT500', 'warranty': '1 year', 'rating': 4.4, 'features': ['Intel Core i7 processor', '16GB RAM', '1TB HDD', 'NVIDIA GeForce GTX 1660'], 'description': 'A powerful desktop computer for work and play.', 'price': 999.99}, {'name': 'BlueWave Chromebook', 'category': 'Computers and Laptops', 'brand': 'BlueWave', 'model_number': 'BW-CB100', 'warranty': '1 year', 'rating': 4.1, 'features': ['11.6-inch display', '4GB RAM', '32GB eMMC', 'Chrome OS'], 'description': 'A compact and affordable Chromebook for everyday tasks.', 'price': 249.99}]
print(user_message_1)

 tell me about the smartx pro phone and  the fotosnap camera, the dslr one.  Also tell me about your tvs 

So here you see we fetched all of the products with this category. So let’s continue our example and just to remember where we are let’s print the user message. So the user message was tell me about the SmartX ProPhone and the camera and the TVs. And then the initial output from the model from the first step was this. And so what we also need to do is read this output from the model which is a string. We need to pass that into a list so that we can use it as input to the helper functions that we just wrote.

print(category_and_product_response_1)
[
    {'category': 'Smartphones and Accessories', 'products': ['SmartX ProPhone']},
    {'category': 'Cameras and Camcorders', 'products': ['FotoSnap DSLR Camera']},
    {'category': 'Televisions and Home Theater Systems'}
]

6 Read Python string into Python list of dictionaries

So let’s write a helper function to do this. So we’re going to use the Python JSON module and we’re going to write a function called “read_string _to_list”, a very descriptive title input string.

import json 

def read_string_to_list(input_string):
    if input_string is None:
        return None

    try:
        input_string = input_string.replace("'", "\"")  # Replace single quotes with double quotes for valid JSON
        data = json.loads(input_string)
        return data
    except json.JSONDecodeError:
        print("Error: Invalid JSON string")
        return None   
    

And so first we’ll just check if the input string is none. In case something in a previous step failed we’re just going to return nothing. And now we’re going to have a try except block to make sure that we catch any errors. And so, first we replace any single quotes with double quotes in the input string to make sure we can pass the JSON, and then we’re going to use the JSON loads function to read the input string into the array, or the list, and then we’re going to return this, and if there’s a decode error, we’re going to print the error and then return none. So, let’s try this with our example.

So, we’re going to get our category and product list using the “read_string_to_list” helper function, and apply it to this response from the model, and then we’re going to print this list.

category_and_product_list = read_string_to_list(category_and_product_response_1)
print(category_and_product_list)
[{'category': 'Smartphones and Accessories', 'products': ['SmartX ProPhone']}, {'category': 'Cameras and Camcorders', 'products': ['FotoSnap DSLR Camera']}, {'category': 'Televisions and Home Theater Systems'}]

And so, as you can see, it’s just the same thing, except now the type of this variable is actually a list instead of a string. So, the whole point of what we’re doing is to get the product information into a list that we can add to the next instruction to the model, which is going to be the instruction where we ask it to answer the user question. And so, to do this, we need to put the product information into a nice string format that we can add to the prompt. And so, let’s also create a helper function to do this.

7 Retrieve information for the relevant products and categories

So, we’re going to call it “generate_output_string”, and it’s going to take in the list of data that we just created. Then I’m going to copy in some code, and then we’ll walk through what it’s doing.

We’re going to get the product information from our first user message, and so we’re going to use this helper function “generate_output_string” on our category and product list, which, if we remember, was this.

def generate_output_string(data_list):
    output_string = ""

    if data_list is None:
        return output_string

    for data in data_list:
        try:
            if "products" in data:
                products_list = data["products"]
                for product_name in products_list:
                    product = get_product_by_name(product_name)
                    if product:
                        output_string += json.dumps(product, indent=4) + "\n"
                    else:
                        print(f"Error: Product '{product_name}' not found")
            elif "category" in data:
                category_name = data["category"]
                category_products = get_products_by_category(category_name)
                for product in category_products:
                    output_string += json.dumps(product, indent=4) + "\n"
            else:
                print("Error: Invalid object format")
        except Exception as e:
            print(f"Error: {e}")

    return output_string 

And so, here we have all of the product information for the products that were mentioned in the user message. So, we have the phone that they mentioned, we have the camera that they mentioned, and then we have all of the product information for all of our TVs. And this is information that will be helpful for the model to be able to answer the user’s initial question.

product_information_for_user_message_1 = generate_output_string(category_and_product_list)
print(product_information_for_user_message_1)
{
    "name": "SmartX ProPhone",
    "category": "Smartphones and Accessories",
    "brand": "SmartX",
    "model_number": "SX-PP10",
    "warranty": "1 year",
    "rating": 4.6,
    "features": [
        "6.1-inch display",
        "128GB storage",
        "12MP dual camera",
        "5G"
    ],
    "description": "A powerful smartphone with advanced camera features.",
    "price": 899.99
}
{
    "name": "FotoSnap DSLR Camera",
    "category": "Cameras and Camcorders",
    "brand": "FotoSnap",
    "model_number": "FS-DSLR200",
    "warranty": "1 year",
    "rating": 4.7,
    "features": [
        "24.2MP sensor",
        "1080p video",
        "3-inch LCD",
        "Interchangeable lenses"
    ],
    "description": "Capture stunning photos and videos with this versatile DSLR camera.",
    "price": 599.99
}
{
    "name": "CineView 4K TV",
    "category": "Televisions and Home Theater Systems",
    "brand": "CineView",
    "model_number": "CV-4K55",
    "warranty": "2 years",
    "rating": 4.8,
    "features": [
        "55-inch display",
        "4K resolution",
        "HDR",
        "Smart TV"
    ],
    "description": "A stunning 4K TV with vibrant colors and smart features.",
    "price": 599.99
}
{
    "name": "SoundMax Home Theater",
    "category": "Televisions and Home Theater Systems",
    "brand": "SoundMax",
    "model_number": "SM-HT100",
    "warranty": "1 year",
    "rating": 4.4,
    "features": [
        "5.1 channel",
        "1000W output",
        "Wireless subwoofer",
        "Bluetooth"
    ],
    "description": "A powerful home theater system for an immersive audio experience.",
    "price": 399.99
}
{
    "name": "CineView 8K TV",
    "category": "Televisions and Home Theater Systems",
    "brand": "CineView",
    "model_number": "CV-8K65",
    "warranty": "2 years",
    "rating": 4.9,
    "features": [
        "65-inch display",
        "8K resolution",
        "HDR",
        "Smart TV"
    ],
    "description": "Experience the future of television with this stunning 8K TV.",
    "price": 2999.99
}
{
    "name": "SoundMax Soundbar",
    "category": "Televisions and Home Theater Systems",
    "brand": "SoundMax",
    "model_number": "SM-SB50",
    "warranty": "1 year",
    "rating": 4.3,
    "features": [
        "2.1 channel",
        "300W output",
        "Wireless subwoofer",
        "Bluetooth"
    ],
    "description": "Upgrade your TV's audio with this sleek and powerful soundbar.",
    "price": 199.99
}
{
    "name": "CineView OLED TV",
    "category": "Televisions and Home Theater Systems",
    "brand": "CineView",
    "model_number": "CV-OLED55",
    "warranty": "2 years",
    "rating": 4.7,
    "features": [
        "55-inch display",
        "4K resolution",
        "HDR",
        "Smart TV"
    ],
    "description": "Experience true blacks and vibrant colors with this OLED TV.",
    "price": 1499.99
}

It simply loops through every object in this list to see whether there are any products. If so, it obtains the details for each product before determining if a category exists in the absence of any items. Then it obtains all of the product information for the items in that category, for example, for this object. And all it does is add them to the string before returning it. We’ve now discovered the key product data to respond to the user’s query.

8 Generate answer to user query based on detailed product information

Now it’s time for the model to actually answer the question. So let’s have our system message. So this is the instruction:

“You’re a customer service assistant for a large electronics store. Respond in a friendly and helpful tone with, let’s say, with very concise answers. Make sure to ask the user relevant follow up questions.

So we want this to be an interactive experience for the user. And so just as a reminder, this was our initial user message.

And so now we’re going to have our messages array. And this is the input to the model.

After that, we get another communication from the assistant. And the message that contains all of the product details that we just looked up is this one. So, we’re listing important product information, a new line, and finally the product information we found. The model now has the key context it requires to respond to this user’s query. Let’s just print the final response. We also anticipate that the model will use this key data from the product information to respond to the user’s query in a useful manner.

The user is first informed about the SmartX ProPhone. It informs the consumer about the Fotosnap camera before going on to discuss the many televisions we carry before posing a follow-up query. So as you can see, we were able to load information key to the user inquiry to provide the model with the necessary context it needed to properly answer the question by dividing this up into a series of steps.

So, you might be wondering why we only load a subset of the product descriptions into the prompt rather than adding them all and letting the model use the data it requires?

system_message = f"""
You are a customer service assistant for a \
large electronic store. \
Respond in a friendly and helpful tone, \
with very concise answers. \
Make sure to ask the user relevant follow up questions.
"""
user_message_1 = f"""
tell me about the smartx pro phone and \
the fotosnap camera, the dslr one. \
Also tell me about your tvs"""
messages =  [  
{'role':'system',
 'content': system_message},   
{'role':'user',
 'content': user_message_1},  
{'role':'assistant',
 'content': f"""Relevant product information:\n\
 {product_information_for_user_message_1}"""},   
]
final_response = get_completion_from_messages(messages)
print(final_response)
The SmartX ProPhone has a 6.1-inch display, 128GB storage, 12MP dual camera, and 5G. The FotoSnap DSLR Camera has a 24.2MP sensor, 1080p video, 3-inch LCD, and interchangeable lenses. We have a variety of TVs, including the CineView 4K TV with a 55-inch display, 4K resolution, HDR, and smart TV features. We also have the SoundMax Home Theater system with 5.1 channel, 1000W output, wireless subwoofer, and Bluetooth. Do you have any specific questions about these products or any other products we offer?

9 Why use a subset rather than all product data?

To clarify, we’re asking is why we didn’t simply put all of this product information in the prompt, saving us the trouble of having to go through all of the extra steps to actually seek up the product information. There are a few reasons for this.

First off, having all the product descriptions could make the context more perplexing for the model, just as it would be for a person attempting to comprehend a lot of information at once. In particular, when the context is well structured, as it is in this example, and the model is intelligent enough to simply ignore the information that is obviously irrelevant, I will say that this is far less relevant for more sophisticated models like GPT-4.

The following points are even more important. A fixed number of tokens are permitted as input and output in language models, which is the second reason. Therefore, you wouldn’t even be able to fit all of the descriptions into the context window if you had a lot of things, like a massive product catalogue. The cost of integrating all of the product descriptions could be prohibitive because, when utilising language models, you pay per token. So you can lower the cost of producing responses by loading information carefully.

In general, one of the best ways to improve the capabilities of these models is to decide when to dynamically load information into the model’s environment and let the model decide when it needs more information.

And once more, consider a language model to be a reasoning agent that needs the right environment in order to make intelligible decisions and carry out productive tasks. We had to provide the model with the relevant product information in this example in order for it to be able to use that information to reason and produce a good response for the user.

Furthermore, we just included a call to a certain function or functions in this example to obtain the product description by product name or the category items by category name. However, the models are truly adept at knowing when to utilise a range of different tools and are capable of using them correctly when given instructions. The concept behind ChatGPT plugins is the same. When the model needs information from a certain source or desires to take another relevant action, we notify it of the tools it has access to and what they perform. The model then decides to employ those tools.

Although there are more sophisticated methods for information retrieval, in our example we can only seek up information by specific product and category name matches. Using text embeddings is one of the best ways to obtain information as I have done previosuly with my articles using LangChain.

Also, embeddings can be used to efficiently conduct knowledge retrieval over a big corpus to locate data relevant to a particular query. The ability to do fuzzy or semantic searches utilising text embeddings, which allow you to locate pertinent material without needing exact keywords, is one of their main benefits. Therefore, in our hypothetical situation, we wouldn’t necessarily need to know the specific name of the device; instead, we could conduct a search using a more broad term, such as “mobile phone.”

10 Acknowledgements

I’d like to express my thanks to the wonderful Building Systems with the ChatGPT API Course by DeepLearning.ai and OpenAI - which i completed, and acknowledge the use of some images and other materials from the course in this article.

Subscribe