From c21f943b12292794e33209b74d8059181589d7b3 Mon Sep 17 00:00:00 2001 From: narawat lamaiin Date: Tue, 1 Apr 2025 21:17:15 +0700 Subject: [PATCH] update --- src/interface.jl | 57 +++++++++++++++++++++++++++++----------------- src/llmfunction.jl | 24 +++++++++++-------- src/util.jl | 18 +++++++++++---- test/test_1.jl | 5 +--- 4 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/interface.jl b/src/interface.jl index 622ef50..cff5752 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -200,9 +200,8 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age 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 which you can use to check info about wine you want in your inventory. The input is a search term in verbal English. - Bad query example 1: red wine that pair well with spicy food. - Bad query example 2: white wine that goes well with party food. + - CHECKINVENTORY which you can use to check info about wine you want in your inventory. The input is a search term is verbal english and it should includes - 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. + Bad query example: red wine that pair well with spicy food. - 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 @@ -264,6 +263,10 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age response = nothing # placeholder for show when error msg show up for attempt in 1:10 + if attempt > 1 + println("\nYiemAgent decisionMaker() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + end + QandA = generatequestion(a, a.func[:text2textInstructLLM]; recent=3) usermsg = @@ -298,17 +301,22 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age end if count > 1 errornote = "You must use only one function" - println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") continue end # check whether response has all header detected_kw = GeneralUtils.detect_keyword(header, response) + kwvalue = [i for i in values(detected_kw)] + zeroind = findall(x -> x == 0, kwvalue) + missingkeys = [header[i] for i in zeroind] if 0 ∈ values(detected_kw) - errornote = "\nYiemAgent decisionMaker() response does not have all header" + errornote = "$missingkeys are missing from your previous response" + println("\nYiemAgent decisionMaker() $errornote:\n $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") continue elseif sum(values(detected_kw)) > length(header) - errornote = "\nYiemAgent decisionMaker() response has duplicated header" + errornote = "Your response has duplicated points" + println("\nYiemAgent decisionMaker() $errornote: $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") continue end @@ -317,7 +325,7 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age if responsedict[:action_name] ∉ ["CHATBOX", "CHECKINVENTORY", "ENDCONVERSATION"] errornote = "You must use the given functions" - println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") continue end @@ -325,7 +333,7 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age for i ∈ Symbol.(dictkey) if length(responsedict[i]) == 0 errornote = "$i is empty" - println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") checkFlag = true break end @@ -338,7 +346,7 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i) if length(matchkeys) > 1 errornote = "DecisionMaker has more than one key per categories" - println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") checkFlag = true break end @@ -372,7 +380,7 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age isWineInEvent == false errornote = "Note: Before recommending a wine, ensure it's in your inventory. Check your stock first." - println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") continue end end @@ -1116,14 +1124,14 @@ function generatechat(a::sommelier, thoughtDict) You should only respond in format as described below: Dialogue: ... Here are some examples: - Your ongoing conversation with the user: "user> hello, I need a new car\n" Additional info: "Car previously found in your inventory: 1) Toyota Camry 2020 2) Honda Civic 2021 3) Ford Mustang 2022" - Your thoughts: "I should recommend the car we have to the user." + Your thoughts: "I should recommend the car we have found in our inventory to the user." + Your ongoing conversation with the user: "user> hello, I need a new car\n" Dialogue: "We have a variety of cars available, including the Toyota Camry 2020, the Honda Civic 2021, and the Ford Mustang 2022. Which one would you like to see?" Let's begin! """ - #[WORKING] remove "chat" + header = ["Dialogue:"] dictkey = ["dialogue"] @@ -1145,6 +1153,7 @@ function generatechat(a::sommelier, thoughtDict) for attempt in 1:10 if attempt > 1 # use to prevent LLM generate the same respond over and over + println("\nYiemAgent generatchat() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())") yourthought1 = paraphrase(a.func[:text2textInstructLLM], yourthought) else yourthought1 = yourthought @@ -1166,12 +1175,15 @@ function generatechat(a::sommelier, thoughtDict) # put in model format prompt = GeneralUtils.formatLLMtext(_prompt; formatname="qwen") - response = a.func[:text2textInstructLLM](prompt) + # sometime the model response like this "here's how I would respond: ..." if occursin("respond:", response) - errornote = "You don't need to intro your response" - error("generatechat() response contain : ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + errornote = "You don't need to put 'response:' in your response" + println("ERROR YiemAgent generatechat() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + elseif occursin("Your thoughts:", response) || occursin("your thoughts:", response) + errornote = "You don't need to put 'Your thoughts:' in your response" + println("ERROR YiemAgent generatechat() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") end response = GeneralUtils.remove_french_accents(response) response = replace(response, '*'=>"") @@ -1651,7 +1663,7 @@ function generatequestion(a, text2textInstructLLM::Function; # check whether response has all header detected_kw = GeneralUtils.detect_keyword(header, response) if 0 ∈ values(detected_kw) - errornote = "\nYiemAgent generatequestion() response does not have all header" + errornote = "\nresponse does not have all header" continue elseif sum(values(detected_kw)) > length(header) errornote = "\nYiemAgent generatequestion() response has duplicated header" @@ -1714,6 +1726,10 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent:: errornote = "" response = nothing # store for show when error msg show up for attempt in 1:10 + if attempt > 1 # use to prevent LLM generate the same respond over and over + println("\nYiemAgent generateSituationReport() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + end + usermsg = """ Total events: $(length(events)) Events timeline: $timeline @@ -1738,11 +1754,11 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent:: missingkeys = [header[i] for i in zeroind] if 0 ∈ values(detected_kw) errornote = "$missingkeys are missing in your previous attempt" - println("\nYiemAgent generateSituationReport() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("\nERROR YiemAgent generateSituationReport() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") continue elseif sum(values(detected_kw)) > length(header) errornote = "Your previous response has duplicated events" - println("\nYiemAgent generateSituationReport() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("\nERROR YiemAgent generateSituationReport() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") continue end @@ -1757,8 +1773,8 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent:: error("generateSituationReport failed to generate a response ", response) end + function detectWineryName(a, text) - systemmsg = """ You are a sommelier of a wine store. @@ -1791,7 +1807,6 @@ function detectWineryName(a, text) usermsg = """ Text: $text """ - _prompt = [ Dict(:name => "system", :text => systemmsg), diff --git a/src/llmfunction.jl b/src/llmfunction.jl index 4f13881..c16bdaf 100644 --- a/src/llmfunction.jl +++ b/src/llmfunction.jl @@ -389,6 +389,10 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2< for attempt in 1:10 #[WORKING] I should add generatequestion() + if attempt > 1 + println("\nYiemAgent extractWineAttributes_1() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + end + usermsg = """ User's query: $input @@ -449,7 +453,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2< # check whether wine_price is in ranged number if !occursin('-', responsedict[:wine_price]) errornote = "wine_price must be a range number" - println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("ERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") checkFlag = true break end @@ -464,7 +468,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2< # price range like 100-100 is not good if minprice == maxprice errornote = "wine_price with minimum equals to maximum is not valid" - println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + println("ERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") checkFlag = true break end @@ -480,14 +484,14 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2< content = [content] end - for x in content #check whether price are mentioned in the input - if !occursin("NA", responsedict[j]) && !occursin(x, input) - errornote = "$x is not mentioned in the user query, you must only use the info from the query." - println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - checkFlag == true - break - end - end + # for x in content #check whether price are mentioned in the input + # if !occursin("NA", responsedict[j]) && !occursin(x, input) + # errornote = "$x is not mentioned in the user query, you must only use the info from the query." + # println("ERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + # checkFlag == true + # break + # end + # end end end end diff --git a/src/util.jl b/src/util.jl index bbc92e1..ba3135a 100644 --- a/src/util.jl +++ b/src/util.jl @@ -268,7 +268,7 @@ end # Returns - `timeline::String` A formatted string representing the events with their subjects, actions, and optional outcomes - Format: "{subject}> {actioninput} {outcome}\n" for each event + Format: "{index}) {subject}> {actioninput} {outcome}\n" for each event # Example @@ -277,24 +277,32 @@ events = [ Dict(:subject => "Assistant", :actioninput => "Hi there!", :outcome => "with a smile") ] timeline = createTimeline(events) -# User> Hello -# Assistant> Hi there! with a smile +# 1) User> Hello +# 2) Assistant> Hi there! with a smile """ function createTimeline(events::T1) where {T1<:AbstractVector} + # Initialize empty timeline string timeline = "" + + # Iterate through events with index for (i, event) in enumerate(events) + # If no outcome exists, format without outcome if event[:outcome] === nothing - timeline *= "$i) $(event[:subject])> $(event[:actioninput])\n" + timeline *= "Event_$i) $(event[:subject])> $(event[:actioninput])\n" + # If outcome exists, include it in formatting else - timeline *= "$i) $(event[:subject])> $(event[:actioninput]) $(event[:outcome])\n" + timeline *= "Event_$i) $(event[:subject])> $(event[:actioninput]) $(event[:outcome])\n" end end + # Return formatted timeline string return timeline end + + # """ Convert a single chat dictionary into LLM model instruct format. # # Llama 3 instruct format example diff --git a/test/test_1.jl b/test/test_1.jl index 057a017..27ddfb5 100644 --- a/test/test_1.jl +++ b/test/test_1.jl @@ -95,10 +95,7 @@ function getEmbedding(text::T) where {T<:AbstractString} ) ) - #BUG it returns nothing from ollama - response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=120, maxattempt=2) - - + response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=120, maxattempt=3) embedding = response[:response][:embeddings] return embedding end