This commit is contained in:
narawat lamaiin
2025-04-29 11:01:36 +07:00
parent 7ca4f5276d
commit f19f302bd9
2 changed files with 184 additions and 172 deletions

View File

@@ -97,7 +97,7 @@ julia> output_thoughtDict = Dict(
# Signature # Signature
""" """
function decisionMaker(a::T; recent::Integer=10 function decisionMaker(a::T; recent::Integer=10, maxattempt=10
) where {T<:agent} ) where {T<:agent}
# lessonDict = copy(JSON3.read("lesson.json")) # lessonDict = copy(JSON3.read("lesson.json"))
@@ -179,9 +179,11 @@ function decisionMaker(a::T; recent::Integer=10
errornote = "" errornote = ""
response = nothing # placeholder for show when error msg show up response = nothing # placeholder for show when error msg show up
for attempt in 1:10 #[WORKING] add 1) 3 decisions samples 2) compare and choose the best decision (correct tolls etc)
for attempt in 1:maxattempt
if attempt > 1 if attempt > 1
println("\nYiemAgent decisionMaker() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nYiemAgent decisionMaker() attempt $attempt/$maxattempt ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end end
QandA = generatequestion(a, a.func[:text2textInstructLLM]; recent=3) QandA = generatequestion(a, a.func[:text2textInstructLLM]; recent=3)
@@ -231,7 +233,7 @@ function decisionMaker(a::T; recent::Integer=10
- CHECKINVENTORY allows you to check information about wines you want in your inventory. The input should be a specific search term in verbal English. A good search term should include details such as winery, wine name, vintage, region, country, wine type, grape varietal, tasting notes, wine price, occasion, food to be paired with wine, intensity, tannin, sweetness, acidity. - CHECKINVENTORY allows you to check information about wines you want in your inventory. The input should be a specific search term in verbal English. A good search term should include details such as winery, wine name, vintage, region, country, wine type, grape varietal, tasting notes, wine price, occasion, food to be paired with wine, intensity, tannin, sweetness, acidity.
Invalid query example: red wine that pair well with spicy food. Invalid query example: red wine that pair well with spicy food.
- PRESENTBOX which you can use to present wines you have found in your inventory to the user. The input are wine names that you want to present. - PRESENTBOX which you can use to present wines you have found in your inventory to the user. The input are wine names that you want to present.
- ENDCONVERSATION which you can use when the user has finished their conversation with you, so that you can properly end the conversation. Input is "NA". - ENDCONVERSATION which you can use to properly end the conversation with the user. Input is "NA".
4) Action_input: The input to the action you are about to perform. This should be aligned with the plan 4) Action_input: The input to the action you are about to perform. This should be aligned with the plan
@@ -243,12 +245,12 @@ function decisionMaker(a::T; recent::Integer=10
Let's begin! Let's begin!
$errornote
$context $context
Your recent events: Your recent events:
$timeline $timeline
Your Q&A: Your Q&A:
$QandA $QandA
P.S. $errornote
""" """
unformatPrompt = unformatPrompt =
@@ -301,7 +303,7 @@ function decisionMaker(a::T; recent::Integer=10
end end
if count > 1 if count > 1
errornote = "You must use only one function" errornote = "You must use only one function"
println("\nYiemAgent decisionMaker() $errornote\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent decisionMaker() $errornote\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -312,11 +314,11 @@ function decisionMaker(a::T; recent::Integer=10
missingkeys = [header[i] for i in zeroind] missingkeys = [header[i] for i in zeroind]
if 0 values(detected_kw) if 0 values(detected_kw)
errornote = "$missingkeys are missing from your previous response" errornote = "$missingkeys are missing from your previous response"
println("\nYiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
elseif sum(values(detected_kw)) > length(header) elseif sum(values(detected_kw)) > length(header)
errornote = "Your previous attempt has duplicated points" errornote = "Your previous attempt has duplicated points"
println("\nYiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -325,7 +327,7 @@ function decisionMaker(a::T; recent::Integer=10
if responsedict[:action_name] ["CHATBOX", "CHECKINVENTORY", "PRESENTBOX", "ENDCONVERSATION"] if responsedict[:action_name] ["CHATBOX", "CHECKINVENTORY", "PRESENTBOX", "ENDCONVERSATION"]
errornote = "Your previous attempt didn't use the given functions" errornote = "Your previous attempt didn't use the given functions"
println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -333,7 +335,7 @@ function decisionMaker(a::T; recent::Integer=10
for i Symbol.(dictkey) for i Symbol.(dictkey)
if length(responsedict[i]) == 0 if length(responsedict[i]) == 0
errornote = "$i is empty" errornote = "$i is empty"
println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true checkFlag = true
break break
end end
@@ -346,7 +348,7 @@ function decisionMaker(a::T; recent::Integer=10
matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i) matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i)
if length(matchkeys) > 1 if length(matchkeys) > 1
errornote = "Your previous attempt has more than one key per categories" errornote = "Your previous attempt has more than one key per categories"
println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true checkFlag = true
break break
end end
@@ -358,7 +360,7 @@ function decisionMaker(a::T; recent::Integer=10
detected_kw = GeneralUtils.detect_keyword(["pair", "pairs", "pairing", "well"], responsedict[:action_input]) detected_kw = GeneralUtils.detect_keyword(["pair", "pairs", "pairing", "well"], responsedict[:action_input])
if responsedict[:action_name] == "CHECKINVENTORY" && sum(values(detected_kw)) != 0 if responsedict[:action_name] == "CHECKINVENTORY" && sum(values(detected_kw)) != 0
errornote = "In your previous attempt, action_input for CHECKINVENTORY function is invalid" errornote = "In your previous attempt, action_input for CHECKINVENTORY function is invalid"
println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -386,7 +388,7 @@ function decisionMaker(a::T; recent::Integer=10
# then the agent is not supposed to recommend the wine # then the agent is not supposed to recommend the wine
if isWineInEvent == false if isWineInEvent == false
errornote = "You recommended wines that are not in your inventory before. Please only recommend wines that you have previously found in your inventory." errornote = "You recommended wines that are not in your inventory before. Please only recommend wines that you have previously found in your inventory."
println("\nERROR YiemAgent generatechat() $errornote $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent decisionMaker() $errornote $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
end end
@@ -399,14 +401,14 @@ function decisionMaker(a::T; recent::Integer=10
# check whether responsedict[:action_input] is the same as previous dialogue # check whether responsedict[:action_input] is the same as previous dialogue
if responsedict[:action_input] == a.chathistory[end][:text] if responsedict[:action_input] == a.chathistory[end][:text]
errornote = "In your previous attempt, you repeated the previous dialogue. Please try again." errornote = "In your previous attempt, you repeated the previous dialogue. Please try again."
println("\nERROR YiemAgent decisionMaker() $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
println("\nERROR YiemAgent generatechat() $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
println("\n$prompt", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\n$prompt", @__FILE__, ":", @__LINE__, " $(Dates.now())")
println("\n$response") # println("\n$response")
responsedict[:prompt] = prompt
return responsedict return responsedict
end end
error("DecisionMaker failed to generate a thought ", response) error("DecisionMaker failed to generate a thought ", response)
@@ -474,69 +476,10 @@ end
# responsedict = similarDecision # responsedict = similarDecision
# return responsedict # return responsedict
# else # else
# systemmsg =
# """
# Your name is $(a.name). You are a helpful English-speaking assistant, acting as a polite, website-based sommelier for $(a.retailername)'s wine store.
# Your goal includes:
# 1) Establish a connection with the customer by greeting them warmly
# 2) Guide them to select the best wines only from your store's 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 don't need any further assistance and invite them to comeback next time
# Your responsibility does NOT includes:
# 1) Requesting the user to place an order, make a purchase, or confirm the order. These are the job of our sales team at the store.
# 2) Processing sales orders or engaging in any other sales-related activities. These are the job of our sales team at the store.
# 3) Answering questions or offering additional services beyond those related to your store's wine recommendations such as discounts, quantity, rewards programs, promotions, delivery options, shipping, boxes, gift wrapping, packaging, personalized messages or something similar. These are the job of our sales team at the store.
# At each round of conversation, the user will gives you with the following information:
# Your recent events: latest 5 events of the situation
# Your Q&A: the question and answer you have asked yourself
# You must follow the following guidelines:
# - Focus on the latest event
# - Generally speaking, your inventory has some wines from France, the United States, Australia, Spain, and Italy, but you won't know exactly until you check your inventory.
# - All wines in your inventory are always in stock
# - Approach each customer with open-ended questions to understand their preferences, budget, and occasion. This will help you guide the conversation naturally while gathering essential insights. Once you have this information, you can efficiently check your inventory for the best match.
# - 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.
# - Spicy foods should not be paired with medium and full-bodied red wines.
# You should follow the following guidelines:
# - When searching an inventory, search as broadly as possible based on the information you have gathered so far.
# - Encourage the customer to explore different options and try new things.
# - 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:
# - Your store carries only wine.
# - Vintage 0 means non-vintage.
# You should then respond to the user with interleaving Thought, Plan, Action_name, Action_input:
# 1) Thought: Articulate your current understanding and consider the current situation.
# 2) Plan: Based on the current situation, state a complete action plan to complete the task. Be specific.
# 3) Action_name: (Typically corresponds to the execution of the first step in your plan) Can be one of the following function names:
# - CHATBOX which you can use to talk with the user. The input is your intentions for the dialogue. Be specific.
# - CHECKINVENTORY allows you to check information about wines you want in your inventory. The input should be a specific search term in verbal English. A good search term should include details such as winery, wine name, vintage, region, country, wine type, grape varietal, tasting notes, wine price, occasion, food to be paired with wine, intensity, tannin, sweetness, acidity.
# Invalid query example: red wine that pair well with spicy food.
# - PRESENTBOX which you can use to present wines you have found in your inventory to the user. The input are wine names that you want to present.
# - ENDCONVERSATION which you can use when the user has finished their conversation with you, so that you can properly end the conversation. Input is "NA".
# 4) Action_input: input of the action
# You should only respond in format as described below:
# Thought: ...
# Plan: ...
# Action_name: ...
# Action_input: ...
# Let's begin!
# """
# header = ["Thought:", "Plan:", "Action_name:", "Action_input:"] # header = ["Thought:", "Plan:", "Action_name:", "Action_input:"]
# dictkey = ["thought", "plan", "action_name", "action_input"] # dictkey = ["thought", "plan", "action_name", "action_input"]
# chathistory = chatHistoryToText(a.chathistory)
# context = # may b add wine name instead of the hold wine data is better # context = # may b add wine name instead of the hold wine data is better
# if length(a.memory[:shortmem][:available_wine]) != 0 # if length(a.memory[:shortmem][:available_wine]) != 0
# winenames = [] # winenames = []
@@ -559,22 +502,78 @@ end
# end # end
# QandA = generatequestion(a, a.func[:text2textInstructLLM]; recent=3) # QandA = generatequestion(a, a.func[:text2textInstructLLM]; recent=3)
# systemmsg =
# usermsg =
# """ # """
# Your name is $(a.name). You are a helpful English-speaking assistant, acting as a polite, website-based sommelier for $(a.retailername)'s wine store.
# Your goal includes:
# 1) Establish a connection with the customer by greeting them warmly
# 2) Guide them to select the best wines only from your store's 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 don't need any further assistance and invite them to comeback next time
# Your responsibility does NOT includes:
# 1) Requesting the user to place an order, make a purchase, or confirm the order. These are the job of our sales team at the store.
# 2) Processing sales orders or engaging in any other sales-related activities. These are the job of our sales team at the store.
# 3) Answering questions or offering additional services beyond those related to your store's wine recommendations such as discounts, quantity, rewards programs, promotions, delivery options, shipping, boxes, gift wrapping, packaging, personalized messages or something similar. These are the job of our sales team at the store.
# At each round of conversation, you will be given the following information:
# Your recent events: latest 5 events of the situation
# Your Q&A: the question and answer you have asked yourself
# You must follow the following guidelines:
# - Focus on the latest event
# - Generally speaking, your inventory has some wines from France, the United States, Australia, Spain, and Italy, but you won't know exactly until you check your inventory.
# - All wines in your inventory are always in stock
# - Approach each customer with open-ended questions to understand their preferences, budget, and occasion. This will help you guide the conversation naturally while gathering essential insights. Once you have this information, you can efficiently check your inventory for the best match.
# - 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.
# - Spicy foods should not be paired with medium and full-bodied red wines.
# You should follow the following guidelines:
# - When searching an inventory, search as broadly as possible based on the information you have gathered so far.
# - Encourage the customer to explore different options and try new things.
# - If you are unable to locate the desired item after multiple attempts, it may 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:
# - Your store carries only wine.
# - Vintage 0 means non-vintage.
# You should then respond to the user with interleaving Thought, Plan, Action_name, Action_input:
# 1) Thought: Articulate your current understanding and consider the current situation.
# 2) Plan: Based on the current situation, state a complete action plan to complete the task. Be specific.
# 3) Action_name: (Typically corresponds to the execution of the first step in your plan) Can be one of the following function names:
# - CHATBOX which you can use to talk with the user. The input is your intentions for the dialogue. Be specific.
# - CHECKINVENTORY allows you to check information about wines you want in your inventory. The input should be a specific search term in verbal English. A good search term should include details such as winery, wine name, vintage, region, country, wine type, grape varietal, tasting notes, wine price, occasion, food to be paired with wine, intensity, tannin, sweetness, acidity.
# Invalid query example: red wine that pair well with spicy food.
# - PRESENTBOX which you can use to present wines you have found in your inventory to the user. The input are wine names that you want to present.
# - ENDCONVERSATION which you can use when the user has finished their conversation with you, so that you can properly end the conversation. Input is "NA".
# 4) Action_input: The input to the action you are about to perform. This should be aligned with the plan
# You should only respond in format as described below:
# Thought: ...
# Plan: ...
# Action_name: ...
# Action_input: ...
# Let's begin!
# $context # $context
# Your recent events: $timeline # Your recent events:
# Your Q&A: $QandA) # $timeline
# $errornote # Your Q&A:
# $QandA
# P.S. $errornote
# """ # """
# unformatPrompt = # unformatPrompt =
# [ # [
# Dict(:name => "system", :text => systemmsg), # Dict(:name => "system", :text => systemmsg),
# Dict(:name => "user", :text => usermsg)
# ] # ]
# #BUG found wine is "count 0" invalid return from CHECKINVENTORY() # # found wine is "count 0" invalid return from CHECKINVENTORY()
# # check if winename in shortmem occurred in chathistory. if not, skip decision and imediately use PRESENTBOX # # check if winename in shortmem occurred in chathistory. if not, skip decision and imediately use PRESENTBOX
# # if length(a.memory[:shortmem][:found_wine]) != 0 # # if length(a.memory[:shortmem][:found_wine]) != 0
# # # check if wine name mentioned in recentevents, only check first wine name is enough # # # check if wine name mentioned in recentevents, only check first wine name is enough
@@ -603,12 +602,12 @@ end
# # change qwen format put in model format # # change qwen format put in model format
# prompt = GeneralUtils.formatLLMtext(unformatPrompt, "granite3") # prompt = GeneralUtils.formatLLMtext(unformatPrompt, "granite3")
# response = a.func[:text2textInstructLLM](prompt) # response = a.func[:text2textInstructLLM](prompt)
# response = GeneralUtils.remove_french_accents(response) # response = GeneralUtils.remove_french_accents(response)
# response = replace(response, "**"=>"") # response = replace(response, "**"=>"")
# response = replace(response, "***"=>"") # response = replace(response, "***"=>"")
# response = replace(response, "<|eot_id|>"=>"") # response = replace(response, "<|eot_id|>"=>"")
# response = GeneralUtils.deFormatLLMtext(response, "granite3")
# # check if response contain more than one functions from ["CHATBOX", "CHECKINVENTORY", "ENDCONVERSATION"] # # check if response contain more than one functions from ["CHATBOX", "CHECKINVENTORY", "ENDCONVERSATION"]
# count = 0 # count = 0
@@ -619,7 +618,7 @@ end
# end # end
# if count > 1 # if count > 1
# errornote = "You must use only one function" # errornote = "You must use only one function"
# println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\nERROR YiemAgent decisionMaker() $errornote\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue # continue
# end # end
@@ -630,11 +629,11 @@ end
# missingkeys = [header[i] for i in zeroind] # missingkeys = [header[i] for i in zeroind]
# if 0 ∈ values(detected_kw) # if 0 ∈ values(detected_kw)
# errornote = "$missingkeys are missing from your previous response" # errornote = "$missingkeys are missing from your previous response"
# println("\nYiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\nERROR YiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue # continue
# elseif sum(values(detected_kw)) > length(header) # elseif sum(values(detected_kw)) > length(header)
# errornote = "Your previous attempt has duplicated points" # errornote = "Your previous attempt has duplicated points"
# println("\nYiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\nERROR YiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue # continue
# end # end
@@ -643,7 +642,7 @@ end
# if responsedict[:action_name] ∉ ["CHATBOX", "CHECKINVENTORY", "PRESENTBOX", "ENDCONVERSATION"] # if responsedict[:action_name] ∉ ["CHATBOX", "CHECKINVENTORY", "PRESENTBOX", "ENDCONVERSATION"]
# errornote = "Your previous attempt didn't use the given functions" # errornote = "Your previous attempt didn't use the given functions"
# println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\nERROR YiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue # continue
# end # end
@@ -651,7 +650,7 @@ end
# for i ∈ Symbol.(dictkey) # for i ∈ Symbol.(dictkey)
# if length(responsedict[i]) == 0 # if length(responsedict[i]) == 0
# errornote = "$i is empty" # errornote = "$i is empty"
# println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\nERROR YiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# checkFlag = true # checkFlag = true
# break # break
# end # end
@@ -664,7 +663,7 @@ end
# matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i) # matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i)
# if length(matchkeys) > 1 # if length(matchkeys) > 1
# errornote = "Your previous attempt has more than one key per categories" # errornote = "Your previous attempt has more than one key per categories"
# println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\nERROR YiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# checkFlag = true # checkFlag = true
# break # break
# end # end
@@ -676,7 +675,7 @@ end
# detected_kw = GeneralUtils.detect_keyword(["pair", "pairs", "pairing", "well"], responsedict[:action_input]) # detected_kw = GeneralUtils.detect_keyword(["pair", "pairs", "pairing", "well"], responsedict[:action_input])
# if responsedict[:action_name] == "CHECKINVENTORY" && sum(values(detected_kw)) != 0 # if responsedict[:action_name] == "CHECKINVENTORY" && sum(values(detected_kw)) != 0
# errornote = "In your previous attempt, action_input for CHECKINVENTORY function is invalid" # errornote = "In your previous attempt, action_input for CHECKINVENTORY function is invalid"
# println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\nERROR YiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue # continue
# end # end
@@ -704,23 +703,35 @@ end
# # then the agent is not supposed to recommend the wine # # then the agent is not supposed to recommend the wine
# if isWineInEvent == false # if isWineInEvent == false
# errornote = "You recommended wines that are not in your inventory before. Please only recommend wines that you have previously found in your inventory." # errornote = "You recommended wines that are not in your inventory before. Please only recommend wines that you have previously found in your inventory."
# println("\nERROR YiemAgent generatechat() $errornote $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\nERROR YiemAgent decisionMaker() $errornote $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue # continue
# end # end
# end # end
# delete!(responsedict, :mentioned_winery) # delete!(responsedict, :mentioned_winery)
# responsedict[:systemmsg] = systemmsg # responsedict[:systemmsg] = systemmsg
# responsedict[:usermsg] = usermsg
# responsedict[:unformatPrompt] = unformatPrompt # responsedict[:unformatPrompt] = unformatPrompt
# responsedict[:QandA] = QandA # responsedict[:QandA] = QandA
# # check whether responsedict[:action_input] is the same as previous dialogue
# if responsedict[:action_input] == a.chathistory[end][:text]
# errornote = "In your previous attempt, you repeated the previous dialogue. Please try again."
# println("\nERROR YiemAgent decisionMaker() $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# # println("\n$prompt", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# # println("\n$response")
# responsedict[:prompt] = prompt
# return responsedict # return responsedict
# end # end
# error("DecisionMaker failed to generate a thought ", response) # error("DecisionMaker failed to generate a thought ", response)
# end # end
# end # end
""" Assigns a scalar value to each new child node to be used for selec- """ Assigns a scalar value to each new child node to be used for selec-
tion and backpropagation. This value effectively quantifies the agent's progress in task completion, tion and backpropagation. This value effectively quantifies the agent's progress in task completion,
serving as a heuristic to steer the search algorithm towards the most promising regions of the tree. serving as a heuristic to steer the search algorithm towards the most promising regions of the tree.
@@ -849,9 +860,11 @@ function evaluator(state::T1, text2textInstructLLM::Function
detected_kw = GeneralUtils.detect_keyword(header, response) detected_kw = GeneralUtils.detect_keyword(header, response)
if 0 values(detected_kw) if 0 values(detected_kw)
errornote = "\nSQL evaluator() response does not have all header" errornote = "\nSQL evaluator() response does not have all header"
println("\nERROR YiemAgent decisionMaker() $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
elseif sum(values(detected_kw)) > length(header) elseif sum(values(detected_kw)) > length(header)
errornote = "\nSQL evaluator() response has duplicated header" errornote = "\nSQL evaluator() response has duplicated header"
println("\nERROR YiemAgent decisionMaker() $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -886,7 +899,7 @@ function evaluator(state::T1, text2textInstructLLM::Function
# evaluation score as reward because different answers hold different value for the user. # evaluation score as reward because different answers hold different value for the user.
state[:reward] = responsedict[:score] state[:reward] = responsedict[:score]
end end
println("\nEvaluator() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR Evaluator() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
pprintln(Dict(responsedict)) pprintln(Dict(responsedict))
return responsedict[:score] return responsedict[:score]
@@ -1378,7 +1391,7 @@ function generatechat(a::sommelier, thoughtDict)
end end
chathistory = chatHistoryToText(a.chathistory) chathistory = chatHistoryToText(a.chathistory)
errornote = "" errornote = "N/A"
response = nothing # placeholder for show when error msg show up response = nothing # placeholder for show when error msg show up
yourthought = "$(thoughtDict[:thought]) $(thoughtDict[:plan])" yourthought = "$(thoughtDict[:thought]) $(thoughtDict[:plan])"
@@ -1400,10 +1413,10 @@ function generatechat(a::sommelier, thoughtDict)
usermsg = usermsg =
""" """
$errornote
Additional info: $context Additional info: $context
Your ongoing conversation with the user: $chathistory Your ongoing conversation with the user: $chathistory
Your thoughts: $yourthought1 Your thoughts: $yourthought1
P.S. $errornote
""" """
_prompt = _prompt =
@@ -1421,9 +1434,11 @@ function generatechat(a::sommelier, thoughtDict)
if occursin("respond:", response) if occursin("respond:", response)
errornote = "Your previous response contains 'response:' which is not allowed" errornote = "Your previous response contains 'response:' which is not allowed"
println("\nERROR YiemAgent generatechat() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent generatechat() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
elseif occursin("Your thoughts:", response) || occursin("your thoughts:", response) elseif occursin("Your thoughts:", response) || occursin("your thoughts:", response)
errornote = "You don't need to put 'Your thoughts:' in your response" errornote = "You don't need to put 'Your thoughts:' in your response"
println("\nERROR YiemAgent generatechat() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent generatechat() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end end
response = GeneralUtils.remove_french_accents(response) response = GeneralUtils.remove_french_accents(response)
response = replace(response, '*'=>"") response = replace(response, '*'=>"")
@@ -1457,7 +1472,7 @@ function generatechat(a::sommelier, thoughtDict)
continue continue
end end
println("\ngeneratechat() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nYiemAgent generatechat() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
pprintln(Dict(responsedict)) pprintln(Dict(responsedict))
# check whether an agent recommend wines before checking inventory or recommend wines # check whether an agent recommend wines before checking inventory or recommend wines
@@ -1499,7 +1514,7 @@ function generatechat(a::companion; converPartnerName::Union{String, Nothing}=no
# dictkey = ["dialogue"] # dictkey = ["dialogue"]
response = nothing # placeholder for show when error msg show up response = nothing # placeholder for show when error msg show up
errornote = "" errornote = "N/A"
llmkwargs=Dict( llmkwargs=Dict(
:num_ctx => 32768, :num_ctx => 32768,
:temperature => 0.3, :temperature => 0.3,
@@ -1509,7 +1524,7 @@ function generatechat(a::companion; converPartnerName::Union{String, Nothing}=no
println("\nYiemAgent generatechat() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nYiemAgent generatechat() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end end
systemmsg = a.systemmsg * "\nError note: $errornote\n" systemmsg = a.systemmsg * "\nP.S. $errornote\n"
_prompt = _prompt =
[ [
Dict(:name => "system", :text => systemmsg), Dict(:name => "system", :text => systemmsg),
@@ -1566,8 +1581,8 @@ function generatechat(a::companion; converPartnerName::Union{String, Nothing}=no
# responsedict = GeneralUtils.textToDict(response, header; # responsedict = GeneralUtils.textToDict(response, header;
# dictKey=dictkey, symbolkey=true) # dictKey=dictkey, symbolkey=true)
println("\n$prompt", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\n$prompt", @__FILE__, ":", @__LINE__, " $(Dates.now())")
println("\n $response") # println("\n $response")
return response return response
end end
error("generatechat failed to generate a response") error("generatechat failed to generate a response")
@@ -1730,7 +1745,7 @@ function generatequestion(a, text2textInstructLLM::Function;
Recap: $recap) Recap: $recap)
Additional info: $context Additional info: $context
Your recent events: $timeline Your recent events: $timeline
$errornote P.S. $errornote
""" """
_prompt = _prompt =
@@ -1766,7 +1781,8 @@ function generatequestion(a, text2textInstructLLM::Function;
# if wine is mentioned but not in timeline or shortmem, # if wine is mentioned but not in timeline or shortmem,
# then the agent is not supposed to recommend the wine # then the agent is not supposed to recommend the wine
if isWineInEvent == false if isWineInEvent == false
errornote = "Previously, You mentioned wines that is not in your inventory which is not allowed." errornote = "Your previous attempt mentioned wines that are not in your inventory which is not allowed."
println("\nERROR YiemAgent generatequestion() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
end end
@@ -1775,12 +1791,12 @@ function generatequestion(a, text2textInstructLLM::Function;
# check for valid response # check for valid response
if q_number < 1 if q_number < 1
errornote = "Your previous response has too few questions." errornote = "Your previous attempt has too few questions."
println("\nERROR YiemAgent generatequestion() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent generatequestion() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
# check whether "A1" is in the response, if not error. # check whether "A1" is in the response, if not error.
elseif !occursin("A1:", response) elseif !occursin("A1:", response)
errornote = "Your previous response does not have A1:" errornote = "Your previous attempt does not have A1:"
println("\nERROR YiemAgent generatequestion() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent generatequestion() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -1860,7 +1876,7 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
usermsg = """ usermsg = """
Total events: $(length(events)) Total events: $(length(events))
Events timeline: $timeline Events timeline: $timeline
$errornote P.S. $errornote
""" """
_prompt = _prompt =
@@ -1944,35 +1960,27 @@ function detectWineryName(a, text)
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3") prompt = GeneralUtils.formatLLMtext(_prompt, "granite3")
try response = a.func[:text2textInstructLLM](prompt)
response = a.func[:text2textInstructLLM](prompt) response = GeneralUtils.deFormatLLMtext(response, "granite3")
response = GeneralUtils.deFormatLLMtext(response, "granite3") println("\ndetectWineryName() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
println("\ndetectWineryName() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") pprintln(response)
pprintln(response)
# check whether response has all header # check whether response has all header
detected_kw = GeneralUtils.detect_keyword(header, response) detected_kw = GeneralUtils.detect_keyword(header, response)
if 0 values(detected_kw) if 0 values(detected_kw)
errornote = "\nYiemAgent detectWineryName() response does not have all header" errornote = "\nYiemAgent detectWineryName() response does not have all header"
continue continue
elseif sum(values(detected_kw)) > length(header) elseif sum(values(detected_kw)) > length(header)
errornote = "\nYiemAgent detectWineryName() response has duplicated header" errornote = "\nYiemAgent detectWineryName() response has duplicated header"
continue continue
end
responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true)
result = responsedict[:winery_names]
return result
catch e
io = IOBuffer()
showerror(io, e)
errorMsg = String(take!(io))
st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
println("\n Attempt $attempt. Error occurred: $errorMsg\n$st ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end end
responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true)
result = responsedict[:winery_names]
return result
end end
error("detectWineryName failed to generate a response") error("detectWineryName failed to generate a response")
end end

View File

@@ -358,36 +358,36 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
Occasion: the occasion the user is having the wine for Occasion: the occasion the user is having the wine for
Food_to_be_paired_with_wine: food that the user will be served with the wine such as poultry, fish, steak, etc Food_to_be_paired_with_wine: food that the user will be served with the wine such as poultry, fish, steak, etc
You should only respond in format as described below: You should only respond in format as described below:
Thought: ... Thought: ...
Wine_name: ... Wine_name: ...
Winery: ... Winery: ...
Vintage: ... Vintage: ...
Region: ... Region: ...
Country: ... Country: ...
Wine_type: Wine_type:
Grape_varietal: ... Grape_varietal: ...
Tasting_notes: ... Tasting_notes: ...
Wine_price: ... Wine_price: ...
Occasion: ... Occasion: ...
Food_to_be_paired_with_wine: ... Food_to_be_paired_with_wine: ...
Here are some example: Here are some example:
User's query: red, Chenin Blanc, Riesling, 20 USD User's query: red, Chenin Blanc, Riesling, 20 USD
{"reasoning": ..., "winery": "NA", "wine_name": "NA", "vintage": "NA", "region": "NA", "country": "NA", "wine_type": "red, white", "grape_varietal": "Chenin Blanc, Riesling", "tasting_notes": "NA", "wine_price": "0-20", "occasion": "NA", "food_to_be_paired_with_wine": "NA"} {"reasoning": ..., "winery": "NA", "wine_name": "NA", "vintage": "NA", "region": "NA", "country": "NA", "wine_type": "red, white", "grape_varietal": "Chenin Blanc, Riesling", "tasting_notes": "NA", "wine_price": "0-20", "occasion": "NA", "food_to_be_paired_with_wine": "NA"}
User's query: Domaine du Collier Saumur Blanc 2019, France, white, Merlot User's query: Domaine du Collier Saumur Blanc 2019, France, white, Merlot
{"reasoning": ..., "winery": "Domaine du Collier", "wine_name": "Saumur Blanc", "vintage": "2019", "region": "Saumur", "country": "France", "wine_type": "white", "grape_varietal": "Merlot", "tasting_notes": "NA", "wine_price": "NA", "occasion": "NA", "food_to_be_paired_with_wine": "NA"} {"reasoning": ..., "winery": "Domaine du Collier", "wine_name": "Saumur Blanc", "vintage": "2019", "region": "Saumur", "country": "France", "wine_type": "white", "grape_varietal": "Merlot", "tasting_notes": "NA", "wine_price": "NA", "occasion": "NA", "food_to_be_paired_with_wine": "NA"}
Let's begin! Let's begin!
""" """
header = ["Thought:", "Wine_name:", "Winery:", "Vintage:", "Region:", "Country:", "Wine_type:", "Grape_varietal:", "Tasting_notes:", "Wine_price:", "Occasion:", "Food_to_be_paired_with_wine:"] header = ["Thought:", "Wine_name:", "Winery:", "Vintage:", "Region:", "Country:", "Wine_type:", "Grape_varietal:", "Tasting_notes:", "Wine_price:", "Occasion:", "Food_to_be_paired_with_wine:"]
dictkey = ["thought", "wine_name", "winery", "vintage", "region", "country", "wine_type", "grape_varietal", "tasting_notes", "wine_price", "occasion", "food_to_be_paired_with_wine"] dictkey = ["thought", "wine_name", "winery", "vintage", "region", "country", "wine_type", "grape_varietal", "tasting_notes", "wine_price", "occasion", "food_to_be_paired_with_wine"]
errornote = "" errornote = "N/A"
for attempt in 1:10 for attempt in 1:10
#[WORKING] I should add generatequestion() #[PENDING] I should add generatequestion()
if attempt > 1 if attempt > 1
println("\nYiemAgent extractWineAttributes_1() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nYiemAgent extractWineAttributes_1() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
@@ -396,7 +396,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
usermsg = usermsg =
""" """
User's query: $input User's query: $input
$errornote P.S. $errornote
""" """
_prompt = _prompt =
@@ -415,8 +415,8 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
checkFlag = false checkFlag = false
for word in header for word in header
if !occursin(word, response) if !occursin(word, response)
errornote = "$word attribute is missing in previous attempts" errornote = "In your previous attempts, the $word attribute is missing. Please try again."
println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nYiemAgent extractWineAttributes_1() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true checkFlag = true
break break
end end
@@ -426,10 +426,12 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
# check whether response has all header # check whether response has all header
detected_kw = GeneralUtils.detect_keyword(header, response) detected_kw = GeneralUtils.detect_keyword(header, response)
if 0 values(detected_kw) if 0 values(detected_kw)
errornote = "\nYiemAgent extractWineAttributes_1() response does not have all header" errornote = "In your previous attempts, the response does not have all header"
println("\nYiemAgent extractWineAttributes_1() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
elseif sum(values(detected_kw)) > length(header) elseif sum(values(detected_kw)) > length(header)
errornote = "\nYiemAgent extractWineAttributes_1() response has duplicated header" errornote = "In your previous attempts, the response has duplicated header"
println("\nYiemAgent extractWineAttributes_1() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
responsedict = GeneralUtils.textToDict(response, header; responsedict = GeneralUtils.textToDict(response, header;
@@ -453,8 +455,8 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
if responsedict[:wine_price] != "NA" if responsedict[:wine_price] != "NA"
# check whether wine_price is in ranged number # check whether wine_price is in ranged number
if !occursin('-', responsedict[:wine_price]) if !occursin('-', responsedict[:wine_price])
errornote = "wine_price must be a range number" errornote = "In your previous attempt, the 'wine_price' was not set to a ranged number. Please adjust it accordingly."
println("ERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true checkFlag = true
break break
end end
@@ -468,8 +470,8 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
end end
# price range like 100-100 is not good # price range like 100-100 is not good
if minprice == maxprice if minprice == maxprice
errornote = "wine_price with minimum equals to maximum is not valid" errornote = "In your previous attempt, you inputted 'wine_price' with a 'minimum' value equaling the 'maximum', which is not valid."
println("ERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true checkFlag = true
break break
end end
@@ -631,7 +633,7 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
""" """
$conversiontable $conversiontable
User's query: $input User's query: $input
$errornote P.S. $errornote
""" """
_prompt = _prompt =
@@ -649,10 +651,12 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
# check whether response has all header # check whether response has all header
detected_kw = GeneralUtils.detect_keyword(header, response) detected_kw = GeneralUtils.detect_keyword(header, response)
if 0 values(detected_kw) if 0 values(detected_kw)
errornote = "\nYiemAgent extractWineAttributes_2() response does not have all header" errornote = "In your previous attempt does not have all header"
println("\nERROR YiemAgent extractWineAttributes_2() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
elseif sum(values(detected_kw)) > length(header) elseif sum(values(detected_kw)) > length(header)
errornote = "\nYiemAgent extractWineAttributes_2() response has duplicated header" errornote = "In your previous attempt has duplicated header"
println("\nERROR YiemAgent extractWineAttributes_2() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -664,8 +668,8 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
keyword = Symbol(i * "_keyword") # e.g. sweetness_keyword keyword = Symbol(i * "_keyword") # e.g. sweetness_keyword
value = responsedict[keyword] value = responsedict[keyword]
if value != "NA" && !occursin(value, input) if value != "NA" && !occursin(value, input)
errornote = "WARNING. Keyword $keyword: $value does not appear in the input. You must use information from the input only" errornote = "In your previous attempt, keyword $keyword: $value does not appear in the input. You must use information from the input only"
println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent extractWineAttributes_2() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -681,7 +685,7 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
if !occursin("keyword", string(k)) if !occursin("keyword", string(k))
if v !== "NA" && (!occursin('-', v) || length(v) > 5) if v !== "NA" && (!occursin('-', v) || length(v) > 5)
errornote = "WARNING: The non-range value {$k: $v} is not allowed. It should be specified in a range format, i.e. min-max." errornote = "WARNING: The non-range value {$k: $v} is not allowed. It should be specified in a range format, i.e. min-max."
println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent extractWineAttributes_2() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
end end
@@ -757,7 +761,7 @@ function paraphrase(text2textInstructLLM::Function, text::String)
for attempt in 1:10 for attempt in 1:10
usermsg = """ usermsg = """
Text: $text Text: $text
$errornote P.S. $errornote
""" """
_prompt = _prompt =