feat: 先进智能体功能上线
- 基于 agent-runtime 打造,默认 ReAct agent - 支持 agent 能力对接,已对接工作流、插件、知识库等 tool 能力 - 全新 agent 编排界面,支持可视化便捷配置 agent - 全新 agent 聊天界面,支持快捷操作、额外知识库选择等
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
package tech.easyflow.agent.runtime;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Agent 聊天临时能力请求项。
|
||||
*/
|
||||
public class AgentChatCapability implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String type;
|
||||
private List<BigInteger> resourceIds = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 获取能力类型。
|
||||
*
|
||||
* @return 能力类型
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置能力类型。
|
||||
*
|
||||
* @param type 能力类型
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源 ID 列表。
|
||||
*
|
||||
* @return 资源 ID 列表
|
||||
*/
|
||||
public List<BigInteger> getResourceIds() {
|
||||
return resourceIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置资源 ID 列表。
|
||||
*
|
||||
* @param resourceIds 资源 ID 列表
|
||||
*/
|
||||
public void setResourceIds(List<BigInteger> resourceIds) {
|
||||
this.resourceIds = resourceIds == null ? new ArrayList<>() : new ArrayList<>(resourceIds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package tech.easyflow.agent.runtime;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.easyflow.agent.entity.Agent;
|
||||
import tech.easyflow.agent.entity.AgentKnowledgeBinding;
|
||||
import tech.easyflow.ai.entity.DocumentCollection;
|
||||
import tech.easyflow.ai.enums.PublishStatus;
|
||||
import tech.easyflow.ai.service.DocumentCollectionService;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
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 java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Agent 聊天临时能力编排服务。
|
||||
*/
|
||||
@Service
|
||||
public class AgentChatCapabilityService {
|
||||
|
||||
private static final String KNOWLEDGE_CAPABILITY_TYPE = "KNOWLEDGE";
|
||||
private static final String DEFAULT_RETRIEVAL_MODE = "HYBRID";
|
||||
private static final int MAX_EXTRA_KNOWLEDGE_COUNT = 3;
|
||||
|
||||
private final DocumentCollectionService documentCollectionService;
|
||||
private final ResourceAccessService resourceAccessService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 创建 Agent 聊天临时能力编排服务。
|
||||
*
|
||||
* @param documentCollectionService 知识库服务
|
||||
* @param resourceAccessService 资源访问服务
|
||||
* @param objectMapper 对象复制工具
|
||||
*/
|
||||
public AgentChatCapabilityService(DocumentCollectionService documentCollectionService,
|
||||
ResourceAccessService resourceAccessService,
|
||||
ObjectMapper objectMapper) {
|
||||
this.documentCollectionService = documentCollectionService;
|
||||
this.resourceAccessService = resourceAccessService;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将临时聊天能力合并到运行时 Agent 定义中。
|
||||
*
|
||||
* @param agent 已发布 Agent 运行视图
|
||||
* @param capabilities 临时能力请求
|
||||
* @param account 当前登录账号
|
||||
* @return 能力解析结果
|
||||
*/
|
||||
public AgentChatCapabilityResolution apply(Agent agent,
|
||||
List<AgentChatCapability> capabilities,
|
||||
LoginAccount account) {
|
||||
List<BigInteger> extraKnowledgeIds = resolveKnowledgeIds(capabilities);
|
||||
boolean knowledgeCapabilityProvided = hasKnowledgeCapability(capabilities);
|
||||
if (agent == null || extraKnowledgeIds.isEmpty()) {
|
||||
return new AgentChatCapabilityResolution(agent, extraKnowledgeIds, knowledgeCapabilityProvided);
|
||||
}
|
||||
Agent runtimeAgent = objectMapper.convertValue(agent, Agent.class);
|
||||
List<AgentKnowledgeBinding> mergedBindings = new ArrayList<>();
|
||||
Set<BigInteger> existingKnowledgeIds = new LinkedHashSet<>();
|
||||
if (runtimeAgent.getKnowledgeBindings() != null) {
|
||||
for (AgentKnowledgeBinding binding : runtimeAgent.getKnowledgeBindings()) {
|
||||
if (binding == null) {
|
||||
continue;
|
||||
}
|
||||
mergedBindings.add(binding);
|
||||
if (Boolean.TRUE.equals(binding.getEnabled()) && binding.getKnowledgeId() != null) {
|
||||
existingKnowledgeIds.add(binding.getKnowledgeId());
|
||||
}
|
||||
}
|
||||
}
|
||||
int sortNo = mergedBindings.size();
|
||||
for (BigInteger knowledgeId : extraKnowledgeIds) {
|
||||
if (existingKnowledgeIds.contains(knowledgeId)) {
|
||||
continue;
|
||||
}
|
||||
DocumentCollection knowledge = documentCollectionService.getById(knowledgeId);
|
||||
validateKnowledge(knowledge);
|
||||
resourceAccessService.assertAccess(
|
||||
CategoryResourceType.KNOWLEDGE,
|
||||
knowledge,
|
||||
ResourceAction.USE,
|
||||
"无权限使用所选知识库"
|
||||
);
|
||||
AgentKnowledgeBinding binding = new AgentKnowledgeBinding();
|
||||
binding.setTenantId(account == null ? runtimeAgent.getTenantId() : account.getTenantId());
|
||||
binding.setAgentId(runtimeAgent.getId());
|
||||
binding.setKnowledgeId(knowledgeId);
|
||||
binding.setRetrievalMode(DEFAULT_RETRIEVAL_MODE);
|
||||
binding.setEnabled(true);
|
||||
binding.setSortNo(sortNo++);
|
||||
mergedBindings.add(binding);
|
||||
existingKnowledgeIds.add(knowledgeId);
|
||||
}
|
||||
runtimeAgent.setKnowledgeBindings(mergedBindings);
|
||||
return new AgentChatCapabilityResolution(runtimeAgent, extraKnowledgeIds, knowledgeCapabilityProvided);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从能力列表提取知识库 ID。
|
||||
*
|
||||
* @param capabilities 临时能力列表
|
||||
* @return 已去重知识库 ID
|
||||
*/
|
||||
public List<BigInteger> resolveKnowledgeIds(List<AgentChatCapability> capabilities) {
|
||||
if (capabilities == null || capabilities.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
LinkedHashSet<BigInteger> ids = new LinkedHashSet<>();
|
||||
for (AgentChatCapability capability : capabilities) {
|
||||
if (capability == null || !isKnowledgeCapability(capability.getType())) {
|
||||
continue;
|
||||
}
|
||||
if (capability.getResourceIds() == null) {
|
||||
continue;
|
||||
}
|
||||
for (BigInteger resourceId : capability.getResourceIds()) {
|
||||
if (resourceId != null) {
|
||||
ids.add(resourceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ids.size() > MAX_EXTRA_KNOWLEDGE_COUNT) {
|
||||
throw new BusinessException("临时知识库最多选择 3 个");
|
||||
}
|
||||
return new ArrayList<>(ids);
|
||||
}
|
||||
|
||||
private boolean isKnowledgeCapability(String type) {
|
||||
return Objects.equals(KNOWLEDGE_CAPABILITY_TYPE, type == null ? null : type.trim().toUpperCase());
|
||||
}
|
||||
|
||||
private boolean hasKnowledgeCapability(List<AgentChatCapability> capabilities) {
|
||||
if (capabilities == null || capabilities.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (AgentChatCapability capability : capabilities) {
|
||||
if (capability != null && isKnowledgeCapability(capability.getType())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void validateKnowledge(DocumentCollection knowledge) {
|
||||
if (knowledge == null) {
|
||||
throw new BusinessException("所选知识库不存在");
|
||||
}
|
||||
if (PublishStatus.from(knowledge.getPublishStatus()) != PublishStatus.PUBLISHED) {
|
||||
throw new BusinessException("所选知识库未发布,无法用于聊天");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Agent 聊天临时能力解析结果。
|
||||
*
|
||||
* @param agent 合并临时能力后的运行时 Agent
|
||||
* @param extraKnowledgeIds 本次选择的临时知识库 ID
|
||||
* @param knowledgeCapabilityProvided 请求是否显式传入知识库能力
|
||||
*/
|
||||
public record AgentChatCapabilityResolution(Agent agent,
|
||||
List<BigInteger> extraKnowledgeIds,
|
||||
boolean knowledgeCapabilityProvided) {
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package tech.easyflow.agent.runtime;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Agent 管理端运行请求。
|
||||
@@ -10,6 +12,7 @@ public class AgentChatRequest {
|
||||
private BigInteger agentId;
|
||||
private BigInteger sessionId;
|
||||
private String prompt;
|
||||
private List<AgentChatCapability> capabilities = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 获取 Agent ID。
|
||||
@@ -52,4 +55,22 @@ public class AgentChatRequest {
|
||||
* @param prompt 用户输入
|
||||
*/
|
||||
public void setPrompt(String prompt) { this.prompt = prompt; }
|
||||
|
||||
/**
|
||||
* 获取本次聊天启用的临时能力。
|
||||
*
|
||||
* @return 临时能力列表
|
||||
*/
|
||||
public List<AgentChatCapability> getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置本次聊天启用的临时能力。
|
||||
*
|
||||
* @param capabilities 临时能力列表
|
||||
*/
|
||||
public void setCapabilities(List<AgentChatCapability> capabilities) {
|
||||
this.capabilities = capabilities == null ? new ArrayList<>() : new ArrayList<>(capabilities);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ public class AgentRunService {
|
||||
@Resource
|
||||
private AgentRuntimeFactory agentRuntimeFactory;
|
||||
@Resource
|
||||
private AgentChatCapabilityService agentChatCapabilityService;
|
||||
@Resource
|
||||
private AgentSessionStore agentSessionStore;
|
||||
@Resource
|
||||
private EasyFlowAgentSessionStore easyFlowAgentSessionStore;
|
||||
@@ -121,10 +123,16 @@ public class AgentRunService {
|
||||
ChatSessionSummary existingSession = resolveExistingSession(account, sessionId, chatRequest.getAgentId());
|
||||
// 获取 Agent 发布快照
|
||||
Agent agent = agentService.getPublishedView(chatRequest.getAgentId());
|
||||
AgentChatCapabilityService.AgentChatCapabilityResolution capabilityResolution =
|
||||
agentChatCapabilityService.apply(agent, chatRequest.getCapabilities(), account);
|
||||
agent = capabilityResolution.agent();
|
||||
String requestId = UUID.randomUUID().toString();
|
||||
String traceId = UUID.randomUUID().toString();
|
||||
// 组建会话上下文必要信息
|
||||
ChatRuntimeContext chatContext = buildChatRuntimeContext(agent, sessionId, chatRequest.getPrompt(), account);
|
||||
if (capabilityResolution.knowledgeCapabilityProvided()) {
|
||||
chatContext.getExt().put(ChatRuntimeExtKeys.EXTRA_KNOWLEDGE_IDS, capabilityResolution.extraKnowledgeIds());
|
||||
}
|
||||
applyFormalSessionTitle(chatContext, chatRequest.getPrompt(), existingSession);
|
||||
// 执行对话
|
||||
return run(agent, chatRequest.getPrompt(), requestId, traceId, sessionId.toString(),
|
||||
|
||||
Reference in New Issue
Block a user