This commit is contained in:
narawat lamaiin
2024-08-03 18:58:16 +07:00
parent 51f91e8492
commit c4864a22ed
3 changed files with 177 additions and 19 deletions

View File

@@ -212,16 +212,23 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent}
systemmsg = systemmsg =
""" """
You are a helpful sommelier working for a wine store. You are an website-based polite sommelier working for an online wine store. You are currently talking with the user.
Your task is to help the user choose the best wine that match the user preferences from your inventory. Your task is to help the user choose the best wine that match the user preferences from your inventory.
Definitions:
"observation" is result of the preceding immediate action.
At each round of conversation, the user will give you the current situation: At each round of conversation, the user will give you the current situation:
Context: ... Context: ...
Your earlier conversation with the user: ... Your conversation with the user: ...
You MUST follow the following guidelines: You MUST follow the following guidelines:
- You won't know which wines are in stock until you check your inventory. - Generally speaking, your inventory has some wines from France, the United States, Australia, Spain, and Italy but you won't know which wines are in stock until you check your inventory.
- Use the "ask the user's preferences, then check inventory" strategy to help the user, as there are many wines in the inventory.
- Check inventory before recommending or suggesting wines to the user.
- Only recommending wine from your inventory. - Only recommending wine from your inventory.
- After recommending wine to the user, ask if there is anything else you can help with, but do not offer any additional services. If the user doesn't need anything else, say thank you and goodbye.
- Do not offer the user to try wine as you are internet-based agent.
You should follow the following guidelines as you see fit: You should follow the following guidelines as you see fit:
- If the user interrupts, prioritize the user. - If the user interrupts, prioritize the user.
@@ -230,8 +237,6 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent}
- Get to know what occasion the user is buying wine for. - 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 characteristics of wine the user is looking for e.g. tannin, sweetness, intensity, acidity.
- Get to know what food will be served with wine. - Get to know what food will be served with wine.
- Based on the user's information, check your inventory and recommend a suitable wine from your stock.
- After recommending wine to the user, ask if there is anything else you can help with, but do not offer any additional services. If the user doesnt need anything else, say thank you and goodbye.
You should then respond to the user with interleaving Thought, Plan, Action and Observation: You should then respond to the user with interleaving Thought, Plan, Action and Observation:
- thought: - thought:
@@ -243,24 +248,33 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent}
Good query example: black car, a stereo, 200 mile range, electric motor. Good query example: black car, a stereo, 200 mile range, electric motor.
Good query example: How many car brand are from Asia? Good query example: How many car brand are from Asia?
- action_input: input to the action - action_input: input to the action
- observation: result of the action. - recommending_wine: Are you recommending wines 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: ...
plan: ... plan: ...
action_name: ... action_name: ...
action_input: ... action_input: ...
observation: ... recommending_wine: ...
Let's begin! Let's begin!
""" """
context = length(a.memory[:shortmem]) > 0 ? vectorOfDictToText(a.memory[:shortmem], withkey=false) : "None" # context = length(a.memory[:shortmem]) > 0 ? vectorOfDictToText(a.memory[:shortmem], withkey=false) : "DO not recommending wine because inventory has not been searched yet"
context =
if length(a.memory[:shortmem]) > 0
vectorOfDictToText(a.memory[:shortmem], withkey=false)
else
"None"
end
chathistory = vectorOfDictToText(a.chathistory) chathistory = vectorOfDictToText(a.chathistory)
checkinventory_flag = ""
usermsg = usermsg =
""" """
Context: $context Context: $context
Your earlier conversation with the user: $chathistory) Your conversation with the user: $chathistory)
$checkinventory_flag
""" """
_prompt = _prompt =
@@ -280,7 +294,7 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent}
try try
response = a.text2textInstructLLM(prompt) response = a.text2textInstructLLM(prompt)
responsedict = GeneralUtils.textToDict(response, responsedict = GeneralUtils.textToDict(response,
["thought", "plan", "action_name", "action_input", "observation"], ["thought", "plan", "action_name", "action_input", "recommending_wine"],
rightmarker=":", symbolkey=true) rightmarker=":", symbolkey=true)
if responsedict[:action_name] ["CHATBOX", "CHECKINVENTORY"] if responsedict[:action_name] ["CHATBOX", "CHECKINVENTORY"]
@@ -294,13 +308,27 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent}
end end
# check if there are more than 1 key per categories # check if there are more than 1 key per categories
for i [:thought, :plan, :action_name, :action_input, :observation] for i [:thought, :plan, :action_name, :action_input, :recommending_wine]
matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i) matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i)
if length(matchkeys) > 1 if length(matchkeys) > 1
error("DecisionMaker has more than one key per categories") error("DecisionMaker has more than one key per categories")
end end
end end
println("")
println("--> Yiem decisionMaker ", @__FILE__, " ", @__LINE__)
pprintln(responsedict)
isMemEmpty = isempty(a.memory[:shortmem])
if occursin("Yes", responsedict[:recommending_wine]) && isMemEmpty &&
responsedict[:action_name] != "CHECKINVENTORY"
checkinventory_flag = "You must check inventory before recommending wine to the user."
error( "You must check inventory before recommending wine")
else
checkinventory_flag = ""
end
delete!(responsedict, :recommending_wine)
return responsedict return responsedict
catch e catch e
io = IOBuffer() io = IOBuffer()
@@ -1016,9 +1044,6 @@ julia>
""" """
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}
thoughtDict = decisionMaker(a) thoughtDict = decisionMaker(a)
println("")
println("--> think ", @__FILE__, " ", @__LINE__)
pprintln(thoughtDict)
actionname = thoughtDict[:action_name] actionname = thoughtDict[:action_name]
actioninput = thoughtDict[:action_input] actioninput = thoughtDict[:action_input]
@@ -1049,7 +1074,7 @@ end
[TESTING] [TESTING]
""" """
function forceInventoryCheck(a::T)::NamedTuple{(:actionname, :result), Tuple{String, String}} where {T<:agent} function forceInventoryCheck(a::T)::NamedTuple{(:actionname, :result), Tuple{String, String}} where {T<:agent}
println("--> forceInventoryCheck()")
thoughtDict = thinkCheckInventory(a) thoughtDict = thinkCheckInventory(a)
actionname = thoughtDict[:action_name] actionname = thoughtDict[:action_name]
actioninput = thoughtDict[:action_input] actioninput = thoughtDict[:action_input]
@@ -1203,7 +1228,8 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F
Your current thoughts in your mind: ... Your current thoughts in your mind: ...
You must follow the following guidelines: You must follow the following guidelines:
- Check your inventory before mentioning any wine or region to the user - You won't know which wines are in stock until you check your inventory.
- Only recommending (suggesting) wine from your inventory.
- Your thoughts matter. - Your thoughts matter.
- Do not offer the user to try wine as you are internet-based agent. - Do not offer the user to try wine as you are internet-based agent.

View File

@@ -394,8 +394,7 @@ function extractWineAttributes_1(a::T1, input::T2
end end
#[TESTING] remove tasting_notes because the database didn't prepare to be search using it #[TESTING] remove tasting_notes because the database didn't prepare to be search using it
responsedict[:tasting_notes] = "NA" responsedict[:tasting_notes] = responsedict[:flavor]
responsedict[:flavor] = "NA"
result = "" result = ""
for (k, v) in responsedict for (k, v) in responsedict
@@ -658,6 +657,138 @@ function jsoncorrection(config::T1, input::T2, correctJsonExample::T3;
end end
end end
# [WORKING] check whether
# function isrecommend(state::T1, text2textInstructLLM::Function
# ) where {T1<:AbstractDict}
# systemmsg =
# """
# You are a helpful assistant that analyzes agent's trajectories to find solutions and observations (i.e., the results of actions) to answer the user's questions.
# Definitions:
# "question" is the user's question.
# "thought" is step-by-step reasoning about the current situation.
# "plan" is what to do to complete the task from the current situation.
# “action_name” is the name of the action taken, which 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. The best query must includes "budget", "type of wine", "characteristics of wine" and "food pairing".
# "action_input" is the input to the action
# "observation" is result of the preceding immediate action.
# At each round of conversation, the user will give you:
# Context: ...
# Trajectories: ...
# You should then respond to the user with:
# 1) trajectory_evaluation:
# - Analyze the trajectories of a solution to answer the user's original question.
# Then given a question and a trajectory, evaluate its correctness and provide your reasoning and
# analysis in detail. Focus on the latest thought, action, and observation.
# Incomplete trajectories can be correct if the thoughts and actions so far are correct,
# even if the answer is not found yet. Do not generate additional thoughts or actions.
# 2) answer_evaluation: Focus only on the matter mentioned in the question and analyze how the latest observation addresses the question.
# 3) accepted_as_answer: Decide whether the latest observation's content answers the question. The possible responses are either 'Yes' or 'No.'
# Bad example (The observation didn't answers the question):
# question: Find cars with 4 wheels.
# observation: There are 2 cars in the table.
# Good example (The observation answers the question):
# question: Find cars with a stereo.
# observation: There are 1 cars in the table. 1) brand: Toyota, model: yaris, color: black.
# 4) score: Correctness score s where s is a single integer between 0 to 9.
# - 0 means the trajectories are incorrect.
# - 9 means the trajectories are correct, and the observation's content directly answers the question.
# 5) suggestion: if accepted_as_answer is "No", provide suggestion.
# You should only respond in format as described below:
# trajectory_evaluation: ...
# answer_evaluation: ...
# accepted_as_answer: ...
# score: ...
# suggestion: ...
# Let's begin!
# """
# thoughthistory = ""
# for (k, v) in state[:thoughtHistory]
# thoughthistory *= "$k: $v\n"
# end
# usermsg =
# """
# Context: None
# Trajectories: $thoughthistory
# """
# _prompt =
# [
# Dict(:name=> "system", :text=> systemmsg),
# Dict(:name=> "user", :text=> usermsg)
# ]
# # put in model format
# prompt = GeneralUtils.formatLLMtext(_prompt, "llama3instruct")
# prompt *=
# """
# <|start_header_id|>assistant<|end_header_id|>
# """
# for attempt in 1:5
# try
# response = text2textInstructLLM(prompt)
# responsedict = GeneralUtils.textToDict(response,
# ["trajectory_evaluation", "answer_evaluation", "accepted_as_answer", "score", "suggestion"],
# rightmarker=":", symbolkey=true)
# # check if dict has all required value
# trajectoryevaluation_text::AbstractString = responsedict[:trajectory_evaluation]
# answerevaluation_text::AbstractString = responsedict[:answer_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[:trajectory_evaluation]) $(responsedict[:answer_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
# end
# println("--> 5 Evaluator ", @__FILE__, " ", @__LINE__)
# pprintln(Dict(responsedict))
# return responsedict[:score]
# 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("evaluator failed to generate an evaluation")
# end

View File

@@ -89,7 +89,8 @@ main()
""" """
I'm having a graduation party this evening. I have no budget limit. I want a bottle of dry white wine. I'm having a graduation party this evening. I have no budget limit. I want a bottle of dry white wine.
I have no specific idea but I like medium bodied wine from France. The party will be casual. The party will be casual with no food serving.
I'm open to suggestion since I have no specific idea about wine other than I like full bodied wine from France.
The latter one seems nice. The latter one seems nice.
""" """