This commit is contained in:
narawat lamaiin
2025-05-04 20:56:17 +07:00
parent 1fc5dfe820
commit a0152a3c29
4 changed files with 370 additions and 243 deletions

View File

@@ -342,26 +342,24 @@ function extractWineAttributes_1(a::T1, input::T2; maxattempt=10
User's query: ...
You must follow the following guidelines:
- 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.
- If specific information required in the preference form is not available in the query or there isn't any, mark with "N/A" to indicate this.
Additionally, words like 'any' or 'unlimited' mean no information is available.
- Do not generate other comments.
You should then respond to the user with:
Thought: 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 (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"
Country: a country where 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_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 range of wine.
Tasting_notes: a word describe the wine's flavor, such as "butter", "oak", "fruity", "raspberry", "earthy", "floral", etc
Wine_price_range: price range of wine. Example: For price 10-20, price range will be "10 to 20". For price 100, price range will be 0 to 100.
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 format as described below:
Thought: ...
Wine_name: ...
Winery: ...
Vintage: ...
@@ -370,41 +368,41 @@ function extractWineAttributes_1(a::T1, input::T2; maxattempt=10
Wine_type:
Grape_varietal: ...
Tasting_notes: ...
Wine_price: ...
Wine_price_range: ...
Occasion: ...
Food_to_be_paired_with_wine: ...
Here are some example:
User's query: red, Chenin Blanc, Riesling, 20 USD
{"reasoning": ..., "winery": "NA", "wine_name": "NA", "vintage": "NA", "region": "NA", "country": "NA", "wine_type": "red, white", "grape_varietal": "Chenin Blanc, Riesling", "tasting_notes": "NA", "wine_price": "0-20", "occasion": "NA", "food_to_be_paired_with_wine": "NA"}
User's query: red, Chenin Blanc, Riesling, 20 USD from Tuscany, Italy or Napa Valley, USA
Wine_name: N/A. Winery: N/A. Vintage: N/A. Region: Tuscany, Napa Valley. Country: Italy, United States. Wine_type: red, white. Grape_varietal: Chenin Blanc, Riesling. Tasting_notes: citrus. Wine_price_range: 0 to 20. Occasion: N/A. Food_to_be_paired_with_wine: N/A
User's query: Domaine du Collier Saumur Blanc 2019, France, white, Merlot
{"reasoning": ..., "winery": "Domaine du Collier", "wine_name": "Saumur Blanc", "vintage": "2019", "region": "Saumur", "country": "France", "wine_type": "white", "grape_varietal": "Merlot", "tasting_notes": "NA", "wine_price": "NA", "occasion": "NA", "food_to_be_paired_with_wine": "NA"}
Winery: Domaine du Collier. Wine_name: Saumur Blanc. Vintage: 2019. Region: Saumur. Country: France. Wine_type: white. Grape_varietal: Merlot. Tasting_notes: plum. Wine_price_range: N/A. Occasion: N/A. Food_to_be_paired_with_wine: N/A.
Let's begin!
"""
header = ["Thought:", "Wine_name:", "Winery:", "Vintage:", "Region:", "Country:", "Wine_type:", "Grape_varietal:", "Tasting_notes:", "Wine_price:", "Occasion:", "Food_to_be_paired_with_wine:"]
dictkey = ["thought", "wine_name", "winery", "vintage", "region", "country", "wine_type", "grape_varietal", "tasting_notes", "wine_price", "occasion", "food_to_be_paired_with_wine"]
header = ["Wine_name:", "Winery:", "Vintage:", "Region:", "Country:", "Wine_type:", "Grape_varietal:", "Tasting_notes:", "Wine_price_range:", "Occasion:", "Food_to_be_paired_with_wine:"]
dictkey = ["wine_name", "winery", "vintage", "region", "country", "wine_type", "grape_varietal", "tasting_notes", "wine_price_range", "occasion", "food_to_be_paired_with_wine"]
errornote = "N/A"
llmkwargs=Dict(
:num_ctx => 32768,
:temperature => 0.5,
:temperature => 0.2,
)
for attempt in 1:maxattempt
#[PENDING] I should add generatequestion()
if attempt > 1
println("\nYiemAgent extractWineAttributes_1() attempt $attempt/$maxattempt ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end
usermsg =
"""
User's query: $input
P.S. $errornote
"""
"""
$input
"""
assistantinfo =
"""
<information>
P.S. $errornote
</information>
"""
_prompt =
[
@@ -413,23 +411,30 @@ function extractWineAttributes_1(a::T1, input::T2; maxattempt=10
]
# put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3")
prompt = GeneralUtils.formatLLMtext(_prompt, a.llmFormatName)
# add info
prompt = prompt * assistantinfo
response = a.func[:text2textInstructLLM](prompt;
modelsize="medium", llmkwargs=llmkwargs, senderId=a.id)
response = GeneralUtils.remove_french_accents(response)
response = GeneralUtils.deFormatLLMtext(response, "granite3")
response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName)
think, response = GeneralUtils.extractthink(response)
# check wheter all attributes are in the response
checkFlag = false
for word in header
if !occursin(word, response)
errornote = "In your previous attempts, the $word attribute is missing. Please try again."
println("\nYiemAgent extractWineAttributes_1() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true
break
end
# check whether response has all header
detected_kw = GeneralUtils.detect_keyword(header, response)
kwvalue = [i for i in values(detected_kw)]
zeroind = findall(x -> x == 0, kwvalue)
missingkeys = [header[i] for i in zeroind]
if 0 values(detected_kw)
errornote = "$missingkeys are missing from your previous response"
println("\nERROR YiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
elseif sum(values(detected_kw)) > length(header)
errornote = "Your previous attempt has duplicated points"
println("\nERROR YiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end
checkFlag == true ? continue : nothing
# check whether response has all answer's key points
detected_kw = GeneralUtils.detect_keyword(header, response)
@@ -440,6 +445,7 @@ function extractWineAttributes_1(a::T1, input::T2; maxattempt=10
elseif sum(values(detected_kw)) > length(header)
errornote = "In your previous attempts, the response has duplicated answer's key points"
println("\nYiemAgent extractWineAttributes_1() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
println(response)
continue
end
responsedict = GeneralUtils.textToDict(response, header;
@@ -460,29 +466,29 @@ function extractWineAttributes_1(a::T1, input::T2; maxattempt=10
if j [:thought, :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"
if responsedict[:wine_price] != "N/A"
# check whether wine_price is in ranged number
if !occursin('-', responsedict[:wine_price])
errornote = "In your previous attempt, the 'wine_price' was not set to a ranged number. Please adjust it accordingly."
if !occursin("to", responsedict[:wine_price])
errornote = "In your previous attempt, the 'wine_price' was set to $(responsedict[:wine_price]) which is not a correct format. Please adjust it accordingly."
println("\nERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true
break
end
# check whether max wine_price is in the input
pricerange = split(responsedict[:wine_price], '-')
minprice = pricerange[1]
maxprice = pricerange[end]
if !occursin(maxprice, input)
responsedict[:wine_price] = "NA"
end
# price range like 100-100 is not good
if minprice == maxprice
errornote = "In your previous attempt, you inputted 'wine_price' with a 'minimum' value equaling the 'maximum', which is not valid."
println("\nERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true
break
end
# # check whether max wine_price is in the input
# pricerange = split(responsedict[:wine_price], '-')
# minprice = pricerange[1]
# maxprice = pricerange[end]
# if !occursin(maxprice, input)
# responsedict[:wine_price] = "N/A"
# end
# # price range like 100-100 is not good
# if minprice == maxprice
# errornote = "In your previous attempt, you inputted 'wine_price' with a 'minimum' value equaling the 'maximum', which is not valid."
# println("\nERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# checkFlag = true
# break
# end
end
else
content = responsedict[j]
@@ -517,7 +523,7 @@ function extractWineAttributes_1(a::T1, input::T2; maxattempt=10
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)
if !occursin("N/A", v) && v != "" && !occursin("none", v) && !occursin("None", v)
result *= "$k: $v, "
end
end
@@ -580,7 +586,7 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
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 or there isn't any, mark with 'NA' to indicate this.
1) If specific information required in the preference form is not available in the query or there isn't any, mark with 'N/A' 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.
@@ -605,8 +611,8 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
Here are some examples:
User's query: I want a wine with a medium-bodied, low acidity, medium tannin.
Sweetness_keyword: NA
Sweetness: NA
Sweetness_keyword: N/A
Sweetness: N/A
Acidity_keyword: low acidity
Acidity: 1-2
Tannin_keyword: medium tannin
@@ -615,20 +621,20 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
Intensity: 3-4
User's query: German red wine, under 100, pairs with spicy food
Sweetness_keyword: NA
Sweetness: NA
Acidity_keyword: NA
Acidity: NA
Tannin_keyword: NA
Tannin: NA
Intensity_keyword: NA
Intensity: NA
Sweetness_keyword: N/A
Sweetness: N/A
Acidity_keyword: N/A
Acidity: N/A
Tannin_keyword: N/A
Tannin: N/A
Intensity_keyword: N/A
Intensity: N/A
Let's begin!
"""
header = ["Sweetness_keyword:", "Sweetness:", "Acidity_keyword:", "Acidity:", "Tannin_keyword:", "Tannin:", "Intensity_keyword:", "Intensity:"]
dictkey = ["sweetness_keyword", "sweetness", "acidity_keyword", "acidity", "tannin_keyword", "tannin", "intensity_keyword", "intensity"]
errornote = ""
errornote = "N/A"
for attempt in 1:10
usermsg =
@@ -645,10 +651,11 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
]
# put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3")
prompt = GeneralUtils.formatLLMtext(_prompt, a.llmFormatName)
response = a.func[:text2textInstructLLM](prompt)
response = GeneralUtils.deFormatLLMtext(response, "granite3")
response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName)
think, response = GeneralUtils.extractthink(response)
# check whether response has all answer's key points
detected_kw = GeneralUtils.detect_keyword(header, response)
@@ -669,23 +676,23 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
for i in ["sweetness", "acidity", "tannin", "intensity"]
keyword = Symbol(i * "_keyword") # e.g. sweetness_keyword
value = responsedict[keyword]
if value != "NA" && !occursin(value, input)
if value != "N/A" && !occursin(value, input)
errornote = "In your previous attempt, keyword $keyword: $value does not appear in the input. You must use information from the input only"
println("\nERROR YiemAgent extractWineAttributes_2() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end
# if value == "NA" then responsedict[i] = "NA"
# e.g. if sweetness_keyword == "NA" then sweetness = "NA"
if value == "NA"
responsedict[Symbol(i)] = "NA"
# if value == "N/A" then responsedict[i] = "N/A"
# e.g. if sweetness_keyword == "N/A" then sweetness = "N/A"
if value == "N/A"
responsedict[Symbol(i)] = "N/A"
end
end
# some time LLM not put integer range
for (k, v) in responsedict
if !occursin("keyword", string(k))
if v !== "NA" && (!occursin('-', v) || length(v) > 5)
if v !== "N/A" && (!occursin('-', v) || length(v) > 5)
errornote = "WARNING: The non-range value {$k: $v} is not allowed. It should be specified in a range format, i.e. min-max."
println("\nERROR YiemAgent extractWineAttributes_2() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
@@ -693,10 +700,10 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
end
end
# some time LLM says NA-2. Need to convert NA to 1
# some time LLM says N/A-2. Need to convert N/A to 1
for (k, v) in responsedict
if occursin("NA", v) && occursin("-", v)
new_v = replace(v, "NA"=>"1")
if occursin("N/A", v) && occursin("-", v)
new_v = replace(v, "N/A"=>"1")
responsedict[k] = new_v
end
end
@@ -704,7 +711,7 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
result = ""
for (k, v) in responsedict
# some time LLM generate text with "(some comment)". this line removes it
if !occursin("NA", v)
if !occursin("N/A", v)
result *= "$k: $v, "
end
end
@@ -756,7 +763,7 @@ function paraphrase(text2textInstructLLM::Function, text::String)
header = ["Paraphrase:"]
dictkey = ["paraphrase"]
errornote = ""
errornote = "N/A"
response = nothing # placeholder for show when error msg show up
@@ -773,11 +780,12 @@ function paraphrase(text2textInstructLLM::Function, text::String)
]
# put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3")
prompt = GeneralUtils.formatLLMtext(_prompt, a.llmFormatName)
try
response = text2textInstructLLM(prompt)
response = GeneralUtils.deFormatLLMtext(response, "granite3")
response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName)
think, response = GeneralUtils.extractthink(response)
# sometime the model response like this "here's how I would respond: ..."
if occursin("respond:", response)
errornote = "You don't need to intro your response"