This commit is contained in:
narawat lamaiin
2024-08-20 20:19:06 +07:00
parent 2b1e280a31
commit a5dc2c1122
3 changed files with 174 additions and 28 deletions

View File

@@ -246,16 +246,16 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent}
- State your reasoning about the current situation. - State your reasoning about the current situation.
2) plan: Based on the current situation, state a complete plan to complete the task. Be specific. 2) plan: Based on the current situation, state a complete plan to complete the task. Be specific.
3) action_name (Must be aligned with your plan): The name of the action which can be one of the following functions: 3) action_name (Must be aligned with your plan): The name of the action which can be one of the following functions:
- CHATBOX which you can use to generate conversation in order to communicate with the user. The input is your intention for the talk. Be specific. - CHATBOX which you can use to generate conversation in order to communicate 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. - 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. Good query example: black car, a stereo, 200 mile range, electric motor.
- PRESENTBOX which you can use to introduce wines you just found in the database to the user. It is better than the CHATBOX function for presenting wines. The input is the names of wines to introduce. - PRESENTBOX which you can use to introduce / suggest / recommend wines you just found in the database to the user. It is better than the CHATBOX function for presenting wines. The input is the names of wines to introduce.
- ENDCONVERSATION which you can use when you want to finish the conversation with the user. The input is "NA". - ENDCONVERSATION which you can use when you want to finish the conversation with the user. The input is "NA".
4) action_input: input details of the action 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 name to the user? Can be "Yes" or "No"
You should only respond in format as described below: You should only respond in format as described below:
thought: ... thought: Let's think step by step. In order to ...
plan: ... plan: ...
action_name: ... action_name: ...
action_input: ... action_input: ...
@@ -339,6 +339,11 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent}
if occursin("Yes", responsedict[:mentioning_wine]) && isMemEmpty && if occursin("Yes", responsedict[:mentioning_wine]) && isMemEmpty &&
responsedict[:action_name] != "CHECKINVENTORY" 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")
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." 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 else
@@ -1094,8 +1099,8 @@ function think(a::T)::NamedTuple{(:actionname, :result), Tuple{String, String}}
checkinventory(a, actioninput) checkinventory(a, actioninput)
elseif actionname == "PRESENTBOX" elseif actionname == "PRESENTBOX"
x = """ x = """
1) Present $actioninput for the user to choose: " 1) Introduce $actioninput in details for the user to choose."
2) compare each option against the others and explain why each one is a suitable match for the user's specific needs. 2) compare each option against the others in details and explain why each one is a suitable match for the user's specific needs.
""" """
(result=x, errormsg=nothing, success=true) (result=x, errormsg=nothing, success=true)
elseif actionname == "ENDCONVERSATION" elseif actionname == "ENDCONVERSATION"
@@ -1283,11 +1288,14 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F
Your goal is: Recommend the best wines from your inventory that align with the user's preferences. Your goal is: Recommend the best wines from your inventory that align with the user's preferences.
Your responsibility includes: Your responsibility includes:
1) Given the situation, convey your thoughts to the user 1) Given the situation, convey your thoughts to the user.
Your responsibility does not include:
1) Processing sales orders or engaging in any other sales-related activities.
At each round of conversation, you will be given the current situation: At each round of conversation, you will be given the current situation:
Your conversation with the user: ... Your conversation with the user: ...
Your thoughts: Your current thoughts in your mind Your thoughts: Your current thoughts in your mind.
Context: ... Context: ...
You MUST follow the following guidelines: You MUST follow the following guidelines:
@@ -1297,12 +1305,14 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F
- Focus on the latest conversation. - Focus on the latest conversation.
You should then respond to the user with: You should then respond to the user with:
1) chat: Given the situation, convey your thoughts to the user 1) chat: Given the situation, what would you say to convey your thoughts to the user?
2) mentioning_wine: Are you mentioning specific wine name to the user? Can be "Yes" or "No" 2) mentioning_wine: Are you mentioning specific wine name to the user? Can be "Yes" or "No"
3) note: Put everything you want to add here
You should only respond in format as described below: You should only respond in format as described below:
chat: ... chat: ...
mentioning_wine: ... mentioning_wine: ...
note: ...
Let's begin! Let's begin!
""" """
@@ -1342,7 +1352,7 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F
try try
response = text2textInstructLLM(prompt) response = text2textInstructLLM(prompt)
responsedict = GeneralUtils.textToDict(response,["chat", "mentioning_wine"], responsedict = GeneralUtils.textToDict(response,["chat", "mentioning_wine", "note"],
rightmarker=":", symbolkey=true) rightmarker=":", symbolkey=true)
for i [:chat] for i [:chat]
@@ -1394,6 +1404,124 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F
end end
error("generatechat failed to generate an evaluation") error("generatechat failed to generate an evaluation")
end end
# function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::Function)
# systemmsg =
# """
# You are a helpful assistant acting as a polite, website-based sommelier for an online wine store.
# Your goal is: Recommend the best wines from your inventory that align with the user's preferences.
# Your responsibility includes:
# 1) Given the situation, convey your thoughts to the user
# At each round of conversation, you will be given the current situation:
# Your conversation with the user: ...
# Your thoughts: Your current thoughts in your mind
# Context: ...
# You MUST follow the following guidelines:
# - Do not offer additional services you didn't thought.
# You should follow the following guidelines:
# - Focus on the latest conversation.
# You should then respond to the user with:
# 1) chat: Given the situation, convey your thoughts to the user
# 2) mentioning_wine: Are you mentioning specific wine name to the user? Can be "Yes" or "No"
# You should only respond in format as described below:
# chat: ...
# mentioning_wine: ...
# Let's begin!
# """
# context =
# if length(memory[:shortmem]) > 0 #[WORKING] add with number order 1), 2)
# vectorOfDictToText(memory[:shortmem], withkey=false)
# else
# ""
# end
# chathistory = vectorOfDictToText(chathistory)
# errornote = ""
# response = nothing # placeholder for show when error msg show up
# for attempt in 1:10
# usermsg =
# """
# Your conversation with the user: $chathistory
# $context
# Your thoughts: $(memory[:CHATBOX])
# $errornote
# """
# _prompt =
# [
# Dict(:name=> "system", :text=> systemmsg),
# Dict(:name=> "user", :text=> usermsg)
# ]
# # put in model format
# prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct")
# prompt *=
# """
# <|start_header_id|>assistant<|end_header_id|>
# """
# try
# response = text2textInstructLLM(prompt)
# responsedict = GeneralUtils.textToDict(response,["chat", "mentioning_wine"],
# rightmarker=":", symbolkey=true)
# for i ∈ [:chat]
# if length(JSON3.write(responsedict[i])) == 0
# error("$i is empty ", @__LINE__)
# end
# end
# # 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
# # check if Context: is in chat
# if occursin("Context:", responsedict[:chat])
# error("Context: is in text. This is not allowed")
# end
# println("")
# println("--> generatechat() ", @__FILE__, " ", @__LINE__)
# pprintln(responsedict)
# # check if LLM recommend wine before checking inventory
# isMemEmpty = isempty(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")
# else
# errornote = ""
# end
# memory[:CHATBOX] = "" # delete content because it no longer used.
# delete!(responsedict, :mentioning_wine)
# result = responsedict[:chat]
# return result
# catch e
# io = IOBuffer()
# showerror(io, e)
# errorMsg = String(take!(io))
# st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
# println("")
# println("Attempt $attempt. Error occurred: $errorMsg\n$st")
# println("")
# end
# end
# error("generatechat failed to generate an evaluation")
# end
function generatequestion(a, text2textInstructLLM::Function)::String function generatequestion(a, text2textInstructLLM::Function)::String
@@ -1470,9 +1598,10 @@ function generatequestion(a, text2textInstructLLM::Function)::String
- All wines in your inventory are always in stock. - All wines in your inventory are always in stock.
You should then respond to the user with: You should then respond to the user with:
1) Reasoning: State your detailed reasoning of the current situation 1) thought: State your reasoning about the current situation
2) Q: Given the situation, "ask yourself" about the situation at least two, but no more than five, questions. 2) Q: Given the situation, "ask yourself" about the situation at least two, but no more than five, questions.
3) A: Given the situation, "answer to yourself" the best you can 3) A: Given the situation, "answer to yourself" the best you can
4) note: Put everything you want to add here
Here are some examples: Here are some examples:
Q: The user is asking for a cappuccino. Do I have it at my cafe? Q: The user is asking for a cappuccino. Do I have it at my cafe?
@@ -1481,16 +1610,21 @@ function generatequestion(a, text2textInstructLLM::Function)::String
A: Yes, I should. A: Yes, I should.
Q: Are they allergic to milk? Q: Are they allergic to milk?
A: Since they mentioned a cappuccino before, I think they are not allergic to milk. A: Since they mentioned a cappuccino before, I think they are not allergic to milk.
Q: Do I search the database yet?
A: I've searched the database and found ...
Q: Did I introduce the wines to the user yet?
A: Not yet. I will introduce the wines now.
You must only respond in format as described below: You must only respond in format as described below:
Reasoning: ... thought: ... , In order to ..., I should ask myself the following questions.
Q 1: ... Q_1: ...
A 1: ... A_1: ...
Q 2: ... Q_2: ...
A 2: ... A_2: ...
Q 3: ... Q_3: ...
A 3: ... A_3: ...
... ...
note: ...
Let's begin! Let's begin!
""" """
@@ -1529,11 +1663,15 @@ function generatequestion(a, text2textInstructLLM::Function)::String
try try
response = text2textInstructLLM(prompt) response = text2textInstructLLM(prompt)
q_number = count("Q ", response) q_number = count("Q_", response)
if q_number < 1 if q_number < 1
error("too few questions only $q_number questions are generated ", @__FILE__, " ", @__LINE__) error("too few questions only $q_number questions are generated ", @__FILE__, " ", @__LINE__)
end end
response = string(split(response, "Please")[1]) # LLM usually add comments which is no need. # response = string(split(response, "Please")[1]) # LLM usually add comments which is no need.
responsedict = GeneralUtils.textToDict(response,
["thought", "Q_1", "note"],
rightmarker=":", symbolkey=true)
response = "Q_1: " * responsedict[:Q_1]
println("--> generatequestion ", @__FILE__, " ", @__LINE__) println("--> generatequestion ", @__FILE__, " ", @__LINE__)
pprintln(response) pprintln(response)
return response return response
@@ -1575,7 +1713,7 @@ function generateSituationReport(a, text2textInstructLLM::Function)::Dict
systemmsg = systemmsg =
""" """
You are the assistant being in the given events. You are the assistant being in the given events.
Your task is to writes a summary for each event. Your task is to writes a summary for each event in an ongoing series.
At each round of conversation, you will be given the situation: At each round of conversation, you will be given the situation:
Total events: number of events you need to summarize. Total events: number of events you need to summarize.

View File

@@ -355,7 +355,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
- occasion: ... - occasion: ...
- food_to_be_paired_with_wine: food that the user will be served with the wine - food_to_be_paired_with_wine: food that the user will be served with the wine
- region: a region in a country where the wine is produced, such as Burgundy, Napa Valley, etc - region: a region in a country where the wine is produced, such as Burgundy, Napa Valley, etc
- country: a country where the wine is produced - country: a country where the wine is produced. Can be "Austria", "Australia", "France", "Germany", "Italy", "Portugal", "Spain", "UnitedStates"
- grape_variety: the name of the primary grape used to make the wine - grape_variety: the name of the primary grape used to make the wine
- flavors: names of items that the wine tastes like, such as citrus, lime, etc - flavors: names of items that the wine tastes like, such as citrus, lime, etc
- aromas: the wine's aromas, such as fruity, floral, etc - aromas: the wine's aromas, such as fruity, floral, etc
@@ -456,6 +456,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
end end
responsedict[:flavors] = replace(responsedict[:flavors], "notes"=>"") responsedict[:flavors] = replace(responsedict[:flavors], "notes"=>"")
delete!(responsedict, :region)
delete!(responsedict, :reasoning) delete!(responsedict, :reasoning)
delete!(responsedict, :tasting_notes) delete!(responsedict, :tasting_notes)
delete!(responsedict, :flavors) delete!(responsedict, :flavors)

View File

@@ -1,5 +1,5 @@
using Revise # remove when this package is completed using Revise # remove when this package is completed
using YiemAgent, GeneralUtils, JSON3, MQTTClient, Dates, UUIDs, LibPQ, DataStructures, SQLLLM using YiemAgent, GeneralUtils, JSON3, MQTTClient, Dates, UUIDs, LibPQ
using Base.Threads using Base.Threads
# ---------------------------------------------- 100 --------------------------------------------- # # ---------------------------------------------- 100 --------------------------------------------- #
@@ -68,19 +68,26 @@ function main()
response = YiemAgent.conversation(a, Dict(:text=> userinput)) response = YiemAgent.conversation(a, Dict(:text=> userinput))
println("") println("")
println("--> assistant response: \n", response) println("--> assistant response: \n", response)
println("") userinput = ""
println("--> user input:") for i in 1:3
userinput = readline() if userinput == ""
println("")
println("--> user input:")
userinput = readline()
else
break
end
end
end end
end end
main() main()
""" """
I'm joining a graduation party this evening. I want a bottle of full bodied, dry white wine from the United States. I'm ok with all price range. I'm joining a graduation party this evening. I want a bottle of full bodied, dry white wine from the US. I'm ok with any price range.
Well, the party is small casual with close friends and no food serving. Well, the party is small casual with close friends and no food serving.
I'm open to suggestion since I have no specific idea about wine. I'm open to suggestion since I have no specific idea about wine.
I'm ok with any region.
""" """