Orchestrating LLMs for Product Recommendations
This recipe demonstrates the manual orchestration of three different LLM product recommendation models. Executing this recipe grants the business user the decision-making power to determine in which circumstances each recommendation model should be used.
In this example, we're using ChatGPT prompts to mock three different product recommendation models for a grocery chain that does online ordering:
- Products frequently purchased together, in aggregate. This requires current cart items.
- Products frequently purchased by the customer, based on their individual purchase history. This requires customer id.
- Products rarely purchased by the customer, based on their individual purchase history. This requires customer id and location.
We have set up three Experience Rules to represent different contexts under which each model makes sense to run:
- Rule 1:
- Context: Checkout page; guest user; cart items
- Message: "Want to add on this product that a lot of people tend to buy when they've bought what you have in your cart?"
- Rule 2:
- Context: Checkout page; logged in user
- Message: "Want to add on this product that you love?"
- Rule 3:
- Context: Checkout page; logged in user; warm sunny weather
- Message: "Want to add on this product that you never buy - but might give a try today, because people are more likely to take chances on buying new things when the weather is warm and sunny according to our analytics?"
With this recipe, you could make further additions or adjustments for selecting recommendation models on your PDPs or in email marketing campaigns. You could take into account a variety of other contextual situations or customer behaviour as well.
There are LLM models that can handle decision-making logic, and would be capable of determining which recommendations model to use under which conditions. It will be up to your business requirements which method works best for you. There is always space for a human in the loop and sometimes that is preferable.
Mapping Out DX Engine Elements
When the frontend calls Conscia's Experience API, it will pass the following Context:
- The
location
value passes the shopper's current location to the Get Current Weather Component which will set theweatherCondition
Context field. - The
pageType
value represents the page where the customer is currently browsing. - The
customerId
value is either the customerId value or the String "guest". Before passing the customerId to one of the product recommendations models, you may need to make a call to a CDP for additional customer data. - The
cartItems
value passes the items currently in the shopper's cart. You may need to retrieve this information through a direct call to the commerce engine based on the current cart id; or it might come from the frontend in a format that requires some transformation before being passed to the product recommendations model. In this recipe, a simple String value is used listing the items.
An example call looks like this:
POST {{engineUrl}}/experience/components/_query
X-Customer-Code: {{customerCode}}
Authorization: Bearer {{dxEngineToken}}
{
"componentCodes": ["mapper-full-recommendations-chatgpt"],
"context": {
"cartItems" : "pumpkin puree, pie shells",
"pageType" : "checkout",
"customerId" : "guest",
"location" : "Vancouver"
}
}
Based on the context provided in this Experience API call, one of the three Experience Rules laid out above will be triggered.
- The Model Component will look at which Experience Rule was triggered and then select the response from the appropriate recommendations model Mapper Component.
- There is a Mapper Component for each recommendations model that will normalize each response.
- There is a ChatGPT Recommendations Selector Component for each recommendations model.
- Finally the Full Recommendations Mapper takes the normalized product recommendations data and stitches it together with the message that sits in each Experience Rule that will serve as a header to the recommendations section on the web page.
Assuming the weather is raining in Vancouver, an anonymous shopper would get a response like the following, depending on your model's whims:
"recommendations": {
"products": [
{
"id": 12345,
"name": "Whipped cream",
"price": 6.99,
"image": "https://www.example.com/whipped_cream.jpg"
},
{
"id": 23456,
"name": "Pumpkin spice",
"price": 8.49,
"image": "https://www.example.com/pumpkin_spice.jpg"
},
{
"id": 34567,
"name": "Cinnamon sticks",
"price": 10.25,
"image": "https://www.example.com/cinnamon_sticks.jpg"
}
]
},
"heading": "Missing one of these products?"
DX Engine Configuration Details
The topics in this section explain how to implement the elements involved in this recipe.
Context Fields
We need to create Context Fields to enable our Experience Rules to access Context values. For the purpose of this recipe, we'll be manually entering a few expected values into the Context Field configuration. For a production implementation, you would want to configure a Component to supply the expected values.
Weather Condition
To create a Context Field used to store the current weather condition, in the DX Engine UI:
- In the top navigation, click Settings, and then click Context Fields. The Manage Context Fields page appears.
- Click Add Context Field. The Create Context Field wizard appears.
- For Context Field Name, enter an identifier for the Context Field:
weatherCondition
. - For Display Name, enter a friendly name for the Context Field:
weatherCondition
. - Optionally, enter a Description for the Context Field.
- For Data Type, select String.
- Under Fixed List of Values, enter a single key/value pair for
Sunny
. - Click Submit.
Page Type
To create a Context Field used to store the current page a shopper is browsing, in the DX Engine UI:
- In the top navigation, click Settings, and then click Context Fields. The Manage Context Fields page appears.
- Click Add Context Field. The Create Context Field wizard appears.
- For Context Field Name, enter an identifier for the Context Field:
pageType
. - For Display Name, enter a friendly name for the Context Field:
pageType
. - Optionally, enter a Description for the Context Field.
- For Data Type, select String.
- Under Fixed List of Values, enter a single key/value pair for
checkout
. - Click Submit.
Customer Id
To create a Context Field used to store the customer id of the current shopper, in the DX Engine UI:
- In the top navigation, click Settings, and then click Context Fields. The Manage Context Fields page appears.
- Click Add Context Field. The Create Context Field wizard appears.
- For Context Field Name, enter an identifier for the Context Field:
customerId
. - For Display Name, enter a friendly name for the Context Field:
customerId
. - Optionally, enter a Description for the Context Field.
- For Data Type, select String.
- Under Fixed List of Values, enter a single key/value pair for
guest
. - Click Submit.
Secrets
WeatherAPI Secret
To create a Secret used to store the WeatherAPI API Key, in the DX Engine UI:
- Navigate to the Secrets page (Settings --> Secrets).
- Click the + Add Secret button.
- Enter the following and click Submit:
Field | Value |
---|---|
Secret Code | weatherapi-key |
Secret Name | WeatherAPI Key |
Secret Value | Enter your WeatherAPI API key. |
ChatGPT Secret
To create a Secret used to store the ChatGPT API Key, in the DX Engine UI:
- Navigate to the Secrets page (Settings --> Secrets).
- Click the + Add Secret button.
- Enter the following and click Submit:
Field | Value |
---|---|
Secret Code | chatgpt-key |
Secret Name | ChatGPT Key |
Secret Value | Enter your ChatGPT API key. |
Connections
Connection to WeatherAPI
- Navigate to the Connections page (Settings --> Connections).
- Click the + Add Connection button.
- Enter the following and click Submit:
Field | Value |
---|---|
Connection Code | weatherapi-connection |
Connection Name | WeatherAPI Connection |
Connector | Universal API Connector |
Base URL | Get value from: Literal http://api.weatherapi.com/v1 |
Query Parameters | Parameter: key Value: Get value from: Secret WeatherAPI Key |
Connection to ChatGPT
- Navigate to the Connections page (Settings --> Connections).
- Click the + Add Connection button.
- Enter the following and click Submit:
Field | Value |
---|---|
Connection Code | chatgpt-connection |
Connection Name | ChatGPT Connection |
Connector | Universal API Connector |
Base URL | Get value from: Literal https://api.openai.com/v1 |
Base Headers | Header: Authorization Value: Get value from: JS Expression `Bearer ` + secret('chatgpt-personalaccount') Key |
Base Headers | Header: OpenAI-Organization Value: Get value from: Literal {your organization id} |
Base Headers | Header: content-type Value: Get value from: Literal application/json |
Components
Component to retrieve current weather conditions
- Navigate to the Experience Components page (Manage Experiences --> Components).
- Click the + Add Component button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Component Code | Main | get-current-weather |
Component Name | Main | Get Current Weather |
No Rules | Main | Checked |
Component Type | Main | Conscia - Universal API Connector |
Connection | Main | Weather API Connection |
Webservice Path | Main | Get value from: Literal/current.json |
Method | Main | GET |
Query Parameters | Main | Parameter: q Value: Get value from: JS Expression `${contextField('location')}` |
Context Field Enrichments | Update Context | Context Field: weatherCondition Expression: response.current.condition.text |
Component to retrieve Frequently Purchased Together recommendations
- Navigate to the Experience Components page (Manage Experiences --> Components).
- Click the + Add Component button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Component Code | Main | chatgpt-rec1 |
Component Name | Main | ChatGPT - Frequently Purchased Recs (Aggregate) |
No Rules | Main | Checked |
Component Type | Main | Conscia - Universal API Connector |
Connection | Main | ChatGPT Connection |
Webservice Path | Main | Get value from: Literal/chat/completions |
Method | Main | POST |
Body | Main | Get value from: JS Expression |
`{
"model": "gpt-3.5-turbo",
"response_format": { "type": "json_object" },
"messages": [{"role": "user", "content": "A person has the following items in their online grocery store shopping cart: ${contextField('cartItems')}. Provide me with a JSON list of 3 grocery store products that people often buy that are complementary to the items in the cart. Each JSON product object should have a numeric id of 5 digits; a name; a price between $5 and $25; and a placeholder url link to a generic image of this product."}],
"temperature": 0.9
}`
Component to retrieve Frequently Purchased recommendations
- Navigate to the Experience Components page (Manage Experiences --> Components).
- Click the + Add Component button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Component Code | Main | chatgpt-rec2 |
Component Name | Main | ChatGPT - Frequently Purchased Recs (Individual) |
No Rules | Main | Checked |
Component Type | Main | Conscia - Universal API Connector |
Connection | Main | ChatGPT Connection |
Webservice Path | Main | Get value from: Literal/chat/completions |
Method | Main | POST |
Body | Main | Get value from: JS Expression |
`{
"model": "gpt-3.5-turbo",
"response_format": { "type": "json_object" },
"messages": [{"role": "user", "content": "Provide me with a list of 3 grocery store products that people would buy in a weekly online order. Each JSON product object should have a numeric id of 5 digits; a name; a price between $5 and $25; and a placeholder url link to a generic image of this product. The products should be in fruit, chocolate, candy, chips, or dip categories."}],
"temperature": 0.9
}`
Component to retrieve Never Purchased recommendations
- Navigate to the Experience Components page (Manage Experiences --> Components).
- Click the + Add Component button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Component Code | Main | chatgpt-rec3 |
Component Name | Main | ChatGPT - Never Purchased Recs (Individual) |
No Rules | Main | Checked |
Component Type | Main | Conscia - Universal API Connector |
Connection | Main | ChatGPT Connection |
Webservice Path | Main | Get value from: Literal/chat/completions |
Method | Main | POST |
Body | Main | Get value from: JS Expression |
`{
"model": "gpt-3.5-turbo",
"response_format": { "type": "json_object" },
"messages": [{"role": "user", "content": "Provide me with a JSON list of 3 grocery store products that people rarely buy in a weekly online order. They should be foods people generally enjoy. Each JSON product object should have a numeric id of 5 digits; a name; a price between $5 and $25; and a placeholder url link to a generic image of this product."}],
"temperature": 0.9
}`
Mapper Components to transform recommendations responses
Because each of our product recommendations models is providing the same output instructions to the ChatGPT prompt, the Mapper Components are virtually identical in this recipe. We'll only show the configuration for the first model. It's likely specific LLMs will have varying response structures, so you would need to create separate Mappers for each of them in your implementation.
- Navigate to the Experience Components page (Manage Experiences --> Components).
- Click the + Add Component button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Component Code | Main | chatgpt-rec1-mapper |
Component Name | Main | Mapper - Frequently Purchased Recs (Aggregate) - ChatGPT |
No Rules | Main | Checked |
Component Type | Main | Conscia - Data Transformation Script |
Data to modify | Main | Get value from: Component Response ChatGPT - Frequently Purchased Recs (Aggregrate) |
Script | Main | This script's main purpose is to extract JSON from the String message ChatGPT returns in its API response. |
let recs = data.choices[0].message.content;
function extractJSON(str) {
var firstOpen, firstClose, candidate;
firstOpen = str.indexOf('{', firstOpen + 1);
do {
firstClose = str.lastIndexOf('}');
console.log('firstOpen: ' + firstOpen, 'firstClose: ' + firstClose);
if(firstClose <= firstOpen) {
return null;
}
do {
candidate = str.substring(firstOpen, firstClose + 1);
console.log('candidate: ' + candidate);
try {
var res = JSON.parse(candidate);
return res;
}
catch(e) {
console.log('...failed');
}
firstClose = str.substr(0, firstClose).lastIndexOf('}');
} while(firstClose > firstOpen);
firstOpen = str.indexOf('{', firstOpen + 1);
} while(firstOpen != -1);
}
var result = extractJSON(recs);
result
Component to allow users to select source LLM for product recommendations
The Experience Rules will be created on this Component. You could choose to define default Metadata here. In this recipe we depend on the existence of a default Rule.
- Navigate to the Experience Components page (Manage Experiences --> Components).
- Click the + Add Component button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Component Code | Main | chatgpt-recs-source |
Component Name | Main | ChatGPT - Recommendations Source |
No Rules | Main | Not Checked |
Component Type | Main | Conscia - Metadata |
Model Component to retrieve model recommendations based on triggered Experience Rule
- Navigate to the Experience Components page (Manage Experiences --> Components).
- Click the + Add Component button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Component Code | Main | model-chatgpt-recs |
Component Name | Main | Model - ChatGPT Recs |
No Rules | Main | Checked |
Component Type | Main | Conscia - Metadata |
Response Transform | Main | (see below) |
componentResponse('chatgpt-recs-source').source[0] === 'recs1' ? componentResponse('chatgpt-rec1-mapper') : componentResponse('chatgpt-recs-source').source[0] === 'recs2' ? componentResponse('chatgpt-rec2-mapper') : componentResponse('chatgpt-recs-source').source[0] === 'recs3' ? componentResponse('chatgpt-rec3-mapper') : {}
Mapper Component to merge message header with recommendations data
- Navigate to the Experience Components page (Manage Experiences --> Components).
- Click the + Add Component button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Component Code | Main | chatgpt-full-recommendations-mapper |
Component Name | Main | Mapper - Full Recommendations - ChatGPT |
No Rules | Main | Checked |
Component Type | Main | Conscia - Property Mapper |
- Create two Property Maps as follows and click Submit.
Field | Value |
---|---|
Source Data | Get value from: Component Response Model - ChatGPT Recs |
Expression Type | Javascript |
Mappings | Target property: recommendations Source expression: data |
Field | Value |
---|---|
Source Data | Get value from: Component Response ChatGPT - Recommendations Source |
Expression Type | javascript |
Mappings | Target property: heading Source expression: data.message[0] |
Experience Rules
Experience Rule to use the Frequently Purchased Together LLM
- Navigate to the Omnichannel Experience Rules page (Manage Experiences --> Experience).
- Navigate to your ChatGPT - Recommendations Source Component under All Components.
- Click the + Add Experience Rule button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Rule ID | Main | frequently-purchased-all |
Rule Name | Main | Frequently Purchased Together (Aggregate) |
Priority | Main | 999 |
Is Default | Main | Checked |
Field | Form Tab | Value |
---|---|---|
Active | Experience | Checked |
Dates | Experience | Optional |
-
Under Real-time Context Evaluation, construct the following logic:
pageType is equal to (=) checkout
ANDcustomerId is equal to (=) guest
-
Under Target Experience, add two instruction key/value pairs then click Submit.
Field | Value |
---|---|
Instructions | Key: source Value: recs1 |
Instructions | Key: message Value: Missing one of these products? |
Experience Rule to use the Frequently Purchased by an Individual LLM
- Navigate to the Omnichannel Experience Rules page (Manage Experiences --> Experience).
- Navigate to your ChatGPT - Recommendations Source Component under All Components.
- Click the + Add Experience Rule button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Rule ID | Main | frequently-purchased |
Rule Name | Main | Frequently Purchased (Individual) |
Priority | Main | 900 |
Is Default | Main | Not Checked |
Field | Form Tab | Value |
---|---|---|
Active | Experience | Checked |
Dates | Experience | Optional |
-
Under Real-time Context Evaluation, construct the following logic:
pageType is equal to (=) checkout
ANDcustomerId is not equal to (=/=) guest
-
Under Target Experience, add two instruction key/value pairs then click Submit.
Field | Value |
---|---|
Instructions | Key: source Value: recs2 |
Instructions | Key: message Value: Did you forget something? |
Experience Rule to use the Never Purchased by an Individual LLM
- Navigate to the Omnichannel Experience Rules page (Manage Experiences --> Experience).
- Navigate to your ChatGPT - Recommendations Source Component under All Components.
- Click the + Add Experience Rule button.
- Enter the following and click Submit.
Field | Form Tab | Value |
---|---|---|
Rule ID | Main | never-purchased |
Rule Name | Main | Never Purchased (Individual) |
Priority | Main | 800 |
Is Default | Main | Not Checked |
Field | Form Tab | Value |
---|---|---|
Active | Experience | Checked |
Dates | Experience | Optional |
-
Under Real-time Context Evaluation, construct the following logic:
pageType is equal to (=) checkout
ANDcustomerId is not equal to (=/=) guest
ANDweatherCondition is equal to (=) Sunny
-
Under Target Experience, add two instruction key/value pairs then click Submit.
Field | Value |
---|---|
Instructions | Key: source Value: recs3 |
Instructions | Key: message Value: Ready for Adventure? Try something new! |