update
This commit is contained in:
@@ -232,7 +232,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:age
|
||||
|
||||
systemmsg =
|
||||
"""
|
||||
You are a helpful assistant acting as a polite, website-based sommelier for an online wine store.
|
||||
Your name is $(a.name). You are a helpful assistant acting as a polite, website-based sommelier for Yiem's online wine store.
|
||||
Your goal includes:
|
||||
1) Help the user select the best wines from your inventory that align with the user's preferences.
|
||||
|
||||
@@ -273,7 +273,12 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:age
|
||||
- CHATBOX which you can use to generate conversation in order to communicate with the user. The input is your intentions for the dialogue. Be specific.
|
||||
- CHECKINVENTORY which you can use to check info about wine in your inventory. The input is a search term in verbal English.
|
||||
Good query example: black car, a stereo, 200 mile range, electric motor.
|
||||
- PRESENTBOX which you can use to introduce / suggest / recommend wines you just found in the database to the user. It is better than the CHATBOX function for presenting wines. The input is the names of wines to introduce.
|
||||
- PRESENTBOX which you can use to introduce / suggest / recommend wines you just found in the database to the user. It is better than the CHATBOX function for presenting wines.
|
||||
The input is instructions on how you want the presentation to be conducted.
|
||||
Here are some input example,
|
||||
"First, provide detailed introductions of the wines to help the user make an informed choice.
|
||||
Second, if there are multiple wines, offer a thorough comparison of each option, highlighting their differences.
|
||||
Third, explain the potential impact each option could bring to the user."
|
||||
- ENDCONVERSATION which you can use when you want to finish the conversation with the user. The input is "NA".
|
||||
4) Action_input: input of the action
|
||||
5) Mentioning_wine: Are you mentioning specific wine name to the user? Can be "Yes" or "No"
|
||||
@@ -951,7 +956,7 @@ function think(a::T)::NamedTuple{(:actionname, :result), Tuple{String, String}}
|
||||
checkinventory(a, actioninput)
|
||||
elseif actionname == "PRESENTBOX"
|
||||
x = """
|
||||
1) Provide detailed introductions of $actioninput to help the user make an informed choice.
|
||||
1) Provide detailed introductions of the wines to help the user make an informed choice.
|
||||
2) If there are multiple wines, offer a thorough comparison of each option, highlighting their differences.
|
||||
3) Explain the potential impact each option could bring to the user.
|
||||
"""
|
||||
@@ -959,7 +964,7 @@ function think(a::T)::NamedTuple{(:actionname, :result), Tuple{String, String}}
|
||||
# 1) Introduce $actioninput in details for the user to choose."
|
||||
# 2) Compare each option against the others in details and explain why each one is a suitable match for the user's specific needs.
|
||||
# """
|
||||
(result=x, errormsg=nothing, success=true)
|
||||
(result=actioninput, errormsg=nothing, success=true)
|
||||
elseif actionname == "ENDCONVERSATION"
|
||||
x = "Conclude the conversation, thanks the user then goodbye and inviting them to return next time."
|
||||
(result=x, errormsg=nothing, success=true)
|
||||
@@ -1016,7 +1021,7 @@ julia>
|
||||
function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::Function)
|
||||
systemmsg =
|
||||
"""
|
||||
You are a helpful assistant acting as a polite, website-based sommelier for an online wine store.
|
||||
Your name is "Jenie". You are a helpful assistant acting as a polite, website-based sommelier for an online wine store.
|
||||
Your goal is: Recommend the best wines from your inventory that align with the user's preferences.
|
||||
|
||||
Your responsibility includes:
|
||||
@@ -1189,7 +1194,7 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
|
||||
|
||||
systemmsg =
|
||||
"""
|
||||
You are a helpful assistant acting as a polite, website-based sommelier for an online wine store.
|
||||
Your name is $(a.name). You are a helpful assistant acting as a polite, website-based sommelier for Yiem's online wine store.
|
||||
Your goal includes:
|
||||
1) Help the user select the best wines from your inventory that align with the user's preferences
|
||||
2) Thanks the user when they don't need any further assistance and invite them to comeback next time
|
||||
|
||||
@@ -295,7 +295,9 @@ function checkinventory(a::T1, input::T2
|
||||
|
||||
inventoryquery = "$wineattributes_1, $wineattributes_2"
|
||||
println("--> checkinventory input: $inventoryquery ", @__FILE__, " ", @__LINE__)
|
||||
result = SQLLLM.query(inventoryquery, a.executeSQL, a.text2textInstructLLM)
|
||||
result = SQLLLM.query(inventoryquery, a.executeSQL, a.text2textInstructLLM,
|
||||
addSQLVectorDB=a.addSQLVectorDB,
|
||||
querySQLVectorDB=a.querySQLVectorDB)
|
||||
|
||||
push!(a.memory[:events],
|
||||
eventdict(;
|
||||
|
||||
10
src/type.jl
10
src/type.jl
@@ -98,11 +98,15 @@ mutable struct sommelier <: agent
|
||||
# communication function
|
||||
text2textInstructLLM::Function
|
||||
executeSQL::Function
|
||||
querySQLVectorDB::Function
|
||||
addSQLVectorDB::Function
|
||||
end
|
||||
|
||||
function sommelier(
|
||||
text2textInstructLLM::Function,
|
||||
executeSQL::Function
|
||||
executeSQL::Function,
|
||||
querySQLVectorDB::Function,
|
||||
addSQLVectorDB::Function
|
||||
;
|
||||
name::String= "Assistant",
|
||||
id::String= string(uuid4()),
|
||||
@@ -143,7 +147,9 @@ function sommelier(
|
||||
chathistory,
|
||||
memory,
|
||||
text2textInstructLLM,
|
||||
executeSQL
|
||||
executeSQL,
|
||||
querySQLVectorDB,
|
||||
addSQLVectorDB
|
||||
)
|
||||
|
||||
return newAgent
|
||||
|
||||
139
test/runtest.jl
139
test/runtest.jl
@@ -1,5 +1,5 @@
|
||||
using Revise # remove when this package is completed
|
||||
using YiemAgent, GeneralUtils, JSON3, MQTTClient, Dates, UUIDs, LibPQ
|
||||
using YiemAgent, GeneralUtils, JSON3, MQTTClient, Dates, UUIDs, LibPQ, Base64, DataFrames
|
||||
using Base.Threads
|
||||
|
||||
# ---------------------------------------------- 100 --------------------------------------------- #
|
||||
@@ -55,20 +55,127 @@ function text2textInstructLLM(prompt::String)
|
||||
return response
|
||||
end
|
||||
|
||||
# Instantiate an agent
|
||||
a = YiemAgent.sommelier(
|
||||
text2textInstructLLM,
|
||||
executeSQL;
|
||||
name="assistant",
|
||||
id="testingSessionID", # agent instance id
|
||||
function executeSQLVectorDB(sql)
|
||||
DBconnection = LibPQ.Connection("host=192.168.88.12 port=5433 dbname=SQLVectorDB user=yiemtechnologies@gmail.com password=yiem@Postgres_0.0")
|
||||
result = LibPQ.execute(DBconnection, sql)
|
||||
close(DBconnection)
|
||||
return result
|
||||
end
|
||||
|
||||
function addSQLVectorDB(state)
|
||||
# get embedding of the query
|
||||
query = [state[:thoughtHistory][:question]]
|
||||
msgMeta = GeneralUtils.generate_msgMeta(
|
||||
config[:externalservice][:text2textinstruct][:mqtttopic];
|
||||
msgPurpose= "embedding",
|
||||
senderName= "yiemagent",
|
||||
senderId= string(uuid4()),
|
||||
receiverName= "text2textinstruct",
|
||||
mqttBrokerAddress= config[:mqttServerInfo][:broker],
|
||||
mqttBrokerPort= config[:mqttServerInfo][:port],
|
||||
)
|
||||
|
||||
outgoingMsg = Dict(
|
||||
:msgMeta=> msgMeta,
|
||||
:payload=> Dict(
|
||||
:text=> query
|
||||
)
|
||||
)
|
||||
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg)
|
||||
embedding = response[:response][:embeddings][1]
|
||||
|
||||
# check whether there is close enough vector already store in vectorDB. if no, add, else skip
|
||||
sql =
|
||||
"""
|
||||
SELECT *, embedding <-> '$embedding' as distance
|
||||
FROM sql_statement_repository
|
||||
ORDER BY distance LIMIT 1;
|
||||
"""
|
||||
response = executeSQLVectorDB(sql)
|
||||
df = DataFrame(response)
|
||||
row, col = size(df)
|
||||
distance = row == 0 ? Inf : df[1, :distance]
|
||||
if row == 0 || distance > 10 # no close enough SQL stored in the database
|
||||
latestKey, _ = GeneralUtils.findHighestIndexKey(state[:thoughtHistory], :action_input)
|
||||
_sqlStatement = state[:thoughtHistory][latestKey]
|
||||
if occursin("SELECT", _sqlStatement) # make sure it is an SQL statement before adding into DB
|
||||
sqlStatementBase64 = base64encode(_sqlStatement)
|
||||
sqlStatement = replace(_sqlStatement, "'"=>"")
|
||||
sql =
|
||||
"""
|
||||
INSERT INTO sql_statement_repository (question, sql_statement, sql_statement_base64, embedding) VALUES ('$query', '$sqlStatement', '$sqlStatementBase64', '$embedding');
|
||||
"""
|
||||
_ = executeSQLVectorDB(sql)
|
||||
println("--> added new SQL statement to vectorDB ", @__FILE__, " ", @__LINE__)
|
||||
println(sqlStatement)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function querySQLVectorDB(state)
|
||||
|
||||
# provide similarSQL at the first time thinking only
|
||||
latestKey, _ = GeneralUtils.findHighestIndexKey(state[:thoughtHistory], :action_input)
|
||||
if latestKey === nothing
|
||||
# get embedding of the query
|
||||
query = [state[:thoughtHistory][:question]]
|
||||
msgMeta = GeneralUtils.generate_msgMeta(
|
||||
config[:externalservice][:text2textinstruct][:mqtttopic];
|
||||
msgPurpose= "embedding",
|
||||
senderName= "yiemagent",
|
||||
senderId= string(uuid4()),
|
||||
receiverName= "text2textinstruct",
|
||||
mqttBrokerAddress= config[:mqttServerInfo][:broker],
|
||||
mqttBrokerPort= config[:mqttServerInfo][:port],
|
||||
)
|
||||
|
||||
outgoingMsg = Dict(
|
||||
:msgMeta=> msgMeta,
|
||||
:payload=> Dict(
|
||||
:text=> query
|
||||
)
|
||||
)
|
||||
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg)
|
||||
embedding = response[:response][:embeddings][1]
|
||||
|
||||
# check whether there is close enough vector already store in vectorDB. if no, add, else skip
|
||||
sql =
|
||||
"""
|
||||
SELECT *, embedding <-> '$embedding' as distance
|
||||
FROM sql_statement_repository
|
||||
ORDER BY distance LIMIT 1;
|
||||
"""
|
||||
response = executeSQLVectorDB(sql)
|
||||
df = DataFrame(response)
|
||||
row, col = size(df)
|
||||
distance = row == 0 ? Inf : df[1, :distance]
|
||||
if row != 0 && distance < 100
|
||||
# if there is usable SQL, return it.
|
||||
sqlStatementBase64 = df[1, :sql_statement_base64]
|
||||
sqlStatement = String(base64decode(sqlStatementBase64))
|
||||
println("--> getting SQL statement from vectorDB ", @__FILE__, " ", @__LINE__)
|
||||
println(sqlStatement)
|
||||
return sqlStatement
|
||||
else
|
||||
return nothing
|
||||
end
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
|
||||
|
||||
# Instantiate an agent
|
||||
a = YiemAgent.sommelier(
|
||||
text2textInstructLLM,
|
||||
executeSQL,
|
||||
querySQLVectorDB,
|
||||
addSQLVectorDB;
|
||||
name= "Janie",
|
||||
id= sessionId, # agent instance id
|
||||
)
|
||||
|
||||
function main()
|
||||
userinput = "Hello, I would like a get a bottle of wine."
|
||||
for i in 1:10
|
||||
response = YiemAgent.conversation(a, Dict(:text=> userinput))
|
||||
println("")
|
||||
println("--> assistant response: \n", response)
|
||||
userinput = ""
|
||||
for i in 1:3
|
||||
if userinput == ""
|
||||
@@ -79,6 +186,9 @@ function main()
|
||||
break
|
||||
end
|
||||
end
|
||||
response = YiemAgent.conversation(a, Dict(:text=> userinput))
|
||||
println("")
|
||||
println("--> assistant response: \n", response)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -87,8 +197,13 @@ main()
|
||||
"""
|
||||
I'm joining a graduation party this evening. I want a bottle of dry white wine from the US. I'm ok with any price range.
|
||||
Well, the party is small casual with close friends and no food serving.
|
||||
I'm open to suggestion since I have no specific idea about wine.
|
||||
I'm open to suggestion since I have no specific idea.
|
||||
I'm ok with any region.
|
||||
|
||||
|
||||
|
||||
|
||||
The input is instructions on how you want the presentation to be conducted.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user