update
This commit is contained in:
@@ -226,7 +226,7 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent}
|
|||||||
- Get to know type of wine the user is looking for e.g. red, white, sparkling, rose, dessert, fortified
|
- 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 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 the user will have with wine
|
- Get to know what food will be served with wine
|
||||||
|
|
||||||
You must follow the following DON'T guidelines:
|
You must follow the following DON'T guidelines:
|
||||||
- Don't mention any specific wine until you've checked your inventory.
|
- Don't mention any specific wine until you've checked your inventory.
|
||||||
@@ -1024,6 +1024,8 @@ end
|
|||||||
# end
|
# end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Arguments
|
# Arguments
|
||||||
@@ -1052,8 +1054,7 @@ function think(a::T) where {T<:agent}
|
|||||||
if actionname == "CHATBOX"
|
if actionname == "CHATBOX"
|
||||||
(result=actioninput, errormsg=nothing, success=true)
|
(result=actioninput, errormsg=nothing, success=true)
|
||||||
elseif actionname == "WINESTOCK"
|
elseif actionname == "WINESTOCK"
|
||||||
DBconnection = LibPQ.Connection("host=192.168.88.12 port=5432 dbname=yiem_wine_assistant user=yiem password=yiem@Postgres_0.0")
|
winestock(a, actioninput)
|
||||||
winestock(actioninput, DBconnection, a.text2textInstructLLM)
|
|
||||||
else
|
else
|
||||||
error("undefined LLM function. Requesting $actionname")
|
error("undefined LLM function. Requesting $actionname")
|
||||||
end
|
end
|
||||||
@@ -1097,7 +1098,7 @@ julia>
|
|||||||
function generatechat(a::T) where {T<:agent}
|
function generatechat(a::T) where {T<:agent}
|
||||||
systemmsg =
|
systemmsg =
|
||||||
"""
|
"""
|
||||||
You are a helpful sommelier working for a wine store.
|
You are a polite 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.
|
Your task is to help the user choose the best wine that match the user preferences from your inventory.
|
||||||
|
|
||||||
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:
|
||||||
|
|||||||
@@ -362,15 +362,14 @@ julia> result = winestock(agent, input)
|
|||||||
|
|
||||||
# Signature
|
# Signature
|
||||||
"""
|
"""
|
||||||
function winestock(input::T, DBconnection, text2textInstructLLM::Function
|
function winestock(a::T1, input::T2
|
||||||
)::Union{Tuple{String, Number, Number, Bool}, Tuple{String, Nothing, Number, Bool}} where {T<:AbstractString}
|
)::Union{Tuple{String, Number, Number, Bool}, Tuple{String, Nothing, Number, Bool}} where {T1<:agent, T2<:AbstractString}
|
||||||
|
|
||||||
# SELECT *
|
# SELECT *
|
||||||
# FROM food
|
# FROM food
|
||||||
# WHERE 'China' = ANY(food_name)
|
# WHERE 'China' = ANY(food_name)
|
||||||
# OR 'India' = ANY(food_name);
|
# OR 'India' = ANY(food_name);
|
||||||
|
|
||||||
|
|
||||||
wineattributes = wineattributes_wordToNumber(a, input)
|
wineattributes = wineattributes_wordToNumber(a, input)
|
||||||
|
|
||||||
systemmsg =
|
systemmsg =
|
||||||
@@ -559,149 +558,162 @@ function winestock(input::T, DBconnection, text2textInstructLLM::Function
|
|||||||
# return result, nothing, 0, false
|
# return result, nothing, 0, false
|
||||||
end
|
end
|
||||||
|
|
||||||
function wineattributes_wordToNumber(config::T1, input::T2
|
|
||||||
)::Dict where {T1<:AbstractDict, T2<:AbstractString}
|
"""
|
||||||
|
|
||||||
|
# Arguments
|
||||||
|
- `v::Integer`
|
||||||
|
dummy variable
|
||||||
|
|
||||||
|
# Return
|
||||||
|
|
||||||
|
# Example
|
||||||
|
```jldoctest
|
||||||
|
julia>
|
||||||
|
```
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
- [] update docstring
|
||||||
|
- [WORKING] implement the function
|
||||||
|
|
||||||
|
# Signature
|
||||||
|
"""
|
||||||
|
function wineattributes_wordToNumber(a::T1, input::T2
|
||||||
|
)::Dict where {T1<:agent, T2<:AbstractString}
|
||||||
|
|
||||||
|
converstiontable =
|
||||||
|
"""
|
||||||
|
Conversion Table:
|
||||||
|
Intensity level:
|
||||||
|
Level 1: May correspond to "light-bodied" or a similar description.
|
||||||
|
Level 2: May correspond to "med light", "medium light" or a similar description.
|
||||||
|
Level 3: May correspond to "medium" or a similar description.
|
||||||
|
Level 4: May correspond to "med full", "medium full" or a similar description.
|
||||||
|
Level 5: May correspond to "full" or a similar description.
|
||||||
|
Sweetness level:
|
||||||
|
Level 1: May correspond to "dry", "no sweet" or a similar description.
|
||||||
|
Level 2: May correspond to "off dry", "less sweet" or a similar description.
|
||||||
|
Level 3: May correspond to "semi sweet" or a similar description.
|
||||||
|
Level 4: May correspond to "sweet" or a similar description.
|
||||||
|
Level 5: May correspond to "very sweet" or a similar description.
|
||||||
|
Tannin level:
|
||||||
|
Level 1: May correspond to "low tannin" or a similar description.
|
||||||
|
Level 2: May correspond to "semi low tannin" or a similar description.
|
||||||
|
Level 3: May correspond to "medium tannin" or a similar description.
|
||||||
|
Level 4: May correspond to "semi high tannin" or a similar description.
|
||||||
|
Level 5: May correspond to "high tannin" or a similar description.
|
||||||
|
Acidity level:
|
||||||
|
Level 1: May correspond to "low acidity" or a similar description.
|
||||||
|
Level 2: May correspond to "semi low acidity" or a similar description.
|
||||||
|
Level 3: May correspond to "medium acidity" or a similar description.
|
||||||
|
Level 4: May correspond to "semi high acidity" or a similar description.
|
||||||
|
Level 5: May correspond to "high acidity" or a similar description.
|
||||||
|
"""
|
||||||
|
|
||||||
systemmsg =
|
systemmsg =
|
||||||
"""
|
"""
|
||||||
As an attentive sommelier, your mission is to determine the user's preferred levels of sweetness, intensity, tannin, acidity and other criteria for a wine based on their input.
|
As an attentive sommelier, your task is to determine the user's wine preferred levels of sweetness, intensity, tannin, acidity and other criteria based on the user query.
|
||||||
You'll achieve this by referring to the provided conversion table.
|
|
||||||
|
|
||||||
Conversion Table:
|
|
||||||
Intensity level:
|
|
||||||
Level 1: May correspond to "light-bodied" or a similar description.
|
|
||||||
Level 2: May correspond to "med light", "medium light" or a similar description.
|
|
||||||
Level 3: May correspond to "medium" or a similar description.
|
|
||||||
Level 4: May correspond to "med full", "medium full" or a similar description.
|
|
||||||
Level 5: May correspond to "full" or a similar description.
|
|
||||||
Sweetness level:
|
|
||||||
Level 1: May correspond to "dry", "no sweet" or a similar description.
|
|
||||||
Level 2: May correspond to "off dry", "less sweet" or a similar description.
|
|
||||||
Level 3: May correspond to "semi sweet" or a similar description.
|
|
||||||
Level 4: May correspond to "sweet" or a similar description.
|
|
||||||
Level 5: May correspond to "very sweet" or a similar description.
|
|
||||||
Tannin level:
|
|
||||||
Level 1: May correspond to "low tannin" or a similar description.
|
|
||||||
Level 2: May correspond to "semi low tannin" or a similar description.
|
|
||||||
Level 3: May correspond to "medium tannin" or a similar description.
|
|
||||||
Level 4: May correspond to "semi high tannin" or a similar description.
|
|
||||||
Level 5: May correspond to "high tannin" or a similar description.
|
|
||||||
Acidity level:
|
|
||||||
Level 1: May correspond to "low acidity" or a similar description.
|
|
||||||
Level 2: May correspond to "semi low acidity" or a similar description.
|
|
||||||
Level 3: May correspond to "medium acidity" or a similar description.
|
|
||||||
Level 4: May correspond to "semi high acidity" or a similar description.
|
|
||||||
Level 5: May correspond to "high acidity" or a similar description.
|
|
||||||
|
|
||||||
You should only respond in JSON format as describe below:
|
At each round of conversation, the user will give you the current situation:
|
||||||
{
|
Conversion Table: ...
|
||||||
"attributes":
|
Query: ...
|
||||||
{
|
Last round missing info: some info are missing
|
||||||
"sweetness": "sweetness level",
|
|
||||||
"acidity": "acidity level",
|
You should follow the following guidelines:
|
||||||
"tannin": "tannin level",
|
- If specific information is unavailable, please use "NA" to indicate this.
|
||||||
"intensity": "intensity level"
|
- Use converstion table to convert sweetness, acidity, tannin, intensity describing word into an integer.
|
||||||
}
|
- Do not generate other comments.
|
||||||
}
|
|
||||||
|
You should then respond to the user with:
|
||||||
|
- wine_type: Can be one of: red, white, sparkling, rose, dessert or fortified
|
||||||
|
- budget: ...
|
||||||
|
- occasion: ...
|
||||||
|
- food_pairing: food that will be served with wine
|
||||||
|
- country: wine's country of origin
|
||||||
|
- grape_variety: ...
|
||||||
|
- sweetness: S where S is an integer of sweetness level
|
||||||
|
- acidity: A where A is an integer of acidity level
|
||||||
|
- tannin: T where T is an integer of tannin level
|
||||||
|
- intensity: I where I is an integer of intensity level
|
||||||
|
|
||||||
|
You should only respond in format as described below:
|
||||||
|
wine_type: ...
|
||||||
|
budget: ...
|
||||||
|
occasion: ...
|
||||||
|
food_pairing: ...
|
||||||
|
country: ...
|
||||||
|
grape_variety: ...
|
||||||
|
sweetness:
|
||||||
|
acidity: ...
|
||||||
|
tannin: ...
|
||||||
|
intensity: ...
|
||||||
|
|
||||||
Here are some examples:
|
Here are some examples:
|
||||||
|
|
||||||
user: "price < 25, full-bodied white wine with sweetness level 2, low tannin level and medium acidity level, Pizza"
|
user: "price < 25, full-bodied white wine with sweetness level 2, low tannin level and medium acidity level, Pizza, France, Riesling"
|
||||||
assistant:
|
assistant:
|
||||||
{
|
wine_type: white, budget: less than 25, food_pairing: Pizza, country: France, grape_variety: Riesling, sweetness: 2, acidity: 3, tannin: 1, intensity: 5
|
||||||
"attributes":
|
|
||||||
{
|
|
||||||
"wine_type": "white"
|
|
||||||
"budget": less than 25",
|
|
||||||
"food_pairing": "Pizza",
|
|
||||||
"sweetness": 2,
|
|
||||||
"acidity": 3,
|
|
||||||
"tannin": 1,
|
|
||||||
"intensity": 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
user: body=full-bodied, off dry, acidity=medium, intensity=intense
|
user: body=full-bodied, dry, acidity=medium and low tannin
|
||||||
assistant:
|
assistant:
|
||||||
{
|
wine_type: NA, budget: NA, food_pairing: NA, country: NA, grape_variety: NA, sweetness: 1, acidity: 3, tannin: 1, intensity: 5
|
||||||
"attributes":
|
|
||||||
{
|
|
||||||
"sweetness": 2,
|
|
||||||
"acidity": 3,
|
|
||||||
"tannin": "not specified",
|
|
||||||
"intensity": 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Let's begin!
|
Let's begin!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
missinginfo = "None"
|
||||||
|
|
||||||
usermsg =
|
usermsg =
|
||||||
"""
|
"""
|
||||||
$input
|
Conversion Table: $converstiontable
|
||||||
|
Query: $input
|
||||||
|
Last round missing info: $missinginfo
|
||||||
"""
|
"""
|
||||||
|
|
||||||
chathistory =
|
_prompt =
|
||||||
[
|
[
|
||||||
Dict(:name=> "system", :text=> systemmsg),
|
Dict(:name=> "system", :text=> systemmsg),
|
||||||
Dict(:name=> "user", :text=> usermsg)
|
Dict(:name=> "user", :text=> usermsg)
|
||||||
]
|
]
|
||||||
|
|
||||||
# put in model format
|
# put in model format
|
||||||
prompt = formatLLMtext(chathistory, "llama3instruct")
|
prompt = GeneralUtils.formatLLMtext(_prompt, "llama3instruct")
|
||||||
prompt *=
|
prompt *=
|
||||||
"""
|
"""
|
||||||
<|start_header_id|>assistant<|end_header_id|>
|
<|start_header_id|>assistant<|end_header_id|>
|
||||||
{
|
"""
|
||||||
"""
|
|
||||||
|
|
||||||
pprint(prompt)
|
attributes = ["wine_type", "budget", "occasion", "food_pairing", "country", "grape_variety", "sweetness", "acidity", "tannin", "intensity"]
|
||||||
externalService = config[:externalservice][:text2textinstruct]
|
|
||||||
|
|
||||||
# send formatted input to user using GeneralUtils.sendReceiveMqttMsg
|
|
||||||
msgMeta = GeneralUtils.generate_msgMeta(
|
|
||||||
externalService[:mqtttopic],
|
|
||||||
senderName= "wineattributes_wordToNumber",
|
|
||||||
senderId= string(uuid4()),
|
|
||||||
receiverName= "text2textinstruct",
|
|
||||||
mqttBroker= config[:mqttServerInfo][:broker],
|
|
||||||
mqttBrokerPort= config[:mqttServerInfo][:port],
|
|
||||||
)
|
|
||||||
|
|
||||||
outgoingMsg = Dict(
|
|
||||||
:msgMeta=> msgMeta,
|
|
||||||
:payload=> Dict(
|
|
||||||
:text=> prompt,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
for attempt in 1:5
|
for attempt in 1:5
|
||||||
try
|
try
|
||||||
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=120)
|
response = a.text2textInstructLLM(prompt)
|
||||||
_responseJsonStr = response[:response][:text]
|
responsedict = GeneralUtils.textToDict(response, attributes, rightmarker=":", symbolkey=true)
|
||||||
expectedJsonExample =
|
|
||||||
"""
|
for i ∈ attributes
|
||||||
Here is an expected JSON format:
|
if length(JSON3.write(responsedict[i])) == 0
|
||||||
{
|
error("$i is empty ", @__LINE__)
|
||||||
"attributes":
|
end
|
||||||
{
|
end
|
||||||
"...": "...",
|
|
||||||
"...": "...",
|
# check if there are more than 1 key per categories
|
||||||
}
|
for i ∈ attributes
|
||||||
}
|
matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i)
|
||||||
"""
|
if length(matchkeys) > 1
|
||||||
responseJsonStr = jsoncorrection(config, _responseJsonStr, expectedJsonExample)
|
error("generatechat has more than one key per categories")
|
||||||
_responseDict = copy(JSON3.read(responseJsonStr))
|
end
|
||||||
responseDict = _responseDict[:attributes]
|
end
|
||||||
|
|
||||||
return responseDict
|
result = responsedict[:chat]
|
||||||
|
|
||||||
|
return result
|
||||||
catch e
|
catch e
|
||||||
io = IOBuffer()
|
io = IOBuffer()
|
||||||
showerror(io, e)
|
showerror(io, e)
|
||||||
errorMsg = String(take!(io))
|
errorMsg = String(take!(io))
|
||||||
st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
|
st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
|
||||||
println("")
|
println("")
|
||||||
@warn "Attempt $attempt. Error occurred: $errorMsg\n$st"
|
println("Attempt $attempt. Error occurred: $errorMsg\n$st")
|
||||||
println("")
|
println("")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -97,10 +97,12 @@ mutable struct sommelier <: agent
|
|||||||
|
|
||||||
# communication function
|
# communication function
|
||||||
text2textInstructLLM::Function
|
text2textInstructLLM::Function
|
||||||
|
executeSQL::Function
|
||||||
end
|
end
|
||||||
|
|
||||||
function sommelier(
|
function sommelier(
|
||||||
text2textInstructLLM::Function
|
text2textInstructLLM::Function,
|
||||||
|
executeSQL::Function
|
||||||
;
|
;
|
||||||
name::String= "Assistant",
|
name::String= "Assistant",
|
||||||
id::String= string(uuid4()),
|
id::String= string(uuid4()),
|
||||||
@@ -140,6 +142,7 @@ function sommelier(
|
|||||||
chathistory,
|
chathistory,
|
||||||
memory,
|
memory,
|
||||||
text2textInstructLLM,
|
text2textInstructLLM,
|
||||||
|
executeSQL
|
||||||
)
|
)
|
||||||
|
|
||||||
return newAgent
|
return newAgent
|
||||||
|
|||||||
@@ -27,6 +27,13 @@ msgMeta = GeneralUtils.generate_msgMeta(
|
|||||||
# :externalservice=> config[:externalservice],
|
# :externalservice=> config[:externalservice],
|
||||||
# )
|
# )
|
||||||
|
|
||||||
|
function executeSQL(sql::T) where {T<:AbstractString}
|
||||||
|
DBconnection = LibPQ.Connection("host=192.168.88.12 port=5432 dbname=yiem_wine_assistant user=yiem password=yiem@Postgres_0.0")
|
||||||
|
result = LibPQ.execute(DBconnection, sql)
|
||||||
|
close(DBconnection)
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
function text2textInstructLLM(prompt::String)
|
function text2textInstructLLM(prompt::String)
|
||||||
msgMeta = GeneralUtils.generate_msgMeta(
|
msgMeta = GeneralUtils.generate_msgMeta(
|
||||||
config[:externalservice][:text2textinstruct][:mqtttopic],
|
config[:externalservice][:text2textinstruct][:mqtttopic],
|
||||||
@@ -58,6 +65,7 @@ end
|
|||||||
# Instantiate an agent
|
# Instantiate an agent
|
||||||
a = YiemAgent.sommelier(
|
a = YiemAgent.sommelier(
|
||||||
text2textInstructLLM,
|
text2textInstructLLM,
|
||||||
|
executeSQL;
|
||||||
name="assistant",
|
name="assistant",
|
||||||
id="testingSessionID", # agent instance id
|
id="testingSessionID", # agent instance id
|
||||||
)
|
)
|
||||||
@@ -71,27 +79,33 @@ end
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
function main()
|
# function main()
|
||||||
userinput = "Hello, I would like a get a bottle of wine."
|
# userinput = "Hello, I would like a get a bottle of wine."
|
||||||
for i in 1:10
|
# for i in 1:10
|
||||||
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("")
|
# println("")
|
||||||
println("--> user input:")
|
# println("--> user input:")
|
||||||
userinput = readline()
|
# userinput = readline()
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
|
|
||||||
main()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# main()
|
||||||
|
|
||||||
|
|
||||||
|
input = "query=\"off dry, medium tannin French Rosé and spicy Thai food pairing under 30 dollars\""
|
||||||
|
YiemAgent.winestock(a, input)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
I'm having a graduation party this evening. I'll pay at most 30 bucks.
|
||||||
|
I have no idea. The party will be formal. What type of wine people usually get for this occasion?
|
||||||
|
What about sparkling Rose?
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user