|
|
@@ -6,13 +6,17 @@ import cn.hutool.json.JSONUtil;
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
import com.alibaba.fastjson.JSONArray;
|
|
|
import com.fs.common.annotation.Excel;
|
|
|
-import com.fs.common.config.FSConfig;
|
|
|
import com.fs.common.core.domain.R;
|
|
|
import com.fs.common.core.redis.RedisCache;
|
|
|
+import com.fs.common.utils.PubFun;
|
|
|
+import com.fs.common.utils.ServletUtils;
|
|
|
+import com.fs.company.domain.Company;
|
|
|
import com.fs.company.domain.CompanyConfig;
|
|
|
import com.fs.company.domain.CompanyUser;
|
|
|
import com.fs.company.mapper.CompanyConfigMapper;
|
|
|
import com.fs.company.mapper.CompanyUserMapper;
|
|
|
+import com.fs.company.service.ICompanyService;
|
|
|
+import com.fs.company.service.ICompanyUserService;
|
|
|
import com.fs.config.ai.AiHostProper;
|
|
|
import com.fs.config.cloud.CloudHostProper;
|
|
|
import com.fs.course.domain.FsUserCourseVideo;
|
|
|
@@ -50,10 +54,20 @@ import com.fs.his.utils.ConfigUtil;
|
|
|
import com.fs.hisStore.enums.SysConfigEnum;
|
|
|
import com.fs.im.dto.OpenImMsgDTO;
|
|
|
import com.fs.im.vo.OpenImMsgCallBackVO;
|
|
|
+import com.fs.ipad.IpadSendUtils;
|
|
|
+import com.fs.ipad.param.WxSendAtMsgParam;
|
|
|
+import com.fs.ipad.vo.WxGetSessionRoomListVo;
|
|
|
+import com.fs.ipad.vo.WxRoomUserListVo;
|
|
|
import com.fs.qw.domain.*;
|
|
|
import com.fs.qw.mapper.*;
|
|
|
import com.fs.qw.param.QwAutoTagsRulesTags;
|
|
|
+import com.fs.qw.param.QwExternalContactAddTagParam;
|
|
|
+import com.fs.qw.param.QwExternalContactParam;
|
|
|
import com.fs.qw.service.*;
|
|
|
+import com.fs.qw.vo.QwExternalContactVO;
|
|
|
+import com.fs.qw.vo.QwTagGroupListVO;
|
|
|
+import com.fs.qw.vo.QwTagGroupVO;
|
|
|
+import com.fs.qw.vo.QwTagVO;
|
|
|
import com.fs.qwApi.config.OpenQwConfig;
|
|
|
import com.fs.qwApi.domain.QwResult;
|
|
|
import com.fs.qwApi.param.QwEditUserTagParam;
|
|
|
@@ -76,6 +90,7 @@ import org.jetbrains.annotations.Nullable;
|
|
|
import org.json.JSONObject;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
|
import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
@@ -94,6 +109,7 @@ import java.time.LocalTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.util.*;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.function.Function;
|
|
|
import java.util.regex.Matcher;
|
|
|
import java.util.regex.Pattern;
|
|
|
import java.util.stream.Collectors;
|
|
|
@@ -188,6 +204,22 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
@Autowired
|
|
|
private CloudHostProper cloudHostProper;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private com.fs.company.service.AiKnowledgeBaseService aiKnowledgeBaseService;
|
|
|
+
|
|
|
+ @Value("${ai.api.base-url:http://localhost:9009}")
|
|
|
+ private String aiApiBaseUrl;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ ICompanyService companyService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ IpadSendUtils ipadSendUtils;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ ICompanyUserService companyUserService;
|
|
|
+
|
|
|
+
|
|
|
private static final String AI_REPLY = "AI_REPLY:";
|
|
|
private static final String AI_REPLY_TAG = "AI_REPLY_TAG:";
|
|
|
|
|
|
@@ -490,6 +522,18 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
if(qwExternalContacts.getType() == 1){
|
|
|
FastGptChatSession fastGptChatSession= getFastGptSession(qwExternalContacts,user,dto);
|
|
|
if (qwContent.contains("验证请求") || qwContent.contains("联系人验证请求") || qwContent.contains("我已经添加了你")){
|
|
|
+ Calendar calendar1 = Calendar.getInstance();
|
|
|
+ //定时任务会处理10分钟以内的,所以设置20分钟
|
|
|
+ calendar1.add(Calendar.MINUTE, 60);
|
|
|
+ Date expireTime = calendar1.getTime();
|
|
|
+
|
|
|
+ FastGptChatSession chatSession = new FastGptChatSession();
|
|
|
+ chatSession.setLastTime(expireTime);
|
|
|
+ chatSession.setIsArtificial(1);
|
|
|
+ chatSession.setUserId(String.valueOf(sender));
|
|
|
+ chatSession.setSessionId(fastGptChatSession.getSessionId());
|
|
|
+
|
|
|
+ fastGptChatSessionMapper.updateFastGptChatSession(chatSession);
|
|
|
return R.ok();
|
|
|
}
|
|
|
if(type == 104||type == 101){
|
|
|
@@ -628,6 +672,12 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
}
|
|
|
Gson gson = new Gson();
|
|
|
contentKh = finalContentKh;*/
|
|
|
+
|
|
|
+ if(contentKh.contains("【转人工】")){
|
|
|
+ log.error("ai请求转人工:"+role.getRoleId()+":"+qwExternalContacts.getName());
|
|
|
+ notifyArtificial(fastGptChatSession.getSessionId(),user,qwExternalContacts.getName(),"ai请求转人工",qwExternalContacts.getId(),sender);
|
|
|
+ return R.ok();
|
|
|
+ }
|
|
|
Gson gson = new Gson();
|
|
|
FastGptChatConversation fastGptChatConversation = gson.fromJson(contentKh, FastGptChatConversation.class);
|
|
|
String content = fastGptChatConversation.getAiContent();
|
|
|
@@ -638,6 +688,15 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
int tokens = responseDatum.getTokens();
|
|
|
token+=tokens;
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 客户打标签
|
|
|
+ */
|
|
|
+ if(fastGptChatConversation.getTags() != null && !fastGptChatConversation.getTags().isEmpty()){
|
|
|
+ saveQwExtTags(qwExternalContacts,fastGptChatConversation,fastGptChatSession,user,tenantId);
|
|
|
+ }
|
|
|
+
|
|
|
//存聊天记录
|
|
|
addSaveAiMsg(2,2,contentKh,user,fastGptChatSession.getSessionId(),role.getRoleId(),qwExternalContacts,fastGptChatSession.getUserId(),result.getUsage().prompt_tokens,result.getUsage().completion_tokens,token);
|
|
|
if (!content.isEmpty()){
|
|
|
@@ -705,6 +764,41 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
return R.ok();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 租户给客户打标签
|
|
|
+ * @param qwExternalContacts
|
|
|
+ * @param fastGptChatConversation
|
|
|
+ * @param fastGptChatSession
|
|
|
+ * @param user
|
|
|
+ * @param tenantId
|
|
|
+ */
|
|
|
+ private void saveQwExtTags(QwExternalContact qwExternalContacts,FastGptChatConversation fastGptChatConversation,FastGptChatSession fastGptChatSession,QwUser user,Long tenantId) {
|
|
|
+ QwExternalContactAddTagParam Param = new QwExternalContactAddTagParam();
|
|
|
+
|
|
|
+ //添加需要打标签的客户
|
|
|
+ QwExternalContactParam param1 = new QwExternalContactParam();
|
|
|
+ param1.setCompanyId(user.getCompanyId());
|
|
|
+ List<Long> list = new ArrayList<>();
|
|
|
+ list.add(qwExternalContacts.getId());
|
|
|
+ Param.setUserIds(list);
|
|
|
+
|
|
|
+ try {
|
|
|
+ String[] split = fastGptChatConversation.getTags().split(",");
|
|
|
+ Param.setTagIds(Arrays.asList(split));
|
|
|
+ Param.setCorpId(user.getCorpId());
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.out.println("标签格式错误,会话id:" + fastGptChatSession.getSessionId());
|
|
|
+ }
|
|
|
+
|
|
|
+ String url = OpenQwConfig.baseApi + "/addTag?tenantId=" + tenantId;
|
|
|
+ String result = HttpUtil.createPost(url)
|
|
|
+ .body(JSON.toJSONString(Param))
|
|
|
+ .execute()
|
|
|
+ .body();
|
|
|
+
|
|
|
+ System.out.println(result);
|
|
|
+ }
|
|
|
+
|
|
|
private void sendImgMsg(String contentKh, Long sender, String uuid, Long serverId) {
|
|
|
com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(contentKh);
|
|
|
JSONArray imgUrls = jsonObject.getJSONArray("imgUrl");
|
|
|
@@ -1278,7 +1372,71 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
crmMsg.setCompanyId(user.getCompanyId());
|
|
|
crmMsg.setCompanyUserId(user.getCompanyUserId());
|
|
|
crmMsgService.insertCrmMsg(crmMsg);
|
|
|
+ try {
|
|
|
+ this.asyncAtMsg(user, "你的客户:" + chatSession.getNickName() + ", 因 \"" + reason + "\" 需要转人工,请及时回复");
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.out.println("转人工发送消息失败:"+e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
+ /**
|
|
|
+ * 异步发送掉线提醒
|
|
|
+ */
|
|
|
+ @Async
|
|
|
+ public void asyncAtMsg(QwUser qwUser, String msg) {
|
|
|
+ atMsg(qwUser, msg);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void atMsg(QwUser qwUser, String msg) {
|
|
|
+ try {
|
|
|
+ String corpId = qwUser.getCorpId();
|
|
|
+ log.info("掉线提醒通知:{}, {}, {}", qwUser.getId(), qwUser.getQwUserName(), corpId);
|
|
|
+ // 获取通知账号
|
|
|
+ QwUser user = qwUserMapper.selectOfflineUser();
|
|
|
+ if(user == null){
|
|
|
+ log.info("qwId:{}=====未找到通知账号", qwUser.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Company company = companyService.selectCompanyById(qwUser.getCompanyId());
|
|
|
+ log.info("查到主体:{}", qwUser);
|
|
|
+ List<WxGetSessionRoomListVo.RoomList> sessionRoomList = ipadSendUtils.getSessionRoomList(user.getUid(), user.getServerId());
|
|
|
+ Optional<WxGetSessionRoomListVo.RoomList> optional = sessionRoomList.stream().filter(e -> e.getNickname().equals(company.getGroupName()) || e.getNickname().equals(company.getCompanyName())).findFirst();
|
|
|
+ if(!optional.isPresent()){
|
|
|
+ log.warn("qwId:{}=====会话管理未找到群聊,corpId:{},群聊名称:{}, 查到群聊名称:{}", qwUser.getId(), corpId, company.getCompanyName(), PubFun.listToNewList(sessionRoomList, WxGetSessionRoomListVo.RoomList::getNickname));
|
|
|
+ log.info("qwId:{}=====会话管理未找到群聊,corpId:{},群聊名称:{}, 查到群聊名称:{}", qwUser.getId(), corpId, company.getCompanyName(), PubFun.listToNewList(sessionRoomList, WxGetSessionRoomListVo.RoomList::getNickname));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ WxGetSessionRoomListVo.RoomList room = optional.get();
|
|
|
+ log.info("找到会话群聊:{}", room);
|
|
|
+ CompanyUser companyUser = companyUserService.selectCompanyUserById(qwUser.getCompanyUserId());
|
|
|
+ log.info("企微账号:{}", JSON.toJSONString(companyUser));
|
|
|
+ List<WxRoomUserListVo.MemberList> memberLists = ipadSendUtils.getSessionRoomUserList(user.getUid(), room.getRoom_id(), user.getServerId());
|
|
|
+ Function<WxRoomUserListVo.MemberList, String> getName = e -> StringUtils.isEmpty(e.getRoom_nickname()) ? e.getNickname() : e.getRoom_nickname();
|
|
|
+ Optional<WxRoomUserListVo.MemberList> first = memberLists.stream().filter(e -> getName.apply(e).equals(companyUser.getUserName()) || getName.apply(e).equals(companyUser.getNickName())).findFirst();
|
|
|
+ String sendMsg = "企微账号:" + qwUser.getQwUserName() + " - " + msg;
|
|
|
+ if(!first.isPresent()){
|
|
|
+ WxWorkSendTextMsgDTO dto = new WxWorkSendTextMsgDTO();
|
|
|
+ dto.setUuid(user.getUid());
|
|
|
+ dto.setSend_userid(room.getRoom_id());
|
|
|
+ dto.setIsRoom(true);
|
|
|
+ dto.setContent(sendMsg);
|
|
|
+ ipadSendUtils.sendTxtNoVo(dto, user.getServerId());
|
|
|
+ }else{
|
|
|
+ WxRoomUserListVo.MemberList memberList = first.get();
|
|
|
+ log.info("找到掉线人:{}", memberList);
|
|
|
+ WxSendAtMsgParam param = new WxSendAtMsgParam();
|
|
|
+ param.setUuid(user.getUid());
|
|
|
+ param.setContent(sendMsg);
|
|
|
+ param.setSend_userid(room.getRoom_id());
|
|
|
+ param.setAtids(Collections.singletonList(memberList.getUin()));
|
|
|
+ param.setRoom(true);
|
|
|
+ log.info("发送数据组装:{}", param);
|
|
|
+ ipadSendUtils.sendTextAtMsg(param, user.getServerId());
|
|
|
+ }
|
|
|
+ }catch (Exception e){
|
|
|
+ log.warn("掉线提醒发送失败", e);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void sendQwAppMsg(String corpId, Integer agentId,String qwUserId,String content){
|
|
|
@@ -1578,7 +1736,7 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
}
|
|
|
|
|
|
//添加关键词
|
|
|
- addPromptWordNew(messageList,msgC,qwExternalContactsId,role,fastGptChatSession);
|
|
|
+ addPromptWordNew(messageList,msgC,qwExternalContactsId,role,fastGptChatSession,user);
|
|
|
R r = chatService.initiatingTakeChat(param, "http://129.28.170.206:3000/api/", appKey);
|
|
|
Object data1 = r.get("data");
|
|
|
if(!(data1 instanceof KnowledgeBaseResult)){
|
|
|
@@ -1598,6 +1756,30 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
}
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+ private void addQwUserTags(FastGptChatConversation conversation, QwUser user,FastGptRole role) {
|
|
|
+ String tagGroups = role.getTagGroups();
|
|
|
+ if (tagGroups==null){
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<QwTagGroupListVO> qwTagGroupListVO = qwTagGroupService.selectQwTagGroupByIds(tagGroups,user.getCorpId());
|
|
|
+
|
|
|
+ List<Map<String, Map<String,String>>> arrayList = new ArrayList<>();
|
|
|
+ Map<String, Map<String,String>> hashMap = new HashMap<>();
|
|
|
+ for (QwTagGroupListVO groupListVO : qwTagGroupListVO) {
|
|
|
+ Map<String,String> map = new HashMap<>();
|
|
|
+ List<QwTagVO> tag = groupListVO.getTag();
|
|
|
+ for (QwTagVO qwTagVO : tag) {
|
|
|
+ map.put(qwTagVO.getName(),qwTagVO.getTagId());
|
|
|
+ }
|
|
|
+ hashMap.put(groupListVO.getName(), map);
|
|
|
+ }
|
|
|
+
|
|
|
+ arrayList.add(hashMap);
|
|
|
+ conversation.setTagMapList(arrayList);
|
|
|
+ }
|
|
|
+
|
|
|
/** 增加课程信息 **/
|
|
|
private void addCourseWatchLog(Long id) {
|
|
|
FsCourseWatchLogVO log = fsCourseWatchLogMapper.selectFsCourseWatchLogByExtId(id);
|
|
|
@@ -1631,11 +1813,12 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
}
|
|
|
}
|
|
|
/** 组装发送AI内容 **/
|
|
|
- private void addPromptWordNew(List<ChatParam.Message> messageList,String count,Long extId,FastGptRole role,FastGptChatSession fastGptChatSession){
|
|
|
+ private void addPromptWordNew(List<ChatParam.Message> messageList,String count,Long extId,FastGptRole role,FastGptChatSession fastGptChatSession,QwUser user){
|
|
|
|
|
|
FastGptChatConversation conversation = new FastGptChatConversation();
|
|
|
conversation.setUserInfo(new com.alibaba.fastjson.JSONObject());
|
|
|
conversation.setHistory(new com.alibaba.fastjson.JSONArray());
|
|
|
+ List<Map<String, String>> knowledgeBase = new ArrayList<>();
|
|
|
|
|
|
if(role.getReminderWords() != null && !role.getReminderWords().isEmpty()){
|
|
|
conversation.setAiInfo(role.getReminderWords());
|
|
|
@@ -1669,10 +1852,14 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
|
|
|
|
|
|
List<FastGptChatMsg> msgs=fastGptChatMsgService.selectFastGptChatMsgByMsgSessionIdAndExtId(fastGptChatSession.getSessionId(),extId);
|
|
|
+
|
|
|
+ String contextQuery = count;
|
|
|
if (!msgs.isEmpty()){
|
|
|
com.alibaba.fastjson.JSONArray historyArray = new com.alibaba.fastjson.JSONArray();
|
|
|
Collections.reverse(msgs);
|
|
|
msgs.remove(msgs.size() - 1);
|
|
|
+ StringBuilder contextBuilder = new StringBuilder();
|
|
|
+ int historyCount = 0;
|
|
|
for (FastGptChatMsg msg : msgs) {
|
|
|
Integer sendType = msg.getSendType();
|
|
|
String content = msg.getContent();
|
|
|
@@ -1685,14 +1872,103 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
msgObj.put("role", sendType==1?"user":"ai");
|
|
|
msgObj.put("content", content);
|
|
|
historyArray.add(msgObj);
|
|
|
+ if (content != null && !content.trim().isEmpty() && historyCount < 6) {
|
|
|
+ if (sendType == 1) {
|
|
|
+ contextBuilder.insert(0, "用户:" + content + "\n");
|
|
|
+ } else {
|
|
|
+ contextBuilder.insert(0, "AI:" + content + "\n");
|
|
|
+ }
|
|
|
+ historyCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (contextBuilder.length() > 0 && count != null && !count.trim().isEmpty()) {
|
|
|
+ contextQuery = contextBuilder.toString().trim() + "\n用户:" + count;
|
|
|
}
|
|
|
conversation.setHistory(historyArray);
|
|
|
}
|
|
|
|
|
|
+ //从向量知识库中检索相关内容
|
|
|
+ if (count != null && !count.trim().isEmpty()) {
|
|
|
+ String searchQuery = contextQuery != null ? contextQuery : count;
|
|
|
+ log.info("知识库检索查询文本 | original={} | contextQuery={}", count, searchQuery);
|
|
|
+ try {
|
|
|
+ com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<com.fs.company.domain.AiKnowledgeBase> lqw =
|
|
|
+ new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
|
|
|
+ lqw.eq(com.fs.company.domain.AiKnowledgeBase::getDelFlag, 0);
|
|
|
+ List<com.fs.company.domain.AiKnowledgeBase> kbList = aiKnowledgeBaseService.list(lqw);
|
|
|
+ if (kbList == null || kbList.isEmpty()) {
|
|
|
+ log.warn("向量知识库检索跳过: 当前租户下无知识库 | roleId={}", role.getRoleId());
|
|
|
+ } else {
|
|
|
+ Set<Long> addedIds = new HashSet<>();
|
|
|
+ for (com.fs.company.domain.AiKnowledgeBase kb : kbList) {
|
|
|
+ String collectionName = kb.getCollectionName();
|
|
|
+ if (collectionName == null || collectionName.trim().isEmpty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第一路:向量语义搜索(使用上下文查询)
|
|
|
+ List<Float> vector = createEmbedding(searchQuery);
|
|
|
+ if (vector != null && !vector.isEmpty()) {
|
|
|
+ List<Map<String, Object>> searchResults = searchQdrant(collectionName, vector, 3, 0.3);
|
|
|
+ if (searchResults != null) {
|
|
|
+ for (Map<String, Object> item : searchResults) {
|
|
|
+ Long pointId = extractPointId(item);
|
|
|
+ if (pointId != null && addedIds.contains(pointId)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (pointId != null) {
|
|
|
+ addedIds.add(pointId);
|
|
|
+ }
|
|
|
+ Map<String, String> kbItem = extractPayloadItem(item);
|
|
|
+ if (!kbItem.isEmpty()) {
|
|
|
+ knowledgeBase.add(kbItem);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第二路:Payload关键词过滤搜索(从上下文和当前消息中提取关键词)
|
|
|
+ List<String> keywords = extractKeywords(searchQuery);
|
|
|
+ List<String> currentKeywords = extractKeywords(count);
|
|
|
+ Set<String> allKeywords = new LinkedHashSet<>(currentKeywords);
|
|
|
+ allKeywords.addAll(keywords);
|
|
|
+ if (!allKeywords.isEmpty()) {
|
|
|
+ for (String keyword : allKeywords) {
|
|
|
+ List<Map<String, Object>> filterResults = searchQdrantByPayload(collectionName, keyword, 10);
|
|
|
+ if (filterResults != null) {
|
|
|
+ for (Map<String, Object> item : filterResults) {
|
|
|
+ Long pointId = extractPointId(item);
|
|
|
+ if (pointId != null && addedIds.contains(pointId)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (pointId != null) {
|
|
|
+ addedIds.add(pointId);
|
|
|
+ }
|
|
|
+ Map<String, String> kbItem = extractPayloadItem(item);
|
|
|
+ if (!kbItem.isEmpty()) {
|
|
|
+ knowledgeBase.add(kbItem);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("向量知识库检索失败 | roleId={} | content={}", role.getRoleId(), count, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("向量知识库检索完成 | knowledgeBaseSize={} | roleId={}", knowledgeBase.size(), role.getRoleId());
|
|
|
+ conversation.setKnowledgeBase(knowledgeBase);
|
|
|
+
|
|
|
if (count!=null&& !count.isEmpty()){
|
|
|
conversation.setUserContent(count);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 添加企微标签
|
|
|
+ */
|
|
|
+ addQwUserTags(conversation,user,role);
|
|
|
|
|
|
ChatParam.Message message1=new ChatParam.Message();
|
|
|
message1.setRole("user");
|
|
|
@@ -1701,6 +1977,178 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
message1.setContent(jsonStr);
|
|
|
messageList.add(message1);
|
|
|
}
|
|
|
+
|
|
|
+ private Long extractPointId(Map<String, Object> item) {
|
|
|
+ Object id = item.get("id");
|
|
|
+ if (id instanceof Number) {
|
|
|
+ return ((Number) id).longValue();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, String> extractPayloadItem(Map<String, Object> item) {
|
|
|
+ Map<String, String> kbItem = new HashMap<>();
|
|
|
+ Object payloadObj = item.get("payload");
|
|
|
+ if (payloadObj instanceof Map) {
|
|
|
+ Map<?, ?> payload = (Map<?, ?>) payloadObj;
|
|
|
+ Object qObj = payload.get("q");
|
|
|
+ Object aObj = payload.get("a");
|
|
|
+ if (qObj != null) {
|
|
|
+ kbItem.put("q", qObj.toString());
|
|
|
+ }
|
|
|
+ if (aObj != null) {
|
|
|
+ kbItem.put("a", aObj.toString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return kbItem;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<String> extractKeywords(String text) {
|
|
|
+ List<String> keywords = new ArrayList<>();
|
|
|
+ if (text == null || text.trim().isEmpty()) {
|
|
|
+ return keywords;
|
|
|
+ }
|
|
|
+ java.util.regex.Matcher durationMatcher = java.util.regex.Pattern.compile("\\d+\\s*[天日周月年]").matcher(text);
|
|
|
+ while (durationMatcher.find()) {
|
|
|
+ keywords.add(durationMatcher.group().replaceAll("\\s+", ""));
|
|
|
+ }
|
|
|
+ String[] productWords = {"套餐", "方案", "服务", "产品", "价格", "费用", "优惠", "活动", "会员", "课程", "项目"};
|
|
|
+ for (String word : productWords) {
|
|
|
+ if (text.contains(word)) {
|
|
|
+ keywords.add(word);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return keywords;
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private List<Map<String, Object>> searchQdrantByPayload(String collectionName, String keyword, int limit) {
|
|
|
+ try {
|
|
|
+ com.alibaba.fastjson.JSONObject req = new com.alibaba.fastjson.JSONObject();
|
|
|
+ req.put("collectionName", collectionName);
|
|
|
+ req.put("vector", Collections.nCopies(1024, 0.0f));
|
|
|
+ req.put("topK", limit);
|
|
|
+ req.put("scoreThreshold", 0.0);
|
|
|
+
|
|
|
+ com.alibaba.fastjson.JSONObject filter = new com.alibaba.fastjson.JSONObject();
|
|
|
+ com.alibaba.fastjson.JSONArray should = new com.alibaba.fastjson.JSONArray();
|
|
|
+ com.alibaba.fastjson.JSONObject qMatch = new com.alibaba.fastjson.JSONObject();
|
|
|
+ qMatch.put("key", "q");
|
|
|
+ com.alibaba.fastjson.JSONObject qMatchValue = new com.alibaba.fastjson.JSONObject();
|
|
|
+ qMatchValue.put("value", keyword);
|
|
|
+ qMatch.put("match", qMatchValue);
|
|
|
+ should.add(qMatch);
|
|
|
+ com.alibaba.fastjson.JSONObject aMatch = new com.alibaba.fastjson.JSONObject();
|
|
|
+ aMatch.put("key", "a");
|
|
|
+ com.alibaba.fastjson.JSONObject aMatchValue = new com.alibaba.fastjson.JSONObject();
|
|
|
+ aMatchValue.put("value", keyword);
|
|
|
+ aMatch.put("match", aMatchValue);
|
|
|
+ should.add(aMatch);
|
|
|
+ filter.put("should", should);
|
|
|
+ req.put("filter", filter);
|
|
|
+
|
|
|
+ String url = aiApiBaseUrl + "/qdrant/point/search";
|
|
|
+ String result = HttpUtil.post(url, req.toJSONString());
|
|
|
+ com.alibaba.fastjson.JSONObject resp = com.alibaba.fastjson.JSONObject.parseObject(result);
|
|
|
+ Integer code = resp.getInteger("code");
|
|
|
+ if (code == null || code != 200) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Object dataObj = resp.get("data");
|
|
|
+ if (dataObj instanceof List) {
|
|
|
+ List<Map<String, Object>> results = new ArrayList<>();
|
|
|
+ for (Object item : (List<?>) dataObj) {
|
|
|
+ if (item instanceof Map) {
|
|
|
+ results.add((Map<String, Object>) item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return results;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("Payload关键词搜索失败 | collectionName={} | keyword={}", collectionName, keyword, e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private List<Float> createEmbedding(String text) {
|
|
|
+ try {
|
|
|
+ com.alibaba.fastjson.JSONObject req = new com.alibaba.fastjson.JSONObject();
|
|
|
+ req.put("text", text);
|
|
|
+ String url = aiApiBaseUrl + "/ai/embedding/create";
|
|
|
+ log.info("请求Embedding API | url={} | textLength={}", url, text.length());
|
|
|
+ String result = HttpUtil.post(url, req.toJSONString());
|
|
|
+ log.info("Embedding API响应 | respLength={}", result != null ? result.length() : 0);
|
|
|
+ com.alibaba.fastjson.JSONObject resp = com.alibaba.fastjson.JSONObject.parseObject(result);
|
|
|
+ Integer code = resp.getInteger("code");
|
|
|
+ if (code == null || code != 200) {
|
|
|
+ log.error("Embedding API返回错误 | code={} | msg={} | resp={}", code, resp.getString("msg"), result);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ com.alibaba.fastjson.JSONArray embeddingArray = resp.getJSONArray("data");
|
|
|
+ if (embeddingArray == null || embeddingArray.isEmpty()) {
|
|
|
+ log.error("Embedding API返回data为空 | resp={}", result);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ List<Float> vector = new ArrayList<>();
|
|
|
+ for (Object item : embeddingArray) {
|
|
|
+ if (item instanceof Number) {
|
|
|
+ vector.add(((Number) item).floatValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("Embedding向量解析成功 | vectorSize={}", vector.size());
|
|
|
+ return vector;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("生成Embedding向量失败 | text={}", text, e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private List<Map<String, Object>> searchQdrant(String collectionName, List<Float> vector, int topK, double scoreThreshold) {
|
|
|
+ try {
|
|
|
+ com.alibaba.fastjson.JSONObject req = new com.alibaba.fastjson.JSONObject();
|
|
|
+ req.put("collectionName", collectionName);
|
|
|
+ req.put("vector", vector);
|
|
|
+ req.put("topK", topK);
|
|
|
+ req.put("scoreThreshold", scoreThreshold);
|
|
|
+ String url = aiApiBaseUrl + "/qdrant/point/search";
|
|
|
+ log.info("请求Qdrant搜索 | url={} | collectionName={} | topK={} | scoreThreshold={}", url, collectionName, topK, scoreThreshold);
|
|
|
+ String result = HttpUtil.post(url, req.toJSONString());
|
|
|
+ log.info("Qdrant搜索响应 | collectionName={} | respLength={}", collectionName, result != null ? result.length() : 0);
|
|
|
+ com.alibaba.fastjson.JSONObject resp = com.alibaba.fastjson.JSONObject.parseObject(result);
|
|
|
+ Integer code = resp.getInteger("code");
|
|
|
+ if (code == null || code != 200) {
|
|
|
+ log.error("Qdrant搜索API返回错误 | code={} | msg={} | collectionName={} | resp={}", code, resp.getString("msg"), collectionName, result);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Object dataObj = resp.get("data");
|
|
|
+ if (dataObj == null) {
|
|
|
+ log.error("Qdrant搜索API返回data为null | collectionName={} | resp={}", collectionName, result);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (dataObj instanceof List) {
|
|
|
+ List<Map<String, Object>> results = new ArrayList<>();
|
|
|
+ for (Object item : (List<?>) dataObj) {
|
|
|
+ if (item instanceof Map) {
|
|
|
+ Map<String, Object> resultMap = (Map<String, Object>) item;
|
|
|
+ Object score = resultMap.get("score");
|
|
|
+ log.info("Qdrant搜索结果 | collectionName={} | score={} | id={}", collectionName, score, resultMap.get("id"));
|
|
|
+ results.add(resultMap);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("Qdrant搜索完成 | collectionName={} | resultCount={}", collectionName, results.size());
|
|
|
+ return results;
|
|
|
+ }
|
|
|
+ log.error("Qdrant搜索API返回data类型异常 | collectionName={} | dataType={}", collectionName, dataObj.getClass().getName());
|
|
|
+ return null;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("Qdrant搜索失败 | collectionName={}", collectionName, e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/** 组装发送AI内容 **/
|
|
|
private void addPromptWord(List<ChatParam.Message> messageList,String count,Long extId,String words,String countInfo,Long sessionId){
|
|
|
|
|
|
@@ -2224,7 +2672,7 @@ public class AiHookServiceImpl implements AiHookService {
|
|
|
if (oneDayAgo.getTime().after(fastGptChatSession.getLastTime())) {
|
|
|
Calendar calendar1 = Calendar.getInstance();
|
|
|
//定时任务会处理10分钟以内的,所以设置20分钟
|
|
|
- calendar1.add(Calendar.MINUTE, 20);
|
|
|
+ calendar1.add(Calendar.MINUTE, 30);
|
|
|
Date expireTime = calendar1.getTime();
|
|
|
|
|
|
FastGptChatSession chatSession = new FastGptChatSession();
|