Commit ba875f4c authored by alex yao's avatar alex yao

feat: 新增AI写作接口

parent 12c2db22
......@@ -8,7 +8,9 @@ public enum AiDialoguesTypeEnum {
PORTAL("portal", "门户页对话"),
LONG_TEXT_DOCUMENT("long_text_document", "长文本文档"),
LONG_TEXT_DOCUMENT("long_text_document", "长文本文档"),
AI_WRITING("ai_writing", "AI写作"),
;
......
......@@ -23,7 +23,6 @@ import cn.com.poc.long_document.domain.LongtextDialoguesResult;
import cn.com.poc.long_document.dto.LongTextExampleDto;
import cn.com.poc.long_document.dto.LongTextSummaryDto;
import cn.com.poc.long_document.entity.BizLongTextSummaryCacheEntity;
import cn.com.poc.long_document.model.BizLongTextDialoguesRecordModel;
import cn.com.poc.long_document.model.BizLongTextExampleModel;
import cn.com.poc.long_document.service.BizLongTextExampleService;
import cn.com.poc.long_document.service.BizLongTextSummaryCacheService;
......
package cn.com.poc.writing.aggregate;
/**
* @author alex.yao
* @date 2025/6/20
*/
public interface AiWritingService {
/**
* 调用AI写作
*
* @param dialoguesId
* @param fileUrl
* @param input
* @param knowledgeIds
* @param userId
*/
void call(String dialoguesId, String fileUrl, String input, Integer[] knowledgeIds, Long userId) throws Exception;
}
package cn.com.poc.writing.aggregate.impl;
import cn.com.poc.agent_application.entity.BizAgentApplicationDialoguesRecordEntity;
import cn.com.poc.agent_application.entity.BizAgentApplicationGcConfigEntity;
import cn.com.poc.agent_application.entity.KnowledgeContentResult;
import cn.com.poc.agent_application.service.BizAgentApplicationDialoguesRecordService;
import cn.com.poc.agent_application.service.BizAgentApplicationGcConfigService;
import cn.com.poc.ai_dialogues.constant.AiDialoguesTypeEnum;
import cn.com.poc.ai_dialogues.entity.BizAiDialoguesEntity;
import cn.com.poc.ai_dialogues.service.BizAiDialoguesService;
import cn.com.poc.common.constant.CommonConstant;
import cn.com.poc.common.service.BizFileUploadRecordService;
import cn.com.poc.common.utils.DocumentLoad;
import cn.com.poc.common.utils.JsonUtils;
import cn.com.poc.common.utils.SSEUtil;
import cn.com.poc.knowledge.aggregate.KnowledgeService;
import cn.com.poc.knowledge.constant.KnowledgeConstant;
import cn.com.poc.knowledge.entity.BizKnowledgeDocumentEntity;
import cn.com.poc.knowledge.query.KnowledgeDocumentRelationQueryItem;
import cn.com.poc.knowledge.service.BizKnowledgeDocumentService;
import cn.com.poc.long_document.domain.LongtextDialoguesResult;
import cn.com.poc.long_document.service.BizLongTextExampleService;
import cn.com.poc.thirdparty.resource.demand.ai.aggregate.DemandKnowledgeService;
import cn.com.poc.thirdparty.resource.demand.ai.constants.KnowledgeSearchTypeEnum;
import cn.com.poc.thirdparty.resource.demand.ai.constants.LLMRoleEnum;
import cn.com.poc.thirdparty.resource.demand.ai.entity.dialogue.Message;
import cn.com.poc.thirdparty.resource.demand.ai.entity.knowledge.SearchKnowledgeResult;
import cn.com.poc.thirdparty.resource.demand.ai.entity.largemodel.LargeModelDemandResult;
import cn.com.poc.thirdparty.resource.demand.ai.entity.largemodel.LargeModelResponse;
import cn.com.poc.thirdparty.service.LLMService;
import cn.com.poc.writing.aggregate.AiWritingService;
import cn.com.yict.framemax.core.exception.BusinessException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author alex.yao
* @date 2025/6/20
*/
@Service
public class AiWritingServiceImpl implements AiWritingService {
private final Logger logger = LoggerFactory.getLogger(AiWritingService.class);
@Resource
private LLMService llmService;
@Resource
private BizFileUploadRecordService bizFileUploadRecordService;
@Resource
private BizAiDialoguesService bizAiDialoguesService;
@Resource
private BizAgentApplicationDialoguesRecordService bizAgentApplicationDialoguesRecordService;
@Resource
private BizLongTextExampleService bizLongTextExampleService;
@Resource
private BizAgentApplicationGcConfigService bizAgentApplicationGcConfigService;
@Resource
private KnowledgeService knowledgeService;
@Resource
private BizKnowledgeDocumentService bizKnowledgeDocumentService;
@Resource
private DemandKnowledgeService demandKnowledgeService;
@Override
public void call(String dialoguesId, String fileUrl, String input, Integer[] knowledgeIds, Long userId) throws Exception {
BizAiDialoguesEntity bizAiDialoguesEntity = new BizAiDialoguesEntity();
bizAiDialoguesEntity.setDialoguesId(dialoguesId);
bizAiDialoguesEntity.setMemberId(userId);
bizAiDialoguesEntity.setDialoguesType(AiDialoguesTypeEnum.AI_WRITING.getType());
bizAiDialoguesEntity.setIsDeleted(CommonConstant.IsDeleted.N);
List<BizAiDialoguesEntity> bizAiDialoguesEntities = bizAiDialoguesService.findByExample(bizAiDialoguesEntity, null);
if (CollectionUtils.isEmpty(bizAiDialoguesEntities)) {
throw new BusinessException("对话ID不存在");
}
BizAiDialoguesEntity aiDialoguesEntity = bizAiDialoguesEntities.get(0);
//更新标题
if (StringUtils.isBlank(aiDialoguesEntity.getTitle())) { // 标题
aiDialoguesEntity.setTitle(input.substring(0, Math.min(input.length(), 150)));
aiDialoguesEntity = bizAiDialoguesService.update(aiDialoguesEntity);
}
BizAgentApplicationDialoguesRecordEntity dialoguesRecordEntity = new BizAgentApplicationDialoguesRecordEntity();
dialoguesRecordEntity.setDialogsId(dialoguesId);
dialoguesRecordEntity.setMemberId(userId);
dialoguesRecordEntity.setIsDeleted(CommonConstant.IsDeleted.N);
List<BizAgentApplicationDialoguesRecordEntity> dialoguesRecordEntities = bizAgentApplicationDialoguesRecordService.findByExample(dialoguesRecordEntity, null);
boolean isInit = false;
if (CollectionUtils.isEmpty(dialoguesRecordEntities)) {
if (StringUtils.isNoneBlank(aiDialoguesEntity.getFileUrl()) && StringUtils.isBlank(fileUrl)) {
fileUrl = aiDialoguesEntity.getFileUrl();
}
isInit = true;
}
// 文件是否变更
boolean isFileUrlChanged = (StringUtils.isNotBlank(fileUrl) && !fileUrl.equals(aiDialoguesEntity.getFileUrl()))
|| isInit;
if (StringUtils.isBlank(fileUrl)) {
fileUrl = aiDialoguesEntity.getFileUrl();
}
// 更新文件URL
if (isFileUrlChanged) {
if (!isInit && StringUtils.isNotBlank(fileUrl)) {
aiDialoguesEntity.setFileUrl(fileUrl);
bizAiDialoguesService.update(aiDialoguesEntity);
}
}
//记录提问时间
long inputTimestamp = System.currentTimeMillis();
SSEUtil sseUtil = new SSEUtil();
// 知识库
//获取知识库配置
List<Integer> kdIds = knowledgeService.getKdIdsByKnowledgeInfoIds(knowledgeIds);
List<KnowledgeContentResult> knowledgeResult = knowledge(kdIds, input, 5, 0.5, KnowledgeSearchTypeEnum.MIX);
if (CollectionUtils.isNotEmpty(knowledgeResult)) {
LargeModelDemandResult result = new LargeModelDemandResult();
result.setCode("0");
result.setFunction(null);
result.setDbChainResult(null);
result.setKnowledgeContentResult(knowledgeResult);
sseUtil.send(JsonUtils.serialize(result));
}
//组装请求参数
List<Message> messages = buildMessages(dialoguesId, userId, input, fileUrl, knowledgeResult);
LargeModelResponse largeModelResponse = new LargeModelResponse();
largeModelResponse.setModel("deepseek-chat");
largeModelResponse.setMessages(messages.toArray(new Message[0]));
largeModelResponse.setStream(true);
BufferedReader bufferedReader = llmService.chatChunk(largeModelResponse);
LongtextDialoguesResult longtextDialoguesResult = textOutputStream(sseUtil, bufferedReader);
// 保存用户输入记录
BizAgentApplicationDialoguesRecordEntity inputRecord = new BizAgentApplicationDialoguesRecordEntity();
inputRecord.setMemberId(userId);
inputRecord.setContent(input);
if (isFileUrlChanged) {
inputRecord.setFileUrl(fileUrl);
}
inputRecord.setDialogsId(dialoguesId);
inputRecord.setRole(LLMRoleEnum.USER.getRole());
inputRecord.setTimestamp(inputTimestamp);
// 保存AI回复记录
BizAgentApplicationDialoguesRecordEntity assistantRecord = new BizAgentApplicationDialoguesRecordEntity();
assistantRecord.setMemberId(userId);
assistantRecord.setContent(longtextDialoguesResult.getMessage());
assistantRecord.setReasoningContent(longtextDialoguesResult.getReasoningContent());
assistantRecord.setDialogsId(dialoguesId);
assistantRecord.setRole(LLMRoleEnum.ASSISTANT.getRole());
assistantRecord.setTimestamp(System.currentTimeMillis());
bizAgentApplicationDialoguesRecordService.save(inputRecord);
bizAgentApplicationDialoguesRecordService.save(assistantRecord);
}
private List<Message> buildMessages(String dialogsId, Long userId, String input, String fileUrl, List<KnowledgeContentResult> knowledgeContentResults) throws Exception {
// 获取对话提示词
String promptCode = "AiWritingPrompt";
BizAgentApplicationGcConfigEntity documentDialoguePrompt = bizAgentApplicationGcConfigService.getByConfigCode(promptCode);
if (documentDialoguePrompt == null || StringUtils.isBlank(documentDialoguePrompt.getConfigSystem())) {
logger.error("获取对话提示词失败 , configCode:{}", promptCode);
throw new BusinessException("获取对话提示词失败");
}
// 加载文件
String fileContent = StringUtils.EMPTY;
if (StringUtils.isNoneBlank(fileUrl)) {
File file = DocumentLoad.downloadURLDocument(fileUrl);
fileContent = DocumentLoad.documentToText(file);
}
// 加载对话提示词
String prompt = documentDialoguePrompt.getConfigSystem();
if (CollectionUtils.isNotEmpty(knowledgeContentResults)) {
String knowledgeContent = JsonUtils.serialize(knowledgeContentResults);
prompt = prompt.replace("${knowledgeContent}", knowledgeContent);
} else {
prompt = prompt.replace("${knowledgeContent}", StringUtils.EMPTY);
}
prompt = prompt.replace("${fileContent}", fileContent);
// 配置message
List<Message> messages = new ArrayList<>();
Message systemMessage = new Message();
systemMessage.setContent(prompt);
systemMessage.setRole(LLMRoleEnum.SYSTEM.getRole());
messages.add(systemMessage);
// 对话记录
BizAgentApplicationDialoguesRecordEntity recordEntity = new BizAgentApplicationDialoguesRecordEntity();
recordEntity.setDialogsId(dialogsId);
recordEntity.setIsDeleted(CommonConstant.IsDeleted.N);
recordEntity.setMemberId(userId);
List<BizAgentApplicationDialoguesRecordEntity> recordEntities = bizAgentApplicationDialoguesRecordService.findByExample(recordEntity, null);
if (CollectionUtils.isNotEmpty(recordEntities)) {
for (BizAgentApplicationDialoguesRecordEntity entity : recordEntities) {
Message message = new Message();
message.setContent(entity.getContent());
message.setRole(entity.getRole());
messages.add(message);
}
}
//判断最后是否为User,若是则删除。
if (CollectionUtils.isNotEmpty(messages)) {
if (LLMRoleEnum.USER.getRole().equals(messages.get(messages.size() - 1).getRole())) {
Long recordId = recordEntities.get(recordEntities.size() - 1).getId();
bizAgentApplicationDialoguesRecordService.deletedById(recordId);
messages.remove(messages.size() - 1);
}
}
// 用户输入
Message message = new Message();
message.setContent(input);
message.setRole(LLMRoleEnum.USER.getRole());
messages.add(message);
logger.info("--------- Build Messages dialogsId:{},messages:{}--------------", dialogsId, messages);
return messages;
}
/**
* 文本输出结果
*
* @param sseUtil
* @param bufferedReader
* @throws IOException
*/
private LongtextDialoguesResult textOutputStream(SSEUtil sseUtil, BufferedReader bufferedReader) throws IOException {
String res = "";
StringBuilder output = new StringBuilder();
StringBuilder reasoningContent = new StringBuilder();
try {
while ((res = bufferedReader.readLine()) != null) {
if (StringUtils.isEmpty(res)) {
continue;
}
res = StringUtils.replace(res, "data: ", StringUtils.EMPTY);
LargeModelDemandResult result = JsonUtils.deSerialize(res, LargeModelDemandResult.class);
if (!"0".equals(result.getCode())) {
logger.error("LLM Error,code:{}", result.getCode());
BusinessException ex = new BusinessException("exception/call.failure");
result.setMessage(ex.getMessage());
sseUtil.send(JsonUtils.serialize(result));
sseUtil.send("[DONE]");
sseUtil.complete();
throw ex;
}
result.setFunction(null);
result.setDbChainResult(null);
result.setKnowledgeContentResult(null);
sseUtil.send(JsonUtils.serialize(result));
if (StringUtils.isNotEmpty(result.getMessage())) {
output.append(result.getMessage());
}
if (StringUtils.isNotEmpty(result.getReasoningContent())) {
reasoningContent.append(result.getReasoningContent());
}
}
// 关闭资源
sseUtil.send("[DONE]");
sseUtil.complete();
LongtextDialoguesResult longtextDialoguesResult = new LongtextDialoguesResult();
longtextDialoguesResult.setMessage(output.toString());
longtextDialoguesResult.setReasoningContent(reasoningContent.toString());
return longtextDialoguesResult;
} catch (IOException e) {
logger.error("连接断开,code:{}", e.getMessage());
sseUtil.completeByError("连接断开");
throw new BusinessException("连接断开");
} finally {
bufferedReader.close();
}
}
private List<KnowledgeContentResult> knowledge(List<Integer> kdIds, String input, Integer topK, Double score, KnowledgeSearchTypeEnum searchTypeEnum) {
List<KnowledgeContentResult> knowledgeContentResults = new ArrayList<>();
if (CollectionUtils.isEmpty(kdIds)) {
return knowledgeContentResults;
}
List<String> knowledgeIds = new ArrayList<>();
for (Integer kdId : kdIds) {
BizKnowledgeDocumentEntity knowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
// 筛选训练完成的文档,否则跳过该文档
if (null == knowledgeDocumentEntity && KnowledgeConstant.TrainStatus.COMPLETE.equals(knowledgeDocumentEntity.getTrainStatus())) {
continue;
}
knowledgeIds.add(knowledgeDocumentEntity.getKnowledgeId());
}
SearchKnowledgeResult searchKnowledgeResult = demandKnowledgeService.searchKnowledge(input, knowledgeIds, topK, score, searchTypeEnum);
if (CollectionUtils.isNotEmpty(searchKnowledgeResult.getDocuments())) {
for (int i = 1; i <= searchKnowledgeResult.getDocuments().size(); i++) {
KnowledgeContentResult knowledgeContentResult = new KnowledgeContentResult();
knowledgeContentResult.setContent(searchKnowledgeResult.getDocuments().get(i - 1));
knowledgeContentResult.setKnowledgeId(searchKnowledgeResult.getKnowledgeIds().get(i - 1));
knowledgeContentResult.setScore(searchKnowledgeResult.getScore().get(i - 1));
knowledgeContentResults.add(knowledgeContentResult);
}
// 根据knowledgeId获取知识库名和文档名
knowledgeIds = knowledgeContentResults.stream().map(KnowledgeContentResult::getKnowledgeId).distinct().collect(Collectors.toList());
List<KnowledgeDocumentRelationQueryItem> knowledgeDocumentRelationQueryItems = bizKnowledgeDocumentService.knowledgeDocumentRelationQuery(knowledgeIds, null);
for (KnowledgeContentResult result : knowledgeContentResults) {
String knowledgeId = result.getKnowledgeId();
KnowledgeDocumentRelationQueryItem item = knowledgeDocumentRelationQueryItems.stream().filter(v -> v.getKnowledgeId().equals(knowledgeId)).findFirst().get();
result.setKnowledgeName(item.getKnowledgeName());
result.setKdId(item.getKdId());
result.setDocumentName(item.getDocumentName());
}
}
return knowledgeContentResults;
}
}
package cn.com.poc.writing.dto;
/**
* @author alex.yao
* @date 2025/6/20
*/
public class AiWritingDto {
/**
* 对话id
*/
private String dialoguesId;
/**
* 文件地址
*/
private String fileUrl;
/**
* 输入内容
*/
private String input;
/**
* 知识库id
*/
private Integer[] knowledgeIds;
public String getDialoguesId() {
return dialoguesId;
}
public void setDialoguesId(String dialoguesId) {
this.dialoguesId = dialoguesId;
}
public String getFileUrl() {
return fileUrl;
}
public void setFileUrl(String fileUrl) {
this.fileUrl = fileUrl;
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public Integer[] getKnowledgeIds() {
return knowledgeIds;
}
public void setKnowledgeIds(Integer[] knowledgeIds) {
this.knowledgeIds = knowledgeIds;
}
}
package cn.com.poc.writing.rest;
import cn.com.poc.writing.dto.AiWritingDto;
import cn.com.yict.framemax.core.rest.BaseRest;
import cn.com.yict.framemax.web.permission.Access;
import cn.com.yict.framemax.web.permission.Permission;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author alex.yao
* @date 2025/6/20
*/
@Permission(Access.Safety)
public interface AiWritingRest extends BaseRest {
void call(@RequestBody AiWritingDto aiWritingDto) throws Exception;
}
package cn.com.poc.writing.rest.impl;
import cn.com.poc.common.utils.Assert;
import cn.com.poc.common.utils.BlContext;
import cn.com.poc.support.security.oauth.entity.UserBaseEntity;
import cn.com.poc.writing.aggregate.AiWritingService;
import cn.com.poc.writing.dto.AiWritingDto;
import cn.com.poc.writing.rest.AiWritingRest;
import cn.com.yict.framemax.core.exception.BusinessException;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author alex.yao
* @date 2025/6/20
*/
@Component
public class AiWritingRestImpl implements AiWritingRest {
@Resource
private AiWritingService aiWritingService;
@Override
public void call(AiWritingDto aiWritingDto) throws Exception {
Assert.notNull(aiWritingDto, "aiWritingDto is null");
Assert.notBlank(aiWritingDto.getInput(), "input is null");
Assert.notBlank(aiWritingDto.getDialoguesId(), "dialogues id is null");
UserBaseEntity userBaseEntity = BlContext.getCurrentUserNotException();
if (userBaseEntity == null) {
throw new BusinessException("用户未登录");
}
aiWritingService.call(aiWritingDto.getDialoguesId(), aiWritingDto.getFileUrl(),
aiWritingDto.getInput(), aiWritingDto.getKnowledgeIds(), userBaseEntity.getUserId());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment