This commit is contained in:
youremail@yourdomain.com
2023-12-22 14:30:57 +00:00
parent f786643fd2
commit 78e99457d0
3 changed files with 99 additions and 335 deletions

View File

@@ -187,8 +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
- type of wine (red, white, etc): ask the user
- user's personal taste of wine characteristic (sweetness, acidity, etc): ask the user
- type of wine (Rose, White, Red and Sparkling): ask the user
- user's personal taste of wine characteristic: ask the user
- wine price range: ask the user
- ambient temperature at the serving location: ask the user
- wines we have in stock
@@ -199,11 +199,9 @@ function planner_mistral_openorca(a::agentReflex)
Your earlier work:
$shorttermMemory
Your task is to do the following:
Plan: first you should always think about your conversation with the user and your earlier work thoroughly then extract and devise a complete, step by step plan to achieve your objective (pay attention to correct numeral calculation and commonsense).
P.S.1 each step of the plan should be a single action.
P.S.2 ask the user if you don't have info.
P.S.3 mark a completed step with done keyword.
Your job is to do the following:
Plan: first you should always think about your conversation with the user and your earlier work thoroughly then extract and devise a complete, task by task plan to achieve your objective (pay attention to correct numeral calculation and commonsense).
P.S.1 each task of the plan should be a single action.
<|/s|>
$conversation
<|assistant|>
@@ -211,97 +209,12 @@ function planner_mistral_openorca(a::agentReflex)
"""
plan = sendReceivePrompt(a, assistant_plan_prompt, max_tokens=512, temperature=0.1)
plan = split(plan, "<|user|>")[1]
plan = split(plan, "<|assistant|>")[1]
plan = split(plan, "<|")[1]
plan = split(plan, "\n\n")[1]
return plan
end
# function planner_mistral_openorca(a::agentReflex)
# """
# general prompt format:
# "
# <|im_start|>system
# {role}
# {tools}
# {thinkingFormat}
# <|im_end|>
# {context}
# <|im_start|>user
# {usermsg}
# <|im_end|>
# <|im_start|>assistant
# "
# Note:
# {context} =
# "
# {earlierConversation}
# {env state}
# {shortterm memory}
# {longterm memory}
# "
# """
# conversation = messagesToString(a.messages)
# toollines = ""
# for (toolname, v) in a.tools
# if toolname ∉ [""]
# toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
# toollines *= toolline
# end
# end
# # skip objective and plan because LLM is going to generate new plan
# shorttermMemory = dictToString(a.memory[:shortterm], skiplist=["Objective:", "Plan 1:"])
# assistant_plan_prompt =
# """
# <|system|>
# $(a.roles[a.role])
# 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
# - wine price range: ask the user
# - ambient temperature at the serving location: ask the user
# - wines we have in stock
# You provide a personalized recommendation of wine based on the user's info above by describing the benefits of each wine in detail.
# You have access to the following tools:
# $toollines
# Your earlier work:
# $shorttermMemory
# Use the following format:
# Objective: what do you think the user needs?
# Plan: first you should always think about your conversation with the user and your earlier work thoroughly then extract and devise a complete, step by step plan (pay attention to correct numeral calculation and commonsense).
# P.S.1 each step of the plan should be a single action.
# P.S.2 ask the user if you don't have info.
# <|/s|>
# $conversation
# <|assistant|>
# Objective:
# """
# result = sendReceivePrompt(a, assistant_plan_prompt, max_tokens=512, temperature=0.1)
# x = split(result, "<|user|>")[1]
# x = split(result, "<|assistant|>")[1]
# x = split(x, "Step")[1]
# x = split(x, "Plan:")
# objective = x[1]
# plan = x[2]
# return objective, plan
# end
""" Update the current plan.
"""
function updatePlan(a::agentReflex)
@@ -353,7 +266,7 @@ function updatePlan(a::agentReflex)
end
function actor_mistral_openorca(a::agentReflex)
function actor_mistral_openorca(a::agentReflex, taskevaluate="")
"""
general prompt format:
@@ -407,19 +320,21 @@ function actor_mistral_openorca(a::agentReflex)
$toollines
Your earlier work:
$shorttermMemory
Your task evaluation:
$taskevaluate
$(a.thinkingFormat[:actor])
<|/s|>
<|assistant|>
Thought $(a.step):
Thought:
"""
prompt = replace(prompt, "{toolnames}" => toolnames)
prompt = replace(prompt, "{step}" => a.step)
println("")
@show prompt_actor = prompt
response = nothing
chunkedtext = nothing
latestTask = nothing
tempcounter = 0.0
while true # while Thought or Act is empty, run actor again
@@ -427,17 +342,18 @@ function actor_mistral_openorca(a::agentReflex)
@show tempcounter
response = sendReceivePrompt(a, prompt, temperature=tempcounter)
response = splittext(response, ["Obs", "<|im_end|>"])
#WORKING
latestTask = shortMemLatestTask(a.memory[:shortterm]) +1
if !occursin("Thought", response)
response = "Thought $(a.step): " * response
response = "Thought $latestTask: " * response
end
headerToDetect = ["Question:", "Plan:", "Thought:",
"Act:", "Actinput:", "Obs:", "...",
"Answer:", "Conclusion:", "Summary:"]
# replace headers with headers with correct attempt and step number
response = replaceHeaders(response, headerToDetect, a.step)
# replace headers with headers with correct attempt and task number
response = replaceHeaders(response, headerToDetect, latestTask)
response = split(response, "\n\n ")[1]
headers = detectCharacters(response, headerToDetect)
@@ -446,39 +362,40 @@ function actor_mistral_openorca(a::agentReflex)
@show response_actor = response
headerToDetect = ["Plan $(a.attempt):",
"Thought $(a.step):",
"Act $(a.step):",
"Actinput $(a.step):",
"Obs $(a.step):",
"Check $(a.step):",]
"Thought $latestTask:",
"Act $latestTask:",
"Actinput $latestTask:",
"Obs $latestTask:",
"Check $latestTask:",]
headers = detectCharacters(response, headerToDetect)
chunkedtext = chunktext(response, headers)
# assuming length more than 10 character means LLM has valid thinking
@show iskey_Thought = haskey(chunkedtext, "Thought $(a.step):")
@show iskey_Act = haskey(chunkedtext, "Act $(a.step):")
@show iskey_Actinput = haskey(chunkedtext, "Actinput $(a.step):")
@show iskey_Thought = haskey(chunkedtext, "Thought $latestTask:")
@show iskey_Act = haskey(chunkedtext, "Act $latestTask:")
@show iskey_Actinput = haskey(chunkedtext, "Actinput $latestTask:")
if iskey_Thought && iskey_Act && iskey_Actinput
if chunkedtext["Thought $(a.step):"] != " " && chunkedtext["Act $(a.step):"] != " "
if chunkedtext["Thought $latestTask:"] != " " && chunkedtext["Act $latestTask:"] != " "
break
end
end
end
toolname = toolNameBeingCalled(chunkedtext["Act $(a.step):"], a.tools)
toolinput = chunkedtext["Actinput $(a.step):"]
toolname = toolNameBeingCalled(chunkedtext["Act $latestTask:"], a.tools)
chunkedtext["Actinput $latestTask:"] = split(chunkedtext["Actinput $latestTask:"], '\n')[1]
toolinput = chunkedtext["Actinput $latestTask:"]
# change trailing number to continue a.memory[:shortterm]
headerToDetect = ["Question:", "Plan:", "Thought:",
"Act:", "Actinput:", "Obs:", "...",
"Answer:", "Conclusion:", "Summary:"]
response = replaceHeaders(response, headerToDetect, a.step)
response = replaceHeaders(response, headerToDetect, latestTask)
headerToDetect = ["Plan $(a.attempt):",
"Thought $(a.step):",
"Act $(a.step):",
"Actinput $(a.step):",
"Obs $(a.step):",
"Check $(a.step):",]
"Thought $latestTask:",
"Act $latestTask:",
"Actinput $latestTask:",
"Obs $latestTask:",
"Check $latestTask:",]
headers = detectCharacters(response, headerToDetect)
chunkedtext = chunktext(response, headers)
@@ -562,10 +479,10 @@ function work(a::agentReflex)
# user answering LLM -> Obs
if length(a.memory[:shortterm]) != 0
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]
latestTask = shortMemLatestTask(a.memory[:shortterm])
if haskey(a.memory[:shortterm], "Act $latestTask:")
if occursin("chatbox", a.memory[:shortterm]["Act $latestTask:"])
a.memory[:shortterm]["Obs $latestTask:"] = a.messages[end][:content]
end
end
end
@@ -578,7 +495,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 = 1 # reset because new plan is created
a.task = 1 # reset because new plan is created
println("")
@show plan
println("")
@@ -596,8 +513,8 @@ function work(a::agentReflex)
response = msgToUser
workstate = actorstate
break
elseif actorstate == "all steps done"
println("all steps done")
elseif actorstate == "all tasks done"
println("all tasks done")
response = formulateUserresponse(a)
@@ -640,7 +557,7 @@ function work(a::agentReflex)
a.memory[:longterm][chunkedtext["Context:"]] = chunkedtext["Lesson:"]
a.attempt += 1
a.step = 0
a.task = 0
a.memory[:shortterm] = OrderedDict{String, Any}()
a.memory[:log] = OrderedDict{String, Any}()
println("")
@@ -667,11 +584,11 @@ end
Arguments:
a, one of ChatAgent's agent.
plan, a step by step plan to response
plan, a task by task plan to response
Return:
case 1) if actor complete the plan successfully.
actorState = "all steps done" inidicates that all step in plan were done.
actorState = "all tasks done" inidicates that all task in plan were done.
msgToUser = nothing.
case 2) if actor needs to talk to user for more context
actorState = "chatbox"
@@ -683,153 +600,41 @@ function actor(a::agentReflex)
actorState = nothing
msgToUser = nothing
totalsteps = checkTotalStepInPlan(a)
totaltasks = checkTotalTaskInPlan(a)
while true # Actor loop
@show a.step
# check whether the current step is completed
isstepcomplete, reason = checkStepCompletion(a)
@show stepcompletecheck = reason
iskey_Thought = haskey(a.memory[:shortterm], "Thought $(a.step):")
chunkedtext = nothing
# check whether the current task is completed
istaskcomplete, taskevaluation = checkTaskCompletion(a)
@show taskevaluation
latestTask = shortMemLatestTask(a.memory[:shortterm]) +1
println(">>> working")
# work
toolname, toolinput, chunkedtext = actor_mistral_openorca(a, taskevaluation)
println("")
@show toolname
@show toolinput
println("")
if isstepcomplete # step complete
println(">>> step +1")
a.step += 1
elseif isstepcomplete == false && iskey_Thought == false # step not done, do work
println(">>> working")
# work
toolname, toolinput, chunkedtext = actor_mistral_openorca(a)
println("")
@show toolname
@show toolinput
println("")
@show a.step
addShortMem!(a.memory[:shortterm], chunkedtext)
println("")
addShortMem!(a.memory[:shortterm], chunkedtext)
println("")
if toolname == "chatbox" # chat with user
msgToUser = toolinput
msgToUser = split(msgToUser, "\n\n")[1]
actorState = toolname
break
elseif toolname == "noaction"
println(">>> already done")
a.step += 1
else # function call
f = a.tools[toolname][:func]
toolresult = f(a, toolinput)
@show toolresult
a.memory[:shortterm]["Obs $(a.step):"] = toolresult
a.memory[:log]["Obs $(a.step):"] = toolresult
end
elseif isstepcomplete == false && iskey_Thought == true # step done but not complete try again
a.memory[:shortterm] = removeHeaders(a.memory[:shortterm], a.step, ["Plan"])
println("repeating step $(a.step)")
else
error("undefined condition. isstepcomplete=$isstepcomplete, Thought key=$iskey_Thought $(@__LINE__)")
if toolname == "chatbox" # chat with user
msgToUser = toolinput
actorState = toolname
break
elseif toolname == "noaction"
println(">>> already done")
else # function call
f = a.tools[toolname][:func]
toolresult = f(a, toolinput)
@show toolresult
a.memory[:shortterm]["Obs $latestTask:"] = toolresult
a.memory[:log]["Obs $latestTask:"] = toolresult
end
# if !haskey(a.memory[:shortterm], "Thought $(a.step):") # agent didn't do this step yet.
# # work
# toolname, toolinput, chunkedtext = actor_mistral_openorca(a)
# println("")
# @show toolname
# @show toolinput
# println("")
# @show a.step
# addShortMem!(a.memory[:shortterm], chunkedtext)
# println("")
# if toolname == "chatbox" # chat with user
# msgToUser = toolinput
# msgToUser = split(msgToUser, "\n\n")[1]
# actorState = toolname
# break
# elseif toolname == "noaction"
# a.step += 1
# else # function call
# f = a.tools[toolname][:func]
# toolresult = f(a, toolinput)
# @show toolresult
# a.memory[:shortterm]["Obs $(a.step):"] = toolresult
# a.memory[:log]["Obs $(a.step):"] = toolresult
# end
# elseif haskey(a.memory[:shortterm], "Thought $(a.step):") && isstepcomplete == false
# a.memory[:shortterm] = removeHeaders(a.memory[:shortterm], a.step, ["Plan"])
# println("repeating step $(a.step)")
# elseif haskey(a.memory[:shortterm], "Thought $(a.step):") && isstepcomplete == true
# println(">>> step +1")
# a.step += 1
# else
# iskey = haskey(a.memory[:shortterm], "Thought $(a.step):")
# error("undefined condition. Thought key=$iskey, isstepcomplete=$isstepcomplete $(@__LINE__)")
# end
# if !iscomplete # not yet done
#
# # has Thought of current step means LLM already did this step
# if didthestep = true
# latestStep = shortMemLatestStep(a.memory[:shortterm])
# a.memory[:shortterm] = removeHeaders(a.memory[:shortterm], latestStep, ["Plan"])
# println("repeating step $(a.step)")
# end
# # work
# toolname, toolinput, chunkedtext = actor_mistral_openorca(a)
# println("")
# @show toolname
# @show toolinput
# addShortMem!(a.memory[:shortterm], chunkedtext)
# if toolname == "chatbox" # chat with user
# msgToUser = toolinput
# msgToUser = split(msgToUser, "\n\n")[1]
# actorState = toolname
# break
# elseif toolname == "noaction"
# a.step += 1
# else # function call
# f = a.tools[toolname][:func]
# toolresult = f(a, toolinput)
# @show toolresult
# a.memory[:shortterm]["Obs $(a.step):"] = toolresult
# a.memory[:log]["Obs $(a.step):"] = toolresult
# end
# else #WORKING already done
# a.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
@@ -905,7 +710,7 @@ julia> shorttermMemory = OrderedDict{String, Any}(
"Act 2:" => " wikisearch\n",
"Actinput 2:" => " amd graphics card latest\n",
"Obs 2:" => "No info available for your search query.")
julia> guideline = "\nEvaluation Guideline:\n1. Check if the user's question has been understood correctly.\n2. Evaluate the steps taken to provide the information requested by the user.\n3. Assess whether the correct tools were used for the task.\n4. Determine if the user's request was successfully fulfilled.\n5. Identify any potential improvements or alternative approaches that could be used in the future.\n\nThe response should include:\n1. A clear understanding of the user's question.\n2. The steps taken to provide the information requested by the user.\n3. An evaluation of whether the correct tools were used for the task.\n4. A confirmation or explanation if the user's request was successfully fulfilled.\n5. Any potential improvements or alternative approaches that could be used in the future."
julia> guideline = "\nEvaluation Guideline:\n1. Check if the user's question has been understood correctly.\n2. Evaluate the tasks taken to provide the information requested by the user.\n3. Assess whether the correct tools were used for the task.\n4. Determine if the user's request was successfully fulfilled.\n5. Identify any potential improvements or alternative approaches that could be used in the future.\n\nThe response should include:\n1. A clear understanding of the user's question.\n2. The tasks taken to provide the information requested by the user.\n3. An evaluation of whether the correct tools were used for the task.\n4. A confirmation or explanation if the user's request was successfully fulfilled.\n5. Any potential improvements or alternative approaches that could be used in the future."
julia> score = grading(agent, guideline, shorttermMemory)
2
```
@@ -985,7 +790,7 @@ function analyze(a)
Your work:
$shorttermMemory
You job is to do each of the following steps in detail to analize your work.
You job is to do each of the following in detail to analize your work.
1. What happened?
2. List all relationships, each with cause and effect.
3. Look at each relationship, figure out why it behaved that way.
@@ -1202,13 +1007,13 @@ end
""" Determine whether LLM should go to next step.
""" Determine whether LLM should go to next task.
Arguments:
a, one of ChatAgent's agent.
Return:
"Yes" or "no" decision to go next step.
"Yes" or "no" decision to go next task.
# Example
```jldoctest
@@ -1224,11 +1029,12 @@ julia> shorttermMemory = OrderedDict{String, Any}(
"Actinput 2:" => " amd graphics card latest\n",
"Obs 2:" => "No info available for your search query.")
julia> decision = checkStepCompletion(agent)
julia> decision = checkTaskCompletion(agent)
"Yes"
```
"""
function checkStepCompletion(a)
function checkTaskCompletion(a)
@show a.memory[:shortterm]["Plan 1:"]
# stimulus = a.memory[:shortterm]["user:"]
work = dictToString(a.memory[:shortterm])
@@ -1245,83 +1051,40 @@ function checkStepCompletion(a)
Your earlier work:
$work
Your task is to check whether step $(a.step) of your work is completed according to the plan.
Your job is to check whether each task of your plan has been completed.
So for instance the following:
Step 2 of the plan: Ask user about their preferred taste of a pizza.
Task 2 of the plan: Ask user about their preferred taste of a pizza.
Obs: I love Malvasia.
assistant: I can't find any relevant info that the user tell me their preferred taste in pizza wine according to Thought and Obs. Thus, step 2 isn't done yet. {answer: not done}
assistant: I can't find any relevant info that the user tell me their preferred taste in pizza wine according to Obs. Thus, task 2 isn't done yet. {answer: not done}
Step 5 of the plan: Ask user if they have any preferred type of car.
Task 5 of the plan: Ask user if they have any preferred type of car.
Obs: I like a semi truck.
assistant: I found relevant info such as the user like a semi truck according to Obs. Thus, step 5 is done. {answer: done}
assistant: I found relevant info such as the user like a semi truck according to Obs. Thus, task 5 is done. {answer: done}
Let's think step by step.
</s|>
<|assistant|>
<|assistant|> I
"""
response = nothing
_response = nothing
while true
_response = sendReceivePrompt(a, prompt, max_tokens=512)
response = split(_response, "}")[1] * "}"
if occursin("{", response)
_response = split(_response, "</s|>")[1]
if occursin("}", _response)
break
end
end
response = "I " * split(_response, "}")[1] * "}" # sometime response have more than 1 {answer: done}
decision = nothing
if occursin("not done", response)
decision = false
elseif occursin("done", response)
if occursin("done", response)
decision = true
else
error("undefied condition, decision $_response $(@__LINE__)")
decision = false
end
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
# 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 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)
# @show checkStepCompletion_response = response
# 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.

View File

@@ -95,7 +95,7 @@ julia> agent = ChatAgent.agentReflex(
newplan::Bool = false # if true, new plan will be generated
attemptlimit::Int = 5 # thinking round limit
attempt::Int = 1 # attempted number
step::Int = 1 # step number
task::Int = 1 # task number
env::AbstractString = "N/A"
thinkingFormat::Union{Dict, Nothing} = nothing
memory::Dict = Dict(
@@ -166,11 +166,12 @@ function agentReflex(
""",
:actor=>
"""
Your task is to do the following:
Thought: using step {step} of the plan as a guideline, what to do? (pay attention to correct numeral calculation and commonsense).
Your task is to use the following format:
Thought: using the plan and task evaluation as a guideline, what to do? (pay attention to correct numeral calculation and commonsense).
Act: the action to take based on your thought, must be one of [{toolnames}]
Actinput: your input to the action you chose (pay attention to the tool's input)
Obs: observed result of the action
Let's think step by step
""",
:actorOriginal=>
"""

View File

@@ -1,10 +1,10 @@
module utils
export makeSummary, sendReceivePrompt, chunktext, extractStepFromPlan, checkTotalStepInPlan,
export makeSummary, sendReceivePrompt, chunktext, extractStepFromPlan, checkTotalTaskInPlan,
detectCharacters, findDetectedCharacter, extract_number, toolNameBeingCalled,
isUsePlans, conversationSummary, checkReasonableness, replaceHeaders,
addShortMem!, splittext, dictToString, removeHeaders, keepOnlyKeys, experience,
messagesToString, messagesToString_nomark, removeTrailingCharacters, shortMemLatestStep
messagesToString, messagesToString_nomark, removeTrailingCharacters, shortMemLatestTask
using UUIDs, Dates, DataStructures
using CommUtils, GeneralUtils
@@ -210,7 +210,7 @@ function extractStepFromPlan(a::agent, plan::T, step::Int) where {T<:AbstractStr
return response
end
function checkTotalStepInPlan(a::agent)
function checkTotalTaskInPlan(a::agent)
headers = []
for (k, v) in a.memory[:shortterm]
@@ -1027,10 +1027,10 @@ Return:
```jldoctest
julia> dict = OrderedDict(
"Plan 1:" => "1. Ask about the type of food that will be served at the wedding party.")
julia>shortMemLatestStep(dict)
julia>shortMemLatestTask(dict)
1
"""
function shortMemLatestStep(dict::T) where {T<:AbstractDict}
function shortMemLatestTask(dict::T) where {T<:AbstractDict}
_latest_step = keys(dict)
_latest_step = [i for i in _latest_step]
_latest_step = _latest_step[end]