diff --git a/src/interface.jl b/src/interface.jl index 609734d..d94fb39 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -176,55 +176,65 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen # systemmsg = # """ - # You are a helpful assistant acting as a polite, website-based sommelier for an online wine store. - # Your goal is: Help the user select the best wines from your inventory that align with the user's preferences. + # 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: + # 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: - # 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. + # 1) Make an informed decision about what you need to do to achieve the goal + # 2) Thanks the user when they don't need any further assistance and invite them to comeback next time - # Your responsibility does not include: - # 1) Processing sales orders or engaging in any other sales-related activities. + # Your responsibility do not include: + # 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: # Recap: ... # 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. # - 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. # - 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: - # - If the user interrupts, prioritize the user. - # - If you don't already know, find out the user's budget. - # - 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 follow the following guidelines: + # - Identifying at least four preferences before checking inventory significantly improves search results + # - 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. + + # For your information: + # - vintage 0 means non-vintage. - # You should then respond to the user with interleaving Thought, Plan, Action: - # 1) thought: - # - State your reasoning about the current situation. - # 2) 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: - # - 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. + # You should then respond to the user with interleaving Understanding, Reasoning, Plan, Action: + # 1) Understanding: + # - 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. # 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". - # 4) action_input: input of the action - # 5) mentioning_wine: Are you mentioning specific wine name to the user? Can be "Yes" or "No" + # 5) Action_input: input of the action + # 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: - # thought: ... - # plan: ... - # action_name: ... - # action_input: ... - # mentioning_wine: ... + # Understanding: ... + # Reasoning: ... + # Plan: ... + # Action_name: ... + # Action_input: ... + # Mentioning_winery: ... # 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. 2) Reasoning: - 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) Action_name (Must be aligned with your plan): The name of the action which can be one of the following functions: + 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. 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. - 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". - 4) 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" + 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 believe the user has concluded their interaction, to properly end the conversation with them. Input is "NA". + 5) Action_input: input of the action + 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: Understanding: ... @@ -289,7 +300,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen Plan: ... Action_name: ... Action_input: ... - Mentioning_wine: ... + Mentioning_winery: ... Let's begin! """ @@ -312,10 +323,10 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen end end - # get only the latest 3 conversation - x = length(a.chathistory) < 3 ? length(a.chathistory) : 3 - x = x - 1 - chathistory = vectorOfDictToText(a.chathistory[end-x:end]) + # # get only the latest 3 conversation + # x = length(a.chathistory) < 3 ? length(a.chathistory) : 3 + # x = x - 1 + # chathistory = vectorOfDictToText(a.chathistory[end-x:end]) errornote = "" 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 response = a.text2textInstructLLM(prompt) 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) 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 # 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) if length(matchkeys) > 1 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__) pprintln(Dict(responsedict)) - # check if LLM recommend wine before checking inventory - isMemEmpty = isempty(a.memory[:shortmem]) - if occursin("Yes", responsedict[:mentioning_wine]) && 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") - 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 = "" + #[WORKING] check whether winery is from agent's memory + mentioned_winery = strip.(split(responsedict[:mentioning_winery], ",")) + for i in mentioned_winery + if i != "None" && !occursin(i, timeline) + 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 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 catch e @@ -1044,41 +1062,42 @@ julia> # Signature """ function generatechat(a::sommelier) - systemmsg = """ - 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. - Your goal includes: - 1) Help the user select the best wines from your inventory that align with the user's preferences. + systemmsg = + """ + 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. + Your goal includes: + 1) Help the user select the best wines from your inventory that align with the user's preferences. - Your responsibility includes: - 1) Given the situation, convey your thoughts to the user. + Your responsibility includes: + 1) Given the situation, convey your thoughts to the user. - Your responsibility do not include: - 1) Asking or guiding the user to make a purchase - 2) Processing sales orders or engaging in any other sales-related activities + Your responsibility do not include: + 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: - Your ongoing conversation with the user: ... - Your thoughts: Your current thoughts in your mind - Context: ... + At each round of conversation, you will be given the current situation: + Your ongoing conversation with the user: ... + Your thoughts: Your current thoughts in your mind + Context: ... - You MUST follow the following guidelines: - - Do not offer additional services you didn't thought. + You MUST follow the following guidelines: + - Do not offer additional services you didn't thought. - You should follow the following guidelines: - - Focus on the latest conversation. - - If the user interrupts, prioritize the user + You should follow the following guidelines: + - Focus on the latest conversation. + - If the user interrupts, prioritize the user - 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" - 2) Chat: Given the situation, what would you say to the user? + You should then respond to the user with: + 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, 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: - Mentioning_wine: ... - Chat: ... + You should only respond in format as described below: + Mentioning_winery: ... + Chat: ... - Let's begin! - """ + Let's begin! + """ context = if length(a.memory[:shortmem]) > 0 @@ -1113,7 +1132,7 @@ function generatechat(a::sommelier) try response = a.text2textInstructLLM(prompt) - responsedict = GeneralUtils.textToDict(response, ["Mentioning_wine", "Chat"], + responsedict = GeneralUtils.textToDict(response, ["Mentioning_winery", "Chat"], rightmarker=":", symbolkey=true, lowercasekey=true) for i ∈ [:chat] @@ -1140,7 +1159,7 @@ function generatechat(a::sommelier) # check if LLM recommend wine before checking inventory 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." error("You must check your inventory before recommending wine") elseif occursin("(check", responsedict[:chat]) || occursin("*check", responsedict[:chat]) || @@ -1153,7 +1172,7 @@ function generatechat(a::sommelier) end a.memory[:CHATBOX] = "" # delete content because it no longer used. - delete!(responsedict, :mentioning_wine) + delete!(responsedict, :mentioning_winery) result = responsedict[:chat] return result @@ -1283,89 +1302,90 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St # Let's begin! # """ - 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 goal includes: - 1) Help the user select the best wines from your inventory that align with the user's preferences - 2) Thanks the user when they don't need any further assistance and invite them to comeback next time + 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 goal includes: + 1) Help the user select the best wines from your inventory that align with the user's preferences + 2) Thanks the user when they don't need any further assistance and invite them to comeback next time - Your responsibility includes: - 1) Ask yourself what to do about the current situation + Your responsibility includes: + 1) Ask yourself what to do about the current situation - Your responsibility does not include: - 1) Processing sales orders or engaging in any other sales-related activities. + Your responsibility does not include: + 1) Processing sales orders or engaging in any other sales-related activities. - At each round of conversation, you will be given the current situation: - Your recent events: ... - Context: ... + At each round of conversation, you will be given the current situation: + Your recent events: ... + Context: ... - You must follow the following guidelines: - - Your question should be specific, self-contained and not require any additional context. - - Do not generate any question or comments at the end. - - 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 must follow the following guidelines: + - Your question should be specific, self-contained and not require any additional context. + - Do not generate any question or comments at the end. + - 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: - - Focus on the latest conversation - - If the user interrupts, prioritize the user - - If you don't already know, find out the user's budget - - 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 follow the following guidelines: + - Focus on the latest conversation + - If the user interrupts, prioritize the user + - If you don't already know, find out the user's budget + - 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: - 1) Understanding: - - State your understanding about the current situation - 2) Q: Given the situation, "ask yourself" at least five, but no more than ten, questions - 3) A: Given the situation, "answer to yourself" the best you can + You should then respond to the user with: + 1) Understanding: + - State your understanding about the current situation + 2) Q: Given the situation, "ask yourself" at least five, but no more than ten, questions + 3) A: Given the situation, "answer to yourself" the best you can - You must only respond in format as described below: - Understanding: ... - Q1: ... - A1: ... - Q2: ... - A2: ... - Q3: ... - A3: ... - ... + You must only respond in format as described below: + Understanding: ... + Q1: ... + A1: ... + Q2: ... + A2: ... + Q3: ... + A3: ... + ... - Here are some examples: - Q: Why the user saying this? - A: According to the situation, ... + Here are some examples: + Q: Why the user saying this? + A: According to the situation, ... - Q: The user is asking for a cappuccino. Do I have it at my cafe? - A: No I don't. - - Q: Since I don't have a cappuccino but I have a Late, should I ask if they are okay with that? - A: Yes, I should. - - 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. - - Q: Have I searched the inventory yet? - A: According to the situation, no. I need more information. + Q: The user is asking for a cappuccino. Do I have it at my cafe? + A: No I don't. + + Q: Since I don't have a cappuccino but I have a Late, should I ask if they are okay with that? + A: Yes, I should. + + 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. + + Q: Have I checked the inventory yet? + A: According to the situation, no. I need more information. - Q: Should I check the inventory now? - A: According to the situation, ... + Q: Should I check the inventory now? + A: According to the situation, ... - Q: What do I have in the inventory? - A: According to the situation, ... + Q: What do I have in the inventory? + A: According to the situation, ... - Q: Which items are within the user price range? And which items are out of the user price rance? - A: According to the situation, ... + Q: Which items are within the user price range? And which items are out of the user price rance? + A: According to the situation, ... - Q: Do I have what the user wants in stock? - A: According to the situation, ... + Q: Do I have them in stock? + A: According to the situation, ... - 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. + Q: Did I introduce the coffee blend varieties to the user 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? - A: According to the situation, ... + 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, ... - Let's begin! - """ + Let's begin! + """ totalevents = length(a.memory[:events]) ind = @@ -1408,7 +1428,17 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St try 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) + + # check for valid response if q_number < 3 error("too few questions only $q_number questions are generated ", @__FILE__, " ", @__LINE__) # check whether "A1" is in the response, if not error. diff --git a/src/llmfunction.jl b/src/llmfunction.jl index 76b8179..e673716 100644 --- a/src/llmfunction.jl +++ b/src/llmfunction.jl @@ -346,7 +346,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2< User's query: ... 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: 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.