update
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
module llmfunction
|
module llmfunction
|
||||||
|
|
||||||
export virtualWineUserChatbox, jsoncorrection, checkinventory,
|
export virtualWineUserChatbox, jsoncorrection, checkinventory,
|
||||||
virtualWineUserRecommendbox, userChatbox, userRecommendbox, extractWineAttributes
|
virtualWineUserRecommendbox, userChatbox, userRecommendbox
|
||||||
|
|
||||||
using HTTP, JSON3, URIs, Random, PrettyPrinting, UUIDs
|
using HTTP, JSON3, URIs, Random, PrettyPrinting, UUIDs
|
||||||
using GeneralUtils, SQLLLM
|
using GeneralUtils, SQLLLM
|
||||||
@@ -365,12 +365,12 @@ julia> result = checkinventory(agent, input)
|
|||||||
function checkinventory(a::T1, input::T2
|
function checkinventory(a::T1, input::T2
|
||||||
)::Union{Tuple{String, Number, Number, Bool}, Tuple{String, Nothing, Number, Bool}} where {T1<:agent, T2<:AbstractString}
|
)::Union{Tuple{String, Number, Number, Bool}, Tuple{String, Nothing, Number, Bool}} where {T1<:agent, T2<:AbstractString}
|
||||||
|
|
||||||
wineattributes = extractWineAttributes(a, input)
|
wineattributes_1 = extractWineAttributes_1(a, input)
|
||||||
|
wineattributes_2 = extractWineAttributes_2(a, input)
|
||||||
|
|
||||||
# replace because SQLLLM didn't know what food_paired means
|
inventoryquery = "$wineattributes_1, $wineattributes_2"
|
||||||
result = replace(wineattributes, "food_pairing" => "food_to_be_paired_with_wine")
|
|
||||||
|
|
||||||
result = SQLLLM.query(result, a.executeSQL, a.text2textInstructLLM)
|
result = SQLLLM.query(inventoryquery, a.executeSQL, a.text2textInstructLLM)
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -394,145 +394,51 @@ julia>
|
|||||||
|
|
||||||
# Signature
|
# Signature
|
||||||
"""
|
"""
|
||||||
function extractWineAttributes(a::T1, input::T2
|
function extractWineAttributes_1(a::T1, input::T2
|
||||||
)::String where {T1<:agent, T2<:AbstractString}
|
)::String 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 task is to determine the user's wine preferred levels of sweetness, intensity, tannin, acidity and other criteria based on the user query.
|
|
||||||
|
|
||||||
# At each round of conversation, the user will give you the current situation:
|
|
||||||
# Conversion Table: ...
|
|
||||||
# Query: ...
|
|
||||||
|
|
||||||
# You must follow the following guidelines:
|
|
||||||
# 1) If specific information is not available in the query, use "NA" to indicate this. Also words like "any" or "unlimited" means no information available.
|
|
||||||
# 2) Use converstion table to convert sweetness, acidity, tannin, intensity describing word into an integer.
|
|
||||||
# 3) Do not generate other comments.
|
|
||||||
|
|
||||||
# You should then respond to the user with the following points:
|
|
||||||
# - wine_type: Can be one of: red, white, sparkling, rose, dessert or fortified
|
|
||||||
# - price: ...
|
|
||||||
# - occasion: ...
|
|
||||||
# - food_pairing: food that will be served with wine
|
|
||||||
# - country: wine's country of origin
|
|
||||||
# - grape_variety: ...
|
|
||||||
# - tasting_notes: wine's flavors
|
|
||||||
# - sweetness: S where S is an integer indicating sweetness level
|
|
||||||
# - acidity: A where A is an integer indicating acidity level
|
|
||||||
# - tannin: T where T is an integer indicating tannin level
|
|
||||||
# - intensity: I where I is an integer indicating intensity level
|
|
||||||
|
|
||||||
# You should only respond in format as described below:
|
|
||||||
# repeat: repeat the user's query
|
|
||||||
# wine_type: ...
|
|
||||||
# price: ...
|
|
||||||
# occasion: ...
|
|
||||||
# food_pairing: ...
|
|
||||||
# country: ...
|
|
||||||
# grape_variety: ...
|
|
||||||
# tasting_notes: ...
|
|
||||||
# sweetness:
|
|
||||||
# acidity: ...
|
|
||||||
# tannin: ...
|
|
||||||
# intensity: ...
|
|
||||||
|
|
||||||
# Here are some examples:
|
|
||||||
|
|
||||||
# user: "price < 25, for wedding party, full-bodied white wine with sweetness level 2, apple and honey notes, low tannin level and medium acidity level, Pizza, France, Riesling"
|
|
||||||
# assistant: repeat: ... \n wine_type: white\n price: less than 25\n occasion: wedding party\n food_pairing: Pizza\n country: France\n grape_variety: Riesling\n tasting_notes: apple, honey\n sweetness: 2\n acidity: 3\n tannin: 1\n intensity: 5
|
|
||||||
|
|
||||||
# user: body=full-bodied, dry, acidity=medium and low tannin
|
|
||||||
# assistant: repeat: ... \n wine_type: NA\n price: NA\n occasion: NA\n food_pairing: NA\n country: NA\n grape_variety: NA\n tasting_notes: NA\n sweetness: 1\n acidity: 3\n tannin: 1\n intensity: 5
|
|
||||||
|
|
||||||
# Let's begin!
|
|
||||||
# """
|
|
||||||
|
|
||||||
systemmsg =
|
systemmsg =
|
||||||
"""
|
"""
|
||||||
As an attentive sommelier, your task is to fillout the form based on the user query. The form is about the user's wine preference.
|
As an helpful sommelier, your task is to fill the user's preference form based on the user query.
|
||||||
|
|
||||||
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:
|
||||||
Conversion Table: ...
|
User query: ...
|
||||||
Query: ...
|
|
||||||
|
The preference form requires the following information:
|
||||||
|
wine_type, price, occasion, food_to_be_paired_with_wine, country, grape_variety, wine_notes.
|
||||||
|
|
||||||
You must follow the following guidelines:
|
You must follow the following guidelines:
|
||||||
1) Use the conversion table to convert the descriptive word level of sweetness, intensity, tannin, and acidity into a corresponding integer.
|
1) If specific information required in the preference form is not available in the query, use 'NA' to indicate this.
|
||||||
2) If specific information required in the form is not available in the query, use 'NA' to indicate this. Additionally, words like 'any' or 'unlimited' mean no information is available.
|
Additionally, words like 'any' or 'unlimited' mean no information is available.
|
||||||
|
2) Use the conversion table to convert the descriptive word level of sweetness, intensity, tannin, and acidity into a corresponding integer.
|
||||||
3) Do not generate other comments.
|
3) Do not generate other comments.
|
||||||
|
|
||||||
You should then respond to the user with the following points:
|
You should then respond to the user with the following points:
|
||||||
1) reasoning: State your reasoning about the current situation.
|
- wine_type: Can be one of: red, white, sparkling, rose, dessert or fortified
|
||||||
2) wine_type: Can be one of: red, white, sparkling, rose, dessert or fortified
|
- price: ...
|
||||||
3) price: ...
|
- occasion: ...
|
||||||
4) occasion: ...
|
- food_to_be_paired_with_wine: food that will be served with wine
|
||||||
5) food_to_be_paired_with_wine: food that will be served with wine
|
- country: wine's country of origin
|
||||||
6) country: wine's country of origin
|
- grape_variety: ...
|
||||||
7) grape_variety: ...
|
- wine_notes:
|
||||||
8) tasting_notes: wine's flavors
|
Bad example (these words are not wine notes - descriptive words): dry, sour, full bodied, etc.
|
||||||
9) sweetness: S where S is an integer indicating sweetness level
|
Good example (these words are wine notes - descriptive words): floral, citrus, earthy, fruity, tropical, nutty, etc.
|
||||||
10) acidity: A where A is an integer indicating acidity level
|
|
||||||
11) tannin: T where T is an integer indicating tannin level
|
|
||||||
12) intensity: I where I is an integer indicating intensity level
|
|
||||||
|
|
||||||
You should only respond in format as described below:
|
You should only respond in the form as described below:
|
||||||
reasoning: ...
|
|
||||||
wine_type: ...
|
wine_type: ...
|
||||||
price: ...
|
price: ...
|
||||||
occasion: ...
|
occasion: ...
|
||||||
food_to_be_paired_with_wine: ...
|
food_to_be_paired_with_wine: ...
|
||||||
country: ...
|
country: ...
|
||||||
grape_variety: ...
|
grape_variety: ...
|
||||||
tasting_notes: ...
|
wine_notes: ...
|
||||||
sweetness:
|
|
||||||
acidity: ...
|
|
||||||
tannin: ...
|
|
||||||
intensity: ...
|
|
||||||
|
|
||||||
Here are some examples:
|
|
||||||
|
|
||||||
user: price < 25, for wedding party, full-bodied white wine with sweetness level 2, apple and honey notes, low tannin level and medium acidity level, Pizza, France, Riesling
|
|
||||||
assistant: reasoning: ... \n wine_type: white\n price: less than 25\n occasion: wedding party\n food_to_be_paired_with_wine: Pizza\n country: France\n grape_variety: Riesling\n tasting_notes: apple, honey\n sweetness: 2\n acidity: 3\n tannin: 1\n intensity: 5
|
|
||||||
|
|
||||||
user: body=full-bodied, dry, acidity=medium and low tannin
|
|
||||||
assistant: reasoning: ... \n wine_type: NA\n price: NA\n occasion: NA\n food_to_be_paired_with_wine: NA\n country: NA\n grape_variety: NA\n tasting_notes: NA\n sweetness: 1\n acidity: 3\n tannin: 1\n intensity: 5
|
|
||||||
|
|
||||||
Let's begin!
|
Let's begin!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
usermsg =
|
usermsg =
|
||||||
"""
|
"""
|
||||||
Conversion Table: $converstiontable
|
User query: $input
|
||||||
Query: $input
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_prompt =
|
_prompt =
|
||||||
@@ -548,7 +454,7 @@ function extractWineAttributes(a::T1, input::T2
|
|||||||
<|start_header_id|>assistant<|end_header_id|>
|
<|start_header_id|>assistant<|end_header_id|>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
attributes = ["reasoning", "wine_type", "price", "occasion", "food_to_be_paired_with_wine", "country", "grape_variety", "sweetness", "acidity", "tannin", "intensity"]
|
attributes = ["wine_type", "price", "occasion", "food_to_be_paired_with_wine", "country", "grape_variety"]
|
||||||
|
|
||||||
for attempt in 1:5
|
for attempt in 1:5
|
||||||
try
|
try
|
||||||
@@ -561,17 +467,9 @@ function extractWineAttributes(a::T1, input::T2
|
|||||||
end
|
end
|
||||||
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 = ""
|
result = ""
|
||||||
for (k, v) in responsedict
|
for (k, v) in responsedict
|
||||||
if k != :reasoning && !occursin("NA", v)
|
if !occursin("NA", v)
|
||||||
result *= "$k: $v, "
|
result *= "$k: $v, "
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -592,6 +490,215 @@ function extractWineAttributes(a::T1, input::T2
|
|||||||
error("wineattributes_wordToNumber() failed to get a response")
|
error("wineattributes_wordToNumber() failed to get a response")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function extractWineAttributes_2(a::T1, input::T2
|
||||||
|
)::String where {T1<:agent, T2<:AbstractString}
|
||||||
|
|
||||||
|
converstiontable =
|
||||||
|
"""
|
||||||
|
Conversion Table:
|
||||||
|
Intensity level:
|
||||||
|
1: May correspond to "light-bodied" or a similar description.
|
||||||
|
2: May correspond to "med light", "medium light" or a similar description.
|
||||||
|
3: May correspond to "medium" or a similar description.
|
||||||
|
4: May correspond to "med full", "medium full" or a similar description.
|
||||||
|
5: May correspond to "full" or a similar description.
|
||||||
|
Sweetness level:
|
||||||
|
1: May correspond to "dry", "no sweet" or a similar description.
|
||||||
|
2: May correspond to "off dry", "less sweet" or a similar description.
|
||||||
|
3: May correspond to "semi sweet" or a similar description.
|
||||||
|
4: May correspond to "sweet" or a similar description.
|
||||||
|
5: May correspond to "very sweet" or a similar description.
|
||||||
|
Tannin level:
|
||||||
|
1: May correspond to "low tannin" or a similar description.
|
||||||
|
2: May correspond to "semi low tannin" or a similar description.
|
||||||
|
3: May correspond to "medium tannin" or a similar description.
|
||||||
|
4: May correspond to "semi high tannin" or a similar description.
|
||||||
|
5: May correspond to "high tannin" or a similar description.
|
||||||
|
Acidity level:
|
||||||
|
1: May correspond to "low acidity" or a similar description.
|
||||||
|
2: May correspond to "semi low acidity" or a similar description.
|
||||||
|
3: May correspond to "medium acidity" or a similar description.
|
||||||
|
4: May correspond to "semi high acidity" or a similar description.
|
||||||
|
5: May correspond to "high acidity" or a similar description.
|
||||||
|
"""
|
||||||
|
|
||||||
|
systemmsg =
|
||||||
|
"""
|
||||||
|
As an helpful sommelier, your task is to fill the user's preference form based on the user query.
|
||||||
|
|
||||||
|
At each round of conversation, the user will give you the current situation:
|
||||||
|
Conversion Table: ...
|
||||||
|
User query: ...
|
||||||
|
|
||||||
|
The preference form requires the following information:
|
||||||
|
sweetness, acidity, tannin, intensity
|
||||||
|
|
||||||
|
You must follow the following guidelines:
|
||||||
|
1) If specific information required in the preference form is not available in the query, use 'NA' to indicate this.
|
||||||
|
Additionally, words like 'any' or 'unlimited' mean no information is available.
|
||||||
|
2) Use the conversion table to convert the descriptive word level of sweetness, intensity, tannin, and acidity into a corresponding integer.
|
||||||
|
3) Do not generate other comments.
|
||||||
|
|
||||||
|
You should then respond to the user with the following points:
|
||||||
|
- sweetness: S where S is an integer indicating sweetness level
|
||||||
|
- acidity: D where D is an integer indicating acidity level
|
||||||
|
- tannin: T where T is an integer indicating tannin level
|
||||||
|
- intensity: I where I is an integer indicating intensity level
|
||||||
|
|
||||||
|
You should only respond in the form as described below:
|
||||||
|
sweetness: ...
|
||||||
|
acidity: ...
|
||||||
|
tannin: ...
|
||||||
|
intensity: ...
|
||||||
|
|
||||||
|
Let's begin!
|
||||||
|
"""
|
||||||
|
|
||||||
|
usermsg =
|
||||||
|
"""
|
||||||
|
Conversion Table: $converstiontable
|
||||||
|
User query: $input
|
||||||
|
"""
|
||||||
|
|
||||||
|
_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|>
|
||||||
|
"""
|
||||||
|
|
||||||
|
attributes = ["sweetness", "acidity", "tannin", "intensity"]
|
||||||
|
|
||||||
|
for attempt in 1:5
|
||||||
|
try
|
||||||
|
response = a.text2textInstructLLM(prompt)
|
||||||
|
responsedict = GeneralUtils.textToDict(response, attributes, rightmarker=":", symbolkey=true)
|
||||||
|
|
||||||
|
for i ∈ attributes
|
||||||
|
if length(JSON3.write(responsedict[Symbol(i)])) == 0
|
||||||
|
error("$i is empty ", @__LINE__)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
for (k, v) in responsedict
|
||||||
|
if !occursin("NA", v)
|
||||||
|
result *= "$k: $v, "
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result = result[1:end-2] # remove the ending ", "
|
||||||
|
|
||||||
|
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("wineattributes_wordToNumber() failed to get a response")
|
||||||
|
end
|
||||||
|
|
||||||
|
# function recheckWineAttributes(a::T1, input::T2
|
||||||
|
# )::String where {T1<:agent, T2<:AbstractString}
|
||||||
|
|
||||||
|
# systemmsg =
|
||||||
|
# """
|
||||||
|
# As an helpful sommelier, your task is to check what preferences the user mentioned in the query.
|
||||||
|
|
||||||
|
# At each round of conversation, the user will give you the current situation:
|
||||||
|
# User query: ...
|
||||||
|
|
||||||
|
# The preferences are:
|
||||||
|
# - wine type: red, white, sparkling, rose, dessert, fortified
|
||||||
|
# - price
|
||||||
|
# - occasion
|
||||||
|
# - food to be paired with wine
|
||||||
|
# - country
|
||||||
|
# - grape_variety
|
||||||
|
# - tasting_notes
|
||||||
|
# - sweetness
|
||||||
|
# - acidity
|
||||||
|
# - tannin
|
||||||
|
# - intensity: wine body e.g. full bodied, light bodied
|
||||||
|
|
||||||
|
# You should then respond to the user with the following points:
|
||||||
|
# 1) mentioned_preferences: list all the preferences in the user's query.
|
||||||
|
# Good example: mentioned_preferences: "price", "country", "wine type"
|
||||||
|
|
||||||
|
# You should only respond in the form as described below:
|
||||||
|
# mentioned_preferences: ...
|
||||||
|
|
||||||
|
# Let's begin!
|
||||||
|
# """
|
||||||
|
|
||||||
|
# usermsg =
|
||||||
|
# """
|
||||||
|
# User query: $input
|
||||||
|
# """
|
||||||
|
|
||||||
|
# _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|>
|
||||||
|
# """
|
||||||
|
|
||||||
|
# attributes = ["mentioned_preferences"]
|
||||||
|
|
||||||
|
# for attempt in 1:5
|
||||||
|
# try
|
||||||
|
# response = a.text2textInstructLLM(prompt)
|
||||||
|
# responsedict = GeneralUtils.textToDict(response, attributes, rightmarker=":", symbolkey=true)
|
||||||
|
|
||||||
|
# for i ∈ attributes
|
||||||
|
# if length(JSON3.write(responsedict[Symbol(i)])) == 0
|
||||||
|
# error("$i is empty ", @__LINE__)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# result = ""
|
||||||
|
# for (k, v) in responsedict
|
||||||
|
# if !occursin("NA", v)
|
||||||
|
# result *= "$k: $v, "
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# result = result[1:end-2] # remove the ending ", "
|
||||||
|
|
||||||
|
# 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("wineattributes_wordToNumber() failed to get a response")
|
||||||
|
# end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
""" Attemp to correct LLM response's incorrect JSON response.
|
""" Attemp to correct LLM response's incorrect JSON response.
|
||||||
|
|
||||||
# Arguments
|
# Arguments
|
||||||
|
|||||||
@@ -120,6 +120,14 @@ end
|
|||||||
|
|
||||||
|
|
||||||
input = "query=\"medium-bodied dry white wine\""
|
input = "query=\"medium-bodied dry white wine\""
|
||||||
|
# input = "the customer is looking for a medium-bodied, dry white wine."
|
||||||
|
result = YiemAgent.checkinventory(a, input)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
result = YiemAgent.extractWineAttributes(a, input)
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user