From 37a9a387968ff73385148fe85350d260ca036d33 Mon Sep 17 00:00:00 2001 From: narawat lamaiin Date: Tue, 16 Jul 2024 15:32:26 +0700 Subject: [PATCH] update --- src/interface.jl | 128 ++++++++++++++++++++++++++++++----------------- src/type.jl | 11 ++-- src/util.jl | 47 +++++++++++++---- test/runtest.jl | 10 ++-- 4 files changed, 132 insertions(+), 64 deletions(-) diff --git a/src/interface.jl b/src/interface.jl index 6dca850..4c49978 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -174,23 +174,59 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent} # {"thought" # """ + # systemmsg = + # """ + # You are a helpful sommelier working for a wine store. + # Your task is to help the user choose the best wine that match the user preferences from your inventory. + # You are also eager to improve your helpfulness. + + # You must follow the following guidelines: + # - Get to know how much the user willing to spend + # - Get to know type of wine the user is looking for e.g. red, white, sparkling, rose, dessert, fortified + # - Get to know what occasion the user is buying wine for + # - Get to know what characteristics of wine the user is looking for e.g. tannin, sweetness, intensity, acidity + # - Get to know what food the user will have with wine + + # At each round of conversation, the user will give you the current situation: + # Context: ... + # Your earlier conversation with the user: ... + + # You should then respond to the user with interleaving Thought, Plan, Action and Observation: + # - thought: + # 1) State your reasoning about the current situation. + # - plan: Based on the current situation, what would you do to complete the task? Be specific. + # - action (Must be aligned with your plan): Can be one of the following functions: + # 1) CHATBOX[text], which you can use to talk with the user. "text" is in verbal English. + # 2) WINESTOCK[query], which you can use to find info about wine in your inventory. "query" is a search term in verbal English. + # - observation: result of the action. + + # You should only respond in format as described below: + # thought: ... + # plan: ... + # action_name: ... + # action_input: ... + # observation: ... + + # Let's begin! + # """ + systemmsg = """ You are a helpful sommelier working for a wine store. Your task is to help the user choose the best wine that match the user preferences from your inventory. You are also eager to improve your helpfulness. - - You must follow the following guidelines: + + At each round of conversation, the user will give you the current situation: + Context: ... + Your earlier conversation with the user: ... + + You should follow the following guidelines: - Get to know how much the user willing to spend - Get to know type of wine the user is looking for e.g. red, white, sparkling, rose, dessert, fortified - Get to know what occasion the user is buying wine for - Get to know what characteristics of wine the user is looking for e.g. tannin, sweetness, intensity, acidity - Get to know what food the user will have with wine - At each round of conversation, the user will give you the current situation: - Context: ... - Your earlier conversation with the user: ... - You should then respond to the user with interleaving Thought, Plan, Action and Observation: - thought: 1) State your reasoning about the current situation. @@ -213,7 +249,7 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent} usermsg = """ Context: None - Your earlier conversation with the user: $(chatHistoryToString(a)) + Your earlier conversation with the user: $(vectorOfDictToText(a.chathistory)) """ _prompt = @@ -902,10 +938,11 @@ function conversation(a::T, userinput::Dict) where {T<:agent} # add usermsg to a.chathistory addNewMessage(a, "user", userinput[:text]) - thought = think(a) + think(a) # thought will be added to chat model via context - chatresponse = generatechat(a, thought) + chatresponse = generatechat(a) + addNewMessage(a, "assistant", chatresponse) return chatresponse end @@ -1024,8 +1061,12 @@ function think(a::T) where {T<:agent} isterminal::Bool = haskey(response, :isterminal) ? response[:isterminal] : false errormsg::Union{AbstractString, Nothing} = haskey(response, :errormsg) ? response[:errormsg] : nothing success::Bool = haskey(response, :success) ? response[:success] : false - a.shortmem - return result + + if actionname == "CHATBOX" + a.memory[:chatbox] = result + else + push!(a.memory[:shortmem], Dict(Symbol(actionname)=> result)) + end end @@ -1049,27 +1090,22 @@ julia> # Signature """ -function generatechat(a::T1, input::T2) where {T1<:agent, T2<:AbstractString} +function generatechat(a::T) where {T<:agent} systemmsg = """ You are a helpful sommelier working for a wine store. Your task is to help the user choose the best wine that match the user preferences from your inventory. - You are also eager to improve your helpfulness. - - You must follow the following guidelines: - - Get to know how much the user willing to spend - - Get to know type of wine the user is looking for e.g. red, white, sparkling, rose, dessert, fortified - - Get to know what occasion the user is buying wine for - - Get to know what characteristics of wine the user is looking for e.g. tannin, sweetness, intensity, acidity - - Get to know what food the user will have with wine - At each round of conversation, the user will give you: + At each round of conversation, the user will give you the current situation: Context: ... Your thoughts: Your current thinking in your mind Your earlier conversation with the user: ... + You must follow the following guidelines (if the user interrupts, prioritize the user): + - Do not recommend specific wine before you checked your inventory. + You should then respond to the user with: - - chat: what do you want to say to the user + - chat: what do you want to say to the user based on the current situation You should only respond in format as described below: chat: ... @@ -1077,11 +1113,13 @@ function generatechat(a::T1, input::T2) where {T1<:agent, T2<:AbstractString} Let's begin! """ + context = length(a.memory[:shortmem]) > 0 ? vectorOfDictToText(a.memory[:shortmem], withkey=false) : "None" + usermsg = """ - Context: None - Your thoughts: $input - Your earlier conversation with the user: $(chatHistoryToString(a)) + Context: $context + Your earlier conversation with the user: $(vectorOfDictToText(a.chathistory)) + Your thoughts: $(a.memory[:chatbox]) """ _prompt = @@ -1099,31 +1137,31 @@ function generatechat(a::T1, input::T2) where {T1<:agent, T2<:AbstractString} for attempt in 1:5 try - response = text2textInstructLLM(prompt) + response = a.text2textInstructLLM(prompt) responsedict = GeneralUtils.textToDict(response, ["chat"], rightmarker=":", symbolkey=true) - # check if dict has all required value - evaluationtext::AbstractString = responsedict[:evaluation] - responsedict[:score] = parse(Int, responsedict[:score]) # convert string "5" into integer 5 - score::Integer = responsedict[:score] - accepted_as_answer::AbstractString = responsedict[:accepted_as_answer] - suggestion::AbstractString = responsedict[:suggestion] - - # add to state here instead to in transition() because the latter causes julia extension crash (a bug in julia extension) - state[:evaluation] = responsedict[:evaluation] - state[:evaluationscore] = responsedict[:score] - state[:accepted_as_answer] = responsedict[:accepted_as_answer] - state[:suggestion] = responsedict[:suggestion] - - # mark as terminal state when the answer is achieved - if accepted_as_answer == "Yes" - state[:isterminal] = true - state[:reward] = 1 + for i ∈ [:chat] + if length(JSON3.write(responsedict[i])) == 0 + error("$i is empty ", @__LINE__) + end end - return responsedict[:score] + # check if there are more than 1 key per categories + for i ∈ [:chat] + matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i) + if length(matchkeys) > 1 + error("generatechat has more than one key per categories") + end + end + + result = responsedict[:chat] + + #[WORKING] delete chat + a.memory[:chatbox] = "" + + return result catch e io = IOBuffer() showerror(io, e) @@ -1134,7 +1172,7 @@ function generatechat(a::T1, input::T2) where {T1<:agent, T2<:AbstractString} println("") end end - error("evaluator failed to generate an evaluation") + error("generatechat failed to generate an evaluation") diff --git a/src/type.jl b/src/type.jl index be7de20..4504071 100644 --- a/src/type.jl +++ b/src/type.jl @@ -93,7 +93,7 @@ mutable struct sommelier <: agent """ chathistory::Vector{Dict{Symbol, Any}} - shortmem::Dict{Symbol, Any} + memory::Dict{Symbol, Any} # communication function text2textInstructLLM::Function @@ -105,7 +105,7 @@ function sommelier( name::String= "Assistant", id::String= string(uuid4()), maxHistoryMsg::Integer= 20, - chathistory::Vector{Dict{Symbol, Any}} = Vector{Dict{Symbol, Any}}(), + chathistory::Vector{Dict{Symbol, String}} = Vector{Dict{Symbol, String}}(), ) tools = Dict( # update input format @@ -127,13 +127,18 @@ function sommelier( # ), ) + memory = Dict{Symbol, Any}( + :chatbox=> "", + :shortmem=> Vector{Dict{Symbol, String}}() + ) + newAgent = sommelier( name, id, tools, maxHistoryMsg, chathistory, - Dict{Symbol, Any}(), + memory, text2textInstructLLM, ) diff --git a/src/util.jl b/src/util.jl index d6c6321..64e9400 100644 --- a/src/util.jl +++ b/src/util.jl @@ -1,6 +1,6 @@ module util -export clearhistory, addNewMessage, chatHistoryToString +export clearhistory, addNewMessage, vectorOfDictToText using UUIDs, Dates, DataStructures, HTTP, MQTTClient, JSON3 using GeneralUtils @@ -113,15 +113,43 @@ function addNewMessage(a::T1, name::String, text::T2; end -function chatHistoryToString(a) - chatHistoryStr = "" - for i in a.chathistory - name = i[:name] - text = i[:text] - chatHistoryStr *= "$name> $text" +""" + +# Arguments + - `v::Integer` + dummy variable + +# Return + +# Example +```jldoctest +julia> +``` + +# TODO + - [] update docstring + - [x] implement the function + +# Signature +""" +function vectorOfDictToText(vecd::Vector; withkey=true) + text = "" + + if withkey + for d in vecd + name = d[:name] + _text = d[:text] + text *= "$name> $_text \n" + end + else + for d in vecd + for (k, v) in d + text *= "$v \n" + end + end end - return chatHistoryStr + return text end @@ -131,9 +159,6 @@ end - - - # """ Convert a single chat dictionary into LLM model instruct format. # # Llama 3 instruct format example diff --git a/test/runtest.jl b/test/runtest.jl index fe30560..0ffb537 100644 --- a/test/runtest.jl +++ b/test/runtest.jl @@ -67,11 +67,11 @@ end response = YiemAgent.conversation(a, Dict(:text=> "Hello, I would like a get a bottle of wine.")) println("---> YiemAgent: ", response) -response = YiemAgent.conversation(a, Dict(:text=> "I'm having a graduation party this evening. I'll pay at most 30 bucks.", - :select=> nothing, - :reward=> 0, - :isterminal=> false, - ) ) +response = YiemAgent.conversation(a, Dict(:text=> "I'm having a graduation party this evening. I'll pay at most 30 bucks.")) +response = YiemAgent.conversation(a, Dict(:text=> "What type of wine do you recommend for a celebration?")) + + + println("---> YiemAgent: ", response)