编写选择组件
完成依赖与配置后,通过 JevSwitchComponent 声明选择逻辑,再使用已有的 SWITCH 语法编排。
# 区分业务输入、判断要求与候选分支
本次用户提出、需要据此选择分支的问题放在 state() 中;instructions() 说明模型应该如何判断。 例如,用户说“暂时先不退了,能不能换一件大一号的?”,这句话属于待判断的业务输入;“识别当前诉求,忽略已经撤回的诉求”属于判断要求。
| 方法 | 职责 | 客服分流示例 |
|---|---|---|
state() | 提供本次用户问题及相关业务上下文 | 用户原话,以及订单状态等相关数据 |
instructions() | 定义本次选择的任务、判断规则和优先级 | 根据客户当前主要诉求选择流程,注意否定表达和意图变化 |
choices() | 声明可选目标 ID 及各自的适用边界 | refund 表示退款,exchange 表示换货,consult 表示咨询 |
这三个方法都是抽象方法,具体组件必须全部实现,state() 和 instructions() 不能二选一。state() 中的“状态”也包含用户问题,并不只指订单状态等业务字段。
state() 可返回字符串、可序列化为 JSON 对象的业务数据或数组,只需提供与本次判断有关的信息。instructions() 必须返回非空白字符串,通常可复用同一套判断规则,也可以根据本次上下文动态生成。两者都会在每次执行选择组件时调用,区别在于内容的用途,而非是否固定不变。
# 准备业务上下文
先定义本次流程的业务上下文。输入和决策结果都保存在上下文中,不放到共享组件的实例字段里:
import com.yomahub.liteflow.agent.jev.JevChoiceResult;
public class SupportContext {
private final String message;
private JevChoiceResult decision;
public SupportContext(String message) {
this.message = message;
}
public String getMessage() { return message; }
public JevChoiceResult getDecision() { return decision; }
public void setDecision(JevChoiceResult decision) { this.decision = decision; }
}
# 声明选择组件
选择组件直接声明候选项。choices() 的 key 为目标 ID,value 为该选项的判断说明:
import com.yomahub.liteflow.agent.jev.JevChoiceResult;
import com.yomahub.liteflow.agent.jev.JevSwitchComponent;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import java.util.LinkedHashMap;
import java.util.Map;
@LiteflowComponent("supportRouter")
public class SupportRouter extends JevSwitchComponent {
@Override
protected Object state() {
// 本次用户问题从流程上下文读取,作为待判断的数据。
return Map.of("customerMessage", getContextBean(SupportContext.class).getMessage());
}
@Override
protected String instructions() {
// 说明如何判断 state() 中的业务输入。
return "根据 customerMessage 中客户当前明确的主要诉求选择处理流程。"
+ "注意否定表达和意图变化;客户已撤回的诉求不应作为当前诉求。"
+ "无法确定或与售后无关时,选择均不适用。";
}
@Override
public Map<String, String> choices() {
Map<String, String> choices = new LinkedHashMap<>();
choices.put("refund", "客户明确希望退货退款或退回款项,且没有撤回退款诉求。");
choices.put("exchange", "客户希望更换商品,例如更换尺寸、颜色或换一个完好的商品。");
choices.put("consult", "客户咨询商品、使用方法或售后规则,尚未提出明确退款或换货要求。");
return choices;
}
@Override
protected void onDecision(JevChoiceResult result) {
getContextBean(SupportContext.class).setDecision(result);
}
}
示例中的 customerMessage 由 state() 提供,instructions() 通过这个字段名说明判断依据。下一次用户换了问题,只需传入新的 SupportContext;判断规则相同时,instructions() 的返回内容可以保持不变。
onDecision() 按需覆写。choices() 可以使用 protected;示例将其声明为 public,便于应用接口复用同一份选项定义。
# 配置流程并执行
将 refund、exchange、consult 和 manual 注册为普通 LiteFlow 组件,或替换成已有的业务组件、Agent 组件、子链。它们负责各自的后续处理,选择组件本身不执行退款等业务动作。
在 src/main/resources/flow.el.xml 中编排:
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="support">
SWITCH(supportRouter).to(refund, exchange, consult).DEFAULT(manual);
</chain>
</flow>
在已注入 FlowExecutor 的业务方法中传入上下文:
SupportContext context = new SupportContext("暂时先不退了,能不能换一件大一号的?");
LiteflowResponse response = flowExecutor.execute2Resp("support", null, context);
if (!response.isSuccess()) {
throw new IllegalStateException("客服分流执行失败", response.getCause());
}
JevChoiceResult decision = context.getDecision();
String selectedOption = decision.choice();
double confidence = decision.confidence();
Map<String, Double> probabilities = decision.probabilities();
String actualModel = decision.model();
框架使用 Jev 返回的选项 ID 执行分支,例如 supportRouter → exchange。原始 decision.choice() 与最终执行目标可能不同:低置信度时,即使模型选了 exchange,流程仍会执行 manual。
有关结果字段、阈值与兜底分支,继续阅读置信度与异常处理。



