update
This commit is contained in:
138
src/interface.jl
138
src/interface.jl
@@ -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)
|
||||
|
||||
134
src/type.jl
134
src/type.jl
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
113
src/utils.jl
113
src/utils.jl
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user