This commit is contained in:
youremail@yourdomain.com
2023-12-20 13:10:40 +00:00
parent 15d00ae0a7
commit 1130268c73
3 changed files with 142 additions and 137 deletions

View File

@@ -3,7 +3,7 @@ module interface
export agentReact, agentReflex,
addNewMessage, clearMessage, removeLatestMsg, conversation, directconversation,
writeEvaluationGuideline, grading, analyze, selfReflext, actor_mistral_openorca2,
writeEvaluationGuideline, grading, analyze, selfReflext,
formulateUserresponse, extractinfo, updateEnvState, chat_mistral_openorca
using JSON3, DataStructures, Dates, UUIDs, HTTP
@@ -178,7 +178,7 @@ function planner_mistral_openorca(a::agentReflex)
end
# skip objective and plan because LLM is going to generate new plan
shorttermMemory = dictToString(a.memory[:shortterm], skiplist=["Objective:", "Plan 0:"])
shorttermMemory = dictToString(a.memory[:shortterm], skiplist=["Objective:", "Plan 1:"])
assistant_plan_prompt =
"""
@@ -187,7 +187,8 @@ function planner_mistral_openorca(a::agentReflex)
The required info you need for wine recommendation:
- type of food: ask the user
- occasion: ask the user
- user's personal taste of wine: ask the user
- type of wine (red, white, rose, sparkling, etc): ask the user
- user's personal taste of wine characteristic (sweetness, acidity, etc): ask the user
- wine price range: ask the user
- ambient temperature at the serving location: ask the user
- wines we have in stock
@@ -255,7 +256,7 @@ end
# end
# # skip objective and plan because LLM is going to generate new plan
# shorttermMemory = dictToString(a.memory[:shortterm], skiplist=["Objective:", "Plan 0:"])
# shorttermMemory = dictToString(a.memory[:shortterm], skiplist=["Objective:", "Plan 1:"])
# assistant_plan_prompt =
# """
@@ -348,7 +349,7 @@ function updatePlan(a::agentReflex)
result = sendReceivePrompt(a, prompt, max_tokens=512, temperature=0.1)
@show updatedPlan = result
a.memory[:shortterm]["Plan 0:"] = result
a.memory[:shortterm]["Plan 1:"] = result
end
@@ -428,9 +429,9 @@ function actor_mistral_openorca(a::agentReflex)
response = splittext(response, ["Obs", "<|im_end|>"])
if !occursin("Thought", response)
response = "Thought: " * response
response = "Thought $(a.step): " * response
end
@show response_actor1 = response
headerToDetect = ["Question:", "Plan:", "Thought:",
"Act:", "Actinput:", "Obs:", "...",
"Answer:", "Conclusion:", "Summary:"]
@@ -442,7 +443,7 @@ function actor_mistral_openorca(a::agentReflex)
headers = detectCharacters(response, headerToDetect)
println("")
@show response_actor = response
@show response_actor2 = response
headerToDetect = ["Plan $(a.attempt):",
"Thought $(a.step):",
@@ -465,14 +466,14 @@ function actor_mistral_openorca(a::agentReflex)
toolinput = chunkedtext["Actinput $(a.step):"]
# change trailing number to continue a.memory[:shortterm]
_latest_step = keys(a.memory[:shortterm])
_latest_step = [i for i in _latest_step]
_latest_step = _latest_step[end]
latest_step = parse(Int, _latest_step[end-2:end-1])
_latestStep = keys(a.memory[:shortterm])
_latestStep = [i for i in _latestStep]
_latestStep = _latestStep[end]
latestStep = parse(Int, _latestStep[end-2:end-1])
headerToDetect = ["Question:", "Plan:", "Thought:",
"Act:", "Actinput:", "Obs:", "...",
"Answer:", "Conclusion:", "Summary:"]
nextstep = latest_step+1 # next step in short term memory
nextstep = latestStep+1 # next step in short term memory
response = replaceHeaders(response, headerToDetect, nextstep)
headerToDetect = ["Plan $(a.attempt):",
"Thought $nextstep:",
@@ -535,12 +536,12 @@ function conversation(a::agentReflex, usermsg::String; attemptlimit::Int=3)
# a.earlierConversation = conversationSummary(a)
_ = addNewMessage(a, "user", usermsg)
isusetools = isUseTools(a)
isuseplan = isUsePlans(a)
# newinfo = extractinfo(a, usermsg)
# a.env = newinfo !== nothing ? updateEnvState(a, newinfo) : a.env
@show isusetools
@show isuseplan
if isusetools # use tools before responseing
if isuseplan # use plan before responding
workstate, response = work(a)
end
@@ -557,21 +558,6 @@ function conversation(a::agentReflex, usermsg::String; attemptlimit::Int=3)
return response
end
""" Direct conversation is not an agent, messages does not pass through logic loop
but goes directly to LLM.
"""
function directconversation(a::agentReflex, usermsg::String)
response = nothing
_ = addNewMessage(a, "user", usermsg)
response = chat_mistral_openorca(a)
response = removeTrailingCharacters(response)
_ = addNewMessage(a, "assistant", response)
return response
end
"""
Continuously run llm functions except when llm is getting Answer: or chatbox.
There are many work() depend on thinking mode.
@@ -582,10 +568,10 @@ function work(a::agentReflex)
# user answering LLM -> Obs
if length(a.memory[:shortterm]) != 0
latest_step = dictLatestStep(a.memory[:shortterm])
if haskey(a.memory[:shortterm], "Act $latest_step:")
if occursin("chatbox", a.memory[:shortterm]["Act $latest_step:"])
a.memory[:shortterm]["Obs $latest_step:"] = a.messages[end][:content]
latestStep = shortMemLatestStep(a.memory[:shortterm])
if haskey(a.memory[:shortterm], "Act $latestStep:")
if occursin("chatbox", a.memory[:shortterm]["Act $latestStep:"])
a.memory[:shortterm]["Obs $latestStep:"] = a.messages[end][:content]
end
end
end
@@ -601,7 +587,7 @@ function work(a::agentReflex)
plan = planner_mistral_openorca(a)
a.memory[:shortterm]["Plan $(a.attempt):"] = plan
a.memory[:log]["Plan $(a.attempt):"] = plan
a.step = 0 # reset because new plan is created
a.step = 1 # reset because new plan is created
println("")
@show plan
@@ -706,30 +692,15 @@ function actor(a::agentReflex)
totalsteps = checkTotalStepInPlan(a)
while true # Actor loop
# decide whether to repeat step or do the next step
decision = "Yes" # yes because a.step start at 0
if a.step != 0
decision, reason = goNogo(a)
end
if decision == "Yes" # in case there is a cancel, go straight to evaluation
a.step += 1
elseif decision == "No" # repeat the latest step
a.memory[:shortterm] = removeHeaders(a.memory[:shortterm], a.step, ["Plan"])
a.memory[:log] = removeHeaders(a.memory[:log], a.step, ["Plan"])
println("repeating step $(a.step)")
else
error("undefined condition decision = $decision $(@__LINE__)")
end
@show a.step
#WORKING checkStepCompletion
iscomplete = checkStepCompletion(a::agentReflex)
# if iscomplete
if a.step < totalsteps # the last step of the plan is responding, let work() do this part
# check whether the current step is completed
iscomplete, reason = checkStepCompletion(a)
@show stepcompletecheck = iscomplete
if !iscomplete # not yet done
# work
toolname, toolinput = actor_mistral_openorca(a)
println("")
@show toolname
@show toolinput
@@ -747,11 +718,35 @@ function actor(a::agentReflex)
a.memory[:shortterm]["Obs $(a.step):"] = toolresult
a.memory[:log]["Obs $(a.step):"] = toolresult
end
else
actorState = "all steps done"
msgToUser = nothing
break
else # already done
a.step +=1
# if not add to memory yet. Add
# step +1
#TODO may be let LLM call finalResponse()
# if a.step > totalsteps # the last step of the plan is responding, let work() do this part
# actorState = "all steps done"
# msgToUser = nothing
# break
# end
end
# elseif decision == "No" # repeat the latest step
# a.memory[:shortterm] = removeHeaders(a.memory[:shortterm], a.step, ["Plan"])
# a.memory[:log] = removeHeaders(a.memory[:log], a.step, ["Plan"])
# println("repeating step $(a.step)")
# else
# error("undefined condition decision = $decision $(@__LINE__)")
# end
end
return actorState, msgToUser
@@ -1144,14 +1139,14 @@ julia> shorttermMemory = OrderedDict{String, Any}(
"Actinput 2:" => " amd graphics card latest\n",
"Obs 2:" => "No info available for your search query.")
julia> decision = goNogo(agent)
julia> decision = checkStepCompletion(agent)
"Yes"
```
"""
function goNogo(a)
function checkStepCompletion(a)
# stimulus = a.memory[:shortterm]["user:"]
work = dictToString(a.memory[:shortterm])
#WORKING
prompt =
"""
<|system|>
@@ -1165,76 +1160,91 @@ function goNogo(a)
Your earlier work:
$work
Your job is to check whether step $(a.step) of your work is completed according to the plan.
So for instance the following:
step 2 of the plan: Ask user about the occasion type. But you can't find any relevant info of occasion type in your work.
assistant: Step 2 isn't done yet. {No}
step 5 of the plan: Ask user if they have any preference for the style of wine. And you found relevant info in your work such as the user like full-bodied wine.
assistant: Step 5 is done. {Yes}
Step 2 of the plan: Ask user about their preferred taste in white wine.
Your interpretation of step 2 of the plan: My understanding of this step is that I need info about their preferred taste in white wine e.g. dryness, sweetness and etc. But I can't find any relevant info that the user tell me their preferred taste in white wine according to Obs.
assistant: Thus, step 2 isn't done yet. Answer: {Not done}
Step 5 of the plan: Ask user if they have any preferred type of wine.
Your interpretation of step 5 of the plan: My understanding of this step is that I need info about user preferred wine type. And I found relevant info such as the user like full-bodied wine according to Obs.
assistant: Step 5 is done. Answer: {done}
Let's think step by step.
</s|>
<|assistant|>
"""
response = sendReceivePrompt(a, prompt)
@show goNogo_response = response
response = sendReceivePrompt(a, prompt, max_tokens=512)
@show checkStepCompletion_response = response
decision = nothing
reason = nothing
if occursin("Yes", response)
decision = "Yes"
elseif occursin("No", response)
decision = "No"
if occursin("Not done", response)
decision = false
elseif occursin("done", response)
decision = true
else
error("undefied condition, decision $decision $(@__LINE__)")
end
startInd = findfirst(decision, response)[end] +2
if occursin(":", response[startInd:end]) # check for ":" after decision cha
startInd2 = findnext(":", response, startInd)[end]+1
reason = response[startInd2:end]
else
reason = response[startInd:end]
end
return decision, reason
return decision, response
end
# function checkStepCompletion(a)
# # stimulus = a.memory[:shortterm]["user:"]
# work = dictToString(a.memory[:shortterm])
# prompt =
# """
# <|system|>
# Symbol meaning:
# Plan: a plan
# Thought: your thought
# Act: the action you took
# Actinput: the input to the action
# Obs: the result of the action
function checkStepCompletion(a::agentReflex)
result = false
#WORKING I need current step of the plan
plan = "Plan $(a.attempt):"
plan = a.memory[:shortterm][plan]
prompt =
"""
<|system|>
Symbol meaning:
Plan: a plan
Thought: your thought
Act: the action you took
Actinput: the input to the action
Obs: the result of the action
Your plan:
$plan
# Your earlier work:
# $work
What is step $(a.step) of the plan?
<|/s|>
<|assistant|>
"""
# Your job is to check whether step $(a.step) of your work is completed according to the plan.
# So for instance the following:
# step 2 of the plan: Ask user about the occasion type. But you can't find any relevant info about occasion type in your work.
# assistant: Step 2 isn't done yet. Answer {Not done}
# step 5 of the plan: Ask user if they have any preference for the style of wine. And you found relevant info in your work such as the user like full-bodied wine.
# assistant: Step 5 is done. Answer {done}
# Let's think step by step.
# </s|>
# <|assistant|>
# """
response = sendReceivePrompt(a, prompt)
response = split(response, "<|im_end|>")[1]
# response = sendReceivePrompt(a, prompt)
# @show checkStepCompletion_response = response
return result
# decision = nothing
# if occursin("Not done", response)
# decision = false
# elseif occursin("done", response)
# decision = true
# else
# error("undefied condition, decision $decision $(@__LINE__)")
# end
# return decision, response
# end
""" Direct conversation is not an agent, messages does not pass through logic loop
but goes directly to LLM.
"""
function directconversation(a::agentReflex, usermsg::String)
response = nothing
_ = addNewMessage(a, "user", usermsg)
response = chat_mistral_openorca(a)
response = removeTrailingCharacters(response)
_ = addNewMessage(a, "assistant", response)
return response
end
@@ -1285,10 +1295,6 @@ end

View File

@@ -94,8 +94,8 @@ julia> agent = ChatAgent.agentReflex(
tools::Union{Dict, Nothing} = nothing
newplan::Bool = false # if true, new plan will be generated
attemptlimit::Int = 5 # thinking round limit
attempt::Int = 0 # attempted number
step::Int = 0 # step number
attempt::Int = 1 # attempted number
step::Int = 1 # step number
env::AbstractString = "N/A"
thinkingFormat::Union{Dict, Nothing} = nothing
memory::Dict = Dict(
@@ -167,7 +167,7 @@ function agentReflex(
:actor=>
"""
Use the following format:
Thought: you should always think about what to do according to step {step} of the plan (pay attention to correct numeral calculation and commonsense).
Thought: you should always think about what to do to achieve step {step} of the plan (pay attention to correct numeral calculation and commonsense).
Act: the action to take that align with your thought, should be one of [{toolnames}]
Actinput: your input to the action you chose (pay attention to the tool's input)
Obs: the result of the action

View File

@@ -2,9 +2,9 @@ module utils
export makeSummary, sendReceivePrompt, chunktext, extractStepFromPlan, checkTotalStepInPlan,
detectCharacters, findDetectedCharacter, extract_number, toolNameBeingCalled,
isUseTools, conversationSummary, checkReasonableness, replaceHeaders,
isUsePlans, conversationSummary, checkReasonableness, replaceHeaders,
addShortMem!, splittext, dictToString, removeHeaders, keepOnlyKeys, experience,
messagesToString, messagesToString_nomark, removeTrailingCharacters, dictLatestStep
messagesToString, messagesToString_nomark, removeTrailingCharacters, shortMemLatestStep
using UUIDs, Dates, DataStructures
using CommUtils, GeneralUtils
@@ -375,7 +375,7 @@ Return:
1. true/false # is LLM going to use tools
2. objective # what LLM going to do
"""
function isUseTools(a::agentReflex)
function isUsePlans(a::agentReflex)
toollines = ""
for (toolname, v) in a.tools
if toolname ["chatbox"] # LLM will always use chatbox
@@ -405,26 +405,26 @@ function isUseTools(a::agentReflex)
"""
# if LLM mentions any tools, use Plan/Thought/Act loop
isusetool = false
isuseplan = false
response = sendReceivePrompt(a, prompt, temperature=0.2, max_tokens=64)
response = split(response, "<|assistant|>")[1]
response = split(response, "<|user|>")[1]
@show response
for (toolname, v) in a.tools
if occursin("Yes", String(response))
isusetool = true
isuseplan = true
break
end
end
if length(a.memory[:shortterm]) != 0
isusetool = true
isuseplan = true
end
return isusetool
return isuseplan
end
# function isUseTools(a::agentReflex)
# function isUsePlans(a::agentReflex)
# toollines = ""
# for (toolname, v) in a.tools
# if toolname ∉ ["chatbox"] # LLM will always use chatbox
@@ -451,21 +451,21 @@ end
# """
# # if LLM mentions any tools, use Plan/Thought/Act loop
# isusetool = false
# isuseplan = false
# response = sendReceivePrompt(a, prompt, temperature=0.0)
# response = split(response, "<|im_end|>")[1]
# for (toolname, v) in a.tools
# if occursin(toolname, String(response))
# isusetool = true
# isuseplan = true
# break
# end
# end
# if length(a.memory[:shortterm]) != 0
# isusetool = true
# isuseplan = true
# end
# return isusetool
# return isuseplan
# end
@@ -1016,8 +1016,7 @@ function experience(dict::T) where {T<:AbstractDict}
end
function dictLatestStep(dict::T) where {T<:AbstractDict}
@show dict
function shortMemLatestStep(dict::T) where {T<:AbstractDict}
_latest_step = keys(dict)
_latest_step = [i for i in _latest_step]
_latest_step = _latest_step[end]