wjg 1 год назад
Родитель
Сommit
c7cf7b33ec
56 измененных файлов с 4769 добавлено и 0 удалено
  1. 21 0
      app/src/main/java/com/ch/jedge/jbot2/JBot2Java.java
  2. 491 0
      app/src/main/java/com/ch/jedge/jbot2/JedgeBotGptService.java
  3. 309 0
      app/src/main/java/com/ch/jedge/jbot2/JedgeVoiceBotService.java
  4. 55 0
      app/src/main/java/com/ch/jedge/jbot2/context/JedgeSmartTaskContext.java
  5. 99 0
      app/src/main/java/com/ch/jedge/jbot2/context/JedgeTalkSession.java
  6. 61 0
      app/src/main/java/com/ch/jedge/jbot2/dict/JEDictUtil.java
  7. 82 0
      app/src/main/java/com/ch/jedge/jbot2/dict/JedgeWordDict.java
  8. 17 0
      app/src/main/java/com/ch/jedge/jbot2/dict/JedgeWordItem.java
  9. 12 0
      app/src/main/java/com/ch/jedge/jbot2/intent/JDataGetter.java
  10. 11 0
      app/src/main/java/com/ch/jedge/jbot2/intent/JbotApi.java
  11. 13 0
      app/src/main/java/com/ch/jedge/jbot2/intent/JbotClass.java
  12. 13 0
      app/src/main/java/com/ch/jedge/jbot2/intent/JbotObject.java
  13. 11 0
      app/src/main/java/com/ch/jedge/jbot2/intent/JedgeDomain.java
  14. 427 0
      app/src/main/java/com/ch/jedge/jbot2/intent/JedgeJBotCBService.java
  15. 84 0
      app/src/main/java/com/ch/jedge/jbot2/intent/bot/JBotAppBase.java
  16. 41 0
      app/src/main/java/com/ch/jedge/jbot2/intent/bot/JBotAppClient.java
  17. 188 0
      app/src/main/java/com/ch/jedge/jbot2/intent/param/JedgeDataSetter.java
  18. 10 0
      app/src/main/java/com/ch/jedge/jbot2/llm/JedgeLLMBaseObject.java
  19. 14 0
      app/src/main/java/com/ch/jedge/jbot2/llm/JedgeLLMContext.java
  20. 19 0
      app/src/main/java/com/ch/jedge/jbot2/llm/agent/JedgeLLMAgentFactory.java
  21. 37 0
      app/src/main/java/com/ch/jedge/jbot2/llm/agent/JedgeLLMAgentPool.java
  22. 10 0
      app/src/main/java/com/ch/jedge/jbot2/llm/agent/JedgeLLMAgentTemplate.java
  23. 290 0
      app/src/main/java/com/ch/jedge/jbot2/llm/pickers/JedgeLLMParamPicker.java
  24. 59 0
      app/src/main/java/com/ch/jedge/jbot2/llm/pickers/JedgeLLMParamPickerManager.java
  25. 16 0
      app/src/main/java/com/ch/jedge/jbot2/llm/pickers/JedgeLLMResultValidator.java
  26. 15 0
      app/src/main/java/com/ch/jedge/jbot2/llm/pickers/JedgeSmartHomeActionPicker.java
  27. 9 0
      app/src/main/java/com/ch/jedge/jbot2/llm/pickers/JedgeTaskObjectivePicker.java
  28. 47 0
      app/src/main/java/com/ch/jedge/jbot2/llm/request/JedgeLLMCOTRequest.java
  29. 7 0
      app/src/main/java/com/ch/jedge/jbot2/llm/request/JedgeLLMCallback.java
  30. 54 0
      app/src/main/java/com/ch/jedge/jbot2/llm/request/JedgeLLMFilterRequest.java
  31. 113 0
      app/src/main/java/com/ch/jedge/jbot2/llm/request/JedgeLLMRequest.java
  32. 27 0
      app/src/main/java/com/ch/jedge/jbot2/llm/response/JedgeLLMResponseGenerator.java
  33. 27 0
      app/src/main/java/com/ch/jedge/jbot2/llm/response/JedgeLLMUserQuestioner.java
  34. 208 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/JedgeLLMDmWorld.java
  35. 15 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/JedgeDomainSelector.java
  36. 89 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/JedgeKnDomain.java
  37. 40 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/JedgeKnObject.java
  38. 225 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/JedgeLLMKnWorld.java
  39. 13 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/task/JedgeContentCutter.java
  40. 20 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/task/JedgeLLMTaskTemplate.java
  41. 67 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/model/JedgeMdDomain.java
  42. 16 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/model/JedgeMdObject.java
  43. 65 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/readme.txt
  44. 114 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/system/JedgeLLMSystemTask.java
  45. 11 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/system/JedgeSystemKnDomain.java
  46. 88 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeLLMAgent.java
  47. 77 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeLLMTask.java
  48. 15 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeLLMTaskGaol.java
  49. 15 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeLLMTaskGaolItem.java
  50. 105 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeLLMTaskPool.java
  51. 40 0
      app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeTaskTester.java
  52. 13 0
      app/src/main/java/com/ch/jedge/jbot2/readme.txt
  53. 136 0
      app/src/main/java/com/ch/jedge/utils/JedgeBotConfigUtil.java
  54. 71 0
      app/src/main/java/com/ch/jedge/utils/JedgeBotConst.java
  55. 502 0
      app/src/main/java/com/ch/jedge/utils/JedgeJDBCUtils.java
  56. 135 0
      app/src/main/java/com/ch/jedge/utils/JedgeLlmUtil.java

+ 21 - 0
app/src/main/java/com/ch/jedge/jbot2/JBot2Java.java

@@ -0,0 +1,21 @@
+package com.ch.jedge.jbot2;
+
+import com.ch.jedge.glm.JedgeGlmBridgeService;
+import com.ch.jedge.jbot2.intent.bot.JBotAppBase;
+import com.changhong.jedge.JMgbusService;
+
+import static com.ch.jedge.utils.JedgeBotConst.*;
+
+public class JBot2Java extends JBotAppBase {
+    public JBot2Java(String botName) {
+        super(botName);
+    }
+
+    public static void main(String[] args) {
+        Class<? extends JMgbusService>[] classes =
+                (Class<? extends JMgbusService>[]) new Class<?>[]{JedgeBotGptService.class, JedgeVoiceBotService.class, JedgeGlmBridgeService.class};
+        run("jbot",
+                new String[] {sval_default_gpt_service,sval_default_gpt_voice_service, sval_default_glm6b_service},
+                classes, args);
+    }
+}

+ 491 - 0
app/src/main/java/com/ch/jedge/jbot2/JedgeBotGptService.java

@@ -0,0 +1,491 @@
+package com.ch.jedge.jbot2;
+
+import com.alibaba.fastjson.JSONArray;
+import com.ch.jedge.jbot2.dict.JEDictUtil;
+import com.ch.jedge.jbot2.dict.JedgeWordDict;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.jbot2.context.JedgeSmartTaskContext;
+import com.ch.jedge.utils.JedgeBotConst;
+import com.ch.jedge.utils.JedgeLlmUtil;
+import com.changhong.jedge.JMgbusModual;
+import com.changhong.jedge.JMgbusService;
+import com.changhong.jedge.JMgbusUtil;
+import com.changhong.jedge.MgbusApi;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.NumberUtils;
+import com.changhong.qlib.util.StringUtils;
+import com.changhong.qlib.util.TimeUtils;
+import com.changhong.qlib.util.file.FileUtils;
+import com.changhong.qlib.util.sync.SystemUtils;
+
+import java.io.File;
+import java.util.*;
+
+import static com.ch.jedge.jbot.other.agent.JedgeGdeConsts.sval_default_app;
+import static com.ch.jedge.utils.JedgeBotConst.*;
+
+public class JedgeBotGptService extends JMgbusService {
+
+    protected final Map<String, JedgeWordDict> dict_ = new HashMap<>();
+    protected final Map<String, JedgeTalkSession> sessions_ = new HashMap<>();
+    protected final Queue<QIData> ret_msgs_ = new LinkedList<>();
+
+    private boolean quit_ = false;
+    private boolean active_ = false;
+    private String kn_talk_log_file;
+    private String talk_log_org_;
+
+    private final JedgeSmartTaskContext smartTaskContext;
+    private final JedgeLLMContext llmCtx;
+
+    //基本词库
+    //
+    public JedgeBotGptService(JMgbusModual holder) {
+        super(holder);
+        llmCtx = new JedgeLLMContext(module, this);
+        smartTaskContext = new JedgeSmartTaskContext(llmCtx);
+        auto_remove_call_info = false;
+
+        kn_talk_log_file = talk_log_org_ = FileUtils.contactPath("./", "logs");
+        try {
+            new File(talk_log_org_).mkdirs();
+        } catch (Exception ignored) {}
+        talk_log_org_ = FileUtils.contactPath(talk_log_org_,"userAsk.log");
+        kn_talk_log_file = FileUtils.contactPath(kn_talk_log_file,"knTalk.log");
+
+        //加载动态词库
+        JEDictUtil.loadStaticCommonDicts(dict_, FileUtils.contactPath("./", "cfg", "dict", "common"));
+
+        watchLocalEvent("OnModuleConnected", (w, keyVal, msg) -> {
+            String host = msg.getString("host");
+            int port = msg.getInteger("port");
+            reactiveCommonDictionaries();
+            return true;
+        });
+        watchLocalEvent("OnModuleDisconnected", (w, keyVal, msg) -> {
+            String host = msg.getString("host");
+            int port = msg.getInteger("port");
+            return true;
+        });
+    }
+
+    private void reactiveCommonDictionaries() {
+        //将词库的所有词汇添加到jieba分词中
+        synchronized (dict_) {
+            for(JedgeWordDict d : dict_.values()) {
+                activeWordDict(d);
+//                SystemUtils.tryWait(50);
+            }
+        }
+    }
+
+    public void activeWordDict(JedgeWordDict dict) {
+        if(dict==null) return;
+        JSONArray items = dict.getWordItemsInJson(false);
+        if(!items.isEmpty()) {
+            postServiceMessage(sval_modual_word_cut, "/cw/word", new QData().put("w", items));
+        }
+    }
+
+
+    private void knItemPoolHeartBeat() {
+        module.warnLog("knItemPoolHeartBeat thread start ok.");
+        while (!quit_) {
+            SystemUtils.tryWaitforSingal(smartTaskContext, 1000);
+            try {
+                smartTaskContext.onHeartbeat();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        module.warnLog("knItemPoolHeartBeat thread end.");
+    }
+
+    @Override
+    public void onServiceStart() {
+        quit_ = false;
+        module.postThread(this::postTextRetSourceMessage);
+        module.postThread(this::clearTimeoutSession);
+        module.postThread(this::knItemPoolHeartBeat);
+
+        super.onServiceStart();
+    }
+
+    public JSONArray cutWords(String query) {
+        if(query.contains("、")) {
+            query = query.replaceAll("、","_");
+        }
+        QIData p = postServiceRequest(sval_modual_word_cut, "/cw/cut", new QData().put("t", query));
+        if(JMgbusUtil.isMgbusResultOk(p)) {
+            JSONArray re = p.getJsonArray("payload");
+            if(re != null)
+                return re;
+        }
+        return new JSONArray();
+    }
+
+    @Override
+    public void onServiceStop() {
+        quit_ = true;
+        SystemUtils.notifyAllObject(sessions_);
+        SystemUtils.notifyAllObject(ret_msgs_);
+        //等待退出完成
+        while(active_) {
+            SystemUtils.tryWaitforSingal(ret_msgs_, 20);
+        }
+        super.onServiceStop();
+    }
+
+    //切换知识库(默认,如果不存在则添加)
+    @MgbusApi
+    public QIData knTalk(QIData req) {
+        String prompt = req.getString("text");
+//        module.SimpleMark("用户输入>>" + prompt);
+        if(StringUtils.isNotValidStr(prompt)) {
+            prompt = req.getObjSimpleFmtString("param.text");
+            if(StringUtils.isNotValidStr(prompt)) {
+                return JMgbusUtil.MgbusResult(401,"未知输入");
+            }
+        }
+
+        JedgeTalkSession s = findSession(req, prompt);
+
+        if(!s.isStream) {  //非流式请求,目前只针对chatGPT
+            return JMgbusUtil.MgbusResult(200, makeRealGptChatCall(s, prompt));
+        }
+
+        String finalPrompt = prompt;
+        module.postThread(() -> {
+            if(s.knTalk) {
+                module.highLog(String.format("GPT Prompt>>%s", finalPrompt));
+                //根据各种类对象,完成对 提示词的多级划分。
+                knTalkLog(finalPrompt);
+                smartTaskContext.handleTask(s, finalPrompt);
+            } else {
+                knTalkLog(finalPrompt);
+                makeRealGptStreamCall(s, finalPrompt);
+            }
+        });
+
+        QIData resp = JedgeLlmUtil.simpleOk();
+        resp.put("sid", s.sid);
+        return resp;
+    }
+
+    @MgbusApi
+    public QIData registerDomain(QIData req) {
+       return smartTaskContext.getKnWorld().addNewDomain(req);
+    }
+
+    @MgbusApi
+    public QIData removeDomain(QIData req) {
+        return smartTaskContext.getKnWorld().removeDomain(req);
+    }
+
+    @MgbusApi
+    public QIData registerDmDomain(QIData req) {
+        return smartTaskContext.getDmWorld().addNewDmDomain(req);
+    }
+
+    @MgbusApi
+    public QIData removeDmDomain(QIData req) {
+        return smartTaskContext.getDmWorld().removeDmDomain(req);
+    }
+
+    @MgbusApi
+    public QIData registerDmObject(QIData req) {
+        return smartTaskContext.getDmWorld().addNewDmObjects(req);
+    }
+
+    @MgbusApi
+    public QIData removeDmObject(QIData req) {
+        return smartTaskContext.getDmWorld().removeDmObjects(req);
+    }
+
+    @MgbusApi
+    public QIData LocalStream(QIData req) {
+        String sid = req.getString("sid");
+        String text = req.asJsonObject().getString("delta");
+        boolean isEnd = req.getBoolean("finished", false);
+        JedgeTalkSession session;
+        synchronized (sessions_) {
+            session = sessions_.get(sid);
+        }
+        if(session != null) {
+            if(session.printSingle) {
+//                module.infoLog(String.format("Gpt>>%s(%s)", text, isEnd));
+                if (isEnd) {
+                    session.buildLastHistory();
+                } else {
+                    //text = text.trim();
+                    if(StringUtils.isValidStr(text) &&
+                            StringUtils.isValidStr(session.currentPrompt))
+                        session.append2LastQA(text);
+                }
+                if (session.srcMod != null && session.cbUri != null) {
+                    returnDirectGptMessage(session, text, isEnd);
+                } else
+                    module.warnLog(String.format("Unknown mod to return, discussed : %s", text));
+            } else {
+                text += ("\n");
+                module.warnLog(String.format("Unhandled yet content : %s", text));
+            }
+        } else {
+            module.warnLog(String.format("Dropping no session return content : %s", text));
+        }
+        return JMgbusUtil.MgbusResult(200,"ok");
+    }
+
+    protected void postTextRetSourceMessage() {
+        active_ = true;
+        module.warnLog("Posting thread start ok.");
+        while(!quit_) {
+            try {
+                QIData msg = null;
+                {
+                    if(!ret_msgs_.isEmpty()) {
+                        msg = ret_msgs_.poll();
+                    }
+                }
+                if(msg!= null) {
+                    String jeMod = msg.getString("~m");
+                    String jeUriCb = msg.getString("~u");
+                    postServiceMessage(jeMod, jeUriCb, msg);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            synchronized (ret_msgs_) {
+                SystemUtils.tryWaitforSingal(ret_msgs_, ival_mufis_text_message_gap_time_);
+            }
+        }
+        module.warnLog("Posting thread end.");
+        active_ = false;
+    }
+
+    protected void clearTimeoutSession() {
+        module.warnLog("clearTimeoutSession thread start ok.");
+        while (!quit_) {
+            synchronized (sessions_) {
+                List<String> toDel = new ArrayList<>();
+                Set<String> keys = sessions_.keySet();
+                for(String k : keys) {
+                    JedgeTalkSession s = sessions_.get(k);
+                    s.ttl ++;
+                    if(s.ttl > JedgeBotConst.ival_timeout_session_ ) {        //会话3分钟没有任何返回,清空
+                        toDel.add(k);
+                        module.highLog(String.format("Timeout talk session %s from %s", k, s.srcMod));
+                        // returnMgbusMessage(s.srcMod, s.cbUri, "会话超时", true, true);
+                    }
+                }
+                for(String key : toDel)
+                    sessions_.remove(key);
+            }
+            SystemUtils.tryWaitforSingal(sessions_, 100);
+        }
+        module.warnLog("clearTimeoutSession thread end.");
+    }
+
+    public void returnGptMessage(JedgeTalkSession s, String txtMessage) {
+        returnGptMessage(s, txtMessage, false, false);
+    }
+
+    public void returnGptMessage(JedgeTalkSession s, String txtMessage, boolean isEndMsg) {
+        returnGptMessage(s, txtMessage, isEndMsg, false);
+    }
+
+    public void returnDirectGptMessage(JedgeTalkSession s, String txtMessage) {
+        returnDirectGptMessage(s, txtMessage, false, false);
+    }
+
+    public void returnDirectGptMessage(JedgeTalkSession s, String txtMessage, boolean isEndMsg) {
+        returnDirectGptMessage(s, txtMessage, isEndMsg, false);
+    }
+
+    public void returnDirectGptMessage(JedgeTalkSession s, String txtMessage, boolean isEndMsg, boolean isErr) {
+        QIData msg = makeGptReturnMessage(txtMessage, isEndMsg, isErr);
+        postServiceMessage(s.srcMod, s.cbUri, msg);
+    }
+
+    public void returnGptMessage(JedgeTalkSession s, String txtMessage, boolean isEndMsg, boolean isErr) {
+        QIData msg = makeGptReturnMessage(txtMessage, isEndMsg, isErr);
+        msg.putString("~m", s.srcMod);
+        msg.putString("~u", s.cbUri);
+        synchronized (ret_msgs_) {
+            ret_msgs_.add(msg);
+            ret_msgs_.notify();
+        }
+    }
+
+    private QIData makeGptReturnMessage(String txtMessage, boolean isEndMsg, boolean isErr) {
+        QIData msg = new QData();
+        msg.putString("msg", txtMessage);
+        if(isEndMsg)
+            msg.putBoolean("end", isEndMsg);
+        if(isErr)
+            msg.putBoolean("err", isErr);
+        QIData d = msg.getData("~c");
+        if(d==null || d.isEmpty()) {
+            d = new QData();
+        }
+        d.put("nb",true);
+        msg.put("~c", d);
+        return msg;
+    }
+
+    public void returnGptMessageWithKey(JedgeTalkSession s, String key, String txtMessage,
+                                        boolean isEndMsg , boolean isErr ) {
+        module.exMarkLog(String.format("Corpus More >>%s",txtMessage));
+        QIData msg = makeGptReturnMessage(txtMessage, isEndMsg, isErr);
+        msg.setKey(key);
+        postServiceMessage(s.srcMod, s.cbUri, msg);
+    }
+
+    public void makeRealGptStreamCall(JedgeTalkSession s, String realPrompt) {
+        //发起gpt的stream请求
+        // chatGLM的聊天
+        // // "history":[["你好","你好👋!我是人工智能助手 ChatGLM-6B,很高兴见到你,欢迎问我任何问题。"]
+        QIData gr = new QData();
+        gr.put("history", s.history_);
+        gr.put("query", realPrompt);
+        gr.put("cb",  "/gpt/LocalStream");//本地stream返回
+        gr.put("sid", s.sid);
+        QIData p = postServiceRequest(sval_module_llm, "/glm6b/stream", gr);
+        if (!JMgbusUtil.isMgbusResultOk(p)) {
+            returnGptMessage(s, "中转服务器请求Stream GPT数据失败", true, true);
+        }
+    }
+
+    public String makeRealGptChatCall(JedgeTalkSession s, String realPrompt) {
+        QIData gr = new QData();
+//        gr.put("history", s.history_);
+        gr.put("query", realPrompt);
+        gr.put("cb",  "/gpt/LocalStream");//本地stream返回
+        gr.put("sid", s.sid);
+        JMgbusUtil.setRequestTimeout(gr,60000);
+        QIData p = postServiceRequest(sval_module_llm, "/glm6b/chat", gr);
+        if (!JMgbusUtil.isMgbusResultOk(p)) {
+            returnDirectGptMessage(s, "中转服务器请求Chat GPT数据失败", true, true);
+            return null;
+        } else {
+            return p.getString("r");
+        }
+    }
+
+    public QIData getJsonResultForPrompt(JedgeTalkSession s, String realPrompt) {
+        String result = makeRealGptChatCall(s, realPrompt);
+        if(result!=null) {
+            result = result.trim();
+            result = JedgeLlmUtil.unescapeString(result);
+            QIData re = QData.fromString(result);
+            if(!re.isEmpty()) {
+                return re;
+            } else {
+                result = JedgeLlmUtil.unescapeString(result);
+                re = QData.fromString(result);
+                if(!re.isEmpty())
+                    return re;
+            }
+            String newResult = StringUtils.getSubStrBetween(result, '{',1,'}',-1);
+            re = QData.fromString(newResult);
+            if(!re.isEmpty())
+                return re;
+            newResult = StringUtils.getSubStrBetween(result, '[',1,']',-1);
+            re = QData.fromString(newResult);
+            if(!re.isEmpty())
+                return re;
+        }
+        return null;
+    }
+
+    private void knTalkLog(String realPrompt) {
+        knTalkLog(realPrompt, false);
+    }
+
+    private void knTalkLog(String prompt, boolean succ) {
+        String log =  "["+ TimeUtils.getNowShortStrWithMillis()+"] ["+(succ?"SUC":"FAL")+"]>>" + prompt;
+        log = log.replaceAll( "\n","");
+        FileUtils.appendLine(kn_talk_log_file, log+"\n", "UTF-8");
+    }
+
+    protected JedgeTalkSession findSession(QIData req, String prompt) {
+        String sid = String.valueOf(req.getInteger("sid"));
+        JedgeTalkSession s = sessions_.get(sid);
+        if(s!=null && req.getBoolean("c", false)) { //清理会话
+            sessions_.remove(sid);
+            s = null;    //清空原会话,重新创建新的对话历史
+        }
+        //更新会话的部分状态
+        if(s!=null) {
+            if(req.containsKey("v") && req.get("v") instanceof Boolean) {
+                s.voiceTalk = req.getBoolean("v", false);
+            }
+            if(req.containsKey("k") && req.get("k") instanceof Boolean) {
+                s.knTalk = req.getBoolean("k", false);
+            }
+            if(req.containsKey("r") && req.get("r") instanceof String) {
+                String Domain = req.getString("r");
+                if(StringUtils.isValidStr(Domain))
+                    s.domain = Domain;
+            }
+            if(req.containsKey("n") && req.get("n") instanceof Integer) {
+                s.rn = req.getInteger("n");
+            }
+            s.srcMod = JMgbusUtil.GetMgModSource(req);
+            // s.cbUri = req.getString("cb", "/spmp/PostGptStream");
+        } else {
+            //创建新的Session
+            boolean localGlm = req.getBoolean("l", true);
+            boolean knTalk = req.getBoolean("k", false);
+            int rn = req.getInteger("n", 6);
+            boolean printSingle = req.getBoolean("talkStream", true);
+            boolean isStream = req.getBoolean("stream", true); //默认是stream对话
+
+            String currentKnBase = null;
+            if((req.containsKey("knn"))) {
+                Object val = req.get("knn");
+                if(val instanceof String)
+                    currentKnBase = (String) val;
+                else
+                    currentKnBase = sval_default_app;
+            } else {
+                //提供一个默认的知识库对话窗口。
+                currentKnBase = sval_default_app;
+            }
+            s = new JedgeTalkSession(this, llmCtx);
+            s.currentKnBase = currentKnBase;
+            s.srcMod = JMgbusUtil.GetMgModSource(req);
+            s.cbUri = req.getString("cb", "/spmp/PostGptStream");
+            s.sid = 0x10000000 + NumberUtils.randomLong(0x6FFFFFFF);
+            sid = String.valueOf(s.sid);
+            s.printSingle = printSingle;
+
+            s.rn = rn;
+            s.localGlm = localGlm;
+            s.knTalk = knTalk;
+            s.voiceTalk = req.getBoolean("v", false);
+            s.isStream = isStream;
+
+            synchronized (sessions_) {
+                sessions_.put(sid, s);
+            }
+        }
+
+        s.currentPrompt = prompt;
+        return s;
+    }
+
+    public JMgbusModual getModule() {
+        return module;
+    }
+
+    public JedgeSmartTaskContext getLLMContext() {
+        return smartTaskContext;
+    }
+
+    public boolean isActive() {
+        return active_;
+    }
+}

+ 309 - 0
app/src/main/java/com/ch/jedge/jbot2/JedgeVoiceBotService.java

@@ -0,0 +1,309 @@
+package com.ch.jedge.jbot2;
+
+import com.ch.jedge.utils.JedgeBotConfigUtil;
+import com.changhong.jedge.JMgbusModual;
+import com.changhong.jedge.JMgbusService;
+import com.changhong.jedge.MgbusApi;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.StringUtils;
+import com.changhong.qlib.util.sync.SystemUtils;
+
+import java.util.LinkedList;
+
+public class JedgeVoiceBotService  extends JMgbusService  {
+    protected String inputVoiceMode;
+    protected String talkUri = "/gpt/knTalk";;
+    private static final String vkey_voice_event_text_ = "textOk";
+    private static final String vkey_voice_event_wakeup_ = "wakeUp";
+    private static final String vkey_voice_text_handle_text_ = "text";
+    private static final String vkey_voice_text_handle_end_ = "end";
+    private static final String vkey_voice_text_handle_result_type_ = "result";
+    private static final String vkey_voice_text_handle_result_response_ = "text";
+    private static final String vkey_voice_text_handle_result_sessoin_ = "edge_session";
+    private static final String sval_uri_text_handle_result_ = "/dds/textHandleResult";
+
+
+    boolean vui_going_ = false;
+    private boolean quit_ = false;
+    private boolean active_ = false;
+    private final LinkedList<String> gptSpeakList = new LinkedList<>();
+
+    int timeOutCount = 0;
+    int speakCount = 0;
+    private final SentenceSplitter sentSpliter = new SentenceSplitter();
+
+    public JedgeVoiceBotService(JMgbusModual glLlmBot) {
+        super(glLlmBot);
+        auto_remove_call_info = false;
+
+        if(StringUtils.isNotValidStr(inputVoiceMode))
+            inputVoiceMode = JedgeBotConfigUtil.getConfigObjSimpleFmtString("jbot","vw", "vui");
+        if(StringUtils.isNotValidStr(inputVoiceMode))
+            inputVoiceMode="vui";
+
+        module.postThread(this::post2TextSpeech);
+        module.postThread(this::checkChatTimeout);
+    }
+
+    private void checkChatTimeout() {
+        while(!quit_) {
+            if(vui_going_) {
+                timeOutCount++;
+                if(timeOutCount > 30) {
+                    module.warnLog("Warning!!! chat return timeout.");
+                    vui_going_ = false;
+                }
+            }
+            SystemUtils.tryWait(1000);
+        }
+    }
+
+    private void post2TextSpeech() {
+        while(!quit_) {
+            speakCount-=250;
+//            QDWarn("speakCount:%d", speakCount);
+            if (speakCount < 1) {
+                //从队列中提取一个消息发送阅读
+                String tts;
+                {
+                    boolean isEmpty;
+                    synchronized (gptSpeakList){
+                        isEmpty = gptSpeakList.isEmpty();
+                    }
+                    if (isEmpty) {
+                        SystemUtils.tryWait(250);
+                        speakCount = 0;
+                        continue;
+                    }
+                    synchronized (gptSpeakList) {
+                        tts = gptSpeakList.pop();
+                    }
+                }
+
+                if(StringUtils.isNotValidStr(tts))
+                    continue;
+                if("[DONE]".equals(tts)) {
+//                    speakText("完毕");
+                    vui_going_ = false;
+                    continue;
+                } else if (StringUtils.isValidStr(tts)) {
+                    //计算speakCount长度
+                    speakCount = tts.length() * 71 * 3;
+                    module.warnLog(String.format("Reset speakCount:%d", speakCount));
+                    speakText(tts);
+                    timeOutCount = 0;
+                }
+            }
+            SystemUtils.tryWait(250);
+        }
+        active_ = false;
+    }
+
+    private void speakText(String text) {
+        module.exMarkLog(String.format("TO SPEECH=>>:%s", text));
+        QIData d = new QData();
+        d.putString("text", text).putInteger("p",1);
+        postServiceMessage(inputVoiceMode, "/dds/speakText", d);
+    }
+
+    @Override
+    public void onServiceStart() {
+        super.onServiceStart();
+
+        watchLocalEvent("OnModuleConnected", (w, keyVal, msg) -> {
+            String host = msg.getString("host");
+            int port = msg.getInteger("port");
+            this.onMgbusConnected(host, port);
+            return true;
+        });
+
+        watchLocalEvent("OnModuleDisconnected", (w, keyVal, msg) -> {
+            String host = msg.getString("host");
+            int port = msg.getInteger("port");
+            this.onMgbusDisconnected(host, port);
+            return true;
+        });
+    }
+
+    //module掉线,啥也不做
+    private void onMgbusConnected(String host, int port) {
+        watchMgbusEvent("online", "vui", msg -> {
+            String mode = msg.getString("module");
+            String mgnet = msg.getString("mgnet");
+            boolean isLocal = msg.getBoolean("local", false);
+            if(isLocal && mode.contains(mgnet))
+                mode = "vui";
+            if (inputVoiceMode.equals(mode) || isLocal) {
+                watchVoiceModule();
+            }
+        });
+        watchMgbusEvent("offline", "vui", msg -> {
+            String mode = msg.getString("module");
+            if (inputVoiceMode.equals(mode)) {
+                removeVoiceWatch();
+            }
+        });
+    }
+
+    private void onMgbusDisconnected(String host, int port) {
+
+    }
+
+    private void removeVoiceWatch() {
+        clearEventWatcher(inputVoiceMode, "textOk","/@c/_ecb" ,"@");
+    }
+
+    private void watchVoiceModule() {
+        watchServiceEvent(inputVoiceMode, vkey_voice_event_text_, eMsg -> {
+            boolean isFinal = eMsg.getBoolean(vkey_voice_text_handle_end_, false);
+            int session = eMsg.getInteger(vkey_voice_text_handle_result_sessoin_);  //可能为空,则单独创建一个对话ID
+            String text = eMsg.getString(vkey_voice_text_handle_text_);
+            module.exMarkLog(String.format("...>>[text]: %s", text));
+            if (isFinal) {
+                //auto mod = GetEventSrc(eMsg); //GetEventSrc(eMsg);
+                /* !mod.empty() && mod == (inputVoiceMode) &&  */
+                if (StringUtils.isValidStr(text) ) {
+                    handleGptVoiceText(session, text);
+                }
+            }
+        });
+
+        watchServiceEvent(inputVoiceMode, vkey_voice_event_wakeup_, eMsg -> {
+            String deviceId = eMsg.getString("deviceId");
+            if(module.getHostSn().contains(deviceId)) {
+                //本地唤醒
+//                module.exMarkLog(String.format("...>>[wakeupEvent]: %s", deviceId));
+                vui_going_ = false;
+                synchronized (this.gptSpeakList) {
+                    gptSpeakList.clear();
+                }
+            }
+        });
+
+    }
+
+    private void handleGptVoiceText(int session, String text) {
+        module.exMarkLog(String.format("[%d]:Voice Text pushed incoming = %s", session, text));    //中间数据
+        //设置返回参数
+        //处理流程 找到对话,再在对话的处理流程中、处理语义的应答信息。根据不同的语境,做出不同的应答。
+        {
+            if ((text.startsWith("退出") || text.startsWith("停止"))) {
+                synchronized (gptSpeakList) {
+                    gptSpeakList.clear();
+                }
+                //快速回答,接管语义处理
+                returnVoiceResult(inputVoiceMode, session, 0, "好的、再见");
+
+                vui_going_ = false;
+                timeOutCount = 0;
+                return;
+            } else if(vui_going_) {
+                module.exMarkLog("已在对话中……");
+                return;
+            } else {
+                timeOutCount = 0;
+                vui_going_ = true;
+                //快速回答,接管语义处理
+                returnVoiceResult(inputVoiceMode, session, 0, "稍等");
+            }
+        }
+        QIData r = new QData();
+        //发送talk请求到mgs,并将分别返回的结果进入队列
+        r.putString("text", text);
+        r.putString("knn", "testGPT");
+        r.putBoolean("stream", true);
+        r.putBoolean("k", true);
+        r.putBoolean("v", true);
+        r.putString("cb", "/gptv/streamBack");
+        r.putBoolean("talkStream", true);     //按句回推
+        postServiceMessage("jbot", talkUri, r);
+    }
+
+    private void returnVoiceResult(String mod, int session, int type, String answerText) {
+        QIData eMsg = new QData();
+        eMsg.putInteger(vkey_voice_text_handle_result_type_, type);  //继续对话
+        eMsg.putString(vkey_voice_text_handle_result_response_, answerText);
+        eMsg.putInteger(vkey_voice_text_handle_result_sessoin_, session);
+        postServiceMessage(mod, sval_uri_text_handle_result_, eMsg);
+        module.exMarkLog(String.format("Post2Speech:%s", answerText));
+    }
+
+    @Override
+    public void onServiceStop() {
+        this.quit_ = true;
+        while(active_) {
+            SystemUtils.tryWait(20);
+        }
+        super.onServiceStop();
+    }
+
+
+    //组装成正常的语句
+    @MgbusApi
+    public QIData streamBack(QIData req) {
+        timeOutCount = 0;
+        boolean next_vui_going_ = !req.getBoolean("end", false);
+        String streamText = req.getString("msg");
+        module.printClientLog(String.format("GPT>>(Going:%s):%s", next_vui_going_?"true":"false", streamText));
+        if(StringUtils.isValidStr(streamText) && !streamText.startsWith("[") && !streamText.startsWith("$")) {
+            streamText = streamText.replaceAll("\n", ""); //去掉空行
+            if (vui_going_ && !streamText.isEmpty()) {
+                synchronized (gptSpeakList) {
+                    String str = sentSpliter.pushChars(streamText);
+                    while (StringUtils.isValidStr(str)) {
+                        module.highLog(String.format("GPT>>(Going:%s):%s", vui_going_ ? "true" : "false", str));
+                        gptSpeakList.add(str);
+                        str = sentSpliter.getNextString();
+                    }
+                }
+            }
+        }
+        if(!next_vui_going_) {
+            String str = sentSpliter.getNextString();
+            synchronized (gptSpeakList) {
+                if(StringUtils.isValidStr(str)) {
+                    gptSpeakList.add(str);
+                }
+                gptSpeakList.add("[DONE]");
+            }
+        }
+        return null;
+    }
+
+    private static class SentenceSplitter {
+        StringBuilder buffer = new StringBuilder();
+        String leftStr = null;
+        public String pushChars(String streamText) {
+            int streamTextLen = streamText.length();
+            for(int i=0;i<streamTextLen;i++) {
+                char subChar = streamText.charAt(i);
+                switch (subChar) {
+                    case ',':
+//                    case '.':
+                    case '!':
+                    case ';':
+                    case '?':
+                    case ',':
+                    case '。':
+                    case '!':
+                    case '?':
+                        leftStr = streamText.substring(i+1);
+                        String result = buffer.toString();
+                        buffer = new StringBuilder();
+                        return result;
+                    default:
+                        buffer.append(subChar);
+                }
+            }
+            return null;
+        }
+
+        public String getNextString() {
+            if(StringUtils.isValidStr(leftStr))
+                return pushChars(leftStr);
+            else
+                return null;
+        }
+    }
+}

+ 55 - 0
app/src/main/java/com/ch/jedge/jbot2/context/JedgeSmartTaskContext.java

@@ -0,0 +1,55 @@
+package com.ch.jedge.jbot2.context;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMAgent;
+import com.ch.jedge.jbot2.llm.world.JedgeLLMDmWorld;
+import com.ch.jedge.jbot2.llm.world.knl.JedgeLLMKnWorld;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMTaskPool;
+
+public class JedgeSmartTaskContext extends JedgeLLMBaseObject {
+    private final JedgeLLMKnWorld KnWorld;
+    private final JedgeLLMDmWorld dmWorld;
+    private final JedgeLLMTaskPool  taskPool;
+
+    public JedgeSmartTaskContext(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+        //世界知识
+        KnWorld = new JedgeLLMKnWorld(llmCtx);
+        //世界模型
+        dmWorld = new JedgeLLMDmWorld(llmCtx);
+        //加载系统默认的领域
+        dmWorld.loadStaticWorldDefs("./cfg/llm/world");
+        //世界任务池
+        taskPool = new JedgeLLMTaskPool(llmCtx, KnWorld, dmWorld);
+        //启动任务池
+        taskPool.startTaskWatching();
+    }
+
+
+    public void handleTask(JedgeTalkSession s, String query) {
+        //判断当前的语句与当前任务的相关性
+        JedgeLLMAgent agent = taskPool.tryHandleTask(s, query);
+        //这是一个实时指令,在任务之外执行的指令。
+        if(agent!=null) {
+            return;
+        }
+        //既不属于当前的任务、也无法创建一个新的任务,就像问候语一样。
+        //系统当作一个无意义的问题进行处理。通过提示词,根据用户的输入,和世界的领域和能力,告诉用户能够做什么、引导用户提问。
+        taskPool.makeUnhandledQueryResponse(s, query);
+    }
+
+    public JedgeLLMKnWorld getKnWorld() {
+        return KnWorld;
+    }
+
+    public JedgeLLMDmWorld getDmWorld() {
+        return dmWorld;
+    }
+
+    public void onHeartbeat() {
+        KnWorld.onHeartbeat();
+        dmWorld.onHeartbeat();
+        taskPool.onHeartbeat();
+    }
+}

+ 99 - 0
app/src/main/java/com/ch/jedge/jbot2/context/JedgeTalkSession.java

@@ -0,0 +1,99 @@
+package com.ch.jedge.jbot2.context;
+
+import com.alibaba.fastjson.JSONArray;
+import com.ch.jedge.jbot2.JedgeBotGptService;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.JedgeLLMDmWorld;
+import com.ch.jedge.jbot2.llm.world.knl.JedgeLLMKnWorld;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMTaskPool;
+import com.changhong.qlib.util.Log.ISLog;
+
+import static com.ch.jedge.utils.JedgeBotConst.max_history_size;
+import static com.ch.jedge.utils.JedgeBotConst.max_history_total_size;
+
+
+/**
+ *  聊天的会话,需要记录 领域、话题 等约束
+ *      需要就如下内容进行缓存。
+ *
+ *      - 知识:
+ *      - 词库:
+ *      - 意图
+ *      - 活跃关键字: (知识点)
+ *
+ *  在对话过程中,系统需要根据聊天的内容,手动或动态切换领域和话题等。
+ *      领域将更新其后台知识库, 每次命中,可切换其关键字池、
+ *      · 加载默认的知识条目、加载其知识条目下的关键字,引导查询多条知识条目在当前对话缓存中
+ *      ·
+ */
+
+
+
+public class JedgeTalkSession {
+
+    public boolean isStream = true;
+    public long sid = 0;
+    public String currentPrompt = "";
+    public String currentResponse = "";
+
+    public boolean knTalk;
+    public boolean voiceTalk;
+    public String domain;
+    public int rn = 6;
+    public String currentKnBase;
+    public boolean localGlm = true;
+
+    public String srcMod;
+    public String cbUri;
+
+    public boolean printSingle = true;
+    public long ttl;
+    public final JSONArray history_ = new JSONArray();
+    private final JedgeBotGptService mBotService;
+    private final JedgeLLMTaskPool taskPool;
+
+    public JedgeTalkSession(JedgeBotGptService service, JedgeLLMContext llmCtx) {
+        mBotService = service;
+        //世界任务池
+        JedgeLLMKnWorld KnWorld = service.getLLMContext().getKnWorld();
+        JedgeLLMDmWorld worldModel =  service.getLLMContext().getDmWorld();
+        taskPool = new JedgeLLMTaskPool(llmCtx, KnWorld, worldModel);
+        //启动会话任务池
+        taskPool.startTaskWatching();
+    }
+
+    public void clearHistory() {
+        history_.clear();
+    }
+
+    public void append2LastQA(String returnText) {
+        ttl = 0;
+//        mGdeApp.getMgModule().infoLog(String.format(">>%s", returnText));
+        currentResponse += (returnText);
+//        mGdeApp.getMgModule().printClientLog(String.format(">>%s", currentResponse));
+    }
+
+    public void buildLastHistory() {
+        JSONArray qa = new JSONArray();
+        qa.add(currentPrompt);
+        qa.add(currentResponse);
+        currentResponse = "";
+        currentPrompt = "";
+        history_.add(qa);
+        if(history_.size()>max_history_size) {
+            history_.remove(0); //移除第0个聊天历史
+        }
+        String log;
+        log =  history_.toJSONString();
+        while (log.length()>max_history_total_size) {  //指令缓存最大256个
+            history_.remove(0); //移除第0个聊天历史
+            log =  (history_.toJSONString());
+        }
+        ISLog.fastLog(log);
+        ttl = 0;
+    }
+
+    public JedgeBotGptService getGptBotService() {
+        return mBotService;
+    }
+}

+ 61 - 0
app/src/main/java/com/ch/jedge/jbot2/dict/JEDictUtil.java

@@ -0,0 +1,61 @@
+package com.ch.jedge.jbot2.dict;
+
+import com.changhong.qlib.util.StringUtils;
+import com.changhong.qlib.util.file.FileUtils;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 词库工具
+ *      扫描词库目录下的通用词库文件,并动态添加到Jieba分词词库中
+ *
+ */
+public class JEDictUtil {
+
+    public static void loadStaticCommonDicts(final Map<String, JedgeWordDict> dictMap, String dictPath) {
+
+        FileUtils.listDirFiles(dictPath, file -> {
+            JedgeWordDict dict = loadStaticCommonDict(dictPath, file);
+            if(dict!=null) {
+                synchronized (dictMap) {
+                    dictMap.put(dict.getName(), dict);
+                }
+            }
+            return null;
+        });
+    }
+
+    public static JedgeWordDict loadStaticCommonDict(String dictPath, File file) {
+        if(file==null || !file.getName().endsWith(".dic") || !file.exists())
+            return null;
+        List<String> lines = StringUtils.readStringLinesFromFile(file);
+        JedgeWordDict dict = new JedgeWordDict(file.getAbsolutePath());
+        String[] defaultWordDef = "100 nz".split(" ");
+        for(String l : lines) {
+            //添加词汇
+            if(StringUtils.isNotValidStr(l) || l.charAt(0) == '#')
+                continue;
+            if(l.startsWith("$")) {
+                defaultWordDef = l.substring(1).trim().split(" ");
+                continue;
+            }
+            String[] disc = l.split(" ");
+            int idx = 0;
+            String[] realWord = new String[3];
+            for(String d : disc) {
+                if(d.isEmpty()) continue;
+                realWord[idx++] = d;
+                if(idx==3) break;
+            }
+            String word = realWord[0],
+                    freq = realWord[1]==null?defaultWordDef[0]:realWord[1],
+                    type = realWord[2]==null?defaultWordDef[1]:realWord[2];
+            if(StringUtils.isNotValidStr(word)) continue;
+            dict.appendWordItem(new JedgeWordItem(word, type, freq, false));
+        }
+        if(!dict.isEmpty()) return dict;
+        return null;
+    }
+}

+ 82 - 0
app/src/main/java/com/ch/jedge/jbot2/dict/JedgeWordDict.java

@@ -0,0 +1,82 @@
+package com.ch.jedge.jbot2.dict;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.ch.jedge.jbot.other.knbase.JedgeGptDomain;
+import com.changhong.qlib.util.file.FileUtils;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *  词库:
+ *      动态加载,可从知识切片、领域或通用预置词库中加载
+ */
+public class JedgeWordDict {
+    private final String name_;
+
+    public String dictPath;
+
+    public int ttl;     //对话一旦进入非专业领域后,在一定时间内自动清理。
+
+    protected final Map<String, JedgeWordItem> wordItems = new HashMap<>();
+
+    public JedgeWordDict(String path) {
+        this.dictPath = path;
+        this.name_ = FileUtils.getFilenameWithoutExt(path);
+    }
+
+    public JedgeWordDict(JedgeGptDomain domain) {
+        this.dictPath = domain.getFilePath();
+        this.name_ = domain.getDomainName();
+    }
+
+    public void appendWordItem(JedgeWordItem jedgeWordItem) {
+        synchronized (wordItems) {
+            JedgeWordItem old = wordItems.get(jedgeWordItem.word);
+            if(old!=null) {
+                old.freq += jedgeWordItem.freq;
+                old.isGated = old.isGated || jedgeWordItem.isGated;
+            } else
+                wordItems.put(jedgeWordItem.word, jedgeWordItem);   //覆盖前面的重复词汇
+        }
+    }
+
+    public String getName() {
+        return name_;
+    }
+
+    public boolean isEmpty() {
+        synchronized (wordItems) {
+            return wordItems.isEmpty();
+        }
+    }
+
+    public JSONArray getWordItemsInJson(boolean gated) {
+        JSONArray re = new JSONArray();
+        synchronized (wordItems) {
+            for (JedgeWordItem entry : wordItems.values()) {
+                if(gated!=entry.isGated) continue;
+                JSONObject obj = new JSONObject();
+                obj.put("w", entry.word);
+                obj.put("t", entry.type);
+                obj.put("f", entry.freq);
+                re.add(obj);
+            }
+        }
+        return re;
+    }
+
+    public Collection<JedgeWordItem> getWordItems() {
+        synchronized (wordItems) {
+            return wordItems.values();
+        }
+    }
+
+    public JedgeWordItem getWordItem(String word) {
+        synchronized (wordItems) {
+            return wordItems.get(word);
+        }
+    }
+}

+ 17 - 0
app/src/main/java/com/ch/jedge/jbot2/dict/JedgeWordItem.java

@@ -0,0 +1,17 @@
+package com.ch.jedge.jbot2.dict;
+
+import com.changhong.qlib.util.StringUtils;
+
+public class JedgeWordItem {
+    public String word;
+    public String type;
+    public int freq;
+    public boolean isGated;
+
+    public JedgeWordItem(String word, String type, String freq, boolean isGateWord) {
+        this.word = word;
+        this.type = type;
+        this.freq = StringUtils.toInteger(freq,100);
+        this.isGated = isGateWord;
+    }
+}

+ 12 - 0
app/src/main/java/com/ch/jedge/jbot2/intent/JDataGetter.java

@@ -0,0 +1,12 @@
+package com.ch.jedge.jbot2.intent;
+
+
+import java.lang.annotation.*;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface JDataGetter {
+    String mod() default "";
+    String uri() default "";
+}

+ 11 - 0
app/src/main/java/com/ch/jedge/jbot2/intent/JbotApi.java

@@ -0,0 +1,11 @@
+package com.ch.jedge.jbot2.intent;
+
+
+import java.lang.annotation.*;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface JbotApi {
+    String domain();
+}

+ 13 - 0
app/src/main/java/com/ch/jedge/jbot2/intent/JbotClass.java

@@ -0,0 +1,13 @@
+package com.ch.jedge.jbot2.intent;
+
+
+import java.lang.annotation.*;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface JbotClass {
+    String domain();
+    String words();
+    boolean nullable() default true;
+}

+ 13 - 0
app/src/main/java/com/ch/jedge/jbot2/intent/JbotObject.java

@@ -0,0 +1,13 @@
+package com.ch.jedge.jbot2.intent;
+
+
+import java.lang.annotation.*;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface JbotObject {
+    String domain();
+    String words();
+    boolean nullable() default true;
+}

+ 11 - 0
app/src/main/java/com/ch/jedge/jbot2/intent/JedgeDomain.java

@@ -0,0 +1,11 @@
+package com.ch.jedge.jbot2.intent;
+
+
+import java.lang.annotation.*;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface JedgeDomain {
+    String domains();
+}

+ 427 - 0
app/src/main/java/com/ch/jedge/jbot2/intent/JedgeJBotCBService.java

@@ -0,0 +1,427 @@
+package com.ch.jedge.jbot2.intent;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.ch.jedge.utils.JedgeLlmUtil;
+import com.changhong.jedge.JMgbusModual;
+import com.changhong.jedge.JMgbusService;
+import com.changhong.jedge.JMgbusUtil;
+import com.changhong.jedge.MgbusApi;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.intf.QIDataList;
+import com.changhong.qlib.util.StringUtils;
+import com.changhong.qlib.util.sync.SystemUtils;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.ch.jedge.utils.JedgeBotConst.sval_default_jbot_jedge;
+
+
+public class JedgeJBotCBService extends JMgbusService {
+
+    private boolean is_active_ = false;
+    private boolean is_online_ = false;
+    private final Object quitCtrl = new Object();
+    //已扫描到的Domain信息
+    private final Map<String, QIData> registeredDomainMap = new HashMap<>();    //已扫描到的Domain信息
+    private final Map<String, QIData> registeredDmDomainMap = new HashMap<>();
+    //已扫描到的数据集信息
+    private final Map<String, JSONArray> registeredObjectsMap = new HashMap<>();
+    private final Map<String, Integer> registeredClassesMap = new HashMap<>();
+    //数据集获取API的信息
+    private final Map<String, Method> DataGetterMap = new HashMap<>();
+
+    public JedgeJBotCBService(JMgbusModual holder) {
+        super(holder);
+        auto_remove_call_info = false;
+        watchLocalEvent("OnModuleConnected", (w, keyVal, msg) -> {
+            String host = msg.getString("host");
+            int port = msg.getInteger("port");
+            is_online_ = true;
+            watchMgbusEvent("online", "jbot!", qiData -> {
+                synchronized (quitCtrl) {
+                    if (!is_active_) {
+                        is_active_= true;
+                        module.postThread(this::refreshJBotRegister);
+                    }
+                }
+                synchronized (quitCtrl) {
+                    quitCtrl.notifyAll();
+                }
+            });
+            //要注意定时刷新
+            return true;
+        });
+
+        watchLocalEvent("OnModuleDisconnected", (w, keyVal, msg) -> {
+            String host = msg.getString("host");
+            int port = msg.getInteger("port");
+            is_online_ = false;
+            synchronized (quitCtrl) {
+                quitCtrl.notifyAll();
+            }
+            return true;
+        });
+    }
+
+    @Override
+    public void onServiceStop() {
+        is_online_ = false;
+        quitCtrl.notifyAll();
+        super.onServiceStop();
+    }
+
+    private void prepareDomainInfo() {
+        JedgeDomain domain = this.getClass().getAnnotation(JedgeDomain.class);
+        if(domain!=null) {
+            String dmList = domain.domains();
+            if(StringUtils.isValidStr(dmList)) {
+                for(String dmDef : dmList.split(",")) {
+                    registerDynamicDomain(dmDef);
+                }
+            }
+        }
+    }
+
+    private void registerDynamicDomain(String dmDef) {
+        if(!StringUtils.isValidStr(dmDef)) return;
+        File file = new File(dmDef + '/' + "domain.json");
+        if(!file.exists()) {
+            module.errLog("domain def not exists %s", dmDef);
+            return;
+        }
+        QIData df = QData.fromFile(file);
+        if(df.isEmpty())  {
+            module.errLog("domain def is empty : %s", dmDef);
+            return;
+        }
+
+        synchronized (registeredDomainMap) {
+            registeredDomainMap.put(df.getKey(), df);
+        }
+
+        synchronized (registeredDmDomainMap) {
+            registeredDmDomainMap.put(df.getKey(), df);
+        }
+    }
+
+    private void refreshJBotRegister() {
+        while(is_online_) {
+            //准备数据集API
+            prepareDataSetMethods();
+
+            //注册领域
+            prepareDomainInfo();
+
+            //注册API回调信息
+            autoRegisterJBotAPIDomains();
+
+            //注册数据集(允许提取的常量参数或对象的状态等)
+            autoRegisterJBotDataSets();
+
+            SystemUtils.tryWaitforSingal(quitCtrl, 90 * 1000);
+        }
+        is_active_ = false;
+    }
+
+    private void prepareDataSetMethods() {
+        Class<?> cls = this.getClass();
+        while(JMgbusService.class.isAssignableFrom(cls)) {
+            Method[] methods = cls.getDeclaredMethods();
+            for (Method m : methods) {
+                JDataGetter a = m.getAnnotation(JDataGetter.class);
+                if (a != null) {
+                    Class<?> r = m.getReturnType();
+                    Class<?>[] pm = m.getParameterTypes();
+                    if (List.class.isAssignableFrom(r) && pm.length == 0) {
+                        registerJBotDataGetter(a, m.getName(), m);
+                    }
+                }
+            }
+            cls = cls.getSuperclass();
+        }
+    }
+
+    private void autoRegisterJBotDataSets() {
+        Class<?> cls = this.getClass();
+        while(JedgeJBotCBService.class.isAssignableFrom(cls)) {
+            Method[] methods = cls.getDeclaredMethods();
+            for (Method m : methods) {
+                JbotClass  c = m.getAnnotation(JbotClass.class);
+                if(c!=null) {
+                    Class<?> r = m.getReturnType();
+                    Class<?>[] pm = m.getParameterTypes();
+                    if (List.class.isAssignableFrom(r) && pm.length == 0) {
+                        String cbUri = String.format("/%s/%s", getName(), m.getName());
+                        registerJBotClasses(c, m.getName(), module.getName(), cbUri);
+                    }
+                }
+                JbotObject a = m.getAnnotation(JbotObject.class);
+                if (a != null) {
+                    Class<?> r = m.getReturnType();
+                    Class<?>[] pm = m.getParameterTypes();
+                    if (List.class.isAssignableFrom(r) && pm.length == 0) {
+                        String cbUri = String.format("/%s/%s", getName(), m.getName());
+                        registerJBotObjects(a, m.getName(), module.getName(), cbUri);
+                    }
+                }
+            }
+            cls = cls.getSuperclass();
+        }
+    }
+
+
+    protected void autoRegisterJBotAPIDomains() {
+        Class<?> cls = this.getClass();
+        while(JMgbusService.class.isAssignableFrom(cls)) {
+            Method[] methods = cls.getDeclaredMethods();
+            for (Method m : methods) {
+                JbotApi a = m.getAnnotation(JbotApi.class);
+                if (a != null) {
+                    Class<?> r = m.getReturnType();
+                    Class<?>[] pm = m.getParameterTypes();
+                    if (QIData.class.isAssignableFrom(r) && pm.length == 1 && QIData.class.isAssignableFrom(pm[0])) {
+                        String cbUri = String.format("/%s/%s", getName(), m.getName());
+                        String domain = a.domain();
+                        registerJBotKnDomain(domain, module.getName(), cbUri);
+                        //先注册DmDomain
+                        registerJBotDmDomain(domain, module.getName(), cbUri);
+                    }
+                }
+            }
+            cls = cls.getSuperclass();
+        }
+    }
+
+    private void registerJBotKnDomain(String domain, String moduleName, String cbUri) {
+        QIData req = new QData().putString("cbUri", cbUri).putString("src", moduleName)
+                .putString("key", domain);
+        synchronized (registeredDomainMap){
+            if(registeredDomainMap.containsKey(domain)) {
+                req.copyFrom(registeredDomainMap.get(domain), "disc","picker","dmid");
+            }
+        }
+
+        QIData re = postServiceRequest(sval_default_jbot_jedge, "/gpt/registerDomain", req);
+        if(!JMgbusUtil.isMgbusResultOk(re)) {
+            module.errLog(String.format("知识领域注册失败 (%s) to %s : %s " , domain, sval_default_jbot_jedge, re.getString("msg")));
+        } else {
+            int dmId = re.getInteger("dmid");
+            if(dmId>-1) {
+                synchronized (registeredDomainMap) {
+                    registeredDomainMap.get(domain).putInteger("dmid", dmId); //也可能更新module
+                }
+            }
+            module.exMarkLog(String.format("知识领域注册成功:%s (%d)", domain, dmId));
+        }
+    }
+
+    private void registerJBotDmDomain(String domain, String moduleName, String cbUri) {
+        QIData req = new QData().putString("cbUri", cbUri).putString("src", moduleName)
+                .putString("key", domain);
+        synchronized (registeredDmDomainMap){
+            if(registeredDmDomainMap.containsKey(domain)) {
+                req.copyFrom(registeredDmDomainMap.get(domain), "disc","picker","dmid");
+            }
+        }
+
+        QIData re = postServiceRequest(sval_default_jbot_jedge, "/gpt/registerDmDomain", req);
+        if(!JMgbusUtil.isMgbusResultOk(re)) {
+            module.errLog(String.format("模型领域注册失败 (%s) to %s : %s " , domain, sval_default_jbot_jedge, re.getString("msg")));
+        } else {
+            int dmId = re.getInteger("dmid");
+            if(dmId>-1) {
+                synchronized (registeredDmDomainMap) {
+                    registeredDmDomainMap.get(domain).putInteger("dmid", dmId); //也可能更新module
+                }
+            }
+            module.highLog(String.format("模型领域注册成功:%s (%d)", domain, dmId));
+        }
+    }
+
+    private void registerJBotObjects(JbotObject a, String initSet, String moduleName, String cbUri) {
+        String words = a.words();
+        String domain = a.domain();
+        boolean isNullable = a.nullable();
+        // 调用初始化函数得到
+        JSONArray initData = null;
+        synchronized (DataGetterMap) {
+            Method m = DataGetterMap.get(initSet);
+            if (m != null) {
+                try {
+                    Object re = m.invoke(this);
+                    if(re instanceof List) {
+                        initData =JedgeLlmUtil.JsonArrayFromStringList((List<String>)re);
+                    }
+                } catch (IllegalAccessException | InvocationTargetException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        QIData req = new QData().putString("domain", domain).putString("cbUri", cbUri)
+                .putString("src", moduleName).putString("key", words)
+                .putBoolean("na", isNullable);
+        if(initData!=null) req.put("payload", initData);
+        synchronized (registeredDmDomainMap){
+            if(registeredDmDomainMap.containsKey(domain)) {
+                req.copyFrom( registeredDmDomainMap.get(domain),"dmid");
+            } else {
+                module.errLog(String.format("Fail to register DataSet!! (%s) to %s : domain %s 尚未注册 " , words, sval_default_jbot_jedge, domain));
+                return;
+            }
+        }
+        synchronized (registeredObjectsMap) {
+            if(registeredObjectsMap.containsKey(words)) {
+                req.put("doid", registeredObjectsMap.get(words));
+            }
+        }
+        QIData re = postServiceRequest(sval_default_jbot_jedge, "/gpt/registerDmObject", req);
+        if(!JMgbusUtil.isMgbusResultOk(re)) {
+            module.errLog(String.format("模型对象集注册失败!! (%s) to %s : %s " , words, sval_default_jbot_jedge, re.getString("msg")));
+        } else {
+            JSONArray dsId = re.getJsonArray("doid");
+            if(dsId!=null && !dsId.isEmpty()) {
+                synchronized (registeredObjectsMap) {
+                    registeredObjectsMap.put(words, dsId);  //也可能更新module
+                }
+            }
+            module.markLog(String.format("模型对象集注册成功:%s(%s)", words, dsId));
+        }
+    }
+
+    private void registerJBotClasses(JbotClass a, String initSet, String moduleName, String cbUri) {
+        String words = a.words();
+        String domain = a.domain();
+        boolean isNullable = a.nullable();
+        // 调用初始化函数得到
+        JSONArray initData = null;
+        synchronized (DataGetterMap) {
+            Method m = DataGetterMap.get(initSet);
+            if (m != null) {
+                try {
+                    Object re = m.invoke(this);
+                    if(re instanceof List) {
+                        initData =JedgeLlmUtil.JsonArrayFromStringList((List<String>)re);
+                    }
+                } catch (IllegalAccessException | InvocationTargetException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        QIData req = new QData().putString("domain", domain).putString("cbUri", cbUri)
+                .putString("src", moduleName).putString("key", words)
+                .putBoolean("na", isNullable);
+        if(initData!=null) req.put("payload", initData);
+        synchronized (registeredDomainMap){
+            if(registeredDomainMap.containsKey(domain)) {
+                req.copyFrom( registeredDomainMap.get(domain),"dmid");
+            } else {
+                module.errLog(String.format("Fail to register DataSet (%s) to %s : domain %s 尚未注册 " , words, sval_default_jbot_jedge, domain));
+                return;
+            }
+        }
+        synchronized (registeredDomainMap) {
+            if(registeredDomainMap.containsKey(words)) {
+                req.putInteger("dsid", registeredClassesMap.get(words));
+            }
+        }
+        QIData re = postServiceRequest(sval_default_jbot_jedge, "/gpt/registerKnClass", req);
+        if(!JMgbusUtil.isMgbusResultOk(re)) {
+            module.errLog(String.format("世界类说明注册失败!! (%s) to %s : %s " , words, sval_default_jbot_jedge, re.getString("msg")));
+        } else {
+            int dsId = re.getInteger("dsid");
+            if(dsId>-1) {
+                synchronized (registeredClassesMap) {
+                    registeredClassesMap.put(words, dsId);  //也可能更新module
+                }
+            }
+            module.markLog(String.format("世界对象模型说明注册成功:%s(%d)", words, dsId));
+        }
+    }
+
+
+    //刷新数据集(从Jbot向本地重新请求数据)
+    @MgbusApi
+    public QIData refreshDataSet(QIData eMsg) {
+         String dsKey = eMsg.getKey();
+        module.warnLog("Fresh Data set by : %s", dsKey);    //从数据集名称到dsKey名称
+        if(StringUtils.isValidStr(dsKey)) {
+            JSONArray initData = null;
+            synchronized (DataGetterMap) {
+                Method m = DataGetterMap.get(dsKey);
+                if (m != null) {
+                    try {
+                        Object re = m.invoke(this);
+                        if(re instanceof List) {
+                            initData =JedgeLlmUtil.JsonArrayFromStringList((List<String>)re);
+                        }
+                    } catch (IllegalAccessException | InvocationTargetException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+            return JMgbusUtil.MgbusResult(200, "").put("payload", initData);
+        }
+        //
+        return JMgbusUtil.MgbusResult(401, "No Key");
+    }
+
+
+    private void registerJBotDataGetter(JDataGetter a, String DGetterKey, Method m) {
+        synchronized (DataGetterMap) {
+            DataGetterMap.put(DGetterKey, m);
+        }
+    }
+
+    //扫描所有的无模型模板,并注册到jbot
+    protected List<String> loadClassInfoFromFile(String clsPath) {
+        return new ArrayList<>();
+    }
+
+    public static List<String> loadDataSetFromString(String dsStr) {
+        List<String> re = new ArrayList<>();
+        QIData d  = QData.fromString(dsStr);
+        if(d instanceof QIDataList) {
+            for(Object o : ((QIDataList) d).asJsonArray()) {
+                if(o instanceof JSONObject) {
+                    re.add(((JSONObject) o).toJSONString());
+                }
+            }
+        } else {
+            re.add(d.toJSONString());
+        }
+        return re;
+    }
+
+    //两种情况,一种是json,一种是每行是json
+    public static List<String> loadDataSetFromFile(String fn) {
+        File dsFile = new File(fn);
+        if(dsFile.exists()) {
+            if(fn.endsWith(".ds")) {
+                List<String> lines = StringUtils.readStringLinesFromFile(fn);
+                List<String> re = new ArrayList<>();
+                for(String l : lines) {
+                    l = l.trim();
+                    if(StringUtils.isNotValidStr(l) || l.charAt(0) == '#') continue;
+                    QIData d = QData.fromString(l);
+                    re.add(d.toJSONString());
+                }
+                return re;
+            } else if(fn.endsWith(".json")) {
+                String dsStr = StringUtils.readStringFromFile(dsFile);
+                return loadDataSetFromString(dsStr);
+            }
+        }
+        return new ArrayList<>();
+    }
+
+}

+ 84 - 0
app/src/main/java/com/ch/jedge/jbot2/intent/bot/JBotAppBase.java

@@ -0,0 +1,84 @@
+package com.ch.jedge.jbot2.intent.bot;
+
+import com.ch.jedge.utils.JedgeBotConfigUtil;
+import com.ch.jedge.utils.JedgeBotConst;
+import com.changhong.jedge.JESocketClient;
+import com.changhong.jedge.JMgbusService;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.cmdline.ArgUtils;
+import com.changhong.qlib.util.cmdline.CmdUtils;
+import com.changhong.qlib.util.sync.SystemUtils;
+
+public class JBotAppBase  extends QData implements CmdUtils.CmdController {
+    static boolean isDebugMode = false;
+
+    public JBotAppBase(String botName) {
+        setName(botName);
+    }
+
+    @Override
+    public String getControllerName() {
+        return getName();
+    }
+
+    @Override
+    public void setControllerName(String s) {
+        setName(s);
+    }
+
+    public static boolean isDebugMode() {
+        return isDebugMode;
+    }
+
+    public static void run(String appName,
+                           String[] serviceNameArray, Class<? extends JMgbusService>[] serviceClsArray,
+                           String[] args) {
+        final JBotAppBase instance =
+                new JBotAppBase(appName);
+
+        JedgeBotConfigUtil.loadConfigs();
+
+        isDebugMode = JedgeBotConfigUtil.getConfigBoolean("jedge", "debugMode", false);
+        JedgeBotConst.debugMode = isDebugMode;
+        JedgeBotConst.enableSmartTaskCreate =
+                JedgeBotConfigUtil.getConfigBoolean("jedge", "taskMode", false);;
+
+        JBotAppClient jsBot = JBotAppClient.createInstance(appName, serviceNameArray, serviceClsArray);
+        if(jsBot==null) {
+            JESocketClient.markLogSt("应用 [%s] 创建服务 (%s) 失败,退出!",
+                    appName, serviceNameArray);
+            JedgeBotConfigUtil.releaseConfigs();
+            return;
+        }
+        QIData param = ArgUtils.handleArgVList(args);
+
+        boolean runBack = JedgeBotConfigUtil.setupInitConfigs(jsBot, param, appName);
+
+        if(!jsBot.start()) {
+            jsBot.errLog("Fail to start ap app : " + jsBot.getName());
+            return;
+        }
+
+        while(!jsBot.isActive()) {
+            //等待接入成功
+            jsBot.warnLog("Wait for apApp to connect to mgbus : " + jsBot.getName());
+            SystemUtils.tryWait(2000);
+        }
+
+        CmdUtils.run(instance, runBack, param);
+
+        jsBot.shutdown();
+
+        JedgeBotConfigUtil.releaseConfigs();
+    }
+
+    public static void run(String appName, String serviceName, Class<? extends JMgbusService> serviceCls, String[] args) {
+        run(appName, new String[]{serviceName}, (Class<? extends JMgbusService>[]) new Class<?>[]{serviceCls}, args);
+    }
+
+    public static void run(String appName, Class<? extends JMgbusService> serviceCls, String[] args) {
+        run(appName, null, (Class<? extends JMgbusService>[]) new Class<?>[]{serviceCls}, args);
+    }
+
+}

+ 41 - 0
app/src/main/java/com/ch/jedge/jbot2/intent/bot/JBotAppClient.java

@@ -0,0 +1,41 @@
+package com.ch.jedge.jbot2.intent.bot;
+
+import com.changhong.jedge.JMgbusClient;
+import com.changhong.jedge.JMgbusModual;
+import com.changhong.jedge.JMgbusService;
+
+import java.lang.reflect.Constructor;
+
+import static com.ch.jedge.utils.JedgeBotConst.sval_default_llm_call_service;
+
+
+public class JBotAppClient extends JMgbusClient {
+
+    public JBotAppClient(String subnet, String mgc_name, int threadCount, boolean tryLocal) {
+        super(subnet, mgc_name, threadCount, tryLocal);
+    }
+
+    public static JBotAppClient createInstance(String botName, String[] serviceNameArray, Class<? extends JMgbusService>[] serviceClsArray) {
+        JBotAppClient jLlmBot = new JBotAppClient("#0", botName, 20, true);
+        if(serviceClsArray==null
+                || (serviceNameArray!=null && serviceClsArray.length != serviceNameArray.length)
+                || (serviceNameArray==null && serviceClsArray.length!=1)) {
+            return null;
+        }
+        int i=0;
+        for(Class<? extends JMgbusService> cls : serviceClsArray) {
+            try {
+                Constructor<? extends JMgbusService> constructor = cls.getConstructor(JMgbusModual.class);
+                JMgbusService service = constructor.newInstance(jLlmBot);
+                String serviceName = serviceNameArray!=null?serviceNameArray[i]:sval_default_llm_call_service;
+                jLlmBot.bindService(serviceName, service);
+                i++;
+            } catch (Exception e) {
+                e.printStackTrace();
+                return null;
+            }
+        }
+        return jLlmBot;
+    }
+
+}

+ 188 - 0
app/src/main/java/com/ch/jedge/jbot2/intent/param/JedgeDataSetter.java

@@ -0,0 +1,188 @@
+package com.ch.jedge.jbot2.intent.param;
+
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.ch.jedge.jbot.other.knbase.JedgeGptApp;
+import com.changhong.jedge.JESocketClient;
+import com.changhong.jedge.JMgbusService;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.util.NumberUtils;
+
+import java.util.*;
+
+import static com.ch.jedge.utils.JedgeBotConst.ival_max_timeout_count;
+
+/**
+ *  大模型的中文语义参数提取器
+ *      结构:
+ *      (1) 参数表, 参数取值表
+ *      (2) 输入文字
+ *      (3) 备选参数
+ *      (4) 输出格式
+ *      (5) 附加说明
+ *
+ *
+ */
+public class JedgeDataSetter {
+
+    private static class JedgeDataSet {
+        public final int dsId;
+        private final boolean isNullable;
+        private final String srcMod;
+        private final String cbUri;
+        protected final JSONArray words = new JSONArray();
+        int ttl = 0;
+
+        public JedgeDataSet(JSONArray words) {
+            updateWords(words);
+            this.dsId = NumberUtils.randomInt(0x7FFFFFF);
+            this.isNullable = true;
+            this.srcMod = "";
+            this.cbUri = "";
+        }
+
+        public JedgeDataSet(String srcMod, String cbUri, JSONArray words, boolean nullable) {
+            updateWords(words);
+            this.dsId = NumberUtils.randomInt(0x7FFFFFF);
+            this.isNullable = nullable;
+            this.srcMod = srcMod;
+            this.cbUri = cbUri;
+        }
+
+        //mediaSearch:查询媒体咨询相关功能,根据用户提供的信息提取关键字,并查找对应的影片信息;
+        //mediaPlay:播放媒体,根据已经命中的媒体信息,去播放队对应的影片或歌曲,并设置对应的播放参数;
+        //mediaSave:记录用户的个性化喜好,根据已经播放的历史或内容,记住用户的一些播放参数;
+        public String getDatasetValueDef() {
+            StringBuilder re = new StringBuilder();
+            synchronized (words) {
+                for (Object w : this.words) {
+                    String disc = ((JSONObject) w).getString("disc");
+                    String valDisc = (disc!=null)?
+                            String.format("\"%s\"(%s);",((JSONObject)w).getString("key"), disc):
+                            String.format("\"%s\";",((JSONObject)w).getString("key"));
+                    re.append(valDisc);
+                }
+            }
+            return re.toString();
+        }
+
+        public String getValueKeyList() {
+            StringBuilder re = new StringBuilder();
+            synchronized (this.words) {
+                for (Object w : words) {
+                    String key = ((JSONObject) w).getString("key");
+                    if (re.length() > 0) re.append(',');
+                    re.append(key);
+                }
+            }
+            return re.toString();
+        }
+
+        public void updateWords(JSONArray words) {
+            this.ttl = 0;
+            synchronized (this.words) {
+                this.words.clear();
+                for (Object w : words) {
+                    if (w instanceof String) {
+                        w = QData.fromString((String) w).asJsonObject();
+                        this.words.add(w);
+                    } else if (w instanceof JSONObject)
+                        this.words.add(w);
+                }
+            }
+        }
+
+    }
+
+    private final JMgbusService service;
+    protected final String targetMessageFormat;
+    private final Map<String, JedgeDataSet> dataSetCache = new HashMap<>();
+
+    public JedgeDataSetter(JMgbusService service, String paramFormat) {
+        this.service = service;
+        this.targetMessageFormat = paramFormat;
+    }
+
+    //获取词库
+    public String getDatasetValueDefine(String pmKey) {
+        synchronized (dataSetCache) {
+            JedgeDataSet ds = dataSetCache.get(pmKey);
+            if(ds!=null) {
+                String dsStr = ds.getDatasetValueDef();
+                if(ds.isNullable) { dsStr += ",若无法取值,则字段为空;";}
+                return dsStr;
+            }
+        }
+        return "";
+    }
+
+    public String getDatasetValList(String dsKey) {
+        //"媒体指令"的取值列表:mediaSearch,mediaPlay,mediaSave;
+        synchronized (dataSetCache) {
+            JedgeDataSet ds = dataSetCache.get(dsKey);
+            if(ds!=null) {
+                String dsStr = String.format("\"%s\"的取值范围:%s",dsKey, ds.getValueKeyList());
+                if(ds.isNullable) { dsStr += ",若无法取值,则字段为空;";}
+                return dsStr;
+            }
+        }
+        return "";
+    }
+
+    public int updateDataset2Cache(int dsid, String dsKey, JSONArray words) {
+        if(JedgeGptApp.isDebugMode())
+            JESocketClient.LogSt("数据集更新(%s):%s",dsKey, words);
+        synchronized (dataSetCache) {
+            JedgeDataSet old = dataSetCache.get(dsKey);
+            if(old==null) {
+                old =  new JedgeDataSet(words);
+                dataSetCache.put(dsKey, old);
+                return old.dsId;
+            } else {
+                old.updateWords(words);
+            }
+        }
+        return dsid;
+    }
+
+    public int addDataSet(int dsid, String srcMod, String cbUri, String dsKey,
+                          JSONArray words, boolean nullable) {
+        //添加数据集
+//        if(JedgeGptApp.isDebugMode()) {
+//            JESocketClient.LogSt("创建数据集(%s):%s", dsKey, words);
+//        }
+        synchronized (dataSetCache) {
+            JedgeDataSet old = dataSetCache.get(dsKey);
+            if(old==null) {
+                old =  new JedgeDataSet(srcMod, cbUri, words, nullable);
+                dataSetCache.put(dsKey, old);
+                return old.dsId;
+            } else {
+                old.updateWords(words);
+            }
+        }
+        return dsid;
+    }
+
+
+    public void onHeartbeat() {
+        synchronized (dataSetCache) {
+            List<String> toDel = new ArrayList<>();
+            for(String key : dataSetCache.keySet()) {
+                JedgeDataSet dsCache = dataSetCache.get(key);
+                if(dsCache!=null) {
+                    if(dsCache.ttl>-1) dsCache.ttl ++;
+                    if(dsCache.ttl > ival_max_timeout_count ) {
+                        //超时退出
+                        toDel.add(key);
+                    }
+                }
+            }
+            for(String dmKey :toDel) {
+                dataSetCache.remove(dmKey);
+            }
+        }
+    }
+
+}

+ 10 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/JedgeLLMBaseObject.java

@@ -0,0 +1,10 @@
+package com.ch.jedge.jbot2.llm;
+
+public class JedgeLLMBaseObject {
+
+    protected final JedgeLLMContext llmContext;
+
+    public JedgeLLMBaseObject(JedgeLLMContext llmCtx) {
+        this.llmContext = llmCtx;
+    }
+}

+ 14 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/JedgeLLMContext.java

@@ -0,0 +1,14 @@
+package com.ch.jedge.jbot2.llm;
+
+import com.ch.jedge.jbot2.JedgeBotGptService;
+import com.changhong.jedge.JMgbusModual;
+
+public class JedgeLLMContext {
+    public final JMgbusModual module;
+    public final JedgeBotGptService service;
+
+    public JedgeLLMContext(JMgbusModual module, JedgeBotGptService service) {
+        this.module = module;
+        this.service = service;
+    }
+}

+ 19 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/agent/JedgeLLMAgentFactory.java

@@ -0,0 +1,19 @@
+package com.ch.jedge.jbot2.llm.agent;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMAgent;
+
+/**
+ *   根据Agent的模板,创建一个可用的Agent
+ *
+ */
+public class JedgeLLMAgentFactory {
+    public static JedgeLLMAgent createNewAgentFromContext(JedgeLLMContext llmContext, String query) {
+        JedgeLLMAgentTemplate template = getAgentTemplate(query);
+        return template.createAgentInstance(llmContext, query);
+    }
+
+    private static JedgeLLMAgentTemplate getAgentTemplate(String query) {
+        return new JedgeLLMAgentTemplate();
+    }
+}

+ 37 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/agent/JedgeLLMAgentPool.java

@@ -0,0 +1,37 @@
+package com.ch.jedge.jbot2.llm.agent;
+
+
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMAgent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *  Agent Pool
+ *      Agent用于管理用户的任务和目标
+ *      管理由AgentFactory创建的Agent,并响应用户的持续任务动作等
+ *
+ */
+
+public class JedgeLLMAgentPool extends JedgeLLMBaseObject {
+    protected final Map<String, JedgeLLMAgent> agentPool = new HashMap<>();
+
+    public JedgeLLMAgentPool(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+    }
+
+    public JedgeLLMAgent checkActiveAgent(String query) {
+        //获得当前的对话与Agent的相关性,如果没有相关性,则创建一个新的Agent
+        JedgeLLMAgent agent = agentPool.get("active");
+        if(agent == null) {
+            return createNewAgent(query);
+        }
+        return agent;
+    }
+
+    private JedgeLLMAgent createNewAgent(String query) {
+        return JedgeLLMAgentFactory.createNewAgentFromContext(llmContext, query);
+    }
+}

+ 10 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/agent/JedgeLLMAgentTemplate.java

@@ -0,0 +1,10 @@
+package com.ch.jedge.jbot2.llm.agent;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMAgent;
+
+public class JedgeLLMAgentTemplate {
+    public JedgeLLMAgent createAgentInstance(JedgeLLMContext llmContext, String query) {
+        return new JedgeLLMAgent(llmContext, null, null);
+    }
+}

+ 290 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/pickers/JedgeLLMParamPicker.java

@@ -0,0 +1,290 @@
+package com.ch.jedge.jbot2.llm.pickers;
+
+//参数提取器
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.request.JedgeLLMRequest;
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.utils.JedgeLlmUtil;
+import com.changhong.jedge.JMgbusUtil;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.QDataList;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.StringUtils;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+
+/**
+ *  通过对大模型的调用和提示词,完成对请求的参数分类。
+ *  可动态添加dataset的item项.当item项超过一定的数量时,需要执行多级分类.
+ */
+public class JedgeLLMParamPicker  extends JedgeLLMBaseObject {
+    protected JedgeLLMRequest llmRequest = null;
+    public String params_;          //所需要提取的参数及其说明,可能需要多次调用,确认最终参数。参数提取,可能需要额外的一次请求。
+
+    //结果校验:
+    protected JedgeLLMResultValidator validator_ = null;  //结果校验器,检验结果是否包含某些参数,主要针对特定的消息格式模板进行
+
+    protected final Map<String, QIData> mDatasetMap = new HashMap<>();
+    private String role = "";
+    private String cmd = "";
+    private String fmt = "";
+    private String prefix = "";
+
+    public JedgeLLMParamPicker(JedgeLLMContext llmCtx, Class<? extends JedgeLLMRequest> requestClass) {
+        super(llmCtx);
+        this.params_ = "描述参数提取说明";
+        if(requestClass!=null) {
+            try {
+                Constructor<? extends JedgeLLMRequest> constructor = requestClass.getConstructor(JedgeLLMContext.class);
+                llmRequest = constructor.newInstance(llmCtx);
+            } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException ignored) {
+            }
+        }
+    }
+
+    public JedgeLLMParamPicker(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+        this.params_ = "描述参数提取说明";
+        llmRequest = new JedgeLLMRequest(llmCtx);
+    }
+
+    //提取特定的参数、提供特定的数据集、可选项等,要去必须能完成。
+    public QIData getClassifiedParamAsData(JedgeTalkSession s, String query, String paramKey) {
+        return pickParamsFromQuery(s, query, paramKey);
+    }
+
+    public String getClassifiedParam(JedgeTalkSession s, String query, String paramKey, String... para) {
+        //对分类结果进行校验
+        QIData params = pickParamsFromQuery(s, query, paramKey, para);
+        // 提取指定参数,如果参数不存在,则需要重新请求,并强调,只提取所需的参数。
+        if(params!=null && !params.isEmpty()) {
+            if(!(params instanceof QDataList)) {
+                Object dv = params.get(paramKey);
+                if(dv instanceof JSONArray) {
+                    return JedgeLlmUtil.StringFromStringJSONArray((JSONArray)dv);
+                } else if(dv instanceof String)
+                    return (String) dv;
+                else
+                    return null;
+            }
+            int count = ((QDataList) params).getItemCount();
+            if(count>0) {
+                List<String> keys = new ArrayList<>();
+                for(int i=0;i<count;i++) {
+                    QIData p1 = ((QDataList) params).getItem(i);
+                    if(p1!=null) {
+                        String kk = p1.getString(paramKey);
+                        keys.add(kk);
+                    }
+                }
+                return StringUtils.ListToString(keys, ',');
+            }
+        }
+        return null;
+    }
+
+    public QIData pickParamsFromQuery(JedgeTalkSession s, String query, String paramKey, String... para) {
+        String dataset = prepareDataset(paramKey);
+        String realQuery = llmRequest.getRealPrompt(s, query, para);
+        if(StringUtils.isValidStr(dataset)) {
+            realQuery = dataset + realQuery;
+        }
+        QIData lmRsp = llmRequest.makeChatRequest(s, realQuery);
+
+        if(validator_!=null && !validator_.validateResult(lmRsp)) {
+            //再次LLM Request,提取专有参数结果。
+            return JMgbusUtil.MgbusResult(402, "Validate failed.");
+        }
+        return lmRsp;
+    }
+
+    protected String prepareDataset(String... paramKey) {
+        StringBuilder re = new StringBuilder();
+        if(paramKey==null || paramKey.length==0 || paramKey[0]==null) {
+            synchronized (mDatasetMap) {
+                for (String k : mDatasetMap.keySet()) {
+                    QIData d = mDatasetMap.get(k);
+                    if (d != null) {
+                        re.append(d.getName()).append(':');
+                        re.append(d.getString("d")).append(',');
+                        re.append("取值依据:").append(d.getData("val").toJSONString()).append('.');
+                    }
+                }
+            }
+        } else {
+            synchronized (mDatasetMap) {
+                for (String k : paramKey) {
+                    QIData d = mDatasetMap.get(k);
+                    if (d != null) {
+                        re.append(d.getName()).append(':');
+                        re.append(d.getString("d")).append(',');
+                        re.append("取值依据:").append(d.getData("val").toJSONString()).append('.');
+                    }
+                }
+            }
+        }
+        return re.toString();
+    }
+
+    protected void addDatasetItem(String key, String name, String disc, QIData vals) {
+        synchronized (mDatasetMap) {
+            mDatasetMap.put(key, new QData().setName(name).putString("d", disc).put("val", vals.asJsonObject()));
+        }
+    }
+
+    public boolean loadPickerFromData(QIData defs) {
+        JSONArray ta = defs.getJsonArray("targets");
+        String targets = null;
+        if(ta!=null)
+            targets =  JedgeLlmUtil.StringFromStringJSONArray(ta);
+        else
+            targets = defs.getString("targets");
+
+        String cmd = defs.getString("cmd");
+        String fmt = defs.getString("fmt");
+        String role = defs.getString("role");
+        String prefix = defs.getString("prefix");
+        String[] targetDefs = null;
+        if(ta!=null)
+            targetDefs = targets.split(",");
+        else {
+            targetDefs = getParamDatasetValues(defs, targets);
+            if (targetDefs == null) {
+                targetDefs = new String[]{targets};
+            }
+        }
+        if(StringUtils.isNotValidStr(cmd) || StringUtils.isNotValidStr(fmt))
+            return false;
+        loadDataset(defs);
+        defineDataPicker( targetDefs, role, cmd, fmt, prefix);
+        return true;
+    }
+
+    public boolean loadPickerFromFile(String filename) {
+        QIData defs = QData.fromFile(filename);
+        if(defs.isEmpty()) return false;
+        return loadPickerFromData(defs);
+    }
+
+    //移除数据项
+    public void addDataToDatasetItem(String dmKey, String valKey, String val) {
+        QIData ds;
+        synchronized (mDatasetMap) {
+            ds = mDatasetMap.get(dmKey);
+        }
+        if(ds==null) {
+            ds = new QData();
+            synchronized (mDatasetMap) {
+                mDatasetMap.put(dmKey, ds);
+            }
+        }
+        JSONObject vals = ds.getJsonObject("val");
+        if(vals==null) {
+            vals = new JSONObject();
+            ds.put("val", vals);
+        }
+        vals.put(valKey, val);
+        rebuildLLMRequest();
+    }
+
+    private void rebuildLLMRequest() {
+        QIData ds;
+        synchronized (mDatasetMap) {
+            ds = mDatasetMap.get("name");
+        }
+        String[] targetDefs;
+        if(ds!=null) {
+            JSONObject nameMap = ds.getJsonObject("val");
+            targetDefs = StringUtils.ArrayListToStringArray(nameMap.keySet());
+        } else {
+            targetDefs = new String[0];
+        }
+        llmRequest.setTemplateStr(targetDefs, role, cmd, fmt, prefix);
+    }
+
+    //增加数据项
+    public void removeDataFromDatasetItem(String dmKey, String valKey) {
+        QIData ds;
+        synchronized (mDatasetMap) {
+            ds = mDatasetMap.get(dmKey);
+        }
+        if(ds!=null) {
+            JSONObject vals = ds.getJsonObject("val");
+            if(vals.remove(valKey)!=null)
+                rebuildLLMRequest();
+        }
+    }
+
+    public void loadDataset(QIData defs) {
+        JSONArray datasets = defs.getJsonArray("datasets");
+        if(datasets==null || datasets.isEmpty())
+            return ;
+        for(Object dsItem : datasets) {
+            if(dsItem instanceof JSONObject) {
+                String name = ((JSONObject) dsItem).getString("name");
+                String key = ((JSONObject) dsItem).getString("key");
+                String disc = ((JSONObject) dsItem).getString("disc");
+                Object values = ((JSONObject) dsItem).get("values");
+                Object pickDisc = ((JSONObject) dsItem).get("pickDisc");
+                if(values instanceof JSONObject) {
+                    //生成一个数据集。在定义param时,需要涉及到对应的数据集
+                    addDatasetItem(key, name, disc, new QData((JSONObject) values));
+                } else if (values instanceof JSONArray
+                        && pickDisc instanceof JSONArray
+                        && ((JSONArray) values).size() == ((JSONArray) pickDisc).size()
+                ) {
+                    QIData vals = new QData();
+                    for(int i=0;i<((JSONArray) values).size();i++) {
+                        Object v = ((JSONArray) values).get(i);
+                        Object d = ((JSONArray) pickDisc).get(i);
+                        if(v instanceof String && d instanceof String) {
+                            vals.put((String) v, d);
+                        }
+                    }
+                    addDatasetItem(key, name, disc, vals);
+                }
+            }
+        }
+    }
+
+    static public String[] getParamDatasetValues(QIData defs, String targets) {
+        JSONArray datasets = defs.getJsonArray("datasets");
+        if(datasets==null || datasets.isEmpty())
+            return null;
+        for(Object dsItem : datasets) {
+            if(dsItem instanceof JSONObject) {
+                String name = ((JSONObject) dsItem).getString("name");
+                if(!targets.equals(name)) continue;
+                Object values = ((JSONObject) dsItem).get("values");
+                if(values instanceof JSONObject) {
+                    return StringUtils.ArrayListToStringArray(((JSONObject) values).keySet());
+                } else if (values instanceof JSONArray) {
+                    String[] vals = new String[((JSONArray) values).size()];
+                    int i=0;
+                    for(Object v : (JSONArray)values) {
+                        if(v instanceof String) {
+                            vals[i++] = (String) v;
+                        }
+                    }
+                    return vals;
+                }
+            }
+        }
+        return null;
+    }
+
+    protected void defineDataPicker(String[] targetDefs, String role, String cmd, String fmt, String prefix) {
+        //todo:: 从直接值中建立DataSetMap
+        this.role = role;
+        this.cmd = cmd;
+        this.fmt = fmt;
+        this.prefix = prefix;
+        llmRequest.setTemplateStr(targetDefs, role, cmd, fmt, prefix);
+    }
+}

+ 59 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/pickers/JedgeLLMParamPickerManager.java

@@ -0,0 +1,59 @@
+package com.ch.jedge.jbot2.llm.pickers;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.StringUtils;
+import com.changhong.qlib.util.file.FileUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *  参数提取管理器
+ *      优化速度,减少无效参数传递,快速产生结果。需要一个叫做任务链的东西。
+ *          :: 设计一个从原始语句到最终执行路径的链条。在这个链条中,有效分类、搜集和传递参数,最终实现对任务的实现。
+ *
+ */
+
+public class JedgeLLMParamPickerManager extends JedgeLLMBaseObject {
+    protected final Map<String, JedgeLLMParamPicker> pickerMap = new HashMap<>();
+
+    public JedgeLLMParamPickerManager(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+    }
+
+    public void loadPickersFromPath(String dirPath) {
+        FileUtils.iteratorDirectory(dirPath, file -> {
+            String fn = file.getName();
+            if(fn.endsWith(".json")) {
+//                llmContext.module.highLog("扫描到 LLM Param Picker:%s", fn);
+                QIData pickerDef = QData.fromFile(file);
+                if(!pickerDef.isEmpty()) {
+                    JedgeLLMParamPicker picker = new JedgeLLMParamPicker(llmContext);
+                    if(picker.loadPickerFromData(pickerDef)) {
+                        String key = pickerDef.getKey();
+                        if(StringUtils.isNotValidStr(key))
+                            key = FileUtils.getFilenameWithoutExt(fn);
+                        llmContext.module.markLog("LLM Param Picker (%s) 成功加载", key);
+                        synchronized (pickerMap) {
+                            pickerMap.put(key, picker);
+                        }
+                    }
+                }
+            }
+            return null;
+        }, null);
+    }
+
+    public void loadPicker(QIData pickerDef) {
+        
+    }
+
+    public JedgeLLMParamPicker getLLMParamPicker(String acKey) {
+        synchronized (pickerMap) {
+            return pickerMap.get(acKey);
+        }
+    }
+}

+ 16 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/pickers/JedgeLLMResultValidator.java

@@ -0,0 +1,16 @@
+package com.ch.jedge.jbot2.llm.pickers;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.changhong.qlib.intf.QIData;
+
+public class JedgeLLMResultValidator  extends JedgeLLMBaseObject {
+
+    public JedgeLLMResultValidator(JedgeLLMContext llmCtx, String validator, String params) {
+        super(llmCtx);
+    }
+
+    public boolean validateResult(QIData resp) {
+        return true;
+    }
+}

+ 15 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/pickers/JedgeSmartHomeActionPicker.java

@@ -0,0 +1,15 @@
+package com.ch.jedge.jbot2.llm.pickers;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+
+
+/**
+ * 多重任务功能提取器
+ */
+public class JedgeSmartHomeActionPicker extends JedgeLLMParamPicker {
+
+    public JedgeSmartHomeActionPicker(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+        loadPickerFromFile("./cfg/picker/action/index.json");
+    }
+}

+ 9 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/pickers/JedgeTaskObjectivePicker.java

@@ -0,0 +1,9 @@
+package com.ch.jedge.jbot2.llm.pickers;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+
+public class JedgeTaskObjectivePicker extends JedgeLLMParamPicker {
+    public JedgeTaskObjectivePicker(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+    }
+}

+ 47 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/request/JedgeLLMCOTRequest.java

@@ -0,0 +1,47 @@
+package com.ch.jedge.jbot2.llm.request;
+
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.changhong.qlib.util.StringUtils;
+
+/**
+ *  发起请求:
+ *  (1) 请求的相关参数结构:
+ *      query
+ *
+ *      template
+ *
+ *      response解析器
+ *
+ *      callback
+ *
+ *      结构化参数的模板
+ *
+ *      参数及其解释
+ *
+ *      校验器
+ *
+ *  (2) 返回
+ *      json结构
+ *
+ *
+ */
+
+public class JedgeLLMCOTRequest extends JedgeLLMRequest {
+    public JedgeLLMCOTRequest(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+    }
+
+    @Override
+    public void setTemplateStr(String[] split, String role, String cmd, String fmt, String prefix) {
+        String targets = StringUtils.ListToString(split, ',');
+        this.template_ =
+                String.format("你的角色是:%s," +
+                        "从用户输入内容中抽取实体或信息,从\"%s\"中%s," +
+                        "用结构化方式的json格式输出,格式:%s.只输出json内容,不输出其他。" +
+                        "用户输入:{query},让我们一步一步地分析:", role, targets, cmd ,fmt);
+        if(StringUtils.isValidStr(prefix))
+            this.template_ = prefix + "," + this.template_;
+    }
+}
+

+ 7 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/request/JedgeLLMCallback.java

@@ -0,0 +1,7 @@
+package com.ch.jedge.jbot2.llm.request;
+
+import com.changhong.qlib.intf.QIData;
+
+public interface JedgeLLMCallback {
+    void onLLMRequestReturned(QIData reponse);
+}

+ 54 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/request/JedgeLLMFilterRequest.java

@@ -0,0 +1,54 @@
+package com.ch.jedge.jbot2.llm.request;
+
+
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.changhong.qlib.util.StringUtils;
+
+/**
+ *  发起请求:
+ *  (1) 请求的相关参数结构:
+ *      query
+ *
+ *      template
+ *
+ *      response解析器
+ *
+ *      callback
+ *
+ *      结构化参数的模板
+ *
+ *      参数及其解释
+ *
+ *      校验器
+ *
+ *  (2) 返回
+ *      json结构
+ *
+ *
+ */
+
+public class JedgeLLMFilterRequest extends JedgeLLMRequest {
+    public JedgeLLMFilterRequest(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+    }
+
+    @Override
+    public void setTemplateStr(String[] split, String role, String cmd, String fmt, String prefix) {
+        String targets = StringUtils.ListToString(split, ',');
+        this.template_ =
+                String.format("你的角色是:%s," +
+                        "要过滤内容的相关领域是{domain},%s," +
+                        "填写到json数据格式中,格式:%s.只输出json内容,不输出其他。" +
+                        "用户输入:{query},你的输出:", role, cmd ,fmt);
+        if(StringUtils.isValidStr(prefix))
+            this.template_ = prefix + "," + this.template_;
+    }
+
+    @Override
+    public String getRealPrompt(JedgeTalkSession s, String query, String[] para) {
+        String p = super.getRealPrompt(s, query, para);
+        return p.replaceAll("\\{domain}", para[0]);
+    }
+}
+

+ 113 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/request/JedgeLLMRequest.java

@@ -0,0 +1,113 @@
+package com.ch.jedge.jbot2.llm.request;
+
+
+import com.ch.jedge.jbot2.intent.bot.JBotAppBase;
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.utils.JedgeLlmUtil;
+import com.changhong.jedge.JMgbusUtil;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.StringUtils;
+
+/**
+ *  发起请求:
+ *  (1) 请求的相关参数结构:
+ *      query
+ *
+ *      template
+ *
+ *      response解析器
+ *
+ *      callback
+ *
+ *      结构化参数的模板
+ *
+ *      参数及其解释
+ *
+ *      校验器
+ *
+ *  (2) 返回
+ *      json结构
+ *
+ *
+ */
+
+public class JedgeLLMRequest extends JedgeLLMBaseObject {
+    public String template_;        //解释这次请求的目的
+
+    public JedgeLLMRequest(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+        this.template_ = "{query}";
+    }
+//
+//    public String getRealPrompt(JedgeTalkSession s, String query) {
+//        return template_.replaceAll("\\{query}", query);
+//    }
+
+    public String getRealPrompt(JedgeTalkSession s, String query, String[] para) {
+        return template_.replaceAll("\\{query}", query);
+    }
+
+    public QIData makeChatRequest(JedgeTalkSession s, String realQuery) {
+        String result = llmContext.service.makeRealGptChatCall(s, realQuery);
+        //结构化处理:
+        QIData resp = handleLLMTextResponse(result);
+        if(JBotAppBase.isDebugMode()) {
+            llmContext.service.returnDirectGptMessage(s, "提取结果:" + resp +"\n");
+        }
+        //参数化
+        if(resp==null || resp.isEmpty()) {
+            return JMgbusUtil.MgbusResult(401, "No effect result");
+        }
+
+        //对结果字符串进行结构:判断其是否包含json结构字符串,斌并校验之
+        return resp;
+    }
+
+    //TODO::截获异步的请求
+    public QIData makeStreamRequest(JedgeTalkSession s, String realQuery) {
+        llmContext.service.makeRealGptStreamCall(s, realQuery);
+        //对结果字符串进行结构:判断其是否包含json结构字符串,斌并校验之
+        return null;
+    }
+
+    public static QIData handleLLMTextResponse(String result) {
+        if(StringUtils.isValidStr(result)) {
+            result = result.trim();
+            QIData re = QData.fromString(result);
+            if(!re.isEmpty()) {
+                return re;
+            }
+            result = JedgeLlmUtil.unescapeString(result);
+            re = QData.fromString(result);
+            if(!re.isEmpty())
+                return re;
+            String newResult = '{' + StringUtils.getSubStrBetween(result, '{',1,'}',-1) + '}';
+            re = QData.fromString(newResult);
+            if(!re.isEmpty())
+                return re;
+            newResult = '[' + StringUtils.getSubStrBetween(result, '[',1,']',-1)+ ']';
+            re = QData.fromString(newResult);
+            if(!re.isEmpty())
+                return re;
+        }
+        return null;
+    }
+
+    public void setTemplateStr(String[] split, String role, String cmd, String fmt, String prefix) {
+        String targets = StringUtils.ListToString(split, ',');
+        this.template_ =
+                String.format("你的角色是:%s," +
+                        "从用户输入内容中抽取实体或信息,从\"%s\"中%s," +
+                        "用结构化方式的json格式输出,格式:%s.只输出json内容,不输出其他。" +
+                        "用户输入:{query},你的输出:", role, targets, cmd ,fmt);
+        if(StringUtils.isValidStr(prefix))
+            this.template_ = prefix + "," + this.template_;
+//        if(JBotAppBase.isDebugMode())
+//            llmContext.module.warnLog("template reset to : %s", template_);
+    }
+
+}
+

+ 27 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/response/JedgeLLMResponseGenerator.java

@@ -0,0 +1,27 @@
+package com.ch.jedge.jbot2.llm.response;
+
+//应答生成器
+
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.changhong.qlib.intf.QIData;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *  通过大模型,针对特定的目标,生成一个应答。
+ *      · 应答可能是一个回复,也可能是提出一个问题。
+ *      · 若是提问器,需要在规定时间内完成回答:如果不能完成回答,则继续追问或采用默认答案。
+ */
+public class JedgeLLMResponseGenerator extends JedgeLLMBaseObject {
+    public String params_;          //所需要提取的参数及其说明,可能需要多次调用,确认最终参数。参数提取,可能需要额外的一次请求。
+
+    protected final Map<String, QIData> mDatasetMap = new HashMap<>();
+
+    public JedgeLLMResponseGenerator(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+        this.params_ = "提问的辅助参数";
+    }
+
+}

+ 27 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/response/JedgeLLMUserQuestioner.java

@@ -0,0 +1,27 @@
+package com.ch.jedge.jbot2.llm.response;
+
+//应答生成器
+
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.changhong.qlib.intf.QIData;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *  通过大模型,针对特定的目标,生成一个应答。
+ *      · 应答可能是一个回复,也可能是提出一个问题。
+ *      · 若是提问器,需要在规定时间内完成回答:如果不能完成回答,则继续追问或采用默认答案。
+ */
+public class JedgeLLMUserQuestioner extends JedgeLLMBaseObject {
+    public String params_;          //所需要提取的参数及其说明,可能需要多次调用,确认最终参数。参数提取,可能需要额外的一次请求。
+
+    protected final Map<String, QIData> mDatasetMap = new HashMap<>();
+
+    public JedgeLLMUserQuestioner(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+        this.params_ = "提问的辅助参数";
+    }
+
+}

+ 208 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/JedgeLLMDmWorld.java

@@ -0,0 +1,208 @@
+package com.ch.jedge.jbot2.llm.world;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.pickers.JedgeLLMParamPicker;
+import com.ch.jedge.jbot2.llm.world.knl.JedgeDomainSelector;
+import com.ch.jedge.jbot2.llm.world.model.JedgeMdDomain;
+import com.ch.jedge.jbot2.llm.world.model.JedgeMdObject;
+import com.ch.jedge.utils.JedgeBotConst;
+import com.changhong.jedge.JMgbusUtil;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.ch.jedge.utils.JedgeBotConst.ival_max_timeout_count;
+
+/**
+ *  世界对话模型:
+ *      用于映射LLM任务系统中各领域的对象状态(属性)值。
+ *      主要通过domain进行管理。
+ *
+ *      · 默认的领域时system,用于创建其它的任务模型:
+ *      · 对象领域,对象类型,
+ */
+
+public class JedgeLLMDmWorld extends JedgeLLMBaseObject {
+    protected final Map<String, JedgeMdDomain> domainMap = new HashMap<>();
+    private final JedgeDomainSelector domainSelector;
+    public JedgeLLMDmWorld(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+        domainSelector = new JedgeDomainSelector(llmCtx);
+    }
+
+    /**
+     *  加载一个世界的静态定义:
+     *
+     * @param wordPath  世界定义的路径
+     */
+    public void loadStaticWorldDefs(String wordPath) {
+        loadStaticDomain("system");
+
+    }
+
+    /**
+     *  系统默认的domain
+     * @param domainName    系统内嵌的domain,包括任务管理、本地记忆等
+     */
+    private void loadStaticDomain(String domainName) {
+
+    }
+
+    /**
+     * 加载动态领域
+     * @param domain    加载动态领域。插件注册到知识库中的领域。
+     */
+    public void loadDynamicDomain(String domain) {
+
+    }
+
+    public void onHeartbeat() {
+        synchronized (domainMap) {
+            List<String> toDel = new ArrayList<>();
+            for(String key : domainMap.keySet()) {
+                JedgeMdDomain domain = domainMap.get(key);
+                if(domain!=null) {
+                    if(domain.ttl>-1) domain.ttl ++;
+                    if(domain.ttl > ival_max_timeout_count ) {
+                        //超时退出
+                        toDel.add(key);
+                    } else
+                        domain.onHeartbeat();
+                }
+            }
+
+            for(String dmKey :toDel) {
+                domainMap.remove(dmKey);
+                domainSelector.removeDataFromDatasetItem("domain", dmKey);
+                if(JedgeBotConst.debugMode)
+                    llmContext.module.infoLog(String.format("Domain超时移除:%s", dmKey));
+            }
+
+        }
+    }
+
+    public String getOperationTarget(JedgeTalkSession s, String domain, String query) {
+        //获取世界模型的操作对象
+        JedgeMdDomain dm = getDomain(domain);
+        if(dm!=null)
+            return dm.getOperationObjectName(s, query);
+        return null;
+    }
+
+    public JedgeMdDomain getDomain(String domain) {
+        synchronized (domainMap) {
+            return domainMap.get(domain);
+        }
+    }
+
+    public QIData addNewDmDomain(QIData req) {
+        String dmKey = req.getKey();
+        if(StringUtils.isNotValidStr(dmKey))
+            return JMgbusUtil.MgbusResult(401, "No domainKey");
+        JedgeMdDomain domainDef;
+        synchronized (domainMap) {
+            if(domainMap.containsKey(dmKey)) {
+                JedgeMdDomain old = domainMap.get(dmKey);
+                int dmid = req.getInteger("dmid");
+                if(old!=null && dmid==old.getDmObjectId()) {
+                    old.ttl = 0;
+                    return JMgbusUtil.MgbusResult(200, "领域已存在,刷新生存期").putInteger("dmid", dmid);
+                }
+            }
+            domainDef = new JedgeMdDomain(llmContext,req);      //
+            domainDef.ttl = 0;  //动态添加的domain
+
+            domainMap.put(dmKey, domainDef);
+        }
+        if(JedgeBotConst.debugMode)
+            llmContext.module.highLog(String.format("领域模块注册成功:%s", dmKey));
+
+        domainSelector.addDataToDatasetItem("domain", dmKey, req.getString("disc"));
+        return JMgbusUtil.MgbusResult(200, "领域添加成功").putInteger("dmid", domainDef.getDmObjectId());
+    }
+
+    public QIData removeDmDomain(QIData req) {
+        int dmid = req.getInteger("dmid");
+        if(dmid>0) {
+            String dmKey = req.getString("key");
+            if(StringUtils.isValidStr(dmKey)) {
+                synchronized (domainMap) {
+                    JedgeMdDomain dmDef = domainMap.get(dmKey);
+                    if(dmDef!=null && dmDef.getDmObjectId()==dmid) {
+                        domainMap.remove(dmKey);
+                        domainSelector.removeDataFromDatasetItem("domain", dmKey);
+                        llmContext.module.highLog(String.format("领域模块移除成功:%s", dmKey));
+                        return JMgbusUtil.MgbusResult(200,"removed " + dmKey);
+                    }
+                }
+            }
+        }
+        return JMgbusUtil.MgbusResult(401, "No dmid or mismatched.");
+    }
+
+    //按照特定无模型添加的对象
+    public QIData addNewDmObjects(QIData req) {
+        String dm = req.getString("domain");
+        int dmId = req.getInteger("dmid");
+        synchronized (domainMap) {
+            if (!StringUtils.isValidStr(dm) || !domainMap.containsKey(dm) || dmId == -1)
+                return JMgbusUtil.MgbusResult(401, "No domain or dmid mismatched.");
+            JedgeMdDomain dmObj = domainMap.get(dm);
+            JSONArray objs = req.getJsonArray("payload");
+            JSONArray ids = new JSONArray();
+            if(objs!=null) {
+                String src = req.getString("src");
+                String cb = req.getString("cbUri");
+                String dmKey = req.getKey();
+                int oldId = req.getInteger("doid");
+                for(Object obj : objs) {
+                    QIData objData;
+                    if(obj instanceof JSONObject) {
+                        objData = new QData((JSONObject) obj);
+                    } else if(obj instanceof String) {
+                        objData = QData.fromString((String) obj);
+                    } else objData = null;
+                    if( objData!=null) {
+                        JedgeMdObject dmObject = dmObj.addDomainObject(dmKey, src, cb, objData);
+                        if(dmObject!=null) {
+                            ids.add(dmObject.getDmObjectId());
+                            llmContext.module.highLog("对象%s添加成功", objData.getName());
+                        }
+                    }
+                }
+            }
+            QIData r = JMgbusUtil.MgbusResult(200, "对象添加成功");
+            r.putJsonArray("diod", ids);
+            return r;
+        }
+    }
+
+    public QIData removeDmObjects(QIData req) {
+        String dm = req.getString("domain");
+        int dmId = req.getInteger("dmid");
+        synchronized (domainMap) {
+            if (!StringUtils.isValidStr(dm) || !domainMap.containsKey(dm) || dmId == -1)
+                return JMgbusUtil.MgbusResult(401, "No domain or dmid mismatched.");
+            JedgeMdDomain dmObj = domainMap.get(dm);
+
+            int doid = req.getInteger("doid");
+            if(doid>-1 && dmObj.containsObject(doid)) {
+                JedgeMdObject obj = dmObj.removeObject(doid);
+                dmObj.removeDomainObjectName(dm, obj.getName());
+
+                return JMgbusUtil.MgbusResult(200, "ok.");
+            }
+            return JMgbusUtil.MgbusResult(400, "No doid or mismatched.");
+        }
+
+    }
+}

+ 15 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/JedgeDomainSelector.java

@@ -0,0 +1,15 @@
+package com.ch.jedge.jbot2.llm.world.knl;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.pickers.JedgeLLMParamPicker;
+import com.ch.jedge.jbot2.llm.request.JedgeLLMCOTRequest;
+import com.ch.jedge.jbot2.llm.request.JedgeLLMRequest;
+
+public class JedgeDomainSelector extends JedgeLLMParamPicker {
+
+    public JedgeDomainSelector(JedgeLLMContext llmContext) {
+        super(llmContext, JedgeLLMRequest.class);
+        loadPickerFromFile("./cfg/picker/system/domain.json");
+    }
+
+}

+ 89 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/JedgeKnDomain.java

@@ -0,0 +1,89 @@
+package com.ch.jedge.jbot2.llm.world.knl;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.knl.task.JedgeLLMTaskTemplate;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMAgent;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMTask;
+import com.ch.jedge.jbot2.llm.world.task.JedgeTaskTester;
+import com.changhong.jedge.JMgbusUtil;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *   知识领域:
+ *      可从知识库或动态注册,
+ *      大模型可从知识领域架构中查询到对应的定义、属性和能力等关系。
+ *      当用户进入到特定领域时,
+ *
+ */
+
+public class JedgeKnDomain extends JedgeKnObject {
+    protected String disc;
+    private String cbMod;
+    private String cbUri;
+    protected final Map<String, JedgeLLMTaskTemplate> mKnTasks = new HashMap<>();
+    private JedgeLLMAgent instructionAgent;
+    protected final JedgeLLMContext llmContext;
+
+    public JedgeKnDomain(JedgeLLMContext llmContext, QIData req) {
+        super(llmContext, req);
+        this.llmContext = llmContext;
+        this.disc = req.getString("disc");
+        this.cbMod = JMgbusUtil.GetMgModSource(req);
+        this.cbUri = req.getString("cbUri");
+        instructionAgent = createActiveAgent(llmContext, req.getString("picker"), cbMod, cbUri);
+
+    }
+
+    public JedgeKnDomain(JedgeLLMContext llmContext, String domainPath) {
+        super(llmContext, QData.fromFile(domainPath + "/domain.json"));
+        this.llmContext = llmContext;
+        if(!isEmpty()) {
+            this.disc = getString("disc");
+            this.cbMod = null;   //本地的领域
+            instructionAgent = createActiveAgent(llmContext, getString("picker"), cbMod, cbUri);
+        }
+    }
+
+    public void onHeartbeat() {
+
+    }
+
+
+    public void addDataPicker(QIData req) {
+
+    }
+
+    //添加任务模板
+    public void addTaskTemplate(QIData req) {
+
+    }
+
+    //根据用户对话,命中任务
+    public JedgeLLMTask createDomainTask(JedgeLLMContext llmContext, String taskKey, JedgeTaskTester taskTester) {
+        JedgeLLMTask task = new JedgeLLMTask(llmContext, taskKey, taskTester);
+        //创建任务后,创建特定的goal
+        // task.loadTaskFromStaticData("");  //任务路径
+        return task;
+
+    }
+
+    public JedgeLLMAgent createActiveAgent(JedgeLLMContext llmContext, String paramPath, String cbMod, String cbUri) {
+        JedgeLLMAgent agent = new JedgeLLMAgent(llmContext, cbMod, cbUri);
+        if(StringUtils.isValidStr(paramPath))
+            agent.loadLocalCommandHandler(paramPath);
+        return agent;
+    }
+
+    public String getCommandKey() {
+        return getString("cmd", "command");
+    }
+
+    public JedgeLLMAgent getInstructionAgent() {
+        return instructionAgent;
+    }
+}

+ 40 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/JedgeKnObject.java

@@ -0,0 +1,40 @@
+package com.ch.jedge.jbot2.llm.world.knl;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.NumberUtils;
+
+/**
+ * 领域内的对象知识(类描述)
+ *      描述一个对象:
+ *      (1) 是什么;
+ *      (2) 属于什么;
+ *      (3) 有哪些属性:属性的取值类型和范围,改变属性的方法
+ *      (4) 有哪些能力:能力的描述、Key、参数及其取值范围
+ */
+public class JedgeKnObject extends QData {
+    public int ttl;
+    private final int dmObjId;
+    protected final JedgeLLMContext llmContext;
+
+    //
+    protected String objDef;        //参数描述文本
+    protected String discString;    //分类或定位到这个对象的描述;
+
+    public JedgeKnObject(JedgeLLMContext llmContext) {
+        super();
+        this.llmContext = llmContext;
+        this.dmObjId = NumberUtils.randomInt(0x7FFFFFF);
+    }
+
+    public JedgeKnObject(JedgeLLMContext llmContext, QIData initData) {
+        super(initData);
+        this.llmContext = llmContext;
+        this.dmObjId = NumberUtils.randomInt(0x7FFFFFF);
+    }
+
+    public int getDmObjectId() {
+        return dmObjId;
+    }
+}

+ 225 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/JedgeLLMKnWorld.java

@@ -0,0 +1,225 @@
+package com.ch.jedge.jbot2.llm.world.knl;
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.jbot2.intent.bot.JBotAppBase;
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.system.JedgeSystemKnDomain;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMTask;
+import com.ch.jedge.jbot2.llm.world.task.JedgeTaskTester;
+import com.ch.jedge.utils.JedgeBotConst;
+import com.changhong.jedge.JMgbusUtil;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.intf.QIDataList;
+import com.changhong.qlib.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.ch.jedge.utils.JedgeBotConst.ival_max_timeout_count;
+
+/**
+ *  世界知识模型:
+ *      世界知识在本地的缓存。
+ *      用于session中,用户对话时,协助LLM从对话数据中分析用户对话的主题领域。
+ *      从知识库中、或者在线热插件中拉取知识,缓存,供用户对话时,判定知识。
+ *
+ *
+ *
+ */
+
+public class JedgeLLMKnWorld extends JedgeLLMBaseObject {
+
+    protected final Map<String, JedgeKnDomain> domainDefMap = new HashMap<>();
+    protected final JedgeDomainSelector domainSelector;
+//    protected final JedgeContentCutter domainContentFilter;
+    protected final JedgeTaskTester taskTester;
+    protected final JedgeSystemKnDomain domainSystem;
+
+    public JedgeLLMKnWorld(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+        taskTester = new JedgeTaskTester(llmCtx);
+        domainSelector = new JedgeDomainSelector(llmCtx);
+//        domainContentFilter = new JedgeContentCutter(llmCtx);
+        if(JedgeBotConst.enableSmartTaskCreate) {
+            String sdPath = "./cfg/domains/system";
+            domainSystem = new JedgeSystemKnDomain(llmCtx, sdPath);
+            if (domainSystem.isEmpty()) {
+                llmCtx.module.errLog("系统领域加载失败!", sdPath);
+            } else
+                addNewDomain(domainSystem);
+        } else {
+            domainSystem = null;
+        }
+    }
+
+    //加上需要带参数的 内容提取请求 content的补充内容
+//    public String filterDomainContent(JedgeTalkSession s, String query, String domain) {
+//        return domainContentFilter.getClassifiedParam(s, query, "domain", domain);
+//    }
+    //判断领域,可能存在多轮。
+    public QIData testDomain(JedgeTalkSession s, String query) {
+        QIData domains = domainSelector.getClassifiedParamAsData(s, query, "domain");
+        if(domains instanceof QIDataList) {
+            if(((QIDataList) domains).getItemCount()>1) {
+                //移除重复或空的domain
+                int last = ((QIDataList) domains).getItemCount();
+                List<Integer> delList = new ArrayList<>();
+                for(int i=last-1;i>-1;i--) {
+                    Object obj = ((QIDataList) domains).asJsonArray().get(i);
+                    if(!(obj instanceof JSONObject) ||
+                            StringUtils.isNotValidStr(((JSONObject) obj).getString("query"))) {
+                        delList.add(i);
+                    }
+                }
+                for(int i : delList) {
+                    ((QIDataList) domains).removeItem(i);
+                }
+                if(((QIDataList) domains).asJsonArray().size()>0) {
+                    int i = 0;
+                    String first = null;
+                    StringBuilder tail = new StringBuilder();
+                    for (Object obj : ((QIDataList) domains).asJsonArray()) {
+                        if (obj instanceof JSONObject) {
+                            String q = ((JSONObject) obj).getString("query");
+                            if (i++ == 0) {
+                                first = q;
+                                if (query.length() - first.length() > 5)
+                                    return domains;
+                            } else
+                                tail.append(q);
+                        }
+                    }
+                    //去掉重复的内容
+                    if (first != null && first.length() > tail.length()) {
+                        int end = first.length() - tail.length();
+                        first = first.substring(0, end);
+                        ((JSONObject) ((QIDataList) domains).asJsonArray().get(0)).put("query", first);
+                        if (JBotAppBase.isDebugMode())
+                            llmContext.module.highLog("Rebound domains: %s", domains);
+                    }
+                }
+            }
+        }
+        return domains;
+    }
+
+    /**
+     * 从domain中获得领域知识。
+     * @param domain    领域
+     * @param taskKey   任务标记(中文)
+     * @return          任务
+     */
+    public JedgeLLMTask createTaskFromTemplate(String domain, String taskKey) {
+        JedgeKnDomain domainDef = getDomain(domain);
+        if(domainDef!=null) {
+            return domainDef.createDomainTask(llmContext, taskKey, taskTester);
+        }
+        return null;
+    }
+
+    public JedgeKnDomain getDomain(String domain) {
+        synchronized (domainDefMap) {
+            return domainDefMap.get(domain);
+        }
+    }
+
+    public void onHeartbeat() {
+        synchronized (domainDefMap) {
+            List<String> toDel = new ArrayList<>();
+            for(String key : domainDefMap.keySet()) {
+                JedgeKnDomain domain = domainDefMap.get(key);
+                if(domain!=null) {
+                    if(domain.ttl>-1) domain.ttl ++;
+                    if(domain.ttl > ival_max_timeout_count ) {
+                        //超时退出
+                        toDel.add(key);
+                    } else
+                        domain.onHeartbeat();
+                }
+            }
+
+            for(String dmKey :toDel) {
+                domainDefMap.remove(dmKey);
+                domainSelector.removeDataFromDatasetItem("domain", dmKey);
+//                domainContentFilter.removeDataFromDatasetItem("domain", dmKey);
+                if(JedgeBotConst.debugMode)
+                    llmContext.module.infoLog(String.format("Domain超时移除:%s", dmKey));
+            }
+
+        }
+    }
+
+    /**
+     *  通过一个文件或rpc的json消息,注册一个domain选项.
+     *
+     * @param req   输入的domain选项
+     * @return  返回值
+     */
+    public QIData addNewDomain(QIData req) {
+//        if(JedgeBotConst.debugMode)
+//            llmContext.module.infoLog("add domain : " + req);
+        String dmKey = req.getKey();
+        if(StringUtils.isNotValidStr(dmKey))
+            return JMgbusUtil.MgbusResult(401, "No domainKey");
+        JedgeKnDomain domainDef;
+        synchronized (domainDefMap) {
+            if(domainDefMap.containsKey(dmKey)) {
+                JedgeKnDomain old = domainDefMap.get(dmKey);
+                int dmid = req.getInteger("dmid");
+                if(old!=null && dmid==old.getDmObjectId()) {
+                    old.ttl = 0;
+                    return JMgbusUtil.MgbusResult(200, "领域已存在,刷新生存期").putInteger("dmid", dmid);
+                }
+            }
+            domainDef = new JedgeKnDomain(llmContext,req);      //
+            //添加消息参数提取器
+            domainDef.addDataPicker(req);
+            domainDef.ttl = 0;  //动态添加的domain
+
+            domainDefMap.put(dmKey, domainDef);
+        }
+
+        QIData result = JMgbusUtil.MgbusResult(200, "领域添加成功");
+        if(JedgeBotConst.debugMode)
+            llmContext.module.highLog(String.format("领域模块注册成功:%s", dmKey));
+
+        domainSelector.addDataToDatasetItem("domain", dmKey, req.getString("disc"));
+//        domainContentFilter.addDataToDatasetItem("domain", dmKey, req.getString("disc"));
+        result.putInteger("dmid", domainDef.getDmObjectId());
+        return result;
+    }
+
+    public QIData removeDomain(QIData req) {
+        int dmid = req.getInteger("dmid");
+        if(dmid>0) {
+            String dmKey = req.getString("key");
+            if(StringUtils.isValidStr(dmKey)) {
+                synchronized (domainDefMap) {
+                    JedgeKnDomain dmDef = domainDefMap.get(dmKey);
+                    if(dmDef!=null && dmDef.getDmObjectId()==dmid) {
+                        domainDefMap.remove(dmKey);
+                        domainSelector.removeDataFromDatasetItem("domain", dmKey);
+                        llmContext.module.highLog(String.format("领域模块移除成功:%s", dmKey));
+                        return JMgbusUtil.MgbusResult(200,"removed " + dmKey);
+                    }
+                }
+            }
+        }
+        return JMgbusUtil.MgbusResult(401, "No dmid or mismatched.");
+    }
+
+    public void makeCommonResponse(JedgeTalkSession s, String query) {
+        //从domain中获得知识前缀,由用户完成输出。
+        llmContext.service.makeRealGptStreamCall(s, query);
+    }
+
+    public JedgeTaskTester getTaskTester() {
+        return taskTester;
+    }
+
+}

+ 13 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/task/JedgeContentCutter.java

@@ -0,0 +1,13 @@
+package com.ch.jedge.jbot2.llm.world.knl.task;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.pickers.JedgeLLMParamPicker;
+import com.ch.jedge.jbot2.llm.request.JedgeLLMFilterRequest;
+
+public class JedgeContentCutter extends JedgeLLMParamPicker {
+
+    public JedgeContentCutter(JedgeLLMContext llmContext) {
+        super(llmContext, JedgeLLMFilterRequest.class);
+        loadPickerFromFile("./cfg/picker/system/cutter.json");
+    }
+}

+ 20 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/knl/task/JedgeLLMTaskTemplate.java

@@ -0,0 +1,20 @@
+package com.ch.jedge.jbot2.llm.world.knl.task;
+
+
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+
+/**
+ *  任务模板:
+ *      用于创建任务的描述结构
+ *
+ *      用于命中什么时候创建该任务、任务需要哪些GoalItem、 启动参数、  执行步骤。
+ *      任务管理器,在创建任务后、从任务模板中创建任务,并填充启动参数,
+ *
+ */
+public class JedgeLLMTaskTemplate extends JedgeLLMBaseObject {
+
+    public JedgeLLMTaskTemplate(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+    }
+}

+ 67 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/model/JedgeMdDomain.java

@@ -0,0 +1,67 @@
+package com.ch.jedge.jbot2.llm.world.model;
+
+
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.pickers.JedgeLLMParamPicker;
+import com.ch.jedge.jbot2.llm.world.knl.JedgeKnObject;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMAgent;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMTask;
+import com.changhong.qlib.intf.QIData;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *  模型的领域:
+ *      当用户对话涉及到某个领域的内容时,创建这个Model
+ *
+ */
+public class JedgeMdDomain extends JedgeKnObject  {
+    protected final Map<String, JedgeLLMTask> mDmTasks = new HashMap<>();
+    protected final Map<Integer, JedgeMdObject> mDmObjects = new HashMap<>();
+    private final JedgeLLMParamPicker objectSelector;
+
+    public JedgeMdDomain(JedgeLLMContext llmContext, QIData initData) {
+        super(llmContext, initData);
+        objectSelector = new JedgeLLMParamPicker(llmContext);
+        objectSelector.loadPickerFromFile("./cfg/picker/system/objectSel.json");
+    }
+
+    //自动移除对象等数据
+    public void onHeartbeat() {
+
+    }
+
+    public JedgeMdObject addDomainObject(String dmKey, String src, String cb, QIData objData) {
+        synchronized (mDmObjects) {
+            String objName = objData.getName();
+            JedgeMdObject obj = new JedgeMdObject(llmContext, src, cb, objData);
+            mDmObjects.put(obj.getDmObjectId(), obj);   //中文命名的对象
+            addDomainObjectName(dmKey, objName, objData.getString("disc"));
+            return obj;
+        }
+    }
+
+    public boolean containsObject(int doid) {
+        return false;
+    }
+
+    public JedgeMdObject removeObject(int doid) {
+
+        return null;
+    }
+
+    public String getOperationObjectName(JedgeTalkSession s, String query) {
+        return objectSelector.getClassifiedParam(s, query, "name");
+    }
+
+    public void addDomainObjectName(String dm, String objName, String objInfo) {
+        objectSelector.addDataToDatasetItem(dm, objName, objInfo);
+    }
+
+    public void removeDomainObjectName(String dm, String name) {
+        objectSelector.removeDataFromDatasetItem(dm, name);
+    }
+
+}

+ 16 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/model/JedgeMdObject.java

@@ -0,0 +1,16 @@
+package com.ch.jedge.jbot2.llm.world.model;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.knl.JedgeKnObject;
+import com.changhong.qlib.intf.QIData;
+
+public class JedgeMdObject extends JedgeKnObject {
+
+    public JedgeMdObject(JedgeLLMContext llmContext, QIData initData) {
+        super(llmContext, initData);
+    }
+
+    public JedgeMdObject(JedgeLLMContext llmContext, String src, String cb, QIData objData) {
+        super(llmContext, objData);
+    }
+}

+ 65 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/readme.txt

@@ -0,0 +1,65 @@
+用户对话时,在session里面会实时生成一个世界模型。
+
+世界模型,最初只有一个任务的调度模型,
+然后根据query,逐步生成各个domain的对象集。
+
+层级关系是:
+world-Knowledge、
+    tasks-template、
+        世界任务描述(跨领域的任务模板);
+        是对domain及下属各项任务的补充。
+
+    domain_defs、
+        描述每个领域要做什么、能做什么;
+        Objects_defs、
+            描述每个领域中有哪些对象;
+            每个对象有哪些属性、每个属性的取值范围;能力的变化需要做哪些能力;
+                属性,如果是对象,则是一种嵌套关系。
+            描述每个对象的能力集、能力集需要哪些参数,执行的后果是什么。
+        tasks-template、
+            描述对该领域的一般性任务有哪些。
+            任务能解决哪些问题(任务如何生成)
+            任务的执行流程;
+            任务的执行后果;
+            任务的执行条件是什么?
+
+world、
+domain、
+    Objects
+        property-value
+
+Task-Pool、
+    Agent、
+        : task-defs
+
+        : target
+            domain-Objects
+                property-value
+
+        : current
+            domain-Objects
+                property-value
+
+        : judges(判断任务是否完成)
+
+        : actions_list
+
+
+
+
+
+进入的数据处理顺序:
+(1) 区分领域,也有可能是多领域的任务;
+(2) 找到当前的活跃任务, 测试当前的指令与当前任务的融洽性;
+(3) 根据与任务的关系,处理当前任务对输入的响应(::创建任务、修正目标、咨询当前任务的状态、咨询进度、咨询结果等)
+    ::如果是任务创建:
+        第一步:区分任务的类型(如果当前的数据无法准确区分任务,则根据当前的数据,向用户进一步提出问题):
+        第二步:在当前领域内检索任务:
+        第三步:可能提出用户希望在哪些方面进行改进?或者提供更详细的信息:
+
+
+(4) 也可能就是一个纯粹的咨询的任务。
+
+任务执行过程中:
+
+

+ 114 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/system/JedgeLLMSystemTask.java

@@ -0,0 +1,114 @@
+package com.ch.jedge.jbot2.llm.world.system;
+
+import com.alibaba.fastjson.JSONObject;
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.jbot2.intent.bot.JBotAppBase;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.JedgeLLMDmWorld;
+import com.ch.jedge.jbot2.llm.world.knl.JedgeKnDomain;
+import com.ch.jedge.jbot2.llm.world.knl.JedgeLLMKnWorld;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMAgent;
+import com.ch.jedge.jbot2.llm.world.task.JedgeLLMTask;
+import com.ch.jedge.jbot2.llm.world.task.JedgeTaskTester;
+import com.ch.jedge.utils.JedgeBotConst;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.intf.QIDataList;
+import com.changhong.qlib.util.StringUtils;
+
+public class JedgeLLMSystemTask extends JedgeLLMTask {
+    protected final JedgeLLMKnWorld knWorld;
+    protected final JedgeLLMDmWorld dmWorld;
+    private JedgeLLMTask createdTask;
+    private TaskCreateParams taskCreateParam = new TaskCreateParams();
+    //待创建的任务
+    static class TaskCreateParams {
+
+    }
+
+    public JedgeLLMSystemTask(JedgeLLMContext llmCtx, JedgeLLMKnWorld knWorld, JedgeLLMDmWorld worldModel, String taskKey, JedgeTaskTester taskTester) {
+        super(llmCtx, taskKey, taskTester);
+        this.knWorld = knWorld;
+        this.dmWorld = worldModel;
+    }
+
+    public JedgeLLMTask getCurrentTask() {
+        return createdTask;
+    }
+
+    @Override
+    public boolean handleQuery(JedgeTalkSession s, String query, QIData domain) {
+        if(domain instanceof QIDataList) {
+            for(Object d : ((QIDataList) domain).asJsonArray()) {
+                if(d instanceof JSONObject) {
+                    runDirectInstruction(s, query, (JSONObject) d, ((QIDataList) domain).getItemCount()==1);
+                }
+            }
+        } else {
+            return runDirectInstruction(s, query, domain.asJsonObject(), true);
+        }
+        return true;
+    }
+
+    private boolean runDirectInstruction(JedgeTalkSession s, String query, JSONObject d, boolean singleMode) {
+        String q;
+        if(!singleMode) {
+            q = d.getString("query");
+            if (StringUtils.isNotValidStr(q))
+                q = query;
+        } else q = query;
+        String dm = d.getString("domain");
+        if(StringUtils.isNotValidStr(dm))
+            return false;
+//        if(JBotAppBase.isDebugMode())   //暂时不执行后续代码
+//            return true;
+        String taskType = taskTester.checkTaskType(s, q);
+        if(taskType!=null) {
+            if("direcInst".equals(taskType) ||
+                    "queryStatus".equals(taskType)) {
+                //直接执行一个即时指令
+                if(JedgeBotConst.debugMode) {
+                    llmContext.service.returnGptMessage(s, String.format("任务类型:%s\n", taskType), false);
+                }
+                JedgeKnDomain dmDef = knWorld.getDomain(dm);
+                if (dmDef != null) {
+                    //要判断这个指令是否具备了所有的参数,如果有参数提取失败,则Agent要发起提问,寻求进一步参数
+                    JedgeLLMAgent agent = dmDef.getInstructionAgent();
+                    if (agent != null) {
+                        String objectId = dmWorld.getOperationTarget(s, dm, q);  //获取操作对象
+                        agent.runInstruction(s, q, objectId, dmDef.getCommandKey());
+                    }
+                    return true;
+                }
+            }
+        }
+        //补充任务创建参数
+        if(!checkTaskCreationParams(s, query, dm)) {
+            return false;
+        }
+        // 创建一个新的任务
+        JedgeLLMTask theTask = knWorld.createTaskFromTemplate(dm, query);
+        if(theTask!=null) {
+            //任务处理对话并创建Agent
+            if(theTask.handleQuery(s, query, new QData(d))) {
+                createdTask = theTask;
+                return true;
+            }
+        } else {
+            //当前参数无法有效创建任务
+            resetTaskCreationStatus();
+        }
+
+        return false;
+    }
+
+    private boolean checkTaskCreationParams(JedgeTalkSession s, String query, String domain) {
+        return false;
+    }
+
+    public void resetTaskCreationStatus() {
+        taskCreateParam = new TaskCreateParams();
+        createdTask = null;
+        //重置任务创建状态
+    }
+}

+ 11 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/system/JedgeSystemKnDomain.java

@@ -0,0 +1,11 @@
+package com.ch.jedge.jbot2.llm.world.system;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.knl.JedgeKnDomain;
+
+public class JedgeSystemKnDomain extends JedgeKnDomain {
+
+    public JedgeSystemKnDomain(JedgeLLMContext llmContext, String domainPath) {
+        super(llmContext, domainPath);
+    }
+}

+ 88 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeLLMAgent.java

@@ -0,0 +1,88 @@
+package com.ch.jedge.jbot2.llm.world.task;
+
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.jbot2.intent.bot.JBotAppBase;
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.pickers.JedgeLLMParamPicker;
+import com.ch.jedge.jbot2.llm.pickers.JedgeLLMParamPickerManager;
+import com.changhong.jedge.JMgbusUtil;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.StringUtils;
+
+public class JedgeLLMAgent extends JedgeLLMBaseObject {
+    private final JedgeLLMParamPickerManager ppManager;
+    private final String cbMod;
+    private final String cbUri;
+
+    public JedgeLLMAgent(JedgeLLMContext llmCtx, String cbMod, String cbUri) {
+        super(llmCtx);
+        ppManager = new JedgeLLMParamPickerManager(llmCtx);
+        this.cbMod = cbMod;
+        this.cbUri = cbUri;
+    }
+
+    public void loadLocalCommandHandler(String path) {
+        ppManager.loadPickersFromPath(path);
+    }
+
+    public boolean runInstruction(JedgeTalkSession s, String query, String objectId, String mainParamKey) {
+        //需要加载哪些数据集
+        JedgeLLMParamPicker indexParser = ppManager.getLLMParamPicker(mainParamKey);
+        if(indexParser==null) {
+            llmContext.service.returnDirectGptMessage(s, "未找到动作识别器:" + mainParamKey + "\n");
+            return false;
+        }
+        String actions = indexParser.getClassifiedParam(s, query, mainParamKey);
+//        llmContext.service.returnDirectGptMessage(s, "识别动作:" + actions + "\n");
+//        if(JBotAppBase.isDebugMode())   //暂时不执行后续代码
+//            return true;
+        QIData params = null;
+        if(actions!=null) {
+            if (actions.contains(",")) {
+                for (String acKey : actions.split(",")) {
+                    JedgeLLMParamPicker picker = ppManager.getLLMParamPicker(acKey);
+                    if (picker != null) {
+                        llmContext.service.returnDirectGptMessage(s, "处理参数:" + acKey + "\n");
+                        params = picker.pickParamsFromQuery(s, query, null);
+                    }
+                    makeInstructionCall(s, query, mainParamKey, acKey, objectId, params);
+                }
+            } else {
+                JedgeLLMParamPicker picker = ppManager.getLLMParamPicker(actions);
+                if (picker != null) {
+                    params = picker.pickParamsFromQuery(s, query, null);
+                }
+                makeInstructionCall(s, query, mainParamKey, actions, objectId, params);
+            }
+        }
+        return true;
+    }
+
+    private void makeInstructionCall(JedgeTalkSession s, String query, String mainParamKey, String acKey, String objId, QIData params) {
+        if(objId.indexOf(',')>-1) {
+            String[] Objs = objId.split(",");
+            for(String o : Objs) {
+                makeInstructionCall(s, query, mainParamKey, acKey, params, o);
+            }
+        } else {
+            makeInstructionCall(s, query, mainParamKey, acKey, params, objId);
+        }
+    }
+
+    private void makeInstructionCall(JedgeTalkSession s, String query, String mainParamKey, String acKey, QIData params, String o) {
+        QIData req = new QData().putString(mainParamKey, acKey).put("params", params).putString("name", o);
+        QIData resp = llmContext.service.postServiceRequest(cbMod, cbUri, req);
+        if (!JMgbusUtil.isMgbusResultOk(resp) && JBotAppBase.isDebugMode())
+            llmContext.service.returnDirectGptMessage(s, "请求" + acKey + "失败.\n");
+        else {
+            String returnMessage = resp.getString("llmMsg");
+            if(StringUtils.isValidStr(returnMessage)) {
+                String responseQuery = String.format("用户输入:%s,系统返回:%s,请生成一个对用户的应答", query, returnMessage );
+                llmContext.service.makeRealGptStreamCall(s, responseQuery);
+            }
+            llmContext.service.returnDirectGptMessage(s, "请求:" + acKey + "成功.\n");
+        }
+    }
+}

+ 77 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeLLMTask.java

@@ -0,0 +1,77 @@
+package com.ch.jedge.jbot2.llm.world.task;
+
+
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+
+/**
+ *  任务对象:
+ *      执行计划:分为子任务和动作
+ *
+ */
+public class JedgeLLMTask extends JedgeLLMBaseObject {
+    protected final JedgeTaskTester taskTester;
+    private JedgeLLMTaskGaol goal;  //任务目标
+
+    public JedgeLLMTask(JedgeLLMContext llmCtx, String taskKey, JedgeTaskTester taskTester) {
+        super(llmCtx);
+        this.taskTester = taskTester;
+    }
+
+    public boolean handleQuery(JedgeTalkSession s, String query, QIData domain) {
+        //
+//        if(containsDomain(domain)) {
+//
+//        }
+
+        //推断任务的目标,与任务目标的关系
+
+
+        //测试与任务的关系:
+//        String taskType = taskTester.getClassifiedParam(s, query, "task");
+//        if(taskType!=null) {
+//            switch (taskType) {
+//                default:
+//                case "other":            //未识别到任务相关的属性
+//                case "":
+//                    //做咨询类回答、不干预当前的任务。
+//                    break;
+//                case "direcInst":        //直接指令
+////                    agent.runInstruction(s, query);
+//                case "giveTarget":       //中止任务
+//                    break;
+//                case "askStatus":       //添加新目标
+//                    break;
+//                case "pauseTask":       //添加新的参数
+//                    break;
+//            }
+//        }
+
+        return false;
+    }
+
+    public boolean containsDomain(String domain) {
+        return true;
+    }
+
+    public JedgeLLMAgent getActiveAgent() {
+        return null;
+    }
+
+    public void runTask() {
+        // 定时检查任务是否已完成,如果未完成,则需要提出新的问题。
+    }
+
+    //检查任务完成状态,用户的参数回答状态。这里可主动向用户发起提问等请求。
+    public boolean checkTaskComplete() {
+        return false;
+    }
+
+    public void makeRespnose(JedgeTalkSession s, String query) {
+        //补充一些任务的状态
+        llmContext.service.makeRealGptStreamCall(s, query);
+    }
+}

+ 15 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeLLMTaskGaol.java

@@ -0,0 +1,15 @@
+package com.ch.jedge.jbot2.llm.world.task;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+
+/**
+ *  任务的目标:
+ *      描述领域-对象-属性:目标值,并可描述对模板的检查方式
+ *
+ */
+public class JedgeLLMTaskGaol extends JedgeLLMBaseObject {
+    public JedgeLLMTaskGaol(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+    }
+}

+ 15 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeLLMTaskGaolItem.java

@@ -0,0 +1,15 @@
+package com.ch.jedge.jbot2.llm.world.task;
+
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+
+/**
+ *  任务的目标项:
+ *      描述领域-对象-属性:目标值,并可描述对模板的检查方式
+ *
+ */
+public class JedgeLLMTaskGaolItem extends JedgeLLMBaseObject {
+    public JedgeLLMTaskGaolItem(JedgeLLMContext llmCtx) {
+        super(llmCtx);
+    }
+}

+ 105 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeLLMTaskPool.java

@@ -0,0 +1,105 @@
+package com.ch.jedge.jbot2.llm.world.task;
+
+
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.jbot2.intent.bot.JBotAppBase;
+import com.ch.jedge.jbot2.llm.JedgeLLMBaseObject;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.world.JedgeLLMDmWorld;
+import com.ch.jedge.jbot2.llm.world.knl.JedgeLLMKnWorld;
+import com.ch.jedge.jbot2.llm.world.system.JedgeLLMSystemTask;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.sync.SystemUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *  任务池:
+ *      对话任务管理器,支持多任务。
+ *      用户对话时生成的智能任务后、在此处进行管理。
+ *      系统启动默认生成一个任务管理器、并自动生成一个世界任务:
+ *          即:创建任务。
+ *
+ */
+
+public class JedgeLLMTaskPool extends JedgeLLMBaseObject {
+    protected final JedgeLLMKnWorld knWorld;
+    protected final JedgeLLMDmWorld dmWorld;
+    protected final JedgeLLMSystemTask systemTask;        //默认为任务创建任务
+    protected JedgeLLMTask activeTask;        //默认为任务创建任务
+    protected final Map<String, JedgeLLMTask> mTasks = new HashMap<>();
+
+    public JedgeLLMTaskPool(JedgeLLMContext llmCtx, JedgeLLMKnWorld knWorld, JedgeLLMDmWorld worldModel) {
+        super(llmCtx);
+        this.dmWorld = worldModel;
+        this.knWorld = knWorld;
+        systemTask = new JedgeLLMSystemTask(llmCtx, knWorld, worldModel, "创建任务", knWorld.getTaskTester());
+        systemTask.runTask();
+    }
+
+    //启动任务监听
+    public void startTaskWatching() {
+        // this.activeTask.
+        llmContext.module.postThread(this::checkTaskStatus);
+    }
+
+    private void checkTaskStatus() {
+        while(llmContext.service.isActive()) {
+            SystemUtils.tryWaitforSingal(mTasks, 1000);
+            synchronized (systemTask) {
+                if (systemTask.checkTaskComplete()) {
+                    systemTask.resetTaskCreationStatus();
+                }
+            }
+            synchronized (mTasks) {
+                for (String tk : mTasks.keySet()) {
+                    JedgeLLMTask task = mTasks.get(tk);
+                    task.checkTaskComplete();
+                }
+            }
+        }
+    }
+
+    public JedgeLLMAgent tryHandleTask(JedgeTalkSession s, String query) {
+        //输入后,判定其对当前任务的融合性?
+
+        //如果不能判定领域,则判断与当前任务领域的融合性。
+        QIData domain = knWorld.testDomain(s, query);
+//        if(JBotAppBase.isDebugMode())
+//            return null;
+        if(activeTask!=null) {
+            if(activeTask.handleQuery(s, query, domain)) {
+                //融合了上一个任务,不做后续任务创建处理
+                return null;
+            } else {
+                //没命中新的任务或命令,需要提出新的问题
+                activeTask.makeRespnose(s, query);
+            }
+        }
+
+        synchronized (systemTask) {
+            if (systemTask.handleQuery(s, query, domain)) {
+                activeTask = systemTask.getCurrentTask();
+                if(activeTask!=null) {
+                    return activeTask.getActiveAgent();
+                }
+            }
+        }
+
+        //获取一个Agent,用于提取任务创建参数,可能时主动发起一个动作
+
+        //如果不能融合,则可考虑转发给一个后端默认的大模型模块
+        return null;
+    }
+
+    public void makeUnhandledQueryResponse(JedgeTalkSession s, String query) {
+        //生成一个提示词?
+        // knWorld.makeCommonResponse(s, query);
+        llmContext.service.returnDirectGptMessage(s, "暂不处理.\n", true);
+    }
+
+    public void onHeartbeat() {
+
+    }
+}

+ 40 - 0
app/src/main/java/com/ch/jedge/jbot2/llm/world/task/JedgeTaskTester.java

@@ -0,0 +1,40 @@
+package com.ch.jedge.jbot2.llm.world.task;
+
+import com.ch.jedge.jbot2.context.JedgeTalkSession;
+import com.ch.jedge.jbot2.llm.JedgeLLMContext;
+import com.ch.jedge.jbot2.llm.pickers.JedgeLLMParamPicker;
+
+public class JedgeTaskTester extends JedgeLLMParamPicker {
+
+    public JedgeTaskTester(JedgeLLMContext llmContext) {
+        super(llmContext);
+        loadPickerFromFile("./cfg/picker/system/task.json");
+    }
+
+    public String checkTaskType(JedgeTalkSession s, String query) {
+        // 测试与任务的关系:
+        return getClassifiedParam(s, query, "task");
+//
+//        String taskType = getClassifiedParam(s, query, "task");
+//        if(taskType!=null) {
+//            switch (taskType) {
+//                default:
+//                case "other":            //未识别到任务相关的属性
+//                case "":
+//                    //做咨询类回答、不干预当前的任务。
+//                    break;
+//                case "direcInst":        //直接指令
+////                    agent.runInstruction(s, query);
+//                case "giveTarget":       //中止任务
+//                    break;
+//                case "askStatus":       //添加新目标
+//                    break;
+//                case "pauseTask":       //添加新的参数
+//                    break;
+//            }
+//        }
+    }
+
+    //获得Task的相关性及其后续动作
+
+}

+ 13 - 0
app/src/main/java/com/ch/jedge/jbot2/readme.txt

@@ -0,0 +1,13 @@
+重构JBot:
+
+· 大模型的主要功能是:根据用户的对话及其历史,推断用户的目标诉求,然后进行领域划分,再判断其目标,然后再分解功能步骤,调用API实现其功能步骤,最后执行完毕。
+· 第一步: 领域划分。  分类器。
+· 第二步: 判断用户的目标(或修正用户目标):  分类器。要求:大模型根据用户提供的可选目标范围,进行推理和分类,确定用户说的话的目标。
+· 第三步: 根据用户目标,提取其执行器的状态参数。
+· 第四步:
+
+
+JBot的主要功能是:实现向JBot注册任务、规划、执行路径和功能、API的消息格式和对应的参数。
+(1) 各Bot应用向JBot注册分类领域和功能;
+(2) JBot根据用户的对话,判断用户的任务意图分类:(补充参数、修改或增加目标,继续还是中止任务:  提供补充的数据:任务状态、用户当前的任务目标、用户当前的任务状态);
+(3)

+ 136 - 0
app/src/main/java/com/ch/jedge/utils/JedgeBotConfigUtil.java

@@ -0,0 +1,136 @@
+package com.ch.jedge.utils;
+
+import com.changhong.jedge.JMgbusClient;
+import com.changhong.qlib.QData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.StringUtils;
+import com.changhong.qlib.util.file.FileUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.ch.jedge.utils.JedgeBotConst.sval_milvus_cfg_path;
+
+public class JedgeBotConfigUtil {
+    protected static final Map<String, QIData> mConfigs= new HashMap<>();
+
+    public static void loadConfigs() {
+        FileUtils.listDirFiles(sval_milvus_cfg_path, file -> {
+            if(!file.getName().endsWith(".json"))
+                return null;
+            QIData cfgData = QData.fromFile(file);
+            if(cfgData.isEmpty()) return null;
+            synchronized (mConfigs) {
+                String fn = FileUtils.getFilenameWithoutExt(file.getName());
+                mConfigs.put(fn, cfgData);
+            }
+            return null;
+        });
+    }
+
+    public static void releaseConfigs() {
+        synchronized (mConfigs) {
+            mConfigs.clear();
+        }
+    }
+
+    public static void setConfigProp(String cfgKey, String prop, Object val) {
+        synchronized (mConfigs) {
+            if(!mConfigs.containsKey(cfgKey))
+                return;
+            QIData cfgData=mConfigs.get(cfgKey);
+            cfgData.put(prop, val);
+        }
+    }
+
+    public static Object getConfigProp(String cfgKey, String prop, Object defVal) {
+        synchronized (mConfigs) {
+            if(!mConfigs.containsKey(cfgKey))
+                return defVal;
+            QIData cfgData=mConfigs.get(cfgKey);
+            return cfgData.containsKey(prop) ?
+                    cfgData.get(prop) : defVal;
+        }
+    }
+
+    public static boolean getConfigBoolean(String cfgKey, String prop, boolean defVal) {
+        synchronized (mConfigs) {
+            if(!mConfigs.containsKey(cfgKey))
+                return defVal;
+            QIData cfgData=mConfigs.get(cfgKey);
+            Object obj = cfgData.containsKey(prop) ?
+                    cfgData.get(prop) : defVal;
+            return obj instanceof Boolean? (boolean) obj: defVal;
+        }
+    }
+
+    public static int getConfigInteger(String cfgKey, String prop, int defVal) {
+        synchronized (mConfigs) {
+            if(!mConfigs.containsKey(cfgKey))
+                return defVal;
+            QIData cfgData=mConfigs.get(cfgKey);
+            Object obj = cfgData.containsKey(prop) ?
+                    cfgData.get(prop) : defVal;
+            return obj instanceof Integer? (int) obj: defVal;
+        }
+    }
+
+    public static String getConfigObjSimpleFmtString(String cfgKey, String prop, String defVal) {
+        synchronized (mConfigs) {
+            if(!mConfigs.containsKey(cfgKey))
+                return null;
+            QIData cfgData=mConfigs.get(cfgKey);
+            return cfgData.getObjSimpleFmtString(prop, defVal);
+        }
+    }
+
+    public static QIData getConfig(String cfgKey) {
+        synchronized (mConfigs) {
+            return mConfigs.get(cfgKey);
+        }
+    }
+
+    public static void saveConfig(String cfgKey) {
+        synchronized (mConfigs) {
+            QIData cfg = mConfigs.get(cfgKey);
+            if(cfg!=null) {
+                cfg.saveToFile(FileUtils.contactPath(sval_milvus_cfg_path, cfgKey+".json"));
+            }
+        }
+    }
+
+    public static boolean setupInitConfigs(JMgbusClient mgClient, QIData param, String defaultClientName) {
+
+        String host = param.getString("host");
+        int port = param.getInteger("port");
+        String jam = param.getString("jam");
+
+        if(StringUtils.isNotValidStr(host)) {
+            host = JedgeBotConfigUtil.getConfigObjSimpleFmtString("jedge", "host", "127.0.0.1");
+            if(StringUtils.isNotValidStr(host))
+                host = JedgeBotConfigUtil.getConfigObjSimpleFmtString(defaultClientName, "jedge.host", null);
+        }
+        if(StringUtils.isNotValidStr(host)) host = "127.0.0.1";
+        if(StringUtils.isNotValidStr(jam))
+            jam = JedgeBotConfigUtil.getConfigObjSimpleFmtString(defaultClientName, "jedge.name", defaultClientName);
+        if(port<0) {
+            Object oport = JedgeBotConfigUtil.getConfigProp("jedge", "port", 8877);
+            if ((oport instanceof Integer))
+                port = (int) oport;
+            if(port<0)
+                oport = JedgeBotConfigUtil.getConfigProp(defaultClientName, "jedge.port", 8877);
+            if (!(oport instanceof Integer))
+                port = 8877;
+            else
+                port = (int) oport;
+        }
+        //设置默认启动接入的站点
+
+        mgClient.setDefaultHost(host, port);
+        if(StringUtils.isNotValidStr(jam)) jam = defaultClientName;
+        mgClient.resetClientName(jam);
+
+        //后台运行 -p
+        return !param.getBoolean("p", false);
+    }
+}

+ 71 - 0
app/src/main/java/com/ch/jedge/utils/JedgeBotConst.java

@@ -0,0 +1,71 @@
+package com.ch.jedge.utils;
+
+import com.changhong.qlib.util.file.FileUtils;
+
+public class JedgeBotConst {
+
+    public static final String sval_sub_dir_cfg = "cfg";
+    public static final String sval_sub_dir_glm = "glm";
+    public static final String sval_sub_dir_field_cfg = "cols";
+
+    // public static final String sval_panel_class_base_path = (new File(ConfigUtil.class.getResource("/").getFile())).getPath();
+    public static final String sval_panel_class_base_path = "./";
+    public static final String sval_server_db_cfg_filename = FileUtils.contactPath(sval_panel_class_base_path, sval_sub_dir_cfg, "db.json");
+
+    public static final String sval_milvus_col_def_filename = FileUtils.contactPath(sval_panel_class_base_path, sval_sub_dir_cfg,"_col_def.json");
+    public static final String sval_milvus_msg_def_filename = FileUtils.contactPath(sval_panel_class_base_path, sval_sub_dir_cfg,"_msg_add.json");
+    public static final String sval_glm_config_filename = FileUtils.contactPath(sval_panel_class_base_path, sval_sub_dir_glm,"glm.json");
+
+    public static final String sval_milvus_cfg_path = FileUtils.contactPath(sval_panel_class_base_path, sval_sub_dir_cfg);
+
+    public static final String sval_corpus_cache_path_debug_ = FileUtils.contactPath(sval_panel_class_base_path, "jbot/unit/jbot/cache/corpus");
+
+    public static final String sval_module_llm = "llm";
+
+    public static final String sval_modual_word_cut = "wcut";
+
+    public static final String sval_modual_vec_db = "vdb";
+    public static final String sval_default_vector_service = "gv";
+    public static final String sval_modual_glm_llm = "llm";
+
+    public static final String sval_default_jbot_jedge = "jbot";
+    public static final String sval_default_gpt_service = "gpt";
+    public static final String sval_default_llm_call_service = "lmc";
+    public static final String sval_default_gpt_voice_service = "gptv";
+    public static final String sval_default_glm6b_service = "glm6b";
+
+    public static final String default_vs_domain_ = "_knDefault";
+    public static final String default_vs_domain_local = "tc";
+
+    public static final long ival_timeout_session_ = 10 * 60 * 10;  //second * 10 : 600S, 10 min.
+    public static final int  max_history_size = 5;
+    public static final int  max_history_total_size = 256;
+    public static final int  ival_mufis_text_message_gap_time_ = 100;
+    public static final int  ival_max_timeout_count = 180;
+
+    public static final String sval_prompt_intentJudge =
+            "意图领域范围:%s,根据对话内容,提取问题的意图范围和知识关键字、意图领域只能在上述意图领域范围内、用如下json格式输出%s";
+    public static final String sval_prompt_kn_build = "就如下内容用提出总共%d个咨询类的问题和答案。每个问题聚焦不超过12字,答案尽可能丰富。问答对的输出格式: Q:这是一个问题A:这是答案";
+    public static final String sval_prompt_kn_talk = "用户希望:%s, 回答内容全部在 %s 中,你需要扮演角色:%s,回答从上述内容中回答下面的问题(只输出答案):%s";
+    public static final String sval_prompt_kn_explain = "用如下内容生成精简的一句话回答:%s";
+
+
+    public static final boolean old_vdb_mode_ = true;
+    public static final boolean local_corpus_ = true;
+
+    public static final int  ival_max_kn_items = 80;
+
+    public static final String sval_verb_type_user_defined_relation_noun = "nur";
+    public static final String sval_verb_type_user_defined_join_noun = "nuj";
+    public static final String val_verb_type_user_defined_noun = "nud";
+    public static final String val_verb_type_user_defined_verb = "vud";
+    public static final String sval_verb_type_user_defined_inference = "nugr";      //需要推理预测结果的词,不是直接命中答案。
+    public static final int lval_min_time_gap = 5000; //每5S添加一条向量数据,防止添加过快导致milvus崩溃
+
+    public static final String sval_predict_split = "$$$";
+    public static final String sval_predict_sign = "@@@";
+    public static final int sval_predict_sign_size = sval_predict_sign.length();
+
+    public static boolean debugMode = false;
+    public static boolean enableSmartTaskCreate = false;
+}

+ 502 - 0
app/src/main/java/com/ch/jedge/utils/JedgeJDBCUtils.java

@@ -0,0 +1,502 @@
+package com.ch.jedge.utils;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONException;
+import com.alibaba.fastjson.JSONObject;
+import com.changhong.qlib.QTimeOutWatchData;
+import com.changhong.qlib.intf.QIData;
+import com.changhong.qlib.util.Log.ISLog;
+import com.changhong.qlib.util.NumberUtils;
+import com.changhong.qlib.util.StringUtils;
+import com.changhong.qlib.util.sync.SystemUtils;
+
+import java.sql.*;
+import java.util.*;
+
+import static com.changhong.qlib.util.net.IPUtils.localAddr;
+
+
+/**
+ *  在这里可添加对mysql的数据库访问的接口,用于对数据库的table做操作。
+ *
+ */
+
+
+public class JedgeJDBCUtils {
+    //type,host,port,charcode
+    private static final String sval_jdbc_source_format = "jdbc:%s://%s:%d/%s?useUnicode=true&characterEncoding=%s&serverTimezone=%s&autoReconnect=true";
+
+    private static final String vkey_jdbc_config = "db.";
+    private static final String vkey_jdbc_db_type = vkey_jdbc_config+"type";
+    private static final String vkey_jdbc_db_name = vkey_jdbc_config+"db";
+    private static final String vkey_jdbc_db_host = vkey_jdbc_config+"host";
+    private static final String vkey_jdbc_db_port = vkey_jdbc_config+"port";
+    private static final String vkey_jdbc_db_charcode = vkey_jdbc_config+"charcode";
+    private static final String vkey_jdbc_db_timezone = vkey_jdbc_config+"timezone";
+    private static final String vkey_jdbc_db_user = vkey_jdbc_config+"user";
+    private static final String vkey_jdbc_db_password = vkey_jdbc_config+"password";
+    private static final String vkey_jdbc_db_driverclass = vkey_jdbc_config+"driverClass";
+
+    private static final String sval_jdbc_db_rebuild_db = "rebuild_db";
+
+    private static final String sval_jdbc_db_default_type = "mysql";
+    private static final String sval_jdbc_db_default_jdbc_driver = "com.mysql.cj.jdbc.Driver";
+//    private static final String sval_jdbc_db_default_jdbc_driver =  "com.mysql.jdbc.Driver";
+    private static final String sval_jdbc_db_default_dbname = "";
+    private static final int ival_jdbc_db_default_port = 3306;
+    private static final String sval_jdbc_db_default_charcode = "utf8";
+    private static final String sval_jdbc_db_default_timezone = "Asia/Shanghai";
+    private static final String sval_jdbc_db_default_user = "root";
+    private static final String sval_jdbc_db_default_password = "";
+
+    private static final String vkey_jdbc_db_conn_cnt = vkey_jdbc_config+"connCnt";
+    //最大数据库连接数,mysql最大一般为150,通常可设为100。
+    //此处参数的含义时:当少于这个数时,找不到立即创建一个新链接,否则需要等待更长时间。
+    private static final int ival_jdbc_db_default_conn_cnt = 50;
+    private static final int ival_jdbc_db_op_wait_time = 100;   //获取SQL失败时的等待时间(单个SQL执行等待时间)。
+
+    private static final List<JDBCConnManItem> mConnections = new ArrayList<>();
+    private static final Map<String,JDBCConnManItem> mConnMap = new HashMap<>();
+    private static final Map<Integer,JDBCConnManItem> mIndexMap = new HashMap<>();
+    private static final List<Integer>      mFreeIndexs = new ArrayList<>();
+
+    private static int mActiveConnectionCount = 0;
+
+    private static String dbURL;
+
+    private static String dbUser = sval_jdbc_db_default_user;
+    private static String dbPassword = sval_jdbc_db_default_password;
+
+    private static String dbName = sval_jdbc_db_default_dbname;
+    private static String mDbType = sval_jdbc_db_default_type;
+    private static boolean mbRebuildDb = false;
+
+    private static int maxConnections;
+    private static boolean initialized = false;
+    private static final String logKey = "JDBCUtils";
+    private static String dbDriverClass = sval_jdbc_db_default_jdbc_driver;
+
+    public static boolean initJDBC(QIData serverConfig) {
+
+        if(!initialized) {
+            mbRebuildDb = serverConfig.getBoolean(sval_jdbc_db_rebuild_db, false);
+            //如果设置为重置数据库,则直接重建数据库,不管数据库本身是否存在,用于开发过程中,快速更新数据库。
+            if (mbRebuildDb) {
+                mbRebuildDb = false;    //一次运行只能做一次数据库初始化。
+                initialized = true;
+                return false;
+            }
+        }
+        initialized = false;
+
+        mDbType = serverConfig.getObjSimpleFmtString(vkey_jdbc_db_type, sval_jdbc_db_default_type);
+        dbDriverClass = serverConfig.getObjSimpleFmtString(vkey_jdbc_db_driverclass,sval_jdbc_db_default_jdbc_driver);
+
+        ISLog.Log(logKey, String.format("Init db with driver : %s", dbDriverClass));
+
+        Driver driver;
+        try {
+            driver = (Driver) Class.forName(dbDriverClass).newInstance();;
+        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
+            ISLog.stackLog(logKey,"Fatal error !!! No jdbc driver was found, check your system config.");
+            return false;
+        }
+
+        String mDbHost = serverConfig.getObjSimpleFmtString(vkey_jdbc_db_host, localAddr);
+        String dbPort = serverConfig.getObjSimpleFmtString(vkey_jdbc_db_port);
+        int mDbPort = StringUtils.toInteger(dbPort, ival_jdbc_db_default_port);
+        String mCharcord = serverConfig.getObjSimpleFmtString(vkey_jdbc_db_charcode, sval_jdbc_db_default_charcode);
+        String timeZone = serverConfig.getObjSimpleFmtString(vkey_jdbc_db_timezone, sval_jdbc_db_default_timezone);
+
+        dbName = serverConfig.getObjSimpleFmtString(vkey_jdbc_db_name, sval_jdbc_db_default_dbname);
+
+        dbUser = serverConfig.getObjSimpleFmtString(vkey_jdbc_db_user, sval_jdbc_db_default_user);
+        dbPassword = serverConfig.getObjSimpleFmtString(vkey_jdbc_db_password, sval_jdbc_db_default_password);
+
+        dbURL= String.format(sval_jdbc_source_format,mDbType,mDbHost,mDbPort,dbName,mCharcord,timeZone);
+        String dbConnCnt = serverConfig.getObjSimpleFmtString(vkey_jdbc_db_conn_cnt);
+        maxConnections = StringUtils.toInteger(dbConnCnt,ival_jdbc_db_default_conn_cnt);
+
+        try {
+            DriverManager.registerDriver(driver);   // 注册 JDBC 驱动程序
+            Connection conn = getFreeConnection();  //链接数据库如果失败
+            if(conn==null)
+                return false;
+            // 如果这是第一次创建数据库连接,即检查数据库,获得此数据库允许支持的
+            // 最大客户连接数目
+            // connections.Qsize()==0 表示目前没有连接己被创建
+            DatabaseMetaData metaData = conn.getMetaData();
+            int driverMaxConnections = metaData.getMaxConnections();
+            // 数据库返回的 driverMaxConnections 若为 0 ,表示此数据库没有最大
+            // 连接限制,或数据库的最大连接限制不知道
+            // driverMaxConnections 为返回的一个整数,表示此数据库允许客户连接的数目
+            // 如果连接池中设置的最大连接数量大于数据库允许的连接数目 , 则置连接池的最大
+            // 连接数目为数据库允许的最大数目
+            if (driverMaxConnections > 0 && maxConnections > driverMaxConnections) {
+                maxConnections = driverMaxConnections;
+            }
+            releaseConnection(conn);
+        } catch (SQLException e) {
+            e.printStackTrace();
+            return false;
+        }
+        initialized = true;
+        return initialized;
+    }
+
+    public static void releaseJDBC() {
+        if(!initialized)
+            return;
+        synchronized (mConnections) {
+            for (JDBCConnManItem conn : mConnections) {
+                try {
+                    conn.mConn.close();
+                } catch (SQLException ignored) {
+                }
+            }
+            mConnections.clear();
+            mFreeIndexs.clear();
+            mConnMap.clear();
+            mIndexMap.clear();
+        }
+        mActiveConnectionCount = 0;
+        initialized = false;
+        try {
+            Enumeration<Driver> dl = DriverManager.getDrivers();
+            while(dl.hasMoreElements()){
+                Driver d = dl.nextElement();
+                DriverManager.deregisterDriver(d);
+            }
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static Connection getNewConnection() {
+        Connection conn = null;
+        try {
+            conn = DriverManager.getConnection(dbURL, dbUser,
+                    dbPassword);
+            if(mActiveConnectionCount<maxConnections) {
+                synchronized (mConnections) {
+                    int freeIdx = getFreeConnectionIndex();
+                    if(freeIdx>-1) {
+                        JDBCConnManItem item = new JDBCConnManItem(freeIdx, conn);
+                        mConnections.add(item);
+                        mConnMap.put(String.valueOf(conn), item);
+                        mIndexMap.put(freeIdx,item);
+                        mActiveConnectionCount++;
+                        return conn;
+                    }
+                }
+            }
+            ISLog.stackLog(logKey,String.format("Max Connection %s exceeds, temp connection was created.", NumberUtils.numberFormat(mActiveConnectionCount)));
+        } catch (SQLException e) {
+            ISLog.stackLog(logKey,String.format("Error when create new connectionto db : %s",e.getMessage()));
+            ISLog.stackLog(logKey,String.format("\t\t Params: %s:%s to %s",dbUser,dbPassword,dbURL));
+        }
+        return conn;
+    }
+
+    private static int getFreeConnectionIndex() {
+        for(int i=0;i<maxConnections;i++){
+            if(!mIndexMap.containsKey(i))
+                return i;
+        }
+        return -1;
+    }
+
+    public static String getDbName() {
+        return dbName;
+    }
+
+    public static String getDbType() {
+        return mDbType;
+    }
+
+    public static String getDbUrl() {
+        return dbURL;
+    }
+
+    public static String getDbUser() {
+        return dbUser;
+    }
+
+    public static String getDbPassword() {
+        return dbPassword;
+    }
+
+    public static String getDbDriverClass() {
+        return dbDriverClass;
+    }
+
+    public static JSONArray resultSetToJsonArry(ResultSet rs) throws SQLException, JSONException
+    {
+        // json数组
+        JSONArray array = new JSONArray();
+        // 获取列数
+        ResultSetMetaData metaData = rs.getMetaData();
+        int columnCount = metaData.getColumnCount();
+        // 遍历ResultSet中的每条数据
+        while (rs.next()) {
+            JSONObject jsonObj = new JSONObject();
+            // 遍历每一列
+            for (int i = 1; i <= columnCount; i++) {
+                String columnName =metaData.getColumnLabel(i);
+                String value = rs.getString(columnName);
+                jsonObj.put(columnName, value);
+            }
+            array.add(jsonObj);
+        }
+        return array;
+    }
+    /**
+     * 将resultSet转化为JSONObject
+     * @param rs
+     * @return
+     * @throws SQLException
+     * @throws JSONException
+     */
+    public static JSONObject resultSetToJsonObject(ResultSet rs) throws SQLException, JSONException
+    {
+        // json对象
+        JSONObject jsonObj = new JSONObject();
+        // 获取列数
+        ResultSetMetaData metaData = rs.getMetaData();
+        int columnCount = metaData.getColumnCount();
+        // 遍历ResultSet中的每条数据
+        if (rs.next()) {
+            // 遍历每一列
+            for (int i = 1; i <= columnCount; i++) {
+                String columnName =metaData.getColumnLabel(i);
+                String value = rs.getString(columnName);
+                jsonObj.put(columnName, value);
+            }
+        }
+        return jsonObj;
+    }
+
+    private static Connection getFreeConnection() {
+        Connection conn = null;
+        int tryCnt = 3;
+        int idx = -1;
+        while (tryCnt > 0) {
+            if (mActiveConnectionCount > 0) {
+                synchronized (mFreeIndexs) {
+                    if (mFreeIndexs.size() > 0)
+                        idx = mFreeIndexs.remove(0);
+                }
+            }
+            if (idx > -1 && idx < mActiveConnectionCount) {
+                conn = tryGetConnection(idx);
+                if (conn != null)
+                    break;
+            }
+            if (mActiveConnectionCount < maxConnections) {
+                conn = getNewConnection();
+                if (conn != null)
+                    break;
+            }
+            SystemUtils.tryWait(ival_jdbc_db_op_wait_time);
+            tryCnt--;
+
+        }
+        if (conn != null) {
+//            ISLog.stackLog(logKey,"Conn["+idx+"] was occupied.");
+        } else {
+            ISLog.stackLog(logKey, "Warning!!! No Free Connection Available!,create new temp one.");
+            try {
+                conn = DriverManager.getConnection(dbURL, dbUser, dbPassword);
+            } catch (SQLException err) {
+                err.printStackTrace();
+            }
+        }
+        return conn;
+    }
+
+    private static Connection tryGetConnection(int idx) {
+        synchronized (mConnections) {
+            if(idx<mConnections.size()) {
+                JDBCConnManItem conn = mConnections.get(idx);
+                if(conn==null || conn.mBusi)
+                    return null;
+                //Connection访问计数+1
+                conn.mBusi = true;
+                conn.mAccCnt++;
+                conn.updateAccessTime();
+                return conn.mConn;
+            }
+        }
+        return null;
+    }
+
+    private static void releaseConnection(Connection conn) {
+        synchronized (mConnections){
+            JDBCConnManItem connItem = mConnMap.get(String.valueOf(conn));
+            if(connItem!=null){
+//                ISLog.stackLog(logKey,"Conn["+connItem.mIndex+"] was released.");
+                connItem.mBusi = false;
+                mFreeIndexs.add(connItem.mIndex);   //加入空闲队列
+            } else {
+                ISLog.stackLog(logKey, "Warning!!! Not cached connection releasing...");
+                try {
+                    conn.close();
+                } catch (SQLException ignored) { }
+            }
+        }
+    }
+
+    public static JSONArray execSqlForRecordSet(String sql) {
+        Connection conn = getFreeConnection();
+        if(conn!=null){
+            try {
+                Statement stmt = conn.createStatement();
+//                ISLog.stackLog(logKey,"SQL:"+sql);
+                ResultSet rs = stmt.executeQuery(sql);
+                JSONArray re = resultSetToJsonArry(rs);
+                releaseConnection(conn);
+                return re;
+            } catch (SQLException e) {
+                releaseConnection(conn);
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    public static JSONObject execSqlForFirstRecord(String sql) {
+        Connection conn = getFreeConnection();
+        if(conn!=null){
+            try {
+                Statement stmt = conn.createStatement();
+                ISLog.stackLog(logKey,"SQL:"+sql);
+                ResultSet rs = stmt.executeQuery(sql);
+                JSONObject re = resultSetToJsonObject(rs);
+                releaseConnection(conn);
+                return re;
+            } catch (SQLException e) {
+                releaseConnection(conn);
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    public static boolean execSqlForSuccess(String sql) {
+        Connection conn = getFreeConnection();
+        if(conn!=null){
+            try {
+                Statement stmt = conn.createStatement();
+                ISLog.stackLog(logKey,sql);
+                boolean ok = stmt.execute(sql);
+                releaseConnection(conn);
+                return true;
+            } catch (SQLException e) {
+                releaseConnection(conn);
+                e.printStackTrace();
+            }
+        }
+        return false;
+    }
+
+    public static int execSqlForUpdatedRecord(String sql) {
+        Connection conn = getFreeConnection();
+        if(conn!=null){
+            try {
+                Statement stmt = conn.createStatement();
+                ISLog.stackLog(logKey,sql);
+                int r = stmt.executeUpdate(sql);
+                releaseConnection(conn);
+                return r;
+            } catch (SQLException e) {
+                e.printStackTrace();
+                releaseConnection(conn);
+                return -1;
+            }
+        }
+        return 0;
+    }
+
+    public static boolean isInitialized() {
+        return initialized;
+    }
+
+    private static class JDBCConnManItem extends QTimeOutWatchData {
+        private static final int ival_db_link_timeout = 300 * 60 * 1000;    //连接超时时间,300分钟
+        final   int     mIndex;
+        boolean mBusi = true;
+        int     mAccCnt = 0;
+        final   Connection mConn;
+
+        JDBCConnManItem(int index, Connection conn){
+            super(ival_db_link_timeout);
+            mIndex = index;
+            mConn = conn;
+        }
+
+        @Override
+        protected String getTimeoutItemInfo() {
+            return "DBConn:" + mIndex;
+        }
+
+        @Override
+        protected void onTimeOut() {
+            JedgeJDBCUtils.closeConnection(mConn);
+        }
+    }
+
+    private static void closeConnection(Connection conn) {
+        if(conn == null ) return;
+        synchronized (mConnections) {
+            JDBCConnManItem connItem = mConnMap.remove(String.valueOf(conn));
+            if(connItem==null){ //注,未搜索conn未命中,但List中包含数据的情况
+                ISLog.stackLog(logKey,"Removing uncached connection : " + conn);
+            }else if (connItem.mIndex < mConnections.size()) {
+                mConnections.remove(connItem.mIndex);
+                mIndexMap.remove(connItem.mIndex);
+                mActiveConnectionCount -- ;
+                ISLog.stackLog(logKey,"Connection " + connItem.mIndex + " removed : " + conn);
+            } else {  //遍历仔细检查,通常不会出现
+                JDBCConnManItem titem = null;
+                for(JDBCConnManItem item : mConnections){
+                    if(item.mConn == conn){
+                        titem = item;
+                        break;
+                    }
+                }
+                if(titem!=null){
+                    mConnections.remove(titem);
+                    mIndexMap.remove(titem.mIndex);
+                    ISLog.stackLog(logKey,"Connection " + titem.mIndex + " removed : " + conn);
+                    mActiveConnectionCount -- ;
+                }
+            }
+        }
+        try {
+            conn.close();
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static List<String> getConnectionInfo() {
+        List<String> infos = new ArrayList<>();
+        int totalAc = 0;
+        synchronized (mConnections) {
+            int i=0; StringBuilder sb=null;
+            for(JDBCConnManItem connItem : mConnections){
+                if(sb==null) sb = new StringBuilder();
+                sb.append(String.format("\t[Con:%2s,Busi:%s,ACnt:%5d],", connItem.mIndex, connItem.mBusi,connItem.mAccCnt));
+                totalAc += connItem.mAccCnt;
+
+                i++;
+                if(i>5){
+                    infos.add(sb.toString());
+                    i = 0; sb = null;
+                }
+            }
+            infos.add(String.format("\t\tTotal jdbc access times %d ,active connections: %s.",totalAc, NumberUtils.numberFormat(mActiveConnectionCount)));
+        }
+        return infos;
+    }
+}

+ 135 - 0
app/src/main/java/com/ch/jedge/utils/JedgeLlmUtil.java

@@ -0,0 +1,135 @@
+package com.ch.jedge.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.parser.Feature;
+import com.changhong.jedge.JMgbusUtil;
+import com.changhong.qlib.intf.QIData;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public class JedgeLlmUtil {
+    public static QIData simpleOk() {
+        return JMgbusUtil.MgbusResult(200,"ok");
+    }
+
+    public static QIData simpleFail(String formatStr, Object... vals) {
+        return JMgbusUtil.MgbusResult(200,String.format(formatStr, vals));
+    }
+
+    public static String getFirstRandomKey(Set<String> keySet) {
+        if(keySet!=null)
+            for(String key : keySet)
+                return key;
+        return null;
+    }
+
+    public static String unescapeString(String input) {
+        if (input == null) {
+            return null;
+        }
+
+        StringBuilder result = new StringBuilder();
+        int length = input.length();
+        boolean isEscaped = false;
+
+        for (int i = 0; i < length; i++) {
+            char currentChar = input.charAt(i);
+
+            if (isEscaped) {
+                switch (currentChar) {
+                    case 'n':
+                        result.append('\n');
+                        break;
+                    case 'r':
+                        result.append('\r');
+                        break;
+                    case 't':
+                        result.append('\t');
+                        break;
+                    case 'b':
+                        result.append('\b');
+                        break;
+                    case 'f':
+                        result.append('\f');
+                        break;
+                    case '\'':
+                        result.append('\'');
+                        break;
+                    case '\"':
+                        result.append('\"');
+                        break;
+                    case '\\':
+                        result.append('\\');
+                        break;
+                    default:
+                        result.append(currentChar);
+                        break;
+                }
+                isEscaped = false;
+            } else {
+                if (currentChar == '\\') {
+                    isEscaped = true;
+                } else {
+                    result.append(currentChar);
+                }
+            }
+        }
+
+        return result.toString();
+    }
+
+    public static JSONArray JsonArrayFromStringArray(String[] split) {
+        JSONArray re = new JSONArray();
+        re.addAll(Arrays.asList(split));
+        return re;
+    }
+
+    public static JSONArray JsonArrayFromJsonString(String initVal) {
+        try {
+            Object target = JSON.parse(initVal, Feature.OrderedField);
+            if (target instanceof JSONObject) {
+                JSONArray re = new JSONArray();
+                re.add(target);
+                return re;
+            } else if(target instanceof JSONArray){
+                return (JSONArray) target;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return new JSONArray();
+    }
+
+    public static JSONArray JsonArrayFromStringList(List<String> items) {
+        JSONArray re = new JSONArray();
+        re.addAll(items);
+        return re;
+    }
+
+    public static String StringFromStringJSONArray(JSONArray ds) {
+        if(ds!=null) {
+            StringBuilder r=new StringBuilder();
+            for(Object d : ds) {
+                if(d instanceof String)
+                    if(r.length()>0) r.append(",");
+                    r.append(d);
+            }
+            return r.toString();
+        }
+        return null;
+    }
+
+    public static String[] StringArrayFromJsonArray(JSONArray ds) {
+        if(ds!=null && !ds.isEmpty()) {
+            String[] result = new String[ds.size()];
+            for(int i=0;i<ds.size();i++)
+                result[i] = String.valueOf(ds.get(i));
+            return result;
+        }
+        return new String[0];
+    }
+}