mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 08:43:02 +08:00
refactor(action): add resume/interrupt methods to ExecutableAction; add related definitions to support acquiring help from user
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package work.slhaf.partner.core.action.entity
|
package work.slhaf.partner.core.action.entity
|
||||||
|
|
||||||
import work.slhaf.partner.module.modules.action.executor.entity.HistoryAction
|
import work.slhaf.partner.module.modules.action.executor.entity.HistoryAction
|
||||||
|
import java.time.Instant
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
@@ -112,6 +113,27 @@ sealed class ExecutableAction : Action() {
|
|||||||
val additionalContext: MutableMap<Int, MutableList<String>> = mutableMapOf()
|
val additionalContext: MutableMap<Int, MutableList<String>> = mutableMapOf()
|
||||||
|
|
||||||
override val timeout: Duration = 10.minutes
|
override val timeout: Duration = 10.minutes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param timeout 最长打断时间
|
||||||
|
* @return 是否超时结束
|
||||||
|
*/
|
||||||
|
fun interrupt(timeout: Int): Boolean {
|
||||||
|
status = Status.INTERRUPTED
|
||||||
|
val interruptAt = Instant.now().epochSecond
|
||||||
|
|
||||||
|
while (status == Status.INTERRUPTED) {
|
||||||
|
Thread.sleep(500);
|
||||||
|
if (Instant.now().epochSecond - interruptAt > timeout) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resume() {
|
||||||
|
status = Status.EXECUTING
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,13 +2,21 @@ package work.slhaf.partner.module.modules.action.builtin;
|
|||||||
|
|
||||||
import com.alibaba.fastjson2.JSONArray;
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
||||||
import work.slhaf.partner.api.agent.factory.component.annotation.AgentComponent;
|
import work.slhaf.partner.api.agent.factory.component.annotation.AgentComponent;
|
||||||
import work.slhaf.partner.core.action.ActionCapability;
|
import work.slhaf.partner.core.action.ActionCapability;
|
||||||
|
import work.slhaf.partner.core.action.entity.Action;
|
||||||
import work.slhaf.partner.core.action.entity.ExecutableAction;
|
import work.slhaf.partner.core.action.entity.ExecutableAction;
|
||||||
import work.slhaf.partner.core.action.entity.MetaActionInfo;
|
import work.slhaf.partner.core.action.entity.MetaActionInfo;
|
||||||
import work.slhaf.partner.core.action.entity.intervention.InterventionType;
|
import work.slhaf.partner.core.action.entity.intervention.InterventionType;
|
||||||
import work.slhaf.partner.core.action.entity.intervention.MetaIntervention;
|
import work.slhaf.partner.core.action.entity.intervention.MetaIntervention;
|
||||||
|
import work.slhaf.partner.core.cognition.BlockContent;
|
||||||
|
import work.slhaf.partner.core.cognition.CognitionCapability;
|
||||||
|
import work.slhaf.partner.core.cognition.ContextBlock;
|
||||||
|
import work.slhaf.partner.core.cognition.ContextWorkspace;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -24,16 +32,145 @@ class BuiltinInterventionActionProvider implements BuiltinActionProvider {
|
|||||||
|
|
||||||
@InjectCapability
|
@InjectCapability
|
||||||
private ActionCapability actionCapability;
|
private ActionCapability actionCapability;
|
||||||
|
@InjectCapability
|
||||||
|
private CognitionCapability cognitionCapability;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions() {
|
public List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions() {
|
||||||
return List.of(
|
return List.of(
|
||||||
buildCreateInterventionDefinition(),
|
buildCreateInterventionDefinition(),
|
||||||
buildShowAvailableMetaActionsDefinition(),
|
buildShowAvailableMetaActionsDefinition(),
|
||||||
buildShowIntervenableActionsDefinition()
|
buildShowIntervenableActionsDefinition(),
|
||||||
|
buildAcquireInterventionDefinition(),
|
||||||
|
buildResumeInterruptedActionDefinition()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BuiltinActionRegistry.BuiltinActionDefinition buildResumeInterruptedActionDefinition() {
|
||||||
|
Set<String> tags = new HashSet<>(basicTags);
|
||||||
|
tags.add("Agent Turn");
|
||||||
|
tags.add("Action Management");
|
||||||
|
|
||||||
|
MetaActionInfo info = new MetaActionInfo(
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
Map.of(
|
||||||
|
"actionId", "Uuid of the interrupted executable action to resume."
|
||||||
|
),
|
||||||
|
"Resume action that is interrupted.",
|
||||||
|
tags,
|
||||||
|
Set.of(),
|
||||||
|
Set.of(),
|
||||||
|
false,
|
||||||
|
JSONObject.of(
|
||||||
|
"result", "Plain text resume result, describes whether it is succeed or fail reason."
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Function<Map<String, Object>, String> invoker = params -> {
|
||||||
|
String actionId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "actionId");
|
||||||
|
try {
|
||||||
|
ExecutableAction executableAction = getExecutableAction(actionId);
|
||||||
|
executableAction.resume();
|
||||||
|
return "Resume succeed";
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "Failed to resume action[" + actionId + "], reason: " + e.getLocalizedMessage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new BuiltinActionRegistry.BuiltinActionDefinition(
|
||||||
|
createActionKey("resume_interrupted_action"),
|
||||||
|
info,
|
||||||
|
invoker
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试向指定用户请求干预,通过自对话通道
|
||||||
|
*
|
||||||
|
* @return 内建 MetaAction 定义数据
|
||||||
|
*/
|
||||||
|
private BuiltinActionRegistry.BuiltinActionDefinition buildAcquireInterventionDefinition() {
|
||||||
|
Set<String> tags = new HashSet<>(basicTags);
|
||||||
|
tags.add("Agent Turn");
|
||||||
|
tags.add("Action Management");
|
||||||
|
|
||||||
|
MetaActionInfo info = new MetaActionInfo(
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
Map.of(
|
||||||
|
"actionId", "Uuid of the executing action that should enter intervention flow.",
|
||||||
|
"actionInfo", "Readable summary of the current action, used to explain the intervention context to the target.",
|
||||||
|
"demand", "What feedback, decision or operation is required from the target user.",
|
||||||
|
"target", "Target user or channel identifier that should receive the intervention request.",
|
||||||
|
"input", "Prompt content used to initiate the intervention turn toward the target.",
|
||||||
|
"timeout", "Maximum wait time for the interruption result, in the unit expected by ExecutableAction.interrupt(timeout)."
|
||||||
|
),
|
||||||
|
"Try to acquire the target user to intervene this Action.",
|
||||||
|
tags,
|
||||||
|
Set.of(),
|
||||||
|
Set.of(),
|
||||||
|
false,
|
||||||
|
JSONObject.of(
|
||||||
|
"result", "Plain text intervention status. It describes whether the target answered before timeout, or returns an error reason when the turn/interruption flow fails."
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Function<Map<String, Object>, String> invoker = params -> {
|
||||||
|
String actionId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "actionId").trim();
|
||||||
|
String actionInfo = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "actionInfo").trim();
|
||||||
|
String demand = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "demand").trim();
|
||||||
|
String target = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "target").trim();
|
||||||
|
String input = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "input").trim();
|
||||||
|
int timeout = BuiltinActionRegistry.BuiltinActionDefinition.requireInt(params, "timeout");
|
||||||
|
|
||||||
|
ContextWorkspace contextWorkspace = cognitionCapability.contextWorkspace();
|
||||||
|
String blockName = "acquire_intervention-" + actionId;
|
||||||
|
String source = "action_executor";
|
||||||
|
contextWorkspace.register(new ContextBlock(
|
||||||
|
new BlockContent(blockName, source) {
|
||||||
|
@Override
|
||||||
|
protected void fillXml(@NotNull Document document, @NotNull Element root) {
|
||||||
|
appendTextElement(document, root, "action_id", actionId);
|
||||||
|
appendTextElement(document, root, "action_info", actionInfo);
|
||||||
|
appendTextElement(document, root, "demand", demand);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Set.of(ContextBlock.VisibleDomain.ACTION, ContextBlock.VisibleDomain.COMMUNICATION),
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
20
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
ExecutableAction executableAction = getExecutableAction(actionId);
|
||||||
|
cognitionCapability.initiateTurn(input, target);
|
||||||
|
boolean normal = executableAction.interrupt(timeout);
|
||||||
|
return normal ? target + "not answered" : target + "answered";
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "Error happened while calling turn: " + e.getLocalizedMessage();
|
||||||
|
} finally {
|
||||||
|
contextWorkspace.expire(blockName, source);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new BuiltinActionRegistry.BuiltinActionDefinition(
|
||||||
|
createActionKey("acquire_intervention"),
|
||||||
|
info,
|
||||||
|
invoker
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExecutableAction getExecutableAction(String actionId) {
|
||||||
|
return actionCapability.listActions(Action.Status.EXECUTING, null)
|
||||||
|
.stream()
|
||||||
|
.filter(action -> action.getUuid().equals(actionId))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于展示当前已存在的可被干预的行动
|
* 用于展示当前已存在的可被干预的行动
|
||||||
*
|
*
|
||||||
@@ -175,6 +312,10 @@ class BuiltinInterventionActionProvider implements BuiltinActionProvider {
|
|||||||
intervention.setActions(actions);
|
intervention.setActions(actions);
|
||||||
|
|
||||||
actionCapability.handleInterventions(List.of(intervention), target);
|
actionCapability.handleInterventions(List.of(intervention), target);
|
||||||
|
|
||||||
|
ExecutableAction executableAction = getExecutableAction(targetId);
|
||||||
|
executableAction.resume();
|
||||||
|
|
||||||
return JSONObject.of("ok", true).toJSONString();
|
return JSONObject.of("ok", true).toJSONString();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user