This commit is contained in:
2026-06-28 22:03:58 +07:00
parent e63dd7d898
commit d076d5f912
8 changed files with 188 additions and 120 deletions
+136 -66
View File
@@ -48,6 +48,7 @@ macro executeStringFunction(functionStr, args...)
end
""" Think and choose action
# Arguments
@@ -125,12 +126,11 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
# recentevents_ind = GeneralUtils.recentElementsIndex(
# length(a.memory["events"]), recentevents; includelatest=true)
requiredKeys = ["plan", "actionname", "actioninput"]
requiredKeys = ["plan", "action_name", "action_input"]
context =
"""
<internal_context_for_assistant>
$(a.memory["shortmem"]["scratchpad"])
$(GeneralUtils.dict_to_string_html(a.memory["shortmem"]))
</internal_context_for_assistant>
"""
@@ -179,9 +179,9 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
continue
end
if responsedict["actionname"] ["CHATBOX", "CHECKWINE", "PRESENTBOX", "ENDCONVERSATION"]
if responsedict["action_name"] ["CHATBOX", "CHECKWINE", "PRESENTBOX", "ENDCONVERGUIDELINE"]
errornote = "Your previous attempt didn't use the given functions"
println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)--> $(responsedict["actionname"])", @__FILE__, ":", @__LINE__, " $(Dates.now())")
println("\nERROR YiemAgent decisionMaker() $errornote --(not qualify response)--> $(responsedict["action_name"])", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end
@@ -216,8 +216,8 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
# delete!(responsedict, :mentioned_winery)
# check whether responsedict["actioninput"] is the same as previous dialogue
# if !isempty(a.chathistory) && responsedict["actioninput"] == a.chathistory[end]["text"]
# check whether responsedict["action_input"] is the same as previous dialogue
# if !isempty(a.chathistory) && responsedict["action_input"] == a.chathistory[end]["text"]
# errornote = "In your previous attempt, you repeated the previous dialogue. Please try again."
# println("\nERROR YiemAgent decisionMaker() $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
@@ -317,16 +317,16 @@ function evaluator(a::T1, timeline, decisiondict, evaluateecontext
- 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
"actionname" is the name of the action taken, which can be one of the available tool name.
"actioninput" is the input to the action.
"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, actionname, and actioninput must be logically consistent
- The trainee's actioninput 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 ENDCONVERSATION tool.
- 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 ENDCONVERGUIDELINE 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.
@@ -368,7 +368,7 @@ function evaluator(a::T1, timeline, decisiondict, evaluateecontext
$evaluateecontext
</evaluatee_context>
<evaluatee_decision>
{plan: $(decisiondict["plan"]), actionname: $(decisiondict["actionname"]), actioninput: $(decisiondict["actioninput"])}
{plan: $(decisiondict["plan"]), action_name: $(decisiondict["action_name"]), action_input: $(decisiondict["action_input"])}
</evaluatee_decision>
P.S. $errornote
</context>
@@ -483,7 +483,7 @@ function conversation(a::sommelier; userinput::Union{Dict{String, Any}, JSON.Obj
end
# place holder
actionname = nothing
action_name = nothing
result = nothing
chatresponse = nothing
@@ -498,8 +498,8 @@ function conversation(a::sommelier; userinput::Union{Dict{String, Any}, JSON.Obj
# thinking loop until AI wants to communicate with the user
chatresponse = nothing
while chatresponse === nothing
actionname, result = think(a)
if actionname ["CHATBOX", "PRESENTBOX", "ENDCONVERSATION"]
action_name, result = think(a)
if action_name ["CHATBOX"]
chatresponse = result
end
end
@@ -532,8 +532,8 @@ function conversation(a::Union{companion, virtualcustomer}, userinput::Dict;
event_description="the user talks to the assistant.",
timestamp=Dates.now(),
subject="user",
actionname="CHATBOX",
actioninput=userinput["text"],
action_name="CHATBOX",
action_input=userinput["text"],
)
)
chatresponse = generatechat(a; converPartnerName=converPartnerName, recentEventNum=20)
@@ -545,8 +545,8 @@ function conversation(a::Union{companion, virtualcustomer}, userinput::Dict;
event_description="the assistant talks to the user.",
timestamp=Dates.now(),
subject="assistant",
actionname="CHATBOX",
actioninput=chatresponse,
action_name="CHATBOX",
action_input=chatresponse,
)
)
return chatresponse
@@ -564,20 +564,20 @@ julia>
```
"""
function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} where {T<:agent}
function think(a::T)::Union{NamedTuple{(:action_name, :result), Tuple{String, String}}, NamedTuple{(:action_name, :result), Tuple{String, Nothing}}} where {T<:agent}
# a.memory[:recap] = generateSituationReport(a, a.context["text"2textInstructLLM]; skiprecent=0)
thoughtDict = decisionMaker(a)
# # map action and input() to llm function
# response =
# if thoughtDict["actionname"] == "CHATBOX" || thoughtDict["actionname"] == "ENDCONVERSATION"
# if thoughtDict["action_name"] == "CHATBOX" || thoughtDict["action_name"] == "ENDCONVERGUIDELINE"
# (result=thoughtDict["plan"], errormsg=nothing, success=true)
# elseif thoughtDict["actionname"] == "CHECKWINE"
# checkwine(a, thoughtDict["actioninput"])
# elseif thoughtDict["actionname"] == "PRESENTBOX"
# (result=thoughtDict["actioninput"], errormsg=nothing, success=true)
# elseif thoughtDict["action_name"] == "CHECKWINE"
# checkwine(a, thoughtDict["action_input"])
# elseif thoughtDict["action_name"] == "PRESENTBOX"
# (result=thoughtDict["action_input"], errormsg=nothing, success=true)
# else
# error("undefined LLM function. Requesting $(thoughtDict["actionname"])")
# error("undefined LLM function. Requesting $(thoughtDict["action_name"])")
# end
# # this section allow LLM functions above to have different return values.
@@ -590,52 +590,52 @@ function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} wh
# success::Bool = haskey(response, "success") ? response["success"] : false
result = nothing
if thoughtDict["actionname"] ["CHATBOX"]
result = thoughtDict["actioninput"]
elseif thoughtDict["actionname"] ["ENDCONVERSATION"]
# WORKING add ENDCONVERSATION guideline in to scratchpad
guideline =
if thoughtDict["action_name"] ["CHATBOX"]
result = thoughtDict["action_input"]
elseif thoughtDict["action_name"] == "ENDCONVERGUIDELINE"
# add ENDCONVERGUIDELINE guideline in to scratchpad
endconversation_guideline =
"""
To end conversation with the user
<end_conversation_guideline>
- Provide customer with store contact info and business hours
- Invite customer to comeback
</end_conversation_guideline>
<store_info>
Business Hours: everyday 9.00-20.00
Tel. 0863055790
</store_info>
"""
a.memory["shortmem"]["scratchpad"] =
result = chatresponse
elseif thoughtDict["actionname"] ["PRESENTBOX"]
chatresponse = presentbox(a, thoughtDict) #PENDING
result = chatresponse
elseif thoughtDict["actionname"] == "CHECKWINE"
if rawresponse !== nothing
vd = GeneralUtils.dfToVectorDict(rawresponse) # comes in dataframe format
# a.memory["shortmem"]["found_wine"] = vd # used by decisionMaker() as a short note
#[PENDING] a.memory["shortmem"]["available_wine"] should be OrderedDict instead of vector dict so that no duplicate item are listed?
if length(a.memory["shortmem"]["db_search_result"]) != 0
a.memory["shortmem"]["db_search_result"] = vcat(a.memory["shortmem"]["db_search_result"], vd)
thoughtDict["action_result"] = endconversation_guideline
max_ind =
if length(a.memory["shortmem"]) == 0
0
else
a.memory["shortmem"]["db_search_result"] = vd
k = keys(a.memory["shortmem"])
maximum(parse.(Int, k))
end
a.memory["shortmem"]["$(max_ind + 1)"] = thoughtDict
# # add to scratchpad
# a.memory["shortmem"]["scratchpad"] *=
# """
# <database_search_result>
# I searched the database with this search term: $actioninput \nThis is what I found: $result
# </database_search_result>
# """
else
# a.memory["shortmem"]["scratchpad"] *=
# """
# <database_search_result>
# I searched the database with this search term: $actioninput \nThis is what I found: $result
# </database_search_result>
# """
end
elseif thoughtDict["action_name"] ["PRESENTBOX"] #PENDING
chatresponse = presentbox(a, thoughtDict)
result = chatresponse
elseif thoughtDict["action_name"] == "CHECKWINE"
df = checkwine(a, thoughtDict["action_input"])
thoughtDict["action_result"] = GeneralUtils.dfToString(df)
max_ind =
if length(a.memory["shortmem"]) == 0
0
else
k = keys(a.memory["shortmem"])
maximum(parse.(Int, k))
end
a.memory["shortmem"]["$(max_ind + 1)"] = thoughtDict
else
error("condition is not defined ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end
return (actionname=thoughtDict["actionname"], result=result)
return (action_name=thoughtDict["action_name"], result=result)
end
@@ -697,7 +697,7 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10, recentev
context =
"""
<context>
Name of the wines that needs to be introduced: $(thoughtDict["actioninput"])
Name of the wines that needs to be introduced: $(thoughtDict["action_input"])
$(a.memory["shortmem"]["scratchpad"])
P.S. $errornote
</context>
@@ -1333,4 +1333,74 @@ function detectWineryName(a, text)
end # module interface
end # module interface