多场景分析代码架构
This commit is contained in:
60
src/main/java/com/rj/common/AudioAnalysisSceneType.java
Normal file
60
src/main/java/com/rj/common/AudioAnalysisSceneType.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package com.rj.common;
|
||||
|
||||
/**
|
||||
* 音频文本分析场景类型
|
||||
* 不同场景对应不同的系统/用户提示词以及大模型配置。
|
||||
*/
|
||||
public enum AudioAnalysisSceneType {
|
||||
|
||||
/**
|
||||
* 家具销售/家居意向场景
|
||||
*/
|
||||
SCENARIO_FURNITURE_SALE(
|
||||
"prompts/audio_text_analysis_furniture_system.txt",
|
||||
"prompts/audio_text_analysis_furniture_user.txt",
|
||||
"qwen-plus"
|
||||
),
|
||||
|
||||
/**
|
||||
* 会议纪要/会议分析场景
|
||||
* 提示词文件和模型可根据实际需要进行调整。
|
||||
*/
|
||||
SCENARIO_MEETING_SUMMARY(
|
||||
"prompts/audio_text_analysis_meeting_system.txt",
|
||||
"prompts/audio_text_analysis_meeting_user.txt",
|
||||
"qwen-plus"
|
||||
),
|
||||
|
||||
/**
|
||||
* 房产销售/置业顾问场景
|
||||
*/
|
||||
SCENARIO_CAR_SALE(
|
||||
"prompts/audio_text_analysis_real_estate_system.txt",
|
||||
"prompts/audio_text_analysis_real_estate_user.txt",
|
||||
"qwen-plus"
|
||||
);
|
||||
|
||||
private final String systemPromptPath;
|
||||
private final String userPromptPath;
|
||||
private final String modelName;
|
||||
|
||||
AudioAnalysisSceneType(String systemPromptPath, String userPromptPath, String modelName) {
|
||||
this.systemPromptPath = systemPromptPath;
|
||||
this.userPromptPath = userPromptPath;
|
||||
this.modelName = modelName;
|
||||
}
|
||||
|
||||
public String getSystemPromptPath() {
|
||||
return systemPromptPath;
|
||||
}
|
||||
|
||||
public String getUserPromptPath() {
|
||||
return userPromptPath;
|
||||
}
|
||||
|
||||
public String getModelName() {
|
||||
return modelName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.rj.service;
|
||||
|
||||
import com.rj.common.AudioAnalysisSceneType;
|
||||
|
||||
/**
|
||||
* 音频文本分析 - 大模型通用服务
|
||||
* <p>
|
||||
* 封装不同业务场景下的大模型调用逻辑(提示词加载、模型选择等),
|
||||
* 控制层只需要关心场景类型和原始录音文本。
|
||||
* </p>
|
||||
*/
|
||||
public interface IAudioTextAnalysisLlmService {
|
||||
|
||||
/**
|
||||
* 调用大模型生成总结/结构化结果(通用方法)
|
||||
*
|
||||
* @param sceneType 业务场景类型(家具、会议、房产等)
|
||||
* @param recordingText 录音转写文本
|
||||
* @return 大模型返回的原始内容(一般是 JSON 字符串或结构化文本)
|
||||
*/
|
||||
String generateSummaryByLLM(AudioAnalysisSceneType sceneType, String recordingText);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.rj.service.impl;
|
||||
|
||||
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.rj.common.AudioAnalysisSceneType;
|
||||
import com.rj.service.IAudioTextAnalysisLlmService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 音频文本分析 - 大模型通用服务实现
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class AudioTextAnalysisLlmServiceImpl implements IAudioTextAnalysisLlmService {
|
||||
|
||||
private static final Map<String, String> SYSTEM_PROMPT_CACHE = new ConcurrentHashMap<>();
|
||||
private static final Map<String, String> USER_PROMPT_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public String generateSummaryByLLM(AudioAnalysisSceneType sceneType, String recordingText) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("开始调用大模型生成总结,场景: {}", sceneType);
|
||||
|
||||
String systemPrompt = getSystemPrompt(sceneType);
|
||||
String userPromptTemplate = getUserPromptTemplate(sceneType);
|
||||
String userPrompt = buildPrompt(userPromptTemplate, recordingText);
|
||||
|
||||
Generation gen = new Generation();
|
||||
Message systemMsg = Message.builder()
|
||||
.role(Role.SYSTEM.getValue())
|
||||
.content(systemPrompt)
|
||||
.build();
|
||||
Message userMsg = Message.builder()
|
||||
.role(Role.USER.getValue())
|
||||
.content(userPrompt)
|
||||
.build();
|
||||
|
||||
GenerationParam param = GenerationParam.builder()
|
||||
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
|
||||
.model(sceneType.getModelName())
|
||||
.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("大模型生成总结完成,场景: {}, 结果长度: {}", sceneType, rawContent != null ? rawContent.length() : 0);
|
||||
return 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("调用大模型生成总结失败, 场景: {}", sceneType, e);
|
||||
throw new RuntimeException("调用大模型失败: " + e.getMessage(), e);
|
||||
} finally {
|
||||
long endTime = System.currentTimeMillis();
|
||||
long durationMillis = endTime - startTime;
|
||||
double durationSeconds = durationMillis / 1000.0;
|
||||
double durationMinutes = durationSeconds / 60.0;
|
||||
log.info("调用大模型耗时: {} 毫秒, {} 秒, {} 分钟, 场景: {}",
|
||||
durationMillis,
|
||||
String.format("%.2f", durationSeconds),
|
||||
String.format("%.2f", durationMinutes),
|
||||
sceneType);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildPrompt(String template, String recordingText) {
|
||||
if (template == null) {
|
||||
return recordingText != null ? recordingText : "";
|
||||
}
|
||||
return template.replace("{RECORDING_TEXT}", recordingText != null ? recordingText : "");
|
||||
}
|
||||
|
||||
private String getSystemPrompt(AudioAnalysisSceneType sceneType) {
|
||||
return SYSTEM_PROMPT_CACHE.computeIfAbsent(sceneType.name(), key -> {
|
||||
String path = sceneType.getSystemPromptPath();
|
||||
String content = readResourceFile(path);
|
||||
if (content == null || content.trim().isEmpty()) {
|
||||
log.warn("无法读取系统提示词文件: {},场景: {},请检查资源文件是否存在", path, sceneType);
|
||||
}
|
||||
return content != null ? content : "";
|
||||
});
|
||||
}
|
||||
|
||||
private String getUserPromptTemplate(AudioAnalysisSceneType sceneType) {
|
||||
return USER_PROMPT_CACHE.computeIfAbsent(sceneType.name(), key -> {
|
||||
String path = sceneType.getUserPromptPath();
|
||||
String content = readResourceFile(path);
|
||||
if (content == null || content.trim().isEmpty()) {
|
||||
log.warn("无法读取用户提示词模板文件: {},场景: {},请检查资源文件是否存在", path, sceneType);
|
||||
}
|
||||
return content != null ? content : "";
|
||||
});
|
||||
}
|
||||
|
||||
private String readResourceFile(String resourcePath) {
|
||||
try {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
InputStream inputStream = classLoader.getResourceAsStream(resourcePath);
|
||||
if (inputStream == null) {
|
||||
log.error("无法找到资源文件: {}", resourcePath);
|
||||
return null;
|
||||
}
|
||||
try (Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) {
|
||||
String content = scanner.hasNext() ? scanner.next() : "";
|
||||
log.info("成功读取提示词文件: {}, 长度: {}", resourcePath, content.length());
|
||||
return content;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("读取资源文件失败: {}", resourcePath, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user