介紹
最近閑來無事,仿照問答、客服類的需求,做了一個需求設計,本人是産品經理+開發,脾氣比較暴躁,哦呵呵,上來一陣開發,幫大家分享軟體開發經驗,提升軟體開發水準
系統導語
針對某高校招生咨詢常見的一些業務提問,設計一個基于自動問答系統。使用者可以用普通的問句對自動問答系統提問,自動問答系統将從知識庫或者網際網路中搜尋相應的答案,然後把答案直接傳回給使用者,使用語言為中文。
系統功能
1.實作自動回答子產品:
本系統實作對輸入問題的自動回答,即根據使用者輸入問題,系統能自動搜尋問答庫,傳回較合适的回答給使用者。
2.實作背景資料庫可視化操作子產品:
本系統需要有強大的問答庫支援,在不斷完善問答庫規模的同時,需要實作對問題庫、回答庫和關鍵詞庫等資料庫的可視化管理,友善使用者進行查詢、添加、 删除和更新操作。
3.使用者管理子產品:
系統所有使用者角色的管理,要有不同的角色和權限
4.資料庫導出導入功能:
實作資料庫中資料的批量導入導出功能。
5.參數設定:
在系統主界面中,實作相關參數的設定。參數設定包括針對某些常見問題的 回答設定,屏蔽一些不友好使用者。
6.詞庫建構:
建構适用聊天環境的專門詞彙庫,結合現在的詞彙庫,確定系統回答的準确性。
7.多分句回複:
實作對多分句問話的回複。在實際的問答環境中,一條問答記錄包含有多個分句,它們可能包含各自的問題,如:“學校在哪裡?搭什麼公共汽車?”能進行2個問題均能識别并回答。
8.相似度小于閥值通路的處理:
對相似度值小于閥值的處理。建立一個預設回答表,如果在問題庫中找不到相似度值大于閥值的句子,就從該表中随機選取一個句子作為回複傳回,應具備添加、删除的預設回答表的功能。
9.記錄導出功能:
能将記錄輸入的問答文本和産生的回複到一個文本檔案中,以備做機器學習的樣本。
10.問題統計管理:
可自動統計使用者關注資訊,并做圖表顯示。
選做:可以考慮通過機器學習做成具備一定AI的形式。
開源位址
https://gitee.com/bysj2021/wenda
設計樣例圖

實作代碼如下:
package com.ukefu.util.ai;
import java.io.IOException;
import java.util.List;
import org.lionsoul.jcseg.tokenizer.core.JcsegException;
import com.ukefu.core.UKDataContext;
import com.ukefu.webim.service.cache.CacheHelper;
import com.ukefu.webim.service.repository.AiConfigRepository;
import com.ukefu.webim.service.repository.SceneItemRepository;
import com.ukefu.webim.web.model.AiConfig;
import com.ukefu.webim.web.model.SceneItem;
public class AiUtils {
private static AiDicTrie aiDicTrie = new AiDicTrie();
/**
* 初始化 AI語料庫
* @param orgi
* @throws IOException
* @throws JcsegException
*/
public static AiDicTrie init(String orgi) throws IOException, JcsegException{
aiDicTrie.clean();
SceneItemRepository sceneItemRes = UKDataContext.getContext().getBean(SceneItemRepository.class) ;
List<SceneItem> sceneItemList = sceneItemRes.findByOrgiAndItemtype(orgi, UKDataContext.AiItemType.USERINPUT.toString()) ;
for(SceneItem item : sceneItemList){
aiDicTrie.insert(item.getContent(), item.getSceneid());
}
return aiDicTrie;
}
public static AiDicTrie getAiDicTrie(){
return aiDicTrie ;
}
/**
* AI配置
* @param orgi
* @return
*/
public static AiConfig initAiConfig(String orgi){
AiConfig aiConfig = null;
if(UKDataContext.getContext() != null && (aiConfig = (AiConfig) CacheHelper.getSystemCacheBean().getCacheObject(UKDataContext.SYSTEM_CACHE_AI_CONFIG, orgi)) == null){
AiConfigRepository aiConfigRepository = UKDataContext.getContext().getBean(AiConfigRepository.class) ;
aiConfig = aiConfigRepository.findByOrgi(orgi) ;
if(aiConfig == null){
aiConfig = new AiConfig() ;
}else{
CacheHelper.getSystemCacheBean().put(UKDataContext.SYSTEM_CACHE_AI_CONFIG,aiConfig, orgi) ;
}
}
return aiConfig ;
}
}
package com.ukefu.util.ai;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.lionsoul.jcseg.extractor.impl.TextRankKeywordsExtractor;
import org.lionsoul.jcseg.tokenizer.core.ADictionary;
import org.lionsoul.jcseg.tokenizer.core.DictionaryFactory;
import org.lionsoul.jcseg.tokenizer.core.ISegment;
import org.lionsoul.jcseg.tokenizer.core.IWord;
import org.lionsoul.jcseg.tokenizer.core.JcsegException;
import org.lionsoul.jcseg.tokenizer.core.JcsegTaskConfig;
import org.lionsoul.jcseg.tokenizer.core.SegmentFactory;
public class DicSegment {
//建立JcsegTaskConfig分詞配置執行個體,自動查找加載jcseg.properties配置項來初始化
private static JcsegTaskConfig config = new JcsegTaskConfig(true);
//建立預設單例詞庫實作,并且按照config配置加載詞庫
private static ADictionary dic = DictionaryFactory.createSingletonDictionary(config);
private synchronized static ISegment getSegment() throws JcsegException{
config.setAppendCJKSyn(false);
//追加拼音, 需要在jcseg.properties中配置jcseg.loadpinyin=1
config.setAppendCJKPinyin(false);
ISegment seg = SegmentFactory.createJcseg(
JcsegTaskConfig.COMPLEX_MODE,
new Object[]{config, dic}
);
return seg ;
}
//依據給定的ADictionary和JcsegTaskConfig來建立ISegment
//為了Api往後相容,建議使用SegmentFactory來建立ISegment對象
//Segment 非線程安全,且切分速度非常快
public synchronized static String[] segment(String content) throws IOException, JcsegException{
IWord word = null;
List<String> words = new ArrayList<String>();
ISegment seg = getSegment();
seg.reset(new StringReader(content));
while ( (word = seg.next()) != null ) {
if(word.getValue().length() == 1 && isChineseByBlock(word.getValue().charAt(0))){
continue ;
}else{
words.add(word.getValue()) ;
}
}
return words.toArray(new String[words.size()]);
}
public static String[] keyword(String content) throws IOException, JcsegException{
return keyword(content , 20);
}
public static String[] keyword(String content , int num) throws IOException, JcsegException{
ISegment seg = getSegment();
TextRankKeywordsExtractor extractor = new TextRankKeywordsExtractor(seg);
extractor.setMaxIterateNum(100); //設定pagerank算法最大疊代次數,非必須,使用預設即可
extractor.setWindowSize(5); //設定textRank計算視窗大小,非必須,使用預設即可
extractor.setKeywordsNum(10); //設定最大傳回的關鍵詞個數,預設為10
List<String> keywords = extractor.getKeywords(new StringReader(content));
return keywords.toArray(new String[keywords.size()]);
}
public static boolean isChineseByBlock(char c) {
Character.UnicodeScript sc = Character.UnicodeScript.of(c);
if (sc == Character.UnicodeScript.COMMON) {
return true;
} else {
return false;
}
}
public static void loadDic(String path) throws IOException{
File dicPath = new File(path) ;
if(!dicPath.exists()){
dicPath.mkdirs() ;
}
dic.loadDirectory(path);
}
public static void removeWord(String word){
int inx = ADictionary.getIndex(word) ;
dic.remove(inx, word);
}
}