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 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 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:
|
||||
- Don't mention any specific wine until you've checked your inventory.
|
||||
@@ -1024,6 +1024,8 @@ end
|
||||
# end
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# Arguments
|
||||
@@ -1052,8 +1054,7 @@ function think(a::T) where {T<:agent}
|
||||
if actionname == "CHATBOX"
|
||||
(result=actioninput, errormsg=nothing, success=true)
|
||||
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(actioninput, DBconnection, a.text2textInstructLLM)
|
||||
winestock(a, actioninput)
|
||||
else
|
||||
error("undefined LLM function. Requesting $actionname")
|
||||
end
|
||||
@@ -1097,7 +1098,7 @@ julia>
|
||||
function generatechat(a::T) where {T<:agent}
|
||||
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.
|
||||
|
||||
At each round of conversation, the user will give you the current situation:
|
||||
|
||||
@@ -362,15 +362,14 @@ julia> result = winestock(agent, input)
|
||||
|
||||
# Signature
|
||||
"""
|
||||
function winestock(input::T, DBconnection, text2textInstructLLM::Function
|
||||
)::Union{Tuple{String, Number, Number, Bool}, Tuple{String, Nothing, Number, Bool}} where {T<:AbstractString}
|
||||
function winestock(a::T1, input::T2
|
||||
)::Union{Tuple{String, Number, Number, Bool}, Tuple{String, Nothing, Number, Bool}} where {T1<:agent, T2<:AbstractString}
|
||||
|
||||
# SELECT *
|
||||
# FROM food
|
||||
# WHERE 'China' = ANY(food_name)
|
||||
# OR 'India' = ANY(food_name);
|
||||
|
||||
|
||||
wineattributes = wineattributes_wordToNumber(a, input)
|
||||
|
||||
systemmsg =
|
||||
@@ -559,149 +558,162 @@ function winestock(input::T, DBconnection, text2textInstructLLM::Function
|
||||
# return result, nothing, 0, false
|
||||
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 =
|
||||
"""
|
||||
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.
|
||||
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.
|
||||
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 should only respond in JSON format as describe below:
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"sweetness": "sweetness level",
|
||||
"acidity": "acidity level",
|
||||
"tannin": "tannin level",
|
||||
"intensity": "intensity level"
|
||||
}
|
||||
}
|
||||
At each round of conversation, the user will give you the current situation:
|
||||
Conversion Table: ...
|
||||
Query: ...
|
||||
Last round missing info: some info are missing
|
||||
|
||||
You should follow the following guidelines:
|
||||
- If specific information is unavailable, please use "NA" to indicate this.
|
||||
- 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:
|
||||
|
||||
user: "price < 25, full-bodied white wine with sweetness level 2, low tannin level and medium acidity level, Pizza"
|
||||
assistant:
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"wine_type": "white"
|
||||
"budget": less than 25",
|
||||
"food_pairing": "Pizza",
|
||||
"sweetness": 2,
|
||||
"acidity": 3,
|
||||
"tannin": 1,
|
||||
"intensity": 5
|
||||
}
|
||||
}
|
||||
user: "price < 25, full-bodied white wine with sweetness level 2, low tannin level and medium acidity level, Pizza, France, Riesling"
|
||||
assistant:
|
||||
wine_type: white, budget: less than 25, food_pairing: Pizza, country: France, grape_variety: Riesling, 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:
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"sweetness": 2,
|
||||
"acidity": 3,
|
||||
"tannin": "not specified",
|
||||
"intensity": 5
|
||||
}
|
||||
}
|
||||
wine_type: NA, budget: NA, food_pairing: NA, country: NA, grape_variety: NA, sweetness: 1, acidity: 3, tannin: 1, intensity: 5
|
||||
|
||||
Let's begin!
|
||||
"""
|
||||
|
||||
missinginfo = "None"
|
||||
|
||||
usermsg =
|
||||
"""
|
||||
$input
|
||||
Conversion Table: $converstiontable
|
||||
Query: $input
|
||||
Last round missing info: $missinginfo
|
||||
"""
|
||||
|
||||
chathistory =
|
||||
_prompt =
|
||||
[
|
||||
Dict(:name=> "system", :text=> systemmsg),
|
||||
Dict(:name=> "user", :text=> usermsg)
|
||||
]
|
||||
|
||||
|
||||
# put in model format
|
||||
prompt = formatLLMtext(chathistory, "llama3instruct")
|
||||
prompt = GeneralUtils.formatLLMtext(_prompt, "llama3instruct")
|
||||
prompt *=
|
||||
"""
|
||||
<|start_header_id|>assistant<|end_header_id|>
|
||||
{
|
||||
"""
|
||||
"""
|
||||
<|start_header_id|>assistant<|end_header_id|>
|
||||
"""
|
||||
|
||||
pprint(prompt)
|
||||
externalService = config[:externalservice][:text2textinstruct]
|
||||
attributes = ["wine_type", "budget", "occasion", "food_pairing", "country", "grape_variety", "sweetness", "acidity", "tannin", "intensity"]
|
||||
|
||||
# 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
|
||||
try
|
||||
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=120)
|
||||
_responseJsonStr = response[:response][:text]
|
||||
expectedJsonExample =
|
||||
"""
|
||||
Here is an expected JSON format:
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"...": "...",
|
||||
"...": "...",
|
||||
}
|
||||
}
|
||||
"""
|
||||
responseJsonStr = jsoncorrection(config, _responseJsonStr, expectedJsonExample)
|
||||
_responseDict = copy(JSON3.read(responseJsonStr))
|
||||
responseDict = _responseDict[:attributes]
|
||||
|
||||
return responseDict
|
||||
response = a.text2textInstructLLM(prompt)
|
||||
responsedict = GeneralUtils.textToDict(response, attributes, rightmarker=":", symbolkey=true)
|
||||
|
||||
for i ∈ attributes
|
||||
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 ∈ attributes
|
||||
matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i)
|
||||
if length(matchkeys) > 1
|
||||
error("generatechat has more than one key per categories")
|
||||
end
|
||||
end
|
||||
|
||||
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("")
|
||||
@warn "Attempt $attempt. Error occurred: $errorMsg\n$st"
|
||||
println("Attempt $attempt. Error occurred: $errorMsg\n$st")
|
||||
println("")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -97,10 +97,12 @@ mutable struct sommelier <: agent
|
||||
|
||||
# communication function
|
||||
text2textInstructLLM::Function
|
||||
executeSQL::Function
|
||||
end
|
||||
|
||||
function sommelier(
|
||||
text2textInstructLLM::Function
|
||||
text2textInstructLLM::Function,
|
||||
executeSQL::Function
|
||||
;
|
||||
name::String= "Assistant",
|
||||
id::String= string(uuid4()),
|
||||
@@ -140,6 +142,7 @@ function sommelier(
|
||||
chathistory,
|
||||
memory,
|
||||
text2textInstructLLM,
|
||||
executeSQL
|
||||
)
|
||||
|
||||
return newAgent
|
||||
|
||||
Reference in New Issue
Block a user