This commit is contained in:
narawat lamaiin
2025-04-01 21:17:15 +07:00
parent b8fd772a28
commit c21f943b12
4 changed files with 64 additions and 40 deletions

View File

@@ -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. 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: 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. - 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. - 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 1: red wine that pair well with spicy food. Bad query example: red wine that pair well with spicy food.
Bad query example 2: white wine that goes well with party 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". - 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 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 response = nothing # placeholder for show when error msg show up
for attempt in 1:10 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) QandA = generatequestion(a, a.func[:text2textInstructLLM]; recent=3)
usermsg = usermsg =
@@ -298,17 +301,22 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age
end end
if count > 1 if count > 1
errornote = "You must use only one function" errornote = "You must use only one function"
println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
# 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)
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) 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 continue
elseif sum(values(detected_kw)) > length(header) 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 continue
end 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"] if responsedict[:action_name] ["CHATBOX", "CHECKINVENTORY", "ENDCONVERSATION"]
errornote = "You must use the given functions" errornote = "You must use the given functions"
println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -325,7 +333,7 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age
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("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nYiemAgent decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true checkFlag = true
break break
end end
@@ -338,7 +346,7 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age
matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i) matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i)
if length(matchkeys) > 1 if length(matchkeys) > 1
errornote = "DecisionMaker has more than one key per categories" 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 checkFlag = true
break break
end end
@@ -372,7 +380,7 @@ function decisionMaker(a::T; recent::Integer=10)::Dict{Symbol,Any} where {T<:age
isWineInEvent == false isWineInEvent == false
errornote = "Note: Before recommending a wine, ensure it's in your inventory. Check your stock first." 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 continue
end end
end end
@@ -1116,14 +1124,14 @@ function generatechat(a::sommelier, thoughtDict)
You should only respond in format as described below: You should only respond in format as described below:
Dialogue: ... Dialogue: ...
Here are some examples: 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" 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?" 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! Let's begin!
""" """
#[WORKING] remove "chat"
header = ["Dialogue:"] header = ["Dialogue:"]
dictkey = ["dialogue"] dictkey = ["dialogue"]
@@ -1145,6 +1153,7 @@ function generatechat(a::sommelier, thoughtDict)
for attempt in 1:10 for attempt in 1:10
if attempt > 1 # use to prevent LLM generate the same respond over and over 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) yourthought1 = paraphrase(a.func[:text2textInstructLLM], yourthought)
else else
yourthought1 = yourthought yourthought1 = yourthought
@@ -1166,12 +1175,15 @@ function generatechat(a::sommelier, thoughtDict)
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(_prompt; formatname="qwen") prompt = GeneralUtils.formatLLMtext(_prompt; formatname="qwen")
response = a.func[:text2textInstructLLM](prompt) response = a.func[:text2textInstructLLM](prompt)
# sometime the model response like this "here's how I would respond: ..." # sometime the model response like this "here's how I would respond: ..."
if occursin("respond:", response) if occursin("respond:", response)
errornote = "You don't need to intro your response" errornote = "You don't need to put 'response:' in your response"
error("generatechat() response contain : ", @__FILE__, ":", @__LINE__, " $(Dates.now())") 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 end
response = GeneralUtils.remove_french_accents(response) response = GeneralUtils.remove_french_accents(response)
response = replace(response, '*'=>"") response = replace(response, '*'=>"")
@@ -1651,7 +1663,7 @@ function generatequestion(a, text2textInstructLLM::Function;
# 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 generatequestion() response does not have all header" errornote = "\nresponse does not have all header"
continue continue
elseif sum(values(detected_kw)) > length(header) elseif sum(values(detected_kw)) > length(header)
errornote = "\nYiemAgent generatequestion() response has duplicated header" errornote = "\nYiemAgent generatequestion() response has duplicated header"
@@ -1714,6 +1726,10 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
errornote = "" errornote = ""
response = nothing # store for show when error msg show up response = nothing # store for show when error msg show up
for attempt in 1:10 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 = """ usermsg = """
Total events: $(length(events)) Total events: $(length(events))
Events timeline: $timeline Events timeline: $timeline
@@ -1738,11 +1754,11 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
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 in your previous attempt" 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 continue
elseif sum(values(detected_kw)) > length(header) elseif sum(values(detected_kw)) > length(header)
errornote = "Your previous response has duplicated events" 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 continue
end end
@@ -1757,8 +1773,8 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
error("generateSituationReport failed to generate a response ", response) error("generateSituationReport failed to generate a response ", response)
end end
function detectWineryName(a, text)
function detectWineryName(a, text)
systemmsg = systemmsg =
""" """
You are a sommelier of a wine store. You are a sommelier of a wine store.
@@ -1791,7 +1807,6 @@ function detectWineryName(a, text)
usermsg = """ usermsg = """
Text: $text Text: $text
""" """
_prompt = _prompt =
[ [
Dict(:name => "system", :text => systemmsg), Dict(:name => "system", :text => systemmsg),

View File

@@ -389,6 +389,10 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
for attempt in 1:10 for attempt in 1:10
#[WORKING] I should add generatequestion() #[WORKING] I should add generatequestion()
if attempt > 1
println("\nYiemAgent extractWineAttributes_1() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end
usermsg = usermsg =
""" """
User's query: $input 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 # 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 = "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 checkFlag = true
break break
end 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 # 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 = "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 checkFlag = true
break break
end end
@@ -480,14 +484,14 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
content = [content] content = [content]
end end
for x in content #check whether price are mentioned in the input # for x in content #check whether price are mentioned in the input
if !occursin("NA", responsedict[j]) && !occursin(x, 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." # 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())") # println("ERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag == true # checkFlag == true
break # break
end # end
end # end
end end
end end
end end

View File

@@ -268,7 +268,7 @@ end
# Returns # Returns
- `timeline::String` - `timeline::String`
A formatted string representing the events with their subjects, actions, and optional outcomes 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 # Example
@@ -277,24 +277,32 @@ events = [
Dict(:subject => "Assistant", :actioninput => "Hi there!", :outcome => "with a smile") Dict(:subject => "Assistant", :actioninput => "Hi there!", :outcome => "with a smile")
] ]
timeline = createTimeline(events) timeline = createTimeline(events)
# User> Hello # 1) User> Hello
# Assistant> Hi there! with a smile # 2) Assistant> Hi there! with a smile
""" """
function createTimeline(events::T1) where {T1<:AbstractVector} function createTimeline(events::T1) where {T1<:AbstractVector}
# Initialize empty timeline string
timeline = "" timeline = ""
# Iterate through events with index
for (i, event) in enumerate(events) for (i, event) in enumerate(events)
# If no outcome exists, format without outcome
if event[:outcome] === nothing 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 else
timeline *= "$i) $(event[:subject])> $(event[:actioninput]) $(event[:outcome])\n" timeline *= "Event_$i) $(event[:subject])> $(event[:actioninput]) $(event[:outcome])\n"
end end
end end
# Return formatted timeline string
return timeline return timeline
end end
# """ Convert a single chat dictionary into LLM model instruct format. # """ Convert a single chat dictionary into LLM model instruct format.
# # Llama 3 instruct format example # # Llama 3 instruct format example

View File

@@ -95,10 +95,7 @@ function getEmbedding(text::T) where {T<:AbstractString}
) )
) )
#BUG it returns nothing from ollama response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=120, maxattempt=3)
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=120, maxattempt=2)
embedding = response[:response][:embeddings] embedding = response[:response][:embeddings]
return embedding return embedding
end end