ai对话优化知识库选择

This commit is contained in:
2025-12-29 18:40:26 +08:00
parent a33720b9f6
commit 4b6cb726d2
14 changed files with 249 additions and 81 deletions

View File

@@ -732,6 +732,56 @@ public class DifyApiClient {
}
}
/**
* 获取对话变量
* @param conversationId 会话ID
* @param userId 用户标识
* @param lastId 当前页最后面一条记录的ID,默认null
* @param limit 一次请求返回多少条记录,默认20条,最大100条,最小1条
* @param apiKey API密钥
* @return 对话变量响应
*/
public ConversationVariablesResponse getConversationVariables(
String conversationId,
String userId,
String lastId,
Integer limit,
String apiKey) {
StringBuilder urlBuilder = new StringBuilder(
difyConfig.getFullApiUrl("/conversations/" + conversationId + "/variables"));
urlBuilder.append("?user=").append(userId);
if (lastId != null && !lastId.isEmpty()) {
urlBuilder.append("&last_id=").append(lastId);
}
if (limit != null) {
urlBuilder.append("&limit=").append(limit);
}
try {
Request httpRequest = new Request.Builder()
.url(urlBuilder.toString())
.header("Authorization", "Bearer " + getApiKey(apiKey))
.get()
.build();
try (Response response = httpClient.newCall(httpRequest).execute()) {
String responseBody = response.body() != null ? response.body().string() : "";
if (!response.isSuccessful()) {
logger.error("获取对话变量失败: {} - {}", response.code(), responseBody);
throw new DifyException("获取对话变量失败: " + responseBody);
}
return JSON.parseObject(responseBody, ConversationVariablesResponse.class);
}
} catch (IOException e) {
logger.error("获取对话变量异常", e);
throw new DifyException("获取对话变量异常: " + e.getMessage(), e);
}
}
// ===================== 通用 HTTP 方法(用于代理转发)=====================
/**

View File

@@ -0,0 +1,69 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
/**
* @description 对话变量响应
* @filename ConversationVariablesResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-12-29
*/
@Data
public class ConversationVariablesResponse {
private Integer limit;
@JSONField(name = "has_more")
private Boolean hasMore;
private List<ConversationVariableItem> data;
/**
* 对话中的变量项
*/
@Data
public static class ConversationVariableItem {
/**
* 变量ID
*/
private String id;
/**
* 变量名称
*/
private String name;
/**
* 变量类型 (string, number, boolean 等)
*/
@JSONField(name = "value_type")
private String valueType;
/**
* 变量值
*/
private String value;
/**
* 变量描述
*/
private String description;
/**
* 创建时间戳
*/
@JSONField(name = "created_at")
private Long createdAt;
/**
* 最后更新时间戳
*/
@JSONField(name = "updated_at")
private Long updatedAt;
}
}

View File

@@ -8,6 +8,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import org.xyzh.ai.client.dto.ConversationVariablesResponse;
import org.xyzh.api.ai.dto.ChatPrepareData;
import org.xyzh.api.ai.dto.TbChat;
import org.xyzh.api.ai.dto.TbChatMessage;
@@ -46,6 +47,9 @@ public class ChatController {
@Autowired
private AIFileUploadService fileUploadService;
@Autowired
private org.xyzh.ai.client.DifyApiClient difyApiClient;
// ====================== 会话管理 ======================
/**
@@ -151,7 +155,7 @@ public class ChatController {
* @since 2025-12-17
*/
@PostMapping("/conversation/page")
public ResultDomain<PageDomain<TbChat>> getChatPage(@RequestBody PageRequest<TbChat> pageRequest, @RequestHeader("Authorization") String token) {
public ResultDomain<TbChat> getChatPage(@RequestBody PageRequest<TbChat> pageRequest, @RequestHeader("Authorization") String token) {
log.info("分页获取会话列表: agentId={}", pageRequest.getFilter().getAgentId());
pageRequest.getFilter().setUserType(false);
@@ -164,6 +168,53 @@ public class ChatController {
return chatService.getChatPage(pageRequest);
}
/**
* @description 获取对话变量
* @param params 请求参数包含agentId, conversationId, userId, lastId, limit
* @author yslg
* @since 2025-12-29
*/
@PostMapping("/conversation/variables")
public ResultDomain<org.xyzh.ai.client.dto.ConversationVariablesResponse> getConversationVariables(
@RequestBody Map<String, Object> params,
@RequestHeader("Authorization") String token) {
// 参数验证
ValidationResult result = ValidationUtils.validateMap(params, Arrays.asList(
ValidationUtils.requiredString("agentId", "智能体ID", 1, 100),
ValidationUtils.requiredString("conversationId", "会话ID", 1, 100),
ValidationUtils.requiredString("userId", "用户ID", 1, 100)
));
if (!result.isValid()) {
return ResultDomain.failure(result.getAllErrors());
}
String agentId = (String) params.get("agentId");
String conversationId = (String) params.get("conversationId");
String userId = (String) params.get("userId");
String lastId = params.containsKey("lastId") ? (String) params.get("lastId") : null;
Integer limit = params.containsKey("limit") ?
Integer.parseInt(params.get("limit").toString()) : 20;
log.info("获取对话变量: agentId={}, conversationId={}, userId={}", agentId, conversationId, userId);
try {
// 获取智能体信息以获取 API Key
// 这里需要根据 agentId 获取对应的 API Key
// 暂时先使用一个占位符,实际使用时需要从数据库或配置中获取
// 或者通过 chatService 获取智能体配置
// 调用 Dify API 获取会话变量
ConversationVariablesResponse response =
difyApiClient.getConversationVariables(conversationId, userId, lastId, limit, agentId);
return ResultDomain.success("获取对话变量成功",response);
} catch (Exception e) {
log.error("获取对话变量失败", e);
return ResultDomain.failure("获取对话变量失败: " + e.getMessage());
}
}
// ====================== 消息管理 ======================
/**

View File

@@ -1,5 +1,6 @@
package org.xyzh.ai.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.apache.dubbo.config.annotation.DubboService;
@@ -12,6 +13,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import org.xyzh.ai.client.DifyApiClient;
import org.xyzh.ai.client.callback.StreamCallback;
import org.xyzh.ai.client.dto.ChatRequest;
import org.xyzh.ai.config.DifyConfig;
import org.xyzh.ai.mapper.TbChatMapper;
import org.xyzh.ai.mapper.TbChatMessageMapper;
import org.xyzh.api.ai.dto.ChatPrepareData;
@@ -19,17 +21,23 @@ import org.xyzh.api.ai.dto.DifyFileInfo;
import org.xyzh.api.ai.dto.TbAgent;
import org.xyzh.api.ai.dto.TbChat;
import org.xyzh.api.ai.dto.TbChatMessage;
import org.xyzh.api.ai.dto.TbKnowledge;
import org.xyzh.api.ai.service.AgentChatService;
import org.xyzh.api.ai.service.AgentService;
import org.xyzh.api.ai.service.KnowledgeService;
import org.xyzh.api.system.service.GuestService;
import org.xyzh.common.core.domain.LoginDomain;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageDomain;
import org.xyzh.common.core.page.PageParam;
import org.xyzh.common.core.page.PageRequest;
import org.xyzh.common.redis.service.RedisService;
import org.xyzh.common.utils.NonUtils;
import org.xyzh.common.utils.id.IdUtil;
import org.xyzh.common.auth.utils.LoginUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -69,20 +77,11 @@ public class AgentChatServiceImpl implements AgentChatService {
@Autowired
private RedisService redisService;
/**
* @description 根据 userType 获取用户ID
* @param chat 会话信息(包含 userId 和 userType
* @return 真实的系统用户ID
*/
private String getUserIdByType(TbChat chat) {
if (!chat.getUserType()) {
// 来客userType=false直接返回传入的 userId已经是真正的系统 userId
return chat.getUserId();
} else {
// 员工userType=true从登录信息获取 userId
return LoginUtil.getCurrentUserId();
}
}
@Autowired
private KnowledgeService knowledgeService;
@Autowired
private DifyConfig difyConfig;
/**
* @description 判断智能体是否是outer
@@ -130,7 +129,8 @@ public class AgentChatServiceImpl implements AgentChatService {
}
// 2. 获取用户ID并校验权限
String userId = getUserIdByType(chat);
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
String userId = loginDomain.getUser().getUserId();
if (userId == null) {
return ResultDomain.failure("用户信息获取失败");
}
@@ -164,7 +164,8 @@ public class AgentChatServiceImpl implements AgentChatService {
return ResultDomain.failure("智能体不可用");
}
// 2. 获取用户ID并校验权限
String userId = getUserIdByType(filter);
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
String userId = loginDomain.getUser().getUserId();
if (userId == null) {
return ResultDomain.failure("用户信息获取失败");
}
@@ -189,12 +190,9 @@ public class AgentChatServiceImpl implements AgentChatService {
@Override
public ResultDomain<TbChat> getChatList(TbChat filter) {
// 判断agent是否是outer
if(!isOuterAgent(filter.getAgentId())){
return ResultDomain.failure("智能体不可用");
}
// 获取用户ID
String userId = getUserIdByType(filter);
String userId = LoginUtil.getCurrentUserId();
if (userId == null) {
return ResultDomain.failure("用户信息获取失败");
}
@@ -204,16 +202,16 @@ public class AgentChatServiceImpl implements AgentChatService {
}
@Override
public ResultDomain<PageDomain<TbChat>> getChatPage(PageRequest<TbChat> pageRequest) {
public ResultDomain<TbChat> getChatPage(PageRequest<TbChat> pageRequest) {
TbChat filter = pageRequest.getFilter();
// 判断agent是否是outer来客才需要校验
if (!filter.getUserType() && !isOuterAgent(filter.getAgentId())) {
return ResultDomain.<PageDomain<TbChat>>failure("智能体不可用");
return ResultDomain.<TbChat>failure("智能体不可用");
}
// 获取用户ID
String userId = getUserIdByType(filter);
String userId = LoginUtil.getCurrentUserId();
if (userId == null) {
return ResultDomain.<PageDomain<TbChat>>failure("用户信息获取失败");
return ResultDomain.<TbChat>failure("用户信息获取失败");
}
filter.setUserId(userId);
@@ -224,7 +222,7 @@ public class AgentChatServiceImpl implements AgentChatService {
pageParam.setTotal((int) total);
PageDomain<TbChat> pageDomain = new PageDomain<>(pageParam, chatList);
return ResultDomain.<PageDomain<TbChat>>success("查询成功", pageDomain);
return ResultDomain.<TbChat>success("查询成功", pageDomain);
}
// ====================== 智能体聊天管理 ======================
@@ -241,7 +239,7 @@ public class AgentChatServiceImpl implements AgentChatService {
return ResultDomain.failure("智能体不可用");
}
// 2. 获取用户ID并校验权限
String userId = getUserIdByType(filter);
String userId = LoginUtil.getCurrentUserId();
if (userId == null) {
return ResultDomain.failure("用户信息获取失败");
}
@@ -272,7 +270,8 @@ public class AgentChatServiceImpl implements AgentChatService {
chatFilter.setUserId(prepareData.getUserId());
chatFilter.setUserType(prepareData.getUserType());
String userId = getUserIdByType(chatFilter);
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
String userId = loginDomain.getUser().getUserId();
if (userId == null) {
return ResultDomain.failure("用户信息获取失败");
}
@@ -299,6 +298,8 @@ public class AgentChatServiceImpl implements AgentChatService {
sessionData.put("userId", userId);
sessionData.put("filesData", prepareData.getFiles());
sessionData.put("apiKey", agent.getApiKey());
sessionData.put("outer", agent.getIsOuter());
sessionData.put("service", prepareData.getService());
String cacheKey = CHAT_SESSION_PREFIX + sessionId;
redisService.set(cacheKey, sessionData, SESSION_TTL, TimeUnit.SECONDS);
@@ -332,6 +333,9 @@ public class AgentChatServiceImpl implements AgentChatService {
String query = (String) sessionData.get("query");
String userId = (String) sessionData.get("userId");
String apiKey = (String) sessionData.get("apiKey");
String service = (String) sessionData.get("service");
Boolean outer = (Boolean) sessionData.get("outer");
@SuppressWarnings("unchecked")
List<DifyFileInfo> filesData = (List<DifyFileInfo>) sessionData.get("filesData");
@@ -365,7 +369,23 @@ public class AgentChatServiceImpl implements AgentChatService {
chatRequest.setQuery(query);
chatRequest.setUser(userId);
chatRequest.setResponseMode("streaming");
chatRequest.setInputs(new HashMap<>()); // Dify API 要求 inputs 必传
Map<String, Object> inputsMap = new HashMap<>();
chatRequest.setInputs(inputsMap); // Dify API 要求 inputs 必传
// 处理动态知识库的问题
if(outer && NonUtils.isNotEmpty(service)){
TbKnowledge filter = new TbKnowledge();
filter.setService(service);
filter.setCategory("external");
ResultDomain<TbKnowledge> knowledgeRD = knowledgeService.listKnowledges(filter);
List<String> datasets = new ArrayList<>();
if(knowledgeRD.getSuccess()){
datasets = knowledgeRD.getDataList().stream().map(TbKnowledge::getDifyDatasetId).toList();
}
inputsMap.put("datasets", JSON.toJSONString(datasets));
inputsMap.put("dataset_apikey", difyConfig.getKnowledgeApiKey());
}
if (filesData != null && !filesData.isEmpty()) {
chatRequest.setFiles(filesData);
@@ -454,7 +474,7 @@ public class AgentChatServiceImpl implements AgentChatService {
TbAgent agent = agentResult.getData();
// 2. 获取用户ID
String userId = getUserIdByType(filter);
String userId = LoginUtil.getCurrentUserId();
if (userId == null) {
return ResultDomain.failure("用户信息获取失败");
}
@@ -480,7 +500,7 @@ public class AgentChatServiceImpl implements AgentChatService {
}
// 2. 获取用户ID
String userId = getUserIdByType(filter);
String userId = LoginUtil.getCurrentUserId();
if (userId == null) {
return ResultDomain.failure("用户信息获取失败");
}

View File

@@ -29,4 +29,6 @@ public class ChatPrepareData implements Serializable {
@Schema(description = "用户类型false=来客true=员工)")
private Boolean userType;
@Schema(description = "服务名称")
private String service;
}

View File

@@ -48,7 +48,7 @@ public interface AgentChatService {
* @param pageRequest 分页请求参数
* @return 分页会话列表
*/
ResultDomain<PageDomain<TbChat>> getChatPage(PageRequest<TbChat> pageRequest);
ResultDomain<TbChat> getChatPage(PageRequest<TbChat> pageRequest);
// ====================== 智能体聊天管理 ======================

View File

@@ -0,0 +1,8 @@
package org.xyzh.api.system.constance;
/**
* 通过redis事件实现数据库更新配置更新其他服务的bean数据
*/
public class SysConfigRedisPrefix {
public static final String SYS_CONFIG_DIFY="sys:config:dify";
}

View File

@@ -88,6 +88,7 @@ export interface ChatPrepareData {
userId?: string
/** 用户类型false=来客true=员工) */
userType?: boolean
service?: string
}
// ==================== 请求参数类型(必传校验) ====================
@@ -106,24 +107,6 @@ export interface CreateChatParam {
title?: string
}
/**
* 准备流式对话参数
*/
export interface PrepareChatParam {
/** 对话ID必传 */
chatId: string
/** 用户问题(必传) */
query: string
/** 智能体ID必传 */
agentId: string
/** 用户类型(必传) */
userType: boolean
/** 用户ID */
userId?: string
/** 文件列表 */
files?: DifyFileInfo[]
}
/**
* 停止对话参数
*/

View File

@@ -131,7 +131,6 @@ declare module 'shared/types' {
DifyFileInfo,
ChatPrepareData,
CreateChatParam,
PrepareChatParam,
StopChatParam,
CommentMessageParam,
ChatListParam,

View File

@@ -275,7 +275,7 @@ import type {
TbChat,
TbChatMessage,
TbAgent,
PrepareChatParam,
ChatPrepareData,
SSEMessageData,
DifyFileInfo,
TbSysFileDTO
@@ -479,13 +479,14 @@ const sendMessage = async () => {
}
// 准备流式对话参数
const prepareParam: PrepareChatParam = {
const prepareParam: ChatPrepareData = {
chatId: currentChatId.value!,
query: query,
agentId: agentId,
userType: userType.value,
userId: userId.value,
files: uploadedFiles.value.length > 0 ? uploadedFiles.value : undefined
files: uploadedFiles.value.length > 0 ? uploadedFiles.value : undefined,
service: "workcase"
}
// 清空已上传的文件

View File

@@ -4,7 +4,7 @@ import type {
TbChat,
TbChatMessage,
CreateChatParam,
PrepareChatParam,
ChatPrepareData,
StopChatParam,
CommentMessageParam,
ChatListParam,
@@ -72,7 +72,7 @@ export const aiChatAPI = {
* 准备流式对话会话
* @param param agentId、chatId、query、userId 必传
*/
prepareChatMessageSession(param: PrepareChatParam): Promise<ResultDomain<string>> {
prepareChatMessageSession(param: ChatPrepareData): Promise<ResultDomain<string>> {
return request<string>({ url: `${this.baseUrl}/stream/prepare`, method: 'POST', data: param })
},

View File

@@ -16,7 +16,7 @@ import type {
TbChat,
TbChatMessage,
CreateChatParam,
PrepareChatParam,
ChatPrepareData,
StopChatParam,
CommentMessageParam,
ChatListParam,

View File

@@ -354,13 +354,14 @@
}
// 准备流式对话(包含文件)
const prepareData = {
const prepareData: ChatPrepareData = {
chatId: chatId.value,
query: query,
agentId: agentId,
userType: userType.value,
userId: userInfo.value.userId,
files: files.length > 0 ? files : undefined
files: files.length > 0 ? files : undefined,
service: "workcase"
}
console.log('准备流式对话参数:', JSON.stringify(prepareData))

View File

@@ -87,6 +87,7 @@ export interface ChatPrepareData {
userId?: string
/** 用户类型false=来客true=员工) */
userType?: boolean
service?: string
}
/**
@@ -125,23 +126,6 @@ export interface CreateChatParam {
title?: string
}
/**
* 准备流式对话参数
*/
export interface PrepareChatParam {
/** 对话ID必传 */
chatId: string
/** 用户问题(必传) */
query: string
/** 智能体ID */
agentId: string
userType: boolean
/** 用户ID */
userId?: string
/** 用户类型 */
/** 文件列表 */
files?: DifyFileInfo[]
}
/**
* 停止对话参数