new version
This commit is contained in:
7
previousVersion/0.0.6/CondaPkg.toml
Executable file
7
previousVersion/0.0.6/CondaPkg.toml
Executable file
@@ -0,0 +1,7 @@
|
||||
channels = ["anaconda", "conda-forge", "pytorch"]
|
||||
|
||||
[deps]
|
||||
python = ">=3.8,<3.11"
|
||||
|
||||
[pip.deps]
|
||||
langchain = ""
|
||||
1171
previousVersion/0.0.6/Manifest.toml
Executable file
1171
previousVersion/0.0.6/Manifest.toml
Executable file
File diff suppressed because it is too large
Load Diff
17
previousVersion/0.0.6/Project.toml
Executable file
17
previousVersion/0.0.6/Project.toml
Executable file
@@ -0,0 +1,17 @@
|
||||
name = "ChatAgent"
|
||||
uuid = "cff63402-b71f-455f-804d-24489fc61e5e"
|
||||
authors = ["narawat <narawat@gmail.com>"]
|
||||
version = "0.1.0"
|
||||
|
||||
[deps]
|
||||
CommUtils = "646cbe82-3d4a-47b2-9440-2e80a472ca20"
|
||||
CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"
|
||||
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
|
||||
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||
GeneralUtils = "c6c72f09-b708-4ac8-ac7c-2084d70108fe"
|
||||
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
|
||||
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
|
||||
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
|
||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
URIs = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
|
||||
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||
101
previousVersion/0.0.6/src/ChatAgent.jl
Executable file
101
previousVersion/0.0.6/src/ChatAgent.jl
Executable file
@@ -0,0 +1,101 @@
|
||||
module ChatAgent
|
||||
|
||||
# export agent, addNewMessage, clearMessage
|
||||
|
||||
|
||||
""" Order by dependencies of each file. The 1st included file must not depend on any other
|
||||
files and each file can only depend on the file included before it.
|
||||
"""
|
||||
|
||||
include("type.jl")
|
||||
using .type
|
||||
|
||||
include("utils.jl")
|
||||
using .utils
|
||||
|
||||
include("llmfunction.jl")
|
||||
using .llmfunction
|
||||
|
||||
include("interface.jl")
|
||||
using .interface
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
""" version 0.0.5
|
||||
Todo:
|
||||
[WORKING] add formulateUserRespond to AI tools
|
||||
|
||||
Change from version: 0.0.4
|
||||
-
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end # module ChatAgent
|
||||
1627
previousVersion/0.0.6/src/interface.jl
Executable file
1627
previousVersion/0.0.6/src/interface.jl
Executable file
File diff suppressed because it is too large
Load Diff
557
previousVersion/0.0.6/src/llmfunction.jl
Normal file
557
previousVersion/0.0.6/src/llmfunction.jl
Normal file
@@ -0,0 +1,557 @@
|
||||
module llmfunction
|
||||
|
||||
export wikisearch, winestock
|
||||
|
||||
using HTTP, JSON3, URIs, Random
|
||||
using GeneralUtils
|
||||
using ..type, ..utils
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
"""
|
||||
Search wikipedia.
|
||||
|
||||
Arguments:
|
||||
query (string): The query to search for
|
||||
|
||||
Returns:
|
||||
string: The search result text from wikipedia
|
||||
```jldoctest
|
||||
julia> using HTTP, JSON3
|
||||
julia> result = wikisearch("AMD")
|
||||
"Advanced Micro Devices, Inc., commonly abbreviated as AMD, is an ..."
|
||||
```
|
||||
"""
|
||||
function wikisearch(a::agentReflex, phrase::T) where {T<:AbstractString}
|
||||
phrase = phrase[1] == " " ? phrase[2:end] : phrase
|
||||
# prepare input phrase
|
||||
if occursin("\"", phrase)
|
||||
phrase = GeneralUtils.getStringBetweenCharacters(phrase, "\"", "\"")
|
||||
end
|
||||
phrase = replace(phrase, "\n"=>"")
|
||||
|
||||
url = "https://en.wikipedia.org/w/api.php?action=query&format=json&prop=extracts&titles=$(replace(phrase, " " => "%20"))&exintro=1&explaintext=1"
|
||||
@show url
|
||||
response = HTTP.get(url)
|
||||
json_data = JSON3.read(String(response.body))
|
||||
page_id = first(keys(json_data["query"]["pages"]))
|
||||
if page_id == "-1"
|
||||
return "Sorry, I couldn't find any Wikipedia page for the given phrase."
|
||||
end
|
||||
|
||||
result = nothing
|
||||
try
|
||||
result = json_data["query"]["pages"][page_id]["extract"]
|
||||
wiki = result
|
||||
@show wiki
|
||||
catch
|
||||
result = "No info available for your search query."
|
||||
end
|
||||
|
||||
# if result == ""
|
||||
# result = "No info available for your search query."
|
||||
# else
|
||||
# result = makeSummary(a, result)
|
||||
# end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
# function winestock(a::agentReflex, phrase::T) where {T<:AbstractString}
|
||||
# result =
|
||||
# """
|
||||
# I found the following wines
|
||||
# {
|
||||
# 1: {
|
||||
# Chateau: Louis Latou,
|
||||
# name: Corton-Charlamagne,
|
||||
# grape variety: chardonnay,
|
||||
# year: 2014,
|
||||
# price: 55 USD,
|
||||
# stock ID: ws-114,
|
||||
# Description: Corton-Charlemagne 2018 is a powerful, complex wine. Its nose is intense, with notes of white stone fruits such as white peach, fresh hazelnut, vanilla, and almond paste. The wine is full-bodied for the palate, and the vanilla is complemented by aromas of fresh almond and lime blossom. The experience ends with a very fine aromatic aftertaste that has subtle saline notes.,
|
||||
# },
|
||||
# 2: {
|
||||
# Chateau: Beaucastel,
|
||||
# name: Malbec Argentino,
|
||||
# grape variety: riesling,
|
||||
# year: 2016,
|
||||
# price: 39 USD,
|
||||
# stock ID: ed-23,
|
||||
# Description: The quintessence of Château de Beaucastel, Hommage à Jacques Perrin delights us every year, and the 2019 vintage is no exception. To the eye it offers a splendid deep red color, verging on black. Full of power and supremely elegant, the nose is of magnificent aromatic complexity with notes of black fruit and spices that offer all the characteristic expression of Mourvèdre. Perfectly balanced by an incredible freshness, the mouth is eminently elegant with intense and complex aromas of great subtlety, a full, refined texture, subtle tannins of great finesse, and infinite length. A great classic Hommage à Jacques Perrin.,
|
||||
# }
|
||||
# }
|
||||
# """
|
||||
# return result
|
||||
# end
|
||||
|
||||
|
||||
#WORKING
|
||||
"""
|
||||
|
||||
Arguments:
|
||||
|
||||
Return:
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
```jldoctest
|
||||
julia> using ChatAgent, CommUtils
|
||||
julia> agent = ChatAgent.agentReflex("Jene")
|
||||
julia> shorttermMemory = OrderedDict{String, Any}(
|
||||
"user" => "What's the latest AMD GPU?",
|
||||
"Plan 1:" => " To answer this question, I will need to search for the latest AMD GPU using the wikisearch tool.\n",
|
||||
"Act 1:" => " wikisearch\n",
|
||||
"Actinput 1:" => " amd gpu latest\n",
|
||||
"Obs 1:" => "No info available for your search query.",
|
||||
"Act 2:" => " wikisearch\n",
|
||||
"Actinput 2:" => " amd graphics card latest\n",
|
||||
"Obs 2:" => "No info available for your search query.")
|
||||
julia> guideline = "\nEvaluation Guideline:\n1. Check if the user's question has been understood correctly.\n2. Evaluate the tasks taken to provide the information requested by the user.\n3. Assess whether the correct tools were used for the task.\n4. Determine if the user's request was successfully fulfilled.\n5. Identify any potential improvements or alternative approaches that could be used in the future.\n\nThe response should include:\n1. A clear understanding of the user's question.\n2. The tasks taken to provide the information requested by the user.\n3. An evaluation of whether the correct tools were used for the task.\n4. A confirmation or explanation if the user's request was successfully fulfilled.\n5. Any potential improvements or alternative approaches that could be used in the future."
|
||||
julia> score = grading(agent, guideline, shorttermMemory)
|
||||
```
|
||||
"""
|
||||
function winestock(a::agentReflex, actorResult::NamedTuple)
|
||||
query = JSON3.write(actorResult[:toolinput])
|
||||
@show query
|
||||
prompt =
|
||||
"""
|
||||
<|system|>
|
||||
<About yourself>
|
||||
Your are a helpful assistant.
|
||||
</About yourself>
|
||||
<You have the following conversion table>
|
||||
Database table name by wine type:
|
||||
Red = table for wine type "red"
|
||||
White = table for wine type "white"
|
||||
Sparkling = table for wine type "sparkling"
|
||||
Rose = table for wine type "rose"
|
||||
Dessert = table for wine type "dessert"
|
||||
Fortified = table for wine type "fortified"
|
||||
Intensity level:
|
||||
intensity = 1, light bodied
|
||||
intensity = 2, light-medium bodied
|
||||
intensity = 3, medium bodied
|
||||
intensity = 4, medium-full bodied
|
||||
intensity = 5, full bodied
|
||||
Sweetness level:
|
||||
sweetness = 1, dry
|
||||
sweetness = 2, off-dry
|
||||
sweetness = 3, semi-sweet
|
||||
sweetness = 4, sweet
|
||||
sweetness = 5, very sweet
|
||||
Tannin level:
|
||||
tannin = 1, low tannin
|
||||
tannin = 2, low-medium tannin
|
||||
tannin = 3, medium tannin
|
||||
tannin = 4, medium-high tannin
|
||||
tannin = 5, high tannin
|
||||
Acidity level:
|
||||
acidity = 1, low acidity
|
||||
acidity = 2, low-medium acidity
|
||||
acidity = 3, medium acidity
|
||||
acidity = 4, medium-high acidity
|
||||
acidity = 5, high acidity
|
||||
</You have the following conversion table>
|
||||
<Your job>
|
||||
Consult the conversion table then write a specific SQL command using only available info from a JSON-format query.
|
||||
List of keywords not allowed in SQL: ["BETWEEN", "--", "WHEN", "IN"]
|
||||
|
||||
Use the following format:
|
||||
Info map: based on conversion table, map the info in query to appropriate variables
|
||||
SQL: write a specific SQL command
|
||||
</Your job>
|
||||
<Example 1>
|
||||
query: {\"wine type\": \"white\", \"wine characteristics\": \"full-bodied | off-dry | low acidity | low to medium tannin\", \"price\": {\"max\": \"50\"}}
|
||||
Think: 1) low to medium tannin is not explicitly stated, but assuming it falls within the range of low-medium tannin.
|
||||
Info map: {\"wine type\": \"white\", \"intensity\": 5, \"sweetness\": 2, \"tannin\": 2, \"acidity\": 1, \"price\": 50}
|
||||
SQL: SELECT * FROM White WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 2 AND price <= 50;
|
||||
</Example 1>
|
||||
<Example 2>
|
||||
query: {\"wine characteristics\":\"low-bodied | a little sweet | low-medium tannin\",\"price\":\"22 USD\",\"occasion\":\"anniversary\",\"wine type\":\"Rose\",\"food\":\"American dishes\"}
|
||||
Think: 1) medium sweet is not explicitly stated, but assuming it falls within the range of dry and off-dry.
|
||||
Info map: {\"wine type\": \"Rose\", \"intensity\": 1, \"sweetness\": 3, \"tannin\": 2, \"acidity\": 3, \"price\": 22, \"food\":\"American dishes\"}
|
||||
SQL: SELECT * FROM Rose WHERE intensity = 1 AND tannin = 2 AND (sweetness = 1 OR sweetness = 2) AND price <= 22 AND food = American;
|
||||
</Example 2>
|
||||
</s>
|
||||
<|query|>
|
||||
$query
|
||||
</s>
|
||||
<|assistant|>
|
||||
"""
|
||||
|
||||
# prompt =
|
||||
# """
|
||||
# <|system|>
|
||||
# <About yourself>
|
||||
# Your are a helpful assistant.
|
||||
# </About yourself>
|
||||
# <You have the following conversion table>
|
||||
# Intensity level:
|
||||
# intensity = 1, light bodied
|
||||
# intensity = 2, light-medium bodied
|
||||
# intensity = 3, medium bodied
|
||||
# intensity = 4, medium-full bodied
|
||||
# intensity = 5, full bodied
|
||||
# Sweetness level:
|
||||
# sweetness = 1, dry
|
||||
# sweetness = 2, off-dry
|
||||
# sweetness = 3, semi-sweet
|
||||
# sweetness = 4, sweet
|
||||
# sweetness = 5, very sweet
|
||||
# Tannin level:
|
||||
# tannin = 1, low tannin
|
||||
# tannin = 2, low-medium tannin
|
||||
# tannin = 3, medium tannin
|
||||
# tannin = 4, medium-high tannin
|
||||
# tannin = 5, high tannin
|
||||
# Acidity level:
|
||||
# acidity = 1, low acidity
|
||||
# acidity = 2, low-medium acidity
|
||||
# acidity = 3, medium acidity
|
||||
# acidity = 4, medium-high acidity
|
||||
# acidity = 5, high acidity
|
||||
# </You have the following conversion table>
|
||||
# <Your job>
|
||||
# Consult the conversion table then write a specific SQL command using the info from the user.
|
||||
# List of keywords not allowed in SQL: ["BETWEEN", "--", "WHEN", "IN"]
|
||||
|
||||
# Use the following format:
|
||||
# Think: How do I map the info in the query to conversion table
|
||||
# Info map: based on conversion table, map the info in query to appropriate variables
|
||||
# SQL: write a specific SQL command
|
||||
# </Your job>
|
||||
# <Example 1>
|
||||
# Think: 1) low to medium tannin is not explicitly stated, but assuming it falls within the range of low-medium tannin.
|
||||
# Info map: {\"wine type\": \"white\", \"intensity\": 5, \"sweetness\": 2, \"tannin\": 2, \"acidity\": 1, \"price\": {\"max\": \"50 USD\"}}
|
||||
# SQL: SELECT * FROM White WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 2 AND price <= 50;
|
||||
# </Example 1>
|
||||
# <Example 2>
|
||||
# Think: 1) medium sweet is not explicitly stated, but assuming it falls within the range of dry and off-dry.
|
||||
# Info map: {\"wine type\": \"Rose\", \"intensity\": 1, \"sweetness\": 3, \"tannin\": 2, \"acidity\": 3, \"price\": {\"max\": \"22 USD\"}}
|
||||
# SQL: SELECT * FROM Rose WHERE intensity = 1 AND tannin = 2 AND (sweetness = 1 OR sweetness = 2) AND price <= 22;
|
||||
# </Example 2>
|
||||
# </s>
|
||||
# <|user's info|>
|
||||
# $(actorResult[:selfaware])
|
||||
# </s>
|
||||
# <|assistant|>
|
||||
# """
|
||||
|
||||
# prompt =
|
||||
# """
|
||||
# <s>[INST]
|
||||
# <About yourself>
|
||||
# Your are a helpful assistant.
|
||||
# </About yourself>
|
||||
# <You have the following conversion table>
|
||||
# Database table name by wine type:
|
||||
# Red = table for wine type "red"
|
||||
# White = table for wine type "white"
|
||||
# Sparkling = table for wine type "sparkling"
|
||||
# Rose = table for wine type "rose"
|
||||
# Dessert = table for wine type "dessert"
|
||||
# Fortified = table for wine type "fortified"
|
||||
# Intensity level:
|
||||
# intensity = 1, light bodied
|
||||
# intensity = 2, light-medium bodied
|
||||
# intensity = 3, medium bodied
|
||||
# intensity = 4, medium-full bodied
|
||||
# intensity = 5, full bodied
|
||||
# Sweetness level:
|
||||
# sweetness = 1, dry
|
||||
# sweetness = 2, off-dry
|
||||
# sweetness = 3, semi-sweet
|
||||
# sweetness = 4, sweet
|
||||
# sweetness = 5, very sweet
|
||||
# Tannin level:
|
||||
# tannin = 1, low tannin
|
||||
# tannin = 2, low-medium tannin
|
||||
# tannin = 3, medium tannin
|
||||
# tannin = 4, medium-high tannin
|
||||
# tannin = 5, high tannin
|
||||
# Acidity level:
|
||||
# acidity = 1, low acidity
|
||||
# acidity = 2, low-medium acidity
|
||||
# acidity = 3, medium acidity
|
||||
# acidity = 4, medium-high acidity
|
||||
# acidity = 5, high acidity
|
||||
# </You have the following conversion table>
|
||||
|
||||
# <query info>
|
||||
# The user is planning a wedding party and needs a bottle of wine for the occasion.\n - Thai dishes will be served at the wedding party.\n - The ambient temperature at the serving location is around 22 degrees Celsius.\n - The user prefers a red wine with medium-bodied, dry and low to medium tannin.\n - The user prefers a medium acidity level for the wine.\n - The user's budget for the bottle of wine is around 15 USD.\n\n Info match:\n - Occasion: wedding party\n - Type of food: Thai dishes\n - Ambient temperature: around 22 degrees Celsius\n - Preferred type of wine: red\n - Wine sweetness level: dry\n - Wine intensity level: medium-bodied\n - Wine tannin level: low to medium\n - Wine acidity level: medium\n - Wine price range: around 15 USD\n\n
|
||||
# <query info>
|
||||
|
||||
# Consult the conversion table then write a specific SQL command using the query info to search a database table.
|
||||
# List of keywords not allowed in SQL: ["BETWEEN", "--", "WHEN", "IN"]
|
||||
|
||||
# Use the following format:
|
||||
# Think: How do I map the info in the query to conversion table
|
||||
# Info map: based on conversion table, map the info in query to appropriate variables
|
||||
# SQL: write a specific SQL command
|
||||
# [/INST]
|
||||
# Think: 1) low to medium tannin is not explicitly stated, but assuming it falls within the range of low-medium tannin.
|
||||
# Info map: {\"wine type\": \"white\", \"intensity\": 5, \"sweetness\": 2, \"tannin\": 2, \"acidity\": 1, \"price\": {\"max\": \"50 USD\"}}
|
||||
# SQL: SELECT * FROM White WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 2 AND price <= 50;
|
||||
# </s>
|
||||
# [INST]
|
||||
# <query info>
|
||||
# $(actorResult[:selfaware])
|
||||
# <query info>
|
||||
|
||||
# Consult the conversion table then write a specific SQL command using the query info.
|
||||
# List of keywords not allowed in SQL: ["BETWEEN", "--", "WHEN", "IN"]
|
||||
# [/INST]
|
||||
# """
|
||||
|
||||
println("")
|
||||
@show db_prompt = prompt
|
||||
_sql = nothing
|
||||
while true
|
||||
_sql = sendReceivePrompt(a, prompt, max_tokens=256, temperature=0.2,
|
||||
stopword=["/n/n", "END", "End", "Obs", "<|", "</"])
|
||||
_sql = split(_sql, ";")[1] * ";"
|
||||
@show _sql
|
||||
# check for valid SQL command
|
||||
check_1 = occursin("BETWEEN", _sql)
|
||||
check_2 = occursin("--", _sql)
|
||||
check_3 = occursin("IN", _sql)
|
||||
|
||||
if check_1 == false && check_2 == false && check_3 == false
|
||||
break
|
||||
end
|
||||
println("invalid SQL command")
|
||||
end
|
||||
|
||||
_sql = split(_sql, "SQL:")[end]
|
||||
println("")
|
||||
@show db_sql = replace(_sql, '\n'=>"")
|
||||
|
||||
# remove any blank character in front of a string
|
||||
newsql = nothing
|
||||
for i in eachindex(_sql)
|
||||
if _sql[i] != ' '
|
||||
newsql = _sql[i:end]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
body = newsql
|
||||
uri = URI(scheme="http", host="192.168.88.12", port="9010", path="/sql", userinfo="root:root")
|
||||
r = HTTP.request("POST", uri, ["Accept" => "application/json", "NS"=>"yiem", "DB"=>"Blossom_wines"], body)
|
||||
println("")
|
||||
@show r
|
||||
a.memory[:r] = r
|
||||
result = copy(JSON3.read(r.body))
|
||||
|
||||
|
||||
wines = shuffle(result[1][:result]) # shuffle in case there are more than 1 result
|
||||
println("")
|
||||
@show wines
|
||||
|
||||
# choose only 2 wines
|
||||
if length(wines) > 2
|
||||
println("$(length(wines)) wines found")
|
||||
wines = wines[1:2]
|
||||
end
|
||||
|
||||
result = nothing
|
||||
if length(wines) == 0
|
||||
result =
|
||||
"""
|
||||
No wine match my search query.
|
||||
"""
|
||||
else
|
||||
# write wines dictionary in to string
|
||||
wines_str = ""
|
||||
for (i, wine) in enumerate(wines)
|
||||
winename = wine[:wine_name]
|
||||
wines_str *= "$i: $(JSON3.write(wines[i])),"
|
||||
end
|
||||
|
||||
result =
|
||||
"""
|
||||
I found the following wines in our stock:
|
||||
{
|
||||
$wines_str
|
||||
}
|
||||
"""
|
||||
end
|
||||
|
||||
@show result
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
# function winestock(a::agentReflex, query::Dict)
|
||||
# query = JSON3.write(query)
|
||||
# @show query
|
||||
# prompt =
|
||||
# """
|
||||
# <|system|>
|
||||
# <About yourself>
|
||||
# Your are a helpful assistant.
|
||||
# </About yourself>
|
||||
|
||||
# <You have the following conversion table>
|
||||
# Intensity level:
|
||||
# intensity = 1, light bodied
|
||||
# intensity = 2, light-medium bodied
|
||||
# intensity = 3, medium bodied
|
||||
# intensity = 4, medium-full bodied
|
||||
# intensity = 5, full bodied
|
||||
# Sweetness level:
|
||||
# sweetness = 1, dry
|
||||
# sweetness = 2, off-dry
|
||||
# sweetness = 3, semi-sweet
|
||||
# sweetness = 4, sweet
|
||||
# sweetness = 5, very sweet
|
||||
# Tannin level:
|
||||
# tannin = 1, low tannin
|
||||
# tannin = 2, low-medium tannin
|
||||
# tannin = 3, medium tannin
|
||||
# tannin = 4, medium-high tannin
|
||||
# tannin = 5, high tannin
|
||||
# Acidity level:
|
||||
# acidity = 1, low acidity
|
||||
# acidity = 2, low-medium acidity
|
||||
# acidity = 3, medium acidity
|
||||
# acidity = 4, medium-high acidity
|
||||
# acidity = 5, high acidity
|
||||
# </You have the following conversion table>
|
||||
# <Your job>
|
||||
# Consult the conversion table then write a specific SQL command using only available info from a JSON-format query.
|
||||
# List of keywords not allowed in SQL: ["BETWEEN", "--", "WHEN", "IN"]
|
||||
# </Your job>
|
||||
# <Example 1>
|
||||
# query: {\"wine type\": \"white\", \"wine characteristics\": \"full-bodied | off-dry | low acidity | medium tannin\", \"price\": {\"max\": \"50\"}}
|
||||
# assistant: SELECT * FROM White WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 3 AND price <= 50;
|
||||
# </Example 1>
|
||||
# <Example 2>
|
||||
# query: {\"wine characteristics\":\"low-bodied | semi-sweet | low-medium tannin\",\"price\":\"22 USD\",\"occasion\":\"anniversary\",\"wine type\":\"Rose\",\"food\":\"American dishes\"}
|
||||
# assistant: SELECT * FROM Rose WHERE intensity = 1 AND sweetness = 3 AND (tannin = 2 OR tannin = 3) AND price <= 22;
|
||||
# </Example 2>
|
||||
# </s>
|
||||
# <|query|>
|
||||
# $query
|
||||
# </s>
|
||||
# <|assistant|>
|
||||
# """
|
||||
# println("")
|
||||
# @show db_prompt = prompt
|
||||
# _sql = nothing
|
||||
# while true
|
||||
# _sql = sendReceivePrompt(a, prompt, max_tokens=256, temperature=0.2,
|
||||
# stopword=["/n/n", "END", "End", "Obs", "<|", "</"])
|
||||
# _sql = split(_sql, ";")[1] * ";"
|
||||
# # check for valid SQL command
|
||||
# check_1 = occursin("BETWEEN", _sql)
|
||||
# check_2 = occursin("--", _sql)
|
||||
# check_3 = occursin("IN", _sql)
|
||||
|
||||
# if check_1 == false && check_2 == false && check_3 == false
|
||||
# break
|
||||
# end
|
||||
# println("illegal SQL command")
|
||||
# end
|
||||
# println("")
|
||||
# @show db_sql = replace(_sql, '\n'=>"")
|
||||
|
||||
# # remove any blank character in front of a string
|
||||
# newsql = nothing
|
||||
# for i in eachindex(_sql)
|
||||
# if _sql[i] != ' '
|
||||
# newsql = _sql[i:end]
|
||||
# break
|
||||
# end
|
||||
# end
|
||||
|
||||
|
||||
# body = newsql
|
||||
# uri = URI(scheme="http", host="192.168.88.12", port="9010", path="/sql", userinfo="root:root")
|
||||
# r = HTTP.request("POST", uri, ["Accept" => "application/json", "NS"=>"yiem", "DB"=>"Blossom_wines"], body)
|
||||
# println("")
|
||||
# @show r
|
||||
# a.memory[:r] = r
|
||||
# result = copy(JSON3.read(r.body))
|
||||
|
||||
|
||||
# wines = shuffle(result[1][:result]) # shuffle in case there are more than 1 result
|
||||
# println("")
|
||||
# @show wines
|
||||
|
||||
# # choose only 2 wines
|
||||
# if length(wines) > 2
|
||||
# println("$(length(wines)) wines found")
|
||||
# wines = wines[1:2]
|
||||
# end
|
||||
|
||||
# result = nothing
|
||||
# if length(wines) == 0
|
||||
# result =
|
||||
# """
|
||||
# Wine not found.
|
||||
# """
|
||||
# else
|
||||
# # write wines dictionary in to string
|
||||
# wines_str = ""
|
||||
# for (i, wine) in enumerate(wines)
|
||||
# winename = wine[:wine_name]
|
||||
# wines_str *= "$i: $(JSON3.write(wines[i])),"
|
||||
# end
|
||||
|
||||
# result =
|
||||
# """
|
||||
# I found the following wines in our stock:
|
||||
# {
|
||||
# $wines_str
|
||||
# }
|
||||
# """
|
||||
# end
|
||||
|
||||
# @show result
|
||||
|
||||
# return result
|
||||
# end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end # end module
|
||||
335
previousVersion/0.0.6/src/type.jl
Normal file
335
previousVersion/0.0.6/src/type.jl
Normal file
@@ -0,0 +1,335 @@
|
||||
module type
|
||||
|
||||
export agent, agentReflex
|
||||
|
||||
using Dates, UUIDs, DataStructures
|
||||
using CommUtils
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
|
||||
abstract type agent end
|
||||
|
||||
""" A LLM agent with self reflect capabilities.
|
||||
|
||||
Example:
|
||||
```jldoctest
|
||||
julia> using ChatAgent
|
||||
julia> mqttClientSpec = (
|
||||
clientName= "someclient", # name of this client
|
||||
clientID= "$(uuid4())",
|
||||
broker= "mqtt.yiem.ai",
|
||||
pubtopic= (imgAI="img/api/v0.0.1/gpu/request",
|
||||
txtAI="txt/api/v0.1.0/gpu/request"), # this is where LLM server located
|
||||
subtopic= (imgAI="agent/api/v0.1.0/img/respond",
|
||||
txtAI="agent/api/v0.1.0/txt/respond"), # this is where this agent located
|
||||
keepalive= 30,
|
||||
)
|
||||
julia> msgMeta = Dict(
|
||||
:msgPurpose=> "updateStatus",
|
||||
:from=> "agent",
|
||||
:to=> "llmAI",
|
||||
:requestrespond=> "request",
|
||||
:sendto=> "", # destination topic
|
||||
:replyTo=> "agent/api/v0.1.0/txt/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())",
|
||||
)
|
||||
julia> tools=Dict(
|
||||
:chatbox=>Dict(
|
||||
:name => "chatbox",
|
||||
:description => "Useful only for when you need to ask the user for more info or context. Do not ask the user their own question.",
|
||||
:input => "Input should be a text.",
|
||||
:output => "" ,
|
||||
:func => nothing,
|
||||
),
|
||||
:wikisearch=>Dict(
|
||||
:name => "wikisearch",
|
||||
:description => "Useful for when you need to search an encyclopedia.",
|
||||
:input => "Input is keywords and not a question.",
|
||||
:output => "",
|
||||
:func => ChatAgent.wikisearch, # put function here
|
||||
),
|
||||
:winestock=>Dict(
|
||||
:name => "wineStock",
|
||||
:description => "useful for when you need to search your wine stock by wine description, price, name or ID.",
|
||||
:input => "Input is a search query.",
|
||||
:output => "Output are Wine name, description, price and ID" ,
|
||||
:func => ChatAgent.winestock,
|
||||
),
|
||||
)
|
||||
julia> agent = ChatAgent.agentReflex(
|
||||
"Jene",
|
||||
role=:assistant,
|
||||
mqttClientSpec=mqttClientSpec,
|
||||
msgMeta=msgMeta,
|
||||
tools=tools
|
||||
)
|
||||
```
|
||||
"""
|
||||
@kwdef mutable struct agentReflex <: agent
|
||||
availableRole::AbstractVector = ["system", "user", "assistant"]
|
||||
agentName::String = "Jene" # ex. Jene
|
||||
maxUserMsg::Int = 30
|
||||
earlierConversation::String = "N/A" # 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}}()
|
||||
tools::Union{Dict, Nothing} = nothing
|
||||
newplan::Bool = false # if true, new plan will be generated
|
||||
attemptlimit::Int = 5 # thinking round limit
|
||||
attempt::Int = 1 # attempted number
|
||||
task::Int = 1 # task number
|
||||
env::AbstractString = "N/A"
|
||||
thinkingFormat::Union{Dict, Nothing} = nothing
|
||||
roleSpecificInstruction::Union{Dict, Nothing} = nothing
|
||||
memory::Dict{Symbol, Any} = Dict(
|
||||
:shortterm=> OrderedDict{String, Any}(),
|
||||
:longterm=> OrderedDict{String, Any}(),
|
||||
:log=> OrderedDict{String, Any}(), # span from user stimulus -> multiples attempts -> final respond
|
||||
)
|
||||
|
||||
# LLM function related
|
||||
winestockResult = ""
|
||||
end
|
||||
|
||||
function agentReflex(
|
||||
agentName::String;
|
||||
mqttClientSpec::NamedTuple=(
|
||||
clientName= "someclient", # name of this client
|
||||
clientID= "$(uuid4())",
|
||||
broker= "mqtt.yiem.ai",
|
||||
pubtopic= (imgAI="img/api/v0.0.1/gpu/request",
|
||||
txtAI="txt/api/v0.1.0/gpu/request"),
|
||||
subtopic= (imgAI="agent/api/v0.1.0/img/respond",
|
||||
txtAI="agent/api/v0.1.0/txt/respond"),
|
||||
keepalive= 30,),
|
||||
role::Symbol=:assistant,
|
||||
roles::Dict=Dict(
|
||||
:assistant =>
|
||||
"""
|
||||
You are a helpful assistant.
|
||||
""",
|
||||
:sommelier =>
|
||||
"""
|
||||
You are a helpful sommelier at a wine retailer. You helps users choosing their wine from your inventory.
|
||||
You don't know other people personal info previously.
|
||||
""",
|
||||
),
|
||||
roleSpecificInstruction::Dict=Dict(
|
||||
:assistant => "",
|
||||
# :sommelier =>
|
||||
# """
|
||||
# Required info you need for wine recommendation:
|
||||
# - occasion: ask the user
|
||||
# - type of food that will be served with wine: ask the user
|
||||
# - type of wine (we have Rose, White, Red, Rose and Sparkling): ask the user
|
||||
# - wine sweetness level (dry to very sweet)
|
||||
# - wine intensity level (light to full bodied)
|
||||
# - wine tannin level (low to high)
|
||||
# - wine acidity level (low to high)
|
||||
# - wine price range: ask the user
|
||||
# - wines we have in stock: use winestock tool
|
||||
# """
|
||||
:sommelier =>
|
||||
"""
|
||||
Request the user’s input for the following info initially, and use alternative sources of information only if they are unable to provide it:
|
||||
- occasion
|
||||
- type of food that will be served with wine
|
||||
- type of wine (we have Rose, White, Red, Rose and Sparkling)
|
||||
- wine sweetness level (dry to very sweet)
|
||||
- wine intensity level (light to full bodied)
|
||||
- wine tannin level (low to high)
|
||||
- wine acidity level (low to high)
|
||||
- wine price range: ask the user
|
||||
- wines we have in stock (use winestock tool)
|
||||
"""
|
||||
),
|
||||
|
||||
thinkingFormat::Dict=Dict(
|
||||
:react=>
|
||||
"""
|
||||
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!""",
|
||||
:planner=>
|
||||
"""
|
||||
Use the following format:
|
||||
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.
|
||||
""",
|
||||
:actor=>
|
||||
"""
|
||||
<Your job>
|
||||
Use the following format:
|
||||
Thought: based on the plan and the recap of the plan, what to do? (pay attention to correct numeral calculation and commonsense).
|
||||
Act: an action to take based on your thought, must be one of [{toolnames}]
|
||||
Actinput: your input to the action based on your thought (pay attention to the tool's input)
|
||||
Obs: observed result of the action
|
||||
|
||||
P.S.1 ask the user one by one question.
|
||||
P.S.2 ask the user what you want to know if you didn't ask yet.
|
||||
</Your job>
|
||||
""",
|
||||
:actorOriginal=>
|
||||
"""
|
||||
Use the following format:
|
||||
Thought: you should always think about do you have all the required info and what to do according to step {step} of the plan and the info you have (pay attention to correct numeral calculation and commonsense).
|
||||
Act: the action to take 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
|
||||
""",
|
||||
),
|
||||
tools::Dict=Dict(
|
||||
:chatbox=>Dict(
|
||||
:name => "chatbox",
|
||||
:description => "Useful for when you need to communicate with the user.",
|
||||
:input => "Input should be a conversation to the user.",
|
||||
:output => "" ,
|
||||
:func => nothing,
|
||||
),
|
||||
# :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
|
||||
# ),
|
||||
# :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=> "agent/api/v0.1.0/txt/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 = agentReflex()
|
||||
newAgent.availableRole = availableRole
|
||||
newAgent.maxUserMsg = maxUserMsg
|
||||
newAgent.mqttClient = CommUtils.mqttClient(mqttClientSpec)
|
||||
newAgent.msgMeta = msgMeta
|
||||
newAgent.tools = tools
|
||||
newAgent.role = role
|
||||
newAgent.roles = roles
|
||||
newAgent.thinkingFormat = thinkingFormat
|
||||
newAgent.roleSpecificInstruction = roleSpecificInstruction
|
||||
newAgent.agentName = agentName
|
||||
|
||||
return newAgent
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end # end module
|
||||
|
||||
|
||||
|
||||
996
previousVersion/0.0.6/src/utils.jl
Normal file
996
previousVersion/0.0.6/src/utils.jl
Normal file
@@ -0,0 +1,996 @@
|
||||
module utils
|
||||
|
||||
export sendReceivePrompt, chunktext, extractStepFromPlan, checkTotalTaskInPlan,
|
||||
detectCharacters, findDetectedCharacter, extract_number, toolNameBeingCalled,
|
||||
isUsePlans, conversationSummary, checkReasonableness, replaceHeaders,
|
||||
addShortMem!, splittext, dictToString, removeHeaders, keepOnlyKeys, experience,
|
||||
messagesToString, messagesToString_nomark, removeTrailingCharacters, shortMemLatestTask
|
||||
|
||||
using UUIDs, Dates, DataStructures
|
||||
using CommUtils, GeneralUtils
|
||||
using ..type
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
"""
|
||||
Send a msg to registered mqtt topic within mqttClient.
|
||||
|
||||
```jldoctest
|
||||
julia> using JSON3, UUIDs, Dates, FileIO, CommUtils, ChatAgent
|
||||
julia> newAgent = ChatAgent.agentReact(
|
||||
"Jene",
|
||||
mqttClientSpec,
|
||||
role=:assistant_react,
|
||||
msgMeta=msgMeta
|
||||
)
|
||||
```
|
||||
"""
|
||||
function sendReceivePrompt(a::T, prompt::String; max_tokens=256, timeout::Int=120,
|
||||
temperature::AbstractFloat=0.2, stopword=[], seed=nothing) where {T<:agent}
|
||||
a.msgMeta[:msgId] = "$(uuid4())" # new msg id for each msg
|
||||
msg = Dict(
|
||||
:msgMeta=> a.msgMeta,
|
||||
:txt=> prompt,
|
||||
:max_tokens=> max_tokens,
|
||||
:temperature=> temperature,
|
||||
:stopword=> stopword,
|
||||
:seed=> seed,
|
||||
)
|
||||
payloadChannel = Channel(1)
|
||||
|
||||
# send prompt
|
||||
CommUtils.request(a.mqttClient, msg)
|
||||
starttime = Dates.now()
|
||||
result = nothing
|
||||
|
||||
while true
|
||||
timepass = (Dates.now() - starttime).value / 1000.0
|
||||
CommUtils.mqttRun(a.mqttClient, payloadChannel)
|
||||
if isready(payloadChannel)
|
||||
topic, payload = take!(payloadChannel)
|
||||
if payload[:msgMeta][:repondToMsgId] == msg[:msgMeta][:msgId]
|
||||
result = haskey(payload, :txt) ? payload[:txt] : nothing
|
||||
break
|
||||
end
|
||||
elseif timepass <= timeout
|
||||
# skip, within waiting period
|
||||
elseif timepass > timeout
|
||||
println("sendReceivePrompt timeout $timepass/$timeout")
|
||||
result = nothing
|
||||
break
|
||||
else
|
||||
error("undefined condition. timepass=$timepass timeout=$timeout $(@__LINE__)")
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Detect given characters. Output is a list of named tuple of detected char.
|
||||
|
||||
```jldoctest
|
||||
julia> text = "I like to eat apples and use utensils."
|
||||
julia> characters = ["eat", "use", "i"]
|
||||
julia> result = detectCharacters(text, characters)
|
||||
4-element Vector{Any}:
|
||||
(char = "i", start = 4, stop = 4)
|
||||
(char = "eat", start = 11, stop = 13)
|
||||
(char = "use", start = 26, stop = 28)
|
||||
(char = "i", start = 35, stop = 35)
|
||||
```
|
||||
"""
|
||||
function detectCharacters(text::T1, characters::Vector{T2}) where {T1<:AbstractString, T2<:AbstractString}
|
||||
result = []
|
||||
for i in eachindex(text)
|
||||
for char in characters
|
||||
l = length(char)
|
||||
char_startInd = i
|
||||
char_endInd = i+l-1 # -1 because Julia use inclusive index
|
||||
|
||||
if char_endInd > length(text)
|
||||
# skip
|
||||
else
|
||||
try # some time StringIndexError: invalid index [535], valid nearby indices [534]=>'é', [536]=>' '
|
||||
if text[char_startInd: char_endInd] == char
|
||||
push!(result, (char=char, start=char_startInd, stop=char_endInd))
|
||||
end
|
||||
catch
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Chunk a text into smaller pieces by header.
|
||||
```jldoctest
|
||||
julia> using ChatAgent
|
||||
julia> text = "Plan: First, we need to find out what kind of wine the user wants."
|
||||
julia> headers = ChatAgent.detectCharacters(text, ["Nope", "sick", "First", "user", "Then", ])
|
||||
3-element Vector{Any}:
|
||||
(char = "First", start = 7, stop = 11)
|
||||
(char = "user", start = 56, stop = 59)
|
||||
(char = "Then", start = 102, stop = 105)
|
||||
julia> chunkedtext = ChatAgent.chunktext(text, headers)
|
||||
OrderedDict{String, String} with 3 entries:
|
||||
"Act 1:" => " wikisearch"
|
||||
"Actinput 1:" => " latest AMD GPU"
|
||||
"Thought 1:" => " I should always think about..."
|
||||
```
|
||||
"""
|
||||
function chunktext(text::T1, headers::T2) where {T1<:AbstractString, T2<:AbstractVector}
|
||||
result = OrderedDict{String, Any}()
|
||||
|
||||
for (i, v) in enumerate(headers)
|
||||
if i < length(headers)
|
||||
nextheader = headers[i+1]
|
||||
body = text[v[:stop]+1: nextheader[:start]-1]
|
||||
# push!(result, (header=v[:char], body=body))
|
||||
result[v[:char]] = body
|
||||
else
|
||||
body = text[v[:stop]+1: end]
|
||||
# push!(result, (header=v[:char], body=body))
|
||||
result[v[:char]] = body
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
function extractStepFromPlan(a::agent, plan::T, step::Int) where {T<:AbstractString}
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant.
|
||||
Your job is to extract step $step in the user plan.
|
||||
|
||||
Use the following format only:
|
||||
{copy the step and put it here}
|
||||
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
$plan
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
|
||||
response = sendReceivePrompt(a, prompt)
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
function checkTotalTaskInPlan(a::agent)
|
||||
headers = []
|
||||
|
||||
for (k, v) in a.memory[:shortterm]
|
||||
push!(headers, k)
|
||||
end
|
||||
|
||||
# Plan will have number e.g. Plan 3: so I need a way to detect latest Plan
|
||||
header = nothing
|
||||
for i in reverse(headers)
|
||||
if occursin("Plan", i)
|
||||
header = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
p = a.memory[:shortterm][header]
|
||||
plan = "Plan: $p"
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant.
|
||||
Your job is to determine how many steps in a user plan.
|
||||
|
||||
Use the following format to answer:
|
||||
Total step number is {}
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
$plan
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
response = sendReceivePrompt(a, prompt)
|
||||
result = extract_number(response)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
Find a given character from a vector of named tuple.
|
||||
Output is character location index inside detectedCharacters
|
||||
|
||||
```jldoctest
|
||||
julia a = [ (char = "i", start = 4, stop = 4)
|
||||
(char = "eat", start = 11, stop = 13)
|
||||
(char = "use", start = 26, stop = 28)
|
||||
(char = "i", start = 35, stop = 35) ]
|
||||
julia> findDetectedCharacter(a, "i")
|
||||
[1, 4]
|
||||
```
|
||||
"""
|
||||
function findDetectedCharacter(detectedCharacters, character)
|
||||
allchar = [i[1] for i in detectedCharacters]
|
||||
return findall(isequal.(allchar, character))
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function extract_number(text::T) where {T<:AbstractString}
|
||||
regex = r"\d+" # regular expression to match one or more digits
|
||||
match = Base.match(regex, text) # find the first match in the text
|
||||
if match !== nothing
|
||||
number = parse(Int, match.match)
|
||||
return number
|
||||
else
|
||||
error("No number found in the text $(@__LINE__)")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Extract toolname from text.
|
||||
```jldoctest
|
||||
julia> text = " internetsearch\n"
|
||||
julia> tools = Dict(
|
||||
:internetsearch=>Dict(
|
||||
:name => "internetsearch",
|
||||
:description => "Useful for when you need to search the Internet",
|
||||
:input => "Input should be a search query.",
|
||||
:output => "",
|
||||
# :func => internetsearch # function
|
||||
),
|
||||
:chatbox=>Dict(
|
||||
:name => "chatbox",
|
||||
:description => "Useful for when you need to ask a customer what you need to know or to talk with them.",
|
||||
:input => "Input should be a conversation to customer.",
|
||||
:output => "" ,
|
||||
),
|
||||
)
|
||||
julia> toolname = toolNameBeingCalled(text, tools)
|
||||
```
|
||||
"""
|
||||
function toolNameBeingCalled(text::T, tools::Dict) where {T<:AbstractString}
|
||||
toolNameBeingCalled = nothing
|
||||
for (k, v) in tools
|
||||
toolname = String(k)
|
||||
if contains(text, toolname)
|
||||
toolNameBeingCalled = toolname
|
||||
break
|
||||
end
|
||||
end
|
||||
return toolNameBeingCalled
|
||||
end
|
||||
|
||||
# function chooseThinkingMode(a::agentReflex, usermsg::String)
|
||||
# thinkingmode = nothing
|
||||
# if length(a.memory[:log]) != 0
|
||||
# thinkingmode = :continue_thinking
|
||||
# else
|
||||
# prompt =
|
||||
# """
|
||||
# <|im_start|>system
|
||||
# {systemMsg}
|
||||
# You always use tools if there is a chance to impove your response.
|
||||
# You have access to the following tools:
|
||||
# {tools}
|
||||
# Your job is to determine whether you will use tools or actions to response.
|
||||
|
||||
# Choose one of the following choices:
|
||||
# Choice 1: If you don't need to use tools or actions to response to the stimulus say, "{no}".
|
||||
# Choice 2: If you think the user want to get wine say, "{yes}".
|
||||
# <|im_end|>
|
||||
|
||||
# <|im_start|>user
|
||||
# {input}
|
||||
# <|im_end|>
|
||||
# <|im_start|>assistant
|
||||
|
||||
# """
|
||||
# toollines = ""
|
||||
# for (toolname, v) in a.tools
|
||||
# if toolname ∉ ["chatbox", "nothing"]
|
||||
# toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
|
||||
# toollines *= toolline
|
||||
# end
|
||||
# end
|
||||
# prompt = replace(prompt, "{systemMsg}" => a.roles[a.role])
|
||||
# prompt = replace(prompt, "{tools}" => toollines)
|
||||
# prompt = replace(prompt, "{input}" => usermsg)
|
||||
# result = sendReceivePrompt(a, prompt)
|
||||
# willusetools = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
|
||||
# thinkingmode = willusetools == "yes" ? :new_thinking : :no_thinking
|
||||
# end
|
||||
|
||||
# return thinkingmode
|
||||
# end
|
||||
|
||||
|
||||
|
||||
""" Determine from a user message whether an assistant need to use tools.
|
||||
|
||||
Arguments:
|
||||
a, one of ChatAgent's agent.
|
||||
|
||||
Return:
|
||||
1. true/false # is LLM going to use tools
|
||||
2. objective # what LLM going to do
|
||||
"""
|
||||
function isUsePlans(a::agentReflex)
|
||||
toollines = ""
|
||||
for (toolname, v) in a.tools
|
||||
if toolname ∉ ["chatbox"] # LLM will always use chatbox
|
||||
toolline = "$toolname is $(v[:description])\n"
|
||||
toollines *= toolline
|
||||
end
|
||||
end
|
||||
|
||||
conversation = messagesToString(a.messages)
|
||||
|
||||
aboutYourself =
|
||||
"""
|
||||
Your name is $(a.agentName)
|
||||
$(a.roles[a.role])
|
||||
"""
|
||||
|
||||
prompt =
|
||||
"""
|
||||
<|system|>
|
||||
<About yourself>
|
||||
$aboutYourself
|
||||
</About yourself>
|
||||
<You have access to the following tools>
|
||||
$toollines
|
||||
</You have access to the following tools>
|
||||
<Your earlier conversation with the user>
|
||||
$conversation
|
||||
</Your earlier conversation with the user>
|
||||
<Your job>
|
||||
Your job is to decide whether you need think thoroughly or use tools in order to respond to the user.
|
||||
Use the following format:
|
||||
Thought: Do you need to think thoroughly or use tools before responding to the user?
|
||||
</Your job>
|
||||
<Example 1>
|
||||
user: Hello!. How are you?
|
||||
assistant: The user is greeting me, I don't need to think about it.
|
||||
</Example 1>
|
||||
<Example 2>
|
||||
user: "What's tomorrow weather like?"
|
||||
assistant: I will need to use weather tools to check for tomorrow's temperature.
|
||||
</Example 2>
|
||||
</s>
|
||||
<|assistant|>
|
||||
"""
|
||||
isuseplan = false
|
||||
@show a.role
|
||||
if length(a.memory[:shortterm]) != 0
|
||||
isuseplan = true
|
||||
elseif a.role == :assistant
|
||||
isuseplan = false
|
||||
else
|
||||
# if LLM mentions any tools, use Plan/Thought/Act loop
|
||||
response = sendReceivePrompt(a, prompt, temperature=0.2, max_tokens=64, stopword=["<|", "</"])
|
||||
response = split(response, "<|")[1]
|
||||
if occursin("yes", String(response))
|
||||
isuseplan = true
|
||||
else
|
||||
for (toolname, v) in a.tools
|
||||
if occursin(toolname, String(response))
|
||||
isuseplan = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return isuseplan
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
make a conversation summary.
|
||||
```jldoctest
|
||||
julia> conversation = [
|
||||
Dict(:role=> "user", :content=> "I would like to get a bottle of wine", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "What kind of Thai dishes are you having?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "It a pad thai.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "Is there any special occasion for this event?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "We'll hold a wedding party at the beach.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "What is your preferred type of wine?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "I like dry white wine with medium tanins.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "What is your preferred price range for this bottle of wine?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "lower than 50 dollars.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "Based on your preferences and our stock, I recommend the following two wines for you:
|
||||
1. Pierre Girardin \"Murgers des Dents de Chien\" - Saint-Aubin 1er Cru (17 USD)
|
||||
2. Etienne Sauzet'Les Perrieres' - Puligny Montrachet Premier Cru (22 USD)
|
||||
The first wine, Pierre Girardin \"Murgers des Dents de Chien\" - Saint-Aubin 1er Cru, is a great choice for its affordable price and refreshing taste.
|
||||
It pairs well with Thai dishes and will be perfect for your beach wedding party.
|
||||
The second wine, Etienne Sauzet'Les Perrieres' - Puligny Montrachet Premier Cru, offers a more complex flavor profile and slightly higher price point, but still remains within your budget.
|
||||
Both wines are suitable for serving at 22 C temperature.", :timestamp=> Dates.now()),
|
||||
]
|
||||
|
||||
julia> summary = conversationSummary(conversation)
|
||||
```
|
||||
"""
|
||||
function conversationSummary(a::T) where {T<:agent}
|
||||
conversation = messagesToString_nomark(a.messages, addressAIas="I")
|
||||
prompt =
|
||||
"""
|
||||
<|system|>
|
||||
Your conversation with the user:
|
||||
$conversation
|
||||
|
||||
Your job is to paraphrase a conversation from your perspective.
|
||||
You must refers to yourself by "I" in the summary.
|
||||
<|/s|>
|
||||
<|assistant|>
|
||||
Paraphrase:
|
||||
"""
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
summary = split(result, "<|/s|>")[1]
|
||||
summary =
|
||||
return summary
|
||||
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)
|
||||
"<|im_start|>user: Hi there.\n<|im_end|><|im_start|>assistant: Hello! How can I assist you today?\n<|im_end|>"
|
||||
```
|
||||
"""
|
||||
function messagesToString(messages::AbstractVector{T}; addressAIas="assistant") where {T<:AbstractDict}
|
||||
conversation = ""
|
||||
if length(messages)!= 0
|
||||
for msg in messages
|
||||
role = msg[:role]
|
||||
content = msg[:content]
|
||||
|
||||
nouse = 0
|
||||
for i in reverse(content)
|
||||
if i == '\n' || i == ' '
|
||||
nouse += 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if role == "user"
|
||||
conversation *= "<|$role|>\n $(content[1:end-nouse])\n</s>"
|
||||
elseif role == "assistant"
|
||||
conversation *= "<|$addressAIas|>\n $(content[1:end-nouse])\n</s>"
|
||||
else
|
||||
error("undefied condition role = $role $(@__LINE__)")
|
||||
end
|
||||
end
|
||||
else
|
||||
conversation = "N/A"
|
||||
end
|
||||
|
||||
return conversation
|
||||
end
|
||||
# function messagesToString(messages::AbstractVector{T}; addressAIas="assistant") where {T<:AbstractDict}
|
||||
# conversation = ""
|
||||
# if length(messages)!= 0
|
||||
# for msg in messages
|
||||
# role = msg[:role]
|
||||
# content = msg[:content]
|
||||
|
||||
# nouse = 0
|
||||
# for i in reverse(content)
|
||||
# if i == '\n' || i == ' '
|
||||
# nouse += 1
|
||||
# else
|
||||
# break
|
||||
# end
|
||||
# end
|
||||
|
||||
# if role == "user"
|
||||
# conversation *= "<|im_start|>$role: $(content[1:end-nouse])\n<|im_end|>"
|
||||
# elseif role == "assistant"
|
||||
# conversation *= "<|im_start|>$addressAIas: $(content[1:end-nouse])\n<|im_end|>"
|
||||
# else
|
||||
# error("undefied condition role = $role $(@__LINE__)")
|
||||
# end
|
||||
# end
|
||||
# else
|
||||
# conversation = "N/A"
|
||||
# end
|
||||
|
||||
# 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
|
||||
for msg in messages
|
||||
role = msg[:role]
|
||||
content = msg[:content]
|
||||
|
||||
content = removeTrailingCharacters(content)
|
||||
|
||||
if role == "user"
|
||||
conversation *= "$role: $content\n"
|
||||
elseif role == "assistant"
|
||||
conversation *= "$addressAIas: $content\n"
|
||||
else
|
||||
error("undefied condition role = $role $(@__LINE__)")
|
||||
end
|
||||
end
|
||||
else
|
||||
conversation = "N/A"
|
||||
end
|
||||
|
||||
return conversation
|
||||
end
|
||||
|
||||
|
||||
function dictToString(shortMemory::T;
|
||||
skiplist::Union{Vector{String}, Vector{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:
|
||||
text, text you want to remove trailing characters
|
||||
charTobeRemoved, a list of characters to be removed
|
||||
|
||||
Return:
|
||||
text with specified trailing characters removed
|
||||
|
||||
Example:
|
||||
```jldoctest
|
||||
julia> text = "Hello! How can I assist you today?\n\n "
|
||||
julia> removelist = ['\n', ' ',]
|
||||
julia> removeTrailingCharacters(text, charTobeRemoved=removelist)
|
||||
"Hello! How can I assist you today?"
|
||||
```
|
||||
"""
|
||||
function removeTrailingCharacters(text; charTobeRemoved::AbstractVector{T}=['\n', ' ',]) where {T<:Char}
|
||||
nouse = 0
|
||||
for i in reverse(text)
|
||||
if i ∈ charTobeRemoved
|
||||
nouse += 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return text[1:end-nouse]
|
||||
end
|
||||
|
||||
|
||||
#TODO
|
||||
function checkReasonableness(userMsg::String, context::String, tools)
|
||||
# Ref: https://www.youtube.com/watch?v=XV4IBaZqbps
|
||||
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant. Your job is to check the reasonableness of user assignments.
|
||||
If the user assignment can be answered given the tools available say, "This is a reasonable assignment".
|
||||
If the user assignment cannot be answered then provide some feedback to the user that may improve
|
||||
their assignment.
|
||||
|
||||
Here is the context for the assignment:
|
||||
{context}
|
||||
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{assignment}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
|
||||
context = "You have access to the following tools:
|
||||
WineStock: useful for when you need to find info about wine by matching your description, price, name or ID. Input should be a search query with as much details as possible."
|
||||
prompt = replace(prompt, "{assignment}" => userMsg)
|
||||
prompt = replace(prompt, "{context}" => context)
|
||||
|
||||
output_py = llm(
|
||||
prompt,
|
||||
max_tokens=512,
|
||||
temperature=0.1,
|
||||
# top_p=top_p,
|
||||
echo=false,
|
||||
stop=["</response>", "<<END>>", ],
|
||||
)
|
||||
_output_jl = pyconvert(Dict, output_py);
|
||||
output = pyconvert(Dict, _output_jl["choices"][1]);
|
||||
output["text"]
|
||||
|
||||
end
|
||||
|
||||
|
||||
""" Add chunked text to a short term memory of a chat agent
|
||||
|
||||
Arguments:
|
||||
shortMem = short memory of a chat agent,
|
||||
chunkedtext = a dict contains text
|
||||
|
||||
Return: no return
|
||||
|
||||
Example:
|
||||
```jldoctest
|
||||
julia> chunkedtext = OrderedDict{String, String}(
|
||||
"Thought 1:" => " I should always think about...",
|
||||
"Act 1:" => " wikisearch",
|
||||
"Actinput 1:" => " latest AMD GPU",)
|
||||
julia> shortMem = OrderedDict{String, Any}()
|
||||
julia> addShortMem!(shortMem, chunkedtext)
|
||||
OrderedDict{String, Any} with 3 entries:
|
||||
"Thought 1:" => " I should always think about..."
|
||||
"Act 1:" => " wikisearch"
|
||||
"Actinput 1:" => " latest AMD GPU"
|
||||
```
|
||||
"""
|
||||
function addShortMem!(shortMem::OrderedDict{String, Any}, chunkedtext::T) where {T<:AbstractDict}
|
||||
for (k, v) in chunkedtext
|
||||
shortMem[k] = v
|
||||
end
|
||||
return shortMem
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
""" Split text using all keywords in a list. Start spliting from rightmost of the text.
|
||||
|
||||
Arguments:
|
||||
text = a text you want to split
|
||||
list = a list of keywords you want to split
|
||||
|
||||
Return:
|
||||
a leftmost text after split
|
||||
|
||||
Example:
|
||||
```jldoctest
|
||||
julia> text = "Consider the type of food, occasion and temperature at the serving location."
|
||||
julia> list = ["at", "and"]
|
||||
"Consider the type of food, occasion "
|
||||
```
|
||||
"""
|
||||
function splittext(text, list)
|
||||
newtext = text
|
||||
for i in list
|
||||
newtext = split(newtext, i)[1]
|
||||
end
|
||||
return newtext
|
||||
end
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Add step number to header in a text
|
||||
"""
|
||||
function addStepNumber(text::T, headers, step::Int) where {T<:AbstractString}
|
||||
newtext = text
|
||||
for i in headers
|
||||
if occursin(i[:char], newtext)
|
||||
new = replace(i[:char], ":"=> " $step:")
|
||||
newtext = replace(newtext, i[:char]=>new )
|
||||
end
|
||||
end
|
||||
return newtext
|
||||
end
|
||||
function addStepNumber(text::T, headers, step::Int, substep::Int) where {T<:AbstractString}
|
||||
newtext = text
|
||||
for i in headers
|
||||
if occursin(i[:char], newtext)
|
||||
new = replace(i[:char], ":"=> " $step-$substep:")
|
||||
newtext = replace(newtext, i[:char]=>new )
|
||||
end
|
||||
end
|
||||
return newtext
|
||||
end
|
||||
|
||||
|
||||
""" Add step number to header in a text
|
||||
|
||||
Arguments:
|
||||
text = a text you want to split
|
||||
headers = a list of keywords you want to add step and substep to
|
||||
step = a number you want to add
|
||||
|
||||
Return:
|
||||
a leftmost text after split
|
||||
|
||||
Example:
|
||||
```jldoctest
|
||||
julia> text = "Consider the type of food, occasion and temperature at the serving location."
|
||||
julia> headers = ["Thought", "Act"]
|
||||
|
||||
```
|
||||
"""
|
||||
function replaceHeaders(text::T, headers, step::Int) where {T<:AbstractString}
|
||||
newtext = text
|
||||
for i in headers
|
||||
header = i[1:end-1] # not include ":"
|
||||
if occursin(header, newtext)
|
||||
startind = findfirst(header, newtext)[1]
|
||||
stopind = findnext(":", newtext, startind+1)[end]
|
||||
word = newtext[startind: stopind]
|
||||
newword = "$header $step:"
|
||||
newtext = replace(newtext, word=> newword)
|
||||
if header == "Thought:"
|
||||
error(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return newtext
|
||||
end
|
||||
|
||||
|
||||
""" Remove headers of specific step from memory.
|
||||
|
||||
Arguments:
|
||||
shortMemory = a short term memory of a ChatAgent's agent
|
||||
skipHeaders = a list of keys in memory you want to skip
|
||||
step = a step number you want to remove
|
||||
|
||||
Return:
|
||||
a short term memory
|
||||
|
||||
Example:
|
||||
```jldoctest
|
||||
julia> shortMemory = OrderedDict(
|
||||
"user:" => "May I try this one?",
|
||||
"Plan 1:" => "testing a small portion of icecream",
|
||||
"Thought 1:" => "I like it.",
|
||||
"Act 1:" => "chatbox",
|
||||
"Actinput 1:" => "I get this one.",
|
||||
"Plan 2:" => "I'm meeting my wife this afternoon",
|
||||
"Thought 2:" => "I also want it for my wife",
|
||||
"Act 2:" => "chatbox",
|
||||
"Actinput 2:" => "I would like to get 2 more",
|
||||
)
|
||||
julia> skipHeaders = ["Plan"]
|
||||
julia> step = 2
|
||||
julia> removeHeaders(shortMemory, step, skipHeaders)
|
||||
OrderedDict(
|
||||
"user:" => "May I try this one?",
|
||||
"Plan 1:" => "testing a small portion of icecream",
|
||||
"Thought 1:" => "I like it.",
|
||||
"Act 1:" => "chatbox",
|
||||
"Actinput 1:" => "I get this one.",
|
||||
"Plan 2:" => "I'm meeting my wife this afternoon",
|
||||
)
|
||||
```
|
||||
"""
|
||||
function removeHeaders(shortMemory::OrderedDict, step::Int,
|
||||
skipHeaders::Union{Array{String}, Array{Symbol}, Nothing}=nothing)
|
||||
|
||||
newdict = similar(shortMemory)
|
||||
|
||||
for (k, v) in shortMemory
|
||||
if occursin("$step", k)
|
||||
if skipHeaders !== nothing
|
||||
for i in skipHeaders
|
||||
if occursin(i, k)
|
||||
newdict[k] = v
|
||||
else
|
||||
# skip, not copy
|
||||
end
|
||||
end
|
||||
else
|
||||
# no copy
|
||||
end
|
||||
else
|
||||
newdict[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
return newdict
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
""" Keep only specified keys in a dictionary. All non-specified keys will be removed.
|
||||
|
||||
Arguments:
|
||||
dict = a dictionary
|
||||
keys = keys you want to keep in a dict
|
||||
|
||||
Return:
|
||||
a dict with all non-specified keys removed
|
||||
|
||||
Example:
|
||||
```jldoctest
|
||||
julia> dict = OrderedDict(
|
||||
"user:" => "May I try this one?",
|
||||
"Plan 1:" => "testing a small portion of icecream",
|
||||
"Thought 1:" => "I like it.",
|
||||
"Act 1:" => "chatbox",
|
||||
"Actinput 1:" => "I get this one.",
|
||||
"Plan 2:" => "I'm meeting my wife this afternoon",
|
||||
"Thought 2:" => "I also want it for my wife",
|
||||
"Act 2:" => "chatbox",
|
||||
"Actinput 2:" => "I would like to get 2 more",
|
||||
)
|
||||
julia> keys = ["user:"]
|
||||
julia> keepOnlyKeys(dict, keys)
|
||||
OrderedDict(
|
||||
"user:" => "May I try this one?",
|
||||
)
|
||||
```
|
||||
"""
|
||||
function keepOnlyKeys(dict::T1, keys::T2) where {T1<:AbstractDict, T2<:AbstractVector}
|
||||
newdict = similar(dict)
|
||||
for (k, v) in dict
|
||||
if k ∈ keys
|
||||
newdict[k] = v
|
||||
end
|
||||
end
|
||||
return newdict
|
||||
end
|
||||
|
||||
|
||||
""" Convert experience dict into 1 string for LLM to use.
|
||||
|
||||
Arguments:
|
||||
dict = a dictionary contain past experience
|
||||
|
||||
Return:
|
||||
An experience in 1 string without context keys.
|
||||
|
||||
Example:
|
||||
```jldoctest
|
||||
julia> dict = OrderedDict{String, Any}(
|
||||
" This lesson can be applied to various situations => " Gathering accurate and relevant information about the user's preferences, budget, and event details is crucial for providing personalized recommendations.\n"
|
||||
)
|
||||
julia> experience(dict)
|
||||
|
||||
```
|
||||
"""
|
||||
function experience(dict::T) where {T<:AbstractDict}
|
||||
s = ""
|
||||
for (k, v) in dict
|
||||
s *= v
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
""" Get the latest step number of short term memory
|
||||
|
||||
Arguments:
|
||||
dict = a dictionary contain past experience
|
||||
|
||||
Return:
|
||||
latest step number
|
||||
|
||||
Example:
|
||||
```jldoctest
|
||||
julia> dict = OrderedDict(
|
||||
"Plan 1:" => "1. Ask about the type of food that will be served at the wedding party.")
|
||||
julia>shortMemLatestTask(dict)
|
||||
"""
|
||||
function shortMemLatestTask(dict::T) where {T<:AbstractDict}
|
||||
_latest_step = keys(dict)
|
||||
_latest_step = [i for i in _latest_step]
|
||||
_latest_step = _latest_step[end]
|
||||
latest_step = parse(Int, _latest_step[end-2:end-1])
|
||||
return latest_step
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end # end module
|
||||
108
src/interface.jl
108
src/interface.jl
@@ -145,6 +145,8 @@ function chat_mistral_openorca(a::agentReflex)
|
||||
"""
|
||||
|
||||
response = sendReceivePrompt(a, prompt, timeout=180, stopword=["<|", "</"])
|
||||
response = split(response, "<|")[1]
|
||||
response = split(response, "</")[1]
|
||||
|
||||
return response
|
||||
end
|
||||
@@ -215,16 +217,12 @@ function planner_mistral_openorca(a::agentReflex)
|
||||
P.S.1 each task of the plan should be a single action.
|
||||
</Your job>
|
||||
<Example>
|
||||
1. Ask the user about the occasion for which they are choosing a wine
|
||||
2. Ask the user about the type of food that will be served with the wine
|
||||
3. Ask about preferred type of wine (Rose, White, Red, Rose or Sparkling) - If the user don't know, suggest a type of wine based on the user's earlier answers
|
||||
4. Ask the user about their preferred sweetness level (dry to very sweet)
|
||||
5. Ask the user about their preferred intensity level (light to full bodied)
|
||||
6. Ask the user about their preferred tannin level (low to high)
|
||||
7. Ask the user about their preferred acidity level (low to high)
|
||||
8. Ask the user about their wine price range
|
||||
9. Use winestock tool to find wines that match the user's preferences and are within their price range
|
||||
10. Use finalanswer tool to present the recommended wines to the user.
|
||||
1. Ask the user about how many miles per day they drive
|
||||
2. Ask the user about what stuff they usually carry with
|
||||
3. Ask the user about preferred type of car they want to buy (sedan, sport, SUV, etc)
|
||||
8. Ask the user about their price range
|
||||
9. Use inventory tool to find cars that match the user's preferences and are within their price range
|
||||
10. Use finalanswer tool to present the recommended car to the user.
|
||||
</Example
|
||||
</s>
|
||||
$conversation
|
||||
@@ -235,6 +233,7 @@ function planner_mistral_openorca(a::agentReflex)
|
||||
plan = sendReceivePrompt(a, assistant_plan_prompt, max_tokens=1024, temperature=0.1,
|
||||
timeout=180, stopword=["<|user|>", "</"])
|
||||
plan = split(plan, "<|")[1]
|
||||
plan = split(plan, "</")[1]
|
||||
# plan = split(plan, "\n\n")[1]
|
||||
|
||||
return plan
|
||||
@@ -259,13 +258,13 @@ function updatePlan(a::agentReflex)
|
||||
"""
|
||||
<|system|>
|
||||
$(a.roles[a.role])
|
||||
The required info you need for wine recommendation:
|
||||
- occasion: ask the user
|
||||
- type of food: ask the user
|
||||
- user's personal taste of wine: ask the user
|
||||
- ambient temperature at the serving location: ask the user
|
||||
- wine price range: ask the user
|
||||
- wines we have in stock
|
||||
Request the user’s input for the following info initially, and use alternative sources of information only if they are unable to provide it:
|
||||
- occasion
|
||||
- type of food ask the user
|
||||
- user's personal taste of wine
|
||||
- ambient temperature at the serving location
|
||||
- wine price range
|
||||
- wines we have in stock (use tools to get the info)
|
||||
You provide a personalized recommendation of up to two wines based on the user's info above, and you describe the benefits of each wine in detail.
|
||||
|
||||
You have access to the following tools:
|
||||
@@ -367,6 +366,40 @@ function selfAwareness(a::agentReflex)
|
||||
|
||||
"""
|
||||
|
||||
# prompt =
|
||||
# """
|
||||
# <|system|>
|
||||
# <About yourself>
|
||||
# $aboutYourself
|
||||
# $(a.roleSpecificInstruction[a.role])
|
||||
# </About yourself>
|
||||
# <Your earlier work>
|
||||
# $work
|
||||
# </Your earlier work>
|
||||
|
||||
# <Your plan>
|
||||
# $(a.memory[:shortterm]["Plan 1:"])
|
||||
# </Your plan>
|
||||
# <Your job>
|
||||
# Use the following format strictly:
|
||||
# What do I know: based on observed results, repeat all the information you got.
|
||||
# Info matching: using JSON format, explicitly state what information matches which variable name in my plan.
|
||||
# What am I missing: based on Info match, describe in detail what you are still missing based on the plan.
|
||||
# P.S. do not mention any toolnames
|
||||
# </Your job>
|
||||
# <Example>
|
||||
# What do I know:
|
||||
# - The user is buying an electric SUV car.
|
||||
# Info matching: {"car type": "SUV", "engine": "electric motor", "color": "N/A", "financing": "N/A"}
|
||||
# What am I missing:
|
||||
# - The user's preferred color
|
||||
# - The user's financing method
|
||||
# </Example>
|
||||
# </s>
|
||||
# <|assistant|>
|
||||
# What I know:
|
||||
# """
|
||||
|
||||
prompt =
|
||||
"""
|
||||
<|system|>
|
||||
@@ -383,17 +416,29 @@ function selfAwareness(a::agentReflex)
|
||||
</Your plan>
|
||||
<Your job>
|
||||
Use the following format strictly:
|
||||
What do I know: based on observed results, repeat all the information you got.
|
||||
Info match: explicitly state what information matches which variable anme in my plan.
|
||||
What am I missing: based on observed results, describe in detail what you are still missing based on the plan.
|
||||
What I know: based on observed results, repeat all the information you got
|
||||
Info match: using JSON format, explicitly state what information matches which variable name in my plan
|
||||
What am I missing: based on Info match, describe in detail what you are still missing based on the plan
|
||||
P.S. do not mention any toolnames
|
||||
</Your job>
|
||||
<Example>
|
||||
user response:
|
||||
- EV could mean electric vehicle
|
||||
What do I know:
|
||||
- The user is buying an electric SUV car.
|
||||
Info matching: {"car type": "SUV", "engine type": "electric motor", "color": "not know yet", "financing": "not know yet"}
|
||||
What am I missing:
|
||||
- The user's preferred color
|
||||
- The user's financing method
|
||||
</Example>
|
||||
</s>
|
||||
<|assistant|>
|
||||
What I know:
|
||||
"""
|
||||
response = sendReceivePrompt(a, prompt, max_tokens=1024, temperature=0.2, timeout=180,
|
||||
response = sendReceivePrompt(a, prompt, max_tokens=1024, temperature=0.4, timeout=180,
|
||||
stopword=["/n/n", "END", "End", "Obs", "<|", "</"])
|
||||
response = split(response, "<|")[1]
|
||||
response = split(response, "</")[1]
|
||||
@show response
|
||||
|
||||
return response
|
||||
@@ -455,19 +500,22 @@ function actor_mistral_openorca(a::agentReflex, selfaware=nothing)
|
||||
push!(toolslist, toolname)
|
||||
end
|
||||
|
||||
println("")
|
||||
@show actor_selfaware = selfaware
|
||||
|
||||
thought = "Thought: you should always think about what to do according to the plan (pay attention to correct numeral calculation and commonsense and do one thing at a time.)"
|
||||
if selfaware !== nothing
|
||||
|
||||
|
||||
# aware = "Self-awareness: based on the recap, assess your progress against the plan then identify areas where you need to address."
|
||||
# aware = "Self-awareness: based on the recap and the plan, state your current understanding of the matter in details then identify areas where you need to address."
|
||||
# aware = "Self-awareness: Based on Obs, review your progress against the plan. Then, describe in detail the results you have achieved so far. Finally, describe in detail what you are missing. (focus on your actions and their results)"
|
||||
# aware = "Self-awareness: Based on action's input and observed results, check your progress against the plan. Then, repeat all the details of what you have been gathered. Finally, describe in detail what you are missing."
|
||||
thought =
|
||||
"Self-awareness: $selfaware
|
||||
Thought: based on self-awareness, think about what to do next by prioritize what you missed first. (P.S. 1) let's think a single step. 2) pay attention to correct numeral calculation and commonsense.)
|
||||
Thought: based on self-awareness, you should always think about what to do next by prioritizing what you missed first. (P.S. 1) let's think a single step. 2) pay attention to correct numeral calculation and commonsense.)
|
||||
"
|
||||
end
|
||||
|
||||
# your should request the missing information first before making a decision
|
||||
aboutYourself =
|
||||
"""
|
||||
Your name is $(a.agentName)
|
||||
@@ -507,9 +555,9 @@ function actor_mistral_openorca(a::agentReflex, selfaware=nothing)
|
||||
Obs: observed result of the action
|
||||
</Your job>
|
||||
<Example>
|
||||
Thought: Greet user and begin the conversation.
|
||||
Act: askbox
|
||||
Actinput: {\"askbox\": \"Hello! Welcome to our wine store.\"}
|
||||
Thought: based on self-awareness, I think he also need to know whether there are any charging station near by his house. I should search the internet to get this info.
|
||||
Act: internetsearch
|
||||
Actinput: {\"internetsearch\": \"EV charging station near Bangkok\"}
|
||||
</Example>
|
||||
</s>
|
||||
<|assistant|>
|
||||
@@ -534,6 +582,9 @@ function actor_mistral_openorca(a::agentReflex, selfaware=nothing)
|
||||
stopword=["Thought:", "Obs:", "<|system|>", "</s>", "<|end|>"],
|
||||
seed=seed)
|
||||
response = splittext(response, ["/n/n", "END", "End","obs", "Obs", "<|im_end|>"])
|
||||
response = split(response, "<|")[1]
|
||||
response = split(response, "</")[1]
|
||||
response = split(response, "Thought:")[end]
|
||||
|
||||
latestTask = shortMemLatestTask(a.memory[:shortterm]) +1
|
||||
|
||||
@@ -555,6 +606,9 @@ function actor_mistral_openorca(a::agentReflex, selfaware=nothing)
|
||||
respone = regexmatch !== nothing ? response = replace(response, match=>"Actinput:") : response
|
||||
response = replace(response, "actinput:"=>"Actinput:")
|
||||
|
||||
println("")
|
||||
@show response
|
||||
|
||||
headerToDetect = ["Plan $(a.attempt):",
|
||||
"Self-awareness $latestTask:",
|
||||
"Thought $latestTask:",
|
||||
|
||||
@@ -194,7 +194,7 @@ function winestock(a::agentReflex, actorResult::NamedTuple)
|
||||
# intensity = 1, light bodied
|
||||
# intensity = 2, light-medium bodied
|
||||
# intensity = 3, medium bodied
|
||||
# intensity = 4, medium-full bodied
|
||||
# intensity = 4, medium-full bodied
|
||||
# intensity = 5, full bodied
|
||||
# Sweetness level:
|
||||
# sweetness = 1, dry
|
||||
|
||||
34
src/type.jl
34
src/type.jl
@@ -134,20 +134,34 @@ function agentReflex(
|
||||
),
|
||||
roleSpecificInstruction::Dict=Dict(
|
||||
:assistant => "",
|
||||
# :sommelier =>
|
||||
# """
|
||||
# Required info you need for wine recommendation:
|
||||
# - occasion: ask the user
|
||||
# - type of food that will be served with wine: ask the user
|
||||
# - type of wine (we have Rose, White, Red, Rose and Sparkling): ask the user
|
||||
# - wine sweetness level (dry to very sweet)
|
||||
# - wine intensity level (light to full bodied)
|
||||
# - wine tannin level (low to high)
|
||||
# - wine acidity level (low to high)
|
||||
# - wine price range: ask the user
|
||||
# - wines we have in stock: use winestock tool
|
||||
# """
|
||||
:sommelier =>
|
||||
"""
|
||||
Required info you need for wine recommendation:
|
||||
- occasion: ask the user
|
||||
- type of food that will be served with wine: ask the user
|
||||
- type of wine (we have Rose, White, Red, Rose and Sparkling): ask the user
|
||||
- wine sweetness level (dry to very sweet)
|
||||
- wine intensity level (light to full bodied)
|
||||
- wine tannin level (low to high)
|
||||
- wine acidity level (low to high)
|
||||
- wine price range: ask the user
|
||||
- wines we have in stock: use winestock tool
|
||||
Request the user’s input for the following info initially, and use alternative sources of information only if they are unable to provide it:
|
||||
- occasion
|
||||
- type of food that will be served with wine
|
||||
- type of wine (we have Rose, White, Red, Rose and Sparkling)
|
||||
- wine sweetness level (dry to very sweet)
|
||||
- wine intensity level (light to full bodied)
|
||||
- wine tannin level (low to high)
|
||||
- wine acidity level (low to high)
|
||||
- wine price range: ask the user
|
||||
- wines we have in stock (use winestock tool)
|
||||
"""
|
||||
),
|
||||
|
||||
thinkingFormat::Dict=Dict(
|
||||
:react=>
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user