ShuanghongS 6 months ago
parent
commit
fe5f422f45
5 changed files with 325 additions and 8 deletions
  1. 4 1
      main_new_version.php
  2. 253 2
      service/robot.class.php
  3. 2 2
      service/tools.class.php
  4. 49 0
      utils/common.class.php
  5. 17 3
      utils/utils.class.php

+ 4 - 1
main_new_version.php

@@ -781,7 +781,10 @@ switch ($action) {
         break;
     case 'robot':
         robot::getInstance()->robot_prompt_configuration();
-        break;     
+        break;
+    case 'robot_chat':
+        robot::getInstance()->ai_chat();
+        break;          
     case 'system_setting':
         tools::getInstance()->user_system_setting();
         break;

+ 253 - 2
service/robot.class.php

@@ -27,7 +27,7 @@ class robot{
         }
         return self::$_robot;
     }
-    public function robot_chat() {
+    public function robot_chat_log() {
         $operate = utils::_get('operate');
         $operate = strtolower($operate);
 
@@ -286,6 +286,7 @@ class robot{
                 $sqlWhere = '  ' . common::searchExtendHand_KLN("ocean", $_SESSION["ONLINE_USER"]);
                 //先不考虑总数,处理有难度
                 foreach($sqlArr as $_sql){
+                    error_log($_sql);
                     if(empty($_sql)){
                         continue;
                     }
@@ -295,7 +296,7 @@ class robot{
                     $new_sql = utils::processLimitClause($new_sql);
                     //$rs = common::excuteListSql($new_sql);
                     $rs =  $mapdb->GetAll($new_sql) or ( (!$mapdb->ErrorMsg()) or error_log(common::dbLog($mapdb, $new_sql), 0));
-                    $reference = utils::replacementsMultiline($rs,$reference);
+                    $reference = utils::replacementsMultiline($rs,$reference,$new_sql);
                 }
                 //有就去掉{{#each hbol_list}}\n  {{/each}}
                 $reference = preg_replace([
@@ -344,6 +345,105 @@ class robot{
         }
     }
 
+    public function ai_chat(){
+        $operate = utils::_get('operate');
+        $operate = strtolower($operate);
+
+        if($operate == "ai_chat_prompt"){
+            //查询所有配置信息 目前只有一个表一个数据
+            $configuration = common::excuteObjectSql("select id, character_name,professional_field,main_tasks,table_name,table_description,
+            table_structure_configuration,response_rule_configuration,output_format_configuration,fixed_problem_configuration
+            from public.kln_robot_prompt_configuration where id = 1");
+
+            $PromptAndVue = $this->dealPromptFormatAndVue($configuration);
+            //promptFormat
+            $promptFormat =$PromptAndVue["promptFormat"];
+
+            $data = array("msg" => "succssful","prompt"=>$promptFormat);
+            common::echo_json_encode(200,$data);
+            exit();
+        }
+
+        /**
+         * ai 自由聊天
+        */
+        if($operate == "ai_chat"){
+            //前端生成一个唯一的serial_no
+            $serial_no = common::check_input($_POST["serial_no"]);
+            //系统提示词
+            $systemPrompt = common::check_input($_POST["prompt"]);
+            $question_type = common::check_input($_POST['question_type']);
+            $question_content = common::check_input($_POST['question_content']);
+
+            //固定问题的原始问题
+            $fixed_faq = common::check_input($_POST['fixed_faq']);
+            //claude  deepseek  根據賬號信息判断
+            $model = common::getUserCountry();
+            $answer_type  = $question_type == "Free Question" ? "AI回答" : "预定义模板回答";
+
+            //获取自然序列
+            $sequence = common::getChatAiSequence();
+            $name = $model == 'deepseek' ? "DS" : "CD";   
+            $request_id = "R".$name."".date("Ymd").$sequence; 
+            $question_id = "Q"."".date("Ymd").$sequence; 
+
+            //处理一下参数
+            $user_name = common::check_input(_getLoginName());
+            $user_type = _isApexLogin() ? "employee" : "customer";
+            
+            //首先生成基本记录数据 以便以后根据情况更新状态
+            $sql = "INSERT INTO public.kln_robot_chat_log(serial_no, question_id, user_name, user_type, question_type, question_content, 
+                    answer_type, question_date, question_time, request_id, request_time)
+            VALUES ('$serial_no','$question_id', '$user_name', '$user_type', '$question_type', '$question_content', 
+                    '$answer_type',now(),to_char(CURRENT_TIME::time, 'HH24:MI:SS'), '$request_id',now());";
+            $rs = common::excuteUpdateSql($sql);
+            if (!$rs) {
+                $data = array("type"=>"markdown","data" => "AI Chat Save Error");
+                common::echo_json_encode(200,$data);
+                exit();
+            }
+
+            if ($question_type == 'Predefined Question'){
+                $answer = $this->doFixedAnswerAndLog($serial_no,$fixed_faq,$question_content);
+            } else {
+                $answer = $this->doFreeAnswerAndLog($serial_no,$model,$systemPrompt, $question_content, $history = []);
+            }
+            $return = array("type"=>"markdown","data" =>$answer);
+            common::echo_json_encode(200,$return);            
+            exit();
+        }
+
+        if($operate == "ai_chat_stop"){
+            $serial_no = common::check_input($_POST["serial_no"]);
+            $updateSql = "update public.kln_robot_chat_log  set answer_type = '回答被动停止' where serial_no = '$serial_no'";
+
+            $updateRs = common::excuteUpdateSql($updateSql);
+            if (!$updateRs) {
+                $data = array("msg" => "AI Chat Response Stop Error");
+            }else{
+                $data = array("msg" => "AI Chat Response Stop succssful");
+            }
+            common::echo_json_encode(200,$data);            
+            exit(); 
+        }
+
+        if($operate == "ai_chat_answer_mark"){
+            $serial_no = common::check_input($_POST["serial_no"]);
+            $answer_satisfication = common::check_input($_POST["answer_satisfication"]);
+            $updateSql = "update public.kln_robot_chat_log  set answer_satisfication = '$answer_satisfication' where serial_no = '$serial_no'";
+
+            $updateRs = common::excuteUpdateSql($updateSql);
+            if (!$updateRs) {
+                $data = array("msg" => "AI Chat Response Mark Error");
+            }else{
+                $data = array("msg" => "AI Chat Response Mark succssful");
+            }
+            common::echo_json_encode(200,$data);            
+            exit(); 
+        }
+
+    }
+
     public function dealPromptFormatAndVue($configuration){
         //tableDat-VUE 
         $tableData = array();
@@ -424,6 +524,18 @@ class robot{
         return $changeLogList;
     }
 
+    public function getSystemPrompt(){
+        //查询所有配置信息 目前只有一个表一个数据
+        $configuration = common::excuteObjectSql("select id, character_name,professional_field,main_tasks,table_name,table_description,
+            table_structure_configuration,response_rule_configuration,output_format_configuration,fixed_problem_configuration
+            from public.kln_robot_prompt_configuration where id = 1");
+
+        $PromptAndVue = $this->dealPromptFormatAndVue($configuration);
+        //promptFormat
+        $promptFormat =$PromptAndVue["promptFormat"];
+        return $promptFormat;
+    }
+
     public function getCompletePromptFormat() {
         $formatTpl = "
 您是专门从<{professional_field}>的 <{character_name}>,请始终以<{output_type}>格式响应。您的主要任务是<{main_tasks}>
@@ -438,6 +550,145 @@ class robot{
     <{output_format_configuration}>";
         return $formatTpl;
     }
+
+    public function doFreeAnswerAndLog($serial_no,$model,$systemPrompt, $question_content, $history){
+        //分担查询
+        include ONLINE_ROOT . 'libs' . DS . 'map_config.ini.php';
+
+        //Ai response
+        $item_value = common::excuteOneSql("select item_value from public.config where item = 'AIAPISetting'");
+        $config = json_decode($item_value,true);
+        $response = AIClientFactory::create($model, $config[$model], $systemPrompt, $question_content, $history = []);
+
+        $input_token = "";
+        $output_token = "";
+        $answer_template = "";
+        $answer ="";
+        $response_pram = common::excuteObjectSql("select to_char(now(), 'YYYY-mm-dd HH24:MI:SS') as response_time,
+            CEIL(EXTRACT(EPOCH FROM (now() - question_date))) AS response_duration from public.kln_robot_chat_log where serial_no = '$serial_no'");
+        $response_duration =   $response_pram['response_duration'];
+        //回答问题类型,在没有超时的情况下,固定:AI回答
+        $answer_type = "AI回答";
+        if ($response_duration > 120){
+            $answer_type = "回答超时";
+        }
+        $request_content = common::check_input(json_encode($response['data']));
+        $ai_response_content = common::check_input(json_encode($response['full_response']));
+        $message = json_decode(common::getChatAimessage($response['message']),true);
+        if($message["can_query"] == "true" && !empty($message["sql"])){
+            $reference = $message["reference"];
+            $answer_template = common::check_input($message["reference"]);
+            $sql = $message["sql"];
+            //拆分sql 存在多条的情况
+            $sqlArr = explode(";", $sql);
+            //给所有sql 拼接用户权限
+            $sqlWhere = '  ' . common::searchExtendHand_KLN("ocean", $_SESSION["ONLINE_USER"]);
+            //先不考虑总数,处理有难度
+            foreach($sqlArr as $_sql){
+                if(empty($_sql)){
+                    continue;
+                }
+                //根据public.kln_ocean 和 WHERE 的位置关系,带入权限
+                $new_sql = utils::modifyString($_sql,$sqlWhere);
+                //处理limit 超过10 先限制10
+                //$new_sql = utils::processLimitClause($new_sql);
+                //$rs = common::excuteListSql($new_sql);
+                $rs =  $mapdb->GetAll($new_sql) or ( (!$mapdb->ErrorMsg()) or error_log(common::dbLog($mapdb, $new_sql), 0));
+                $reference = utils::replacementsMultiline($rs,$reference,$new_sql);
+            }
+            //有就去掉{{#each hbol_list}}\n  {{/each}}
+            $reference = preg_replace([
+                '/\{\{[^}]+\}\}\n/',  // 匹配开始标签及换行
+                '/\{\{\/[^}]+\}\}/'   // 匹配结束标签
+            ], '', $reference);
+            $answer = $reference;
+        }else{
+            $answer = $message["response"];
+        }
+
+        //update 更新
+        $updateSql = "update public.kln_robot_chat_log set answer_duration = '$response_duration',answer_template='$answer_template',
+            answer_type = case when COALESCE(answer_type,'') = '回答被动停止' then '回答被动停止' else '$answer_type' end,
+            request_content='$request_content',
+            input_token='$input_token',ai_response_content='$ai_response_content',
+            output_token='$output_token',response_time=now(),response_duration='$response_duration'
+        where serial_no = '$serial_no'";
+
+        $updateRs = common::excuteUpdateSql($updateSql);
+        if (!$updateRs) {
+            $data = array("type"=>"markdown","data" => "AI Chat Response Save Error");
+            common::echo_json_encode(200,$data);
+            exit();
+        }
+        return $answer;
+    }
+
+    public function doFixedAnswerAndLog($serial_no,$fixed_faq,$question_content){
+        $input_token = "";
+        $output_token = "";
+        $answer_template = "";
+        $answer ="";
+        $fixedChat = common::excuteObjectSql("select * from public.kln_robot_chat_fixed where fixed_faq = '$fixed_faq'");
+
+        if (empty($fixedChat['secondary_interaction_content']) || $question_content != $fixedChat['fixed_faq'] ){          
+            $reference = $fixedChat["answer_style"];
+            $answer_template = common::check_input($fixedChat["answer_style"]);
+            //执行sql 语句
+            $sql = $fixedChat["fixed_sql"];
+            //拆分sql 存在多条的情况
+            $sqlArr = explode(";", $sql);
+            //给所有sql 拼接用户权限
+            $sqlWhere = '  ' . common::searchExtendHand_KLN("ocean", $_SESSION["ONLINE_USER"]);
+            //先不考虑总数,处理有难度
+            foreach($sqlArr as $_sql){
+                if(empty($_sql)){
+                    continue;
+                }
+                //根据public.kln_ocean 和 WHERE 的位置关系,带入权限
+                $new_sql = utils::modifyString($_sql,$sqlWhere);
+                //处理limit 超过10 先限制10
+                //$new_sql = utils::processLimitClause($new_sql);
+                $rs = common::excuteListSql($new_sql);
+                $reference = utils::replacementsMultiline($rs,$reference,$new_sql);
+            }
+            //有就去掉{{#each hbol_list}}\n  {{/each}}
+            $reference = preg_replace([
+                '/\{\{[^}]+\}\}\n/',  // 匹配开始标签及换行
+                '/\{\{\/[^}]+\}\}/'   // 匹配结束标签
+            ], '', $reference);
+            $answer = $reference;
+        } else {
+            $answer = $fixedChat['secondary_interaction_content'];
+        }
+
+        //回答完毕计时
+        $response_pram = common::excuteObjectSql("select to_char(now(), 'YYYY-mm-dd HH24:MI:SS') as response_time,
+            CEIL(EXTRACT(EPOCH FROM (now() - question_date))) AS response_duration from public.kln_robot_chat_log where serial_no = '$serial_no'");
+        $response_duration =   $response_pram['response_duration'];
+        //回答问题类型,在没有超时的情况下,固定:AI回答
+        $answer_type = "AI回答";
+        if ($response_duration > 120){
+            $answer_type = "回答超时";
+        }
+        
+        $request_content = common::check_input($question_content);
+        $ai_response_content = common::check_input($answer);
+        //update 更新
+        $updateSql = "update public.kln_robot_chat_log set answer_duration = '$response_duration',answer_template='$answer_template',
+            answer_type = case when COALESCE(answer_type,'') = '回答被动停止' then '回答被动停止' else '$answer_type' end,
+            request_content='$request_content',
+            input_token='$input_token',ai_response_content='$ai_response_content',
+            output_token='$output_token',response_time=now(),response_duration='$response_duration'
+        where serial_no = '$serial_no'";
+
+        $updateRs = common::excuteUpdateSql($updateSql);
+        if (!$updateRs) {
+            $data = array("type"=>"markdown","data" => "AI Chat Response Save Error");
+            common::echo_json_encode(200,$data);
+            exit();
+        }
+        return $answer;
+    }
 }
 
 ?>

+ 2 - 2
service/tools.class.php

@@ -1696,7 +1696,7 @@ class tools {
                 $eventCard["numericRecords"] = !empty($mInfo["numericRecords"]) ? $mInfo["numericRecords"] : 0;
                 $eventCard["info"]["etdOrdeparturNum"] = !empty($mInfo["numericRecords_one"]) ? $mInfo["numericRecords_one"] : 0;
                 $eventCard["info"]["etaOrarrivalNum"] = !empty($mInfo["numericRecords_two"]) ? $mInfo["numericRecords_two"] : 0;
-                $eventCard["title"] = "Container Status Update Daily Summary(".$mInfo["insert_date_format"].")";
+                $eventCard["title"] = "Delay Daily Summary(".$mInfo["insert_date_format"].")";
                 $eventCard["insert_date_format"] = $mInfo["insert_date_format"];
                 //seeall 的时候,前端不需要id
                 $eventCard["id"] = '';
@@ -1704,7 +1704,7 @@ class tools {
                 $eventCard["numericRecords"] = !empty($mInfo["numericRecords"]) ? $mInfo["numericRecords"] : 0;
                 $eventCard["info"]["etdOrdeparturNum"] = !empty($mInfo["numericRecords_one"]) ? $mInfo["numericRecords_one"] : 0;
                 $eventCard["info"]["etaOrarrivalNum"] = !empty($mInfo["numericRecords_two"]) ? $mInfo["numericRecords_two"] : 0;
-                $eventCard["title"] = "Container Status Update Weekly Summary(".$mInfo["insert_date_format"].")";
+                $eventCard["title"] = "Delay Weekly Summary(".$mInfo["insert_date_format"].")";
                 $eventCard["insert_date_format"] = $mInfo["insert_date_format"];
                 //seeall 的时候,前端不需要id
                 $eventCard["id"] = '';

+ 49 - 0
utils/common.class.php

@@ -2507,5 +2507,54 @@ class common {
        //格式:"```json ```"
        return  str_replace(["```json", "```"], "", $message);
     }
+
+    public static function extractSelectFields($sql){
+        // 正则表达式匹配 SELECT 和 FROM 之间的内容
+        if (preg_match('/SELECT\s+(.*?)\s+FROM/si', $sql, $matches)) {
+            $selectPart = $matches[1];
+
+            // 去除注释和换行
+            $selectPart = preg_replace('/--.*$/m', '', $selectPart); // 去除单行注释
+            $selectPart = preg_replace('/\/\*.*?\*\//s', '', $selectPart); // 去除多行注释
+            $selectPart = str_replace(["\r", "\n"], ' ', $selectPart); // 换行替换成空格
+
+            // 分割字段
+            $fields = [];
+            foreach (explode(',', $selectPart) as $field) {
+                $field = trim($field);
+                if (empty($field)) continue;
+
+                // 匹配 AS 别名
+                if (preg_match('/(?:AS\s+)?(\w+)$/i', $field, $aliasMatch)) {
+                    // 如果有别名,则使用别名
+                    $fields[] = strtolower($aliasMatch[1]);
+                } else {
+                    // 否则直接添加字段名
+                    $fields[] = strtolower(trim($field));
+                }
+            }
+
+            return $fields;
+        }
+
+        return []; // 如果没有找到 SELECT 字段
+    }
+
+    public static function getUserCountry(){
+        return "deepseek";
+        // $contact_id = $_SESSION['ONLINE_USER']['contact_id'];
+        // if(empty($contact_id)){
+        //     return "claude";
+        // }
+
+        // $model = "deepseek";
+        // $data = common::excuteListSql("select LEFT(country, 2) as country from ocean.contacts where contact_id = '$contact_id'");
+        // foreach($data as $country){
+        //     if($country['country'] != "CN"){
+        //         $model = "claude";
+        //     }
+        // }
+        // return $model;
+    }
 }
 ?>

+ 17 - 3
utils/utils.class.php

@@ -927,7 +927,7 @@ class utils {
     /**
      * 替换 single reference
     */
-    public static function replacements($data, $template) {
+    public static function replacements($data, $template,$new_sql) {
         // 动态构建替换数组(格式:[key] => value)
         $replacements = [];
         foreach ($data as $key => $value) {
@@ -935,6 +935,14 @@ class utils {
             $replacements["{{$key}}"] = $value; 
             $replacements["{{{$key}}}"] = $value; 
         }
+        if(empty($data)){
+            $fileds = common::extractSelectFields($new_sql);
+            foreach($fileds as $key){
+                $replacements["{$key}"] = ""; // 将键名包裹在方括号中
+                $replacements["{{$key}}"] = ""; 
+                $replacements["{{{$key}}}"] = "";
+            }
+        }
         // 执行替换
         $result = strtr($template, $replacements);
         // 输出结果
@@ -944,7 +952,7 @@ class utils {
      /**
      * 替换复杂的reference
     */
-    public static function replacementsMultiline($data, $template) {
+    public static function replacementsMultiline($data, $template,$new_sql) {
         //| {{h_bol}} | {{m_bol}} | {{transport_mode}} | {{service}} | 格式
         $replaceTemplate = "";
         $explode_str = "|";
@@ -957,6 +965,12 @@ class utils {
                 continue;
             }
         }
+        if(empty($data)){
+            $fileds = common::extractSelectFields($new_sql);
+            foreach($fileds as $fieldVal){
+                $explode_str.=" {{{$fieldVal}}} |";
+            }
+        }
         //检查模板 是否已经带有特定表格的序列
         if(strpos($template, $explode_str) !== false){
             $spacing = utils::getMarkDownTableSpacing($template,$explode_str);
@@ -974,7 +988,7 @@ class utils {
             $replaceTemplate = $parts[0] . implode($spacing, $generatedRows) . $parts[1];
         }else{
             //全文替换 上面统一有excuteListSql 这里的结果要变一下
-            $replaceTemplate = utils::replacements($data[0],$template);
+            $replaceTemplate = utils::replacements($data[0],$template,$new_sql);
         }
         return $replaceTemplate;
     }