This commit is contained in:
narawat lamaiin
2025-01-10 08:06:01 +07:00
parent 022cb5caf0
commit 616c159336
3 changed files with 130 additions and 261 deletions

View File

@@ -330,183 +330,6 @@ julia>
# Signature
"""
# function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<:AbstractString}
# systemmsg =
# """
# As a helpful sommelier, your task is to extract the user information from the user's query as much as possible to fill out user's preference form.
# At each round of conversation, the user will give you the current situation:
# User's query: ...
# You must follow the following guidelines:
# 1) If specific information required in the preference form is not available in the query or there isn't any, mark with "NA" to indicate this.
# Additionally, words like 'any' or 'unlimited' mean no information is available.
# 2) Do not generate other comments.
# You should then respond to the user with the following points:
# - reasoning: state your understanding of the current situation
# - wine_name: name of the wine
# - winery: name of the winery
# - vintage: the year of the wine
# - 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. Can be "Austria", "Australia", "France", "Germany", "Italy", "Portugal", "Spain", "United States"
# - wine_type: can be one of: "red", "white", "sparkling", "rose", "dessert" or "fortified"
# - grape_variety: the name of the primary grape used to make the wine
# - tasting_notes: a brief description of the wine's taste, such as "butter", "oak", "fruity", etc
# - wine_price: price of wine. For example, up to 100, less than 100, 20 to 100, 30-79.95
# - occasion: the occasion the user is having the wine for
# - food_to_be_paired_with_wine: food that the user will be served with the wine such as poultry, fish, steak, etc
# You should only respond in the user's preference form as described below:
# reasoning: ...
# winery: ...
# wine_name: ...
# vintage: ...
# region: ...
# country: ...
# wine_type: ...
# grape_variety: ...
# tasting_notes: ...
# wine_price: ...
# occasion: ...
# food_to_be_paired_with_wine: ...
# Here are some example:
# User's query: red, Chenin Blanc, Riesling, under 20
# reasoning: ...
# winery: NA
# wine_name: NA
# vintage: NA
# region: NA
# country: NA
# wine_type: red
# grape_variety: Chenin Blanc, Riesling
# tasting_notes: NA
# wine_price: under 20
# occasion: NA
# food_to_be_paired_with_wine: NA
# User's query: Domaine du Collier Saumur Blanc 2019, France, white, Chenin Blanc
# reasoning: ...
# winery: Domaine du Collier
# wine_name: Saumur Blanc
# vintage: 2019
# region: Saumur
# country: France
# wine_type: white
# grape_variety: Chenin Blanc
# tasting_notes: NA
# wine_price: 109
# occasion: NA
# food_to_be_paired_with_wine: NA
# Let's begin!
# """
# attributes = ["reasoning", "winery", "wine_name", "vintage", "region", "country", "wine_type", "grape_variety", "tasting_notes", "wine_price", "occasion", "food_to_be_paired_with_wine"]
# errornote = ""
# maxattempt = 5
# for attempt in 1:maxattempt
# usermsg =
# """
# User's query: $input
# $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 = a.func[:text2textInstructLLM](prompt)
# response = GeneralUtils.remove_french_accents(response)
# # check wheter all attributes are in the response
# for word in attributes
# if !occursin(word, response)
# error("$word attribute is missing")
# end
# end
# 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
# #check if the following attributes has more than 1 name
# # responsedict[:grape_variety] = split(responsedict[:grape_variety], ',')[1]
# # responsedict[:grape_variety] = split(responsedict[:grape_variety], '/')[1]
# responsedict[:country] = split(responsedict[:country], ',')[1]
# responsedict[:country] = split(responsedict[:country], '/')[1]
# responsedict[:region] = split(responsedict[:region], ',')[1]
# responsedict[:region] = split(responsedict[:region], '/')[1]
# delete!(responsedict, :reasoning)
# delete!(responsedict, :tasting_notes)
# delete!(responsedict, :occasion)
# delete!(responsedict, :food_to_be_paired_with_wine)
# # check if winery, wine_name, region, country, wine_type, grape_variety are in the query because sometime AI halucinates
# for i in [:grape_variety, :winery, :wine_name, :region]
# result = check_key_in_input(input, responsedict, attempt, maxattempt, i)
# if result === nothing
# # nothing wrong
# elseif result == "NA"
# responsedict[i] = "NA"
# else
# errornote = result
# error(errornote)
# end
# end
# # remove (some text)
# for (k, v) in responsedict
# _v = replace(v, r"\(.*?\)" => "")
# responsedict[k] = _v
# end
# result = ""
# for (k, v) in responsedict
# # some time LLM generate text with "(some comment)". this line removes it
# if !occursin("NA", v) && v != "" && !occursin("none", v) && !occursin("None", v)
# result *= "$k: $v, "
# end
# end
# #[PENDING] remove halucination. "highend dry white wine" --> "wine_type: white, occasion: special occasion, food_to_be_paired_with_wine: seafood, fish, country: France, Italy, USA, grape_variety: Chardonnay, Sauvignon Blanc, Pinot Grigio\nwine_notes: citrus, green apple, floral"
# 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 ", @__FILE__, " ", @__LINE__)
# println("")
# end
# end
# error("wineattributes_wordToNumber() failed to get a response")
# end
function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<:AbstractString}
systemmsg =
@@ -529,27 +352,27 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
- region: a region (NOT a country) where the wine is produced, such as Burgundy, Napa Valley, etc
- country: a country where the wine is produced. Can be "Austria", "Australia", "France", "Germany", "Italy", "Portugal", "Spain", "United States"
- wine_type: can be one of: "red", "white", "sparkling", "rose", "dessert" or "fortified"
- grape_variety: the name of the primary grape used to make the wine
- grape_varietal: the name of the primary grape used to make the wine
- tasting_notes: a brief description of the wine's taste, such as "butter", "oak", "fruity", etc
- wine_price: price of wine. For example, up to 100, less than 100, 20 to 100, 30-79.95
- wine_price: price range of wine.
- occasion: the occasion the user is having the wine for
- food_to_be_paired_with_wine: food that the user will be served with the wine such as poultry, fish, steak, etc
You should only respond in the user's preference form (JSON) as described below:
{"reasoning": ..., "winery": ..., "wine_name": ..., "vintage": ..., "region": ..., "country": ..., "wine_type": ..., "grape_variety": ..., "tasting_notes": ..., "wine_price": ..., "occasion": ..., "food_to_be_paired_with_wine": ...}
{"reasoning": ..., "winery": ..., "wine_name": ..., "vintage": ..., "region": ..., "country": ..., "wine_type": ..., "grape_varietal": ..., "tasting_notes": ..., "wine_price": ..., "occasion": ..., "food_to_be_paired_with_wine": ...}
Here are some example:
User's query: red, Chenin Blanc, Riesling, under 20
{"reasoning": ..., "winery": "NA", "wine_name": "NA", "vintage": "NA", "region": "NA", "country": "NA", "wine_type": "red", "grape_variety": "Chenin Blanc, Riesling", "tasting_notes": "NA", "wine_price": "under 20", "occasion": "NA", "food_to_be_paired_with_wine": "NA"}
User's query: red, Chenin Blanc, Riesling, 20 USD
{"reasoning": ..., "winery": "NA", "wine_name": "NA", "vintage": "NA", "region": "NA", "country": "NA", "wine_type": "red", "grape_varietal": "Chenin Blanc, Riesling", "tasting_notes": "NA", "wine_price": "0-20", "occasion": "NA", "food_to_be_paired_with_wine": "NA"}
User's query: Domaine du Collier Saumur Blanc 2019, France, white, Chenin Blanc
{"reasoning": ..., "winery": "Domaine du Collier", "wine_name": "Saumur Blanc", "vintage": "2019", "region": "Saumur", "country": "France", "wine_type": "white", "grape_variety": "Chenin Blanc", "tasting_notes": "NA", "wine_price": "109", "occasion": "NA", "food_to_be_paired_with_wine": "NA"}
{"reasoning": ..., "winery": "Domaine du Collier", "wine_name": "Saumur Blanc", "vintage": "2019", "region": "Saumur", "country": "France", "wine_type": "white", "grape_varietal": "Chenin Blanc", "tasting_notes": "NA", "wine_price": "NA", "occasion": "NA", "food_to_be_paired_with_wine": "NA"}
Let's begin!
"""
attributes = ["reasoning", "winery", "wine_name", "vintage", "region", "country", "wine_type", "grape_variety", "tasting_notes", "wine_price", "occasion", "food_to_be_paired_with_wine"]
attributes = ["reasoning", "winery", "wine_name", "vintage", "region", "country", "wine_type", "grape_varietal", "tasting_notes", "wine_price", "occasion", "food_to_be_paired_with_wine"]
errornote = ""
for attempt in 1:5
@@ -576,13 +399,16 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
response = GeneralUtils.remove_french_accents(response)
# check wheter all attributes are in the response
checkFlag = false
for word in attributes
if !occursin(word, response)
errornote = "$word attribute is missing in previous attempts"
println("Attempt $attempt $errornote ", @__FILE__, " ", @__LINE__)
continue
checkFlag = true
break
end
end
checkFlag == true ? continue : nothing
responsedict = copy(JSON3.read(response))
@@ -591,24 +417,52 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
delete!(responsedict, :occasion)
delete!(responsedict, :food_to_be_paired_with_wine)
# check if winery, wine_name, region, country, wine_type, grape_variety are in the query because sometime AI halucinates
for i in [:grape_variety, :winery, :wine_name, :region]
content = responsedict[i]
if occursin(",", content)
content = split(content, ",") # sometime AI generates multiple values e.g. "Chenin Blanc, Riesling"
content = strip.(content)
else
content = [content]
end
println(@__FILE__, " ", @__LINE__)
pprintln(responsedict)
for x in content
if !occursin("NA", responsedict[i]) && !occursin(x, input)
errornote = "$x is not mentioned in the user query, you must only use the info from the query."
println("Attempt $attempt $errornote ", @__FILE__, " ", @__LINE__)
continue
# check if winery, wine_name, region, country, wine_type, grape_varietal's value are in the query because sometime AI halucinates
checkFlag = false
for i in attributes
j = Symbol(i)
if j [:reasoning, :tasting_notes, :occasion, :food_to_be_paired_with_wine]
# in case j is wine_price it needs to be checked differently because its value is ranged
if j == "wine_price"
if responsedict[:wine_price] != "NA"
# check whether wine_price is in ranged number
if !occursin('-', responsedict[:wine_price])
errornote = "wine_price must be a range number"
println("Attempt $attempt $errornote ", @__FILE__, " ", @__LINE__)
checkFlag = true
break
end
# check whether max wine_price is in the input
maxprice = split(responsedict[:wine_price], '-')[end]
if !occursin(maxprice, input)
responsedict[:wine_price] = "NA"
end
end
else
content = responsedict[j]
if occursin(",", content)
content = split(content, ",") # sometime AI generates multiple values e.g. "Chenin Blanc, Riesling"
content = strip.(content)
else
content = [content]
end
for x in content
if !occursin("NA", responsedict[j]) && !occursin(x, input)
errornote = "$x is not mentioned in the user query, you must only use the info from the query."
println("Attempt $attempt $errornote ", @__FILE__, " ", @__LINE__)
checkFlag == true
break
end
end
end
end
end
checkFlag == true ? continue : nothing
# remove (some text)
for (k, v) in responsedict
@@ -624,7 +478,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
end
end
#[PENDING] remove halucination. "highend dry white wine" --> "wine_type: white, occasion: special occasion, food_to_be_paired_with_wine: seafood, fish, country: France, Italy, USA, grape_variety: Chardonnay, Sauvignon Blanc, Pinot Grigio\nwine_notes: citrus, green apple, floral"
#[PENDING] remove halucination. "highend dry white wine" --> "wine_type: white, occasion: special occasion, food_to_be_paired_with_wine: seafood, fish, country: France, Italy, USA, grape_varietal: Chardonnay, Sauvignon Blanc, Pinot Grigio\nwine_notes: citrus, green apple, floral"
result = result[1:end-2] # remove the ending ", "
@@ -785,7 +639,7 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
for (k, v) in responsedict
if !occursin("keyword", string(k))
if v !== "NA" && (!occursin('-', v) || length(v) > 5)
errornote = "WARNING: The non-range value {$k: $v} is not allowed. It should be specified in a range format, such as min-max."
errornote = "WARNING: The non-range value {$k: $v} is not allowed. It should be specified in a range format, i.e. min-max."
println("Attempt $attempt $errornote ", @__FILE__, " ", @__LINE__)
continue
end