update
This commit is contained in:
+128
-421
@@ -122,21 +122,21 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
|
||||
# """
|
||||
# end
|
||||
|
||||
recentevents_ind = GeneralUtils.recentElementsIndex(
|
||||
length(a.memory["events"]), recentevents; includelatest=true)
|
||||
# recentevents_ind = GeneralUtils.recentElementsIndex(
|
||||
# length(a.memory["events"]), recentevents; includelatest=true)
|
||||
|
||||
requiredKeys = ["plan", "actionname", "actioninput"]
|
||||
|
||||
context =
|
||||
"""
|
||||
<internal_context_for_assistant>
|
||||
$(a.memory["scratchpad"])
|
||||
$(a.memory["shortmem"]["scratchpad"])
|
||||
</internal_context_for_assistant>
|
||||
"""
|
||||
|
||||
#WORKING add context to text of the latest message (in the front).
|
||||
# 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 enumerate(a.chathistory[end]["content"])
|
||||
for d in a.chathistory[end]["content"]
|
||||
if d["type"] == "text"
|
||||
d["text"] = context * d["text"]
|
||||
break
|
||||
@@ -145,28 +145,27 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
|
||||
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
|
||||
|
||||
|
||||
openai_msg = Dict(
|
||||
"model" => "gemma-4-E4B-it-UD-Q4_K_XL",
|
||||
"messages" => a.chathistory,
|
||||
"temperature" => 0.7
|
||||
)
|
||||
|
||||
|
||||
response = a.context.text2textInstructLLM(prompt; senderId=a.id)
|
||||
response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName)
|
||||
response = a.context.text2textInstructLLM(a.id, openai_msg)
|
||||
# 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))
|
||||
_responsedict = JSON.parse(response)
|
||||
responsedict = GeneralUtils.dictify(_responsedict, keytype=String)
|
||||
catch
|
||||
println("\nERROR YiemAgent decisionMaker() failed to parse response: $response", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
continue
|
||||
@@ -179,21 +178,6 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
|
||||
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 responsedict["actionname"] ∉ ["CHATBOX", "CHECKWINE", "PRESENTBOX", "ENDCONVERSATION"]
|
||||
errornote = "Your previous attempt didn't use the given functions"
|
||||
@@ -204,64 +188,64 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
|
||||
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
|
||||
end
|
||||
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
|
||||
# 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
|
||||
# # 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)
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
# # 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
|
||||
# # 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())")
|
||||
@@ -286,7 +270,7 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
|
||||
# println("\nYiemAgent decisionMaker() saved to disk is done. agent $(a.id)")
|
||||
|
||||
|
||||
responsedict["prompt"] = prompt
|
||||
# responsedict["prompt"] = prompt
|
||||
return responsedict
|
||||
end
|
||||
error("DecisionMaker failed to generate a thought ", response)
|
||||
@@ -468,7 +452,7 @@ base64_string = base64encode(image_bytes)
|
||||
|
||||
# 2. Match the MIME type according to your file extension (e.g., png, jpeg)
|
||||
mime_type = "image/png"
|
||||
data_uri = "data:$(mime_type);base64,$(base64_string)"
|
||||
data1_uri = "data:<mime_type>;base64,<image1_base64_string>"
|
||||
|
||||
# 3. Construct payload with the Data URI
|
||||
message => Dict(
|
||||
@@ -483,33 +467,31 @@ message => Dict(
|
||||
)
|
||||
|
||||
# ---------------------------------------------- 100 --------------------------------------------- #
|
||||
""" #WORKING
|
||||
function conversation(a::sommelier; userinput::Union{Dict{String, Any}, JSON.Object{String, Any}}, maximumMsg=50)
|
||||
"""
|
||||
function conversation(a::sommelier; userinput::Union{Dict{String, Any}, JSON.Object{String, Any}},
|
||||
maximumMsg=50)
|
||||
userinput = GeneralUtils.dictify(userinput; keytype=String)
|
||||
|
||||
# find text in usermsg
|
||||
usertext = nothing
|
||||
text_position = nothing
|
||||
for (i, d) in enumerate(userinput["content"])
|
||||
if d["type"] == "text"
|
||||
usertext = d["text"]
|
||||
text_position = i
|
||||
end
|
||||
end
|
||||
|
||||
# place holder
|
||||
actionname = nothing
|
||||
result = nothing
|
||||
chatresponse = nothing
|
||||
|
||||
if userinput === nothing
|
||||
# thinking loop until AI wants to communicate with the user
|
||||
chatresponse = nothing
|
||||
while chatresponse === nothing
|
||||
actionname, result = think(a)
|
||||
if actionname ∈ ["CHATBOX", "PRESENTBOX", "ENDCONVERSATION"]
|
||||
chatresponse = result
|
||||
end
|
||||
end
|
||||
|
||||
addNewMessage(a, "assistant", chatresponse; maximumMsg=maximumMsg)
|
||||
|
||||
return chatresponse
|
||||
elseif user_text_input == "newtopic"
|
||||
if usertext == "newtopic"
|
||||
clearhistory(a)
|
||||
return "Okay. What shall we talk about?"
|
||||
else
|
||||
userinput["content"][text_position] = GeneralUtils.remove_french_accents(user_text_input)
|
||||
userinput["content"][text_position]["text"] = GeneralUtils.remove_french_accents(usertext)
|
||||
# add usermsg to a.chathistory but how do I handle images?
|
||||
addNewMessage(a, "user", userinput; maximumMsg=maximumMsg)
|
||||
|
||||
@@ -521,7 +503,11 @@ function conversation(a::sommelier; userinput::Union{Dict{String, Any}, JSON.Obj
|
||||
chatresponse = result
|
||||
end
|
||||
end
|
||||
addNewMessage(a, "assistant", chatresponse; maximumMsg=maximumMsg)
|
||||
assistant_response = Dict{String, Any}(
|
||||
"role" => "assistant",
|
||||
"content" => [Dict("type" => "text", "text" => chatresponse),]
|
||||
)
|
||||
addNewMessage(a, "assistant", assistant_response; maximumMsg=maximumMsg)
|
||||
|
||||
return chatresponse
|
||||
end
|
||||
@@ -568,7 +554,6 @@ function conversation(a::Union{companion, virtualcustomer}, userinput::Dict;
|
||||
end
|
||||
|
||||
"""
|
||||
|
||||
# Arguments
|
||||
|
||||
# Return
|
||||
@@ -578,92 +563,49 @@ end
|
||||
julia>
|
||||
```
|
||||
|
||||
""" # WORKING
|
||||
function think(a::T)::namedTuple{(:actionname, :result),Tuple{String,String}} where {T<:agent}
|
||||
"""
|
||||
function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} where {T<:agent}
|
||||
# a.memory[:recap] = generateSituationReport(a, a.context["text"2textInstructLLM]; skiprecent=0)
|
||||
thoughtDict = decisionMaker(a)
|
||||
|
||||
actionname = thoughtDict["actionname"]
|
||||
actioninput = thoughtDict["actioninput"]
|
||||
# # map action and input() to llm function
|
||||
# response =
|
||||
# if thoughtDict["actionname"] == "CHATBOX" || thoughtDict["actionname"] == "ENDCONVERSATION"
|
||||
# (result=thoughtDict["plan"], errormsg=nothing, success=true)
|
||||
# elseif thoughtDict["actionname"] == "CHECKWINE"
|
||||
# checkwine(a, thoughtDict["actioninput"])
|
||||
# elseif thoughtDict["actionname"] == "PRESENTBOX"
|
||||
# (result=thoughtDict["actioninput"], errormsg=nothing, success=true)
|
||||
# else
|
||||
# error("undefined LLM function. Requesting $(thoughtDict["actionname"])")
|
||||
# end
|
||||
|
||||
# map action and input() to llm function
|
||||
response =
|
||||
if actionname == "CHATBOX" || actionname == "ENDCONVERSATION"
|
||||
(result=thoughtDict["plan"], errormsg=nothing, success=true)
|
||||
elseif actionname == "CHECKWINE"
|
||||
checkwine(a, actioninput)
|
||||
elseif actionname == "PRESENTBOX"
|
||||
(result=actioninput, errormsg=nothing, success=true)
|
||||
# elseif actionname == "ENDCONVERSATION"
|
||||
# x = "Conclude the conversation, thanks the user then goodbye and inviting them to return next time."
|
||||
# (result=actioninput, errormsg=nothing, success=true)
|
||||
else
|
||||
error("undefined LLM function. Requesting $actionname")
|
||||
end
|
||||
# # this section allow LLM functions above to have different return values.
|
||||
# result = haskey(response, "result") ? response["result"] : nothing
|
||||
# rawresponse = haskey(response, "rawresponse") ? response["rawresponse"] : nothing
|
||||
# select = haskey(response, "select") ? response["select"] : nothing
|
||||
# reward::Integer = haskey(response, "reward") ? response["reward"] : 0
|
||||
# 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
|
||||
|
||||
# this section allow LLM functions above to have different return values.
|
||||
result = haskey(response, "result") ? response["result"] : nothing
|
||||
rawresponse = haskey(response, "rawresponse") ? response["rawresponse"] : nothing
|
||||
select = haskey(response, "select") ? response["select"] : nothing
|
||||
reward::Integer = haskey(response, "reward") ? response["reward"] : 0
|
||||
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
|
||||
result = nothing
|
||||
if thoughtDict["actionname"] ∈ ["CHATBOX"]
|
||||
result = thoughtDict["actioninput"]
|
||||
elseif thoughtDict["actionname"] ∈ ["ENDCONVERSATION"]
|
||||
# WORKING add ENDCONVERSATION guideline in to scratchpad
|
||||
guideline =
|
||||
"""
|
||||
To end conversation with the user
|
||||
"""
|
||||
|
||||
# # manage memory (pass msg to generatechat)
|
||||
# if actionname ∈ ["CHATBOX", "PRESENTBOX", "ENDCONVERSATION"]
|
||||
# chatresponse = generatechat(a, thoughtDict)
|
||||
# push!(a.memory["events"],
|
||||
# eventdict(;
|
||||
# event_description="the assistant talks to the user.",
|
||||
# timestamp=Dates.now(),
|
||||
# subject="assistant",
|
||||
# thought=thoughtDict,
|
||||
# actionname=actionname,
|
||||
# actioninput=actioninput,
|
||||
# )
|
||||
# )
|
||||
# result = chatresponse
|
||||
if actionname ∈ ["CHATBOX"]
|
||||
push!(a.memory["events"],
|
||||
eventdict(;
|
||||
event_description="an assistant talks to the user.",
|
||||
timestamp=Dates.now(),
|
||||
subject="assistant",
|
||||
thought=thoughtDict,
|
||||
actionname=actionname,
|
||||
actioninput=actioninput,
|
||||
)
|
||||
)
|
||||
result = actioninput
|
||||
elseif actionname ∈ ["ENDCONVERSATION"]
|
||||
chatresponse = generatechat(a, thoughtDict)
|
||||
push!(a.memory["events"],
|
||||
eventdict(;
|
||||
event_description="an assistant talks to the user to end conversation.",
|
||||
timestamp=Dates.now(),
|
||||
subject="assistant",
|
||||
thought=thoughtDict,
|
||||
actionname=actionname,
|
||||
actioninput=chatresponse,
|
||||
)
|
||||
)
|
||||
a.memory["shortmem"]["scratchpad"] =
|
||||
result = chatresponse
|
||||
elseif actionname ∈ ["PRESENTBOX"]
|
||||
chatresponse = presentbox(a, thoughtDict)
|
||||
push!(a.memory["events"],
|
||||
eventdict(;
|
||||
event_description="the assistant presents wines to the user.",
|
||||
timestamp=Dates.now(),
|
||||
subject="assistant",
|
||||
thought=thoughtDict,
|
||||
actionname=actionname,
|
||||
actioninput=chatresponse,
|
||||
)
|
||||
)
|
||||
elseif thoughtDict["actionname"] ∈ ["PRESENTBOX"]
|
||||
chatresponse = presentbox(a, thoughtDict) #PENDING
|
||||
result = chatresponse
|
||||
|
||||
elseif actionname == "CHECKWINE"
|
||||
elseif thoughtDict["actionname"] == "CHECKWINE"
|
||||
if rawresponse !== nothing
|
||||
vd = GeneralUtils.dfToVectorDict(rawresponse) # comes in dataframe format
|
||||
# a.memory["shortmem"]["found_wine"] = vd # used by decisionMaker() as a short note
|
||||
@@ -689,24 +631,11 @@ function think(a::T)::namedTuple{(:actionname, :result),Tuple{String,String}} wh
|
||||
# </database_search_result>
|
||||
# """
|
||||
end
|
||||
|
||||
push!(a.memory["events"],
|
||||
eventdict(;
|
||||
event_description= "the assistant searched the database.",
|
||||
timestamp= Dates.now(),
|
||||
subject= "assistant",
|
||||
thought=thoughtDict,
|
||||
actionname=actionname,
|
||||
actioninput= "I search the database with this search term: $actioninput",
|
||||
observation= "This is what I found:, $result"
|
||||
)
|
||||
)
|
||||
|
||||
else
|
||||
error("condition is not defined ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
end
|
||||
|
||||
return (actionname=actionname, result=result)
|
||||
return (actionname=thoughtDict["actionname"], result=result)
|
||||
end
|
||||
|
||||
|
||||
@@ -866,94 +795,22 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10, recentev
|
||||
error("presentbox() failed to generate a response")
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# Arguments
|
||||
- `a::T1`
|
||||
one of ChatAgent's agent.
|
||||
- `input::T2`
|
||||
# Return
|
||||
A JSON string of available wine
|
||||
|
||||
# Example
|
||||
```jldoctest
|
||||
julia>
|
||||
```
|
||||
|
||||
# TODO
|
||||
- [] update docs
|
||||
|
||||
# Signature
|
||||
"""
|
||||
function generatechat(a::sommelier, thoughtDict; maxattempt::Integer=10)
|
||||
systemmsg =
|
||||
function endconversation(a::sommelier, thoughtDict; maxattempt::Integer=10)
|
||||
text =
|
||||
"""
|
||||
Your role:
|
||||
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.
|
||||
Situation:
|
||||
You have some thinking in mind while you are talking with the user.
|
||||
Your mission:
|
||||
Concentrate on your thoughts and articulate them clearly. Keep the conversation engaging.
|
||||
Your responsibility does NOT includes:
|
||||
- Requesting the user to place an order, make a purchase, or confirm the order. These are the job of our sales team at the store.
|
||||
- Processing sales orders or engaging in any other sales-related activities. These are the job of our sales team at the store.
|
||||
- Answering questions or offering additional services beyond those related to your store's wine recommendations such as discounts, quantity, rewards programs, promotions, delivery options, shipping, boxes, gift wrapping, packaging, personalized messages or something similar. These are the job of our sales team at the store.
|
||||
At each round of conversation, you will be given the following:
|
||||
Your ongoing conversation with the user: ...
|
||||
Your thoughts: Your current thoughts in your mind
|
||||
You must follow the following guidelines:
|
||||
- Do not offer additional services you didn't think
|
||||
You should follow the following guidelines:
|
||||
- Focus on the latest conversation
|
||||
- If the user interrupts, prioritize the user
|
||||
- Be honest
|
||||
You should then respond to the user with:
|
||||
dialogue: what you want to say to the user
|
||||
You should only respond in JSON format as described below:
|
||||
{
|
||||
"dialogue": "..."
|
||||
}
|
||||
Here are some examples:
|
||||
Your ongoing conversation with the user: "user> hello, I need a new car\n"
|
||||
Your thoughts: "I should recommend the car we have found in our inventory to the user."
|
||||
{"dialogue": "We have a variety of cars available, including the Toyota Camry 2020, the Honda Civic 2021, and the Ford Mustang 2022. Which one would you like to see?"}
|
||||
|
||||
Let's begin!
|
||||
---
|
||||
|
||||
"""
|
||||
requiredKeys = [:dialogue]
|
||||
requiredKeys = ["dialogue"]
|
||||
|
||||
# a.memory["shortmem"]["available_wine"] is a vector of dictionary
|
||||
# context =
|
||||
# if length(a.memory["shortmem"]["available_wine"]) != 0
|
||||
# "Wines previously found in your inventory: $(availableWineToText(a.memory["shortmem"]["available_wine"]))"
|
||||
# else
|
||||
# "N/A"
|
||||
# end
|
||||
|
||||
chathistory = chatHistoryToText(a.chathistory)
|
||||
errornote = "N/A"
|
||||
response = nothing # placeholder for show when error msg show up
|
||||
|
||||
yourthought = thoughtDict["plan"]
|
||||
# yourthought1 = nothing
|
||||
system_msg = Dict(
|
||||
"role" => "system",
|
||||
"content" => [
|
||||
Dict("type" => "text", "text" => systemmsg),
|
||||
]
|
||||
)
|
||||
|
||||
for attempt in 1:maxattempt
|
||||
# if attempt > 1 # use to prevent LLM generate the same respond over and over
|
||||
# println("\nYiemAgent generatchat() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
# yourthought1 = paraphrase(a.context["text"2textInstructLLM], yourthought)
|
||||
# else
|
||||
# yourthought1 = yourthought
|
||||
# end
|
||||
|
||||
context =
|
||||
"""
|
||||
<context>
|
||||
Your ongoing conversation with the user: $chathistory
|
||||
Your thoughts: $yourthought
|
||||
P.S. $errornote
|
||||
</context>
|
||||
"""
|
||||
|
||||
unformatPrompt =
|
||||
[
|
||||
@@ -1036,156 +893,6 @@ function generatechat(a::sommelier, thoughtDict; maxattempt::Integer=10)
|
||||
end
|
||||
|
||||
|
||||
function generatechat(a::companion; recentevents::Integer=10,
|
||||
converPartnerName::Union{String, Nothing}=nothing, maxattempt=10)
|
||||
|
||||
recentchat_ind = GeneralUtils.recentElementsIndex(length(a.chathistory), recentevents;
|
||||
includelatest=true);
|
||||
recentchat = createChatLog(a.chathistory; index=recentchat_ind)
|
||||
|
||||
response = nothing # placeholder for show when error msg show up
|
||||
errornote = "N/A"
|
||||
|
||||
for attempt in 1:maxattempt
|
||||
if attempt > 1
|
||||
println("\nYiemAgent generatechat() attempt $attempt/$maxattempt ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
end
|
||||
|
||||
context =
|
||||
"""
|
||||
<context>
|
||||
P.S. $errornote
|
||||
</context>
|
||||
"""
|
||||
|
||||
unformatPrompt =
|
||||
[
|
||||
Dict("name" => "system", "text" => a.systemmsg),
|
||||
]
|
||||
|
||||
unformatPrompt = vcat(unformatPrompt, recentchat)
|
||||
# put in model format
|
||||
prompt = GeneralUtils.formatLLMtext(unformatPrompt, a.llmFormatName)
|
||||
# add info
|
||||
prompt = prompt * context
|
||||
|
||||
|
||||
# replace user and assistant with partner name
|
||||
prompt = replace(_prompt, "|>user"=>"|>$(converPartnerName)")
|
||||
prompt = replace(prompt, "|>assistant"=>"|>$(a.name)")
|
||||
|
||||
response = a.context.text2textInstructLLM(prompt; llmkwargs=llmkwargs, senderId=a.id)
|
||||
response = replace(response, "<|im_start|>"=> "")
|
||||
response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName)
|
||||
think, response = GeneralUtils.extractthink(response)
|
||||
|
||||
# check whether LLM just repeat the previous dialogue
|
||||
for msg in a.chathistory
|
||||
if msg["text"] == response
|
||||
errornote = "In your previous attempt, you repeated the previous dialogue. Please try again."
|
||||
println("\nYiemAgent generatechat() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
continue
|
||||
end
|
||||
end
|
||||
|
||||
return response
|
||||
end
|
||||
error("generatechat failed to generate a response")
|
||||
end
|
||||
|
||||
# modify it to work with customer object
|
||||
function generatechat(a::virtualcustomer;
|
||||
converPartnerName::Union{String, Nothing}=nothing, maxattempt=10, recentEventNum=10
|
||||
)
|
||||
|
||||
recent_ind = GeneralUtils.recentElementsIndex(length(a.memory["events"]), recentEventNum; includelatest=true)
|
||||
recentEventsDict = createEventsLog(a.memory["events"]; index=recent_ind)
|
||||
response = nothing # placeholder for show when error msg show up
|
||||
errornote = "N/A"
|
||||
header = ["Dialogue:", "Role:"]
|
||||
dictkey = ["dialogue", "role"]
|
||||
llmkwargs=Dict(
|
||||
"num_ctx" => 32768,
|
||||
"temperature" => 0.5,
|
||||
)
|
||||
|
||||
for attempt in 1:maxattempt
|
||||
if attempt > 1
|
||||
println("\nYiemAgent generatechat() attempt $attempt/$maxattempt ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
end
|
||||
context =
|
||||
"""
|
||||
<context>
|
||||
P.S. $errornote
|
||||
</context>
|
||||
"""
|
||||
|
||||
unformatPrompt =
|
||||
[
|
||||
Dict("name" => "system", "text" => a.systemmsg),
|
||||
]
|
||||
unformatPrompt = vcat(unformatPrompt, recentEventsDict)
|
||||
# put in model format
|
||||
prompt = GeneralUtils.formatLLMtext(unformatPrompt, a.llmFormatName)
|
||||
# add info
|
||||
prompt = prompt * context
|
||||
|
||||
response = a.context.text2textInstructLLM(prompt; llmkwargs=llmkwargs, senderId=a.id)
|
||||
response = replace(response, "<|im_start|>"=> "")
|
||||
response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName)
|
||||
think, response = GeneralUtils.extractthink(response)
|
||||
|
||||
# check whether response has all header
|
||||
detected_kw = GeneralUtils.detectKeywordVariation(header, response)
|
||||
missingkeys = [k for (k, v) in detected_kw if v === nothing]
|
||||
|
||||
if !isempty(missingkeys)
|
||||
errornote = "$missingkeys are missing from your previous response"
|
||||
println("\nERROR YiemAgent rolegenerator() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
continue
|
||||
elseif sum([length(i) for i in values(detected_kw)]) > length(header)
|
||||
errornote = "\nYour previous attempt has duplicated points according to the required response format"
|
||||
println("\nERROR YiemAgent rolegenerator() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
continue
|
||||
end
|
||||
|
||||
responsedict = GeneralUtils.textToDict(response, header;
|
||||
dictKey=dictkey, symbolkey=true)
|
||||
if responsedict["role"] == "no"
|
||||
errornote = "In your previous attempt you said $(responsedict["dialogue"]) which you, as a customer of a wine store, are not supposed to speak."
|
||||
println("\nYiemAgent generatechat() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
continue
|
||||
end
|
||||
|
||||
# check if the dialogue is the same as the previous one
|
||||
if length(responsedict["dialogue"]) != 0 && responsedict["dialogue"] == a.chathistory[end]["text"]
|
||||
errornote = "In your previous attempt you said $(responsedict["dialogue"]) which was the same as the previous one."
|
||||
println("\nYiemAgent generatechat() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
continue
|
||||
end
|
||||
|
||||
# check whether LLM just repeat the previous dialogue
|
||||
dublicate = false
|
||||
for msg in a.chathistory
|
||||
if msg["text"] == responsedict["dialogue"]
|
||||
errornote = "In your previous attempt, you repeated the earlier dialogue. Please try again."
|
||||
println("\nYiemAgent generatechat() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
dublicate = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if dublicate
|
||||
continue
|
||||
end
|
||||
|
||||
# println("\n$prompt", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
# println("\n $response")
|
||||
return responsedict["dialogue"]
|
||||
end
|
||||
error("generatechat failed to generate a response")
|
||||
end
|
||||
|
||||
|
||||
function generatequestion(a, text2textInstructLLM::Function, timeline)::String
|
||||
systemmsg =
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user