From 919d8ec85eb72043ed5b36db43b7696e8237a62b Mon Sep 17 00:00:00 2001 From: narawat lamaiin Date: Sun, 18 May 2025 17:21:51 +0700 Subject: [PATCH] update --- src/interface.jl | 35 +++++++++++++++++++++------------ src/llmfunction.jl | 49 +++++++++++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/src/interface.jl b/src/interface.jl index ae79aba..c5b5ad2 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -249,8 +249,9 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10 - CHATBOX which you can use to talk with the user. Be specific. - - CHECKINVENTORY allows you to check information about wines you want in your inventory's database. The input must be supported search criteria includeing: wine price, winery, name, vintage, region, country, type, grape varietal, tasting notes, occasion, food pairing, intensity, tannin, sweetness, and acidity. - Example query: "Dry, full-bodied red wine from Burgundy, France or Tuscany, Italy. Merlot varietal. price 100 to 1000 USD." + - CHECKWINE allows you to check information about wines you want in your inventory's database. The input must be supported search criteria includeing: wine price, winery, name, vintage, region, country, type, grape varietal, tasting notes, occasion, food pairing, intensity, tannin, sweetness, and acidity. + Example query 1: "Dry, full-bodied red wine from Burgundy region, France or Tuscany region, Italy. Merlot varietal. price 100 to 1000 USD." + Example query 2: "Red wine, bold intensity, price under 300 USD" - 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 to properly end the conversation with the user. Input is a dialogue where you wrap up the conversation, thank the user, and invite them to return next time. @@ -332,9 +333,9 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10 # responsedict = GeneralUtils.textToDict(response, header; # dictKey=dictkey, symbolkey=true) - if responsedict[:action_name] ∉ ["CHATBOX", "CHECKINVENTORY", "PRESENTBOX", "ENDCONVERSATION"] + if responsedict[:action_name] ∉ ["CHATBOX", "CHECKWINE", "PRESENTBOX", "ENDCONVERSATION"] errornote = "Your previous attempt didn't use the given functions" - println("\nERROR YiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("\nERROR YiemAgent decisionMaker() $errornote --> $(responsedict[:action_name])", @__FILE__, ":", @__LINE__, " $(Dates.now())") continue end @@ -420,7 +421,7 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10 if evaluationdict[:approved] == "no" mentor_comment = evaluationdict[:suggestion] errornote = "Your previous attempt was not good enough. Please try again. Here is the mentor's suggestion: $mentor_comment" - println("\nERROR YiemAgent decisionMaker() - $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("\nERROR YiemAgent decisionMaker() $errornote --> \n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") continue end @@ -814,7 +815,7 @@ function evaluator(a::T1, timeline, decisiondict, evaluateecontext - Under your supervision, a trainee sommelier is engaging with a store customer. Each time the customer speaks, the trainee will assess the situation, determine the next course of action, and pause to await your guidance before proceeding. - - To improve a trainee sommelier decision based on the store policy and guidelines. + - Improve a trainee sommelier decision based on the store policy and guidelines while ensuring seamless interactions between the trainee and customers. - trajectory: A conversation between your trainee and the customer that have occurred up until now @@ -1084,7 +1085,7 @@ function conversation(a::Union{companion, virtualcustomer}, userinput::Dict; actioninput=userinput[:text], ) ) - chatresponse = generatechat(a; converPartnerName=converPartnerName) + chatresponse = generatechat(a; converPartnerName=converPartnerName, recentEventNum=20) addNewMessage(a, "assistant", chatresponse; maximumMsg=maximumMsg) @@ -1127,8 +1128,8 @@ function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} wh response = if actionname == "CHATBOX" || actionname == "ENDCONVERSATION" (result=thoughtDict[:plan], errormsg=nothing, success=true) - elseif actionname == "CHECKINVENTORY" - checkinventory(a, actioninput) + elseif actionname == "CHECKWINE" + checkwine(a, actioninput) elseif actionname == "PRESENTBOX" (result=actioninput, errormsg=nothing, success=true) # elseif actionname == "ENDCONVERSATION" @@ -1200,7 +1201,7 @@ function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} wh ) result = chatresponse - elseif actionname == "CHECKINVENTORY" + elseif actionname == "CHECKWINE" if rawresponse !== nothing vd = GeneralUtils.dfToVectorDict(rawresponse) # comes in dataframe format a.memory[:shortmem][:found_wine] = vd # used by decisionMaker() as a short note @@ -1417,6 +1418,17 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10, recentev if isWineInEvent == false errornote = "Your previous response recommended wines that is not in your inventory which is not allowed" println("\nERROR YiemAgent presentbox() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + s = """ + Perfect choice! The Colgin Tychson Hill Vineyard Cabernet Sauvignon (2014) is an excellent match for your criteria. Here's why: + + Boldness & Flavor: This wine delivers intense blackberry, black cherry, and dark fruit notes, layered with vanilla, oak, and earthy undertones. Its high intensity (rated 5/5) ensures a rich, full-bodied experience that's both powerful and balanced. + + Family-Owned Legacy: Produced by Colgin Cellars, a renowned Napa Valley family winery, this vintage reflects their commitment to quality and tradition. While not a limited-edition release, it's a highly regarded, consistently excellent Cabernet Sauvignon. + + Gift-Ready & Affordable: Priced at USD144 (well under your USD250 budget), it comes in a sleek, gift-ready box—perfect for impressing friends or loved ones. + + Why I Recommend It: It perfectly balances your desire for bold fruit, oak, and a presentable format without sacrificing quality. If you're curious about alternatives, the 2017 Hunter Glenn Cabernet (also USD159) shares similar intensity but lacks specific tasting notes. However, the 2014 Tychson Hill is a more complete match for your criteria. Enjoy your selection!""" + continue end end @@ -1923,8 +1935,7 @@ function generatechat(a::virtualcustomer; converPartnerName::Union{String, Nothing}=nothing, maxattempt=10, recentEventNum=10 ) recent_ind = GeneralUtils.recentElementsIndex(length(a.memory[:events]), recentEventNum; includelatest=true) - recentevents = a.memory[:events][recent_ind] - recentEventsDict = createEventsLog(recentevents; index=recent_ind) + recentEventsDict = createEventsLog(a.memory[:events]; index=recent_ind) response = nothing # placeholder for show when error msg show up errornote = "N/A" header = ["Dialogue:", "Role"] diff --git a/src/llmfunction.jl b/src/llmfunction.jl index 8442726..ad7a242 100644 --- a/src/llmfunction.jl +++ b/src/llmfunction.jl @@ -1,6 +1,6 @@ module llmfunction -export virtualWineUserChatbox, jsoncorrection, checkinventory, # recommendbox, +export virtualWineUserChatbox, jsoncorrection, checkwine, # recommendbox, virtualWineUserRecommendbox, userChatbox, userRecommendbox, extractWineAttributes_1, extractWineAttributes_2, paraphrase @@ -288,28 +288,45 @@ julia> result = checkinventory(agent, input) # Signature """ -function checkinventory(a::T1, input::T2 +function checkwine(a::T1, input::T2; maxattempt::Int=3 ) where {T1<:agent, T2<:AbstractString} println("\ncheckinventory order: $input ", @__FILE__, ":", @__LINE__, " $(Dates.now())") wineattributes_1 = extractWineAttributes_1(a, input) wineattributes_2 = extractWineAttributes_2(a, input) - #CHANGE if you want to add retailer name - # _inventoryquery = "retailer name: $(a.retailername), $wineattributes_1, $wineattributes_2" - _inventoryquery = "$wineattributes_1, $wineattributes_2" - inventoryquery = "Retrieves winery, wine_name, wine_id, vintage, region, country, wine_type, grape, serving_temperature, sweetness, intensity, tannin, acidity, tasting_notes, price and currency of wines that match the following criteria - {$_inventoryquery}" - println("\ncheckinventory input: $inventoryquery ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - # add suppport for similarSQLVectorDB - textresult, rawresponse = SQLLLM.query(inventoryquery, a.func[:executeSQL], - a.func[:text2textInstructLLM]; - insertSQLVectorDB=a.func[:insertSQLVectorDB], - similarSQLVectorDB=a.func[:similarSQLVectorDB], - llmFormatName="qwen3") - #[PENDING] sometime wine data comeout {wine name, price, wine_id} and nothing else. I need to make sure that wine data include all of its attributes + # placeholder + textresult = nothing + rawresponse = nothing + + for i in 1:maxattempt + + #CHANGE if you want to add retailer name + # _inventoryquery = "retailer name: $(a.retailername), $wineattributes_1, $wineattributes_2" + _inventoryquery = "$wineattributes_1, $wineattributes_2" + + retrieve_attributes = ["winery", "wine_name", "wine_id", "vintage", "region", "country", "wine_type", "grape", "serving_temperature", "sweetness", "intensity", "tannin", "acidity", "tasting_notes", "price", "currency"] + inventoryquery = "Retrieves $retrieve_attributes of wines that match the following criteria - {$_inventoryquery}" + println("\ncheckinventory input: $inventoryquery ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + # add suppport for similarSQLVectorDB + textresult, rawresponse = SQLLLM.query(inventoryquery, a.func[:executeSQL], + a.func[:text2textInstructLLM]; + insertSQLVectorDB=a.func[:insertSQLVectorDB], + similarSQLVectorDB=a.func[:similarSQLVectorDB], + llmFormatName="qwen3") + # check if all of retrieve_attributes appears in textresult + isin = [occursin(x, textresult) for x in retrieve_attributes] + if !all(isin) && !occursin("The resulting table has 0 row", textresult) + errornote = "Not all of $retrieve_attributes appear in search result" + println("\nERROR YiemAgent checkwine() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + continue + else + break + end + end println("\ncheckinventory result ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println(textresult) - #[PENDING] if there is no wine id, it is not a valid result + return (result=textresult, rawresponse=rawresponse, success=true, errormsg=nothing) end @@ -352,7 +369,7 @@ function extractWineAttributes_1(a::T1, input::T2; maxattempt=10 wine_name: name of the wine winery: name of the winery vintage: the year of the wine - region: a region (NOT a country) where the wine is produced, such as Burgundy, Napa Valley, etc + region: a region, such as Burgundy, Bordeaux, Champagne, Napa Valley, Tuscany, California, Oregon, etc country: a country where wine is produced. Can be "Austria", "Australia", "France", "Germany", "Italy", "Portugal", "Spain", "United States" wine_type: can be one of: "red", "white", "sparkling", "rose", "dessert" or "fortified" grape_varietal: the name of the primary grape used to make the wine