This commit is contained in:
2023-11-19 14:28:10 +00:00
parent 1658041f0e
commit 337f581da4

View File

@@ -60,7 +60,8 @@ abstract type agent end
context::String = "nothing" # internal thinking area
tools::Union{Dict, Nothing} = nothing
thought::String = "nothing" # contain unfinished thoughts for ReAct agent only
thoughtround::Int = 0
thoughtround::Int = 0 # no. of thinking round
thoughtlimit::Int = 5 # thinking round limit
thinkingMode::Union{Dict, Nothing} = nothing
end
@@ -98,10 +99,9 @@ function agentReact(
Act: the tool that match your thought, should be one of {toolnames}
ActInput: the input to the action (pay attention to the tool's input)
Obs: the result of the action
... (this Plan/Thought/Act/ActInput/Obs loop can repeat N times until you know the answer.)
Answer: Answer of the original question.
When you get "No info available." 3 times in a row, just answer the question.
... (this Plan/Thought/Act/ActInput/Obs can repeat N times until you know the answer.)
Thought: I think I know the answer
Answer: Answer of the original question
Begin!""",
),
@@ -109,7 +109,7 @@ function agentReact(
:wikisearch=>Dict(
:name => "wikisearch",
:description => "Useful for when you need to search the Internet",
:input => "Input should be a keyword not a question.",
:input => "Input should be keywords not a question.",
:output => "",
:func => wikisearch, # put function here
),
@@ -350,21 +350,27 @@ end
"""
Continuously run llm functions except when llm is getting Answer: or chatbox.
"""
function work(a::T, prompt::String) where {T<:agent}
function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
respond = nothing
while true
a.thoughtround += 1
@show prompt
@show a.thoughtround
toolname = nothing
toolinput = nothing
#WORKING force answer if thoughtround exceed limit
if a.thoughtround > a.thoughtlimit
a.thought *= "Thought $(a.thoughtround): I think I know the answer."
prompt = a.thought
end
@show prompt
respond = sendReceivePrompt(a, prompt)
@show respond
headerToDetect = nothing
if a.thoughtround == 1
try
respond = split(respond, "Obs:")[1]
@show respond
headerToDetect = ["Question:", "Plan:", "Thought:", "Act:", "ActInput:", "Obs:", "...", "Answer:",
"Conclusion:", "Summary:"]
catch
@@ -372,6 +378,7 @@ function work(a::T, prompt::String) where {T<:agent}
else
try
respond = split(respond, "Obs $(a.thoughtround):")[1]
@show respond
headerToDetect = ["Question $(a.thoughtround):", "Plan $(a.thoughtround):",
"Thought $(a.thoughtround):", "Act $(a.thoughtround):",
"ActInput $(a.thoughtround):", "Obs $(a.thoughtround):",
@@ -382,23 +389,9 @@ function work(a::T, prompt::String) where {T<:agent}
end
headers = detectCharacters(respond, headerToDetect)
@show headers
chunkedtext = chunktext(respond, headers)
@show chunkedtext
if a.thought == "nothing"
thought = ""
for i in chunkedtext
header = i[:header]
header = replace(header, ":"=>" $(a.thoughtround):") # add number so that llm not confused
body = i[:body]
thought *= "$header $body"
end
a.thought = thought
else
a.thought *= respond
end
Answer = findDetectedCharacter(headers, "Answer:")
AnswerInd = length(Answer) != 0 ? Answer[1] : nothing
Act = findDetectedCharacter(headers, "Act $(a.thoughtround):")
@@ -409,18 +402,49 @@ function work(a::T, prompt::String) where {T<:agent}
_ = addNewMessage(a, "assistant", respond)
break
else
# check for tool being called
ActHeader = a.thoughtround == 1 ? "Act:" : "Act $(a.thoughtround):"
ActInd = findDetectedCharacter(headers, ActHeader)[1]
toolname = toolNameBeingCalled(chunkedtext[ActInd][:body], a.tools)
toolinput = chunkedtext[ActInd+1][:body]
if length(findDetectedCharacter(headers, ActHeader)) != 0 # check whether there is Act: in a respond
ActInd = findDetectedCharacter(headers, ActHeader)[1]
toolname = toolNameBeingCalled(chunkedtext[ActInd][:body], a.tools)
end
ActInputHeader = a.thoughtround == 1 ? "ActInput:" : "ActInput $(a.thoughtround):"
if length(findDetectedCharacter(headers, ActInputHeader)) != 0 # check whether there is ActInput: in a respond
ActInputInd = findDetectedCharacter(headers, ActInputHeader)[1]
toolinput = chunkedtext[ActInputInd][:body]
end
# clean up
if occursin(" \"", toolinput)
toolinput = GeneralUtils.getStringBetweenCharacters(toolinput, " \"", "\"\n")
else
toolinput = GeneralUtils.getStringBetweenCharacters(toolinput, " ", "\n")
end
@show toolname
@show toolname #BUG llm not specify tools
@show toolinput
if toolname === nothing || toolinput === nothing
println("retry think")
a.thoughtround -= 1
continue
end
if a.thought == "nothing"
thought = ""
for i in chunkedtext
header = i[:header]
header = replace(header, ":"=>" $(a.thoughtround):") # add number so that llm not confused
body = i[:body]
thought *= "$header $body"
end
a.thought = thought #BUG should be prompt + thought
else
a.thought *= respond
end
if toolname == "chatbox" # chat with user
a.thought *= toolinput
@@ -435,7 +459,6 @@ function work(a::T, prompt::String) where {T<:agent}
_result = makeSummary(a, _result)
end
result = "Obs $(a.thoughtround): $_result\n"
@show result
a.thought *= result
prompt = a.thought
end
@@ -445,6 +468,107 @@ function work(a::T, prompt::String) where {T<:agent}
return respond
end
# function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
# respond = nothing
# while true
# a.thoughtround += 1
# toolname = nothing
# toolinput = nothing
# #WORKING force answer if thoughtround exceed limit
# if a.thoughtround > a.thoughtlimit
# a.thought *= "Thought $(a.thoughtround): I think I know the answer."
# prompt = a.thought
# end
# @show prompt
# respond = sendReceivePrompt(a, prompt)
# headerToDetect = nothing
# if a.thoughtround == 1
# try
# respond = split(respond, "Obs:")[1]
# @show respond
# headerToDetect = ["Question:", "Plan:", "Thought:", "Act:", "ActInput:", "Obs:", "...", "Answer:",
# "Conclusion:", "Summary:"]
# catch
# end
# else
# try
# respond = split(respond, "Obs $(a.thoughtround):")[1]
# @show respond
# headerToDetect = ["Question $(a.thoughtround):", "Plan $(a.thoughtround):",
# "Thought $(a.thoughtround):", "Act $(a.thoughtround):",
# "ActInput $(a.thoughtround):", "Obs $(a.thoughtround):",
# "...", "Answer:",
# "Conclusion:", "Summary:"]
# catch
# end
# end
# headers = detectCharacters(respond, headerToDetect)
# chunkedtext = chunktext(respond, headers)
# @show chunkedtext
# if a.thought == "nothing"
# thought = ""
# for i in chunkedtext
# header = i[:header]
# header = replace(header, ":"=>" $(a.thoughtround):") # add number so that llm not confused
# body = i[:body]
# thought *= "$header $body"
# end
# a.thought = thought
# else
# a.thought *= respond
# end
# Answer = findDetectedCharacter(headers, "Answer:")
# AnswerInd = length(Answer) != 0 ? Answer[1] : nothing
# Act = findDetectedCharacter(headers, "Act $(a.thoughtround):")
# if length(Answer) == 1 && length(Act) == 0
# a.thought = "nothing" # question finished, no more thought
# a.thoughtround = 0
# respond = chunkedtext[AnswerInd][:body]
# _ = addNewMessage(a, "assistant", respond)
# break
# else
# # check for tool being called
# ActHeader = a.thoughtround == 1 ? "Act:" : "Act $(a.thoughtround):"
# ActInd = findDetectedCharacter(headers, ActHeader)[1]
# toolname = toolNameBeingCalled(chunkedtext[ActInd][:body], a.tools)
# toolinput = chunkedtext[ActInd+1][:body]
# if occursin(" \"", toolinput)
# toolinput = GeneralUtils.getStringBetweenCharacters(toolinput, " \"", "\"\n")
# else
# toolinput = GeneralUtils.getStringBetweenCharacters(toolinput, " ", "\n")
# end
# @show toolname #BUG llm not specify tools
# @show toolinput
# if toolname == "chatbox" # chat with user
# a.thought *= toolinput
# respond = toolinput
# _ = addNewMessage(a, "assistant", respond)
# break
# else # function call
# println("//////////// $(a.thoughtround)")
# f = a.tools[Symbol(toolname)][:func]
# _result = f(toolinput)
# if _result != "No info available." #TODO for use with wikisearch(). Not good for other tools
# _result = makeSummary(a, _result)
# end
# result = "Obs $(a.thoughtround): $_result\n"
# a.thought *= result
# prompt = a.thought
# end
# end
# end
# @show respond
# return respond
# end
"""
make a conversation summary.
```jldoctest
@@ -715,7 +839,22 @@ function toolNameBeingCalled(text::T, tools::Dict) where {T<:AbstractString}
end
function answerNow(a::T) where {T<:agent}
prompt =
"""
<|im_start|>system
{systemMsg}
Your need to determine now whether you will use tools or actions to answer the question.
You have the following choices:
If you don't need tools or actions to answer the question say, "{no}".
If you need tools or actions to answer the question say, "{yes}".
<|im_end|>
"""
prompt = replace(prompt, "{systemMsg}" => a.thought)
error("answerNow done")
end
#TODO
@@ -761,23 +900,6 @@ function checkReasonableness(userMsg::String, context::String, tools)
end
function react_plan(text::String, firstlast="first")
"Plan" * GeneralUtils.getStringBetweenCharacters(text, "Plan", "Thought", firstlast=firstlast)
end
function react_thought(text::String, firstlast="first")
"Thought" * GeneralUtils.getStringBetweenCharacters(text, "Thought", "Act", firstlast=firstlast)
end
function react_act(text::String, firstlast="first")
"Act" * GeneralUtils.getStringBetweenCharacters(text, "Act", "ActInput", firstlast=firstlast)
end
function react_actinput(text::String, firstlast="first")
"ActInput" * GeneralUtils.getStringBetweenCharacters(text, "ActInput", "Obs", firstlast=firstlast)
end
"""
Detect given characters. Output is a list of named tuple of detected char.