feat: 全新智能体功能
- 基于先进智能体框架,增加智能体编排功能 - 增加智能体聊天,并对接持久化
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package tech.easyflow.agent.service;
|
||||
|
||||
import tech.easyflow.agent.entity.Agent;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Agent 审批状态派生服务。
|
||||
*/
|
||||
public interface AgentApprovalStateService {
|
||||
|
||||
/**
|
||||
* 填充单个 Agent 的审批展示状态。
|
||||
*
|
||||
* @param agent Agent 资源
|
||||
*/
|
||||
void fillAgentApprovalState(Agent agent);
|
||||
|
||||
/**
|
||||
* 批量填充 Agent 的审批展示状态。
|
||||
*
|
||||
* @param agents Agent 资源集合
|
||||
*/
|
||||
void fillAgentApprovalState(Collection<Agent> agents);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package tech.easyflow.agent.service;
|
||||
|
||||
import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.agent.entity.AgentCategory;
|
||||
|
||||
/**
|
||||
* Agent 分类服务。
|
||||
*/
|
||||
public interface AgentCategoryService extends IService<AgentCategory> {
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package tech.easyflow.agent.service;
|
||||
|
||||
import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.agent.entity.AgentKnowledgeBinding;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Agent 知识库绑定服务。
|
||||
*/
|
||||
public interface AgentKnowledgeBindingService extends IService<AgentKnowledgeBinding> {
|
||||
|
||||
/**
|
||||
* 替换 Agent 知识库绑定。
|
||||
*
|
||||
* @param agentId Agent ID
|
||||
* @param bindings 新绑定列表
|
||||
* @return 保存后的绑定列表
|
||||
*/
|
||||
List<AgentKnowledgeBinding> replaceBindings(BigInteger agentId, List<AgentKnowledgeBinding> bindings);
|
||||
|
||||
/**
|
||||
* 查询 Agent 启用知识库绑定。
|
||||
*
|
||||
* @param agentId Agent ID
|
||||
* @return 启用绑定列表
|
||||
*/
|
||||
List<AgentKnowledgeBinding> listEnabled(BigInteger agentId);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package tech.easyflow.agent.service;
|
||||
|
||||
import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.agent.entity.Agent;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Agent 配置服务。
|
||||
*/
|
||||
public interface AgentService extends IService<Agent> {
|
||||
|
||||
/**
|
||||
* 获取 Agent 详情。
|
||||
*
|
||||
* @param id Agent ID
|
||||
* @return Agent 详情
|
||||
*/
|
||||
Agent getDetail(BigInteger id);
|
||||
|
||||
/**
|
||||
* 保存草稿 Agent。
|
||||
*
|
||||
* @param agent Agent 草稿
|
||||
* @return 保存后的 Agent
|
||||
*/
|
||||
Agent saveDraft(Agent agent);
|
||||
|
||||
/**
|
||||
* 更新草稿 Agent。
|
||||
*
|
||||
* @param agent Agent 草稿
|
||||
* @return 更新后的 Agent
|
||||
*/
|
||||
Agent updateDraft(Agent agent);
|
||||
|
||||
/**
|
||||
* 获取已发布运行视图。
|
||||
*
|
||||
* @param id Agent ID
|
||||
* @return 已发布运行视图
|
||||
*/
|
||||
Agent getPublishedView(BigInteger id);
|
||||
|
||||
/**
|
||||
* 构建发布快照。
|
||||
*
|
||||
* @param agent Agent 当前草稿
|
||||
* @return 发布快照
|
||||
*/
|
||||
Map<String, Object> buildPublishSnapshot(Agent agent);
|
||||
|
||||
/**
|
||||
* 从快照还原运行视图。
|
||||
*
|
||||
* @param snapshot 发布快照
|
||||
* @return Agent 运行视图
|
||||
*/
|
||||
Agent fromSnapshot(Map<String, Object> snapshot);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package tech.easyflow.agent.service;
|
||||
|
||||
import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.agent.entity.AgentToolBinding;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Agent 工具绑定服务。
|
||||
*/
|
||||
public interface AgentToolBindingService extends IService<AgentToolBinding> {
|
||||
|
||||
/**
|
||||
* 替换 Agent 工具绑定。
|
||||
*
|
||||
* @param agentId Agent ID
|
||||
* @param bindings 新绑定列表
|
||||
* @return 保存后的绑定列表
|
||||
*/
|
||||
List<AgentToolBinding> replaceBindings(BigInteger agentId, List<AgentToolBinding> bindings);
|
||||
|
||||
/**
|
||||
* 查询 Agent 启用工具绑定。
|
||||
*
|
||||
* @param agentId Agent ID
|
||||
* @return 启用绑定列表
|
||||
*/
|
||||
List<AgentToolBinding> listEnabled(BigInteger agentId);
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package tech.easyflow.agent.service.impl;
|
||||
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import tech.easyflow.agent.entity.Agent;
|
||||
import tech.easyflow.agent.service.AgentApprovalStateService;
|
||||
import tech.easyflow.ai.enums.PublishStatus;
|
||||
import tech.easyflow.approval.entity.ApprovalInstance;
|
||||
import tech.easyflow.approval.enums.ApprovalActionType;
|
||||
import tech.easyflow.approval.enums.ApprovalInstanceStatus;
|
||||
import tech.easyflow.approval.enums.ApprovalResourceType;
|
||||
import tech.easyflow.approval.mapper.ApprovalInstanceMapper;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Agent 审批状态派生服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class AgentApprovalStateServiceImpl implements AgentApprovalStateService {
|
||||
|
||||
private final ApprovalInstanceMapper approvalInstanceMapper;
|
||||
|
||||
/**
|
||||
* 创建 Agent 审批状态派生服务。
|
||||
*
|
||||
* @param approvalInstanceMapper 审批实例 Mapper
|
||||
*/
|
||||
public AgentApprovalStateServiceImpl(ApprovalInstanceMapper approvalInstanceMapper) {
|
||||
this.approvalInstanceMapper = approvalInstanceMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void fillAgentApprovalState(Agent agent) {
|
||||
fillAgentApprovalState(agent == null ? List.of() : List.of(agent));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void fillAgentApprovalState(Collection<Agent> agents) {
|
||||
if (CollectionUtils.isEmpty(agents)) {
|
||||
return;
|
||||
}
|
||||
List<Agent> validAgents = agents.stream().filter(Objects::nonNull).toList();
|
||||
if (validAgents.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<BigInteger, ApprovalInstance> instanceMap = loadInstanceMap(validAgents, Agent::getCurrentApprovalInstanceId);
|
||||
for (Agent agent : validAgents) {
|
||||
fillOne(agent, instanceMap.get(agent.getCurrentApprovalInstanceId()));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillOne(Agent agent, ApprovalInstance instance) {
|
||||
PublishStatus currentStatus = PublishStatus.from(agent.getPublishStatus());
|
||||
if (!isValidCurrentInstance(instance)) {
|
||||
agent.setApprovalPending(false);
|
||||
agent.setCurrentApprovalActionType(null);
|
||||
agent.setDisplayPublishStatus(currentStatus.getCode());
|
||||
return;
|
||||
}
|
||||
ApprovalInstanceStatus instanceStatus = ApprovalInstanceStatus.from(instance.getStatus());
|
||||
if (instanceStatus.isFinished()) {
|
||||
agent.setApprovalPending(false);
|
||||
agent.setCurrentApprovalActionType(null);
|
||||
agent.setDisplayPublishStatus(currentStatus.getCode());
|
||||
return;
|
||||
}
|
||||
ApprovalActionType actionType = ApprovalActionType.from(instance.getActionType());
|
||||
agent.setApprovalPending(true);
|
||||
agent.setCurrentApprovalActionType(actionType.getCode());
|
||||
agent.setDisplayPublishStatus(resolveDisplayStatusWithActiveInstance(currentStatus, actionType).getCode());
|
||||
}
|
||||
|
||||
private Map<BigInteger, ApprovalInstance> loadInstanceMap(Collection<Agent> agents,
|
||||
Function<Agent, BigInteger> instanceIdGetter) {
|
||||
Set<BigInteger> instanceIds = agents.stream()
|
||||
.map(instanceIdGetter)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
if (instanceIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<ApprovalInstance> instances = approvalInstanceMapper.selectListByQuery(
|
||||
QueryWrapper.create().in(ApprovalInstance::getId, instanceIds)
|
||||
);
|
||||
return instances.stream().collect(Collectors.toMap(ApprovalInstance::getId, Function.identity()));
|
||||
}
|
||||
|
||||
private boolean isValidCurrentInstance(ApprovalInstance instance) {
|
||||
return instance != null && ApprovalResourceType.AGENT.getCode().equals(instance.getResourceType());
|
||||
}
|
||||
|
||||
private PublishStatus resolveDisplayStatusWithActiveInstance(PublishStatus currentStatus,
|
||||
ApprovalActionType actionType) {
|
||||
if (currentStatus == PublishStatus.PUBLISH_PENDING
|
||||
|| currentStatus == PublishStatus.OFFLINE_PENDING
|
||||
|| currentStatus == PublishStatus.DELETE_PENDING) {
|
||||
return currentStatus;
|
||||
}
|
||||
return switch (actionType) {
|
||||
case PUBLISH -> PublishStatus.PUBLISH_PENDING;
|
||||
case OFFLINE -> PublishStatus.OFFLINE_PENDING;
|
||||
case DELETE -> PublishStatus.DELETE_PENDING;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package tech.easyflow.agent.service.impl;
|
||||
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.agent.entity.AgentCategory;
|
||||
import tech.easyflow.agent.mapper.AgentCategoryMapper;
|
||||
import tech.easyflow.agent.service.AgentCategoryService;
|
||||
|
||||
/**
|
||||
* Agent 分类服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class AgentCategoryServiceImpl extends ServiceImpl<AgentCategoryMapper, AgentCategory> implements AgentCategoryService {
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package tech.easyflow.agent.service.impl;
|
||||
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.agent.entity.Agent;
|
||||
import tech.easyflow.agent.entity.AgentKnowledgeBinding;
|
||||
import tech.easyflow.agent.mapper.AgentKnowledgeBindingMapper;
|
||||
import tech.easyflow.agent.mapper.AgentMapper;
|
||||
import tech.easyflow.agent.service.AgentKnowledgeBindingService;
|
||||
import tech.easyflow.ai.entity.DocumentCollection;
|
||||
import tech.easyflow.ai.enums.PublishStatus;
|
||||
import tech.easyflow.ai.rag.KnowledgeRetrievalModes;
|
||||
import tech.easyflow.ai.service.DocumentCollectionService;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
import tech.easyflow.common.satoken.util.SaTokenUtil;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.system.enums.CategoryResourceType;
|
||||
import tech.easyflow.system.enums.ResourceAction;
|
||||
import tech.easyflow.system.service.ResourceAccessService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Agent 知识库绑定服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class AgentKnowledgeBindingServiceImpl extends ServiceImpl<AgentKnowledgeBindingMapper, AgentKnowledgeBinding>
|
||||
implements AgentKnowledgeBindingService {
|
||||
|
||||
private static final String DEFAULT_RETRIEVAL_MODE = "HYBRID";
|
||||
|
||||
@Resource
|
||||
private AgentMapper agentMapper;
|
||||
@Resource
|
||||
private DocumentCollectionService documentCollectionService;
|
||||
@Resource
|
||||
private ResourceAccessService resourceAccessService;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<AgentKnowledgeBinding> replaceBindings(BigInteger agentId, List<AgentKnowledgeBinding> bindings) {
|
||||
Agent agent = requireAgent(agentId);
|
||||
resourceAccessService.assertAccess(CategoryResourceType.AGENT, agent, ResourceAction.MANAGE, "无权限管理该 Agent");
|
||||
remove(QueryWrapper.create().where("agent_id = ?", agentId));
|
||||
if (bindings == null || bindings.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
for (int i = 0; i < bindings.size(); i++) {
|
||||
AgentKnowledgeBinding binding = bindings.get(i);
|
||||
validateBinding(binding);
|
||||
applyBindingDefaults(agent, binding, i);
|
||||
}
|
||||
saveBatch(bindings);
|
||||
return listEnabled(agentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<AgentKnowledgeBinding> listEnabled(BigInteger agentId) {
|
||||
return list(QueryWrapper.create()
|
||||
.where("agent_id = ?", agentId)
|
||||
.and("enabled = ?", true)
|
||||
.orderBy("sort_no asc, id asc"));
|
||||
}
|
||||
|
||||
private Agent requireAgent(BigInteger agentId) {
|
||||
Agent agent = agentMapper.selectOneById(agentId);
|
||||
if (agent == null) {
|
||||
throw new BusinessException("Agent 不存在");
|
||||
}
|
||||
return agent;
|
||||
}
|
||||
|
||||
private void validateBinding(AgentKnowledgeBinding binding) {
|
||||
if (binding == null || binding.getKnowledgeId() == null) {
|
||||
throw new BusinessException("知识库绑定参数不完整");
|
||||
}
|
||||
DocumentCollection knowledge = documentCollectionService.getById(binding.getKnowledgeId());
|
||||
if (knowledge == null || PublishStatus.from(knowledge.getPublishStatus()) != PublishStatus.PUBLISHED) {
|
||||
throw new BusinessException("绑定知识库不存在或未发布");
|
||||
}
|
||||
KnowledgeRetrievalModes.parse(binding.getRetrievalMode());
|
||||
resourceAccessService.assertAccess(CategoryResourceType.KNOWLEDGE, knowledge, ResourceAction.USE, "无权限绑定该知识库");
|
||||
}
|
||||
|
||||
private void applyBindingDefaults(Agent agent, AgentKnowledgeBinding binding, int index) {
|
||||
LoginAccount account = requireCurrentLoginAccount();
|
||||
Date now = new Date();
|
||||
binding.setId(null);
|
||||
binding.setTenantId(agent.getTenantId());
|
||||
binding.setAgentId(agent.getId());
|
||||
if (binding.getRetrievalMode() == null || binding.getRetrievalMode().isBlank()) {
|
||||
binding.setRetrievalMode(DEFAULT_RETRIEVAL_MODE);
|
||||
} else {
|
||||
binding.setRetrievalMode(binding.getRetrievalMode().trim().toUpperCase());
|
||||
}
|
||||
binding.setEnabled(binding.getEnabled() == null || binding.getEnabled());
|
||||
binding.setSortNo(binding.getSortNo() == null ? index : binding.getSortNo());
|
||||
binding.setCreated(now);
|
||||
binding.setCreatedBy(account.getId());
|
||||
binding.setModified(now);
|
||||
binding.setModifiedBy(account.getId());
|
||||
}
|
||||
|
||||
private LoginAccount requireCurrentLoginAccount() {
|
||||
try {
|
||||
return SaTokenUtil.getLoginAccount();
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException("当前登录状态失效,请重新登录后再试");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
package tech.easyflow.agent.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.agent.entity.Agent;
|
||||
import tech.easyflow.agent.entity.AgentKnowledgeBinding;
|
||||
import tech.easyflow.agent.entity.AgentToolBinding;
|
||||
import tech.easyflow.agent.mapper.AgentMapper;
|
||||
import tech.easyflow.agent.service.AgentKnowledgeBindingService;
|
||||
import tech.easyflow.agent.service.AgentService;
|
||||
import tech.easyflow.agent.service.AgentToolBindingService;
|
||||
import tech.easyflow.ai.entity.*;
|
||||
import tech.easyflow.ai.enums.PublishStatus;
|
||||
import tech.easyflow.ai.service.*;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
import tech.easyflow.common.satoken.util.SaTokenUtil;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.system.enums.CategoryResourceType;
|
||||
import tech.easyflow.system.enums.ResourceAction;
|
||||
import tech.easyflow.system.enums.VisibilityScope;
|
||||
import tech.easyflow.system.service.ResourceAccessService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Agent 配置服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent> implements AgentService {
|
||||
|
||||
private static final TypeReference<List<AgentToolBinding>> TOOL_BINDING_LIST_TYPE = new TypeReference<>() {};
|
||||
private static final TypeReference<List<AgentKnowledgeBinding>> KNOWLEDGE_BINDING_LIST_TYPE = new TypeReference<>() {};
|
||||
|
||||
@Resource
|
||||
private AgentToolBindingService agentToolBindingService;
|
||||
@Resource
|
||||
private AgentKnowledgeBindingService agentKnowledgeBindingService;
|
||||
@Resource
|
||||
private ModelService modelService;
|
||||
@Resource
|
||||
private WorkflowService workflowService;
|
||||
@Resource
|
||||
private PluginItemService pluginItemService;
|
||||
@Resource
|
||||
private McpService mcpService;
|
||||
@Resource
|
||||
private DocumentCollectionService documentCollectionService;
|
||||
@Resource
|
||||
private ResourceAccessService resourceAccessService;
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Agent getDetail(BigInteger id) {
|
||||
Agent agent = requireAgent(id);
|
||||
resourceAccessService.assertAccess(CategoryResourceType.AGENT, agent, ResourceAction.READ, "无权限查看该 Agent");
|
||||
agent.setToolBindings(agentToolBindingService.listEnabled(id));
|
||||
agent.setKnowledgeBindings(agentKnowledgeBindingService.listEnabled(id));
|
||||
return agent;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Agent saveDraft(Agent agent) {
|
||||
validateDraft(agent);
|
||||
applyDraftDefaults(agent);
|
||||
save(agent);
|
||||
return getDetail(agent.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Agent updateDraft(Agent agent) {
|
||||
if (agent == null || agent.getId() == null) {
|
||||
throw new BusinessException("Agent ID 不能为空");
|
||||
}
|
||||
Agent existing = requireAgent(agent.getId());
|
||||
resourceAccessService.assertAccess(CategoryResourceType.AGENT, existing, ResourceAction.MANAGE, "无权限管理该 Agent");
|
||||
validateDraft(agent);
|
||||
applyDraftUpdate(existing, agent);
|
||||
updateById(existing);
|
||||
return getDetail(existing.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Agent getPublishedView(BigInteger id) {
|
||||
Agent agent = requireAgent(id);
|
||||
PublishStatus status = PublishStatus.from(agent.getPublishStatus());
|
||||
if (!status.isExternallyVisible() || agent.getPublishedSnapshotJson() == null || agent.getPublishedSnapshotJson().isEmpty()) {
|
||||
throw new BusinessException("Agent 未发布");
|
||||
}
|
||||
return fromSnapshot(agent.getPublishedSnapshotJson());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> buildPublishSnapshot(Agent agent) {
|
||||
Agent detail = getDetail(agent.getId());
|
||||
Map<String, Object> snapshot = new LinkedHashMap<>();
|
||||
snapshot.put("id", detail.getId());
|
||||
snapshot.put("tenantId", detail.getTenantId());
|
||||
snapshot.put("deptId", detail.getDeptId());
|
||||
snapshot.put("createdBy", detail.getCreatedBy());
|
||||
snapshot.put("name", detail.getName());
|
||||
snapshot.put("description", detail.getDescription());
|
||||
snapshot.put("avatar", detail.getAvatar());
|
||||
snapshot.put("categoryId", detail.getCategoryId());
|
||||
snapshot.put("modelId", detail.getModelId());
|
||||
snapshot.put("modelConfigJson", detail.getModelConfigJson());
|
||||
snapshot.put("generationConfigJson", detail.getGenerationConfigJson());
|
||||
snapshot.put("promptConfigJson", detail.getPromptConfigJson());
|
||||
snapshot.put("memoryConfigJson", detail.getMemoryConfigJson());
|
||||
snapshot.put("executionConfigJson", detail.getExecutionConfigJson());
|
||||
snapshot.put("visibilityScope", detail.getVisibilityScope());
|
||||
snapshot.put("toolBindings", snapshotToolBindings(detail.getToolBindings()));
|
||||
snapshot.put("knowledgeBindings", snapshotKnowledgeBindings(detail.getKnowledgeBindings()));
|
||||
snapshot.put("basicSummary", basicSummary(detail));
|
||||
snapshot.put("modelSummary", modelSummary(detail.getModelId()));
|
||||
snapshot.put("parameterSummary", parameterSummary(detail));
|
||||
snapshot.put("promptSummary", promptSummary(detail));
|
||||
snapshot.put("toolSummaries", toolSummaries(detail.getToolBindings()));
|
||||
snapshot.put("knowledgeSummaries", knowledgeSummaries(detail.getKnowledgeBindings()));
|
||||
snapshot.put("snapshotAt", new Date());
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Agent fromSnapshot(Map<String, Object> snapshot) {
|
||||
if (snapshot == null || snapshot.isEmpty()) {
|
||||
throw new BusinessException("Agent 发布快照为空");
|
||||
}
|
||||
Agent agent = objectMapper.convertValue(snapshot, Agent.class);
|
||||
agent.setId(toBigInteger(snapshot.get("id")));
|
||||
agent.setTenantId(toBigInteger(snapshot.get("tenantId")));
|
||||
agent.setDeptId(toBigInteger(snapshot.get("deptId")));
|
||||
agent.setCreatedBy(toBigInteger(snapshot.get("createdBy")));
|
||||
agent.setModelId(toBigInteger(snapshot.get("modelId")));
|
||||
agent.setCategoryId(toBigInteger(snapshot.get("categoryId")));
|
||||
agent.setPublishStatus(PublishStatus.PUBLISHED.getCode());
|
||||
agent.setPublishedSnapshotJson(snapshot);
|
||||
agent.setToolBindings(objectMapper.convertValue(snapshot.get("toolBindings"), TOOL_BINDING_LIST_TYPE));
|
||||
agent.setKnowledgeBindings(objectMapper.convertValue(snapshot.get("knowledgeBindings"), KNOWLEDGE_BINDING_LIST_TYPE));
|
||||
return agent;
|
||||
}
|
||||
|
||||
private Agent requireAgent(BigInteger id) {
|
||||
Agent agent = getById(id);
|
||||
if (agent == null) {
|
||||
throw new BusinessException("Agent 不存在");
|
||||
}
|
||||
return agent;
|
||||
}
|
||||
|
||||
private void validateDraft(Agent agent) {
|
||||
if (agent == null) {
|
||||
throw new BusinessException("Agent 不能为空");
|
||||
}
|
||||
if (agent.getName() == null || agent.getName().isBlank()) {
|
||||
throw new BusinessException("Agent 名称不能为空");
|
||||
}
|
||||
if (agent.getModelId() == null) {
|
||||
throw new BusinessException("Agent 模型不能为空");
|
||||
}
|
||||
Model model = modelService.getModelInstance(agent.getModelId());
|
||||
if (model == null) {
|
||||
throw new BusinessException("Agent 模型不存在");
|
||||
}
|
||||
agent.setVisibilityScope(VisibilityScope.fromOrDefault(agent.getVisibilityScope(), VisibilityScope.PRIVATE).name());
|
||||
}
|
||||
|
||||
private void applyDraftDefaults(Agent agent) {
|
||||
LoginAccount account = requireCurrentLoginAccount();
|
||||
Date now = new Date();
|
||||
agent.setTenantId(account.getTenantId());
|
||||
agent.setDeptId(account.getDeptId());
|
||||
agent.setCreated(now);
|
||||
agent.setCreatedBy(account.getId());
|
||||
agent.setModified(now);
|
||||
agent.setModifiedBy(account.getId());
|
||||
agent.setStatus(agent.getStatus() == null ? 1 : agent.getStatus());
|
||||
agent.setPublishStatus(PublishStatus.DRAFT.getCode());
|
||||
}
|
||||
|
||||
private Map<String, Object> basicSummary(Agent agent) {
|
||||
Map<String, Object> summary = new LinkedHashMap<>();
|
||||
summary.put("id", agent.getId());
|
||||
summary.put("name", agent.getName());
|
||||
summary.put("description", agent.getDescription());
|
||||
summary.put("avatar", agent.getAvatar());
|
||||
summary.put("categoryId", agent.getCategoryId());
|
||||
summary.put("status", agent.getStatus());
|
||||
summary.put("publishStatus", PublishStatus.PUBLISHED.getCode());
|
||||
summary.put("visibilityScope", agent.getVisibilityScope());
|
||||
return summary;
|
||||
}
|
||||
|
||||
private void applyDraftUpdate(Agent existing, Agent incoming) {
|
||||
LoginAccount account = requireCurrentLoginAccount();
|
||||
existing.setName(incoming.getName());
|
||||
existing.setDescription(incoming.getDescription());
|
||||
existing.setAvatar(incoming.getAvatar());
|
||||
existing.setCategoryId(incoming.getCategoryId());
|
||||
existing.setModelId(incoming.getModelId());
|
||||
existing.setModelConfigJson(incoming.getModelConfigJson());
|
||||
existing.setGenerationConfigJson(incoming.getGenerationConfigJson());
|
||||
existing.setPromptConfigJson(incoming.getPromptConfigJson());
|
||||
existing.setMemoryConfigJson(incoming.getMemoryConfigJson());
|
||||
existing.setExecutionConfigJson(incoming.getExecutionConfigJson());
|
||||
existing.setStatus(incoming.getStatus() == null ? 1 : incoming.getStatus());
|
||||
existing.setVisibilityScope(incoming.getVisibilityScope());
|
||||
existing.setModified(new Date());
|
||||
existing.setModifiedBy(account.getId());
|
||||
}
|
||||
|
||||
private Map<String, Object> modelSummary(BigInteger modelId) {
|
||||
Model model = modelService.getModelInstance(modelId);
|
||||
Map<String, Object> summary = new LinkedHashMap<>();
|
||||
summary.put("id", model.getId());
|
||||
summary.put("title", model.getTitle());
|
||||
summary.put("modelName", model.getModelName());
|
||||
summary.put("providerType", model.getModelProvider() == null ? null : model.getModelProvider().getProviderType());
|
||||
return summary;
|
||||
}
|
||||
|
||||
private Map<String, Object> parameterSummary(Agent agent) {
|
||||
Map<String, Object> summary = new LinkedHashMap<>();
|
||||
summary.put("generationConfigJson", agent.getGenerationConfigJson());
|
||||
summary.put("modelConfigJson", agent.getModelConfigJson());
|
||||
summary.put("memoryConfigJson", agent.getMemoryConfigJson());
|
||||
summary.put("executionConfigJson", agent.getExecutionConfigJson());
|
||||
return summary;
|
||||
}
|
||||
|
||||
private Map<String, Object> promptSummary(Agent agent) {
|
||||
Map<String, Object> summary = new LinkedHashMap<>();
|
||||
summary.put("promptConfigJson", agent.getPromptConfigJson());
|
||||
summary.put("systemPrompt", agent.getPromptConfigJson() == null ? null : agent.getPromptConfigJson().get("systemPrompt"));
|
||||
summary.put("prompt", agent.getPromptConfigJson() == null ? null : agent.getPromptConfigJson().get("prompt"));
|
||||
return summary;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> toolSummaries(List<AgentToolBinding> bindings) {
|
||||
if (bindings == null) {
|
||||
return List.of();
|
||||
}
|
||||
return bindings.stream().map(this::toolSummary).toList();
|
||||
}
|
||||
|
||||
private Map<String, Object> toolSummary(AgentToolBinding binding) {
|
||||
Map<String, Object> summary = new LinkedHashMap<>();
|
||||
summary.put("bindingId", binding.getId());
|
||||
summary.put("toolType", binding.getToolType());
|
||||
summary.put("targetId", binding.getTargetId());
|
||||
summary.put("toolName", binding.getToolName());
|
||||
summary.put("enabled", Boolean.TRUE.equals(binding.getEnabled()));
|
||||
summary.put("hitlEnabled", Boolean.TRUE.equals(binding.getHitlEnabled()));
|
||||
summary.put("hitlConfigJson", binding.getHitlConfigJson());
|
||||
summary.put("sortNo", binding.getSortNo());
|
||||
if ("WORKFLOW".equalsIgnoreCase(binding.getToolType())) {
|
||||
Workflow workflow = workflowService.getById(binding.getTargetId());
|
||||
summary.put("title", workflow == null ? null : workflow.getTitle());
|
||||
} else if ("PLUGIN".equalsIgnoreCase(binding.getToolType())) {
|
||||
PluginItem pluginItem = pluginItemService.getById(binding.getTargetId());
|
||||
summary.put("title", pluginItem == null ? null : pluginItem.getName());
|
||||
} else {
|
||||
Mcp mcp = mcpService.getById(binding.getTargetId());
|
||||
summary.put("title", mcp == null ? null : mcp.getTitle());
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
private List<AgentToolBinding> snapshotToolBindings(List<AgentToolBinding> bindings) {
|
||||
if (bindings == null) {
|
||||
return List.of();
|
||||
}
|
||||
return bindings.stream().map(binding -> {
|
||||
AgentToolBinding snapshot = objectMapper.convertValue(binding, AgentToolBinding.class);
|
||||
snapshot.setResourceSummary(toolSummary(binding));
|
||||
snapshot.setResourceSnapshot(toolResourceSnapshot(binding));
|
||||
return snapshot;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private Map<String, Object> toolResourceSnapshot(AgentToolBinding binding) {
|
||||
if ("WORKFLOW".equalsIgnoreCase(binding.getToolType())) {
|
||||
Workflow workflow = workflowService.getPublishedById(binding.getTargetId());
|
||||
if (workflow == null || !PublishStatus.from(workflow.getPublishStatus()).isExternallyVisible()) {
|
||||
throw new BusinessException("绑定工作流不存在或未发布");
|
||||
}
|
||||
return objectMapper.convertValue(workflow, new TypeReference<Map<String, Object>>() {});
|
||||
}
|
||||
if ("PLUGIN".equalsIgnoreCase(binding.getToolType())) {
|
||||
PluginItem pluginItem = pluginItemService.getById(binding.getTargetId());
|
||||
if (pluginItem == null) {
|
||||
throw new BusinessException("绑定插件不存在");
|
||||
}
|
||||
return objectMapper.convertValue(pluginItem, new TypeReference<Map<String, Object>>() {});
|
||||
}
|
||||
Mcp mcp = mcpService.getById(binding.getTargetId());
|
||||
if (mcp == null) {
|
||||
throw new BusinessException("绑定 MCP 不存在");
|
||||
}
|
||||
return objectMapper.convertValue(mcp, new TypeReference<Map<String, Object>>() {});
|
||||
}
|
||||
|
||||
private List<AgentKnowledgeBinding> snapshotKnowledgeBindings(List<AgentKnowledgeBinding> bindings) {
|
||||
if (bindings == null) {
|
||||
return List.of();
|
||||
}
|
||||
return bindings.stream().map(binding -> {
|
||||
AgentKnowledgeBinding snapshot = objectMapper.convertValue(binding, AgentKnowledgeBinding.class);
|
||||
snapshot.setResourceSummary(knowledgeSummary(binding));
|
||||
snapshot.setResourceSnapshot(knowledgeResourceSnapshot(binding));
|
||||
return snapshot;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private Map<String, Object> knowledgeResourceSnapshot(AgentKnowledgeBinding binding) {
|
||||
DocumentCollection knowledge = documentCollectionService.getPublishedById(binding.getKnowledgeId());
|
||||
if (knowledge == null || !PublishStatus.from(knowledge.getPublishStatus()).isExternallyVisible()) {
|
||||
throw new BusinessException("绑定知识库不存在或未发布");
|
||||
}
|
||||
return objectMapper.convertValue(knowledge, new TypeReference<Map<String, Object>>() {});
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> knowledgeSummaries(List<AgentKnowledgeBinding> bindings) {
|
||||
if (bindings == null) {
|
||||
return List.of();
|
||||
}
|
||||
return bindings.stream().map(this::knowledgeSummary).toList();
|
||||
}
|
||||
|
||||
private Map<String, Object> knowledgeSummary(AgentKnowledgeBinding binding) {
|
||||
DocumentCollection knowledge = documentCollectionService.getById(binding.getKnowledgeId());
|
||||
Map<String, Object> summary = new LinkedHashMap<>();
|
||||
summary.put("bindingId", binding.getId());
|
||||
summary.put("knowledgeId", binding.getKnowledgeId());
|
||||
summary.put("retrievalMode", binding.getRetrievalMode());
|
||||
summary.put("enabled", Boolean.TRUE.equals(binding.getEnabled()));
|
||||
summary.put("optionsJson", binding.getOptionsJson());
|
||||
summary.put("sortNo", binding.getSortNo());
|
||||
summary.put("title", knowledge == null ? null : knowledge.getTitle());
|
||||
return summary;
|
||||
}
|
||||
|
||||
private LoginAccount requireCurrentLoginAccount() {
|
||||
try {
|
||||
return SaTokenUtil.getLoginAccount();
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException("当前登录状态失效,请重新登录后再试");
|
||||
}
|
||||
}
|
||||
|
||||
private BigInteger toBigInteger(Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof BigInteger bigInteger) {
|
||||
return bigInteger;
|
||||
}
|
||||
if (value instanceof Number number) {
|
||||
return BigInteger.valueOf(number.longValue());
|
||||
}
|
||||
return new BigInteger(String.valueOf(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package tech.easyflow.agent.service.impl;
|
||||
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.agent.entity.Agent;
|
||||
import tech.easyflow.agent.entity.AgentToolBinding;
|
||||
import tech.easyflow.agent.enums.AgentToolType;
|
||||
import tech.easyflow.agent.mapper.AgentMapper;
|
||||
import tech.easyflow.agent.mapper.AgentToolBindingMapper;
|
||||
import tech.easyflow.agent.service.AgentToolBindingService;
|
||||
import tech.easyflow.ai.entity.Mcp;
|
||||
import tech.easyflow.ai.entity.PluginItem;
|
||||
import tech.easyflow.ai.entity.Workflow;
|
||||
import tech.easyflow.ai.enums.PublishStatus;
|
||||
import tech.easyflow.ai.service.McpService;
|
||||
import tech.easyflow.ai.service.PluginItemService;
|
||||
import tech.easyflow.ai.service.WorkflowService;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
import tech.easyflow.common.satoken.util.SaTokenUtil;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.system.enums.CategoryResourceType;
|
||||
import tech.easyflow.system.enums.ResourceAction;
|
||||
import tech.easyflow.system.service.ResourceAccessService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Agent 工具绑定服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class AgentToolBindingServiceImpl extends ServiceImpl<AgentToolBindingMapper, AgentToolBinding>
|
||||
implements AgentToolBindingService {
|
||||
|
||||
@Resource
|
||||
private AgentMapper agentMapper;
|
||||
@Resource
|
||||
private WorkflowService workflowService;
|
||||
@Resource
|
||||
private PluginItemService pluginItemService;
|
||||
@Resource
|
||||
private McpService mcpService;
|
||||
@Resource
|
||||
private ResourceAccessService resourceAccessService;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<AgentToolBinding> replaceBindings(BigInteger agentId, List<AgentToolBinding> bindings) {
|
||||
Agent agent = requireAgent(agentId);
|
||||
resourceAccessService.assertAccess(CategoryResourceType.AGENT, agent, ResourceAction.MANAGE, "无权限管理该 Agent");
|
||||
remove(QueryWrapper.create().where("agent_id = ?", agentId));
|
||||
if (bindings == null || bindings.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
for (int i = 0; i < bindings.size(); i++) {
|
||||
AgentToolBinding binding = bindings.get(i);
|
||||
validateBinding(binding);
|
||||
applyBindingDefaults(agent, binding, i);
|
||||
}
|
||||
saveBatch(bindings);
|
||||
return listEnabled(agentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<AgentToolBinding> listEnabled(BigInteger agentId) {
|
||||
return list(QueryWrapper.create()
|
||||
.where("agent_id = ?", agentId)
|
||||
.and("enabled = ?", true)
|
||||
.orderBy("sort_no asc, id asc"));
|
||||
}
|
||||
|
||||
private Agent requireAgent(BigInteger agentId) {
|
||||
Agent agent = agentMapper.selectOneById(agentId);
|
||||
if (agent == null) {
|
||||
throw new BusinessException("Agent 不存在");
|
||||
}
|
||||
return agent;
|
||||
}
|
||||
|
||||
private void validateBinding(AgentToolBinding binding) {
|
||||
if (binding == null || binding.getTargetId() == null || binding.getToolType() == null) {
|
||||
throw new BusinessException("工具绑定参数不完整");
|
||||
}
|
||||
AgentToolType type = AgentToolType.from(binding.getToolType());
|
||||
if (type == AgentToolType.WORKFLOW) {
|
||||
Workflow workflow = workflowService.getById(binding.getTargetId());
|
||||
if (workflow == null || PublishStatus.from(workflow.getPublishStatus()) != PublishStatus.PUBLISHED) {
|
||||
throw new BusinessException("绑定工作流不存在或未发布");
|
||||
}
|
||||
resourceAccessService.assertAccess(CategoryResourceType.WORKFLOW, workflow, ResourceAction.USE, "无权限绑定该工作流");
|
||||
return;
|
||||
}
|
||||
if (type == AgentToolType.PLUGIN) {
|
||||
PluginItem pluginItem = pluginItemService.getById(binding.getTargetId());
|
||||
if (pluginItem == null || pluginItem.getStatus() == null || pluginItem.getStatus() != 1) {
|
||||
throw new BusinessException("绑定插件不存在或未启用");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Mcp mcp = mcpService.getById(binding.getTargetId());
|
||||
if (mcp == null || !Boolean.TRUE.equals(mcp.getStatus())) {
|
||||
throw new BusinessException("绑定 MCP 不存在或未启用");
|
||||
}
|
||||
}
|
||||
|
||||
private void applyBindingDefaults(Agent agent, AgentToolBinding binding, int index) {
|
||||
LoginAccount account = requireCurrentLoginAccount();
|
||||
Date now = new Date();
|
||||
binding.setId(null);
|
||||
binding.setTenantId(agent.getTenantId());
|
||||
binding.setAgentId(agent.getId());
|
||||
binding.setEnabled(binding.getEnabled() == null || binding.getEnabled());
|
||||
binding.setHitlEnabled(Boolean.TRUE.equals(binding.getHitlEnabled()));
|
||||
binding.setSortNo(binding.getSortNo() == null ? index : binding.getSortNo());
|
||||
binding.setCreated(now);
|
||||
binding.setCreatedBy(account.getId());
|
||||
binding.setModified(now);
|
||||
binding.setModifiedBy(account.getId());
|
||||
}
|
||||
|
||||
private LoginAccount requireCurrentLoginAccount() {
|
||||
try {
|
||||
return SaTokenUtil.getLoginAccount();
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException("当前登录状态失效,请重新登录后再试");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user