This commit is contained in:
2026-07-09 07:56:33 +07:00
parent d33aa14dc8
commit 4d57f0146b
5 changed files with 163 additions and 104 deletions
+152 -35
View File
@@ -64,36 +64,14 @@ end
# Example
```jldoctest
julia> config = Dict(
"mqttServerInfo" => Dict(
"description" => "mqtt server info",
"port" => 1883,
"broker" => "mqtt.yiem.cc"
),
"externalservice" => Dict(
"text2textinstruct" => Dict(
"mqtttopic" => "/loadbalancer/requestingservice",
"description" => "text to text service with instruct LLM",
"llminfo" => Dict(
"name" => "llama3instruct"
)
),
)
)
julia> result = decisionMaker(agent)
julia> output_thoughtDict = Dict(
"thought_1" => "The customer wants to buy a bottle of wine. This is a good start!",
"action_1" => Dict{String, Any}(
"action"=>"CHAT_BOX",
"input"=>"What occasion are you buying the wine for?"
),
"observation_1" => ""
)
OrderedDict{String, Any} with 4 entries:
"plan" => "The user provided an image of a sparkling white wine (Asolo Prosecco Bella Principessa from Italy) and requested a search for similar wines in the inventory. According to store guidelines, I must st…
"action_name" => "CHECK_WINE"
"action_input" => "Sparkling white wine from Italy"
"action_result" => "1) winery: Terrazze dell Etna, wine_name: Rose Brut.
```
- [] update docstring
- [] use customerinfo
- [] user storeinfo
"""
function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
) where {T<:agent}
@@ -164,7 +142,6 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
think, response = GeneralUtils.extractthink(response)
response = String(split(response, ", observation")[1]) # in case LLM generate observation key which it isn't supposed to
response = strip(response)
@show response
responsedict = nothing
if occursin(requiredKeys[2], response)
@@ -371,6 +348,148 @@ function evaluator(a::T1, timeline, decisiondict, evaluateecontext
end
error("Evaluator failed to generate an evaluation, Response: \n$response\n<|End of error|>")
end
# function evaluator(a::T1, timeline, decisiondict, evaluateecontext
# ) where {T1<:agent}
# systemmsg =
# """
# <Your role>
# - You are a master sommelier of an online wine store.
# </Your role>
# <Situation>
# - Under your supervision, a trainee sommelier is engaging with a store customer. Each time the customer speaks, the trainee will assess the situation, determine the next course of action, and pause to await your guidance before proceeding.
# </Situation>
# <Your mission>
# - Improve a trainee sommelier decision based on the store policy and guidelines while ensuring seamless interactions between the trainee and customers.
# </Your mission>
# <At each round of conversation, you will be given the following information>
# - trajectory: A conversation between your trainee and the customer that have occurred up until now
# - evaluatee_context: The context that evaluatee use to make a decision
# - evaluatee_decision: The decision made by the evaluatee, consists of the following elements:
# "plan" is the trainee's plan
# "action_name" is the name of the action taken, which can be one of the available tool name.
# "action_input" is the input to the action.
# </At each round of conversation, you will be given the following information>
# <You must follow the following policy>
# - Use only infomation provided by the store policy and guidelines as a bedrocks for your response.
# </You must follow the following policy>
# <You should follow the following guidelines>
# - The trainee's plan, action_name, and action_input must be logically consistent
# - The trainee's action_input should be in a proper format as specified by the tools.
# - The trainee's action name and action input should make sense. For example, if the trainee isn't finished talking, he shouldn't use the END_CONVER_GUIDELINE tool.
# </You should follow the following guidelines>
# <You should then respond to the user with>
# 1) trajectory_evaluation: Analyze the trajectory of a solution to answer the user's original question.
# - Evaluate the correctness of each section and the overall trajectory based on the given question.
# - Provide detailed reasoning and analysis, focusing on the latest thought, action, and observation.
# - Incomplete trajectory are acceptable if the thoughts and actions up to that point are correct, even if the final answer isn't reached.
# - Do not generate additional thoughts or actions.
# 2) decision_evaluation:
# - Examine how the trainee's decisions align with the store's policies and guidelines before proceeding.
# 3) suggestion: Based store policy and guidelines, provide a suggestion for the immediate decision step only.
# 4) approval: Can be "yes" or "no". "no" if the suggestion contradict the trainee's decision; otherwise, it is "yes".
# </You should then respond to the user with>
# <You should only respond in JSON format as described below>
# {
# "trajectory_evaluation": "...",
# "decision_evaluation": "...",
# "suggestion": "...",
# "approval": "...",
# }
# </You should only respond in format as described below>
# Let's begin!
# """
# requiredKeys = [:trajectory_evaluation, :decision_evaluation, :approval, :suggestion]
# errornote = "N/A"
# for attempt in 1:10
# evaluateecontext = replace(evaluateecontext, "<context>" => "")
# evaluateecontext = replace(evaluateecontext, "</context>" => "")
# context =
# """
# <context>
# <trajectory>
# $timeline
# </trajectory>
# <evaluatee_context>
# $evaluateecontext
# </evaluatee_context>
# <evaluatee_decision>
# {plan: $(decisiondict["plan"]), action_name: $(decisiondict["action_name"]), action_input: $(decisiondict["action_input"])}
# </evaluatee_decision>
# P.S. $errornote
# </context>
# """
# unformatPrompt =
# [
# Dict("name" => "system", "text" => systemmsg),
# ]
# # put in model format
# prompt = GeneralUtils.formatLLMtext(unformatPrompt, a.llmFormatName)
# # add info
# prompt = prompt * context
# response = a.context.text2textInstructLLM(prompt; senderId=a.id)
# response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName)
# response = GeneralUtils.remove_french_accents(response)
# # response = replace(response, '$'=>"USD")
# think, response = GeneralUtils.extractthink(response)
# responsedict = nothing
# try
# responsedict = copy(JSON.parsefile(response))
# catch
# println("\nERROR YiemAgent generatechat() failed to parse response: $response", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# # check whether all answer's key points are in responsedict
# ispass, errormsg = checkAgentResponse_JSON(responsedict, requiredKeys)
# if !ispass
# errornote = errormsg
# println("\nERROR YiemAgent evaluator() $errornote --(not qualify response)> $responsedict", @__FILE__, ":", @__LINE__, " $(Dates.now())\n")
# continue
# end
# # if accepted_as_answer ∉ ["yes", "no"] # [PENDING] add errornote into the prompt
# # error("generated accepted_as_answer has wrong format")
# # end
# println("\nEvaluator() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# pprintln(Dict(responsedict))
# # # read sessionId
# # sessionid = a.id
# # # save to filename ./log/decisionlog.txt
# # println("saving SQLLLM evaluator() to disk")
# # filename = "agent_evaluator_log_$(sessionid[:id]).json"
# # filepath = "/appfolder/app/log/$filename"
# # # check whether there is a file path exists before writing to it
# # if !isfile(filepath)
# # decisionlist = [responsedict]
# # println("Creating file $filepath")
# # open(filepath, "w") do io
# # JSON.pretty(io, decisionlist)
# # end
# # else
# # # read the file and append new data
# # decisionlist = copy(JSON.parsefile(filepath))
# # push!(decisionlist, responsedict)
# # println("Appending new data to file $filepath")
# # open(filepath, "w") do io
# # JSON.pretty(io, decisionlist)
# # end
# # end
# return responsedict
# end
# error("Evaluator failed to generate an evaluation, Response: \n$response\n<|End of error|>")
# end
""" Chat with llm.
@@ -406,11 +525,10 @@ function conversation(a::sommelier; userinput::Union{Dict{String, Any}, JSON.Obj
# find text in usermsg
usertext = nothing
text_position = nothing
for (i, d) in enumerate(userinput["content"])
if d["type"] == "text"
d["text"] = GeneralUtils.remove_french_accents(d["text"])
usertext = d["text"]
text_position = i
end
end
@@ -419,7 +537,6 @@ function conversation(a::sommelier; userinput::Union{Dict{String, Any}, JSON.Obj
return "Okay. What shall we talk about?"
else
@info "YiemAgent conversation() 2" @__LINE__
userinput["content"][text_position]["text"] = GeneralUtils.remove_french_accents(usertext)
# add usermsg to a.chathistory but how do I handle images?
addNewMessage(a, "user", userinput; maximumMsg=maximumMsg)
@@ -504,7 +621,7 @@ function think(a::T)::NamedTuple{(:thoughtdict, :result_raw), Tuple{OrderedDict,
# a.memory[:recap] = generateSituationReport(a, a.context["text"2textInstructLLM]; skiprecent=0)
thoughtdict = decisionMaker(a)
@info "YiemAgent think() 1" @__LINE__
pprintln(thoughtdict)
# pprintln(thoughtdict)
result_raw = nothing
if thoughtdict["action_name"] ["CHAT_BOX"]
@@ -657,7 +774,7 @@ function generatechat(a::T; recentevents::Integer=20, maxattempt=10
- Once the user has selected their wine, if you haven't already, ask the user whether they need any further assistance. Do not offer any additional services.
- Only end the conversation when the user explicitly intends to do so. When ending, ensure a polite farewell and an invitation to return in the future.
- Spicy foods should be paired only with light red wines.
- We do not sell organic, sustainable, gluten-free, and sulfite-free wine. Inform the user imediately if they are looking for these types of wines. Do not sell our wines as such.
- We do not sell organic, sustainable, gluten-free, and sulfite-free wine. Inform the user immediately if they are looking for these types of wines. Do not sell our wines as such.
- Gift box, gift card, and custom messages are available. Inform the user to contact our sales team.
# store_guidelines
@@ -834,7 +951,7 @@ end
# - Once the user has selected their wine, if you haven't already, ask the user whether they need any further assistance. Do not offer any additional services.
# - Only end the conversation when the user explicitly intends to do so. When ending, ensure a polite farewell and an invitation to return in the future.
# - Spicy foods should be paired only with light red wines.
# - We do not sell organic, sustainable, gluten-free, and sulfite-free wine. Inform the user imediately if they are looking for these types of wines. Do not sell our wines as such.
# - We do not sell organic, sustainable, gluten-free, and sulfite-free wine. Inform the user immediately if they are looking for these types of wines. Do not sell our wines as such.
# - Gift box, gift card, and custom messages are available. Inform the user to contact our sales team.
# </store_policy>
# <store_guidelines>