From 1577d7ae258dcf04060e255027ee761f8a0aa94b Mon Sep 17 00:00:00 2001 From: narawat Date: Thu, 2 Jul 2026 18:25:56 +0700 Subject: [PATCH] update --- src/interface.jl | 202 +++++++++++++++++++++------------------------ src/llmfunction.jl | 57 +++++-------- 2 files changed, 114 insertions(+), 145 deletions(-) diff --git a/src/interface.jl b/src/interface.jl index b330672..ec1c1ec 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -193,9 +193,9 @@ function decisionMaker(state::T1, text2textInstructLLM::Function, llmFormatName: end end - println("\nSQLLLM decisionMaker() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - pprintln(responsedict) - println("---") + # println("\nSQLLLM decisionMaker() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + # pprintln(responsedict) + # println("---") return responsedict end @@ -770,36 +770,25 @@ function transition(state::T, args::NamedTuple # getting SQL from vectorDB thoughtDict = decisionMakerF(state, text2textInstructLLM, llmFormatName; querySQLVectorDBF) - println("\n--- SQLLLM transition() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - pprintln(thoughtDict) - println("---") - rawresponse = nothing + # println("\n--- SQLLLM transition() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + # pprintln(thoughtDict) + # println("---") # map action and input() to llm function - response = - if thoughtDict["action_name"] == "RUNSQL" - response = SQLexecution(executeSQL, thoughtDict["action_input"]) - if response[:success] - thoughtDict["action_result"] = GeneralUtils.dfToString(response[:result]) - rawresponse = response[:result] - (rawresponse=response[:result], result=extracted, errormsg=nothing, success=true) - else - thoughtDict["action_result"] = response[:errormsg] - rawresponse = nothing - (result=nothing, errormsg=response[:errormsg], success=false) - end + response = nothing + if thoughtDict["action_name"] == "RUNSQL" + response = SQLexecution(executeSQL, thoughtDict["action_input"]) + else + error("undefined LLM function. Requesting $(thoughtDict["action_name"])") + end + + newNodeKey, newstate = makeNewState(state, thoughtDict, response) + progressvalue::Integer = + if response[:success] + 8 # for faster agent response. if success just skip evaluation else - error("undefined LLM function. Requesting $(thoughtDict["action_name"])") + evaluatorF(newstate, text2textInstructLLM, llmFormatName) end - # this section allow LLM functions above to have different return values. - success::Bool = haskey(response, :success) ? response[:success] : false - result = success ? response[:result] : response[:errormsg] - select = haskey(response, :select) ? response[:select] : nothing - reward::Integer = haskey(response, :reward) ? response[:reward] : 0 - isterminal::Bool = haskey(response, :isterminal) ? response[:isterminal] : false - newNodeKey, newstate = makeNewState(state, thoughtDict, rawresponse, JSON.json(result), - select, reward, isterminal) - progressvalue::Integer = evaluatorF(newstate, text2textInstructLLM, llmFormatName) return (newNodeKey=newNodeKey, newstate=newstate, progressvalue=progressvalue) end @@ -891,19 +880,20 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function; insertSQLVectorDB::Union{Function, Nothing}=nothing, similarSQLVectorDB::Union{Function, Nothing}=nothing, llmFormatName="qwen3" - )::NamedTuple{(:text, :rawresponse), Tuple{Any, Any}} where {T<:AbstractString} + ) where {T<:AbstractString} # use similarSQLVectorDB to find similar SQL for the query sql, distance = similarSQLVectorDB(query) + + # if sql is really match, immediately check database then return if sql !== nothing && distance <= 1 # query vector db to get wine response = SQLexecution(executeSQL, sql) if response[:success] - # intention = Dict(:intention=> "$(thoughtDict[:plan])") - extracted = extractContent_dataframe(response[:result], text2textInstructLLM, sql, - llmFormatName) - return (text=extracted, rawresponse=response[:result]) - end + return (result_str=response[:result_str], result_raw=response[:result_raw]) + else + error(response[:errormsg]) + end end """ @@ -918,44 +908,44 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function; """ systemmsg = - """ - - - RUNSQL, which you can use to execute SQL against the database. - action_input for this function must be a single SQL query to be executed against the database. - For more effective text search, it's necessary to use case-insensitivity and the ILIKE operator. - Do not wrap the SQL as it will be executed against the database directly and SQL must be ended with ';'. - - - At each round of conversation, you will be given the following: - - user question - You are working under your mentor supervision and you are also eager to improve your helpfulness. - - - Consult the database search guidelines. Then find the data from a database to satisfy the user's question. - - - Fulfill the objective. - - - - Keep SQL queries focused only on the provided information. - - Do not create any table in the database - - A junction table can be used to link tables together. Another use case is for filtering data. - - If you can't find a single table that can be used to answer the user's query, try joining multiple tables to see if you can obtain the answer. - - Text information in the database usually stored in lower case. If your search returns empty, try using lower case to search. - - If there is no search result from the database, remove the restrictive criteria until a search result is available, and proceed from there. - - - 1) plan: Based on the current situation, state a complete action plan to complete the task. Be specific. - 2) action_name: (Typically corresponds to the execution of the first step in your plan) Can be one of the available_actions name - 3) action_input: The input to the action you are about to perform according to your plan. - After the action is executed you gets "action_result". It is the output from the action you selected. - - - "plan": "...", - "action_name": "...", - "action_input": "..." - - """ + """ + + - RUNSQL, which you can use to execute SQL against the database. + action_input for this function must be a single SQL query to be executed against the database. + For more effective text search, it's necessary to use case-insensitivity and the ILIKE operator. + Do not wrap the SQL as it will be executed against the database directly and SQL must be ended with ';'. + + + At each round of conversation, you will be given the following: + - user question + You are working under your mentor supervision and you are also eager to improve your helpfulness. + + + Consult the database search guidelines. Then find the data from a database to satisfy the user's question. + + + Fulfill the objective. + + + - Keep SQL queries focused only on the provided information. + - Do not create any table in the database + - A junction table can be used to link tables together. Another use case is for filtering data. + - If you can't find a single table that can be used to answer the user's query, try joining multiple tables to see if you can obtain the answer. + - Text information in the database usually stored in lower case. If your search returns empty, try using lower case to search. + - If there is no search result from the database, remove the restrictive criteria until a search result is available, and proceed from there. + + + 1) plan: Based on the current situation, state a complete action plan to complete the task. Be specific. + 2) action_name: (Typically corresponds to the execution of the first step in your plan) Can be one of the available_actions name + 3) action_input: The input to the action you are about to perform according to your plan. + After the action is executed you gets "action_result". It is the output from the action you selected. + + + "plan": "...", + "action_name": "...", + "action_input": "..." + + """ @@ -1129,11 +1119,11 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function; root, _, resultState, highValueState = LLMMCTS.runMCTS(initialstate, transition, transitionargs; - horizontalSampleExpansionPhase=1, - horizontalSampleSimulationPhase=1, - maxSimulationDepth=1, - maxiterations=1, - explorationweight=1.0, + horizontalSampleExpansionPhase=2, + horizontalSampleSimulationPhase=2, + maxSimulationDepth=2, + maxiterations=2, + explorationweight=0.2, earlystop=earlystop, saveSimulatedNode=true, multithread=false) @@ -1144,11 +1134,6 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function; resultState = highValueState[selected] end - - println("\n--- SQLLLM query() resultState ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - pprintln(resultState) - println("---") - max_ind = if length(resultState["action_history"]) == 0 0 @@ -1157,25 +1142,20 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function; maximum(parse.(Int, k)) end latest_action = resultState["action_history"]["$max_ind"] - sql = latest_action["action_input"] - - # add to vectorDB only if the answer is achieved and the state is terminal - if insertSQLVectorDB !== nothing && resultState["isterminal"] == true && - resultState["accepted_as_answer"] == "yes" - - insertSQLVectorDB(resultState["question"], sql) - end - if latest_action["action_result"] === nothing - println("\nSQLLLM query() return nothing ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - end - #WORKING 1 - error("SQLLLM query() end") - result = (text=latest_action["action_result"], rawresponse=resultState["rawresponse"]) - println("\n--- SQLLLM query() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") - println("---") - error("SQLLLM query() end") - return result + #CHANGE add to vectorDB only if the answer is achieved and the state is terminal + # sql = latest_action["action_input"] + # if insertSQLVectorDB !== nothing && resultState["isterminal"] == true && + # resultState["accepted_as_answer"] == "yes" + # insertSQLVectorDB(resultState["question"], sql) + # end + + # println("\n--- SQLLLM query() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") + # println(latest_action) + # println("---") + # error("SQLLLM query() end") + + return (result_str=latest_action["action_result"], result_raw=resultState["result_raw"]) end @@ -1192,9 +1172,14 @@ julia> # Signature """ -function makeNewState(currentstate::T1, thoughtDict::T4, rawresponse, response::T2, select::Union{T3, Nothing}, - reward::T3, isterminal::Bool - )::NamedTuple{(:newNodeKey, :newstate), Tuple{String, Dict{String, <:Any}}} where {T1<:AbstractDict, T2<:AbstractString, T3<:Number, T4<:AbstractDict} +function makeNewState(currentstate::T1, thoughtDict::T2, response::NamedTuple, + )::NamedTuple{(:newNodeKey, :newstate), Tuple{String, Dict{String, <:Any}}} where {T1<:AbstractDict, T2<:AbstractDict} + + if response[:success] + thoughtDict["action_result"] = response[:result_str] + else + error(response[:errormsg]) + end newstate = deepcopy(currentstate) max_ind = @@ -1205,17 +1190,16 @@ function makeNewState(currentstate::T1, thoughtDict::T4, rawresponse, response:: maximum(parse.(Int, k)) end newstate["action_history"]["$(max_ind + 1)"] = thoughtDict - newstate["reward"] = reward - newstate["select"] = select - newstate["isterminal"] = isterminal - newstate["rawresponse"] = rawresponse # whatever return from action + newstate["reward"] = haskey(response, :reward) ? response[:reward] : 0 + newstate["select"] = haskey(response, :select) ? response[:select] : nothing + newstate["isterminal"] = haskey(response, :isterminal) ? response[:isterminal] : false + newstate["result_raw"] = response[:result_raw] # whatever return from action newNodeKey = GeneralUtils.uuid4snakecase() return (newNodeKey=newNodeKey, newstate=newstate) end - function generatequestion(state::T1, context, text2textInstructLLM::Function, llmFormatName::String; similarSQL::Union{T2, Nothing}=nothing, maxattempt=10, diff --git a/src/llmfunction.jl b/src/llmfunction.jl index 6d16059..33fa2fb 100644 --- a/src/llmfunction.jl +++ b/src/llmfunction.jl @@ -481,20 +481,9 @@ julia> response = SQLLLM.SQLexecution(executeSQL, sql) # Signature """ function SQLexecution(executeSQL::Function, sql::T -) where {T<:AbstractString} + )::NamedTuple where {T<:AbstractString} try - #XXX dummy SQL. use for testing - # sql = "SELECT w.wine_name FROM wine w JOIN wine_food wf ON w.wine_id = wf.wine_id JOIN food f ON wf.food_id = f.food_id WHERE f.\"food_name\" = 'lamb';" - # sql = " SELECT w.wine_name FROM wine w JOIN food f ON f.food_name = 'lamb' JOIN wine_food wf ON w.wine_id = wf.wine_id AND f.food_id = wf.food_id GROUP BY w.wine_name ORDER BY COUNT(DISTINCT w.wine_id) DESC;" - # sql = " SELECT COUNT(DISTINCT wf.wine_id) FROM wine w JOIN wine_food wf ON w.wine_id = wf.wine_id JOIN food f ON wf.food_id = f.food_id WHERE f.food_name ILIKE '%lamb%'" - - #XXX use for package testing, remove when done - # ans = "1.schilfwein zweigelt 2.cabernet sauvignon reserve limited edition" - # ans = "There are 1500 wines that can be paired with lamb." - # ans = "1500" - # return (response=ans, errormsg=nothing, reward=1, isterminal=true) - # add LIMIT to the SQL to prevent loading large data sql = strip(sql) @@ -508,39 +497,36 @@ function SQLexecution(executeSQL::Function, sql::T else sql = sql * ";" end - println("\n~~~ SQLexecution() SQL: ", @__FILE__, " ", @__LINE__) - println(sql) - result = executeSQL(sql) df = DataFrame(result) - tablesize = size(df) row, column = tablesize if row == 0 - error("\nThe resulting table has 0 row. Please try again.") - elseif column > 50 - error("\nSQL execution success but there are more than 50 rows Please be more specific.") + return (result_str="The resulting table has 0 row.", result_raw=df, success=true, errormsg=nothing) + elseif column > 30 + return (result_str="There are more than 30 columns. Please be more specific.", result_raw=df, success=true, errormsg=nothing) + else + df1 = + if row > 2 + # ramdom row to pick + df[sample(1:nrow(df), 2, replace=false), :] # random select 2 rows from df + else + df + end + result = GeneralUtils.dfToString(df1) + println("\n~~~ SQLexecution() result: ", @__FILE__, " ", @__LINE__) + println(sql) + println(df1) + println("\n") + return (result_str=result, result_raw=df1, success=true, errormsg=nothing) end - - df1 = - if row > 2 - # ramdom row to pick - df[sample(1:nrow(df), 2, replace=false), :] # random select 2 rows from df - else - df - end - - println("\n~~~ SQLexecution() result: ", @__FILE__, " ", @__LINE__) - println(df1) - return (result=df1, success=true, errormsg=nothing) catch e io = IOBuffer() showerror(io, e) errorMsg = String(take!(io)) st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace())) println(errorMsg) - response = (result=nothing, success=false, errormsg=errorMsg) - return response + return (result_str=nothing, result_raw=nothing, success=false, errormsg=errorMsg) end end @@ -559,7 +545,7 @@ end - `result::String` # Signature -""" +""" #WORKING function extractContent_dataframe(df::DataFrame, text2textInstructLLM::Function, action::String, llmFormatName::String )::String @@ -633,7 +619,7 @@ function extractContent_dataframe(df::DataFrame, text2textInstructLLM::Function, dictkey = ["about_resulting_table", "search_summary"] for i in 1:5 - response = text2textInstructLLM(prompt, modelsize="medium") + response = text2textInstructLLM("ramdom_id", prompt) response = GeneralUtils.deFormatLLMtext(response, llmFormatName) think, response = GeneralUtils.extractthink(response) @@ -653,7 +639,6 @@ function extractContent_dataframe(df::DataFrame, text2textInstructLLM::Function, responsedict = GeneralUtils.textToDict(response, header; dictKey=dictkey, symbolkey=false) - # result = dfstr result = """ Summary: $(responsedict["search_summary"])