update
This commit is contained in:
343
src/interface.jl
343
src/interface.jl
@@ -60,8 +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 # no. of thinking round
|
||||
thoughtlimit::Int = 5 # thinking round limit
|
||||
thinkinground::Int = 0 # no. of thinking round
|
||||
thinkingroundlimit::Int = 5 # thinking round limit
|
||||
thinkingMode::Union{Dict, Nothing} = nothing
|
||||
end
|
||||
|
||||
@@ -163,6 +163,138 @@ function agentReact(
|
||||
return newAgent
|
||||
end
|
||||
|
||||
@kwdef mutable struct agentReflex <: agent
|
||||
availableRole::AbstractVector = ["system", "user", "assistant"]
|
||||
agentName::String = "assistant"
|
||||
maxUserMsg::Int = 10
|
||||
earlierConversation::String = "" # summary of earlier conversation
|
||||
mqttClient::Union{mqttClient, Nothing} = nothing
|
||||
msgMeta::Union{Dict, Nothing} = nothing
|
||||
|
||||
""" Dict(Role=> Content) ; Role can be system, user, assistant
|
||||
Example:
|
||||
messages=[
|
||||
Dict(:role=>"system", :content=> "You are a helpful assistant."),
|
||||
Dict(:role=>"assistant", :content=> "How may I help you"),
|
||||
Dict(:role=>"user", :content=> "Hello, how are you"),
|
||||
]
|
||||
"""
|
||||
role::Symbol = :assistant
|
||||
roles::Dict = Dict(:assistant => "You are a helpful assistant.",)
|
||||
|
||||
# Ref: Chat prompt format https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML/discussions/3
|
||||
# messages= [Dict(:role=>"system", :content=> "", :timestamp=> Dates.now()),]
|
||||
messages = Vector{Dict{Symbol, Any}}()
|
||||
context::String = "nothing" # internal thinking area
|
||||
tools::Union{Dict, Nothing} = nothing
|
||||
thought::String = "nothing" # contain unfinished thoughts for ReAct agent only
|
||||
thinkinground::Int = 0 # no. of thinking round
|
||||
thinkingroundlimit::Int = 5 # thinking round limit
|
||||
thinkingMode::Union{Dict, Nothing} = nothing
|
||||
memory::Dict = Dict(
|
||||
:shortterm=> "",
|
||||
:longterm=>""
|
||||
)
|
||||
end
|
||||
|
||||
function agentReflex(
|
||||
agentName::String,
|
||||
mqttClientSpec::NamedTuple;
|
||||
role::Symbol=:assistant,
|
||||
roles::Dict=Dict(
|
||||
:assistant =>
|
||||
"""
|
||||
You are a helpful assistant who answer the user's questions as best you can.
|
||||
""",
|
||||
:sommelier =>
|
||||
"""
|
||||
You are a sommelier at an online wine reseller who always ask user for wine relevant info before you could help them choosing wine.
|
||||
You provide a personalized recommendation of up to two wines based on the user's preference, and you describe the benefits of each wine in detail.
|
||||
You don't know other people personal info previously.
|
||||
|
||||
Info used to select wine:
|
||||
- type of food
|
||||
- occasion
|
||||
- user's personal taste of wine
|
||||
- wine price range
|
||||
- temperature at the serving location
|
||||
- wine we have in stock
|
||||
""",
|
||||
),
|
||||
thinkingMode::Dict=Dict(
|
||||
:no_thinking=> "",
|
||||
:thinking=>
|
||||
"""Use the following format:
|
||||
Question: the input question your user is asking and you must answer
|
||||
Plan: first you should always think about the question and the info you have thoroughly then extract and devise a complete plan to find the answer (pay attention to variables and their corresponding numerals).
|
||||
Thought: ask yourself do you have all the info you need? And what to do according to the plan (pay attention to correct numeral calculation and commonsense).
|
||||
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 can repeat N times until you know the answer.)
|
||||
Thought: I think I know the answer
|
||||
Answer: Answer of the original question
|
||||
|
||||
Begin!""",
|
||||
),
|
||||
tools::Dict=Dict(
|
||||
:wikisearch=>Dict(
|
||||
:name => "wikisearch",
|
||||
:description => "Useful for when you need to search an encyclopedia",
|
||||
:input => "Input is keywords and not a question.",
|
||||
:output => "",
|
||||
:func => wikisearch, # put function here
|
||||
),
|
||||
:chatbox=>Dict(
|
||||
:name => "chatbox",
|
||||
:description => "Useful for when you need to ask a customer for more context.",
|
||||
:input => "Input should be a conversation to customer.",
|
||||
:output => "" ,
|
||||
:func => nothing,
|
||||
),
|
||||
# :wineStock=>Dict(
|
||||
# :name => "wineStock",
|
||||
# :description => "useful for when you need to search for wine by your description, price, name or ID.",
|
||||
# :input => "Input should be a search query with as much details as possible.",
|
||||
# :output => "" ,
|
||||
# :func => nothing,
|
||||
# ),
|
||||
# :NTHING=>Dict(
|
||||
# :name => "NTHING",
|
||||
# :description => "useful for when you don't need to use tools or actions",
|
||||
# :input => "No input is needed",
|
||||
# :output => "" ,
|
||||
# :func => nothing,
|
||||
# ),
|
||||
),
|
||||
msgMeta::Dict=Dict(
|
||||
:msgPurpose=> "updateStatus",
|
||||
:from=> "chatbothub",
|
||||
:to=> "llmAI",
|
||||
:requestrespond=> "request",
|
||||
:sendto=> "", # destination topic
|
||||
:replyTo=> "chatbothub/llm/respond", # requester ask responder to send reply to this topic
|
||||
:repondToMsgId=> "", # responder is responding to this msg id
|
||||
:taskstatus=> "", # "complete", "fail", "waiting" or other status
|
||||
:timestamp=> Dates.now(),
|
||||
:msgId=> "$(uuid4())",
|
||||
),
|
||||
availableRole::AbstractArray=["system", "user", "assistant"],
|
||||
maxUserMsg::Int=10,)
|
||||
|
||||
newAgent = agentReact()
|
||||
newAgent.availableRole = availableRole
|
||||
newAgent.maxUserMsg = maxUserMsg
|
||||
newAgent.mqttClient = CommUtils.mqttClient(mqttClientSpec)
|
||||
newAgent.msgMeta = msgMeta
|
||||
newAgent.tools = tools
|
||||
newAgent.role = role
|
||||
newAgent.roles = roles
|
||||
newAgent.thinkingMode = thinkingMode
|
||||
|
||||
return newAgent
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
add new message to agent
|
||||
@@ -356,7 +488,7 @@ function conversation(a::T, usermsg::String) where {T<:agent}
|
||||
|
||||
if a.thought != "nothing" # continue thought
|
||||
_ = addNewMessage(a, "user", usermsg)
|
||||
a.thought *= "Obs $(a.thoughtround): $usermsg\n"
|
||||
a.thought *= "Obs $(a.thinkinground): $usermsg\n"
|
||||
prompt = a.thought
|
||||
respond = work(a, prompt)
|
||||
else # new thought
|
||||
@@ -392,20 +524,21 @@ end
|
||||
function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
|
||||
respond = nothing
|
||||
while true
|
||||
a.thoughtround += 1
|
||||
@show a.thoughtround
|
||||
a.thinkinground += 1
|
||||
@show a.thinkinground
|
||||
toolname = nothing
|
||||
toolinput = nothing
|
||||
|
||||
if a.thoughtround > a.thoughtlimit
|
||||
a.thought *= "Thought $(a.thoughtround): I think I know the answer."
|
||||
if a.thinkinground > a.thinkingroundlimit
|
||||
a.thought *= "Thought $(a.thinkinground): I think I know the answer."
|
||||
prompt = a.thought
|
||||
end
|
||||
|
||||
@show prompt
|
||||
respond = sendReceivePrompt(a, prompt)
|
||||
|
||||
headerToDetect = nothing
|
||||
if a.thoughtround == 1
|
||||
if a.thinkinground == 1
|
||||
try
|
||||
respond = split(respond, "Obs:")[1]
|
||||
headerToDetect = ["Question:", "Plan:", "Thought:", "Act:", "ActInput:", "Obs:", "...", "Answer:",
|
||||
@@ -414,10 +547,10 @@ function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
|
||||
end
|
||||
else
|
||||
try
|
||||
respond = split(respond, "Obs $(a.thoughtround):")[1]
|
||||
headerToDetect = ["Question $(a.thoughtround):", "Plan $(a.thoughtround):",
|
||||
"Thought $(a.thoughtround):", "Act $(a.thoughtround):",
|
||||
"ActInput $(a.thoughtround):", "Obs $(a.thoughtround):",
|
||||
respond = split(respond, "Obs $(a.thinkinground):")[1]
|
||||
headerToDetect = ["Question $(a.thinkinground):", "Plan $(a.thinkinground):",
|
||||
"Thought $(a.thinkinground):", "Act $(a.thinkinground):",
|
||||
"ActInput $(a.thinkinground):", "Obs $(a.thinkinground):",
|
||||
"...", "Answer:",
|
||||
"Conclusion:", "Summary:"]
|
||||
catch
|
||||
@@ -429,11 +562,11 @@ function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
|
||||
|
||||
Answer = findDetectedCharacter(headers, "Answer:")
|
||||
AnswerInd = length(Answer) != 0 ? Answer[1] : nothing
|
||||
Act = findDetectedCharacter(headers, "Act $(a.thoughtround):")
|
||||
Act = findDetectedCharacter(headers, "Act $(a.thinkinground):")
|
||||
if length(Answer) == 1 && length(Act) == 0
|
||||
a.thought = "nothing" # question finished, no more thought
|
||||
a.context = "nothing"
|
||||
a.thoughtround = 0
|
||||
a.thinkinground = 0
|
||||
respond = chunkedtext[AnswerInd][:body]
|
||||
respond = replace(respond, "<|im_end|>"=>"")
|
||||
_ = addNewMessage(a, "assistant", respond)
|
||||
@@ -441,12 +574,12 @@ function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
|
||||
else
|
||||
|
||||
# check for tool being called
|
||||
ActHeader = a.thoughtround == 1 ? "Act:" : "Act $(a.thoughtround):"
|
||||
ActHeader = a.thinkinground == 1 ? "Act:" : "Act $(a.thinkinground):"
|
||||
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):"
|
||||
ActInputHeader = a.thinkinground == 1 ? "ActInput:" : "ActInput $(a.thinkinground):"
|
||||
if length(findDetectedCharacter(headers, ActInputHeader)) != 0 # check whether there is ActInput: in a respond
|
||||
ActInputInd = findDetectedCharacter(headers, ActInputHeader)[1]
|
||||
toolinput = chunkedtext[ActInputInd][:body]
|
||||
@@ -462,7 +595,7 @@ function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
|
||||
@show toolinput
|
||||
if toolname === nothing || toolinput === nothing
|
||||
println("toolname $toolname toolinput $toolinput retry thinking")
|
||||
a.thoughtround -= 1
|
||||
a.thinkinground -= 1
|
||||
continue
|
||||
end
|
||||
|
||||
@@ -470,7 +603,7 @@ function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
|
||||
thought = ""
|
||||
for i in chunkedtext
|
||||
header = i[:header]
|
||||
header = replace(header, ":"=>" $(a.thoughtround):") # add number so that llm not confused
|
||||
header = replace(header, ":"=>" $(a.thinkinground):") # add number so that llm not confused
|
||||
body = i[:body]
|
||||
thought *= "$header $body"
|
||||
end
|
||||
@@ -491,7 +624,7 @@ function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
|
||||
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"
|
||||
result = "Obs $(a.thinkinground): $_result\n"
|
||||
a.thought *= result
|
||||
prompt = a.thought
|
||||
end
|
||||
@@ -501,6 +634,78 @@ function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
|
||||
return respond
|
||||
end
|
||||
|
||||
function conversation(a::agentReflex, usermsg::String; thinkingroundlimit::Int=3)
|
||||
a.thinkingroundlimit = thinkingroundlimit
|
||||
respond = nothing
|
||||
|
||||
# determine thinking mode
|
||||
a.thinkingMode = chooseThinkingMode(a, usermsg)
|
||||
|
||||
if a.thinkingMode == :no_thinking
|
||||
a.earlierConversation = conversationSummary(a) #TODO should be long conversation before use summary because it leaves out details
|
||||
_ = addNewMessage(a, "user", usermsg)
|
||||
prompt = generatePrompt_mistral_openorca(a, usermsg, thinkingmode)
|
||||
@show prompt
|
||||
respond = sendReceivePrompt(a, prompt)
|
||||
respond = split(respond, "<|im_end|>")[1]
|
||||
respond = replace(respond, "\n" => "")
|
||||
_ = addNewMessage(a, "assistant", respond)
|
||||
@show respond
|
||||
else
|
||||
respond = work(a, usermsg)
|
||||
end
|
||||
|
||||
respond = work(a, usermsg)
|
||||
|
||||
return respond
|
||||
end
|
||||
|
||||
#WORKING
|
||||
function work(a::agentReflex, usermsg::String)
|
||||
if a.thinkingMode == :new_thinking
|
||||
a.earlierConversation = conversationSummary(a)
|
||||
_ = addNewMessage(a, "user", usermsg)
|
||||
elseif a.thinkingMode == :continue_thinking
|
||||
_ = addNewMessage(a, "user", usermsg)
|
||||
a.thought *= "Obs $(a.thinkinground): $usermsg\n"
|
||||
else
|
||||
error("undefined condition thinkingmode = $thinkingmode")
|
||||
end
|
||||
|
||||
while true
|
||||
# plan
|
||||
a.thinkinground += 1
|
||||
@show a.thinkinground
|
||||
toolname = nothing
|
||||
toolinput = nothing
|
||||
plan = planning(a, prompt)
|
||||
@show plan
|
||||
# for
|
||||
# # execute
|
||||
# end
|
||||
# evaluate
|
||||
#
|
||||
end
|
||||
end
|
||||
|
||||
#WORKING
|
||||
function planning()
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant.
|
||||
Your job is to make a concise summary of user's text.
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
make a conversation summary.
|
||||
```jldoctest
|
||||
@@ -615,42 +820,88 @@ function makeSummary(a::T1, input::T2) where {T1<:agent, T2<:AbstractString}
|
||||
end
|
||||
|
||||
function chooseThinkingMode(a::T, usermsg::String) where {T<:agent}
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
{systemMsg}
|
||||
You have access to the following tools:
|
||||
{tools}
|
||||
Your need to determine now whether you will use tools or actions to answer the question.
|
||||
thinkingMode = nothing
|
||||
if a.thought != "nothing"
|
||||
thinkingMode = :continue_thinking
|
||||
else
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
{systemMsg}
|
||||
You have access to the following tools:
|
||||
{tools}
|
||||
Your need to determine now whether you will use tools or actions to answer the question.
|
||||
|
||||
You have the following choices:
|
||||
If you already know the answer or don't need tools or actions say, "{no}".
|
||||
If you need tools or actions to answer the question say, "{yes}".
|
||||
<|im_end|>
|
||||
You have the following choices:
|
||||
If you already know the answer or don't need tools or actions say, "{no}".
|
||||
If you need tools or actions to answer the question say, "{yes}".
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
toollines = ""
|
||||
for (toolname, v) in a.tools
|
||||
if toolname ∉ ["chatbox", "nothing"]
|
||||
toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
|
||||
toollines *= toolline
|
||||
"""
|
||||
toollines = ""
|
||||
for (toolname, v) in a.tools
|
||||
if toolname ∉ ["chatbox", "nothing"]
|
||||
toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
|
||||
toollines *= toolline
|
||||
end
|
||||
end
|
||||
prompt = replace(prompt, "{systemMsg}" => a.roles[a.role])
|
||||
prompt = replace(prompt, "{tools}" => toollines)
|
||||
prompt = replace(prompt, "{input}" => usermsg)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
willusetools = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
|
||||
thinkingMode = willusetools == "yes" ? :thinking : :no_thinking
|
||||
end
|
||||
prompt = replace(prompt, "{systemMsg}" => a.roles[a.role])
|
||||
prompt = replace(prompt, "{tools}" => toollines)
|
||||
prompt = replace(prompt, "{input}" => usermsg)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
willusetools = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
|
||||
thinkingMode = willusetools == "yes" ? :thinking : :no_thinking
|
||||
|
||||
return thinkingMode
|
||||
end
|
||||
|
||||
function chooseThinkingMode(a::agentReflex, usermsg::String)
|
||||
thinkingMode = nothing
|
||||
if a.thought != "nothing"
|
||||
thinkingMode = :continue_thinking
|
||||
else
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
{systemMsg}
|
||||
You have access to the following tools:
|
||||
{tools}
|
||||
Your need to determine now whether you will use tools or actions to answer the question.
|
||||
|
||||
You have the following choices:
|
||||
If you already know the answer or don't need tools or actions say, "{no}".
|
||||
If you need tools or actions to answer the question say, "{yes}".
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
toollines = ""
|
||||
for (toolname, v) in a.tools
|
||||
if toolname ∉ ["chatbox", "nothing"]
|
||||
toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
|
||||
toollines *= toolline
|
||||
end
|
||||
end
|
||||
prompt = replace(prompt, "{systemMsg}" => a.roles[a.role])
|
||||
prompt = replace(prompt, "{tools}" => toollines)
|
||||
prompt = replace(prompt, "{input}" => usermsg)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
willusetools = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
|
||||
thinkingMode = willusetools == "yes" ? :new_thinking : :no_thinking
|
||||
end
|
||||
|
||||
return thinkingMode
|
||||
end
|
||||
|
||||
function identifyUserIntention(a::T, usermsg::String) where {T<:agent}
|
||||
prompt =
|
||||
|
||||
Reference in New Issue
Block a user