mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2026-02-04 01:25:34 +08:00
Compare commits
2 Commits
107e13c8af
...
03e6c97d80
| Author | SHA1 | Date | |
|---|---|---|---|
| 03e6c97d80 | |||
| b9f6f6dc53 |
@ -31,10 +31,28 @@
|
||||
</repositories>
|
||||
|
||||
<properties>
|
||||
<langchain4j.version>0.35.0</langchain4j.version>
|
||||
<apache-tika.version>2.9.1</apache-tika.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-bom</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-community-bom</artifactId>
|
||||
<version>1.3.0-beta9</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- system单体 api-->
|
||||
<dependency>
|
||||
@ -55,7 +73,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot3</groupId>
|
||||
<artifactId>jeecg-aiflow</artifactId>
|
||||
<version>1.2.0</version>
|
||||
<version>3.8.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- beigin 这两个依赖太多每个包50M左右,如果你发布需要使用,请把<scope>provided</scope>删掉 -->
|
||||
@ -72,7 +90,6 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- end 这两个依赖太多每个包50M左右,如果你发布需要使用,请把<scope>provided</scope>删掉 -->
|
||||
|
||||
<!-- aiflow 脚本依赖 -->
|
||||
<dependency>
|
||||
<groupId>com.yomahub</groupId>
|
||||
@ -105,17 +122,19 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- aiflow 脚本依赖 -->
|
||||
|
||||
|
||||
<!-- langChain4j model support -->
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-ollama</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
<artifactId>langchain4j-open-ai</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-zhipu-ai</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
<artifactId>langchain4j-ollama</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-community-zhipu-ai</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>checker-qual</artifactId>
|
||||
@ -129,13 +148,11 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-qianfan</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
<artifactId>langchain4j-community-qianfan</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-dashscope</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
<artifactId>langchain4j-community-dashscope</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@ -151,7 +168,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework</groupId>
|
||||
<artifactId>langchain4j-pgvector</artifactId>
|
||||
<version>${langchain4j.version}</version>
|
||||
<version>1.3.0-beta9</version>
|
||||
</dependency>
|
||||
<!-- langChain4j Document Parser -->
|
||||
<dependency>
|
||||
|
||||
@ -71,7 +71,7 @@ public class AiragAppServiceImpl extends ServiceImpl<AiragAppMapper, AiragApp> i
|
||||
AtomicBoolean isThinking = new AtomicBoolean(false);
|
||||
String requestId = UUIDGenerator.generate();
|
||||
// ai聊天响应逻辑
|
||||
tokenStream.onNext((String resMessage) -> {
|
||||
tokenStream.onPartialResponse((String resMessage) -> {
|
||||
// 兼容推理模型
|
||||
if ("<think>".equals(resMessage)) {
|
||||
isThinking.set(true);
|
||||
@ -99,9 +99,9 @@ public class AiragAppServiceImpl extends ServiceImpl<AiragAppMapper, AiragApp> i
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.onComplete((responseMessage) -> {
|
||||
.onCompleteResponse((responseMessage) -> {
|
||||
// 记录ai的回复
|
||||
AiMessage aiMessage = responseMessage.content();
|
||||
AiMessage aiMessage = responseMessage.aiMessage();
|
||||
FinishReason finishReason = responseMessage.finishReason();
|
||||
String respText = aiMessage.text();
|
||||
if (FinishReason.STOP.equals(finishReason) || null == finishReason) {
|
||||
@ -114,9 +114,6 @@ public class AiragAppServiceImpl extends ServiceImpl<AiragAppMapper, AiragApp> i
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
closeSSE(emitter, eventData);
|
||||
} else if (FinishReason.TOOL_EXECUTION.equals(finishReason)) {
|
||||
// 需要执行工具
|
||||
// TODO author: chenrui for: date:2025/3/7
|
||||
} else {
|
||||
// 异常结束
|
||||
log.error("调用模型异常:" + respText);
|
||||
|
||||
@ -860,7 +860,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
*/
|
||||
AtomicBoolean isThinking = new AtomicBoolean(false);
|
||||
// ai聊天响应逻辑
|
||||
chatStream.onNext((String resMessage) -> {
|
||||
chatStream.onPartialResponse((String resMessage) -> {
|
||||
// 兼容推理模型
|
||||
if ("<think>".equals(resMessage)) {
|
||||
isThinking.set(true);
|
||||
@ -886,12 +886,12 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
return;
|
||||
}
|
||||
sendMessage2Client(emitter, eventData);
|
||||
}).onComplete((responseMessage) -> {
|
||||
}).onCompleteResponse((responseMessage) -> {
|
||||
// 打印流程耗时日志
|
||||
printChatDuration(requestId, "LLM输出消息完成");
|
||||
AiragLocalCache.remove(AiragConsts.CACHE_TYPE_SSE_SEND_TIME, requestId);
|
||||
// 记录ai的回复
|
||||
AiMessage aiMessage = responseMessage.content();
|
||||
AiMessage aiMessage = responseMessage.aiMessage();
|
||||
FinishReason finishReason = responseMessage.finishReason();
|
||||
String respText = aiMessage.text();
|
||||
// sse
|
||||
|
||||
@ -105,14 +105,14 @@ public class AIChatHandler implements IAIChatHandler {
|
||||
// langchain4j 异常友好提示
|
||||
String errMsg = "调用大模型接口失败,详情请查看后台日志。";
|
||||
if (oConvertUtils.isNotEmpty(e.getMessage())) {
|
||||
// // 根据常见异常关键字做细致翻译
|
||||
// for (Map.Entry<String, String> entry : MODEL_ERROR_MAP.entrySet()) {
|
||||
// String key = entry.getKey();
|
||||
// String value = entry.getValue();
|
||||
// if (errMsg.contains(key)) {
|
||||
// errMsg = value;
|
||||
// }
|
||||
// }
|
||||
// 根据常见异常关键字做细致翻译
|
||||
for (Map.Entry<String, String> entry : MODEL_ERROR_MAP.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
if (errMsg.contains(key)) {
|
||||
errMsg = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.error("AI模型调用异常: {}", errMsg, e);
|
||||
throw new JeecgBootException(errMsg);
|
||||
|
||||
@ -9,7 +9,6 @@ import dev.langchain4j.data.document.splitter.DocumentSplitters;
|
||||
import dev.langchain4j.data.embedding.Embedding;
|
||||
import dev.langchain4j.data.segment.TextSegment;
|
||||
import dev.langchain4j.model.embedding.EmbeddingModel;
|
||||
import dev.langchain4j.model.openai.OpenAiTokenizer;
|
||||
import dev.langchain4j.rag.content.retriever.ContentRetriever;
|
||||
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
|
||||
import dev.langchain4j.rag.query.router.DefaultQueryRouter;
|
||||
@ -167,7 +166,7 @@ public class EmbeddingHandler implements IEmbeddingHandler {
|
||||
// 删除旧数据
|
||||
embeddingStore.removeAll(metadataKey(EMBED_STORE_METADATA_DOCID).isEqualTo(doc.getId()));
|
||||
// 分段器
|
||||
DocumentSplitter splitter = DocumentSplitters.recursive(DEFAULT_SEGMENT_SIZE, DEFAULT_OVERLAP_SIZE, new OpenAiTokenizer());
|
||||
DocumentSplitter splitter = DocumentSplitters.recursive(DEFAULT_SEGMENT_SIZE, DEFAULT_OVERLAP_SIZE);
|
||||
// 分段并存储
|
||||
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
|
||||
.documentSplitter(splitter)
|
||||
|
||||
@ -5,13 +5,11 @@ import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import dev.langchain4j.agent.tool.JsonSchemaProperty;
|
||||
import dev.langchain4j.agent.tool.ToolSpecification;
|
||||
import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
|
||||
import dev.langchain4j.service.tool.ToolExecutor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||
import org.jeecg.common.util.PasswordUtil;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.airag.llm.handler.JeecgToolsProvider;
|
||||
@ -85,12 +83,17 @@ public class JeecgBizToolsProvider implements JeecgToolsProvider {
|
||||
"\n\n - 提前使用用户名查询用户是否存在,如果存在则不能添加." +
|
||||
"\n\n - 添加成功后返回成功消息,如果失败则返回失败原因." +
|
||||
"\n\n - 用户名,工号,邮箱,手机号均要求唯一,提前通过查询用户工具确认唯一性." )
|
||||
.addParameter("username", JsonSchemaProperty.STRING, JsonSchemaProperty.description("用户名,必填,只允许使用字母、数字、下划线,且必须以字母开头,唯一"))
|
||||
.addParameter("password", JsonSchemaProperty.STRING, JsonSchemaProperty.description("用户密码,必填"))
|
||||
.addParameter("realname", JsonSchemaProperty.STRING, JsonSchemaProperty.description("真实姓名,必填"))
|
||||
.addParameter("workNo", JsonSchemaProperty.STRING, JsonSchemaProperty.description("工号,必填,唯一"))
|
||||
.addParameter("email", JsonSchemaProperty.STRING, JsonSchemaProperty.description("邮箱,必填,唯一"))
|
||||
.addParameter("phone", JsonSchemaProperty.STRING, JsonSchemaProperty.description("手机号,必填,唯一"))
|
||||
.parameters(
|
||||
JsonObjectSchema.builder()
|
||||
.addStringProperty("username", "用户名,必填,只允许使用字母、数字、下划线,且必须以字母开头,唯一")
|
||||
.addStringProperty("password", "用户密码,必填")
|
||||
.addStringProperty("realname", "真实姓名,必填")
|
||||
.addStringProperty("workNo", "工号,必填,唯一")
|
||||
.addStringProperty("email", "邮箱,必填,唯一")
|
||||
.addStringProperty("phone", "手机号,必填,唯一")
|
||||
.required("username","password","realname","workNo","email","phone")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
ToolExecutor toolExecutor = (toolExecutionRequest, memoryId) -> {
|
||||
JSONObject arguments = JSONObject.parseObject(toolExecutionRequest.arguments());
|
||||
@ -138,11 +141,15 @@ public class JeecgBizToolsProvider implements JeecgToolsProvider {
|
||||
ToolSpecification toolSpecification = ToolSpecification.builder()
|
||||
.name("query_user_by_name")
|
||||
.description("查询用户详细信息,返回json数组。支持用户名、真实姓名、邮箱、手机号、工号多字段组合查询,用户名、真实姓名、邮箱、手机号均为模糊查询,工号为精确查询。无条件则返回全部用户。")
|
||||
.addParameter("username", JsonSchemaProperty.STRING, JsonSchemaProperty.description("用户名"))
|
||||
.addParameter("realname", JsonSchemaProperty.STRING, JsonSchemaProperty.description("真实姓名"))
|
||||
.addParameter("email", JsonSchemaProperty.STRING, JsonSchemaProperty.description("电子邮件"))
|
||||
.addParameter("phone", JsonSchemaProperty.STRING, JsonSchemaProperty.description("手机号"))
|
||||
.addParameter("workNo", JsonSchemaProperty.STRING, JsonSchemaProperty.description("工号"))
|
||||
.parameters(
|
||||
JsonObjectSchema.builder()
|
||||
.addStringProperty("username", "用户名")
|
||||
.addStringProperty("realname", "真实姓名")
|
||||
.addStringProperty("email", "电子邮件")
|
||||
.addStringProperty("phone", "手机号")
|
||||
.addStringProperty("workNo", "工号")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
ToolExecutor toolExecutor = (toolExecutionRequest, memoryId) -> {
|
||||
SysUser args = JSONObject.parseObject(toolExecutionRequest.arguments(), SysUser.class);
|
||||
@ -180,8 +187,12 @@ public class JeecgBizToolsProvider implements JeecgToolsProvider {
|
||||
ToolSpecification spec = ToolSpecification.builder()
|
||||
.name("query_all_roles")
|
||||
.description("查询所有角色,返回json数组。包含字段:id、roleName、roleCode;默认按创建时间/排序号规则由后端决定。")
|
||||
.addParameter("roleName", JsonSchemaProperty.STRING, JsonSchemaProperty.description("角色姓名"))
|
||||
.addParameter("roleCode", JsonSchemaProperty.STRING, JsonSchemaProperty.description("角色编码"))
|
||||
.parameters(
|
||||
JsonObjectSchema.builder()
|
||||
.addStringProperty("roleName", "角色姓名")
|
||||
.addStringProperty("roleCode", "角色编码")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
ToolExecutor exec = (toolExecutionRequest, memoryId) -> {
|
||||
// 做租户隔离查询(若开启)
|
||||
@ -194,10 +205,10 @@ public class JeecgBizToolsProvider implements JeecgToolsProvider {
|
||||
qw.like("role_code", sysRole.getRoleCode());
|
||||
}
|
||||
// 未删除
|
||||
List<org.jeecg.modules.system.entity.SysRole> roles = sysRoleService.list(qw);
|
||||
List<SysRole> roles = sysRoleService.list(qw);
|
||||
// 仅返回核心字段
|
||||
JSONArray arr = new JSONArray();
|
||||
for (org.jeecg.modules.system.entity.SysRole r : roles) {
|
||||
for (SysRole r : roles) {
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("id", r.getId());
|
||||
o.put("roleName", r.getRoleName());
|
||||
@ -219,17 +230,22 @@ public class JeecgBizToolsProvider implements JeecgToolsProvider {
|
||||
ToolSpecification spec = ToolSpecification.builder()
|
||||
.name("grant_user_roles")
|
||||
.description("给用户授予角色,支持一次授予多个角色;如果关系已存在则跳过。返回授予结果统计。")
|
||||
.addParameter("userId", JsonSchemaProperty.STRING, JsonSchemaProperty.description("用户ID,必填"))
|
||||
.addParameter("roleIds", JsonSchemaProperty.STRING, JsonSchemaProperty.description("角色ID列表,必填,使用英文逗号分隔"))
|
||||
.parameters(
|
||||
JsonObjectSchema.builder()
|
||||
.addStringProperty("userId", "用户ID,必填")
|
||||
.addStringProperty("roleIds", "角色ID列表,必填,使用英文逗号分隔")
|
||||
.required("userId","roleIds")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
ToolExecutor exec = (toolExecutionRequest, memoryId) -> {
|
||||
JSONObject args = JSONObject.parseObject(toolExecutionRequest.arguments());
|
||||
String userId = args.getString("userId");
|
||||
String roleIdsStr = args.getString("roleIds");
|
||||
if (org.apache.commons.lang3.StringUtils.isAnyBlank(userId, roleIdsStr)) {
|
||||
if (StringUtils.isAnyBlank(userId, roleIdsStr)) {
|
||||
return "参数缺失:userId 或 roleIds";
|
||||
}
|
||||
org.jeecg.modules.system.entity.SysUser user = sysUserService.getById(userId);
|
||||
SysUser user = sysUserService.getById(userId);
|
||||
if (user == null) {
|
||||
return "用户不存在:" + userId;
|
||||
}
|
||||
@ -238,9 +254,9 @@ public class JeecgBizToolsProvider implements JeecgToolsProvider {
|
||||
for (String roleId : roleIds) {
|
||||
roleId = roleId.trim();
|
||||
if (roleId.isEmpty()) continue;
|
||||
org.jeecg.modules.system.entity.SysRole role = sysRoleService.getById(roleId);
|
||||
SysRole role = sysRoleService.getById(roleId);
|
||||
if (role == null) { invalid++; continue; }
|
||||
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<org.jeecg.modules.system.entity.SysUserRole> q = new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
|
||||
QueryWrapper<org.jeecg.modules.system.entity.SysUserRole> q = new QueryWrapper<>();
|
||||
q.eq("role_id", roleId).eq("user_id", userId);
|
||||
org.jeecg.modules.system.entity.SysUserRole one = sysUserRoleService.getOne(q);
|
||||
if (one == null) {
|
||||
|
||||
@ -524,7 +524,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot3</groupId>
|
||||
<artifactId>jeecg-boot-starter-chatgpt</artifactId>
|
||||
<version>${jeecgboot.version}</version>
|
||||
<version>3.8.3.1</version>
|
||||
</dependency>
|
||||
<!--flyway 支持 mysql5.7+、MariaDB10.3.16-->
|
||||
<!--mysql5.6,需要把版本号改成5.2.1-->
|
||||
|
||||
Reference in New Issue
Block a user