This commit is contained in:
narawat lamaiin
2024-08-25 11:50:50 +07:00
parent 2e90665fb6
commit 232202d1ca
4 changed files with 149 additions and 21 deletions

View File

@@ -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

View File

@@ -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(;

View File

@@ -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

View File

@@ -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
# Instantiate an agent function executeSQLVectorDB(sql)
a = YiemAgent.sommelier( DBconnection = LibPQ.Connection("host=192.168.88.12 port=5433 dbname=SQLVectorDB user=yiemtechnologies@gmail.com password=yiem@Postgres_0.0")
text2textInstructLLM, result = LibPQ.execute(DBconnection, sql)
executeSQL; close(DBconnection)
name="assistant", return result
id="testingSessionID", # agent instance id 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() 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.
""" """