家居大模型联调,分析客户产品需求,家庭结构和情感倾向

This commit is contained in:
ZLI263
2025-11-19 17:14:20 +08:00
parent 45d4f668d3
commit 8720f97ed4
6 changed files with 262 additions and 1 deletions

View File

@@ -1,8 +1,20 @@
package com.rj.controller;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rj.entity.AudioManagement;
import com.rj.entity.AudioTextAnalysisFurniture;
import com.rj.service.IAudioManagementService;
import com.rj.service.IAudioTextAnalysisFurnitureService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -14,6 +26,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -31,6 +44,12 @@ public class AudioTextAnalysisFurnitureController {
@Autowired
private IAudioTextAnalysisFurnitureService furnitureService;
@Autowired
private IAudioManagementService audioManagementService;
@Autowired
private ObjectMapper objectMapper;
@PostMapping("/add")
@Operation(summary = "新增记录", description = "新增一条家具意向分析记录")
public ResponseEntity<Map<String, Object>> add(@RequestBody AudioTextAnalysisFurniture furniture) {
@@ -179,5 +198,152 @@ public class AudioTextAnalysisFurnitureController {
return ResponseEntity.internalServerError().body(result);
}
}
@PostMapping("/generateSummary/{id}")
@Operation(summary = "生成总结", description = "根据ID获取recordingText调用大模型生成总结并保存")
public ResponseEntity<Map<String, Object>> generateSummary(
@Parameter(description = "家具意向分析记录ID", required = true)
@PathVariable String id) {
Map<String, Object> result = new HashMap<>();
try {
// 1. 根据ID查询家具意向分析记录
AudioManagement audioManagement = audioManagementService.getById(id);
if (audioManagement == null) {
result.put("success", false);
result.put("message", "记录不存在");
return ResponseEntity.badRequest().body(result);
}
// 3. 获取录音文本
String recordingText = audioManagement.getRecordingText();
if (recordingText == null || recordingText.trim().isEmpty()) {
result.put("success", false);
result.put("message", "录音文本为空");
return ResponseEntity.badRequest().body(result);
}
AudioTextAnalysisFurniture furniture = new AudioTextAnalysisFurniture();
furniture.setRecordingText(recordingText);
// 4. 调用大模型生成总结
generateSummaryByLLM(furniture);
audioManagement = new AudioManagement();
audioManagement.setSummary(furniture.getSummarySentence());
audioManagement.setId(id);
audioManagementService.updateById(audioManagement);
// 5. 保存总结到数据库
LambdaQueryWrapper<AudioTextAnalysisFurniture> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(AudioTextAnalysisFurniture::getParentId, id);
AudioTextAnalysisFurniture one = furnitureService.getOne(wrapper);
if (one != null) {
furniture.setId(one.getId());
}
furniture.setUpdatedAt(LocalDateTime.now());
furniture.setParentId( id);
furniture.setRecordingText(null);
boolean updateSuccess = furnitureService.saveOrUpdate(furniture);
if (updateSuccess) {
result.put("success", true);
result.put("message", "生成总结成功");
result.put("data", furniture);
return ResponseEntity.ok(result);
} else {
result.put("success", false);
result.put("message", "保存总结失败");
return ResponseEntity.badRequest().body(result);
}
} catch (Exception e) {
log.error("生成总结失败ID: {}", id, e);
result.put("success", false);
result.put("message", "生成总结异常:" + e.getMessage());
return ResponseEntity.internalServerError().body(result);
}
}
/**
* 调用大模型生成总结
*
* @param
* @return 生成的总结
*/
private void generateSummaryByLLM(AudioTextAnalysisFurniture furniture) {
Generation gen = new Generation();
Message systemMsg = Message.builder()
.role(Role.SYSTEM.getValue())
.content("你是一个专业的家居销售顾问助手需要从录音文本中抽取结构化信息并输出JSON。必须严格按照要求输出JSON不能包含额外文字。")
.build();
Message userMsg = Message.builder()
.role(Role.USER.getValue())
.content(buildPrompt(furniture.getRecordingText()))
.build();
GenerationParam param = GenerationParam.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model("qwen-plus")
.messages(Arrays.asList(systemMsg, userMsg))
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.build();
try {
GenerationResult call = gen.call(param);
String rawContent = call.getOutput().getChoices().get(0).getMessage().getContent();
log.info("大模型生成总结完成,结果长度: {},内容是:{}", rawContent.length(), rawContent);
applyStructuredResult(furniture, rawContent);
} catch (NoApiKeyException e) {
log.error("API密钥未配置", e);
throw new RuntimeException("API密钥未配置: " + e.getMessage(), e);
} catch (InputRequiredException e) {
log.error("输入参数错误", e);
throw new RuntimeException("输入参数错误: " + e.getMessage(), e);
} catch (Exception e) {
log.error("调用大模型生成总结失败", e);
throw new RuntimeException("调用大模型失败: " + e.getMessage(), e);
}
}
private String buildPrompt(String recordingText) {
return "请阅读以下录音文本,从中提取客户信息并生成 JSON。字段要求\n"
+ "1. customer_type可选值仅限「新房装修」「二次翻修」「补充家居」从谈话中判断最匹配的一个\n"
+ "2. family_structure描述家庭人数、孩子数量、老人情况必须突出家庭成员组成\n"
+ "3. intention_products只列出客户提到的家居类产品如床、柜子、沙发等多个产品用顿号或逗号分隔\n"
+ "4. summary一句话总结客户需求必须提到预算若未提及则默认为3万元还需涵盖客户类型、装修风格偏好及主要意向产品。\n"
+ "严格输出 JSON{\"customer_type\":\"二次翻修\",\"family_structure\":\"三口之家,一个小男孩\",\"intention_products\":\"床、沙发、酒柜、厨房柜子\",\"summary\":\"...\"}\n"
+ "不要添加任何额外文字。\n\n录音文本\n" + recordingText;
}
private void applyStructuredResult(AudioTextAnalysisFurniture furniture, String rawContent) {
String normalized = normalizeJson(rawContent);
try {
JsonNode root = objectMapper.readTree(normalized);
furniture.setCustomerType(textValue(root, "customer_type"));
furniture.setFamilyStructure(textValue(root, "family_structure"));
furniture.setIntentionProducts(textValue(root, "intention_products"));
furniture.setSummarySentence(textValue(root, "summary"));
} catch (JsonProcessingException e) {
log.warn("解析大模型返回JSON失败使用原始内容作为总结: {}", e.getMessage());
furniture.setSummarySentence(rawContent);
}
}
private String normalizeJson(String content) {
String trimmed = content.trim();
if (trimmed.startsWith("```")) {
int firstLineBreak = trimmed.indexOf('\n');
int lastFence = trimmed.lastIndexOf("```");
if (firstLineBreak >= 0 && lastFence > firstLineBreak) {
trimmed = trimmed.substring(firstLineBreak + 1, lastFence).trim();
}
}
return trimmed;
}
private String textValue(JsonNode root, String field) {
JsonNode node = root.get(field);
return node == null || node.isNull() ? null : node.asText();
}
}

View File

@@ -167,6 +167,10 @@ public class AudioManagement implements Serializable {
@TableField("recording_text")
private String recordingText;
@Schema(description = "概要总结")
@TableField("summary")
private String summary;
/**
* 前端上传的录音文件
* 此字段不保存在数据库中,仅用于接收前端上传的文件

View File

@@ -1,5 +1,6 @@
package com.rj.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -24,7 +25,7 @@ public class AudioTextAnalysisFurniture implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "记录唯一标识存放UUID字符")
@TableId("id")
@TableId(type = IdType.ASSIGN_UUID)
private String id;
@Schema(description = "音频管理ID/父级ID")
@@ -111,6 +112,10 @@ public class AudioTextAnalysisFurniture implements Serializable {
@TableField("customer_id")
private String customerId;
@Schema(description = "录音文本")
@TableField(exist = false)
private String recordingText;
@Schema(description = "创建时间")
@TableField("created_at")
private LocalDateTime createdAt;