1.调整ai话术生成逻辑

master
gitee 1 week ago
parent 760c69e3a0
commit e07684fbb4

@ -0,0 +1,20 @@
package com.supervision.livedigitalavatarmanage.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class ThreadPoolConfig {
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(1000);
executor.setThreadNamePrefix("Async-Task-");
executor.initialize();
return executor;
}
}

@ -1,5 +1,12 @@
package com.supervision.livedigitalavatarmanage.constant; package com.supervision.livedigitalavatarmanage.constant;
import cn.hutool.core.util.StrUtil;
import com.supervision.livedigitalavatarmanage.dto.ScriptKeyPointsDTO;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/** /**
* *
*/ */
@ -27,4 +34,35 @@ public class PromptTemplate {
3. {num}**** 3. {num}****
4. jsonArray["话术1","话术2"]/no_think 4. jsonArray["话术1","话术2"]/no_think
"""; """;
public static final String GENERATE_SALESPITCH_TEMPLATE_V2 = """
`{productCategory}``{productCategory}````{scriptType}`
`{productCategory}`
```text
{productInfo}
```
****
- **** `{productCategory}`
- **** 使````使{keyPointTitles1}
- **** **仿**
- **** **{keyPointTitles2}**
``
```text
{keyPointExamples}
```
****
1. **** `{productCategory}```{keyPointTitles3}
2. **仿** ``仿``
3. **** {keyPointTitles4}`{scriptType}`****
****
""";
} }

@ -0,0 +1,20 @@
package com.supervision.livedigitalavatarmanage.constant;
import lombok.Getter;
@Getter
public enum TaskStautsEnum {
WAITING(0, "等待中"),
RUNNING(1, "进行中"),
SUCCESS(2, "成功"),
FAILED(3, "失败");
private final int code;
private final String description;
TaskStautsEnum(int code, String description) {
this.code = code;
this.description = description;
}
}

@ -1,8 +1,10 @@
package com.supervision.livedigitalavatarmanage.controller; package com.supervision.livedigitalavatarmanage.controller;
import com.supervision.livedigitalavatarmanage.dto.AsyncTaskResultDTO;
import com.supervision.livedigitalavatarmanage.dto.R; import com.supervision.livedigitalavatarmanage.dto.R;
import com.supervision.livedigitalavatarmanage.service.LiveDigitalService; import com.supervision.livedigitalavatarmanage.service.LiveDigitalService;
import com.supervision.livedigitalavatarmanage.vo.SalesPitchReqVo; import com.supervision.livedigitalavatarmanage.vo.SalesPitchReqVo;
import com.supervision.livedigitalavatarmanage.vo.SalesPitchResVo;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@ -23,9 +25,34 @@ public class LiveDigitalController {
* @return * @return
*/ */
@PostMapping("/generate/salespitch") @PostMapping("/generate/salespitch")
public R<List<String>> generateSalesPitch(@RequestBody SalesPitchReqVo salespitchReqVo) { public R<List<SalesPitchResVo>> generateSalesPitch(@RequestBody SalesPitchReqVo salespitchReqVo) {
List<String> salesPitchResVos = liveDigitalService.generateSalesPitch(salespitchReqVo); List<SalesPitchResVo> salesPitchResVos = liveDigitalService.generateSalesPitch(salespitchReqVo);
return R.ok(salesPitchResVos); return R.ok(salesPitchResVos);
} }
/**
*
* @param salespitchReqVo
* @return
*/
@PostMapping("/submit/salespitch/task")
public R<String> submitSalesPitchTask(@RequestBody SalesPitchReqVo salespitchReqVo) {
String taskId = liveDigitalService.submitSalesPitchTask(salespitchReqVo);
return R.ok(taskId);
}
/**
*
* @param taskId
* @return
*/
@PostMapping("/query/salespitch/task")
public R<AsyncTaskResultDTO<List<SalesPitchResVo>>> querySalespitchTaskResult(String taskId) {
AsyncTaskResultDTO<List<SalesPitchResVo>> result = liveDigitalService.querySalespitchTaskResult(taskId);
return R.ok(result);
}
} }

@ -0,0 +1,67 @@
package com.supervision.livedigitalavatarmanage.domain;
import com.baomidou.mybatisplus.annotation.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Data;
/**
*
* @TableName async_task_result
*/
@TableName(value ="async_task_result")
@Data
public class AsyncTaskResult implements Serializable {
/**
* ID
*/
@TableId
private String id;
/**
* 0:
*/
private String taskType;
/**
* 0-1-2-
*/
private Integer status;
/**
*
*/
private String requestData;
/**
*
*/
private String resultData;
/**
*
*/
private String errorMessage;
/**
*
*/
private LocalDateTime expireTime;
/**
*
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
*
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

@ -0,0 +1,66 @@
package com.supervision.livedigitalavatarmanage.domain;
import com.baomidou.mybatisplus.annotation.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Data;
/**
*
* @TableName script_keypoints
*/
@TableName(value ="script_keypoints")
@Data
public class ScriptKeypoints implements Serializable {
/**
*
*/
@TableId
private String id;
/**
*
*/
private String scriptType;
/**
* id
*/
private String scriptId;
/**
*
*/
private String keyPoint;
/**
*
*/
private String keyPointExample;
/**
*
*/
private String scriptTypeOrderNum;
/**
*
*/
private Integer keyPointOrderNum;
/**
*
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
*
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

@ -0,0 +1,51 @@
package com.supervision.livedigitalavatarmanage.domain;
import com.baomidou.mybatisplus.annotation.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Data;
/**
*
* @TableName script_user
*/
@TableName(value ="script_user")
@Data
public class ScriptUser implements Serializable {
/**
*
*/
@TableId
private String id;
/**
*
*/
private String scriptName;
/**
*
*/
private String productCategory;
/**
* id
*/
private String userId;
/**
*
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
*
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

@ -0,0 +1,61 @@
package com.supervision.livedigitalavatarmanage.dto;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.supervision.livedigitalavatarmanage.constant.TaskStautsEnum;
import com.supervision.livedigitalavatarmanage.domain.AsyncTaskResult;
import lombok.Data;
@Data
public class AsyncTaskResultDTO<T> {
private static final ObjectMapper objectMapper = new ObjectMapper();
private String id;
/**
* 0:
*/
private String taskType;
/**
* 0-1-2-
*/
private Integer status;
/**
*
*/
private String requestData;
/**
*
*/
private T resultData;
/**
*
*/
private String errorMessage;
public AsyncTaskResultDTO() {
}
public AsyncTaskResultDTO(AsyncTaskResult asyncTaskResult, TypeReference<T> typeReference) {
this.id = asyncTaskResult.getId();
this.taskType = asyncTaskResult.getTaskType();
this.status = asyncTaskResult.getStatus();
this.requestData = asyncTaskResult.getRequestData();
this.errorMessage = asyncTaskResult.getErrorMessage();
if (TaskStautsEnum.SUCCESS.getCode() == this.status && StrUtil.isNotEmpty(asyncTaskResult.getResultData())) {
try {
this.resultData = objectMapper.readValue(asyncTaskResult.getResultData(), typeReference);
} catch (JsonProcessingException e) {
this.status = TaskStautsEnum.FAILED.getCode();
}
}
}
}

@ -0,0 +1,70 @@
package com.supervision.livedigitalavatarmanage.dto;
import lombok.Data;
/**
* DTO
*/
@Data
public class ScriptKeyPointsDTO {
/**
* id
*/
private String scriptGroupId;
/**
*
*/
private String scriptName;
/**
*
*/
private String productCategory;
/**
* id
*/
private String userId;
/**
*
*/
private String scriptType;
/**
* id
*/
private String scriptId;
/**
*
*/
private String keyPoint;
/**
*
*/
private String keyPointExample;
/**
*
*/
private Integer scriptTypeOrderNum;
/**
*
*/
private Integer keyPointOrderNum;
public ScriptKeyPointsDTO() {
}
public ScriptKeyPointsDTO(String scriptType, String keyPoint, String keyPointExample) {
this.scriptType = scriptType;
this.keyPoint = keyPoint;
this.keyPointExample = keyPointExample;
}
}

@ -0,0 +1,18 @@
package com.supervision.livedigitalavatarmanage.mapper;
import com.supervision.livedigitalavatarmanage.domain.AsyncTaskResult;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author Administrator
* @description async_task_result()Mapper
* @createDate 2025-09-17 15:35:10
* @Entity com.supervision.livedigitalavatarmanage.domain.AsyncTaskResult
*/
public interface AsyncTaskResultMapper extends BaseMapper<AsyncTaskResult> {
}

@ -0,0 +1,23 @@
package com.supervision.livedigitalavatarmanage.mapper;
import com.supervision.livedigitalavatarmanage.domain.ScriptKeypoints;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.supervision.livedigitalavatarmanage.dto.ScriptKeyPointsDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author Administrator
* @description script_keypoints()Mapper
* @createDate 2025-09-17 10:57:12
* @Entity com.supervision.livedigitalavatarmanage.domain.ScriptKeypoints
*/
public interface ScriptKeypointsMapper extends BaseMapper<ScriptKeypoints> {
List<ScriptKeyPointsDTO> listByUserIdAndProductCategory(@Param("userId") String userId,@Param("productCategory") String productCategory);
}

@ -0,0 +1,18 @@
package com.supervision.livedigitalavatarmanage.mapper;
import com.supervision.livedigitalavatarmanage.domain.ScriptUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author Administrator
* @description script_user()Mapper
* @createDate 2025-09-17 10:57:12
* @Entity com.supervision.livedigitalavatarmanage.domain.ScriptUser
*/
public interface ScriptUserMapper extends BaseMapper<ScriptUser> {
}

@ -0,0 +1,19 @@
package com.supervision.livedigitalavatarmanage.service;
import com.supervision.livedigitalavatarmanage.domain.AsyncTaskResult;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.concurrent.Callable;
/**
* @author Administrator
* @description async_task_result()Service
* @createDate 2025-09-17 15:35:10
*/
public interface AsyncTaskResultService extends IService<AsyncTaskResult> {
<T> String submitAsyncTask(String taskType, Object requestData, Callable<T> callable);
int cleanExpiredTasks();
}

@ -0,0 +1,19 @@
package com.supervision.livedigitalavatarmanage.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
@Slf4j
@RequiredArgsConstructor
public class ExpiredTaskCleaner {
private final AsyncTaskResultService asyncTaskResultService;
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
public void cleanExpiredTasks() {
int count = asyncTaskResultService.cleanExpiredTasks();
log.info("清理了 {} 条过期任务记录", count);
}
}

@ -1,10 +1,14 @@
package com.supervision.livedigitalavatarmanage.service; package com.supervision.livedigitalavatarmanage.service;
import com.supervision.livedigitalavatarmanage.dto.AsyncTaskResultDTO;
import com.supervision.livedigitalavatarmanage.vo.SalesPitchReqVo; import com.supervision.livedigitalavatarmanage.vo.SalesPitchReqVo;
import com.supervision.livedigitalavatarmanage.vo.SalesPitchResVo;
import java.util.List; import java.util.List;
public interface LiveDigitalService { public interface LiveDigitalService {
List<String> generateSalesPitch(SalesPitchReqVo salespitchReqVo); List<SalesPitchResVo> generateSalesPitch(SalesPitchReqVo salespitchReqVo);
String submitSalesPitchTask(SalesPitchReqVo salespitchReqVo);
AsyncTaskResultDTO<List<SalesPitchResVo>> querySalespitchTaskResult(String taskId);
} }

@ -8,7 +8,7 @@ public interface OllamaAgentService {
/** /**
* *
* @param agentChatReqDTO agentChatReqDTO * @param ollamaDTO agentChatReqDTO
* @return * @return
*/ */
Flux<OllamResponse> streamChat(OllamaDTO ollamaDTO); Flux<OllamResponse> streamChat(OllamaDTO ollamaDTO);

@ -0,0 +1,23 @@
package com.supervision.livedigitalavatarmanage.service;
import com.supervision.livedigitalavatarmanage.domain.ScriptKeypoints;
import com.baomidou.mybatisplus.extension.service.IService;
import com.supervision.livedigitalavatarmanage.dto.ScriptKeyPointsDTO;
import java.util.List;
/**
* @author Administrator
* @description script_keypoints()Service
* @createDate 2025-09-17 10:57:12
*/
public interface ScriptKeypointsService extends IService<ScriptKeypoints> {
/**
* ID
* @param userId ID
* @param productCategory
* @return
*/
List<ScriptKeyPointsDTO> listByUserIdAndProductCategory(String userId, String productCategory);
}

@ -0,0 +1,13 @@
package com.supervision.livedigitalavatarmanage.service;
import com.supervision.livedigitalavatarmanage.domain.ScriptUser;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author Administrator
* @description script_user()Service
* @createDate 2025-09-17 10:57:12
*/
public interface ScriptUserService extends IService<ScriptUser> {
}

@ -0,0 +1,99 @@
package com.supervision.livedigitalavatarmanage.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.supervision.livedigitalavatarmanage.constant.TaskStautsEnum;
import com.supervision.livedigitalavatarmanage.domain.AsyncTaskResult;
import com.supervision.livedigitalavatarmanage.service.AsyncTaskResultService;
import com.supervision.livedigitalavatarmanage.mapper.AsyncTaskResultMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.Callable;
/**
* @author Administrator
* @description async_task_result()Service
* @createDate 2025-09-17 15:35:10
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AsyncTaskResultServiceImpl extends ServiceImpl<AsyncTaskResultMapper, AsyncTaskResult>
implements AsyncTaskResultService{
@Autowired
@Qualifier("taskExecutor")
private ThreadPoolTaskExecutor taskExecutor;
@Override
public <T> String submitAsyncTask(String taskType, Object requestData, Callable<T> callable) {
String taskId = UUID.fastUUID().toString();
AsyncTaskResult result = new AsyncTaskResult();
result.setId(taskId);
result.setTaskType(taskType);
result.setStatus(TaskStautsEnum.RUNNING.getCode());
result.setRequestData(JSONUtil.toJsonStr(requestData));
result.setExpireTime(DateUtil.offset(DateUtil.date(), DateField.DAY_OF_YEAR,7).toLocalDateTime()); // 7天后过期
super.save(result);
// 异步执行任务
taskExecutor.execute(() -> executeTask(taskId, callable));
return taskId;
}
/**
*
*/
private <T> void executeTask(String taskId, Callable<T> callable) {
try {
// 执行您的耗时操作
T result = callable.call();
// 更新任务结果
super.lambdaUpdate().eq(AsyncTaskResult::getId, taskId)
.set(AsyncTaskResult::getStatus, TaskStautsEnum.SUCCESS.getCode())
.set(AsyncTaskResult::getResultData, JSONUtil.toJsonStr(result))
.update();
log.info("异步任务执行成功任务ID: {}", taskId);
} catch (Exception e) {
log.error("异步任务执行失败任务ID: {}", taskId, e);
super.lambdaUpdate().eq(AsyncTaskResult::getId, taskId)
.set(AsyncTaskResult::getStatus, TaskStautsEnum.SUCCESS.getCode())
.set(AsyncTaskResult::getErrorMessage, e.getMessage())
.update();
}
}
@Override
public int cleanExpiredTasks() {
List<AsyncTaskResult> list = super.lambdaQuery()
.lt(AsyncTaskResult::getExpireTime, DateUtil.date().toLocalDateTime())
.list();
if (CollUtil.isEmpty(list)){
return 0;
}
List<String> ids = list.stream().map(AsyncTaskResult::getId).toList();
super.lambdaUpdate().in(AsyncTaskResult::getId, ids).remove();
return ids.size();
}
}

@ -4,9 +4,16 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.supervision.livedigitalavatarmanage.constant.PromptTemplate; import com.supervision.livedigitalavatarmanage.constant.PromptTemplate;
import com.supervision.livedigitalavatarmanage.domain.AsyncTaskResult;
import com.supervision.livedigitalavatarmanage.dto.AsyncTaskResultDTO;
import com.supervision.livedigitalavatarmanage.dto.ScriptKeyPointsDTO;
import com.supervision.livedigitalavatarmanage.service.AsyncTaskResultService;
import com.supervision.livedigitalavatarmanage.service.LiveDigitalService; import com.supervision.livedigitalavatarmanage.service.LiveDigitalService;
import com.supervision.livedigitalavatarmanage.service.ScriptKeypointsService;
import com.supervision.livedigitalavatarmanage.vo.SalesPitchReqVo; import com.supervision.livedigitalavatarmanage.vo.SalesPitchReqVo;
import com.supervision.livedigitalavatarmanage.vo.SalesPitchResVo;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.SystemMessage;
@ -16,6 +23,8 @@ import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import java.util.*;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
@ -23,40 +32,133 @@ import java.util.*;
public class LiveDigitalServiceImpl implements LiveDigitalService { public class LiveDigitalServiceImpl implements LiveDigitalService {
private final OllamaChatModel ollamaChatModel; private final OllamaChatModel ollamaChatModel;
private final ScriptKeypointsService scriptKeypointsService;
private final AsyncTaskResultService asyncTaskResultService;
@Override @Override
public List<String> generateSalesPitch(SalesPitchReqVo salespitchReqVo) { public List<SalesPitchResVo> generateSalesPitch(SalesPitchReqVo salespitchReqVo) {
Assert.notEmpty(salespitchReqVo.getProductName(), "产品名称不能为空"); Assert.notEmpty(salespitchReqVo.getProductName(), "产品名称不能为空");
Assert.notEmpty(salespitchReqVo.getSpecifications(), "产品规格不能为空"); Assert.notEmpty(salespitchReqVo.getSpecifications(), "产品规格不能为空");
Assert.notEmpty(salespitchReqVo.getDetail(), "产品详情不能为空"); Assert.notEmpty(salespitchReqVo.getDetail(), "产品详情不能为空");
List<SalesPitchResVo> salesPitchResVos = new ArrayList<>();
// 根据用户id和产品品类查询话术要点
List<ScriptKeyPointsDTO> scriptKeyPointsDTOS = scriptKeypointsService.listByUserIdAndProductCategory(null, salespitchReqVo.getProductCategory());
// 对模板数据进行分组 (话术组ID -> 话术类型 -> 话术要点列表(按升序顺序排序))
Map<String, Map<String, List<ScriptKeyPointsDTO>>> scriptKeyPointsGroup = scriptKeyPointsDTOS.stream()
.collect(Collectors.groupingBy(
ScriptKeyPointsDTO::getScriptGroupId,
Collectors.groupingBy(
ScriptKeyPointsDTO::getScriptType,
Collectors.collectingAndThen(
Collectors.toList(),
list -> list.stream()
.sorted(Comparator.comparing(ScriptKeyPointsDTO::getKeyPointOrderNum))
.collect(Collectors.toList())
)
)
));
if (CollUtil.isEmpty(scriptKeyPointsGroup)){
return salesPitchResVos;
}
List<String> points = generateCopywritingPoints(salespitchReqVo); // 生成话术
Assert.notEmpty(points, "生成话术失败!请稍后重试"); for (Map.Entry<String, Map<String, List<ScriptKeyPointsDTO>>> entry : scriptKeyPointsGroup.entrySet()) {
List<Prompt> prompts = generatePrompts(points, salespitchReqVo); // 话术类型 -> 话术要点列表
List<String> result = new ArrayList<>(); Map<String, List<ScriptKeyPointsDTO>> scriptTypeMapPoints = entry.getValue();
for (Prompt prompt : prompts) { for (Map.Entry<String, List<ScriptKeyPointsDTO>> scriptTypeMapPointsEntry : scriptTypeMapPoints.entrySet()) {
for (int i = 0; i < 2; i++) { // 话术要点列表
List<ScriptKeyPointsDTO> scriptKeyPointsDTOList = scriptTypeMapPointsEntry.getValue();
if (CollUtil.isEmpty(scriptKeyPointsDTOList)){
continue;
}
// 生成销售话术
String productCategory = CollUtil.getFirst(scriptKeyPointsDTOList).getProductCategory();
String scriptType = CollUtil.getFirst(scriptKeyPointsDTOList).getScriptType();
String salesPitch = null;
try { try {
log.info("======================>>>>"); salesPitch = generateSalesPitchByPoints(productCategory, scriptType,
log.info("开始生成销售话术,请求内容:{}", prompt.getContents()); salespitchReqVo.buildProductInfo(), scriptKeyPointsDTOList);
ChatResponse call = ollamaChatModel.call(prompt);
String text = call.getResult().getOutput().getText();
// 去除think标签
text = removeThinkTag(text);
log.info("生成销售话术成功,结果:{}", text);
log.info("<<<<======================");
result.addAll(JSONUtil.toList(text, String.class));
break;
} catch (Exception e) { } catch (Exception e) {
log.error("生成销售话术失败,尝试第 {} 次。请求内容:{}", i + 1, salespitchReqVo.buildTemplate(),e); log.error("generateSalesPitch:生成销售话术失败", e);
} }
salesPitchResVos.add(new SalesPitchResVo(scriptType, salesPitch, CollUtil.getFirst(scriptKeyPointsDTOList).getScriptName()));
} }
} }
if (result.size() > 10){ return salesPitchResVos;
result = result.subList(0, 10);
} }
return result;
@Override
public String submitSalesPitchTask(SalesPitchReqVo salespitchReqVo) {
SalesPitchTask salesPitchTask = new SalesPitchTask(salespitchReqVo);
return asyncTaskResultService.submitAsyncTask("0", salespitchReqVo, salesPitchTask);
}
@Override
public AsyncTaskResultDTO<List<SalesPitchResVo>> querySalespitchTaskResult(String taskId) {
AsyncTaskResult asyncTaskResult = asyncTaskResultService.getById(taskId);
return new AsyncTaskResultDTO<>(asyncTaskResult, new TypeReference<>() {});
}
class SalesPitchTask implements Callable<List<SalesPitchResVo>> {
private final SalesPitchReqVo salespitchReqVo;
public SalesPitchTask(SalesPitchReqVo salespitchReqVo) {
this.salespitchReqVo = salespitchReqVo;
} }
@Override
public List<SalesPitchResVo> call() {
return generateSalesPitch(salespitchReqVo);
}
}
/**
*
* @param scriptKeyPointsDTOS
* @return
*/
private String generateSalesPitchByPoints(String productCategory,String scriptType,String productInfo,List<ScriptKeyPointsDTO> scriptKeyPointsDTOS) {
List<ScriptKeyPointsDTO> sps = scriptKeyPointsDTOS.stream().sorted(Comparator.comparing(ScriptKeyPointsDTO::getScriptTypeOrderNum))
.map(skp -> new ScriptKeyPointsDTO(skp.getScriptType(), skp.getKeyPoint(), skp.getKeyPointExample())).toList();
// “品牌故事”、“生产工艺”、“市场口碑”
String keyPointTitles1 = sps.stream().map(sp -> "“" +sp.getKeyPoint() + ":”").reduce((a, b) -> a + "、" + b).orElse("");
//“品牌故事:”、“生产工艺:”、“市场口碑:”
String keyPointTitles2 = sps.stream().map(sp -> "“" +sp.getKeyPoint() + ":”").reduce((a, b) -> a + "、" + b).orElse("");
//品牌故事、生产工艺、市场口碑
String keyPointTitles3 = sps.stream().map(ScriptKeyPointsDTO::getKeyPoint).reduce((a, b) -> a + "、" + b).orElse("");
// 品牌故事 -> 生产工艺 -> 市场口碑
String keyPointTitles4 = sps.stream().map(ScriptKeyPointsDTO::getKeyPoint).reduce((a, b) -> a + " -> " + b).orElse("");
//关键点:品牌故事 示例:衡恩老白干有着几十年的酿酒历史啦,一直坚持传统工艺,就为了给大家带来地道的老白干风味~
String keyPointExamples = sps.stream().map(sp -> "关键点:" + sp.getKeyPoint() + " 示例:" + sp.getKeyPointExample()).reduce((a, b) -> a + "\n" + b).orElse("");
Map<String,String> map = Map.of(
"productCategory", productCategory,
"scriptType", scriptType,
"productInfo", productInfo,
"keyPointTitles1", keyPointTitles1,
"keyPointTitles2", keyPointTitles2,
"keyPointTitles3", keyPointTitles3,
"keyPointTitles4", keyPointTitles4,
"keyPointExamples", keyPointExamples
);
String format = StrUtil.format(PromptTemplate.GENERATE_SALESPITCH_TEMPLATE_V2, map);
log.info("generateSalesPitchByPoints:提示词:{}",format);
UserMessage userMessage = new UserMessage(format);
Prompt prompt = new Prompt(userMessage);
ChatResponse call = ollamaChatModel.call(prompt);
String text = call.getResult().getOutput().getText();
// 去除think标签
text = removeThinkTag(text);
log.info("generateSalesPitchByPoints:模型生成的话术:{}",text);
return text;
}
private List<Prompt> generatePrompts(List<String> points, SalesPitchReqVo salespitchReqVo) { private List<Prompt> generatePrompts(List<String> points, SalesPitchReqVo salespitchReqVo) {
int promptNum = 10; // 需要生成的销售话术数量 int promptNum = 10; // 需要生成的销售话术数量

@ -0,0 +1,29 @@
package com.supervision.livedigitalavatarmanage.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.supervision.livedigitalavatarmanage.domain.ScriptKeypoints;
import com.supervision.livedigitalavatarmanage.dto.ScriptKeyPointsDTO;
import com.supervision.livedigitalavatarmanage.service.ScriptKeypointsService;
import com.supervision.livedigitalavatarmanage.mapper.ScriptKeypointsMapper;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author Administrator
* @description script_keypoints()Service
* @createDate 2025-09-17 10:57:12
*/
@Service
public class ScriptKeypointsServiceImpl extends ServiceImpl<ScriptKeypointsMapper, ScriptKeypoints>
implements ScriptKeypointsService{
@Override
public List<ScriptKeyPointsDTO> listByUserIdAndProductCategory(String userId, String productCategory) {
return super.getBaseMapper().listByUserIdAndProductCategory(userId,productCategory);
}
}

@ -0,0 +1,22 @@
package com.supervision.livedigitalavatarmanage.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.supervision.livedigitalavatarmanage.domain.ScriptUser;
import com.supervision.livedigitalavatarmanage.service.ScriptUserService;
import com.supervision.livedigitalavatarmanage.mapper.ScriptUserMapper;
import org.springframework.stereotype.Service;
/**
* @author Administrator
* @description script_user()Service
* @createDate 2025-09-17 10:57:12
*/
@Service
public class ScriptUserServiceImpl extends ServiceImpl<ScriptUserMapper, ScriptUser>
implements ScriptUserService{
}

@ -27,6 +27,11 @@ public class SalesPitchReqVo {
*/ */
private String detail; private String detail;
/**
*
*/
private String productCategory;
public String buildTemplate() { public String buildTemplate() {
return "请根据产品信息生成销售话术:" + return "请根据产品信息生成销售话术:" +

@ -0,0 +1,30 @@
package com.supervision.livedigitalavatarmanage.vo;
import lombok.Data;
@Data
public class SalesPitchResVo {
/**
*
*/
private String salesPitch;
/**
*
*/
private String scriptType;
/**
*
*/
private String scriptName;
public SalesPitchResVo() {
}
public SalesPitchResVo(String salesPitch, String scriptType, String scriptName) {
this.salesPitch = salesPitch;
this.scriptType = scriptType;
this.scriptName = scriptName;
}
}

@ -1,13 +1,10 @@
package com.supervision.livedigitalavatarmanage.vo.ollama; package com.supervision.livedigitalavatarmanage.vo.ollama;
import cn.hutool.core.lang.Assert;
import lombok.Data; import lombok.Data;
import org.springframework.ai.chat.messages.*; import org.springframework.ai.chat.messages.*;
import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.ollama.api.OllamaOptions; import org.springframework.ai.ollama.api.OllamaOptions;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@Data @Data
public class OllamaDTO { public class OllamaDTO {
@ -36,7 +33,6 @@ public class OllamaDTO {
}).toList(); }).toList();
OllamaOptions.Builder optionsBuilder = OllamaOptions.builder(); OllamaOptions.Builder optionsBuilder = OllamaOptions.builder();
optionsBuilder.model(model); optionsBuilder.model(model);
optionsBuilder.model(model);
return new Prompt(list,optionsBuilder.build()); return new Prompt(list,optionsBuilder.build());
} }
} }

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.supervision.livedigitalavatarmanage.mapper.AsyncTaskResultMapper">
<resultMap id="BaseResultMap" type="com.supervision.livedigitalavatarmanage.domain.AsyncTaskResult">
<id property="id" column="id" jdbcType="VARCHAR"/>
<result property="taskType" column="task_type" jdbcType="VARCHAR"/>
<result property="status" column="status" jdbcType="SMALLINT"/>
<result property="requestData" column="request_data" jdbcType="VARCHAR"/>
<result property="resultData" column="result_data" jdbcType="VARCHAR"/>
<result property="errorMessage" column="error_message" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="expireTime" column="expire_time" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,task_type,status,
request_data,result_data,error_message,
create_time,update_time,expire_time
</sql>
</mapper>

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.supervision.livedigitalavatarmanage.mapper.ScriptKeypointsMapper">
<resultMap id="BaseResultMap" type="com.supervision.livedigitalavatarmanage.domain.ScriptKeypoints">
<id property="id" column="id" jdbcType="VARCHAR"/>
<result property="scriptType" column="script_type" jdbcType="VARCHAR"/>
<result property="scriptId" column="script_id" jdbcType="VARCHAR"/>
<result property="keyPoint" column="key_point " jdbcType="VARCHAR"/>
<result property="keyPointExample" column="key_point_example" jdbcType="VARCHAR"/>
<result property="scriptTypeOrderNum" column="script_type_order_num" jdbcType="INTEGER"/>
<result property="keyPointOrderNum" column="keypoint_order_num" jdbcType="INTEGER"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,script_type,script_id,
key_point ,key_point_example,script_type_order_num,keypoint_order_num,
create_time,update_time
</sql>
<select id="listByUserIdAndProductCategory" resultType="com.supervision.livedigitalavatarmanage.dto.ScriptKeyPointsDTO">
SELECT
su.script_name,
su.product_category,
su.user_id,
sk.script_id as script_group_id,
sk.script_type,
sk.key_point,
sk.key_point_example,
sk.keypoint_order_num,
sk.script_type_order_num
FROM script_user su
LEFT JOIN script_keypoints sk ON su.id = sk.script_id
<where>
<if test="userId != null">
AND su.user_id = #{userId}
</if>
<if test="productCategory != null and productCategory != ''">
AND su.product_category = #{productCategory}
</if>
</where>
</select>
</mapper>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.supervision.livedigitalavatarmanage.mapper.ScriptUserMapper">
<resultMap id="BaseResultMap" type="com.supervision.livedigitalavatarmanage.domain.ScriptUser">
<id property="id" column="id" jdbcType="VARCHAR"/>
<result property="scriptName" column="script_name" jdbcType="VARCHAR"/>
<result property="productCategory" column="product_category" jdbcType="VARCHAR"/>
<result property="userId" column="user_id" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,script_name,product_category,
user_id,create_time,update_time
</sql>
</mapper>
Loading…
Cancel
Save