update
This commit is contained in:
@@ -232,7 +232,7 @@ function decisionMaker(a::T; recent::Integer=5)::Dict{Symbol, Any} where {T<:age
|
|||||||
|
|
||||||
systemmsg =
|
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:
|
Your goal includes:
|
||||||
1) Help the user select the best wines from your inventory that align with the user's preferences.
|
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.
|
- 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.
|
- 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.
|
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".
|
- 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
|
4) Action_input: input of the action
|
||||||
5) Mentioning_wine: Are you mentioning specific wine name to the user? Can be "Yes" or "No"
|
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)
|
checkinventory(a, actioninput)
|
||||||
elseif actionname == "PRESENTBOX"
|
elseif actionname == "PRESENTBOX"
|
||||||
x = """
|
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.
|
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.
|
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."
|
# 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.
|
# 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"
|
elseif actionname == "ENDCONVERSATION"
|
||||||
x = "Conclude the conversation, thanks the user then goodbye and inviting them to return next time."
|
x = "Conclude the conversation, thanks the user then goodbye and inviting them to return next time."
|
||||||
(result=x, errormsg=nothing, success=true)
|
(result=x, errormsg=nothing, success=true)
|
||||||
@@ -1016,7 +1021,7 @@ julia>
|
|||||||
function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::Function)
|
function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::Function)
|
||||||
systemmsg =
|
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 goal is: Recommend the best wines from your inventory that align with the user's preferences.
|
||||||
|
|
||||||
Your responsibility includes:
|
Your responsibility includes:
|
||||||
@@ -1189,7 +1194,7 @@ function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::St
|
|||||||
|
|
||||||
systemmsg =
|
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:
|
Your goal includes:
|
||||||
1) Help the user select the best wines from your inventory that align with the user's preferences
|
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
|
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"
|
inventoryquery = "$wineattributes_1, $wineattributes_2"
|
||||||
println("--> checkinventory input: $inventoryquery ", @__FILE__, " ", @__LINE__)
|
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],
|
push!(a.memory[:events],
|
||||||
eventdict(;
|
eventdict(;
|
||||||
|
|||||||
10
src/type.jl
10
src/type.jl
@@ -98,11 +98,15 @@ mutable struct sommelier <: agent
|
|||||||
# communication function
|
# communication function
|
||||||
text2textInstructLLM::Function
|
text2textInstructLLM::Function
|
||||||
executeSQL::Function
|
executeSQL::Function
|
||||||
|
querySQLVectorDB::Function
|
||||||
|
addSQLVectorDB::Function
|
||||||
end
|
end
|
||||||
|
|
||||||
function sommelier(
|
function sommelier(
|
||||||
text2textInstructLLM::Function,
|
text2textInstructLLM::Function,
|
||||||
executeSQL::Function
|
executeSQL::Function,
|
||||||
|
querySQLVectorDB::Function,
|
||||||
|
addSQLVectorDB::Function
|
||||||
;
|
;
|
||||||
name::String= "Assistant",
|
name::String= "Assistant",
|
||||||
id::String= string(uuid4()),
|
id::String= string(uuid4()),
|
||||||
@@ -143,7 +147,9 @@ function sommelier(
|
|||||||
chathistory,
|
chathistory,
|
||||||
memory,
|
memory,
|
||||||
text2textInstructLLM,
|
text2textInstructLLM,
|
||||||
executeSQL
|
executeSQL,
|
||||||
|
querySQLVectorDB,
|
||||||
|
addSQLVectorDB
|
||||||
)
|
)
|
||||||
|
|
||||||
return newAgent
|
return newAgent
|
||||||
|
|||||||
133
test/runtest.jl
133
test/runtest.jl
@@ -1,5 +1,5 @@
|
|||||||
using Revise # remove when this package is completed
|
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
|
using Base.Threads
|
||||||
|
|
||||||
# ---------------------------------------------- 100 --------------------------------------------- #
|
# ---------------------------------------------- 100 --------------------------------------------- #
|
||||||
@@ -55,20 +55,127 @@ function text2textInstructLLM(prompt::String)
|
|||||||
return response
|
return response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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
|
# Instantiate an agent
|
||||||
a = YiemAgent.sommelier(
|
a = YiemAgent.sommelier(
|
||||||
text2textInstructLLM,
|
text2textInstructLLM,
|
||||||
executeSQL;
|
executeSQL,
|
||||||
name="assistant",
|
querySQLVectorDB,
|
||||||
id="testingSessionID", # agent instance id
|
addSQLVectorDB;
|
||||||
|
name= "Janie",
|
||||||
|
id= sessionId, # agent instance id
|
||||||
)
|
)
|
||||||
|
|
||||||
function main()
|
function main()
|
||||||
userinput = "Hello, I would like a get a bottle of wine."
|
|
||||||
for i in 1:10
|
for i in 1:10
|
||||||
response = YiemAgent.conversation(a, Dict(:text=> userinput))
|
|
||||||
println("")
|
|
||||||
println("--> assistant response: \n", response)
|
|
||||||
userinput = ""
|
userinput = ""
|
||||||
for i in 1:3
|
for i in 1:3
|
||||||
if userinput == ""
|
if userinput == ""
|
||||||
@@ -79,6 +186,9 @@ function main()
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
response = YiemAgent.conversation(a, Dict(:text=> userinput))
|
||||||
|
println("")
|
||||||
|
println("--> assistant response: \n", response)
|
||||||
end
|
end
|
||||||
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.
|
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.
|
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.
|
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