fix(plans): parent delegated-step child conversations so they don't leak into the conversation list

This commit is contained in:
matevip 2026-06-21 23:18:49 +08:00
parent eca4229751
commit 2664b26763
2 changed files with 45 additions and 4 deletions

View File

@ -30,10 +30,13 @@ import vip.mate.planning.service.PlanningService;
import vip.mate.agent.context.ChatOrigin;
import vip.mate.skill.runtime.SkillCatalogRenderer;
import vip.mate.tool.builtin.DelegateAgentTool;
import vip.mate.tool.builtin.DelegationContext;
import vip.mate.tool.builtin.ToolExecutionContext;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 步骤执行节点
@ -221,7 +224,7 @@ public class StepExecutionNode implements NodeAction {
if (delegateAgentTool != null && assignedAgentId != null
&& !assignedAgentId.equals(parseLongOrNull(agentId))) {
return executeDelegatedStep(accessor, stepIndex, step, planId, assignedAgentId,
chatOrigin, events, iterationEventsOn);
conversationId, chatOrigin, events, iterationEventsOn);
}
if (iterationEventsOn) {
@ -658,7 +661,7 @@ public class StepExecutionNode implements NodeAction {
*/
private Map<String, Object> executeDelegatedStep(
PlanStateAccessor accessor, int stepIndex, String step, Long planId,
Long assignedAgentId, ChatOrigin chatOrigin,
Long assignedAgentId, String conversationId, ChatOrigin chatOrigin,
List<GraphEventPublisher.GraphEvent> events, boolean iterationEventsOn) {
if (iterationEventsOn) {
@ -672,12 +675,28 @@ public class StepExecutionNode implements NodeAction {
log.info("[StepExecution] Delegating step {} to agent {}", stepIndex + 1, assignedAgentId);
// Seed the delegation context with the plan's REAL conversation id (from
// graph state) so the delegated child conversation is parented to it and
// stays hidden from the user's conversation list. The ChatOrigin in the
// plan-execute path carries no conversationId, so delegateByAgentId can't
// derive the parent on its own we provide it here.
boolean seeded = false;
if (conversationId != null && !conversationId.isBlank()
&& DelegationContext.parentConversationId() == null
&& ToolExecutionContext.conversationId() == null) {
DelegationContext.enter(conversationId, Set.of(), conversationId, null, 0);
seeded = true;
}
String result;
try {
result = delegateAgentTool.delegateByAgentId(assignedAgentId, step, chatOrigin);
} catch (Exception e) {
log.error("[StepExecution] Delegated step {} threw: {}", stepIndex, e.getMessage(), e);
result = "[错误] 委派执行异常:" + e.getMessage();
} finally {
if (seeded) {
DelegationContext.exit();
}
}
String finalResult = result != null ? result : "";

View File

@ -340,8 +340,30 @@ public class DelegateAgentTool {
if (target == null || !Boolean.TRUE.equals(target.getEnabled())) {
return "[错误] 未找到 id=" + agentId + " 的已启用 Agent。";
}
ToolContext ctx = (parentOrigin != null ? parentOrigin : ChatOrigin.EMPTY).toToolContext();
return delegateToAgent(target.getName(), task, false, ctx);
ChatOrigin origin = parentOrigin != null ? parentOrigin : ChatOrigin.EMPTY;
ToolContext ctx = origin.toToolContext();
// A graph-node-initiated delegation (e.g. a plan step) runs outside any
// tool-execution / delegation context, so resolveParentConversationId()
// would return null and the child conversation would be created with a
// null parent leaking it into the user's top-level conversation list
// (the list filters on parentConversationId IS NULL). Seed a depth-0
// delegation frame carrying the parent conversation id from the
// ChatOrigin so the child is correctly parented and hidden, mirroring how
// a tool-initiated delegation gets its conv id from ToolExecutionContext.
String parentConvId = origin.conversationId();
boolean seedContext = parentConvId != null && !parentConvId.isBlank()
&& ToolExecutionContext.conversationId() == null
&& DelegationContext.parentConversationId() == null;
if (seedContext) {
DelegationContext.enter(parentConvId, Set.of(), parentConvId, null, 0);
}
try {
return delegateToAgent(target.getName(), task, false, ctx);
} finally {
if (seedContext) {
DelegationContext.exit();
}
}
}
// ==================== Parallel delegation ====================