This commit is contained in:
narawat lamaiin
2024-10-15 07:55:03 +07:00
parent aaaadc7377
commit 523a8a7b41

View File

@@ -96,7 +96,7 @@ julia> output_thoughtDict = Dict(
# Signature
"""
function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:agent}
function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol,Any} where {T<:agent}
# lessonDict = copy(JSON3.read("lesson.json"))
@@ -258,6 +258,10 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:age
You should follow the following guidelines:
- Identifying at least four preferences before checking inventory significantly improves search results
- 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.
For your information:
- vintage 0 means non-vintage.
You should then respond to the user with interleaving Understanding, Reasoning, Plan, Action:
1) Understanding:
@@ -269,7 +273,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:age
- CHATBOX which you can use to talk with the user. The input is your intentions for the dialogue. Be specific.
- CHECKINVENTORY which you can use to check info about wine in your inventory. The input is a search term in verbal English.
Good query example: black car, a stereo, 200 mile range, electric motor.
- PRESENTBOX which you can use to introduce / suggest / recommend wines you just found in the database to the user.
- PRESENTBOX which you can use to introduce / suggest / recommend wine label you just found in the inventory to the user. Not for general conversation nor follow up conversation.
The input is instructions on how you want the presentation to be conducted.
Here are some input examples,
"First, provide detailed introductions of Zena Crown, Schrader Cabernet Sauvignon.
@@ -277,7 +281,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:age
Third, explain the potential impact each option could bring to the user."
- ENDCONVERSATION which you can use when you want to finish the conversation with the user. The input is "NA".
4) Action_input: input of the action
5) Mentioning_wine: Are you mentioning specific wine name to the user? Can be "Yes" or "No"
5) Mentioning_wine: Are you mentioning specific wine label or winery to the user? Can be "Yes" or "No"
You should only respond in format as described below:
Understanding: ...
@@ -293,7 +297,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:age
totalevents = length(a.memory[:events])
ind =
if totalevents > recent
start = totalevents-recent
start = totalevents - recent
start:totalevents
else
1:totalevents
@@ -317,8 +321,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:age
response = nothing # placeholder for show when error msg show up
for attempt in 1:10
usermsg =
"""
usermsg = """
Recap: $(a.memory[:recap])
Your recent events: $timeline
Your Q&A: $(a.memory[:QandA])
@@ -327,14 +330,13 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:age
_prompt =
[
Dict(:name=> "system", :text=> systemmsg),
Dict(:name=> "user", :text=> usermsg)
Dict(:name => "system", :text => systemmsg),
Dict(:name => "user", :text => usermsg)
]
# put in model format
prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct")
prompt *=
"""
prompt *= """
<|start_header_id|>assistant<|end_header_id|>
"""
@@ -372,12 +374,12 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:age
responsedict[:action_name] != "CHECKINVENTORY"
errornote = "Note: You can't recommend wines yet. You must check your inventory before recommending wine to the user."
error( "You can't recommend wines yet. You must check your inventory before recommending wines")
error("You can't recommend wines yet. You must check your inventory before recommending wines")
elseif responsedict[:action_name] == "PRESENTBOX" && isMemEmpty &&
responsedict[:action_name] != "CHECKINVENTORY"
errornote = "Note: You can't recommend wines yet. You must check your inventory before recommending wine to the user."
error( "You can't recommend wines yet. You must check your inventory before recommending wines")
error("You can't recommend wines yet. You must check your inventory before recommending wines")
else
errornote = ""
end
@@ -419,10 +421,9 @@ julia>
# Signature
"""
function evaluator(config::T1, state::T2
)::Tuple{String, Integer} where {T1<:AbstractDict, T2<:AbstractDict}
)::Tuple{String,Integer} where {T1<:AbstractDict,T2<:AbstractDict}
systemmsg =
"""
systemmsg = """
Analyze the trajectories of a solution to a question answering task. The trajectories are
labeled by environmental observations about the situation, thoughts that can reason about
the current situation and actions that can be three types:
@@ -475,21 +476,19 @@ function evaluator(config::T1, state::T2
Let's begin!
"""
usermsg =
"""
usermsg = """
$(JSON3.write(state[:thoughtHistory]))
"""
chathistory =
[
Dict(:name=> "system", :text=> systemmsg),
Dict(:name=> "user", :text=> usermsg)
Dict(:name => "system", :text => systemmsg),
Dict(:name => "user", :text => usermsg)
]
# put in model format
prompt = formatLLMtext(chathistory, "llama3instruct")
prompt *=
"""
prompt *= """
<|start_header_id|>assistant<|end_header_id|>
{
"""
@@ -503,20 +502,20 @@ function evaluator(config::T1, state::T2
msgMeta = GeneralUtils.generate_msgMeta(
externalService[:mqtttopic],
senderName= "evaluator",
senderId= string(uuid4()),
receiverName= "text2textinstruct",
mqttBroker= config[:mqttServerInfo][:broker],
mqttBrokerPort= config[:mqttServerInfo][:port],
senderName="evaluator",
senderId=string(uuid4()),
receiverName="text2textinstruct",
mqttBroker=config[:mqttServerInfo][:broker],
mqttBrokerPort=config[:mqttServerInfo][:port],
)
outgoingMsg = Dict(
:msgMeta=> msgMeta,
:payload=> Dict(
:text=> prompt,
:kwargs=> Dict(
:max_tokens=> 512,
:stop=> ["<|eot_id|>"],
:msgMeta => msgMeta,
:payload => Dict(
:text => prompt,
:kwargs => Dict(
:max_tokens => 512,
:stop => ["<|eot_id|>"],
)
)
)
@@ -525,8 +524,7 @@ function evaluator(config::T1, state::T2
try
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg)
_responseJsonStr = response[:response][:text]
expectedJsonExample =
"""
expectedJsonExample = """
Here is an expected JSON format:
{"evaluation": "...", "score": "..."}
"""
@@ -568,11 +566,10 @@ julia>
# Signature
"""
function reflector(config::T1, state::T2)::String where {T1<:AbstractDict, T2<:AbstractDict}
function reflector(config::T1, state::T2)::String where {T1<:AbstractDict,T2<:AbstractDict}
# https://github.com/andyz245/LanguageAgentTreeSearch/blob/main/hotpot/hotpot.py
_prompt =
"""
_prompt = """
You are a helpful sommelier working for a wine store.
Your goal is to recommend the best wine from your inventory that match the user preferences.
You will be given a question and a trajectory of the previous help you've done for a user.
@@ -635,20 +632,20 @@ function reflector(config::T1, state::T2)::String where {T1<:AbstractDict, T2<:A
msgMeta = GeneralUtils.generate_msgMeta(
a.config[:externalservice][:text2textinstruct][:mqtttopic],
senderName= "reflector",
senderId= string(uuid4()),
receiverName= "text2textinstruct",
mqttBroker= config[:mqttServerInfo][:broker],
mqttBrokerPort= config[:mqttServerInfo][:port],
senderName="reflector",
senderId=string(uuid4()),
receiverName="text2textinstruct",
mqttBroker=config[:mqttServerInfo][:broker],
mqttBrokerPort=config[:mqttServerInfo][:port],
)
outgoingMsg = Dict(
:msgMeta=> msgMeta,
:payload=> Dict(
:text=> prompt,
:kwargs=> Dict(
:max_tokens=> 512,
:stop=> ["<|eot_id|>"],
:msgMeta => msgMeta,
:payload => Dict(
:text => prompt,
:kwargs => Dict(
:max_tokens => 512,
:stop => ["<|eot_id|>"],
)
)
)
@@ -657,8 +654,7 @@ function reflector(config::T1, state::T2)::String where {T1<:AbstractDict, T2<:A
try
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg)
_responseJsonStr = response[:response][:text]
expectedJsonExample =
"""
expectedJsonExample = """
Here is an expected JSON format:
{"reflection": "..."}
"""
@@ -883,10 +879,10 @@ function conversation(a::sommelier, userinput::Dict)
# add user activity to events memory
push!(a.memory[:events],
eventdict(;
event_description= "the user talks to the assistant.",
timestamp= Dates.now(),
subject= "user",
action_or_dialogue= userinput[:text],
event_description="the user talks to the assistant.",
timestamp=Dates.now(),
subject="user",
action_or_dialogue=userinput[:text],
)
)
@@ -905,10 +901,10 @@ function conversation(a::sommelier, userinput::Dict)
push!(a.memory[:events],
eventdict(;
event_description= "the assistant talks to the user.",
timestamp= Dates.now(),
subject= "assistant",
action_or_dialogue= chatresponse,
event_description="the assistant talks to the user.",
timestamp=Dates.now(),
subject="assistant",
action_or_dialogue=chatresponse,
)
)
return chatresponse
@@ -928,10 +924,10 @@ function conversation(a::companion, userinput::Dict)
# add user activity to events memory
push!(a.memory[:events],
eventdict(;
event_description= "the user talks to the assistant.",
timestamp= Dates.now(),
subject= "user",
action_or_dialogue= userinput[:text],
event_description="the user talks to the assistant.",
timestamp=Dates.now(),
subject="user",
action_or_dialogue=userinput[:text],
)
)
chatresponse = generatechat(a)
@@ -940,10 +936,10 @@ function conversation(a::companion, userinput::Dict)
push!(a.memory[:events],
eventdict(;
event_description= "the assistant talks to the user.",
timestamp= Dates.now(),
subject= "assistant",
action_or_dialogue= chatresponse,
event_description="the assistant talks to the user.",
timestamp=Dates.now(),
subject="assistant",
action_or_dialogue=chatresponse,
)
)
return chatresponse
@@ -966,7 +962,7 @@ julia>
# Signature
"""
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.text2textInstructLLM; skiprecent=5)
a.memory[:QandA] = generatequestion(a, a.text2textInstructLLM; recent=5)
@@ -1006,14 +1002,14 @@ function think(a::T)::NamedTuple{(:actionname, :result), Tuple{String, String}}
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
errormsg::Union{AbstractString,Nothing} = haskey(response, :errormsg) ? response[:errormsg] : nothing
success::Bool = haskey(response, :success) ? response[:success] : false
# manage memory (pass msg to generatechat)
if actionname == "CHATBOX"
a.memory[:CHATBOX] = result
elseif actionname == "CHECKINVENTORY"
push!(a.memory[:shortmem], Dict(Symbol(actionname)=> result))
push!(a.memory[:shortmem], Dict(Symbol(actionname) => result))
elseif actionname == "PRESENTBOX" # tell the generatechat()
a.memory[:CHATBOX] = result
elseif actionname == "ENDCONVERSATION"
@@ -1048,8 +1044,7 @@ julia>
# Signature
"""
function generatechat(a::sommelier)
systemmsg =
"""
systemmsg = """
Your name is $(a.name). You are a helpful assistant acting as a polite, website-based sommelier for an online wine store.
You are currently talking with the user.
Your goal includes:
@@ -1075,7 +1070,7 @@ function generatechat(a::sommelier)
- If the user interrupts, prioritize the user
You should then respond to the user with:
1) Mentioning_wine: Are you going to mentioning specific wine name to the user? Can be "Yes" or "No"
1) Mentioning_wine: Are you going to mentioning specific wine label or winery to the user? Can be "Yes" or "No"
2) Chat: Given the situation, what would you say to the user?
You should only respond in format as described below:
@@ -1097,8 +1092,7 @@ function generatechat(a::sommelier)
response = nothing # placeholder for show when error msg show up
for attempt in 1:10
usermsg =
"""
usermsg = """
Your ongoing conversation with the user: $chathistory
$context
Your thoughts: $(a.memory[:CHATBOX])
@@ -1107,20 +1101,19 @@ function generatechat(a::sommelier)
_prompt =
[
Dict(:name=> "system", :text=> systemmsg),
Dict(:name=> "user", :text=> usermsg)
Dict(:name => "system", :text => systemmsg),
Dict(:name => "user", :text => usermsg)
]
# put in model format
prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct")
prompt *=
"""
prompt *= """
<|start_header_id|>assistant<|end_header_id|>
"""
try
response = a.text2textInstructLLM(prompt)
responsedict = GeneralUtils.textToDict(response,["Mentioning_wine", "Chat"],
responsedict = GeneralUtils.textToDict(response, ["Mentioning_wine", "Chat"],
rightmarker=":", symbolkey=true, lowercasekey=true)
for i [:chat]
@@ -1149,12 +1142,12 @@ function generatechat(a::sommelier)
isMemEmpty = isempty(a.memory[:shortmem])
if occursin("Yes", responsedict[:mentioning_wine]) && isMemEmpty
errornote = "Note: You can't recommend wines yet. You must check your inventory before recommending wine to the user."
error( "You must check your inventory before recommending wine")
error("You must check your inventory before recommending wine")
elseif occursin("(check", responsedict[:chat]) || occursin("*check", responsedict[:chat]) ||
occursin("inventory)", responsedict[:chat]) || occursin("inventory*", responsedict[:chat])
errornote = "Note: You can't recommend wines yet. You must check your inventory before recommending wine to the user."
error( "You must check your inventory before recommending wine")
error("You must check your inventory before recommending wine")
else
errornote = ""
end
@@ -1176,8 +1169,7 @@ function generatechat(a::sommelier)
end
function generatechat(a::companion)
systemmsg =
"""
systemmsg = """
Your name is $(a.name). You are a helpful assistant.
You are currently talking with the user.
Your goal includes:
@@ -1204,22 +1196,20 @@ function generatechat(a::companion)
noise = ""
for attempt in 1:10
usermsg =
"""
usermsg = """
Your ongoing conversation with the user: $chathistory
$noise
"""
_prompt =
[
Dict(:name=> "system", :text=> systemmsg),
Dict(:name=> "user", :text=> usermsg)
Dict(:name => "system", :text => systemmsg),
Dict(:name => "user", :text => usermsg)
]
# put in model format
prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct")
prompt *=
"""
prompt *= """
<|start_header_id|>assistant<|end_header_id|>
"""
@@ -1228,7 +1218,7 @@ function generatechat(a::companion)
println("\n~~~ generatechat() ", @__FILE__, " ", @__LINE__)
pprintln(response)
responsedict = GeneralUtils.textToDict(response,["Chat"],
responsedict = GeneralUtils.textToDict(response, ["Chat"],
rightmarker=":", symbolkey=true, lowercasekey=true)
result = responsedict[:chat]
@@ -1293,8 +1283,7 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
# Let's begin!
# """
systemmsg =
"""
systemmsg = """
Your name is $(a.name). You are a helpful assistant acting as a polite, website-based sommelier for $(a.retailername)'s online store.
Your goal includes:
1) Help the user select the best wines from your inventory that align with the user's preferences
@@ -1342,6 +1331,9 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
...
Here are some examples:
Q: Why the user saying this?
A: According to the situation, ...
Q: The user is asking for a cappuccino. Do I have it at my cafe?
A: No I don't.
@@ -1351,17 +1343,26 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
Q: Are they allergic to milk?
A: According to the situation, since they mentioned a cappuccino before, it seems they are not allergic to milk.
Q: Have I searched the database yet?
Q: Have I searched the inventory yet?
A: According to the situation, no. I need more information.
Q: Did I found something in the database?
Q: Should I check the inventory now?
A: According to the situation, ...
Q: What do I have in the inventory?
A: According to the situation, ...
Q: Which items are within the user price range? And which items are out of the user price rance?
A: According to the situation, ...
Q: Do I have what the user wants in stock?
A: According to the situation, ...
Q: Did I introduce the coffee blend varieties to the user yet?
A: According to the situation, no, I didn't because I have not searched the database yet.
A: According to the situation, no, I didn't because I have not searched the inventory yet.
Q: Am I certain about the information I'm going to share with the user, or should I verify the information first?
A: According to the situation, ...
Let's begin!
"""
@@ -1369,7 +1370,7 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
totalevents = length(a.memory[:events])
ind =
if totalevents > recent
start = totalevents-recent
start = totalevents - recent
start:totalevents
else
1:totalevents
@@ -1387,8 +1388,7 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
response = nothing # store for show when error msg show up
for attempt in 1:10
usermsg =
"""
usermsg = """
Recap: $(a.memory[:recap])
Your recent events: $timeline
$errornote
@@ -1396,14 +1396,13 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
_prompt =
[
Dict(:name=> "system", :text=> systemmsg),
Dict(:name=> "user", :text=> usermsg)
Dict(:name => "system", :text => systemmsg),
Dict(:name => "user", :text => usermsg)
]
# put in model format
prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct")
prompt *=
"""
prompt *= """
<|start_header_id|>assistant<|end_header_id|>
"""
@@ -1413,10 +1412,10 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
if q_number < 3
error("too few questions only $q_number questions are generated ", @__FILE__, " ", @__LINE__)
# check whether "A1" is in the response, if not error.
elseif !occursin("A1", response)
elseif !occursin("A1:", response)
error("no answer found in the response ", @__FILE__, " ", @__LINE__)
end
# response = string(split(response, "Please")[1]) # LLM usually add comments which is no need.
responsedict = GeneralUtils.textToDict(response,
["Understanding", "Q1"],
rightmarker=":", symbolkey=true, lowercasekey=true)
@@ -1429,7 +1428,7 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
showerror(io, e)
errorMsg = String(take!(io))
st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
println("\n Attempt $attempt. Error occurred: $errorMsg\n$st ", @__FILE__, " ", @__LINE__)
println("\nAttempt $attempt. Error occurred: $errorMsg\n$st ", @__FILE__, " ", @__LINE__)
end
end
error("generatequestion failed to generate a thought ", response)
@@ -1597,7 +1596,7 @@ end
# end
function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::Integer=0
)::Dict
)::Dict
# systemmsg =
# """
@@ -1619,8 +1618,7 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
# Let's begin!
# """
systemmsg =
"""
systemmsg = """
You are the assistant being in the given events.
Your task is to writes a summary for each event in an ongoing, interleaving series.
@@ -1645,7 +1643,7 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
"""
if length(a.memory[:events]) <= skiprecent
return Dict(:recap=> "None")
return Dict(:recap => "None")
end
events = deepcopy(a.memory[:events][1:end-skiprecent])
@@ -1663,8 +1661,7 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
response = nothing # store for show when error msg show up
for attempt in 1:10
usermsg =
"""
usermsg = """
Total events: $(length(events))
Events timeline: $timeline
$errornote
@@ -1672,14 +1669,13 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
_prompt =
[
Dict(:name=> "system", :text=> systemmsg),
Dict(:name=> "user", :text=> usermsg)
Dict(:name => "system", :text => systemmsg),
Dict(:name => "user", :text => usermsg)
]
# put in model format
prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct")
prompt *=
"""
prompt *= """
<|start_header_id|>assistant<|end_header_id|>
"""
@@ -1687,9 +1683,10 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
# responsedict = GeneralUtils.textToDict(response,
# ["summary", "presented", "selected"],
# rightmarker=":", symbolkey=true)
# println("--> generateSituationReport ", @__FILE__, " ", @__LINE__)
println("\n~~~ generateSituationReport() ", @__FILE__, " ", @__LINE__)
pprintln(response)
eventcount = count("Event_", response)
# if eventcount < (length(events))
@@ -1698,7 +1695,7 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
# else
# return Dict(:recap=> response)
# end
return Dict(:recap=> response)
return Dict(:recap => response)
end
error("generateSituationReport failed to generate a thought ", response)
end