This commit is contained in:
narawat lamaiin
2025-01-20 18:19:38 +07:00
parent 4197625e57
commit bb81b973d3
3 changed files with 71 additions and 33 deletions

View File

@@ -1,7 +1,7 @@
module interface module interface
export addNewMessage, conversation, decisionMaker, evaluator, reflector, generatechat, export addNewMessage, conversation, decisionMaker, evaluator, reflector, generatechat,
generalconversation, detectWineryName generalconversation, detectWineryName, generateSituationReport
using JSON3, DataStructures, Dates, UUIDs, HTTP, Random, PrettyPrinting, Serialization, using JSON3, DataStructures, Dates, UUIDs, HTTP, Random, PrettyPrinting, Serialization,
DataFrames DataFrames
@@ -176,16 +176,17 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
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 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: Your goal includes:
1) Establish a connection with the customer by greeting them warmly 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 2) Help them select the best wines only from your store's inventory that align with their preferences
Your responsibility includes: Your responsibility includes:
1) Make an informed decision about what you need to do to achieve the goal 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 2) Thanks the user when they don't need any further assistance and invite them to comeback next time
Your responsibility excludes: Your responsibility excludes:
1) Asking or guiding the user to make a purchase 1) Asking or guiding the user to make an order or purchase
2) Processing sales orders or engaging in any other sales-related activities 2) Processing sales orders or engaging in any other sales-related activities
3) Answering questions and offering additional services beyond just recommendations. 3) Answering questions beyond just recommendations.
4) Offering additional services beyond just recommendations.
At each round of conversation, you will be given the current situation: At each round of conversation, you will be given the current situation:
Your recent events: latest 5 events of the situation Your recent events: latest 5 events of the situation
@@ -204,6 +205,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
- Encourage the customer to explore different options and try new things. - 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. - 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.
- If a customer requests information about discounts, quantity, rewards programs, promotions, delivery options, boxes, gift wrapping, packaging, or personalized messages, please inform them that they can contact our sales team at the store. - If a customer requests information about discounts, quantity, rewards programs, promotions, delivery options, boxes, gift wrapping, packaging, or personalized messages, please inform them that they can contact our sales team at the store.
- Only recommend
For your information: For your information:
- vintage 0 means non-vintage. - vintage 0 means non-vintage.
@@ -232,14 +234,17 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
Let's begin! Let's begin!
""" """
chathistory = vectorOfDictToText(a.chathistory) chathistory = chatHistoryToText(a.chathistory)
# 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 haskey(a.memory[:shortmem], :available_wine) if haskey(a.memory[:shortmem], :available_wine)
# 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
# because agent will recommend every wines it found each time. # because agent will recommend every wines it found each time.
df = a.memory[:shortmem][:available_wine] winenames = []
winenames = df[:, :wine_name] for wine in a.memory[:shortmem][:available_wine]
push!(winenames, wine["wine_name"])
end
for winename in winenames for winename in winenames
if !occursin(winename, chathistory) if !occursin(winename, chathistory)
println("\n~~~ Yiem decisionMaker() found wines from DB ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("\n~~~ Yiem decisionMaker() found wines from DB ", Dates.now(), " ", @__FILE__, " ", @__LINE__)
@@ -257,6 +262,19 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
end end
end end
context = # may b add wine name instead of the hold wine data is better
if haskey(a.memory[:shortmem], :available_wine)
winenames = []
for (i, wine) in enumerate(a.memory[:shortmem][:available_wine])
name = "$i) $(wine["wine_name"]) "
push!(winenames, name)
end
availableWineName = join(winenames, ',')
"You found information about the following wines in your inventory: $availableWineName"
else
""
end
errornote = "" errornote = ""
response = nothing # placeholder for show when error msg show up response = nothing # placeholder for show when error msg show up
@@ -265,6 +283,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agen
usermsg = usermsg =
""" """
$context
Your recent events: $recentevents Your recent events: $recentevents
Your Q&A: $QandA) Your Q&A: $QandA)
$errornote $errornote
@@ -734,14 +753,12 @@ function conversation(a::sommelier, userinput::Dict; maximumMsg=50)
# thinking loop until AI wants to communicate with the user # thinking loop until AI wants to communicate with the user
chatresponse = nothing chatresponse = nothing
for i in 1:5 while chatresponse === nothing
actionname, result = think(a) actionname, result = think(a)
if actionname ["CHATBOX", "PRESENTBOX", "ENDCONVERSATION"] if actionname ["CHATBOX", "PRESENTBOX", "ENDCONVERSATION"]
chatresponse = result chatresponse = result
break
end end
end end
addNewMessage(a, "assistant", chatresponse; maximumMsg=maximumMsg) addNewMessage(a, "assistant", chatresponse; maximumMsg=maximumMsg)
return chatresponse return chatresponse
@@ -856,22 +873,13 @@ function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} wh
elseif actionname == "CHECKINVENTORY" elseif actionname == "CHECKINVENTORY"
if rawresponse !== nothing if rawresponse !== nothing
if haskey(a.memory[:shortmem], :available_wine) if haskey(a.memory[:shortmem], :available_wine)
df = a.memory[:shortmem][:available_wine] vd = GeneralUtils.dfToVectorDict(rawresponse)
#[TESTING] sometime df 2 df has different column size a.memory[:shortmem][:available_wine] = vcat(vd, rawresponse)
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 else
a.memory[:shortmem][:available_wine] = vcat(df, rawresponse) a.memory[:shortmem][:available_wine] = GeneralUtils.dfToVectorDict(rawresponse)
end end
else else
a.memory[:shortmem][:available_wine] = rawresponse println("checkinventory return nothing")
end
else
# no result, skip
end end
push!(a.memory[:events], push!(a.memory[:events],
@@ -889,7 +897,6 @@ function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} wh
error("condition is not defined ", Dates.now(), " ", @__FILE__, " ", @__LINE__) error("condition is not defined ", Dates.now(), " ", @__FILE__, " ", @__LINE__)
end end
return (actionname=actionname, result=result) return (actionname=actionname, result=result)
end end
@@ -956,15 +963,15 @@ function generatechat(a::sommelier, thoughtDict)
Let's begin! Let's begin!
""" """
# a.memory[:shortmem][:available_wine] is a dataframe. # a.memory[:shortmem][:available_wine] is a vector of dictionary
context = context =
if haskey(a.memory[:shortmem], :available_wine) if haskey(a.memory[:shortmem], :available_wine)
"Available wines $(GeneralUtils.dfToString(a.memory[:shortmem][:available_wine]))" "Wines previously found in your inventory: $(availableWineToText(a.memory[:shortmem][:available_wine]))"
else else
"None" "N/A"
end end
chathistory = vectorOfDictToText(a.chathistory) chathistory = chatHistoryToText(a.chathistory)
errornote = "" errornote = ""
response = nothing # placeholder for show when error msg show up response = nothing # placeholder for show when error msg show up
@@ -1092,7 +1099,7 @@ function generatechat(a::companion)
a.systemmsg a.systemmsg
end end
chathistory = vectorOfDictToText(a.chathistory) chathistory = chatHistoryToText(a.chathistory)
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
@@ -1340,6 +1347,9 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
Event_1: The user ask me about where to buy a toy. Event_1: The user ask me about where to buy a toy.
Event_2: I told the user to go to the store at 2nd floor. Event_2: I told the user to go to the store at 2nd floor.
Event_1: The user greets the assistant by saying 'hello'.
Event_2: The assistant respond warmly and inquire about how he can assist the user.
Let's begin! Let's begin!
""" """

View File

@@ -307,6 +307,8 @@ function checkinventory(a::T1, input::T2
println("\n~~~ checkinventory result ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("\n~~~ checkinventory result ", Dates.now(), " ", @__FILE__, " ", @__LINE__)
println(textresult) println(textresult)
#[WORKING] when rawresponse is nothing, AI get errors
return (result=textresult, rawresponse=rawresponse, success=true, errormsg=nothing) return (result=textresult, rawresponse=rawresponse, success=true, errormsg=nothing)
end end
@@ -412,6 +414,8 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
responsedict = copy(JSON3.read(response)) responsedict = copy(JSON3.read(response))
# convert
delete!(responsedict, :reasoning) delete!(responsedict, :reasoning)
delete!(responsedict, :tasting_notes) delete!(responsedict, :tasting_notes)
delete!(responsedict, :occasion) delete!(responsedict, :occasion)
@@ -444,7 +448,9 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
end end
else else
content = responsedict[j] content = responsedict[j]
if occursin(',', content) if typeof(content) <: AbstractVector
content = strip.(content)
elseif occursin(',', content)
content = split(content, ",") # sometime AI generates multiple values e.g. "Chenin Blanc, Riesling" content = split(content, ",") # sometime AI generates multiple values e.g. "Chenin Blanc, Riesling"
content = strip.(content) content = strip.(content)
else else

View File

@@ -1,6 +1,7 @@
module util module util
export clearhistory, addNewMessage, vectorOfDictToText, eventdict, noises, createTimeline export clearhistory, addNewMessage, chatHistoryToText, eventdict, noises, createTimeline,
availableWineToText
using UUIDs, Dates, DataStructures, HTTP, JSON3 using UUIDs, Dates, DataStructures, HTTP, JSON3
using GeneralUtils using GeneralUtils
@@ -138,7 +139,7 @@ julia> GeneralUtils.vectorOfDictToText(vecd, withkey=true)
``` ```
# Signature # Signature
""" """
function vectorOfDictToText(vecd::Vector; withkey=true)::String function chatHistoryToText(vecd::Vector; withkey=true)::String
# Initialize an empty string to hold the final text # Initialize an empty string to hold the final text
text = "" text = ""
@@ -169,6 +170,27 @@ function vectorOfDictToText(vecd::Vector; withkey=true)::String
end end
function availableWineToText(vecd::Vector)::String
# Initialize an empty string to hold the final text
rowtext = ""
# Loop through each dictionary in the input vector
for (i, d) in enumerate(vecd)
# Iterate over all key-value pairs in the dictionary
temp = []
for (k, v) in d
# Append the formatted string to the text variable
t = "$k:$v"
push!(temp, t)
end
_rowtext = join(temp, ',')
rowtext *= "$i) $_rowtext "
end
return rowtext
end
function eventdict(; function eventdict(;
event_description::Union{String, Nothing}=nothing, event_description::Union{String, Nothing}=nothing,
timestamp::Union{DateTime, Nothing}=nothing, timestamp::Union{DateTime, Nothing}=nothing,