人机交互节点逻辑修改

This commit is contained in:
zhang
2026-02-09 17:43:29 +08:00
parent eb2e8f3ff8
commit 1cd8ae1cd9
9 changed files with 289 additions and 44 deletions

View File

@@ -98,7 +98,7 @@ public class WorkflowComponentService extends ServiceImpl<WorkflowComponentMappe
return baseMapper.selectPage(new Page<>(currentPage, pageSize), wrapper);
}
@Cacheable(cacheNames = WORKFLOW_COMPONENTS)
// @Cacheable(cacheNames = WORKFLOW_COMPONENTS)
public List<WorkflowComponent> getAllEnable() {
return ChainWrappers.lambdaQueryChain(baseMapper)
.eq(WorkflowComponent::getIsEnable, true)

View File

@@ -6,6 +6,7 @@ import org.ruoyi.workflow.workflow.node.AbstractWfNode;
import org.ruoyi.workflow.workflow.node.EndNode;
import org.ruoyi.workflow.workflow.node.answer.LLMAnswerNode;
import org.ruoyi.workflow.workflow.node.httpRequest.HttpRequestNode;
import org.ruoyi.workflow.workflow.node.humanFeedBack.HumanFeedbackNode;
import org.ruoyi.workflow.workflow.node.keywordExtractor.KeywordExtractorNode;
import org.ruoyi.workflow.workflow.node.knowledgeRetrieval.KnowledgeRetrievalNode;
import org.ruoyi.workflow.workflow.node.mailSend.MailSendNode;
@@ -25,6 +26,7 @@ public class WfNodeFactory {
case MAIL_SEND -> wfNode = new MailSendNode(wfComponent, nodeDefinition, wfState, nodeState);
case HTTP_REQUEST -> wfNode = new HttpRequestNode(wfComponent, nodeDefinition, wfState, nodeState);
case SWITCHER -> wfNode = new SwitcherNode(wfComponent, nodeDefinition, wfState, nodeState);
case HUMAN_FEEDBACK -> wfNode = new HumanFeedbackNode(wfComponent, nodeDefinition, wfState, nodeState);
default -> {
}
}

View File

@@ -35,6 +35,12 @@ public class WorkflowUtil {
@Resource
private ChatServiceFactory chatServiceFactory;
// 添加默认名称的成员变量
private static final String DEFAULT_NODE_NAME = "input";
// 添加文档解析的前缀字段
private static final String UPLOAD_FILE_API_PREFIX = "fileid";
public static String renderTemplate(String template, List<NodeIOData> values) {
// 🔒 关键修复:如果 template 为 null直接返回 null 或空字符串
if (template == null) {
@@ -125,9 +131,9 @@ public class WorkflowUtil {
// 构建 ruoyi-ai 的 ChatRequest
List<Message> messages = new ArrayList<>();
addUserMessage(node, state.getInputs(), messages);
addSystemMessage(systemMessage, messages);
List<NodeIOData> inputs = state.getInputs();
addUserMessage(node, inputs, messages);
addSystemMessage(systemMessage, inputs, messages);
ChatRequest chatRequest = new ChatRequest();
chatRequest.setModel(modelName);
@@ -150,20 +156,44 @@ public class WorkflowUtil {
}
WfNodeInputConfig nodeInputConfig = NodeInputConfigTypeHandler.fillNodeInputConfig(node.getInputConfig());
List<WfNodeParamRef> refInputs = nodeInputConfig.getRefInputs();
Set<String> nameSet = CollStreamUtil.toSet(refInputs, WfNodeParamRef::getName);
userMessage.stream().filter(item -> nameSet.contains(item.getName()))
.map(item -> getMessage("user", item.getContent().getValue())).forEach(messages::add);
if (CollUtil.isNotEmpty(messages)) {
return;
// 检查是否存在包含fileId的NodeIOData对象
boolean hasFileIdData = hasFileIdData(userMessage);
// 构建消息列表
List<Message> messageList = buildMessageList(userMessage, nameSet, hasFileIdData, DEFAULT_NODE_NAME);
// 如果没有找到匹配的消息尝试使用input字段
if (CollUtil.isEmpty(messageList)) {
messageList = buildMessageList(userMessage, Set.of("input"), hasFileIdData, DEFAULT_NODE_NAME);
}
messages.addAll(messageList);
}
userMessage.stream().filter(item -> "input".equals(item.getName()))
.map(item -> getMessage("user", item.getContent().getValue())).forEach(messages::add);
/**
* 检查是否包含fileId数据
*/
private boolean hasFileIdData(List<NodeIOData> userMessage) {
return userMessage.stream().anyMatch(item ->
item != null &&
item.getContent() != null &&
item.getContent().getValue() != null &&
String.valueOf(item.getContent().getValue()).toLowerCase().contains(UPLOAD_FILE_API_PREFIX)
);
}
/**
* 构建消息列表
*/
private List<Message> buildMessageList(List<NodeIOData> userMessage, Set<String> nameSet, boolean hasFileIdData, String defaultName) {
String role = hasFileIdData ? "system" : "user";
return userMessage.stream()
.filter(item -> item != null && item.getName() != null)
.filter(item -> nameSet.contains(item.getName()) || defaultName.equals(item.getName()))
.map(item -> getMessage(role, item.getContent().getValue()))
.toList();
}
/**
@@ -187,14 +217,22 @@ public class WorkflowUtil {
* @param systemMessage
* @param messages
*/
private void addSystemMessage(List<UserMessage> systemMessage, List<Message> messages) {
log.info("addSystemMessage received: {}", systemMessage); // 🔥 加这一行
private void addSystemMessage(List<UserMessage> systemMessage, List<NodeIOData> userMessage, List<Message> messages) {
log.info("addSystemMessage received: {}", systemMessage);
if (CollUtil.isEmpty(systemMessage)) {
return;
}
// 检查是否存在包含fileId的NodeIOData对象
boolean hasFileIdData = hasFileIdData(userMessage);
// 根据是否有fileId数据确定消息角色
String role = hasFileIdData ? "user" : "system";
// 添加消息
systemMessage.stream()
.map(userMsg -> getMessage("system", userMsg.singleText()))
.map(userMsg -> getMessage(role, userMsg.singleText()))
.forEach(messages::add);
}
}

View File

@@ -6,7 +6,6 @@ import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.ruoyi.common.core.exception.base.BaseException;
import org.ruoyi.workflow.base.NodeInputConfigTypeHandler;
import org.ruoyi.workflow.entity.WorkflowComponent;
@@ -124,14 +123,6 @@ public abstract class AbstractWfNode {
log.info("↓↓↓↓↓ node process start,name:{},uuid:{}", node.getTitle(), node.getUuid());
state.setProcessStatus(NODE_PROCESS_STATUS_DOING);
initInput();
//HumanFeedback的情况
Object humanFeedbackState = state.data().get(HUMAN_FEEDBACK_KEY);
if (null != humanFeedbackState) {
String userInput = humanFeedbackState.toString();
if (StringUtils.isNotBlank(userInput)) {
state.getInputs().add(NodeIOData.createByText(HUMAN_FEEDBACK_KEY, "default", userInput));
}
}
if (null != inputConsumer) {
inputConsumer.accept(state);
}

View File

@@ -0,0 +1,56 @@
package org.ruoyi.workflow.workflow.node.humanFeedBack;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.ruoyi.workflow.entity.WorkflowComponent;
import org.ruoyi.workflow.entity.WorkflowNode;
import org.ruoyi.workflow.workflow.NodeProcessResult;
import org.ruoyi.workflow.workflow.WfNodeState;
import org.ruoyi.workflow.workflow.WfState;
import org.ruoyi.workflow.workflow.data.NodeIOData;
import org.ruoyi.workflow.workflow.node.AbstractWfNode;
import static org.ruoyi.workflow.cosntant.AdiConstant.WorkflowConstant.*;
/**
* 人机交互节点实现类
*/
@Slf4j
public class HumanFeedbackNode extends AbstractWfNode {
public HumanFeedbackNode(WorkflowComponent component, WorkflowNode nodeDefinition, WfState wfState, WfNodeState nodeState) {
super(component, nodeDefinition, wfState, nodeState);
}
// 人机交互节点的处理逻辑
@Override
public NodeProcessResult onProcess() {
log.info("Processing HumanFeedback node: {}", node.getTitle());
// 从状态中获取用户输入数据
Object humanFeedbackState = state.data().get(HUMAN_FEEDBACK_KEY);
if (null != humanFeedbackState) {
String userInput = humanFeedbackState.toString();
if (StringUtils.isNotBlank(userInput)) {
// 用户已提供输入,将用户输入添加到节点输入和输出中
NodeIOData feedbackData = NodeIOData.createByText("output", "default", userInput);
// 添加到输入列表,这样当前节点处理时可以使用
state.getInputs().add(feedbackData);
// 添加到输出列表,这样后续节点可以使用
state.getOutputs().add(feedbackData);
// 设置为成功状态
state.setProcessStatus(NODE_PROCESS_STATUS_SUCCESS);
log.info("Human feedback processed for node: {}, content: {}", node.getTitle(), userInput);
} else {
// 用户输入为空,设置等待状态
state.setProcessStatus(NODE_PROCESS_STATUS_DOING);
log.info("Human feedback is empty for node: {}", node.getTitle());
}
} else {
// 没有用户输入,这可能是正常情况(等待用户输入)
// 但为了确保流程可以继续,我们仍然标记为成功
state.setProcessStatus(NODE_PROCESS_STATUS_SUCCESS);
log.info("No human feedback found for node: {}, continuing workflow", node.getTitle());
}
return new NodeProcessResult();
}
}