Merge branch 'master' of ssh://git-a.9artedu.com:2222/jqb/agent_9art
This commit is contained in:
commit
984b3dba70
@ -49,5 +49,9 @@
|
|||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@ -0,0 +1,139 @@
|
|||||||
|
package com.artedu.common.util;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import okhttp3.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class HttpUtils {
|
||||||
|
|
||||||
|
private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder()
|
||||||
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(30, TimeUnit.SECONDS)
|
||||||
|
.writeTimeout(30, TimeUnit.SECONDS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static final MediaType JSON_MEDIA_TYPE = MediaType.get("application/json; charset=utf-8");
|
||||||
|
private static final MediaType FORM_MEDIA_TYPE = MediaType.get("application/x-www-form-urlencoded; charset=utf-8");
|
||||||
|
|
||||||
|
public static String get(String url) throws IOException {
|
||||||
|
return get(url, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String get(String url, Map<String, String> headers) throws IOException {
|
||||||
|
Request.Builder requestBuilder = new Request.Builder().url(url).get();
|
||||||
|
|
||||||
|
if (headers != null) {
|
||||||
|
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||||
|
requestBuilder.addHeader(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Request request = requestBuilder.build();
|
||||||
|
return executeRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String post(String url, String jsonBody) throws IOException {
|
||||||
|
return post(url, jsonBody, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String post(String url, String jsonBody, Map<String, String> headers) throws IOException {
|
||||||
|
RequestBody body = RequestBody.create(jsonBody, JSON_MEDIA_TYPE);
|
||||||
|
Request.Builder requestBuilder = new Request.Builder().url(url).post(body);
|
||||||
|
|
||||||
|
if (headers != null) {
|
||||||
|
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||||
|
requestBuilder.addHeader(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Request request = requestBuilder.build();
|
||||||
|
return executeRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String postForm(String url, Map<String, Object> formParams) throws IOException {
|
||||||
|
return postForm(url, formParams, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String postForm(String url, Map<String, Object> formParams, Map<String, String> headers) throws IOException {
|
||||||
|
FormBody.Builder formBodyBuilder = new FormBody.Builder();
|
||||||
|
if (formParams != null) {
|
||||||
|
for (Map.Entry<String, Object> entry : formParams.entrySet()) {
|
||||||
|
String value = entry.getValue() != null ? String.valueOf(entry.getValue()) : "";
|
||||||
|
formBodyBuilder.add(entry.getKey(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBody body = formBodyBuilder.build();
|
||||||
|
Request.Builder requestBuilder = new Request.Builder().url(url).post(body);
|
||||||
|
|
||||||
|
if (headers != null) {
|
||||||
|
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||||
|
requestBuilder.addHeader(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Request request = requestBuilder.build();
|
||||||
|
return executeRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String put(String url, String jsonBody) throws IOException {
|
||||||
|
return put(url, jsonBody, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String put(String url, String jsonBody, Map<String, String> headers) throws IOException {
|
||||||
|
RequestBody body = RequestBody.create(jsonBody, JSON_MEDIA_TYPE);
|
||||||
|
Request.Builder requestBuilder = new Request.Builder().url(url).put(body);
|
||||||
|
|
||||||
|
if (headers != null) {
|
||||||
|
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||||
|
requestBuilder.addHeader(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Request request = requestBuilder.build();
|
||||||
|
return executeRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String delete(String url) throws IOException {
|
||||||
|
return delete(url, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String delete(String url, Map<String, String> headers) throws IOException {
|
||||||
|
Request.Builder requestBuilder = new Request.Builder().url(url).delete();
|
||||||
|
|
||||||
|
if (headers != null) {
|
||||||
|
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||||
|
requestBuilder.addHeader(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Request request = requestBuilder.build();
|
||||||
|
return executeRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String executeRequest(Request request) throws IOException {
|
||||||
|
log.debug("HTTP request: {} {}", request.method(), request.url());
|
||||||
|
|
||||||
|
try (Response response = OK_HTTP_CLIENT.newCall(request).execute()) {
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
String errorBody = response.body() != null ? response.body().string() : "";
|
||||||
|
log.error("HTTP request failed: {} {} - {} - {}",
|
||||||
|
request.method(), request.url(), response.code(), errorBody);
|
||||||
|
throw new IOException("HTTP request failed: " + response.code() + " " + errorBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseBody body = response.body();
|
||||||
|
String result = body != null ? body.string() : "";
|
||||||
|
log.debug("HTTP response: {} {} - {}", request.method(), request.url(), result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OkHttpClient getClient() {
|
||||||
|
return OK_HTTP_CLIENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.artedu.generation.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "qa")
|
||||||
|
public class QaConfig {
|
||||||
|
|
||||||
|
private String apiUrl = "http://localhost:8000/api/qa/query";
|
||||||
|
}
|
||||||
@ -1,12 +1,15 @@
|
|||||||
package com.artedu.generation.controller;
|
package com.artedu.generation.controller;
|
||||||
|
|
||||||
import com.artedu.common.result.Result;
|
import com.artedu.common.result.Result;
|
||||||
|
import com.artedu.common.util.HttpUtils;
|
||||||
import com.artedu.generation.client.LLMClient;
|
import com.artedu.generation.client.LLMClient;
|
||||||
|
import com.artedu.generation.config.QaConfig;
|
||||||
import com.artedu.generation.entity.ArchiveMessage;
|
import com.artedu.generation.entity.ArchiveMessage;
|
||||||
import com.artedu.generation.mapper.ArchiveMessageMapper;
|
import com.artedu.generation.mapper.ArchiveMessageMapper;
|
||||||
import com.artedu.generation.service.LlmModelService;
|
import com.artedu.generation.service.LlmModelService;
|
||||||
import com.artedu.generation.service.PromptEngine;
|
import com.artedu.generation.service.PromptEngine;
|
||||||
import com.artedu.generation.service.QianwenClient;
|
import com.artedu.generation.service.QianwenClient;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@ -16,12 +19,16 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
|||||||
|
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Validated
|
@Validated
|
||||||
@ -41,6 +48,9 @@ public class GenerationController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ArchiveMessageMapper archiveMessageMapper;
|
private ArchiveMessageMapper archiveMessageMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QaConfig qaConfig;
|
||||||
|
|
||||||
private final ExecutorService sseExecutor = Executors.newCachedThreadPool();
|
private final ExecutorService sseExecutor = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
private static final List<Map<String, String>> STYLES = List.of(
|
private static final List<Map<String, String>> STYLES = List.of(
|
||||||
@ -115,16 +125,54 @@ public class GenerationController {
|
|||||||
@RequestParam(value = "roomid", required = false) String roomid,
|
@RequestParam(value = "roomid", required = false) String roomid,
|
||||||
@RequestParam(value = "staffName", required = false) String staffName) {
|
@RequestParam(value = "staffName", required = false) String staffName) {
|
||||||
|
|
||||||
String chatContext = buildChatContext(fromUser, toUser, roomid);
|
List<ArchiveMessage> messages = buildChatContext(fromUser, toUser, roomid);
|
||||||
|
// 遍历消息:只取前三条是客户的消息,组装 查询知识库
|
||||||
|
String external_str = ""; //原始问题
|
||||||
|
// 历史消息上下文
|
||||||
|
List<ArchiveMessage> top15 = (messages == null || messages.isEmpty())
|
||||||
|
? new ArrayList<>()
|
||||||
|
: messages.stream().limit(15).collect(Collectors.toList());
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean is_external = true;
|
||||||
|
int count = 0;
|
||||||
|
for (ArchiveMessage msg : messages) {
|
||||||
|
sb.append(msg.getCreatedAt()).append(" ").append(msg.getFromRole()).append(" ").append(msg.getContent()).append("\n");
|
||||||
|
if ("EXTERNAL".equals(messages.get(0).getFromRole())){ //第一条消息是客户的消息 则继续
|
||||||
|
if(count<3){
|
||||||
|
if ("EXTERNAL".equals(msg.getFromRole()) && is_external){ //判断判断前三条是否只包含客户消息
|
||||||
|
external_str += msg.getContent()+",";
|
||||||
|
}
|
||||||
|
if (!"EXTERNAL".equals(msg.getFromRole())){ //判断前三条是否包含非客户的消息,如果包含,则不需要后续的消息
|
||||||
|
is_external = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
String answerNew = "";
|
||||||
|
//如果最后几条都是客户消息返回不为空,则直接返回结果,否则走原逻辑
|
||||||
|
if (!StringUtils.isBlank(external_str)){ //不为空 发起知识库的查询
|
||||||
|
String answer = getAnswer(top15, external_str);
|
||||||
|
if(!StringUtils.isBlank(answer)){
|
||||||
|
answer = answer.replaceAll("\"", "").replace("[","").replace("]","");
|
||||||
|
if(!StringUtils.isBlank(answer)){
|
||||||
|
String promptNew = promptEngine.buildAnswerPrompt(answer);
|
||||||
|
answerNew = qianwenClient.generate(promptNew, 800);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//没有命中知识库,则根据当天聊天内容生成
|
||||||
|
if(StringUtils.isBlank(answerNew)){
|
||||||
|
String chatContext = sb.toString();
|
||||||
String[] promptPair = promptEngine.buildUtterancePrompt(chatContext, staffName);
|
String[] promptPair = promptEngine.buildUtterancePrompt(chatContext, staffName);
|
||||||
String systemPrompt = promptPair[0];
|
String systemPrompt = promptPair[0];
|
||||||
String userPrompt = promptPair[1];
|
String userPrompt = promptPair[1];
|
||||||
|
|
||||||
String generated = qianwenClient.generate(systemPrompt, userPrompt, 800);
|
answerNew = qianwenClient.generate(systemPrompt, userPrompt, 800);
|
||||||
|
}
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
result.put("content", generated);
|
result.put("content", answerNew);
|
||||||
result.put("source", "LLM");
|
result.put("source", "LLM");
|
||||||
result.put("model", qianwenClient.getCurrentModel() != null
|
result.put("model", qianwenClient.getCurrentModel() != null
|
||||||
? qianwenClient.getCurrentModel().getModelName() : "default");
|
? qianwenClient.getCurrentModel().getModelName() : "default");
|
||||||
@ -132,14 +180,47 @@ public class GenerationController {
|
|||||||
return Result.success(result);
|
return Result.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildChatContext(String fromUser, String toUser, String roomid) {
|
private String getAnswer(List<ArchiveMessage> messages, String external_str){
|
||||||
|
List<String> msgInfo = messages.stream()
|
||||||
|
.map(msg -> String.join(" ",
|
||||||
|
String.valueOf(msg.getCreatedAt()),
|
||||||
|
String.valueOf(msg.getFromRole()),
|
||||||
|
String.valueOf(msg.getContent())
|
||||||
|
) + "\n").collect(Collectors.toList()) ;
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("external_str", external_str);
|
||||||
|
params.put("messages_top15", msgInfo);
|
||||||
|
try {
|
||||||
|
String jsonBody = com.artedu.common.util.JsonUtils.toJson(params);
|
||||||
|
String response = HttpUtils.post(qaConfig.getApiUrl(), jsonBody);
|
||||||
|
log.info("知识库答案:{}",response);
|
||||||
|
return response;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ArchiveMessage> buildChatContext(String fromUser, String toUser, String roomid) {
|
||||||
List<ArchiveMessage> messages;
|
List<ArchiveMessage> messages;
|
||||||
boolean isGroupChat = roomid != null && !roomid.isEmpty();
|
boolean isGroupChat = roomid != null && !roomid.isEmpty();
|
||||||
|
|
||||||
// 查最近 7 天,避免今天没聊天就找不到记录
|
// 查最近 今 天,避免今天没聊天就找不到记录
|
||||||
long startTime = get7DaysAgoStartTime();
|
long startTime = getTodayStartTime();
|
||||||
long endTime = getTodayEndTime();
|
long endTime = getTodayEndTime();
|
||||||
|
//// 指定日期:2026-05-22 todo 测试需要
|
||||||
|
// LocalDate targetDate = LocalDate.of(2026, 6, 18);
|
||||||
|
//
|
||||||
|
//// 当天 00:00:00 毫秒时间戳
|
||||||
|
// long startTime = targetDate.atStartOfDay()
|
||||||
|
// .atZone(ZoneId.systemDefault())
|
||||||
|
// .toInstant()
|
||||||
|
// .toEpochMilli();
|
||||||
|
//
|
||||||
|
//// 当天 23:59:59 毫秒时间戳
|
||||||
|
// long endTime = LocalDateTime.of(targetDate, java.time.LocalTime.of(23, 59, 59))
|
||||||
|
// .atZone(ZoneId.systemDefault())
|
||||||
|
// .toInstant()
|
||||||
|
// .toEpochMilli();
|
||||||
if (isGroupChat) {
|
if (isGroupChat) {
|
||||||
messages = archiveMessageMapper.selectGroupChatMessages(roomid, startTime, endTime);
|
messages = archiveMessageMapper.selectGroupChatMessages(roomid, startTime, endTime);
|
||||||
} else {
|
} else {
|
||||||
@ -159,14 +240,10 @@ public class GenerationController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (messages == null || messages.isEmpty()) {
|
if (messages == null || messages.isEmpty()) {
|
||||||
return null;
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
return messages;
|
||||||
for (ArchiveMessage msg : messages) {
|
|
||||||
sb.append(msg.getCreatedAt()).append(" ").append(msg.getFromRole()).append(msg.getContent()).append("\n");
|
|
||||||
}
|
|
||||||
return sb.toString().trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getTodayStartTime() {
|
private long getTodayStartTime() {
|
||||||
@ -201,7 +278,39 @@ public class GenerationController {
|
|||||||
startEvent.put("styles", STYLES);
|
startEvent.put("styles", STYLES);
|
||||||
emitter.send(SseEmitter.event().name("start").data(startEvent));
|
emitter.send(SseEmitter.event().name("start").data(startEvent));
|
||||||
|
|
||||||
String chatContext = buildChatContext(fromUser, toUser, roomid);
|
List<ArchiveMessage> messages = buildChatContext(fromUser, toUser, roomid);
|
||||||
|
// 遍历消息:只取前三条是客户的消息,组装 查询知识库
|
||||||
|
String external_str = ""; //原始问题
|
||||||
|
// 历史消息上下文
|
||||||
|
List<ArchiveMessage> top15 = (messages == null || messages.isEmpty())
|
||||||
|
? new ArrayList<>()
|
||||||
|
: messages.stream().limit(15).collect(Collectors.toList());
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean is_external = true;
|
||||||
|
for (ArchiveMessage msg : messages) {
|
||||||
|
sb.append(msg.getCreatedAt()).append(" ").append(msg.getFromRole()).append(" ").append(msg.getContent()).append("\n");
|
||||||
|
if ("EXTERNAL".equals(messages.get(0).getFromRole())){ //第一条消息是客户的消息 则继续
|
||||||
|
if ("EXTERNAL".equals(msg.getFromRole()) && is_external){ //判断只要消息不是内部人员,则后续不考虑
|
||||||
|
is_external = false;
|
||||||
|
external_str += msg.getContent()+",";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String generated = "";
|
||||||
|
//如果最后几条都是客户消息返回不为空,则直接返回结果,否则走原逻辑
|
||||||
|
if (!StringUtils.isBlank(external_str)){ //不为空 发起知识库的查询
|
||||||
|
String answer = getAnswer(top15, external_str);
|
||||||
|
if(!StringUtils.isBlank(answer)){
|
||||||
|
answer = answer.replaceAll("\"", "").replace("[","").replace("]","");
|
||||||
|
if(!StringUtils.isBlank(answer)){
|
||||||
|
String promptNew = promptEngine.buildAnswerPrompt(answer);
|
||||||
|
generated = qianwenClient.generate(promptNew, 800);
|
||||||
|
log.info("原问题:{},改写后的问题:{},命中知识库结果:{}", external_str, answer, generated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String chatContext = sb.toString();
|
||||||
if (chatContext == null || chatContext.isEmpty()) {
|
if (chatContext == null || chatContext.isEmpty()) {
|
||||||
Map<String, Object> err = new HashMap<>();
|
Map<String, Object> err = new HashMap<>();
|
||||||
err.put("type", "error");
|
err.put("type", "error");
|
||||||
@ -210,13 +319,15 @@ public class GenerationController {
|
|||||||
emitter.complete();
|
emitter.complete();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(StringUtils.isBlank(generated)){
|
||||||
String systemPrompt = promptEngine.buildSystemPrompt(chatContext, staffName);
|
String systemPrompt = promptEngine.buildSystemPrompt(chatContext, staffName);
|
||||||
String userPrompt = promptEngine.buildMultiStyleUserPrompt(staffName);
|
String userPrompt = promptEngine.buildMultiStyleUserPrompt(staffName);
|
||||||
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
String generated = qianwenClient.generate(systemPrompt, userPrompt, 1200);
|
generated = qianwenClient.generate(systemPrompt, userPrompt, 1200);
|
||||||
log.info("多风格话术生成完成, elapsed={}ms", System.currentTimeMillis() - startTime);
|
log.info("多风格话术生成完成, elapsed={}ms", System.currentTimeMillis() - startTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
List<String> utterances = parseMultiStyle(generated);
|
List<String> utterances = parseMultiStyle(generated);
|
||||||
if (utterances.size() < 3) {
|
if (utterances.size() < 3) {
|
||||||
|
|||||||
@ -31,8 +31,8 @@ public class PromptEngine {
|
|||||||
sb.append("每条话术固定三要素,缺一不可:完整话术文本、预期效果、INTERNAL 语气;\n");
|
sb.append("每条话术固定三要素,缺一不可:完整话术文本、预期效果、INTERNAL 语气;\n");
|
||||||
sb.append("单条话术文本≤100 字,整体风格专业、自然、有温度。\n");
|
sb.append("单条话术文本≤100 字,整体风格专业、自然、有温度。\n");
|
||||||
sb.append("## 单条话术固定格式(严格遵守,不改动结构)\n");
|
sb.append("## 单条话术固定格式(严格遵守,不改动结构)\n");
|
||||||
sb.append("1、李先生您好,这个产品标准版 9800/年。不过不同企业的需求差别很大,方便说一下您这边大概的使用场景和人数吗?我可以帮您推荐最合适的版本,避免买多了浪费。\n");
|
sb.append("1、您好,第九联盟(点晴科技)提供线上课程11800元/8-10个月和线下课程33800元/6个月全日制,大学生可线上+寒暑假线下结合(按线下收费)。支持包括3年回放和答疑、重读机会,以及简历辅导、模拟面试、测试指导、校招群内推等服务。\n");
|
||||||
sb.append("2、价格方面我先给您一个参考——标准版 9800/年。上周有个和您同行做电商的客户,用了我们系统后客服效率提升了 40%。要不我发一份他们的使用案例给您看看?\n");
|
sb.append("2、您好,有什么可以帮助你吗\n");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,4 +164,27 @@ public class PromptEngine {
|
|||||||
public String buildSummaryPrompt(String context) {
|
public String buildSummaryPrompt(String context) {
|
||||||
return "请对以下对话进行简要总结(50字以内),提炼学员核心需求和当前阶段:\n\n" + context;
|
return "请对以下对话进行简要总结(50字以内),提炼学员核心需求和当前阶段:\n\n" + context;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 构建话术改写提示词
|
||||||
|
*/
|
||||||
|
public String buildAnswerPrompt(String answer) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append("你是一位经验丰富的课程顾问,擅长用亲切、自然、像朋友聊天的方式跟学生/家长沟通。\n\n\n");
|
||||||
|
|
||||||
|
sb.append("【任务】把\"原内容信息\"改写成温暖、有说服力、让人愿意继续聊下去的回复。\n\n");
|
||||||
|
sb.append("【执行流程】\n");
|
||||||
|
sb.append("1. 首先检查是否有\"原内容信息\":\n");
|
||||||
|
sb.append("- 如果没有提供,或内容为空,或内容仅为系统提示本身 → 必须直接输出空,不输出任何其他文字。\n");
|
||||||
|
sb.append("- 如果提供了 → 执行步骤2\n");
|
||||||
|
sb.append("2. 润色原内容,保持字数相近,只输出润色结果。\n");
|
||||||
|
sb.append("【约束】\n");
|
||||||
|
sb.append("- 严禁在原内容为空时编造、引导、解释。\n");
|
||||||
|
sb.append("- 输出字数必须与原信息字数相近。\n");
|
||||||
|
sb.append("- 只输出润色结果,禁止输出\"好的\"\"请提供\"等额外文字。\n");
|
||||||
|
sb.append("【原内容】\n");
|
||||||
|
sb.append(answer);
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,9 @@ llm:
|
|||||||
api-key: sk-39ceb8d746014b349109e76f893acb18
|
api-key: sk-39ceb8d746014b349109e76f893acb18
|
||||||
model: qwen-plus
|
model: qwen-plus
|
||||||
|
|
||||||
|
qa:
|
||||||
|
api-url: ${QA_API_URL:http://8.159.132.53:8000/api/qa/query}
|
||||||
|
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
configuration:
|
configuration:
|
||||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
|||||||
@ -91,6 +91,11 @@
|
|||||||
<version>2.2.224</version>
|
<version>2.2.224</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
<version>4.12.0</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user