From 95546315718fafa1e09d5b9dc2edb40697679def Mon Sep 17 00:00:00 2001 From: spllzh <28668817@qq.com> Date: Thu, 25 Sep 2025 20:55:31 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A4=BE=E5=8C=BAfeed=E6=B5=81/topsales?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...CommunityFeedAnalysisResultController.java | 115 ++++++ .../rj/controller/biz/TopSalesController.java | 70 ++++ .../com/rj/dto/TopSalesScoreRequestDto.java | 18 + .../bz/CommunityFeedAnalysisResult.java | 77 ++++ .../com/rj/entity/bz/TopSalesScoreResult.java | 95 +++++ .../CommunityFeedAnalysisResultMapper.java | 12 + .../rj/mapper/TopSalesScoreResultMapper.java | 11 + .../com/rj/service/DifyWorkflowService.java | 60 +++ .../ICommunityFeedAnalysisResultService.java | 14 + .../biz/ITopSalesScoreResultService.java | 13 + ...ommunityFeedAnalysisResultServiceImpl.java | 143 +++++++ .../impl/TopSalesScoreResultServiceImpl.java | 140 +++++++ src/main/resources/application.yml | 3 +- src/main/sql/A-dify/AI-Feed流.yml | 372 ++++++++++++++++++ .../A-dify/【AI】topsales 所有维度-只打分.yml | 185 +++++++++ .../【AI客户画像】-李中华专用-test.yml | 0 .../sql/community_feed_analysis_result.sql | 25 ++ 17 files changed, 1352 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/rj/controller/biz/CommunityFeedAnalysisResultController.java create mode 100644 src/main/java/com/rj/controller/biz/TopSalesController.java create mode 100644 src/main/java/com/rj/dto/TopSalesScoreRequestDto.java create mode 100644 src/main/java/com/rj/entity/bz/CommunityFeedAnalysisResult.java create mode 100644 src/main/java/com/rj/entity/bz/TopSalesScoreResult.java create mode 100644 src/main/java/com/rj/mapper/CommunityFeedAnalysisResultMapper.java create mode 100644 src/main/java/com/rj/mapper/TopSalesScoreResultMapper.java create mode 100644 src/main/java/com/rj/service/biz/ICommunityFeedAnalysisResultService.java create mode 100644 src/main/java/com/rj/service/biz/ITopSalesScoreResultService.java create mode 100644 src/main/java/com/rj/service/biz/impl/CommunityFeedAnalysisResultServiceImpl.java create mode 100644 src/main/java/com/rj/service/biz/impl/TopSalesScoreResultServiceImpl.java create mode 100644 src/main/sql/A-dify/AI-Feed流.yml create mode 100644 src/main/sql/A-dify/【AI】topsales 所有维度-只打分.yml rename src/main/sql/{ => A-dify}/【AI客户画像】-李中华专用-test.yml (100%) create mode 100644 src/main/sql/community_feed_analysis_result.sql diff --git a/src/main/java/com/rj/controller/biz/CommunityFeedAnalysisResultController.java b/src/main/java/com/rj/controller/biz/CommunityFeedAnalysisResultController.java new file mode 100644 index 0000000..05496c6 --- /dev/null +++ b/src/main/java/com/rj/controller/biz/CommunityFeedAnalysisResultController.java @@ -0,0 +1,115 @@ +package com.rj.controller.biz; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rj.dto.CommunityFeedDTO; +import com.rj.dto.DifyWorkflowResponseDto; +import com.rj.entity.bz.CommunityFeedAnalysisResult; +import com.rj.service.biz.ICommunityFeedAnalysisResultService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController +@RequestMapping("/api/community-feed") +@Tag(name = "社区Feed分析", description = "社区Feed分析结果API") +public class CommunityFeedAnalysisResultController { + + + + @Autowired + private ICommunityFeedAnalysisResultService resultService; + + /** + * AI-Feed 流(社区Feed流优质内容) + */ + @PostMapping("/callAIFeedWorkflow") + @Operation(summary = "AI-Feed 工作流", description = "调用Dify AI-Feed 工作流(社区优质内容评估)") + public ResponseEntity aiFeedWorkflow( + @Valid @RequestBody CommunityFeedDTO requestDto) { + try { + log.info("开始调用AI-Feed工作流,参数: {}", requestDto); + return ResponseEntity.ok(resultService.callAiFeedWorkflow(requestDto)); + + } catch (Exception e) { + log.error("调用AI-Feed工作流失败", e); + + DifyWorkflowResponseDto errorResult = DifyWorkflowResponseDto.failure( + "AI-Feed 工作流调用失败: " + e.getMessage(), + e.getClass().getSimpleName() + ); + + return ResponseEntity.status(500).body(errorResult); + } + } + + + + @PostMapping("/save") + @Operation(summary = "保存分析结果") + public boolean save(@RequestBody CommunityFeedAnalysisResult result) { + return resultService.saveResult(result); + } + + @GetMapping("/by-workflow/{workflowId}") + @Operation(summary = "按workflowId查询") + public CommunityFeedAnalysisResult getByWorkflowId(@PathVariable String workflowId) { + return resultService.getByWorkflowId(workflowId); + } + + @GetMapping("/page") + @Operation(summary = "分页查询Feed分析结果") + public ResponseEntity> page( + @Parameter(description = "页码", example = "1") + @RequestParam(defaultValue = "1") Integer current, + @Parameter(description = "每页大小", example = "10") + @RequestParam(defaultValue = "10") Integer size, + @Parameter(description = "工作流ID(模糊查询)") + @RequestParam(required = false) String workflowId, + @Parameter(description = "状态(精确匹配)") + @RequestParam(required = false) String status + ) { + Map result = new HashMap<>(); + try { + Page page = new Page<>(current, size); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + if (workflowId != null && !workflowId.trim().isEmpty()) { + wrapper.like(CommunityFeedAnalysisResult::getWorkflowId, workflowId); + } + if (status != null && !status.trim().isEmpty()) { + wrapper.eq(CommunityFeedAnalysisResult::getStatus, status); + } + + wrapper.orderByDesc(CommunityFeedAnalysisResult::getCreatedAt); + + Page dataPage = resultService.page(page, wrapper); + + result.put("success", true); + result.put("message", "查询成功"); + result.put("data", dataPage.getRecords()); + result.put("total", dataPage.getTotal()); + result.put("current", dataPage.getCurrent()); + result.put("size", dataPage.getSize()); + result.put("pages", dataPage.getPages()); + + return ResponseEntity.ok(result); + } catch (Exception e) { + log.error("分页查询Feed分析结果失败", e); + result.put("success", false); + result.put("message", "查询失败: " + e.getMessage()); + return ResponseEntity.status(500).body(result); + } + } +} + + diff --git a/src/main/java/com/rj/controller/biz/TopSalesController.java b/src/main/java/com/rj/controller/biz/TopSalesController.java new file mode 100644 index 0000000..8059204 --- /dev/null +++ b/src/main/java/com/rj/controller/biz/TopSalesController.java @@ -0,0 +1,70 @@ +package com.rj.controller.biz; + +import com.rj.dto.TopSalesScoreRequestDto; +import com.rj.dto.DifyWorkflowResponseDto; +import com.rj.service.biz.ITopSalesScoreResultService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rj.entity.bz.TopSalesScoreResult; + +@Slf4j +@RestController +@RequestMapping("/api/topsales") +@Tag(name = "TopSales评分工作流", description = "调用【AI】topsales 所有维度-只打分") +public class TopSalesController { + + @Autowired + private ITopSalesScoreResultService topSalesScoreResultService; + + @PostMapping("/score") + @Operation(summary = "调用TopSales评分工作流") + public ResponseEntity score(@Valid @RequestBody TopSalesScoreRequestDto requestDto) { + try { + return ResponseEntity.ok(topSalesScoreResultService.callAndSave(requestDto)); + } catch (Exception e) { + log.error("调用TopSales评分工作流失败", e); + DifyWorkflowResponseDto error = DifyWorkflowResponseDto.failure( + "TopSales评分工作流调用失败: " + e.getMessage(), + e.getClass().getSimpleName() + ); + return ResponseEntity.status(500).body(error); + } + } + + @GetMapping("/page") + @Operation(summary = "分页查询TopSales评分结果") + public ResponseEntity page( + @RequestParam(defaultValue = "1") Integer current, + @RequestParam(defaultValue = "10") Integer size, + @RequestParam(required = false) String workflowId, + @RequestParam(required = false) String status + ) { + Page page = new Page<>(current, size); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (workflowId != null && !workflowId.trim().isEmpty()) { + wrapper.like(TopSalesScoreResult::getWorkflowId, workflowId); + } + if (status != null && !status.trim().isEmpty()) { + wrapper.eq(TopSalesScoreResult::getStatus, status); + } + wrapper.orderByDesc(TopSalesScoreResult::getCreatedAt); + Page result = topSalesScoreResultService.page(page, wrapper); + return ResponseEntity.ok(result); + } + + @GetMapping("/by-workflow/{workflowId}") + @Operation(summary = "按workflowId查询TopSales评分结果") + public ResponseEntity getByWorkflowId(@PathVariable String workflowId) { + return ResponseEntity.ok(topSalesScoreResultService.getByWorkflowId(workflowId)); + } +} + + diff --git a/src/main/java/com/rj/dto/TopSalesScoreRequestDto.java b/src/main/java/com/rj/dto/TopSalesScoreRequestDto.java new file mode 100644 index 0000000..c4f4c0c --- /dev/null +++ b/src/main/java/com/rj/dto/TopSalesScoreRequestDto.java @@ -0,0 +1,18 @@ +package com.rj.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +@Data +public class TopSalesScoreRequestDto { + + @NotBlank(message = "chat不能为空") + private String chat; + + // 可选:原始内容与来源信息 + private String srcContent; // 待分析内容(不传则使用 chat ) + private String srcId; // 原内容ID + private String srcDesc; // 来源说明 +} + + diff --git a/src/main/java/com/rj/entity/bz/CommunityFeedAnalysisResult.java b/src/main/java/com/rj/entity/bz/CommunityFeedAnalysisResult.java new file mode 100644 index 0000000..407bb26 --- /dev/null +++ b/src/main/java/com/rj/entity/bz/CommunityFeedAnalysisResult.java @@ -0,0 +1,77 @@ +package com.rj.entity.bz; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 社区 Feed 流分析结果实体 + */ +@Data +@TableName("community_feed_analysis_result") +public class CommunityFeedAnalysisResult { + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + @TableField("workflow_id") + private String workflowId; + + @TableField("length_score") + private Integer lengthScore; + + @TableField("image_score") + private Integer imageScore; + + @TableField("topics_score") + private Integer topicsScore; + + @TableField("emotion_score") + private Integer emotionScore; + + @TableField("theme_score") + private Integer themeScore; + + @TableField("fluency_score") + private Integer fluencyScore; + + @TableField("contentScore") + private Integer contentScore; + + @TableField("evaluationReason") + private String evaluationReason; + + @TableField("total_tokens") + private Integer totalTokens; + + @TableField("elapsed_time") + private BigDecimal elapsedTime; + + @TableField("status") + private String status; + + @TableField("error") + private String error; + + @TableField("src_content") + private String srcContent; + + @TableField("src_id") + private String srcId; + + @TableField("src_type") + private String srcType; + + @TableField("created_at") + private LocalDateTime createdAt; + + @TableField("updated_at") + private LocalDateTime updatedAt; +} + + diff --git a/src/main/java/com/rj/entity/bz/TopSalesScoreResult.java b/src/main/java/com/rj/entity/bz/TopSalesScoreResult.java new file mode 100644 index 0000000..d53bb70 --- /dev/null +++ b/src/main/java/com/rj/entity/bz/TopSalesScoreResult.java @@ -0,0 +1,95 @@ +package com.rj.entity.bz; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@TableName("topsales_score_result") +public class TopSalesScoreResult { + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + @TableField("workflow_id") + private String workflowId; + + @TableField("naturalness_score") + private Integer naturalnessScore; + + @TableField("coherence_score") + private Integer coherenceScore; + + @TableField("fluency_score") + private Integer fluencyScore; + + @TableField("advisor_aggressiveness_score") + private Integer advisorAggressivenessScore; + + @TableField("advisor_consistency_score") + private Integer advisorConsistencyScore; + + @TableField("greeting_reception_score") + private Integer greetingReceptionScore; + + @TableField("brand_introduction_score") + private Integer brandIntroductionScore; + + @TableField("customer_needs_understanding_score") + private Integer customerNeedsUnderstandingScore; + + @TableField("budget_understanding_score") + private Integer budgetUnderstandingScore; + + @TableField("recommendation_score") + private Integer recommendationScore; + + @TableField("safety_introduction_score") + private Integer safetyIntroductionScore; + + @TableField("health_environment_introduction_score") + private Integer healthEnvironmentIntroductionScore; + + @TableField("technology_introduction_score") + private Integer technologyIntroductionScore; + + @TableField("test_drive_invitation_score") + private Integer testDriveInvitationScore; + + @TableField("negotiation_deal_score") + private Integer negotiationDealScore; + + @TableField("total_tokens") + private Integer totalTokens; + + @TableField("elapsed_time") + private BigDecimal elapsedTime; + + @TableField("status") + private String status; + + @TableField("error") + private String error; + + @TableField("src_content") + private String srcContent; + + @TableField("src_id") + private String srcId; + + @TableField("src_desc") + private String srcDesc; + + @TableField("created_at") + private LocalDateTime createdAt; + + @TableField("updated_at") + private LocalDateTime updatedAt; +} + + diff --git a/src/main/java/com/rj/mapper/CommunityFeedAnalysisResultMapper.java b/src/main/java/com/rj/mapper/CommunityFeedAnalysisResultMapper.java new file mode 100644 index 0000000..07cfe3a --- /dev/null +++ b/src/main/java/com/rj/mapper/CommunityFeedAnalysisResultMapper.java @@ -0,0 +1,12 @@ +package com.rj.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.rj.entity.bz.CommunityFeedAnalysisResult; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CommunityFeedAnalysisResultMapper extends BaseMapper { +} + + diff --git a/src/main/java/com/rj/mapper/TopSalesScoreResultMapper.java b/src/main/java/com/rj/mapper/TopSalesScoreResultMapper.java new file mode 100644 index 0000000..e8091dc --- /dev/null +++ b/src/main/java/com/rj/mapper/TopSalesScoreResultMapper.java @@ -0,0 +1,11 @@ +package com.rj.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.rj.entity.bz.TopSalesScoreResult; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TopSalesScoreResultMapper extends BaseMapper { +} + + diff --git a/src/main/java/com/rj/service/DifyWorkflowService.java b/src/main/java/com/rj/service/DifyWorkflowService.java index 8e7982d..25f8dd8 100644 --- a/src/main/java/com/rj/service/DifyWorkflowService.java +++ b/src/main/java/com/rj/service/DifyWorkflowService.java @@ -47,6 +47,11 @@ public class DifyWorkflowService { @Value("${dify.api.ai-feed-token}") private String aiFeedToken; // AI-Feed流(社区优质内容评估) + @Value("${dify.api.top-sales-alldim-token}") + private String topSalesAllDimToken; // 【AI】topsales 所有维度-只打分 + + + private final RestTemplate restTemplate; @Autowired @@ -259,6 +264,61 @@ public class DifyWorkflowService { } } + /** + * 调用【AI】TopSales 所有维度-只打分 工作流 + * 输入:chat + * 输出:data.outputs.text(可能是纯文本或JSON字符串) + */ + public DifyWorkflowResponse callTopSalesScoringWorkflow(DifyWorkflowRequest request) { + try { + String url = difyBaseUrl + workflowEndpoint; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + topSalesAllDimToken); + + Map requestBody = new HashMap<>(); + requestBody.put("inputs", request.getInputs()); + requestBody.put("response_mode", "blocking"); + requestBody.put("user", request.getUserId()); + + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + + log.info("调用TopSales评分工作流API: {}", url); + log.info("TopSales评分请求参数: {}", JSON.toJSONString(requestBody)); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.POST, + entity, + String.class + ); + + log.info("TopSales评分工作流响应状态: {}", response.getStatusCode()); + log.info("TopSales评分工作流响应内容: {}", response.getBody()); + + if (response.getStatusCode() == HttpStatus.OK) { + JSONObject responseJson = JSON.parseObject(response.getBody()); + DifyWorkflowResponse result = new DifyWorkflowResponse(); + if (responseJson.containsKey("data")) { + JSONObject data = responseJson.getJSONObject("data"); + result.setWorkflowRunId(data.getString("workflow_run_id")); + result.setTaskId(data.getString("task_id")); + result.setData(data); + } + if (responseJson.containsKey("metadata")) { + result.setMetadata(responseJson.getJSONObject("metadata")); + } + return result; + } else { + throw new RuntimeException("TopSales评分工作流调用失败,状态码: " + response.getStatusCode()); + } + } catch (Exception e) { + log.error("调用TopSales评分工作流异常", e); + throw new RuntimeException("调用TopSales评分工作流异常: " + e.getMessage(), e); + } + } + /** * 解析工作流响应并保存数据 */ diff --git a/src/main/java/com/rj/service/biz/ICommunityFeedAnalysisResultService.java b/src/main/java/com/rj/service/biz/ICommunityFeedAnalysisResultService.java new file mode 100644 index 0000000..9633248 --- /dev/null +++ b/src/main/java/com/rj/service/biz/ICommunityFeedAnalysisResultService.java @@ -0,0 +1,14 @@ +package com.rj.service.biz; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.rj.entity.bz.CommunityFeedAnalysisResult; +import com.rj.dto.CommunityFeedDTO; +import com.rj.dto.DifyWorkflowResponseDto; + +public interface ICommunityFeedAnalysisResultService extends IService { + boolean saveResult(CommunityFeedAnalysisResult result); + CommunityFeedAnalysisResult getByWorkflowId(String workflowId); + DifyWorkflowResponseDto callAiFeedWorkflow(CommunityFeedDTO requestDto); +} + + diff --git a/src/main/java/com/rj/service/biz/ITopSalesScoreResultService.java b/src/main/java/com/rj/service/biz/ITopSalesScoreResultService.java new file mode 100644 index 0000000..b0d2016 --- /dev/null +++ b/src/main/java/com/rj/service/biz/ITopSalesScoreResultService.java @@ -0,0 +1,13 @@ +package com.rj.service.biz; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.rj.dto.TopSalesScoreRequestDto; +import com.rj.dto.DifyWorkflowResponseDto; +import com.rj.entity.bz.TopSalesScoreResult; + +public interface ITopSalesScoreResultService extends IService { + DifyWorkflowResponseDto callAndSave(TopSalesScoreRequestDto requestDto); + TopSalesScoreResult getByWorkflowId(String workflowId); +} + + diff --git a/src/main/java/com/rj/service/biz/impl/CommunityFeedAnalysisResultServiceImpl.java b/src/main/java/com/rj/service/biz/impl/CommunityFeedAnalysisResultServiceImpl.java new file mode 100644 index 0000000..6ab2cc2 --- /dev/null +++ b/src/main/java/com/rj/service/biz/impl/CommunityFeedAnalysisResultServiceImpl.java @@ -0,0 +1,143 @@ +package com.rj.service.biz.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.rj.dto.CommunityFeedDTO; +import com.rj.dto.DifyWorkflowResponseDto; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.rj.entity.bz.CommunityFeedAnalysisResult; +import com.rj.service.DifyWorkflowService; +import com.rj.mapper.CommunityFeedAnalysisResultMapper; +import com.rj.service.biz.ICommunityFeedAnalysisResultService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; +import java.util.HashMap; +import java.util.Map; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Slf4j +@Service +public class CommunityFeedAnalysisResultServiceImpl + extends ServiceImpl + implements ICommunityFeedAnalysisResultService { + + @Autowired + private DifyWorkflowService difyWorkflowService; + + @Override + public boolean saveResult(CommunityFeedAnalysisResult result) { + try { + log.info("保存社区Feed分析结果:{}", result); + return this.save(result); + } catch (Exception e) { + log.error("保存社区Feed分析结果异常", e); + return false; + } + } + + @Override + public CommunityFeedAnalysisResult getByWorkflowId(String workflowId) { + return this.getOne(new LambdaQueryWrapper() + .eq(CommunityFeedAnalysisResult::getWorkflowId, workflowId) + .last("limit 1")); + } + + @Override + public DifyWorkflowResponseDto callAiFeedWorkflow(CommunityFeedDTO requestDto) { + Map inputs = new HashMap<>(); + inputs.put("targetContent", requestDto.getTargetContent()); + inputs.put("targetId", requestDto.getTargetId()); + inputs.put("targetType", requestDto.getTargetType()); + + DifyWorkflowService.DifyWorkflowRequest request = + new DifyWorkflowService.DifyWorkflowRequest(inputs, requestDto.getTargetId()); + + DifyWorkflowService.DifyWorkflowResponse response = + difyWorkflowService.callAiFeedWorkflow(request); + + try { + JSONObject data = response.getData(); + if (data != null) { + CommunityFeedAnalysisResult entity = new CommunityFeedAnalysisResult(); + + // 顶层通用信息 + entity.setWorkflowId(data.getString("workflow_id")); + entity.setStatus(data.getString("status")); + entity.setTotalTokens(data.getInteger("total_tokens")); + if (data.containsKey("elapsed_time")) { + try { + entity.setElapsedTime(data.getBigDecimal("elapsed_time")); + } catch (Exception ex) { + try { + entity.setElapsedTime(new BigDecimal(String.valueOf(data.get("elapsed_time")))); + } catch (Exception ig) { + log.warn("elapsed_time 解析失败: {}", data.get("elapsed_time")); + } + } + } + entity.setError(data.getString("error")); + + // 解析 outputs.feedAiResult(可能是 ```json 包裹的字符串) + JSONObject outputs = data.getJSONObject("outputs"); + if (outputs != null && outputs.containsKey("feedAiResult")) { + String feedAiResult = outputs.getString("feedAiResult"); + if (feedAiResult != null) { + // 清理 ```json 代码块 + String cleaned = feedAiResult.trim(); + if (cleaned.startsWith("```json")) { + cleaned = cleaned.substring(7).trim(); + } else if (cleaned.startsWith("```")) { + cleaned = cleaned.substring(3).trim(); + } + if (cleaned.endsWith("```")) { + cleaned = cleaned.substring(0, cleaned.length() - 3).trim(); + } + try { + JSONObject scoreJson = JSON.parseObject(cleaned); + entity.setLengthScore(scoreJson.getInteger("length")); + entity.setImageScore(scoreJson.getInteger("image")); + entity.setTopicsScore(scoreJson.getInteger("topics")); + entity.setEmotionScore(scoreJson.getInteger("emotion")); + entity.setThemeScore(scoreJson.getInteger("theme")); + entity.setFluencyScore(scoreJson.getInteger("fluency")); + entity.setContentScore(scoreJson.getInteger("contentScore")); + entity.setEvaluationReason(scoreJson.getString("evaluationReason")); + } catch (Exception ignore) { + log.warn("解析feedAiResult为JSON失败,cleaned: {}", cleaned); + } + } + } + + // 源内容:从请求与 outputs 回填 + entity.setSrcContent(requestDto.getTargetContent()); + if (outputs != null) { + entity.setSrcId(outputs.getString("targetId")); + entity.setSrcType(outputs.getString("targetType")); + } + + // 时间字段 + LocalDateTime now = LocalDateTime.now(); + entity.setCreatedAt(now); + entity.setUpdatedAt(now); + + boolean saved = this.save(entity); + log.info("AI-Feed分析结果保存{}: {}", saved ? "成功" : "失败", entity); + } + } catch (Exception e) { + log.error("保存AI-Feed分析结果到DB失败", e); + } + + return DifyWorkflowResponseDto.success( + response.getWorkflowRunId(), + response.getTaskId(), + response.getData(), + response.getMetadata() + ); + } +} + + diff --git a/src/main/java/com/rj/service/biz/impl/TopSalesScoreResultServiceImpl.java b/src/main/java/com/rj/service/biz/impl/TopSalesScoreResultServiceImpl.java new file mode 100644 index 0000000..295c3ec --- /dev/null +++ b/src/main/java/com/rj/service/biz/impl/TopSalesScoreResultServiceImpl.java @@ -0,0 +1,140 @@ +package com.rj.service.biz.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.rj.dto.DifyWorkflowResponseDto; +import com.rj.dto.TopSalesScoreRequestDto; +import com.rj.entity.bz.TopSalesScoreResult; +import com.rj.mapper.TopSalesScoreResultMapper; +import com.rj.service.DifyWorkflowService; +import com.rj.service.biz.ITopSalesScoreResultService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Slf4j +@Service +public class TopSalesScoreResultServiceImpl + extends ServiceImpl + implements ITopSalesScoreResultService { + + @Autowired + private DifyWorkflowService difyWorkflowService; + + @Override + public DifyWorkflowResponseDto callAndSave(TopSalesScoreRequestDto requestDto) { + DifyWorkflowService.DifyWorkflowResponse response = null; + try { + log.info("[TopSales] 开始调用工作流,chat长度={}", requestDto.getChat() == null ? 0 : requestDto.getChat().length()); + JSONObject inputs = new JSONObject(); + inputs.put("chat", requestDto.getChat()); + DifyWorkflowService.DifyWorkflowRequest req = new DifyWorkflowService.DifyWorkflowRequest(inputs, "topsales_user"); + response = difyWorkflowService.callTopSalesScoringWorkflow(req); + + JSONObject data = response.getData(); + if (data != null) { + log.info("[TopSales] 响应data: {}", data); + TopSalesScoreResult entity = new TopSalesScoreResult(); + entity.setWorkflowId(data.getString("workflow_id")); + entity.setStatus(data.getString("status")); + entity.setTotalTokens(data.getInteger("total_tokens")); + try { + entity.setElapsedTime(data.getBigDecimal("elapsed_time")); + } catch (Exception ex) { + Object et = data.get("elapsed_time"); + if (et != null) { + entity.setElapsedTime(new BigDecimal(String.valueOf(et))); + } + } + + // 输出在 data.outputs.text + JSONObject outputs = data.getJSONObject("outputs"); + if (outputs != null) { + String text = outputs.getString("text"); + if (text != null) { + String cleaned = text.trim(); + if (cleaned.startsWith("```json")) cleaned = cleaned.substring(7).trim(); + if (cleaned.startsWith("```")) cleaned = cleaned.substring(3).trim(); + if (cleaned.endsWith("```")) cleaned = cleaned.substring(0, cleaned.length() - 3).trim(); + log.info("[TopSales] 清理后的text: {}", cleaned); + try { + JSONObject score = JSON.parseObject(cleaned); + // 同时兼容中文与英文snake_case键 + entity.setNaturalnessScore(firstInt(score, "自然度分数", "naturalness_score")); + entity.setCoherenceScore(firstInt(score, "连贯性分数", "coherence_score")); + entity.setFluencyScore(firstInt(score, "流畅度分数", "fluency_score")); + entity.setAdvisorAggressivenessScore(firstInt(score, "顾问攻击性分数", "advisor_aggressiveness_score")); + entity.setAdvisorConsistencyScore(firstInt(score, "顾问一致性分数", "advisor_consistency_score")); + entity.setGreetingReceptionScore(firstInt(score, "欢迎接待分数", "greeting_reception_score")); + entity.setBrandIntroductionScore(firstInt(score, "品牌介绍分数", "brand_introduction_score")); + entity.setCustomerNeedsUnderstandingScore(firstInt(score, "了解客户用车需求分数", "customer_needs_understanding_score")); + entity.setBudgetUnderstandingScore(firstInt(score, "了解客户经济预算分数", "budget_understanding_score")); + entity.setRecommendationScore(firstInt(score, "推荐配置或车型分数", "recommendation_score")); + entity.setSafetyIntroductionScore(firstInt(score, "重点介绍安全性分数", "safety_introduction_score")); + entity.setHealthEnvironmentIntroductionScore(firstInt(score, "重点介绍健康环保分数", "health_environment_introduction_score")); + entity.setTechnologyIntroductionScore(firstInt(score, "重点介绍科技性分数", "technology_introduction_score")); + entity.setTestDriveInvitationScore(firstInt(score, "邀请试驾分数", "test_drive_invitation_score")); + entity.setNegotiationDealScore(firstInt(score, "商谈成交分数", "negotiation_deal_score")); + } catch (Exception ex) { + log.warn("TopSales 输出解析失败,text= {}", text); + } + } + // 回填源内容字段(允许外部覆盖) + String srcContent = requestDto.getSrcContent() == null || requestDto.getSrcContent().trim().isEmpty() + ? requestDto.getChat() + : requestDto.getSrcContent(); + entity.setSrcContent(srcContent); + entity.setSrcId(requestDto.getSrcId() != null ? requestDto.getSrcId() : data.getString("id")); + entity.setSrcDesc(requestDto.getSrcDesc() != null ? requestDto.getSrcDesc() : "topsales_all_dim_score"); + } + + LocalDateTime now = LocalDateTime.now(); + entity.setCreatedAt(now); + entity.setUpdatedAt(now); + boolean saved = this.save(entity); + log.info("TopSales评分结果保存{}: {}", saved ? "成功" : "失败", entity); + } + } catch (Exception e) { + log.error("调用并保存TopSales评分结果异常", e); + } + + return DifyWorkflowResponseDto.success( + response != null ? response.getWorkflowRunId() : null, + response != null ? response.getTaskId() : null, + response != null ? response.getData() : null, + response != null ? response.getMetadata() : null + ); + } + + private Integer parseInt(String s) { + try { + if (s == null) return null; + return Integer.parseInt(s.trim()); + } catch (Exception e) { + return null; + } + } + + private Integer firstInt(JSONObject obj, String... keys) { + for (String k : keys) { + String v = obj.getString(k); + Integer parsed = parseInt(v); + if (parsed != null) return parsed; + } + return null; + } + + @Override + public TopSalesScoreResult getByWorkflowId(String workflowId) { + return this.getOne(new LambdaQueryWrapper() + .eq(TopSalesScoreResult::getWorkflowId, workflowId) + .last("limit 1")); + } +} + + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e4eb300..5be347c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -134,4 +134,5 @@ dify: summary-ddc-token: app-rgaQbIir7vrVb1473Z3Puz6w summary-nameplate-token: app-cv5glaYrY4zgjq0XIidSoJea portrait-3in1-token: app-jlrV6lLDlhy1YDEdUiK4hdVZ - ai-feed-token: app-b6kFOvLvAsAOxB1vDgSNa0OO \ No newline at end of file + ai-feed-token: app-b6kFOvLvAsAOxB1vDgSNa0OO + top-sales-alldim-token: app-99nVuuKfcRR2vikxgOTiNQm0 \ No newline at end of file diff --git a/src/main/sql/A-dify/AI-Feed流.yml b/src/main/sql/A-dify/AI-Feed流.yml new file mode 100644 index 0000000..9c93077 --- /dev/null +++ b/src/main/sql/A-dify/AI-Feed流.yml @@ -0,0 +1,372 @@ +app: + description: 生产环境运行,请勿动! + icon: 🤖 + icon_background: '#FFEAD5' + mode: workflow + name: 【AI-Feed流】社区Feed流优质内容 + use_icon_as_answer_icon: false +dependencies: +- current_identifier: null + type: marketplace + value: + marketplace_plugin_unique_identifier: langgenius/siliconflow:0.0.26@fb0da47fbd9113a2f7c1a70f4683be043df705e9318ff0aca4fc1d6fc9f9fe3d +- current_identifier: null + type: marketplace + value: + marketplace_plugin_unique_identifier: langgenius/tongyi:0.0.47@c6d9e43800aee0a9b71940310d320b8dd2214f98a725f8c8d79442aa5842dc77 +kind: app +version: 0.3.1 +workflow: + conversation_variables: [] + environment_variables: [] + features: + file_upload: + allowed_file_extensions: + - .JPG + - .JPEG + - .PNG + - .GIF + - .WEBP + - .SVG + allowed_file_types: + - image + allowed_file_upload_methods: + - local_file + - remote_url + enabled: false + fileUploadConfig: + audio_file_size_limit: 50 + batch_count_limit: 5 + file_size_limit: 15 + image_file_size_limit: 10 + video_file_size_limit: 100 + workflow_file_upload_limit: 10 + image: + enabled: false + number_limits: 3 + transfer_methods: + - local_file + - remote_url + number_limits: 3 + opening_statement: '' + retriever_resource: + enabled: true + sensitive_word_avoidance: + enabled: false + speech_to_text: + enabled: false + suggested_questions: [] + suggested_questions_after_answer: + enabled: false + text_to_speech: + enabled: false + language: '' + voice: '' + graph: + edges: + - data: + isInIteration: false + sourceType: llm + targetType: end + id: 1743489785452-source-1745640429913-target + selected: false + source: '1743489785452' + sourceHandle: source + target: '1745640429913' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + sourceType: llm + targetType: llm + id: 1745806378959-source-1743489785452-target + selected: false + source: '1745806378959' + sourceHandle: source + target: '1743489785452' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + sourceType: llm + targetType: llm + id: 1745810872275-source-1743489785452-target + selected: false + source: '1745810872275' + sourceHandle: source + target: '1743489785452' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + sourceType: start + targetType: llm + id: 1743489580997-source-1745806378959-target + selected: false + source: '1743489580997' + sourceHandle: source + target: '1745806378959' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + sourceType: start + targetType: llm + id: 1743489580997-source-1745810872275-target + selected: false + source: '1743489580997' + sourceHandle: source + target: '1745810872275' + targetHandle: target + type: custom + zIndex: 0 + nodes: + - data: + desc: '' + selected: false + title: 开始 + type: start + variables: + - label: targetContent + max_length: null + options: [] + required: true + type: paragraph + variable: targetContent + - label: targetId + max_length: null + options: [] + required: true + type: text-input + variable: targetId + - label: targetType + max_length: null + options: [] + required: true + type: text-input + variable: targetType + height: 142 + id: '1743489580997' + position: + x: 30 + y: 271 + positionAbsolute: + x: 30 + y: 271 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + model: + completion_params: + temperature: 0.6 + mode: chat + name: deepseek-ai/DeepSeek-V3 + provider: langgenius/siliconflow/siliconflow + prompt_template: + - id: 0ee31105-e8cc-4a89-8abf-e9318b431d38 + role: system + text: '' + - id: 28b19102-ba61-4ab1-ab23-999d21c4ad40 + role: user + text: "###角色设定\n你是一位经验丰富的内容审核专家,专注于用户生成内容(UGC)质量评估,具备舆情分析、社区管理、品牌维护的专业知识。请严格按照标准评估,禁止主观发挥\n\ + \n###文本说明\n1. 需分析内容:{{#1743489580997.targetContent#}}\n2. 话题标签:#话题#格式的都是话题\n\ + 3. 图片说明:【图片推荐】:xxx【图片分析】:xxx格式的是图片说明信息\n\n###内容评估维度\n1. 字数:{{#1745806378959.text#}}\n\ + 2. 图片数量:{{#1745810872275.text#}}\n3. 话题相关性:评估插入的话题和内容本身相关性\n4. 内容正负面属性(请结合图片说明信息和内容一起分析):正面、中性、负面,内容、图片任一有负面或者合规风险,即为负面内容\n\ + 5. 内容主题是否符合以下任一\n -知识分享(用车知识、技巧、技术讲解)\n -试驾体验(试驾感受、评价)\n -用车体验\n\ + \ -车友互动(探店打卡、参与活动、节日祝福、俱乐部车友会、提车作业、旅行日记、景点推荐)\n -品牌文化(沃尔沃历史、安全理念分享)\n\ + 6. 内容流畅性\n -观点鲜明,能引发思考和互动,包含实质干货\n -情感真挚,语言通顺,调理清晰,有逻辑,不机械\n\n###评分标准(总分100)\n\ + 1. 字数{{#1745806378959.text#}}:小于250字0分,250至300字10分,300以上20分\n2. 图片:\n\ + \ -图片数量:{{#1745810872275.text#}}\n -图片数量小于4张,图片得分 0分;若存在一张不合规图片,图片得分\ + \ 0分;\n -图片数量大于等于4张:\n   - 若高推荐图片数量大于等于4,图片得分 20分。\n   - 若高推荐图片数量少于4张,根据图片数量给5-10分\n\ + 3. 话题相关性(10分):0个话题3分,1个话题以上10分(每个不相关的话题扣1分)\n4. 内容正负面属性(10分):正面10分,中性5分,负面不合规0分\n\ + 5. 内容主题(20分):符合任意一种或多个且正面给20分,不符合则根据主题吸引力酌情给5-10分,主题负面0分\n6. 内容价值(20分):根据要求可在0-20分浮动打分\n\ + \n###回复校验\n1. 校验输出是否为json格式,是则输出;否,则重新按照json格式输出\n2. 输出字段必须严格参照示例,只要结果,不要解释说明" + - id: 8c40ccef-1a8f-447a-9fce-fab7504f0892 + role: assistant + text: "###输出示例(严格以json格式输出)\n{\n    \"length\": 10, // 字数得分\n    \"image\"\ + : 7, // 图片得分\n    \"topics\": 9, // 话题相关性得分\n    \"emotion\": 6, // 正负面得分\n\ +     \"theme\": 8, // 主题得分\n    \"fluency\": 7, // 内容流畅度得分\n \"contentScore\"\ + : 47, //总得分\n “evaluationReason”:\"(简单总结内容是否值得推荐)“\n}" + selected: false + title: 内容分析 + type: llm + variables: [] + vision: + configs: + detail: high + variable_selector: + - sys + - files + enabled: false + height: 90 + id: '1743489785452' + position: + x: 742.6070949556324 + y: 271 + positionAbsolute: + x: 742.6070949556324 + y: 271 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + - data: + desc: '' + outputs: + - value_selector: + - '1743489580997' + - targetId + variable: targetId + - value_selector: + - '1743489580997' + - targetType + variable: targetType + - value_selector: + - '1743489785452' + - text + variable: feedAiResult + selected: false + title: 结束 2 + type: end + height: 142 + id: '1745640429913' + position: + x: 1167.0995027951408 + y: 221.25425352593578 + positionAbsolute: + x: 1167.0995027951408 + y: 221.25425352593578 + selected: true + sourcePosition: right + targetPosition: left + type: custom + width: 244 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + model: + completion_params: + temperature: 0.4 + mode: chat + name: qwen2.5-72b-instruct + provider: langgenius/tongyi/tongyi + prompt_template: + - id: e7784a4d-d466-4acf-b588-107331269ea8 + role: system + text: '请你作为一个精确的文字分析助手,帮我完成以下任务: + + ###文本说明 + + 1. 话题标签:#话题#格式的都是 + + 2. 图片说明:【图片推荐】:xxx【图片分析】:xxx格式的是 + + + ###正文字数统计 + + 1. 识别文本中不同部分(正文、图片说明、话题标签) + + 2. 仅统计正文部分的字数 + + 3. 最后清晰地给出最终结果 + + + ###下面是需要分析的文本: + + {{#1743489580997.targetContent#}} + + 严格按照实际输入内容分析,禁止联想发挥,随意生成 + + + ' + - id: 3826e25e-fc3c-4288-b6c7-4224f632686e + role: assistant + text: '输出正文字数,如果没有需要输出的内容,请给默认值 0 , 示例 + + {"num":} + + + 回复校验: + + 1. 输出的字数是否是正文部分的字数,如不是,请重新分析输出 + + 2. 输出格式是否和示例一致,不一致请输出一致 + + 3. 仅输出最终结果即可 + + ' + selected: false + title: 正文字数 + type: llm + variables: [] + vision: + enabled: false + height: 90 + id: '1745806378959' + position: + x: 465.42857142857144 + y: 190.65556981915387 + positionAbsolute: + x: 465.42857142857144 + y: 190.65556981915387 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + model: + completion_params: + temperature: 0.3 + mode: chat + name: qwen2.5-14b-instruct + provider: langgenius/tongyi/tongyi + prompt_template: + - id: 8925f396-1deb-4ab5-b0ae-f6b878a7accd + role: system + text: "请你作为一个精确的文字分析助手,帮我完成以下任务:\n###文本说明\n2. 图片说明:【图片推荐】:xxx【图片分析】:xxx格式的是\n\ + ###图片数量统计\n1. 识识别文本中不同部分(正文、图片说明、话题标签)\n2. 仅考虑有几条图片说明信息,每一条算1张图片\n3. 最后清晰地给出最终结果\n\ + ###下面是需要分析的文本:\n{{#1743489580997.targetContent#}}\n严格按照实际输入内容分析,禁止联想发挥,随意生成\n\ + \n###输出示例,如果没有需要输出的内容,请给默认值 0 \n{图片数量:}\n\n###回复校验:\n1.输出格式是否和示例一致,不一致请输出一致\n\ + 2. 仅输出最终结果即可\n" + selected: false + title: 图片数量 + type: llm + variables: [] + vision: + enabled: false + height: 90 + id: '1745810872275' + position: + x: 348.28571428571433 + y: 373.2857142857143 + positionAbsolute: + x: 348.28571428571433 + y: 373.2857142857143 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + viewport: + x: 218.700086255442 + y: 144.6111724133774 + zoom: 0.5305007982786393 diff --git a/src/main/sql/A-dify/【AI】topsales 所有维度-只打分.yml b/src/main/sql/A-dify/【AI】topsales 所有维度-只打分.yml new file mode 100644 index 0000000..9817d5e --- /dev/null +++ b/src/main/sql/A-dify/【AI】topsales 所有维度-只打分.yml @@ -0,0 +1,185 @@ +app: + description: '' + icon: 🤖 + icon_background: '#FFEAD5' + mode: workflow + name: 【AI】topsales 所有维度-只打分 + use_icon_as_answer_icon: false +kind: app +version: 0.1.5 +workflow: + conversation_variables: [] + environment_variables: [] + features: + file_upload: + allowed_file_extensions: + - .JPG + - .JPEG + - .PNG + - .GIF + - .WEBP + - .SVG + allowed_file_types: + - image + allowed_file_upload_methods: + - local_file + - remote_url + enabled: false + fileUploadConfig: + audio_file_size_limit: 50 + batch_count_limit: 5 + file_size_limit: 15 + image_file_size_limit: 10 + video_file_size_limit: 100 + workflow_file_upload_limit: 50 + image: + enabled: false + number_limits: 3 + transfer_methods: + - local_file + - remote_url + number_limits: 3 + opening_statement: '' + retriever_resource: + enabled: true + sensitive_word_avoidance: + enabled: false + speech_to_text: + enabled: false + suggested_questions: [] + suggested_questions_after_answer: + enabled: false + text_to_speech: + enabled: false + language: '' + voice: '' + graph: + edges: + - data: + isInIteration: false + sourceType: start + targetType: llm + id: 1741767329780-source-1741767359679-target + source: '1741767329780' + sourceHandle: source + target: '1741767359679' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + sourceType: llm + targetType: end + id: 1741767359679-source-1741767361631-target + source: '1741767359679' + sourceHandle: source + target: '1741767361631' + targetHandle: target + type: custom + zIndex: 0 + nodes: + - data: + desc: '' + selected: false + title: 开始 + type: start + variables: + - label: chat + max_length: null + options: [] + required: true + type: paragraph + variable: chat + height: 90 + id: '1741767329780' + position: + x: 28.571428571428555 + y: 248.5 + positionAbsolute: + x: 28.571428571428555 + y: 248.5 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + model: + completion_params: + temperature: 0.7 + mode: chat + name: qwen-plus-latest + provider: tongyi + prompt_template: + - id: 104c7afc-d68b-4c57-a1a6-19e357531ec4 + role: system + text: "##以下是“沃尔沃汽车销售顾问与潜在客户的现场接待语音转义语料”{{#1741767329780.chat#}},销售顾问的目标是有效挖掘客户需求、顺利引导对话至车辆介绍及销售流程,并最终促成客户下单交易。\n\ + 请深度剖析对话语料内容,并进行 0-100 分的连续评分,分数越高,代表销售顾问在该环节的表现越优秀。\n\n\n##评分维度及分数定义:\n\ + 1、自然度(分数越高越自然): 对话内容整体上自然且接近日常交流,使客户感到舒适和被理解。\n\n2、连贯性(分数越高越连贯): 对话逻辑清晰、信息传递有序,易于客户跟随并理解。\n\ + \n3、流畅度(分数越高越流畅): 对话过程中销售顾问能够无缝地将话题导向到产品介绍或客户需求探索,进而推进至促成交易的话题,而不会让客户感觉到突兀。\n\ + \n4、顾问攻击性(分数越高代表越攻击性越低,分数越低代表攻击性越强): 如果顾问在对话中表现得过于急切或者不尊重客户的节奏,例如过早地推销产品或施加压力,则被视为具有攻击性。\n\ + \n5、顾问一致性(分数越高越一致): 销售顾问在整场对话中保持专业形象和服务态度,信息准确一致,有助于建立信任。\n\n6、欢迎接待: 销售顾问应让客户感受到重视与热情,例如“主动问候、自我介绍,并邀请客户入座,提供品味沃茶点”。\n\ + \n7、品牌介绍:详细介绍沃尔沃的品牌价值观、造车理念、品牌安全、品牌健康以及正面车主形象等方面。\n\n\n8、了解客户用车需求:通过询问了解客户的购车需求和最重要的决策因素,例如:(1)了解客户的具体用车场景是城市通勤为主还是长途出行为主、(2)了解用车人是家庭为主还是个人为主、(3)了解用户关注内饰外观为主还是操作性能为主、(4)了解用户当前的持有车辆信息以及关注的其他竞品车型。\n\ + \n\n9、了解客户经济预算:例如购车预算、是否有金融贷款需求等\n\n\n10、推荐配置或车型:根据客户需求推荐适合的车型或配置,并进行详细介绍,突出其设计风格和特色功能,\n\ + 例如:考虑客户主要个人用车,日常通勤居多,想买油车且重点考虑舒适性和安全性,可推荐沃尔沃XC40。它配备了先进的安全系统,如自动紧急制动和车道保持辅助,非常适合日常驾驶。同时,XC40燃油经济性好,节省油费。车内空间宽敞,周末出游携带行李也很方便。\n\ + \n11、重点介绍安全性:能够结合车辆不同的动力类型(如电车、燃油车、混动),全面介绍车辆的安全特性,包括城市智能安全系统(例如:前向自动刹车、对向车辆智能避让、后向自动刹车、车道保持辅助)、硼钢笼式车身、领航辅助、插混安全与品质、电池安全等。\ + \ 介绍时可以结合用户使用场景,通过专业的技术名词和具体数据,同时结合各种修饰方式使对话生动。\n\n\n12、重点介绍健康环保:介绍沃尔沃车辆的健康材质(例如:皮革、织物、塑料、金属、阻尼材料、座椅骨架)及清洁座舱(例如:双效增强型空气净化、主动式预净化、智能空气循环、空气质量监测)”,突出沃尔沃有远超行业的测试标准。\n\ + \n13、重点介绍科技性:能够结合车辆不同的动力类型(如电车、燃油车、混动),介绍车辆的动力性能(例如底盘操控、插混续航、插混性能)、车辆的自动泊车、全景影像、车机系统、灵巧车身、回音壁音响、续航扎实等优势,可以结合用户场景,通过专业的技术名词和具体数据,说明车辆的科技功能给用户带来的价值。\n\ + \n\n14、邀请试驾:(1)主动邀请客户进行试驾体验,介绍试驾的好处、流程及所需时间,向客户介绍体验路线及其特点。 (2)在试驾过程中,介绍车辆功能操作,例如:手机APP远程控制、体验动力加速性和领航系统、音响系统、盲点信息系统、启动安静省油、多种驾驶模式、自动泊车与灵巧车身等。\n\ + \n15、商谈成交:在商谈过程中,销售顾问应结合销售政策及延伸业务促进客户成交,清晰解释报价单及交付细节。\n例如:沃尔沃60期金融方案、保险、延保、二手车置换、附件精品等;介绍库存信息、生产状况及交车日期;结合客户需求/预算/期望车型等制定《报价单》,并清晰地为客户讲解各项费用及有效期限。\n\ + \n\n##输出示例(只打分,不要输出理由、分析过程。务必严格按照示例输出):\n{\n \"自然度分数\": \"\",\n \"连贯性分数\"\ + : \"\",\n \"流畅度分数\": \"\",\n \"顾问攻击性分数\": \"\",\n \"顾问一致性分数\": \"\"\ + ,\n \"欢迎接待分数\": \"\",\n \"品牌介绍分数\": \"\",\n \"了解客户用车需求分数\": \"\",\n\ + \ \"了解客户经济预算分数\": \"\",\n \"推荐配置或车型分数\": \"\",\n \"重点介绍安全性分数\": \"\"\ + ,\n \"重点介绍健康环保分数\": \"\",\n \"重点介绍科技性分数\": \"\",\n \"邀请试驾分数\": \"\"\ + ,\n \"商谈成交分数\": \"\"\n}" + selected: true + title: LLM + type: llm + variables: [] + vision: + enabled: false + height: 98 + id: '1741767359679' + position: + x: 345.42857142857144 + y: 248.5 + positionAbsolute: + x: 345.42857142857144 + y: 248.5 + selected: true + sourcePosition: right + targetPosition: left + type: custom + width: 244 + - data: + desc: '' + outputs: + - value_selector: + - '1741767359679' + - text + variable: text + selected: false + title: 结束 + type: end + height: 90 + id: '1741767361631' + position: + x: 638 + y: 248.5 + positionAbsolute: + x: 638 + y: 248.5 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + viewport: + x: 62.99090169418105 + y: 189.2453904900185 + zoom: 0.8418581379299643 diff --git a/src/main/sql/【AI客户画像】-李中华专用-test.yml b/src/main/sql/A-dify/【AI客户画像】-李中华专用-test.yml similarity index 100% rename from src/main/sql/【AI客户画像】-李中华专用-test.yml rename to src/main/sql/A-dify/【AI客户画像】-李中华专用-test.yml diff --git a/src/main/sql/community_feed_analysis_result.sql b/src/main/sql/community_feed_analysis_result.sql new file mode 100644 index 0000000..fb4f599 --- /dev/null +++ b/src/main/sql/community_feed_analysis_result.sql @@ -0,0 +1,25 @@ +CREATE TABLE `community_feed_analysis_result` ( + `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULLCOMMENT '主键', + `workflow_id` varchar(128) NOT NULL COMMENT '工作流运行ID', + `length_score` int NOT NULL DEFAULT 0 COMMENT '字数得分', + `image_score` int NOT NULL DEFAULT 0 COMMENT '图片得分', + `topics_score` int NOT NULL DEFAULT 0 COMMENT '话题相关性得分', + `emotion_score` int NOT NULL DEFAULT 0 COMMENT '正负面得分', + `theme_score` int NOT NULL DEFAULT 0 COMMENT '主题得分', + `fluency_score` int NOT NULL DEFAULT 0 COMMENT '内容流畅度得分', + `contentScore` int NOT NULL DEFAULT 0 COMMENT '总得分', + `evaluationReason` varchar(1000) DEFAULT NULL COMMENT '推荐理由简述', + `src_content` text DEFAULT NULL COMMENT '待分析内容', + `src_id` varchar(128) DEFAULT NULL COMMENT '原内容ID', + `src_type` varchar(64) DEFAULT NULL COMMENT '原分类', + `total_tokens` int DEFAULT NULL COMMENT '总tokens', + `elapsed_time` decimal(10,2) DEFAULT NULL COMMENT '耗时(秒)', + `status` varchar(32) DEFAULT NULL COMMENT '运行状态', + `error` text DEFAULT NULL COMMENT '错误信息', + `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `idx_workflow_id` (`workflow_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='社区feed流分析结果'; + +