diff --git a/backend/analytics-service/.gitignore b/backend/analytics-service/.gitignore new file mode 100644 index 0000000..59a54cd --- /dev/null +++ b/backend/analytics-service/.gitignore @@ -0,0 +1,2 @@ +target/ +target \ No newline at end of file diff --git a/backend/archive-service/.gitignore b/backend/archive-service/.gitignore new file mode 100644 index 0000000..59a54cd --- /dev/null +++ b/backend/archive-service/.gitignore @@ -0,0 +1,2 @@ +target/ +target \ No newline at end of file diff --git a/backend/auth-service/.gitignore b/backend/auth-service/.gitignore new file mode 100644 index 0000000..59a54cd --- /dev/null +++ b/backend/auth-service/.gitignore @@ -0,0 +1,2 @@ +target/ +target \ No newline at end of file diff --git a/backend/common/.gitignore b/backend/common/.gitignore new file mode 100644 index 0000000..59a54cd --- /dev/null +++ b/backend/common/.gitignore @@ -0,0 +1,2 @@ +target/ +target \ No newline at end of file diff --git a/backend/conversation-service/.gitignore b/backend/conversation-service/.gitignore new file mode 100644 index 0000000..59a54cd --- /dev/null +++ b/backend/conversation-service/.gitignore @@ -0,0 +1,2 @@ +target/ +target \ No newline at end of file diff --git a/backend/gateway/.gitignore b/backend/gateway/.gitignore new file mode 100644 index 0000000..59a54cd --- /dev/null +++ b/backend/gateway/.gitignore @@ -0,0 +1,2 @@ +target/ +target \ No newline at end of file diff --git a/backend/generation-service/.gitignore b/backend/generation-service/.gitignore new file mode 100644 index 0000000..59a54cd --- /dev/null +++ b/backend/generation-service/.gitignore @@ -0,0 +1,2 @@ +target/ +target \ No newline at end of file diff --git a/backend/generation-service/src/main/java/com/artedu/generation/client/BailianClient.java b/backend/generation-service/src/main/java/com/artedu/generation/client/BailianClient.java index dc512b5..1dc304c 100644 --- a/backend/generation-service/src/main/java/com/artedu/generation/client/BailianClient.java +++ b/backend/generation-service/src/main/java/com/artedu/generation/client/BailianClient.java @@ -32,6 +32,11 @@ public class BailianClient implements LLMClient { @Override public String generate(String system, String prompt, Integer maxTokens) { + return generate(system, prompt, maxTokens, true); + } + + @Override + public String generate(String system, String prompt, Integer maxTokens, boolean thinkingDisabled) { if (model == null || model.getApiKey() == null || model.getApiKey().isEmpty()) { log.error("百炼模型配置不完整"); return null; @@ -65,6 +70,13 @@ public class BailianClient implements LLMClient { if (maxTokens != null) { parameters.put("max_tokens", maxTokens); } + + if (thinkingDisabled) { + Map thinking = new HashMap<>(); + thinking.put("type", "disabled"); + parameters.put("thinking", thinking); + } + requestBody.put("parameters", parameters); try { diff --git a/backend/generation-service/src/main/java/com/artedu/generation/client/DoubaoClient.java b/backend/generation-service/src/main/java/com/artedu/generation/client/DoubaoClient.java index 76f2bad..efe50ea 100644 --- a/backend/generation-service/src/main/java/com/artedu/generation/client/DoubaoClient.java +++ b/backend/generation-service/src/main/java/com/artedu/generation/client/DoubaoClient.java @@ -33,6 +33,11 @@ public class DoubaoClient implements LLMClient { @Override public String generate(String system, String prompt, Integer maxTokens) { + return generate(system, prompt, maxTokens, true); + } + + @Override + public String generate(String system, String prompt, Integer maxTokens, boolean thinkingDisabled) { if (model == null || model.getApiKey() == null || model.getApiKey().isEmpty()) { log.error("豆包模型配置不完整"); return null; @@ -59,12 +64,18 @@ public class DoubaoClient implements LLMClient { if (maxTokens != null) { requestBody.put("max_tokens", maxTokens); } - // 从 configJson 读取 temperature,默认 0.7 + Double temperature = parseTemperature(); if (temperature != null) { requestBody.put("temperature", temperature); } + if (thinkingDisabled) { + Map thinking = new HashMap<>(); + thinking.put("type", "disabled"); + requestBody.put("thinking", thinking); + } + try { HttpEntity> entity = new HttpEntity<>(requestBody, headers); Map response = restTemplate.postForObject(url, entity, Map.class); diff --git a/backend/generation-service/src/main/java/com/artedu/generation/client/HunyuanClient.java b/backend/generation-service/src/main/java/com/artedu/generation/client/HunyuanClient.java index 6d692ab..77bb282 100644 --- a/backend/generation-service/src/main/java/com/artedu/generation/client/HunyuanClient.java +++ b/backend/generation-service/src/main/java/com/artedu/generation/client/HunyuanClient.java @@ -32,6 +32,11 @@ public class HunyuanClient implements LLMClient { @Override public String generate(String system, String prompt, Integer maxTokens) { + return generate(system, prompt, maxTokens, true); + } + + @Override + public String generate(String system, String prompt, Integer maxTokens, boolean thinkingDisabled) { if (model == null || model.getApiKey() == null || model.getApiKey().isEmpty()) { log.error("混元模型配置不完整"); return null; @@ -58,6 +63,12 @@ public class HunyuanClient implements LLMClient { if (maxTokens != null) { requestBody.put("MaxTokens", maxTokens); } + + if (thinkingDisabled) { + Map thinking = new HashMap<>(); + thinking.put("type", "disabled"); + requestBody.put("thinking", thinking); + } try { HttpEntity> entity = new HttpEntity<>(requestBody, headers); diff --git a/backend/generation-service/src/main/java/com/artedu/generation/client/LLMClient.java b/backend/generation-service/src/main/java/com/artedu/generation/client/LLMClient.java index 38d825f..c2e9f00 100644 --- a/backend/generation-service/src/main/java/com/artedu/generation/client/LLMClient.java +++ b/backend/generation-service/src/main/java/com/artedu/generation/client/LLMClient.java @@ -26,6 +26,17 @@ public interface LLMClient { */ String generate(String system, String prompt, Integer maxTokens); + /** + * 文本生成(支持system消息和关闭推理过程) + * + * @param system 系统提示词 + * @param prompt 用户提示词 + * @param maxTokens 最大token数 + * @param thinkingDisabled 是否关闭推理过程 + * @return 生成的文本 + */ + String generate(String system, String prompt, Integer maxTokens, boolean thinkingDisabled); + /** * 获取当前使用的模型配置 */ diff --git a/backend/generation-service/src/main/java/com/artedu/generation/controller/GenerationController.java b/backend/generation-service/src/main/java/com/artedu/generation/controller/GenerationController.java index fba74ed..ca3d71d 100644 --- a/backend/generation-service/src/main/java/com/artedu/generation/controller/GenerationController.java +++ b/backend/generation-service/src/main/java/com/artedu/generation/controller/GenerationController.java @@ -2,24 +2,20 @@ package com.artedu.generation.controller; import com.artedu.common.result.Result; import com.artedu.generation.client.LLMClient; +import com.artedu.generation.entity.ArchiveMessage; +import com.artedu.generation.mapper.ArchiveMessageMapper; import com.artedu.generation.service.LlmModelService; import com.artedu.generation.service.PromptEngine; import com.artedu.generation.service.QianwenClient; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import javax.validation.constraints.NotBlank; -import java.io.IOException; -import java.util.ArrayList; +import javax.validation.constraints.NotBlank; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; @Slf4j @Validated @@ -36,31 +32,21 @@ public class GenerationController { @Autowired private LlmModelService llmModelService; - private final ExecutorService sseExecutor = Executors.newCachedThreadPool(); - - private static final List> STYLES = List.of( - Map.of("id", "friendly", "name", "亲和风格", "desc", "温暖亲切,像朋友聊天"), - Map.of("id", "professional", "name", "专业风格", "desc", "数据驱动,专业权威"), - Map.of("id", "urgent", "name", "促单风格", "desc", "制造紧迫感,促成决策") - ); + @Autowired + private ArchiveMessageMapper archiveMessageMapper; @PostMapping("/utterance") public Result> generateUtterance( - @NotBlank(message = "message不能为空") @RequestParam("message") String message, - @RequestParam(value = "context", required = false) String context, - @RequestParam(value = "customerProfile", required = false) String customerProfile, - @RequestParam(value = "conversationContext", required = false) String conversationContext, - @RequestParam(value = "referenceUtterances", required = false) String referenceUtterances, - @NotBlank(message = "intent不能为空") @RequestParam("intent") String intent, - @NotBlank(message = "stage不能为空") @RequestParam("stage") String stage, - @RequestParam(value = "stageLabel", required = false) String stageLabel, - @RequestParam(value = "intentLabel", required = false) String intentLabel) { + @RequestParam(value = "fromUser", required = false) String fromUser, + @RequestParam(value = "toUser", required = false) String toUser, + @RequestParam(value = "roomid", required = false) String roomid) { - String[] promptPair = promptEngine.buildUtterancePrompt( - message, context, customerProfile, conversationContext, - referenceUtterances, intent, stage, stageLabel, intentLabel); + String chatContext = buildChatContext(fromUser, toUser, roomid); + + String[] promptPair = promptEngine.buildUtterancePrompt(chatContext); String systemPrompt = promptPair[0]; String userPrompt = promptPair[1]; + String generated = qianwenClient.generate(systemPrompt, userPrompt, 800); Map result = new HashMap<>(); @@ -72,6 +58,43 @@ public class GenerationController { return Result.success(result); } + private String buildChatContext(String fromUser, String toUser, String roomid) { + List messages; + boolean isGroupChat = roomid != null && !roomid.isEmpty(); + + long startTime = getTodayStartTime(); + long endTime = getTodayEndTime(); + + if (isGroupChat) { + messages = archiveMessageMapper.selectGroupChatMessages(roomid, startTime, endTime); + } else { + if (fromUser == null || fromUser.isEmpty() || toUser == null || toUser.isEmpty()) { + log.warn("单聊模式需要fromUser和toUser参数"); + return null; + } + toUser = "[\""+toUser+"\"]"; + messages = archiveMessageMapper.selectSingleChatMessages(fromUser, toUser, startTime, endTime); + } + + if (messages == null || messages.isEmpty()) { + return null; + } + + StringBuilder sb = new StringBuilder(); + for (ArchiveMessage msg : messages) { + sb.append(msg.getCreatedAt()).append(" ").append(msg.getFromRole()).append(msg.getContent()).append("\n"); + } + return sb.toString().trim(); + } + + private long getTodayStartTime() { + return java.time.LocalDate.now().atStartOfDay(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + private long getTodayEndTime() { + return java.time.LocalDate.now().plusDays(1).atStartOfDay(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli() - 1; + } + @PostMapping("/summary") public Result> generateSummary( @NotBlank(message = "context不能为空") @RequestParam("context") String context) { @@ -85,131 +108,6 @@ public class GenerationController { return Result.success(result); } - /** - * 流式生成多风格话术(SSE) - * 页面打开即自动推荐 3 条不同风格话术 - */ - @GetMapping(value = "/utterance-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public SseEmitter streamUtterance( - @NotBlank(message = "message不能为空") @RequestParam("message") String message, - @RequestParam(value = "context", required = false) String context, - @RequestParam(value = "customerProfile", required = false) String customerProfile, - @RequestParam(value = "conversationContext", required = false) String conversationContext, - @RequestParam(value = "referenceUtterances", required = false) String referenceUtterances, - @NotBlank(message = "intent不能为空") @RequestParam("intent") String intent, - @NotBlank(message = "stage不能为空") @RequestParam("stage") String stage, - @RequestParam(value = "stageLabel", required = false) String stageLabel, - @RequestParam(value = "intentLabel", required = false) String intentLabel) { - - SseEmitter emitter = new SseEmitter(120000L); // 2分钟超时 - - sseExecutor.execute(() -> { - try { - // 1. 发送开始事件,告知前端3种风格 - Map startEvent = new HashMap<>(); - startEvent.put("type", "start"); - startEvent.put("styles", STYLES); - emitter.send(SseEmitter.event().name("start").data(startEvent)); - - // 2. 构建 system + user prompt - String systemPrompt = promptEngine.buildSystemPrompt(stageLabel, intentLabel); - String userPrompt = promptEngine.buildMultiStyleUserPrompt( - message, context, customerProfile, conversationContext, - referenceUtterances, intent, stage, stageLabel, intentLabel); - - // 3. 调用 LLM 生成(非流式,后台一次完成) - long startTime = System.currentTimeMillis(); - String generated = qianwenClient.generate(systemPrompt, userPrompt, 1200); - log.info("多风格话术生成完成, elapsed={}ms", System.currentTimeMillis() - startTime); - - // 4. 解析出3条话术 - List utterances = parseMultiStyle(generated); - if (utterances.size() < 3) { - // 如果解析失败,用回退方案:把整个内容当一条 - while (utterances.size() < 3) { - utterances.add(generated != null ? generated : "生成失败,请稍后重试"); - } - } - - // 5. 逐条逐字流式推送给前端(伪流式,打字机效果) - for (int i = 0; i < Math.min(3, utterances.size()); i++) { - String text = utterances.get(i); - String styleId = STYLES.get(i).get("id"); - - // 先发送该条话术的起始事件 - Map itemStart = new HashMap<>(); - itemStart.put("type", "item-start"); - itemStart.put("styleId", styleId); - itemStart.put("index", i); - emitter.send(SseEmitter.event().name("item-start").data(itemStart)); - - // 逐字推送(每20-40ms推送一个字,模拟打字速度) - for (int j = 0; j < text.length(); j++) { - String ch = text.substring(j, j + 1); - Map chunk = new HashMap<>(); - chunk.put("type", "chunk"); - chunk.put("styleId", styleId); - chunk.put("text", ch); - chunk.put("index", i); - emitter.send(SseEmitter.event().name("chunk").data(chunk)); - Thread.sleep(25); // 25ms/字,约 40字/秒 - } - - // 该条话术完成 - Map complete = new HashMap<>(); - complete.put("type", "complete"); - complete.put("styleId", styleId); - complete.put("fullText", text); - complete.put("index", i); - emitter.send(SseEmitter.event().name("complete").data(complete)); - } - - // 6. 全部完成 - Map done = new HashMap<>(); - done.put("type", "done"); - done.put("model", qianwenClient.getCurrentModel() != null - ? qianwenClient.getCurrentModel().getModelName() : "default"); - emitter.send(SseEmitter.event().name("done").data(done)); - emitter.complete(); - - } catch (Exception e) { - log.error("流式生成失败", e); - try { - Map error = new HashMap<>(); - error.put("type", "error"); - error.put("message", e.getMessage()); - emitter.send(SseEmitter.event().name("error").data(error)); - } catch (IOException ignored) {} - emitter.completeWithError(e); - } - }); - - return emitter; - } - - /** - * 解析多风格话术,按 "|||" 分隔 - */ - private List parseMultiStyle(String text) { - List result = new ArrayList<>(); - if (text == null || text.isEmpty()) return result; - - // 去掉常见的AI输出前缀 - String cleaned = text.replaceAll("^(风格一[::]?|风格二[::]?|风格三[::]?|亲和风格[::]?|专业风格[::]?|促单风格[::]?)", "").trim(); - - // 按 ||| 分隔 - String[] parts = cleaned.split("\\|\\|\\|"); - for (String part : parts) { - String trimmed = part.trim(); - if (!trimmed.isEmpty() && trimmed.length() > 5) { - // 去掉可能的风格标签前缀 - trimmed = trimmed.replaceAll("^(风格[一二三四五][::]?|【.*?】[::]?)", "").trim(); - result.add(trimmed); - } - } - return result; - } - /** * 通用文本生成接口(供其他服务调用) */ diff --git a/backend/generation-service/src/main/java/com/artedu/generation/entity/ArchiveMessage.java b/backend/generation-service/src/main/java/com/artedu/generation/entity/ArchiveMessage.java new file mode 100644 index 0000000..7da48ba --- /dev/null +++ b/backend/generation-service/src/main/java/com/artedu/generation/entity/ArchiveMessage.java @@ -0,0 +1,32 @@ +package com.artedu.generation.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@TableName("archive_messages") +public class ArchiveMessage { + @TableId(type = IdType.AUTO) + private Long id; + private String msgid; + private Long seq; + private String corpId; + private String action; + private String fromUser; + private String fromRole; + private String toUser; + private String tolist; + private String roomid; + private String msgtype; + private Long msgtime; + private String content; + private String mediaData; + private Integer decryptStatus; + private String sessionId; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; +} \ No newline at end of file diff --git a/backend/generation-service/src/main/java/com/artedu/generation/mapper/ArchiveMessageMapper.java b/backend/generation-service/src/main/java/com/artedu/generation/mapper/ArchiveMessageMapper.java new file mode 100644 index 0000000..18f15c2 --- /dev/null +++ b/backend/generation-service/src/main/java/com/artedu/generation/mapper/ArchiveMessageMapper.java @@ -0,0 +1,39 @@ +package com.artedu.generation.mapper; + +import com.artedu.generation.entity.ArchiveMessage; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +@Mapper +public interface ArchiveMessageMapper extends BaseMapper { + + @Select("") + List selectSingleChatMessages(@Param("fromUser") String fromUser, + @Param("toUser") String toUser, + @Param("startTime") Long startTime, + @Param("endTime") Long endTime); + + @Select("SELECT created_at, from_role, content FROM archive_messages " + + "WHERE roomid = #{roomid} " + + "AND msgtime >= #{startTime} AND msgtime < #{endTime} AND msgtype='text' " + + "AND content IS NOT NULL AND content != '' " + + "ORDER BY msgtime DESC") + List selectGroupChatMessages(@Param("roomid") String roomid, + @Param("startTime") Long startTime, + @Param("endTime") Long endTime); +} \ No newline at end of file diff --git a/backend/generation-service/src/main/java/com/artedu/generation/service/PromptEngine.java b/backend/generation-service/src/main/java/com/artedu/generation/service/PromptEngine.java index b0feabd..92bd109 100644 --- a/backend/generation-service/src/main/java/com/artedu/generation/service/PromptEngine.java +++ b/backend/generation-service/src/main/java/com/artedu/generation/service/PromptEngine.java @@ -10,20 +10,24 @@ public class PromptEngine { /** * 构建生成话术的系统提示词(角色设定) */ - public String buildSystemPrompt(String stageLabel, String intentLabel) { + public String buildSystemPrompt(String chatHistoryContext) { StringBuilder sb = new StringBuilder(); - sb.append("你是「小九」,第九联盟(CG美术培训)的资深课程顾问,从业8年。"); - sb.append("第九联盟专注CG数字艺术教育10年,累计培养8000+学员,合作企业包括腾讯、网易、米哈游等500+家。"); - sb.append("6大校区:上海、西安、厦门、武汉、青岛、合肥。"); - sb.append("\n\n回复原则:\n"); - sb.append("1. 语气像微信聊天一样自然亲切,不要机械生硬、不要堆砌数据\n"); - sb.append("2. 结合学员具体情况给针对性建议,不要泛泛而谈\n"); - sb.append("3. 适当用学员成功案例或真实数据增强说服力,点到为止\n"); - sb.append("4. 长度控制在150-250字,简洁有力\n"); - sb.append("5. 如知道学员真实姓名则称呼姓名,不知道真实姓名时绝对不要编造名字,可以直接用自然的方式开头\n"); - if (stageLabel != null && !stageLabel.isEmpty()) { - sb.append("6. 当前处于「").append(stageLabel).append("」阶段,回复要匹配该阶段的沟通目标\n"); - } + sb.append("资深销售教练,仅输出符合规范的 3 条推荐回复话术,全程禁止输出任何对话分析、客户判断、前置解读、总结说明文字,任何分析类文字一律不写,正文只保留 3 条话术条目。\n"); + sb.append("## 对话历史\n"); + sb.append(chatHistoryContext); + sb.append("\n"); + sb.append("## 说明\n"); + sb.append("其中INTERNAL表示销售,EXTERNAL表示客户\n"); + sb.append(" ## 内置分析逻辑(仅 AI 内部运算,绝对不能打印 / 展示出来)\n"); + sb.append("内部自动完成 5 项分析,仅用于生成话术,不对外输出:1. 客户意图;2. 情绪状态;3. 购买阶段;4. 关键信号;5. 未解决问题\n"); + sb.append("## 硬性输出铁律(违规判定:出现任何分析文字直接不合格)\n"); + sb.append("禁止输出:客户情况总结、对话解读、阶段判断、需求分析、前置铺垫、过渡介绍、标题说明、总述段落;\n"); + sb.append("全文只能存在 3 条话术条目,除此以外无任何文字,不能加开头、结尾、备注类语句;\n"); + sb.append("每条话术固定三要素,缺一不可:完整话术文本、预期效果、INTERNAL 语气;\n"); + sb.append("单条话术文本≤100 字,整体风格专业、自然、有温度。\n"); + sb.append("## 单条话术固定格式(严格遵守,不改动结构)\n"); + sb.append("1、李先生您好,这个产品标准版 9800/年。不过不同企业的需求差别很大,方便说一下您这边大概的使用场景和人数吗?我可以帮您推荐最合适的版本,避免买多了浪费。\n"); + sb.append("2、价格方面我先给您一个参考——标准版 9800/年。上周有个和您同行做电商的客户,用了我们系统后客服效率提升了 40%。要不我发一份他们的使用案例给您看看?\n"); return sb.toString(); } @@ -76,14 +80,11 @@ public class PromptEngine { * 构建完整话术 Prompt(system + user 分离) * 返回 String[]{systemPrompt, userPrompt} */ - public String[] buildUtterancePrompt(String message, String context, - String customerProfile, String conversationContext, - String referenceUtterances, - String intent, String stage, - String stageLabel, String intentLabel) { - String system = buildSystemPrompt(stageLabel, intentLabel); - String user = buildUserPrompt(message, context, customerProfile, conversationContext, - referenceUtterances, intent, stage, stageLabel, intentLabel); + public String[] buildUtterancePrompt(String chatHistoryContext) { + String system = buildSystemPrompt(chatHistoryContext); +// String user = buildUserPrompt(message, context, customerProfile, conversationContext, +// referenceUtterances, intent, stage, stageLabel, intentLabel); + String user = ""; return new String[]{system, user}; } @@ -128,59 +129,6 @@ public class PromptEngine { return sb.toString(); } - /** - * 构建多风格话术的用户提示词 - * 一次生成 3 条不同风格的话术,用特定分隔符 - */ - public String buildMultiStyleUserPrompt(String message, String context, - String customerProfile, String conversationContext, - String referenceUtterances, - String intent, String stage, - String stageLabel, String intentLabel) { - StringBuilder sb = new StringBuilder(); - - if (customerProfile != null && !customerProfile.isEmpty()) { - sb.append("【学员画像】\n").append(customerProfile).append("\n\n"); - } - - String history = null; - if (conversationContext != null && !conversationContext.isEmpty()) { - history = conversationContext; - } else if (context != null && !context.isEmpty()) { - history = context; - } - if (history != null && !history.isEmpty()) { - sb.append("【最近对话】\n").append(history).append("\n\n"); - } - - String stageDisplay = (stageLabel != null && !stageLabel.isEmpty()) ? stageLabel : stage; - String intentDisplay = (intentLabel != null && !intentLabel.isEmpty()) ? intentLabel : intent; - sb.append("【当前阶段】").append(stageDisplay).append("\n"); - sb.append("【学员意图】").append(intentDisplay).append("\n\n"); - - if (referenceUtterances != null && !referenceUtterances.isEmpty()) { - sb.append("【参考话术风格】\n").append(referenceUtterances).append("\n\n"); - } - - sb.append("学员最新消息:\"").append(message).append("\"\n\n"); - sb.append("请为这条学员消息生成3种不同风格的回复话术,每种风格完全不同,让学员有更多选择:\n\n"); - sb.append("风格一【亲和风格】:语气温暖、亲切、像朋友聊天,拉近距离,适合建立好感\n"); - sb.append("风格二【专业风格】:用数据、案例、专业分析增强说服力,适合建立信任\n"); - sb.append("风格三【促单风格】:制造紧迫感,强调限时优惠或名额有限,适合促成决策\n\n"); - sb.append("要求:\n"); - sb.append("- 每条话术150-250字\n"); - sb.append("- 直接输出话术内容,不要加任何前缀或解释\n"); - sb.append("- 每条话术独立成行,用「|||」分隔\n\n"); - sb.append("输出格式示例:\n"); - sb.append("亲和风格话术内容...\n"); - sb.append("|||\n"); - sb.append("专业风格话术内容...\n"); - sb.append("|||\n"); - sb.append("促单风格话术内容..."); - - return sb.toString(); - } - public String buildSummaryPrompt(String context) { return "请对以下对话进行简要总结(50字以内),提炼学员核心需求和当前阶段:\n\n" + context; } diff --git a/backend/generation-service/target/classes/com/artedu/generation/client/BailianClient.class b/backend/generation-service/target/classes/com/artedu/generation/client/BailianClient.class index ea4b949..ec3b8fd 100644 Binary files a/backend/generation-service/target/classes/com/artedu/generation/client/BailianClient.class and b/backend/generation-service/target/classes/com/artedu/generation/client/BailianClient.class differ diff --git a/backend/generation-service/target/classes/com/artedu/generation/client/DoubaoClient.class b/backend/generation-service/target/classes/com/artedu/generation/client/DoubaoClient.class index 831798f..b4858b7 100644 Binary files a/backend/generation-service/target/classes/com/artedu/generation/client/DoubaoClient.class and b/backend/generation-service/target/classes/com/artedu/generation/client/DoubaoClient.class differ diff --git a/backend/generation-service/target/classes/com/artedu/generation/client/HunyuanClient.class b/backend/generation-service/target/classes/com/artedu/generation/client/HunyuanClient.class index 0fc8ee8..562f5e3 100644 Binary files a/backend/generation-service/target/classes/com/artedu/generation/client/HunyuanClient.class and b/backend/generation-service/target/classes/com/artedu/generation/client/HunyuanClient.class differ diff --git a/backend/generation-service/target/classes/com/artedu/generation/client/LLMClient.class b/backend/generation-service/target/classes/com/artedu/generation/client/LLMClient.class index b724616..9a2ee92 100644 Binary files a/backend/generation-service/target/classes/com/artedu/generation/client/LLMClient.class and b/backend/generation-service/target/classes/com/artedu/generation/client/LLMClient.class differ diff --git a/backend/generation-service/target/classes/com/artedu/generation/client/LLMClientFactory.class b/backend/generation-service/target/classes/com/artedu/generation/client/LLMClientFactory.class index 6976415..fe3ef45 100644 Binary files a/backend/generation-service/target/classes/com/artedu/generation/client/LLMClientFactory.class and b/backend/generation-service/target/classes/com/artedu/generation/client/LLMClientFactory.class differ diff --git a/backend/generation-service/target/classes/com/artedu/generation/controller/GenerationController.class b/backend/generation-service/target/classes/com/artedu/generation/controller/GenerationController.class index f7fa1bf..760bcc2 100644 Binary files a/backend/generation-service/target/classes/com/artedu/generation/controller/GenerationController.class and b/backend/generation-service/target/classes/com/artedu/generation/controller/GenerationController.class differ diff --git a/backend/generation-service/target/classes/com/artedu/generation/controller/LlmModelController.class b/backend/generation-service/target/classes/com/artedu/generation/controller/LlmModelController.class index ce732ea..99e0867 100644 Binary files a/backend/generation-service/target/classes/com/artedu/generation/controller/LlmModelController.class and b/backend/generation-service/target/classes/com/artedu/generation/controller/LlmModelController.class differ diff --git a/backend/generation-service/target/classes/com/artedu/generation/service/LlmModelService.class b/backend/generation-service/target/classes/com/artedu/generation/service/LlmModelService.class index d49d8fe..d9ce4e4 100644 Binary files a/backend/generation-service/target/classes/com/artedu/generation/service/LlmModelService.class and b/backend/generation-service/target/classes/com/artedu/generation/service/LlmModelService.class differ diff --git a/backend/generation-service/target/classes/com/artedu/generation/service/PromptEngine.class b/backend/generation-service/target/classes/com/artedu/generation/service/PromptEngine.class index 8d9f4cd..a5b39e0 100644 Binary files a/backend/generation-service/target/classes/com/artedu/generation/service/PromptEngine.class and b/backend/generation-service/target/classes/com/artedu/generation/service/PromptEngine.class differ diff --git a/backend/generation-service/target/classes/com/artedu/generation/service/QianwenClient.class b/backend/generation-service/target/classes/com/artedu/generation/service/QianwenClient.class index 84fa7e0..7f7cf2c 100644 Binary files a/backend/generation-service/target/classes/com/artedu/generation/service/QianwenClient.class and b/backend/generation-service/target/classes/com/artedu/generation/service/QianwenClient.class differ diff --git a/backend/intent-service/.gitignore b/backend/intent-service/.gitignore new file mode 100644 index 0000000..59a54cd --- /dev/null +++ b/backend/intent-service/.gitignore @@ -0,0 +1,2 @@ +target/ +target \ No newline at end of file diff --git a/backend/kb-admin-service/.gitignore b/backend/kb-admin-service/.gitignore new file mode 100644 index 0000000..59a54cd --- /dev/null +++ b/backend/kb-admin-service/.gitignore @@ -0,0 +1,2 @@ +target/ +target \ No newline at end of file diff --git a/backend/recommendation-service/.gitignore b/backend/recommendation-service/.gitignore new file mode 100644 index 0000000..59a54cd --- /dev/null +++ b/backend/recommendation-service/.gitignore @@ -0,0 +1,2 @@ +target/ +target \ No newline at end of file