This commit is contained in:
narawat lamaiin
2025-01-10 08:06:01 +07:00
parent 022cb5caf0
commit 616c159336
3 changed files with 130 additions and 261 deletions

View File

@@ -150,7 +150,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
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 online store.
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) Help them select the best wines from your inventory that align with their preferences
@@ -159,10 +159,10 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
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:
Your responsibility excludes:
1) Asking or guiding the user to make a purchase
2) Processing sales orders or engaging in any other sales-related activities
3) Providing services other than making recommendations.
3) Answering questions and offering additional services beyond just recommendations, such as delivery, box, gift wrapping or packaging, personalized messages. Customers can reach out to our sales at the store.
At each round of conversation, you will be given the current situation:
Your recent events: latest 5 events of the situation
@@ -194,7 +194,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
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 you want in your inventory. The input is a search term in verbal English.
Good query example: black car, a stereo, 200 mile range, electric motor.
Good query example: white wine, full-bodied, France, less than 2000 USD.
- 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
@@ -214,7 +214,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
if haskey(a.memory[:shortmem], :available_wine)
# check if wine name mentioned in timeline, only check first wine name is enough
# because agent will recommend every wines it found each time.
df = a.memory[:shortmem][:available_wine]
df = a.memory[:shortmem][:available_wine]
winenames = df[:, :wine_name]
for winename in winenames
if !occursin(winename, chathistory)
@@ -260,6 +260,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
response = a.func[:text2textInstructLLM](prompt)
response = GeneralUtils.remove_french_accents(response)
response = replace(response, '*'=>"")
# check if response contain more than one functions from ["CHATBOX", "CHECKINVENTORY", "ENDCONVERSATION"]
count = 0
@@ -284,24 +285,30 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
continue
end
checkFlag = false
for i [:understanding, :plan, :action_name]
if length(responsedict[i]) == 0
error("$i is empty ", @__FILE__, " ", @__LINE__)
errornote = "$i is empty"
println("Attempt $attempt $errornote ", @__FILE__, " ", @__LINE__)
continue
checkFlag = true
break
end
end
checkFlag == true ? continue : nothing
# check if there are more than 1 key per categories
checkFlag = false
for i [:understanding, :plan, :action_name, :action_input]
matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i)
if length(matchkeys) > 1
errornote = "DecisionMaker has more than one key per categories"
println("Attempt $attempt $errornote ", @__FILE__, " ", @__LINE__)
continue
checkFlag = true
break
end
end
checkFlag == true ? continue : nothing
println("\n~~~ Yiem decisionMaker() ", @__FILE__, " ", @__LINE__)
pprintln(Dict(responsedict))
@@ -691,6 +698,7 @@ function conversation(a::sommelier, userinput::Dict)
actionname = nothing
result = nothing
chatresponse = nothing
userinput[:text] = GeneralUtils.remove_french_accents(userinput[:text])
if userinput[:text] == "newtopic"
clearhistory(a)
@@ -808,7 +816,7 @@ function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} wh
errormsg::Union{AbstractString,Nothing} = haskey(response, :errormsg) ? response[:errormsg] : nothing
success::Bool = haskey(response, :success) ? response[:success] : false
#[WORKING] manage memory (pass msg to generatechat)
# manage memory (pass msg to generatechat)
if actionname ["CHATBOX", "PRESENTBOX", "ENDCONVERSATION"]
chatresponse = generatechat(a, thoughtDict)
push!(a.memory[:events],
@@ -829,25 +837,28 @@ function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} wh
# )
)
result = chatresponse
if actionname == "PRESENTBOX"
df = a.memory[:shortmem][:available_wine]
winename = join(df[:, :wine_name], ", ")
if a.memory[:state][:wine_presented_to_user] == "None"
a.memory[:state][:wine_presented_to_user] = winename
else
a.memory[:state][:wine_presented_to_user] *= ", $winename"
end
end
elseif actionname == "CHECKINVENTORY"
if haskey(a.memory[:shortmem], :available_wine) # store wines in dataframe format
df = a.memory[:shortmem][:available_wine]
a.memory[:shortmem][:available_wine] = vcat(df, rawresponse)
elseif rawresponse !== nothing
a.memory[:shortmem][:available_wine] = rawresponse
if rawresponse !== nothing
if haskey(a.memory[:shortmem], :available_wine)
df = a.memory[:shortmem][:available_wine]
#[TESTING] sometime df 2 df has different column size
dfCol = names(df)
rawresponse_dfCol = names(rawresponse)
if length(dfCol) > length(rawresponse_dfCol)
a.memory[:shortmem][:available_wine] = DataFrames.outerjoin(df, rawresponse, on=rawresponse_dfCol)
elseif length(dfCol) < length(rawresponse_dfCol)
a.memory[:shortmem][:available_wine] = DataFrames.outerjoin(df, rawresponse, on=dfCol)
else
a.memory[:shortmem][:available_wine] = vcat(df, rawresponse)
end
else
a.memory[:shortmem][:available_wine] = rawresponse
end
else
# skip, no result
# no result, skip
end
push!(a.memory[:events],
eventdict(;
event_description= "the assistant searched the database.",
@@ -891,7 +902,7 @@ julia>
function generatechat(a::sommelier, thoughtDict)
systemmsg =
"""
Your name is $(a.name). You are a helpful English-speaking assistant, acting as a polite, website-based sommelier for an online wine store.
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.
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.
@@ -899,9 +910,10 @@ function generatechat(a::sommelier, thoughtDict)
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 excludes:
1) Asking or guiding the user to make a purchase
2) Processing sales orders or engaging in any other sales-related activities
3) Answering questions and offering additional services beyond just recommendations, such as delivery, box, gift wrapping, personalized messages. Customers can reach out to our sales at the store.
At each round of conversation, you will be given the current situation:
Your ongoing conversation with the user: ...
@@ -961,15 +973,17 @@ function generatechat(a::sommelier, thoughtDict)
"""
try
response_1 = a.func[:text2textInstructLLM](prompt)
response = a.func[:text2textInstructLLM](prompt)
# sometime the model response like this "here's how I would respond: ..."
if occursin("respond:", response_1)
if occursin("respond:", response)
errornote = "You don't need to intro your response"
error("generatechat() response contain : ", @__FILE__, " ", @__LINE__)
end
response_2 = replace(response_1, '*' => "")
response_3 = replace(response_2, '$' => "USD")
response = replace(response_3, '`' => "")
response = GeneralUtils.remove_french_accents(response)
response = replace(response, '*'=>"")
response = replace(response, '$' => "USD")
response = replace(response, '`' => "")
response = GeneralUtils.remove_french_accents(response)
responsedict = GeneralUtils.textToDict(response, ["Chat"],
rightmarker=":", symbolkey=true, lowercasekey=true)
@@ -1017,8 +1031,8 @@ function generatechat(a::sommelier, thoughtDict)
# then the agent is not supposed to recommend the wine
if isWineInEvent == false
errornote = "Note: You are not supposed to recommend a wine that is not in your inventory."
error("Note: You are not supposed to recommend a wine that is not in your inventory.")
errornote = "Previously: You recommend a wine that is not in your inventory which is not allowed."
error("Previously: You recommend a wine that is not in your inventory which is not allowed.")
end
end