From 57dd6df942af1b893287c8b456f44b5676774bb8 Mon Sep 17 00:00:00 2001 From: narawat Date: Wed, 24 Jun 2026 21:02:56 +0700 Subject: [PATCH] add context --- src/interface.jl | 283 +++++++++++++++++++++++++---------------------- src/type.jl | 24 +++- 2 files changed, 167 insertions(+), 140 deletions(-) diff --git a/src/interface.jl b/src/interface.jl index d672e60..f06265a 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -78,7 +78,7 @@ julia> config = Dict( ) ), ) - ) + ) julia> output_thoughtDict = Dict( "thought_1" => "The customer wants to buy a bottle of wine. This is a good start!", @@ -93,7 +93,7 @@ julia> output_thoughtDict = Dict( - [] use customerinfo - [] user storeinfo -""" #WORKING +""" function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10 ) where {T<:agent} println("\nExecuting YiemAgent decisionMaker()") @@ -127,159 +127,172 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10 requiredKeys = ["plan", "actionname", "actioninput"] + context = + """ + + $(a.memory["scratchpad"]) + + """ + + #WORKING add context to the latest message (in the front) + for d in enumerate(a.chathistory[end]["content"]) + if d["type"] == "text" + d["text"] = context * d["text"] + break + end + end errornote = "N/A" 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 - - unformatPrompt = - [ - Dict("name" => "system", "text" => systemmsg), - ] + for attempt in 1:maxattempt + if attempt > 1 + println("\nYiemAgent decisionMaker() attempt $attempt/$maxattempt ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + end + + unformatPrompt = + [ + Dict("name" => "system", "text" => systemmsg), + ] - unformatPrompt = vcat(unformatPrompt, recentEvents) - # put in model format - prompt = GeneralUtils.formatLLMtext(unformatPrompt, a.llmFormatName) - # add info - prompt = prompt * context + unformatPrompt = vcat(unformatPrompt, recentEvents) + # put in model format + prompt = GeneralUtils.formatLLMtext(unformatPrompt, a.llmFormatName) + # add info + prompt = prompt * context - response = a.context.text2textInstructLLM(prompt; senderId=a.id) - response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName) - 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) - responsedict = nothing - try - responsedict = copy(JSON.parsefile(response)) - catch - println("\nERROR YiemAgent decisionMaker() failed to parse response: $response", @__FILE__, ":", @__LINE__, " $(Dates.now())") - continue - end + response = a.context.text2textInstructLLM(prompt; senderId=a.id) + response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName) + 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) + responsedict = nothing + try + responsedict = copy(JSON.parsefile(response)) + catch + println("\nERROR YiemAgent decisionMaker() failed to parse response: $response", @__FILE__, ":", @__LINE__, " $(Dates.now())") + continue + end - # check whether all answer's key points are in responsedict - ispass, errormsg = checkAgentResponse_JSON(responsedict, requiredKeys) - if !ispass - errornote = errormsg - println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)> $responsedict", @__FILE__, ":", @__LINE__, " $(Dates.now())\n") - continue - end - # _responsedictKey = keys(responsedict) - # responsedictKey = [i for i in _responsedictKey] # convert into a list - # is_requiredKeys_in_responsedictKey = [i ∈ responsedictKey for i in requiredKeys] + # check whether all answer's key points are in responsedict + ispass, errormsg = checkAgentResponse_JSON(responsedict, requiredKeys) + if !ispass + errornote = errormsg + println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)> $responsedict", @__FILE__, ":", @__LINE__, " $(Dates.now())\n") + continue + end + # _responsedictKey = keys(responsedict) + # responsedictKey = [i for i in _responsedictKey] # convert into a list + # is_requiredKeys_in_responsedictKey = [i ∈ responsedictKey for i in requiredKeys] - # if length(is_requiredKeys_in_responsedictKey) > length(requiredKeys) - # errornote = "Your previous attempt has more key points than answer's required key points." - # println("\nERROR YiemAgent decisionMaker() $errornote ----(not qualify response)--> $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - # continue - # elseif !all(is_requiredKeys_in_responsedictKey) - # zeroind = findall(x -> x == 0, is_requiredKeys_in_responsedictKey) - # missingkeys = [requiredKeys[i] for i in zeroind] - # errornote = "$missingkeys are missing from your previous response" - # println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)--> $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - # continue - # end + # if length(is_requiredKeys_in_responsedictKey) > length(requiredKeys) + # errornote = "Your previous attempt has more key points than answer's required key points." + # println("\nERROR YiemAgent decisionMaker() $errornote ----(not qualify response)--> $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + # continue + # elseif !all(is_requiredKeys_in_responsedictKey) + # zeroind = findall(x -> x == 0, is_requiredKeys_in_responsedictKey) + # missingkeys = [requiredKeys[i] for i in zeroind] + # errornote = "$missingkeys are missing from your previous response" + # println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)--> $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + # continue + # end - if responsedict["actionname"] ∉ ["CHATBOX", "CHECKWINE", "PRESENTBOX", "ENDCONVERSATION"] - errornote = "Your previous attempt didn't use the given functions" - println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)--> $(responsedict["actionname"])", @__FILE__, ":", @__LINE__, " $(Dates.now())") - continue - end + if responsedict["actionname"] ∉ ["CHATBOX", "CHECKWINE", "PRESENTBOX", "ENDCONVERSATION"] + errornote = "Your previous attempt didn't use the given functions" + println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)--> $(responsedict["actionname"])", @__FILE__, ":", @__LINE__, " $(Dates.now())") + continue + end - println("\nYiem decisionMaker() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - pprintln(Dict(responsedict)) + println("\nYiem decisionMaker() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + pprintln(Dict(responsedict)) - # check whether an agent recommend wines before checking inventory or recommend wines - # outside its inventory - # ask LLM whether there are any winery mentioned in the response - mentioned_winery = detectWineryName(a, response) - if mentioned_winery != "None" - mentioned_winery = String.(strip.(split(mentioned_winery, ","))) + # check whether an agent recommend wines before checking inventory or recommend wines + # outside its inventory + # ask LLM whether there are any winery mentioned in the response + mentioned_winery = detectWineryName(a, response) + if mentioned_winery != "None" + mentioned_winery = String.(strip.(split(mentioned_winery, ","))) - # check whether the wine is in event - isWineInEvent = false - for winename in mentioned_winery - for event in a.memory["events"] - if event["observation"] !== nothing && occursin(winename, event["observation"]) - isWineInEvent = true - break - end + # check whether the wine is in event + isWineInEvent = false + for winename in mentioned_winery + for event in a.memory["events"] + if event["observation"] !== nothing && occursin(winename, event["observation"]) + isWineInEvent = true + break end end - - # then the agent is not supposed to recommend the wine - if isWineInEvent == false - errornote = "You recommended wines that are not in your inventory before. Please only recommend wines that you have previously found in your inventory." - println("\nERROR YiemAgent decisionMaker() $errornote $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - continue - end end - delete!(responsedict, :mentioned_winery) - - # check whether responsedict["actioninput"] is the same as previous dialogue - if !isempty(a.chathistory) && responsedict["actioninput"] == a.chathistory[end]["text"] - errornote = "In your previous attempt, you repeated the previous dialogue. Please try again." - println("\nERROR YiemAgent decisionMaker() $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + # then the agent is not supposed to recommend the wine + if isWineInEvent == false + errornote = "You recommended wines that are not in your inventory before. Please only recommend wines that you have previously found in your inventory." + println("\nERROR YiemAgent decisionMaker() $errornote $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") continue end - - evaluationdict = evaluator(a, timeline, responsedict, context) - if evaluationdict[:approval] == "no" - mentor_comment = evaluationdict[:suggestion] - errornote = "Your previous attempt was not good enough. Please try again. Here is the mentor's suggestion: $mentor_comment" - println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)--> \n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - continue - end - - # store for later training - responsedict["system"] = systemmsg - responsedict["unformatPrompt"] = unformatPrompt - responsedict["prompt"] = prompt - responsedict["context"] = context - responsedict["think"] = think - responsedict["response"] = response - # responsedict["QandA"] = QandA - - # check whether there is a file path exists before writing to it - if !haskey(a.memory["shortmem"], "decisionlog") - a.memory["shortmem"]["decisionlog"] = [responsedict] - else - push!(a.memory["shortmem"]["decisionlog"], responsedict) - end - - # # save to filename ./log/decisionlog.txt - # println("\nsaving YiemAgent decisionMaker() to disk ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - # filename = "agent_decision_log_$(a.id).json" - # filepath = "/appfolder/app/log/$filename" - # # check whether there is a file path exists before writing to it - # if !isfile(filepath) - # decisionlist = [responsedict] - # println("Creating file $filepath") - # open(filepath, "w") do io - # JSON.pretty(io, decisionlist) - # end - # else - # # read the file and append new data - # decisionlist = copy(JSON.parsefile(filepath)) - # push!(decisionlist, responsedict) - # println("Appending new data to file $filepath") - # open(filepath, "w") do io - # JSON.pretty(io, decisionlist) - # end - # end - # println("\nYiemAgent decisionMaker() saved to disk is done. agent $(a.id)") - - - responsedict["prompt"] = prompt - return responsedict end - error("DecisionMaker failed to generate a thought ", response) + + delete!(responsedict, :mentioned_winery) + + # check whether responsedict["actioninput"] is the same as previous dialogue + if !isempty(a.chathistory) && responsedict["actioninput"] == a.chathistory[end]["text"] + errornote = "In your previous attempt, you repeated the previous dialogue. Please try again." + println("\nERROR YiemAgent decisionMaker() $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + continue + end + + evaluationdict = evaluator(a, timeline, responsedict, context) + if evaluationdict[:approval] == "no" + mentor_comment = evaluationdict[:suggestion] + errornote = "Your previous attempt was not good enough. Please try again. Here is the mentor's suggestion: $mentor_comment" + println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)--> \n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + continue + end + + # store for later training + responsedict["system"] = systemmsg + responsedict["unformatPrompt"] = unformatPrompt + responsedict["prompt"] = prompt + responsedict["context"] = context + responsedict["think"] = think + responsedict["response"] = response + # responsedict["QandA"] = QandA + + # check whether there is a file path exists before writing to it + if !haskey(a.memory["shortmem"], "decisionlog") + a.memory["shortmem"]["decisionlog"] = [responsedict] + else + push!(a.memory["shortmem"]["decisionlog"], responsedict) + end + + # # save to filename ./log/decisionlog.txt + # println("\nsaving YiemAgent decisionMaker() to disk ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + # filename = "agent_decision_log_$(a.id).json" + # filepath = "/appfolder/app/log/$filename" + # # check whether there is a file path exists before writing to it + # if !isfile(filepath) + # decisionlist = [responsedict] + # println("Creating file $filepath") + # open(filepath, "w") do io + # JSON.pretty(io, decisionlist) + # end + # else + # # read the file and append new data + # decisionlist = copy(JSON.parsefile(filepath)) + # push!(decisionlist, responsedict) + # println("Appending new data to file $filepath") + # open(filepath, "w") do io + # JSON.pretty(io, decisionlist) + # end + # end + # println("\nYiemAgent decisionMaker() saved to disk is done. agent $(a.id)") + + + responsedict["prompt"] = prompt + return responsedict end + error("DecisionMaker failed to generate a thought ", response) end diff --git a/src/type.jl b/src/type.jl index 54e9253..57b48a1 100644 --- a/src/type.jl +++ b/src/type.jl @@ -321,16 +321,30 @@ function virtualcustomer( ) """ Memory - Ref: Chat prompt format https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML/discussions/3 - NO "system" message in chathistory because I want to add it at the inference time - chathistory= [ - Dict("name"=>"user", "text"=> "Wassup!", "timestamp"=> Dates.now()), - Dict("name"=>"assistant", "text"=> "Hi I'm your assistant.", "timestamp"=> Dates.now()), + Ref: Chat prompt format is openai + chathistory = [ + Dict( + "role" => "system", + "content" => [ + Dict("type" => "text", "text" => system_msg), + ] + ), + Dict( + "role" => "user", + "content" => [ + Dict("type" => "text", "text" => "Do you know this wine? Just give me brief intro."), + Dict( + "type" => "image_url", + "image_url" => Dict("url" => data1_uri) + ) + ] + ) ] """ memory = Dict{String, Any}( "shortmem"=> OrderedDict{String, Any}( ), + "scratchpad"=> "", "events"=> Vector{Dict{String, Any}}(), "state"=> Dict{String, Any}( ),