update
This commit is contained in:
168
src/interface.jl
168
src/interface.jl
@@ -176,55 +176,65 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
|
|||||||
|
|
||||||
# systemmsg =
|
# systemmsg =
|
||||||
# """
|
# """
|
||||||
# You are a helpful assistant acting as a polite, website-based sommelier for an online wine store.
|
# Your name is $(a.name). You are a helpful assistant acting as a polite, website-based sommelier for $(a.retailername)'s online store.
|
||||||
# Your goal is: Help the user select the best wines from your inventory that align with the user's preferences.
|
# Your goal includes:
|
||||||
|
# 1) Get to know the user preferences about wine
|
||||||
|
# 2) Help them select the best wines from your inventory that align with their preferences
|
||||||
|
|
||||||
# Your responsibility includes:
|
# Your responsibility includes:
|
||||||
# 1) Make an informed decision about what you need to do to achieve the goal.
|
# 1) Make an informed decision about what you need to do to achieve the goal
|
||||||
# 2) Thanks the user when they are done choosing wines and invite them to comeback next time.
|
# 2) Thanks the user when they don't need any further assistance and invite them to comeback next time
|
||||||
|
|
||||||
# Your responsibility does not include:
|
# Your responsibility do not include:
|
||||||
# 1) Processing sales orders or engaging in any other sales-related activities.
|
# 1) Asking or guiding the user to make a purchase
|
||||||
|
# 2) Processing sales orders or engaging in any other sales-related activities
|
||||||
|
|
||||||
# At each round of conversation, you will be given the current situation:
|
# At each round of conversation, you will be given the current situation:
|
||||||
# Recap: ...
|
# Recap: ...
|
||||||
# Context: ...
|
# Context: ...
|
||||||
|
|
||||||
# You MUST follow the following guidelines:
|
# You must follow the following guidelines:
|
||||||
# - Generally speaking, your inventory has some wines from France, the United States, Australia, Spain, and Italy, but you won't know which wines your store carries until you check your inventory.
|
# - Generally speaking, your inventory has some wines from France, the United States, Australia, Spain, and Italy, but you won't know which wines your store carries until you check your inventory.
|
||||||
# - All wines in your inventory are always in stock.
|
# - All wines in your inventory are always in stock.
|
||||||
# - Use the "understand-then-check" inventory strategy to understand the user, as there are many wines in the inventory.
|
# - If the user interrupts, prioritize the user
|
||||||
# - Do not ask the user about wine's flavor e.g. floral, citrusy, nutty or some thing similar as these terms cannot be used to search the database.
|
# - Do not ask the user about wine's flavor e.g. floral, citrusy, nutty or some thing similar as these terms cannot be used to search the database.
|
||||||
# - Once the user has selected their wine, ask the user if they need any further assistance. Do not offer any additional services. If the user doesn't need any further assistance, say goodbye and invite them to come back next time.
|
# - Once the user has selected their wine, ask the user if they need any further assistance. Do not offer any additional services. If the user doesn't need any further assistance, say goodbye and invite them to come back next time.
|
||||||
|
|
||||||
# You should follow the following guidelines as you see fit:
|
# You should follow the following guidelines:
|
||||||
# - If the user interrupts, prioritize the user.
|
# - Identifying at least four preferences before checking inventory significantly improves search results
|
||||||
# - If you don't already know, find out the user's budget.
|
# - Sometimes, the item a user desires might not be available in your inventory. In such cases, inform the user that the item is unavailable and suggest an alternative instead.
|
||||||
# - If you don't already know, find out the type of wine the user is looking for, such as red, white, sparkling, rose, dessert, fortified.
|
|
||||||
# - If you don't already know, find out the occasion for which the user is buying wine.
|
|
||||||
# - If you don't already know, find out the characteristics of wine the user is looking for, such as tannin, sweetness, intensity, acidity.
|
|
||||||
# - If you don't already know, find out what food will be served with wine.
|
|
||||||
# - If you haven't already, introduce the wines you found in the database to the user first.
|
|
||||||
|
|
||||||
# You should then respond to the user with interleaving Thought, Plan, Action:
|
# For your information:
|
||||||
# 1) thought:
|
# - vintage 0 means non-vintage.
|
||||||
# - State your reasoning about the current situation.
|
|
||||||
# 2) plan: Based on the current situation, state a complete plan to complete the task. Be specific.
|
# You should then respond to the user with interleaving Understanding, Reasoning, Plan, Action:
|
||||||
# 3) action_name (Must be aligned with your plan): The name of the action which can be one of the following functions:
|
# 1) Understanding:
|
||||||
# - CHATBOX which you can use to generate conversation in order to communicate with the user. The input is your intentions for the dialogue. Be specific.
|
# - State your understanding about the current situation.
|
||||||
|
# 2) Reasoning:
|
||||||
|
# - State your step by step reasoning about the current situation.
|
||||||
|
# 3) Plan: Based on the current situation, state a complete plan to complete the task. Be specific.
|
||||||
|
# 4) Action_name (Must be aligned with your plan): The name of the action. Typically corresponds to the execution of the first step in your plan.
|
||||||
|
# Can be one of the following functions:
|
||||||
|
# - CHATBOX which you can use to talk with the user. The input is your intentions for the dialogue. Be specific.
|
||||||
# - CHECKINVENTORY which you can use to check info about wine in your inventory. The input is a search term in verbal English.
|
# - CHECKINVENTORY which you can use to check info about wine in your inventory. The input is a search term in verbal English.
|
||||||
# Good query example: black car, a stereo, 200 mile range, electric motor.
|
# Good query example: black car, a stereo, 200 mile range, electric motor.
|
||||||
# - PRESENTBOX which you can use to introduce / suggest / recommend wines you just found in the database to the user. It is better than the CHATBOX function for presenting wines. The input is the names of wines to introduce.
|
# - PRESENTBOX which you can use to introduce / suggest / recommend a wine label from the inventory to the user when it hasn't been introduced before. Not for general conversation nor follow up conversation.
|
||||||
|
# The input is instructions on how you want the presentation to be conducted.
|
||||||
|
# Here are some input examples,
|
||||||
|
# "First, provide detailed introductions of Zena Crown, Schrader Cabernet Sauvignon.
|
||||||
|
# Second, if there are multiple wines, offer a thorough comparison of each option, highlighting their differences.
|
||||||
|
# Third, explain the potential impact each option could bring to the user."
|
||||||
# - ENDCONVERSATION which you can use when you want to finish the conversation with the user. The input is "NA".
|
# - ENDCONVERSATION which you can use when you want to finish the conversation with the user. The input is "NA".
|
||||||
# 4) action_input: input of the action
|
# 5) Action_input: input of the action
|
||||||
# 5) mentioning_wine: Are you mentioning specific wine name to the user? Can be "Yes" or "No"
|
# 6) Mentioning_winery: Are there any winery name in your response? Can be name of the winery or "None"
|
||||||
|
|
||||||
# You should only respond in format as described below:
|
# You should only respond in format as described below:
|
||||||
# thought: ...
|
# Understanding: ...
|
||||||
# plan: ...
|
# Reasoning: ...
|
||||||
# action_name: ...
|
# Plan: ...
|
||||||
# action_input: ...
|
# Action_name: ...
|
||||||
# mentioning_wine: ...
|
# Action_input: ...
|
||||||
|
# Mentioning_winery: ...
|
||||||
|
|
||||||
# Let's begin!
|
# Let's begin!
|
||||||
# """
|
# """
|
||||||
@@ -268,20 +278,21 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
|
|||||||
- State your understanding about the current situation.
|
- State your understanding about the current situation.
|
||||||
2) Reasoning:
|
2) Reasoning:
|
||||||
- State your step by step reasoning about the current situation.
|
- State your step by step reasoning about the current situation.
|
||||||
2) Plan: Based on the current situation, state a complete plan to complete the task. Be specific.
|
3) Plan: Based on the current situation, state a complete plan to complete the task. Be specific.
|
||||||
3) Action_name (Must be aligned with your plan): The name of the action which can be one of the following functions:
|
4) Action_name (Must be aligned with your plan): The name of the action. Typically corresponds to the execution of the first step in your plan.
|
||||||
|
Can be one of the following functions:
|
||||||
- CHATBOX which you can use to talk with the user. The input is your intentions for the dialogue. Be specific.
|
- CHATBOX which you can use to talk with the user. The input is your intentions for the dialogue. Be specific.
|
||||||
- CHECKINVENTORY which you can use to check info about wine in your inventory. The input is a search term in verbal English.
|
- CHECKINVENTORY which you can use to check info about wine in your inventory. The input is a search term in verbal English.
|
||||||
Good query example: black car, a stereo, 200 mile range, electric motor.
|
Good query example: black car, a stereo, 200 mile range, electric motor.
|
||||||
- PRESENTBOX which you can use to introduce / suggest / recommend wine label you just found in the inventory to the user. Not for general conversation nor follow up conversation.
|
- PRESENTBOX which you can use to introduce / suggest / recommend a wine label from the inventory to the user when it hasn't been introduced before. Not for general conversation nor follow up conversation.
|
||||||
The input is instructions on how you want the presentation to be conducted.
|
The input is instructions on how you want the presentation to be conducted.
|
||||||
Here are some input examples,
|
Here are some input examples,
|
||||||
"First, provide detailed introductions of Zena Crown, Schrader Cabernet Sauvignon.
|
"First, provide detailed introductions of Zena Crown, Schrader Cabernet Sauvignon.
|
||||||
Second, if there are multiple wines, offer a thorough comparison of each option, highlighting their differences.
|
Second, if there are multiple wines, offer a thorough comparison of each option, highlighting their differences.
|
||||||
Third, explain the potential impact each option could bring to the user."
|
Third, explain the potential impact each option could bring to the user."
|
||||||
- ENDCONVERSATION which you can use when you want to finish the conversation with the user. The input is "NA".
|
- ENDCONVERSATION which you can use when you believe the user has concluded their interaction, to properly end the conversation with them. Input is "NA".
|
||||||
4) Action_input: input of the action
|
5) Action_input: input of the action
|
||||||
5) Mentioning_wine: Are you mentioning specific wine label or winery to the user? Can be "Yes" or "No"
|
6) Mentioning_winery: Are there any winery names mentioned in your response? Can be the names of the wineries or "None".
|
||||||
|
|
||||||
You should only respond in format as described below:
|
You should only respond in format as described below:
|
||||||
Understanding: ...
|
Understanding: ...
|
||||||
@@ -289,7 +300,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
|
|||||||
Plan: ...
|
Plan: ...
|
||||||
Action_name: ...
|
Action_name: ...
|
||||||
Action_input: ...
|
Action_input: ...
|
||||||
Mentioning_wine: ...
|
Mentioning_winery: ...
|
||||||
|
|
||||||
Let's begin!
|
Let's begin!
|
||||||
"""
|
"""
|
||||||
@@ -312,10 +323,10 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# get only the latest 3 conversation
|
# # get only the latest 3 conversation
|
||||||
x = length(a.chathistory) < 3 ? length(a.chathistory) : 3
|
# x = length(a.chathistory) < 3 ? length(a.chathistory) : 3
|
||||||
x = x - 1
|
# x = x - 1
|
||||||
chathistory = vectorOfDictToText(a.chathistory[end-x:end])
|
# chathistory = vectorOfDictToText(a.chathistory[end-x:end])
|
||||||
|
|
||||||
errornote = ""
|
errornote = ""
|
||||||
response = nothing # placeholder for show when error msg show up
|
response = nothing # placeholder for show when error msg show up
|
||||||
@@ -343,7 +354,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
|
|||||||
try
|
try
|
||||||
response = a.text2textInstructLLM(prompt)
|
response = a.text2textInstructLLM(prompt)
|
||||||
responsedict = GeneralUtils.textToDict(response,
|
responsedict = GeneralUtils.textToDict(response,
|
||||||
["Understanding", "Reasoning", "Plan", "Action_name", "Action_input", "Mentioning_wine"],
|
["Understanding", "Reasoning", "Plan", "Action_name", "Action_input", "Mentioning_winery"],
|
||||||
rightmarker=":", symbolkey=true, lowercasekey=true)
|
rightmarker=":", symbolkey=true, lowercasekey=true)
|
||||||
|
|
||||||
if responsedict[:action_name] ∉ ["CHATBOX", "PRESENTBOX", "CHECKINVENTORY", "ENDCONVERSATION"]
|
if responsedict[:action_name] ∉ ["CHATBOX", "PRESENTBOX", "CHECKINVENTORY", "ENDCONVERSATION"]
|
||||||
@@ -358,7 +369,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
|
|||||||
end
|
end
|
||||||
|
|
||||||
# check if there are more than 1 key per categories
|
# check if there are more than 1 key per categories
|
||||||
for i ∈ [:understanding, :plan, :action_name, :action_input, :mentioning_wine]
|
for i ∈ [:understanding, :plan, :action_name, :action_input, :mentioning_winery]
|
||||||
matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i)
|
matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i)
|
||||||
if length(matchkeys) > 1
|
if length(matchkeys) > 1
|
||||||
error("DecisionMaker has more than one key per categories")
|
error("DecisionMaker has more than one key per categories")
|
||||||
@@ -368,23 +379,30 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
|
|||||||
println("\n~~~ Yiem decisionMaker() ", @__FILE__, " ", @__LINE__)
|
println("\n~~~ Yiem decisionMaker() ", @__FILE__, " ", @__LINE__)
|
||||||
pprintln(Dict(responsedict))
|
pprintln(Dict(responsedict))
|
||||||
|
|
||||||
# check if LLM recommend wine before checking inventory
|
#[WORKING] check whether winery is from agent's memory
|
||||||
isMemEmpty = isempty(a.memory[:shortmem])
|
mentioned_winery = strip.(split(responsedict[:mentioning_winery], ","))
|
||||||
if occursin("Yes", responsedict[:mentioning_wine]) && isMemEmpty &&
|
for i in mentioned_winery
|
||||||
responsedict[:action_name] != "CHECKINVENTORY"
|
if i != "None" && !occursin(i, timeline)
|
||||||
|
errornote = "Note: Before recommending a wine, ensure it's in your inventory. Check your stock first."
|
||||||
errornote = "Note: You can't recommend wines yet. You must check your inventory before recommending wine to the user."
|
error("Before recommending a wine, ensure it's in your inventory. Check your stock first.")
|
||||||
error("You can't recommend wines yet. You must check your inventory before recommending wines")
|
end
|
||||||
elseif responsedict[:action_name] == "PRESENTBOX" && isMemEmpty &&
|
|
||||||
responsedict[:action_name] != "CHECKINVENTORY"
|
|
||||||
|
|
||||||
errornote = "Note: You can't recommend wines yet. You must check your inventory before recommending wine to the user."
|
|
||||||
error("You can't recommend wines yet. You must check your inventory before recommending wines")
|
|
||||||
else
|
|
||||||
errornote = ""
|
|
||||||
end
|
end
|
||||||
|
|
||||||
delete!(responsedict, :mentioning_wine)
|
# # check if LLM recommend wine before checking inventory
|
||||||
|
# isMemEmpty = isempty(a.memory[:shortmem])
|
||||||
|
# if !occursin("None", responsedict[:mentioning_winery]) && isMemEmpty &&
|
||||||
|
# responsedict[:action_name] != "CHECKINVENTORY"
|
||||||
|
|
||||||
|
# errornote = "Note: You can't recommend wines yet. You must check your inventory before recommending wine to the user."
|
||||||
|
# error("You can't recommend wines yet. You must check your inventory before recommending wines")
|
||||||
|
# else
|
||||||
|
|
||||||
|
if responsedict[:action_name] == "PRESENTBOX" && mentioned_winery == "None"
|
||||||
|
errornote = "Note: Before recommending a wine, ensure it's in your inventory. Check your stock first."
|
||||||
|
error("Before recommending a wine, ensure it's in your inventory. Check your stock first.")
|
||||||
|
end
|
||||||
|
|
||||||
|
delete!(responsedict, :mentioning_winery)
|
||||||
|
|
||||||
return responsedict
|
return responsedict
|
||||||
catch e
|
catch e
|
||||||
@@ -1044,7 +1062,8 @@ julia>
|
|||||||
# Signature
|
# Signature
|
||||||
"""
|
"""
|
||||||
function generatechat(a::sommelier)
|
function generatechat(a::sommelier)
|
||||||
systemmsg = """
|
systemmsg =
|
||||||
|
"""
|
||||||
Your name is $(a.name). You are a helpful assistant acting as a polite, website-based sommelier for an online wine store.
|
Your name is $(a.name). You are a helpful assistant acting as a polite, website-based sommelier for an online wine store.
|
||||||
You are currently talking with the user.
|
You are currently talking with the user.
|
||||||
Your goal includes:
|
Your goal includes:
|
||||||
@@ -1070,11 +1089,11 @@ function generatechat(a::sommelier)
|
|||||||
- If the user interrupts, prioritize the user
|
- If the user interrupts, prioritize the user
|
||||||
|
|
||||||
You should then respond to the user with:
|
You should then respond to the user with:
|
||||||
1) Mentioning_wine: Are you going to mentioning specific wine label or winery to the user? Can be "Yes" or "No"
|
1) Mentioning_winery: Are there any winery names mentioned in your response? Can be the names of the wineries or "None".
|
||||||
2) Chat: Given the situation, what would you say to the user?
|
2) Chat: Given the situation, How would you respond to the user in a way that expresses your thoughts while keeping the conversation going smoothly?
|
||||||
|
|
||||||
You should only respond in format as described below:
|
You should only respond in format as described below:
|
||||||
Mentioning_wine: ...
|
Mentioning_winery: ...
|
||||||
Chat: ...
|
Chat: ...
|
||||||
|
|
||||||
Let's begin!
|
Let's begin!
|
||||||
@@ -1113,7 +1132,7 @@ function generatechat(a::sommelier)
|
|||||||
|
|
||||||
try
|
try
|
||||||
response = a.text2textInstructLLM(prompt)
|
response = a.text2textInstructLLM(prompt)
|
||||||
responsedict = GeneralUtils.textToDict(response, ["Mentioning_wine", "Chat"],
|
responsedict = GeneralUtils.textToDict(response, ["Mentioning_winery", "Chat"],
|
||||||
rightmarker=":", symbolkey=true, lowercasekey=true)
|
rightmarker=":", symbolkey=true, lowercasekey=true)
|
||||||
|
|
||||||
for i ∈ [:chat]
|
for i ∈ [:chat]
|
||||||
@@ -1140,7 +1159,7 @@ function generatechat(a::sommelier)
|
|||||||
|
|
||||||
# check if LLM recommend wine before checking inventory
|
# check if LLM recommend wine before checking inventory
|
||||||
isMemEmpty = isempty(a.memory[:shortmem])
|
isMemEmpty = isempty(a.memory[:shortmem])
|
||||||
if occursin("Yes", responsedict[:mentioning_wine]) && isMemEmpty
|
if occursin("Yes", responsedict[:mentioning_winery]) && isMemEmpty
|
||||||
errornote = "Note: You can't recommend wines yet. You must check your inventory before recommending wine to the user."
|
errornote = "Note: You can't recommend wines yet. You must check your inventory before recommending wine to the user."
|
||||||
error("You must check your inventory before recommending wine")
|
error("You must check your inventory before recommending wine")
|
||||||
elseif occursin("(check", responsedict[:chat]) || occursin("*check", responsedict[:chat]) ||
|
elseif occursin("(check", responsedict[:chat]) || occursin("*check", responsedict[:chat]) ||
|
||||||
@@ -1153,7 +1172,7 @@ function generatechat(a::sommelier)
|
|||||||
end
|
end
|
||||||
|
|
||||||
a.memory[:CHATBOX] = "" # delete content because it no longer used.
|
a.memory[:CHATBOX] = "" # delete content because it no longer used.
|
||||||
delete!(responsedict, :mentioning_wine)
|
delete!(responsedict, :mentioning_winery)
|
||||||
result = responsedict[:chat]
|
result = responsedict[:chat]
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -1283,7 +1302,8 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
|
|||||||
# Let's begin!
|
# Let's begin!
|
||||||
# """
|
# """
|
||||||
|
|
||||||
systemmsg = """
|
systemmsg =
|
||||||
|
"""
|
||||||
Your name is $(a.name). You are a helpful assistant acting as a polite, website-based sommelier for $(a.retailername)'s online store.
|
Your name is $(a.name). You are a helpful assistant acting as a polite, website-based sommelier for $(a.retailername)'s online store.
|
||||||
Your goal includes:
|
Your goal includes:
|
||||||
1) Help the user select the best wines from your inventory that align with the user's preferences
|
1) Help the user select the best wines from your inventory that align with the user's preferences
|
||||||
@@ -1343,7 +1363,7 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
|
|||||||
Q: Are they allergic to milk?
|
Q: Are they allergic to milk?
|
||||||
A: According to the situation, since they mentioned a cappuccino before, it seems they are not allergic to milk.
|
A: According to the situation, since they mentioned a cappuccino before, it seems they are not allergic to milk.
|
||||||
|
|
||||||
Q: Have I searched the inventory yet?
|
Q: Have I checked the inventory yet?
|
||||||
A: According to the situation, no. I need more information.
|
A: According to the situation, no. I need more information.
|
||||||
|
|
||||||
Q: Should I check the inventory now?
|
Q: Should I check the inventory now?
|
||||||
@@ -1355,11 +1375,11 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
|
|||||||
Q: Which items are within the user price range? And which items are out of the user price rance?
|
Q: Which items are within the user price range? And which items are out of the user price rance?
|
||||||
A: According to the situation, ...
|
A: According to the situation, ...
|
||||||
|
|
||||||
Q: Do I have what the user wants in stock?
|
Q: Do I have them in stock?
|
||||||
A: According to the situation, ...
|
A: According to the situation, ...
|
||||||
|
|
||||||
Q: Did I introduce the coffee blend varieties to the user yet?
|
Q: Did I introduce the coffee blend varieties to the user yet?
|
||||||
A: According to the situation, no, I didn't because I have not searched the inventory yet.
|
A: According to the situation, No, because I haven't checked the inventory yet.
|
||||||
|
|
||||||
Q: Am I certain about the information I'm going to share with the user, or should I verify the information first?
|
Q: Am I certain about the information I'm going to share with the user, or should I verify the information first?
|
||||||
A: According to the situation, ...
|
A: According to the situation, ...
|
||||||
@@ -1408,7 +1428,17 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
|
|||||||
|
|
||||||
try
|
try
|
||||||
response = text2textInstructLLM(prompt)
|
response = text2textInstructLLM(prompt)
|
||||||
|
|
||||||
|
# sometime LLM generate more than 1 Understanding:
|
||||||
|
understanding_number = count("Understanding:", response)
|
||||||
|
if understanding_number > 1
|
||||||
|
x = split(response, "Understanding:")[2]
|
||||||
|
response = "Understanding:" * x
|
||||||
|
end
|
||||||
|
|
||||||
q_number = count("Q", response)
|
q_number = count("Q", response)
|
||||||
|
|
||||||
|
# check for valid response
|
||||||
if q_number < 3
|
if q_number < 3
|
||||||
error("too few questions only $q_number questions are generated ", @__FILE__, " ", @__LINE__)
|
error("too few questions only $q_number questions are generated ", @__FILE__, " ", @__LINE__)
|
||||||
# check whether "A1" is in the response, if not error.
|
# check whether "A1" is in the response, if not error.
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
|
|||||||
User's query: ...
|
User's query: ...
|
||||||
|
|
||||||
The preference form requires the following information:
|
The preference form requires the following information:
|
||||||
wine_type, price, occasion, food_to_be_paired_with_wine, country, grape_variety, flavors, aromas.
|
wine_type, price, occasion, food_to_be_paired_with_wine, region, country, grape_variety, flavors, aromas.
|
||||||
|
|
||||||
You must follow the following guidelines:
|
You must follow the following guidelines:
|
||||||
1) If specific information required in the preference form is not available in the query or there isn't any, mark with 'NA' to indicate this.
|
1) If specific information required in the preference form is not available in the query or there isn't any, mark with 'NA' to indicate this.
|
||||||
|
|||||||
Reference in New Issue
Block a user