This commit is contained in:
2023-12-16 03:58:46 +00:00
parent bd9ae8ad52
commit d202690286
3 changed files with 145 additions and 240 deletions

View File

@@ -292,11 +292,14 @@ function planner_mistral_openorca(a::agentReflex)
Objective: the objective you intend to do
Plan: first you should always think about the objective, the info you need and the info you have thoroughly then extract and devise a 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 make sure you gather all the required information.
<|im_end|>
$conversation
<|im_start|>assistant
Objective:
"""
# p.s.2 the last step should be about responding.
result = sendReceivePrompt(a, assistant_plan_prompt, max_tokens=1024, temperature=0.2)
@show raw_plan = result
x = split(result, "<|im_end|>")[1]
@@ -396,7 +399,6 @@ end
# return prompt
# end
function actor_mistral_openorca(a::agentReflex)
"""
general prompt format:
@@ -430,20 +432,16 @@ function actor_mistral_openorca(a::agentReflex)
prompt =
"""
<|im_start|>system
{role}
$(a.roles[a.role])
{tools}
{thinkingFormat}
$(a.thinkingFormat[:actor])
{context}
<|im_end|>
{shorttermMemory}
Thought $(a.step):
"""
prompt = replace(prompt, "{role}" => a.roles[a.role])
prompt = replace(prompt, "{thinkingFormat}" => a.thinkingFormat[:actor])
prompt = replace(prompt, "{step}" => a.step)
s = dictToString(a.memory[:shortterm], ["user:", "Plan 1:"])
s = dictToString(a.memory[:shortterm], skiplist=["user:", "Plan 1:"])
prompt = replace(prompt, "{shorttermMemory}" => s)
toolnames = ""
@@ -455,9 +453,13 @@ function actor_mistral_openorca(a::agentReflex)
end
prompt = replace(prompt, "{toolnames}" => toolnames)
prompt = replace(prompt, "{tools}" => "You have access to the following tools:\n$toollines")
conversation = messagesToString_nomark(a.messages, addressAIas="I")
context =
"""
Your talk with the user:
$conversation
{env state}
{longterm memory}
{plan}
@@ -471,6 +473,80 @@ function actor_mistral_openorca(a::agentReflex)
return prompt
end
# function actor_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}
# "
# """
# mark = "$(a.step)"
# prompt =
# """
# <|im_start|>system
# {role}
# {tools}
# {thinkingFormat}
# {context}
# <|im_end|>
# {shorttermMemory}
# Thought $(a.step):
# """
# prompt = replace(prompt, "{role}" => a.roles[a.role])
# prompt = replace(prompt, "{thinkingFormat}" => a.thinkingFormat[:actor])
# prompt = replace(prompt, "{step}" => a.step)
# s = dictToString(a.memory[:shortterm], skiplist=["user:", "Plan 1:"])
# prompt = replace(prompt, "{shorttermMemory}" => s)
# toolnames = ""
# toollines = ""
# for (toolname, v) in a.tools
# toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
# toollines *= toolline
# toolnames *= "$toolname, "
# end
# prompt = replace(prompt, "{toolnames}" => toolnames)
# prompt = replace(prompt, "{tools}" => "You have access to the following tools:\n$toollines")
# context =
# """
# {env state}
# {longterm memory}
# {plan}
# """
# # context = replace(context, "{earlierConversation}" => "My earlier talk with the user:\n$(a.earlierConversation)")
# context = replace(context, "{env state}" => "")
# context = replace(context, "{longterm memory}" => "")
# context = replace(context, "{plan}" => "My plan:\n$(a.memory[:shortterm]["Plan $(a.attempt):"])")
# prompt = replace(prompt, "{context}" => context)
# return prompt
# end
"""
@@ -526,7 +602,7 @@ function conversation(a::agentReflex, usermsg::String; attemptlimit::Int=3)
end
# if LLM using chatbox, use returning msg form chatbox as conversation response
if workstate == "chatbox" ?
if workstate == "chatbox"
#TODO paraphrase msg so that it is human friendlier word.
else
response = chat_mistral_openorca(a)
@@ -916,8 +992,6 @@ function actor(a::agentReflex)
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)")
#WORKING
else
error("undefined condition decision = $decision $(@__LINE__)")
end
@@ -1238,7 +1312,7 @@ julia> report = formulateUserresponse(agent, shorttermMemory)
```
"""
function formulateUserresponse(a)
stimulus = a.memory[:shortterm]["user:"]
stimulus = a.memory[:shortterm]["user:"] #BUG no "user" key
work = dictToString(a.memory[:shortterm], ["user:"])
@@ -1294,34 +1368,10 @@ julia> shorttermMemory = OrderedDict{String, Any}(
julia> decision = goNogo(agent)
"Yes"
```
"""
""" #WORKING add choice skip, if LLM already completed the step
function goNogo(a)
stimulus = a.memory[:shortterm]["user:"]
work = dictToString(a.memory[:shortterm], ["user:"])
# prompt =
# """
# <|im_start|>system
# Symbol meaning:
# Stimulus: the input user gives to you and you must response
# Plan: a plan
# Thought: your thought
# Act: the action you took
# Actinput: the input to the action
# Obs: the result of the action
# Stimulus:
# $stimulus
# Your work:
# $work
# From your work, you job is to decide what to do next by choosing one of the following choices:
# If you are ready to do the next step of the plan say, "{Yes}". And what is the rationale behind the decision?
# If you need to repeat the latest step say, "{No}". And what is the rationale behind the decision?
# If you are ready to formulate a final response to user original stimulus say, {formulateUserresponse}. And what is the rationale behind the decision?
# <|im_end|>
# """
# stimulus = a.memory[:shortterm]["user:"]
work = dictToString(a.memory[:shortterm])
prompt =
"""
@@ -1334,24 +1384,18 @@ function goNogo(a)
Actinput: the input to the action
Obs: the result of the action
Stimulus:
$stimulus
Your work:
$work
Your job is to check whether step $(a.step) of your work is completed according to the plan and choose only one of the following choices.
choice 1: If you are ready to do the next step of the plan say, "{Yes}". And what is the rationale behind the decision?
choice 2: If you need to repeat the latest step say, "{No}". And what is the rationale behind the decision?
choice 1: If you get what you intend to do and you are ready to do the next step of the plan say, "{Yes}". And what is the rationale behind the decision to do the next step?
choice 2: If you didn't get what you intend to do and you need to repeat the latest step say, "{No}". And what is the rationale behind the decision to repeat the latest step?
<|im_end|>
<|im_start|>assistant
"""
response = sendReceivePrompt(a, prompt)
decision = nothing
reason = nothing
if occursin("Yes", response)

View File

@@ -124,7 +124,7 @@ function agentReflex(
""",
:sommelier =>
"""
You are a sommelier at an online wine reseller who always help users choosing their wine from your inventory.
You are a sommelier at an online wine reseller who always help users choosing their wine from your store.
You don't know other people personal info previously.
""",
# :sommelier =>
@@ -163,7 +163,6 @@ function agentReflex(
Stimulus: the input user gives to you and you must respond
Plan: first you should always think about the stimulus, the info you need and the info you have thoroughly then extract and devise a step by step plan (pay attention to correct numeral calculation and commonsense).
p.s.1 each step should be a single action.
p.s.2 don't respond to the stimulus yet.
""",
:actor=>
"""
@@ -246,137 +245,6 @@ end
@kwdef mutable struct agentReact <: 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
end
function agentReact(
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 helpful 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

View File

@@ -4,7 +4,7 @@ export makeSummary, sendReceivePrompt, chunktext, extractStepFromPlan, checkTota
detectCharacters, findDetectedCharacter, extract_number, toolNameBeingCalled,
isUseTools, conversationSummary, checkReasonableness, replaceHeaders,
addShortMem!, splittext, dictToString, removeHeaders, keepOnlyKeys, experience,
messagesToString, removeTrailingCharacters
messagesToString, messagesToString_nomark, removeTrailingCharacters
using UUIDs, Dates, DataStructures
using CommUtils, GeneralUtils
@@ -624,6 +624,25 @@ function messagesToString(messages::AbstractVector{T}; addressAIas="assistant")
return conversation
end
""" Convert a vector of dict into 1-continous string.
Arguments:
vecofdict, a vector of dict
Return:
1-continous string
# Example
```jldoctest
julia> using ChatAgent
julia> agent = ChatAgent.agentReflex("Jene")
julia> agent.messages = [Dict(:role=> "user", :content=> "Hi there."),
Dict(:role=> "assistant", :content=> "Hello! How can I assist you today?"),]
julia> messagesToString(agent.messages)
"user: Hi there.\nassistant: Hello! How can I assist you today?\n"
```
"""
function messagesToString_nomark(messages::AbstractVector{T}; addressAIas="assistant") where {T<:AbstractDict}
conversation = ""
if length(messages)!= 0
@@ -648,6 +667,39 @@ function messagesToString_nomark(messages::AbstractVector{T}; addressAIas="assis
return conversation
end
function dictToString(shortMemory::T;
skiplist::Union{Array{String}, Array{Symbol}}=[""]) where {T<:AbstractDict}
s = ""
for (k, v) in shortMemory
if k skiplist
new_v = removeTrailingCharacters(v)
s1 = "$k $new_v\n"
s *= s1
end
end
return s
end
# function dictToString(dict::T;
# skiplist::Union{Array{String}, Array{Symbol}}=[]) where {T<:AbstractDict}
# s = ""
# for (k, v) in dict
# if k ∉ skiplist
# s1 = "$k $v"
# s *= s1
# # ensure a newline seperate each sentences
# if s[end] != "\n"
# s *= "\n"
# end
# end
# end
# return s
# end
""" Remove trailing characters from text.
Arguments:
@@ -840,65 +892,6 @@ function replaceHeaders(text::T, headers, step::Int) where {T<:AbstractString}
end
""" Convert short term memory into 1 continous string.
Arguments:
shortMemory = a short term memory of a ChatAgent's agent
skiplist = a list of keys in memory you want to skip
Return:
a short term memory in 1 countinuous string
# Example
```jldoctest
julia> shortMemory = OrderedDict(
"user:" => "Umm",
"Thought 1:" => "I like it.",
"Act 1:" => "chatbox",
"Actinput 1:" => "I get this one.",
)
julia> headers = ["user:"]
julia> dictToString(shortMemory, headers)
"Thought 1: I like it.\nAct 1: chatbox\nActinput 1: I get this one.\n"
```
"""
function shortMemoryToString(shortMemory::OrderedDict,
skiplist::Union{Array{String}, Array{Symbol}})
s = ""
for (k, v) in shortMemory
if k skiplist
s1 = "$k $v"
s *= s1
# ensure a newline seperate each sentences
if s[end] != "\n"
s *= "\n"
end
end
end
return s
end
function dictToString(dict::T,
skiplist::Union{Array{String}, Array{Symbol}}) where {T<:AbstractDict}
s = ""
for (k, v) in dict
if k skiplist
s1 = "$k $v"
s *= s1
# ensure a newline seperate each sentences
if s[end] != "\n"
s *= "\n"
end
end
end
return s
end
""" Remove headers of specific step from memory.
Arguments: