This commit is contained in:
2026-07-16 22:31:40 +07:00
parent 44bb8baf7c
commit 87bc6a46a1
7 changed files with 273 additions and 138 deletions
+101 -44
View File
@@ -75,7 +75,7 @@ OrderedDict{String, Any} with 4 entries:
"""
function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
) where {T<:agent}
@info "YiemAgent decisionMaker() 1 " @__LINE__
# lessonDict = copy(JSON.parsefile("lesson.json"))
# lesson =
@@ -110,7 +110,7 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
<internal_context_for_assistant>
</internal_context_for_assistant>
"""
@info "YiemAgent decisionMaker() 2 " @__LINE__
# add context to text of the latest message (in the front).
# use for loop because in openai format, each msg may contain both text and image.
for d in a.chathistory[end]["content"]
@@ -123,28 +123,27 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
response = nothing # placeholder for show when error msg show up
for attempt in 1:maxattempt
if attempt > 1
println("\nYiemAgent decisionMaker() attempt $attempt/$maxattempt ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end
@info "YiemAgent decisionMaker() 3 " @__LINE__
msg = Dict(
"model" => "gemma-4-E4B-it-UD-Q4_K_XL",
"messages" => a.chathistory,
"temperature" => 0.7
)
@info "YiemAgent decisionMaker() 4 " @__LINE__
response = a.context.text2textInstructLLM(a.id, msg)
@info "YiemAgent decisionMaker() 5 " @__LINE__
response = GeneralUtils.clean_json_response(response)
response = GeneralUtils.remove_french_accents(response)
think, response = GeneralUtils.extractthink(response)
response = String(split(response, ", observation")[1]) # in case LLM generate observation key which it isn't supposed to
response = strip(response)
@info "YiemAgent decisionMaker() 6 " @__LINE__
# dollar sign in Julia means string interpolation
while occursin('$', response)
response = replace(response, '$' => "USD")
end
@info "YiemAgent decisionMaker() 7 " @__LINE__
responsedict = nothing
if occursin(requiredKeys[2], response)
try
@@ -155,14 +154,24 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
continue
end
end
@info "YiemAgent decisionMaker() 8 " @__LINE__
# check whether all answer's key points are in responsedict
println("\n---")
println(responsedict)
println("---\n")
ispass, errormsg = GeneralUtils.checkAgentResponse_JSON(responsedict, requiredKeys)
@info "YiemAgent decisionMaker() 9 " @__LINE__
if !ispass
errornote = errormsg
println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)-> $responsedict", @__FILE__, ":", @__LINE__, " $(Dates.now())\n")
continue
end
@info "YiemAgent decisionMaker() 10 " @__LINE__
if responsedict["action_input"] == "CHAT_BOX" &&
occursin("similar", responsedict["action_input"])
@info "illegal CHAT_BOX search term" @__LINE__
continue
end
# if responsedict["action_name"] ∉ ["CHAT_BOX", "SEARCH_WINE_DATABASE", "PRESENT_WINE_GUIDELINE", "END_CONVER_GUIDELINE"]
# errornote = "Your previous attempt didn't use the given functions"
@@ -172,7 +181,7 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
# println("\nYiem decisionMaker() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# pprintln(responsedict)
@info "YiemAgent decisionMaker() 11 " @__LINE__
return responsedict
end
error("DecisionMaker failed to generate a thought ", response)
@@ -380,49 +389,85 @@ function conversation(a::sommelier; userinput::Union{Dict{String, Any}, JSON.Obj
@info "YiemAgent conversation() 2-3 think count $loopcount " @__LINE__
thoughtdict, result_raw = think(a)
@info "YiemAgent conversation() 2-4 think count $loopcount " @__LINE__
if thoughtdict["action_name"] ["CHAT_BOX"]
@info "YiemAgent conversation() 2-4 think count $loopcount " @__LINE__
@info "YiemAgent conversation() 2-5 think count $loopcount " @__LINE__
assistant_response = Dict{String, Any}(
"role" => "assistant",
"content" => [Dict("type" => "text", "text" => thoughtdict["action_input"]),]
)
addNewMessage(a, "assistant", assistant_response; maximumMsg=maximumMsg)
@info "YiemAgent conversation() 2-5 think count $loopcount " @__LINE__
return thoughtdict["action_input"]
# elseif thoughtdict["action_name"] ∈ ["CHAT_BOX"]
# @info "YiemAgent conversation() 2-4 think count $loopcount " @__LINE__
# assistant_response = Dict{String, Any}(
# "role" => "assistant",
# "content" => [
# Dict("type" => "text", "text" => thoughtdict["action_input"]),
# Dict( #WORKING put 1st image here
# "type" => "image_url",
# "image_url" => Dict("url" => image1_data_uri)
# ),
# Dict( #WORKING put 2nd image here
# "type" => "image_url",
# "image_url" => Dict("url" => image2_data_uri)
# ),
# ]
# )
# addNewMessage(a, "assistant", assistant_response; maximumMsg=maximumMsg)
# return thoughtdict["action_input"] #XXX change output from string to dict
else
items_info = []
if haskey(a.memory["shortmem"], "items_info")
for (i, item) in enumerate(a.memory["shortmem"]["items_info"])
if haskey(item, "wine_name") && occursin(item["wine_name"], thoughtdict["action_input"])
push!(items_info, item)
deleteat!(a.memory["shortmem"]["items_info"], i)
end
end
end
response_to_frontend = Dict{String, Any}(
"role" => "assistant",
"content" => [
Dict("type" => "text", "text" => thoughtdict["action_input"]),
Dict(
"type" => "items_info",
"items_info" => items_info
),
]
)
""" intended message to send to frontend should have the following format.
response_to_frontend = Dict{String, Any}(
"role" => "assistant",
"content" => [
Dict("type" => "text", "text" => "assistant_text_response"),
Dict(
"type" => "items_info",
"items_info" => [
Dict(
"wine_name"=> "wine name 1",
"wine_id"=> "...",
"image"=> base64 encoded image,
...
),
Dict(
"wine_name"=> "wine name 2",
"wine_id"=> "...",
"image"=> base64 encoded image,
...
),
]
),
]
)
"""
@info "YiemAgent conversation() 2-6 think count $loopcount " @__LINE__
return response_to_frontend
else # still in action
@info "YiemAgent conversation() 2-7 think count $loopcount " @__LINE__
action_name = thoughtdict["action_name"]
action_input = thoughtdict["action_input"]
@info "YiemAgent conversation() 2-8 think count $loopcount " @__LINE__
action_call = Dict{String, Any}(
"role" => "action_call",
"content" => [Dict("type" => "text", "text" => "{action_name: $action_name, action_input: $action_input}"),]
)
@info "YiemAgent conversation() 2-9 think count $loopcount " @__LINE__
addNewMessage(a, "action_call", action_call; maximumMsg=maximumMsg)
@info "YiemAgent conversation() 2-10 think count $loopcount " @__LINE__
action_result = thoughtdict["action_result"]
actionresult = Dict{String, Any}(
"role" => "action_result",
"content" => [Dict("type" => "text", "text" => "$action_result"),]
)
@info "YiemAgent conversation() 2-11 think count $loopcount " @__LINE__
addNewMessage(a, "actionresult", actionresult; maximumMsg=maximumMsg)
@info "YiemAgent conversation() 2-12 think count $loopcount " @__LINE__
end
end
end
@@ -442,15 +487,16 @@ julia>
"""
function think(a::T)::NamedTuple{(:thoughtdict, :result_raw), Tuple{OrderedDict, Any}} where {T<:agent}
# a.memory[:recap] = generateSituationReport(a, a.context["text"2textInstructLLM]; skiprecent=0)
@info "YiemAgent think() 1-1 " @__LINE__
thoughtdict = decisionMaker(a)
@info "YiemAgent think() 1 " @__LINE__
@info "YiemAgent think() 1-2 " @__LINE__
# pprintln(thoughtdict)
result_raw = nothing
if thoughtdict["action_name"] ["CHAT_BOX"]
@info "YiemAgent think() 2 " @__LINE__
@info "YiemAgent think() 2-1 " @__LINE__
thoughtdict, result_raw = generatechat!(a)
@info "YiemAgent think() 2-2 " @__LINE__
elseif thoughtdict["action_name"] == "END_CONVER_GUIDELINE"
@info "YiemAgent think() 3 " @__LINE__
thoughtdict, result_raw = end_conversation_guideline!(a, thoughtdict)
@@ -462,7 +508,14 @@ function think(a::T)::NamedTuple{(:thoughtdict, :result_raw), Tuple{OrderedDict,
elseif thoughtdict["action_name"] == "SEARCH_WINE_DATABASE"
@info "YiemAgent think() 5 " @__LINE__
thoughtdict, result_raw = search_wine_database!(a, thoughtdict; useSQLLLM=false)
#WORKING result_raw will be a df. i need to get images so i can send to frontend
if result_raw !== nothing && result_raw isa Vector
if haskey(a.memory["shortmem"], "items_info")
append!(a.memory["shortmem"]["items_info"], result_raw)
else
a.memory["shortmem"]["items_info"] = result_raw
end
end
else
@info "YiemAgent think() 6 " @__LINE__
error("condition is not defined ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
@@ -629,7 +682,7 @@ function generatechat!(a::T; maxattempt::Integer=10
"action_name": "...",
"action_input": "..."
"""
@info "YiemAgent generatechat!() 1 " @__LINE__
system_msg = Dict(
"role" => "system",
"content" => [
@@ -644,7 +697,7 @@ function generatechat!(a::T; maxattempt::Integer=10
errornote = "N/A"
response = nothing # placeholder for show when error msg show up
@info "YiemAgent generatechat!() 2 " @__LINE__
for attempt in 1:maxattempt
if attempt > 1
println("\nYiemAgent generatechat() attempt $attempt/$maxattempt ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
@@ -660,10 +713,9 @@ function generatechat!(a::T; maxattempt::Integer=10
response = GeneralUtils.clean_json_response(response)
response = GeneralUtils.remove_french_accents(response)
think, response = GeneralUtils.extractthink(response)
response = String(split(response, ", observation")[1]) # in case LLM generate observation key which it isn't supposed to
response = strip(response)
response = strip(response)
@info "YiemAgent generatechat!() 3 " @__LINE__
responsedict = nothing
if occursin(requiredKeys[2], response)
try
@@ -673,8 +725,12 @@ function generatechat!(a::T; maxattempt::Integer=10
println("\nERROR YiemAgent generatechat() failed to parse response: $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end
else
println("\nERROR YiemAgent generatechat() $errornote --(not qualify response)> $responsedict", @__FILE__, ":", @__LINE__, " $(Dates.now())\n")
continue
end
@info "YiemAgent generatechat!() 4 " @__LINE__
# check whether all answer's key points are in responsedict
ispass, errormsg = GeneralUtils.checkAgentResponse_JSON(responsedict, requiredKeys)
if !ispass
@@ -682,7 +738,7 @@ function generatechat!(a::T; maxattempt::Integer=10
println("\nERROR YiemAgent generatechat() $errornote --(not qualify response)> $responsedict", @__FILE__, ":", @__LINE__, " $(Dates.now())\n")
continue
end
@info "YiemAgent generatechat!() 5 " @__LINE__
# if responsedict["action_name"] ∉ ["CHAT_BOX", "SEARCH_WINE_DATABASE", "PRESENT_WINE_GUIDELINE", "END_CONVER_GUIDELINE"]
# errornote = "Your previous attempt didn't use the given functions"
# println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)--> $(responsedict["action_name"])", @__FILE__, ":", @__LINE__, " $(Dates.now())")
@@ -691,7 +747,8 @@ function generatechat!(a::T; maxattempt::Integer=10
# println("\nYiem decisionMaker() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# pprintln(responsedict)
responsedict["action_result"] = "Action result is the next user dialogue."
@info "YiemAgent generatechat!() 6 " @__LINE__
return (thoughtdict=responsedict, result_raw=responsedict["action_input"])
end
error("YiemAgent generatechat() failed to generate a thought ", response)