refactor: use sequential agent for chat workflow
This commit is contained in:
@@ -3,7 +3,7 @@ package com.superbiz.agent.service;
|
||||
import com.alibaba.cloud.ai.graph.OverAllState;
|
||||
import com.alibaba.cloud.ai.graph.RunnableConfig;
|
||||
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
|
||||
import com.alibaba.cloud.ai.graph.agent.flow.agent.SupervisorAgent;
|
||||
import com.alibaba.cloud.ai.graph.agent.flow.agent.SequentialAgent;
|
||||
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -358,7 +358,7 @@ public class ChatService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 多 Agent 复杂对话执行(Planner + Executor + Supervisor)
|
||||
* 多 Agent 复杂对话执行(Planner -> Executor -> Verifier)
|
||||
*/
|
||||
public ChatResult executeChatComplex(ChatModel chatModel, ToolCallback[] toolCallbacks,
|
||||
String question, List<Map<String, String>> history) throws GraphRunnerException {
|
||||
@@ -395,18 +395,16 @@ public class ChatService {
|
||||
ReactAgent executor = buildChatExecutorAgent(chatModel, toolCallbacks, history, retryContext);
|
||||
ReactAgent verifier = buildChatVerifierAgent(chatModel);
|
||||
|
||||
SupervisorAgent supervisor = SupervisorAgent.builder()
|
||||
.name("chat_supervisor")
|
||||
.description("负责按单轮顺序调度 Planner、Executor、Verifier 的多 Agent 控制器")
|
||||
.model(chatModel)
|
||||
.systemPrompt(buildSupervisorPrompt(round))
|
||||
SequentialAgent workflow = SequentialAgent.builder()
|
||||
.name("chat_workflow")
|
||||
.description("按固定顺序执行 Planner、Executor、Verifier 的多 Agent 工作流")
|
||||
.subAgents(List.of(planner, executor, verifier))
|
||||
.build();
|
||||
|
||||
String supervisorInput = buildSupervisorInput(question, retryContext);
|
||||
Optional<OverAllState> stateOptional = supervisor.invoke(supervisorInput, config);
|
||||
String workflowInput = buildWorkflowInput(question, retryContext);
|
||||
Optional<OverAllState> stateOptional = workflow.invoke(workflowInput, config);
|
||||
if (stateOptional.isEmpty()) {
|
||||
finalDecision = buildVerifierFallbackDecision(round, "supervisor 未返回有效状态");
|
||||
finalDecision = buildVerifierFallbackDecision(round, "workflow 未返回有效状态");
|
||||
answer = buildLowConfidenceOutput(answer, finalDecision);
|
||||
persistVerifierEvaluation(session, finalDecision, round);
|
||||
break;
|
||||
@@ -420,7 +418,7 @@ public class ChatService {
|
||||
verifierOutput = invokeVerifierFallback(verifier, question, round, config);
|
||||
}
|
||||
finalDecision = parseVerifierDecision(verifierOutput, round);
|
||||
logger.debug("Supervisor round {} finished: plannerPlanLength={}, answerLength={}, verifierOutputLength={}",
|
||||
logger.debug("Sequential workflow round {} finished: plannerPlanLength={}, answerLength={}, verifierOutputLength={}",
|
||||
round,
|
||||
plannerPlan != null ? plannerPlan.length() : 0,
|
||||
answer != null ? answer.length() : 0,
|
||||
@@ -580,20 +578,20 @@ public class ChatService {
|
||||
return session;
|
||||
}
|
||||
|
||||
private String buildSupervisorInput(String question, String retryContext) {
|
||||
private String buildWorkflowInput(String question, String retryContext) {
|
||||
StringBuilder input = new StringBuilder();
|
||||
input.append("请按 supervisor 系统提示完成本轮 Planner -> Executor -> Verifier 编排。\n\n");
|
||||
input.append("请按固定工作流完成本轮 Planner -> Executor -> Verifier。\n\n");
|
||||
input.append("--- 用户问题 ---\n").append(question);
|
||||
if (retryContext != null && !retryContext.isBlank()) {
|
||||
input.append("\n\n--- retry_context ---\n").append(retryContext);
|
||||
}
|
||||
input.append("\n\n完成 verifier 后立即 FINISH,不要额外生成最终答案。");
|
||||
input.append("\n\nVerifier 完成后由外层代码读取 verifier_output 并决定最终用户输出。");
|
||||
return input.toString();
|
||||
}
|
||||
|
||||
private String invokeVerifierFallback(ReactAgent verifier, String question, int round, RunnableConfig config) {
|
||||
try {
|
||||
logger.warn("Supervisor round {} finished without verifier_output, invoking chat_verifier fallback", round);
|
||||
logger.warn("Sequential workflow round {} finished without verifier_output, invoking chat_verifier fallback", round);
|
||||
return verifier.call("请基于 executor_final_answer 和 tool_trace_summary 输出 verifier JSON。原始问题:" + question, config)
|
||||
.getText();
|
||||
} catch (Exception e) {
|
||||
@@ -685,21 +683,6 @@ public class ChatService {
|
||||
return new VerifierDecision("LOW_CONFID", 0.0, 0, List.of(), rationale, round);
|
||||
}
|
||||
|
||||
private String buildSupervisorPrompt(int round) {
|
||||
return """
|
||||
你是一个多 Agent 调度器。每一轮必须严格按顺序完成以下动作:
|
||||
1. 先调用 chat_planner 生成执行计划
|
||||
2. 再调用 chat_executor 执行计划并形成最终答案
|
||||
3. 最后调用 chat_verifier 对 executor 最终答案做事实核查
|
||||
|
||||
规则:
|
||||
- 本轮只允许完成一次 Planner -> Executor -> Verifier 链路
|
||||
- Verifier 完成后立即停止,不要继续调用任何 Agent
|
||||
- 不要自己编造答案,最终用户输出由外层代码根据 verifier_output 决定
|
||||
- 当前是第 %d 轮,保持单轮内顺序稳定
|
||||
""".formatted(round);
|
||||
}
|
||||
|
||||
private String extractStateText(Optional<OverAllState> stateOptional, String key) {
|
||||
if (stateOptional.isEmpty()) {
|
||||
return null;
|
||||
|
||||
+17
-43
@@ -31,10 +31,10 @@ import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class ChatServiceSupervisorAgentTest {
|
||||
class ChatServiceSequentialAgentTest {
|
||||
|
||||
@Test
|
||||
void executeChatComplexInvokesSupervisorFlow() throws Exception {
|
||||
void executeChatComplexInvokesSequentialWorkflow() throws Exception {
|
||||
ChatService chatService = createChatService();
|
||||
ScriptedChatModel chatModel = new ScriptedChatModel();
|
||||
|
||||
@@ -43,12 +43,12 @@ class ChatServiceSupervisorAgentTest {
|
||||
new ToolCallback[0],
|
||||
"请分析订单支付超时的原因,并给出修复建议",
|
||||
List.of(),
|
||||
"supervisor-test-session"
|
||||
"sequential-test-session"
|
||||
);
|
||||
|
||||
assertEquals("EXECUTOR_FINAL_ANSWER", result.answer());
|
||||
assertEquals("supervisor-test-session", result.sessionId());
|
||||
assertEquals(List.of("chat_planner", "chat_executor", "chat_verifier", "FINISH"), chatModel.decisions);
|
||||
assertEquals("sequential-test-session", result.sessionId());
|
||||
assertEquals(List.of("chat_planner", "chat_executor", "chat_verifier"), chatModel.agentCalls);
|
||||
assertTrue(chatModel.sawVerifierPrompt);
|
||||
}
|
||||
|
||||
@@ -78,47 +78,28 @@ class ChatServiceSupervisorAgentTest {
|
||||
new ToolCallback[0],
|
||||
"请分析订单支付超时的原因,并给出修复建议",
|
||||
List.of(),
|
||||
"supervisor-low-confidence-session"
|
||||
"sequential-low-confidence-session"
|
||||
);
|
||||
|
||||
assertTrue(result.answer().contains("EXECUTOR_FINAL_ANSWER"));
|
||||
assertEquals(List.of("chat_planner", "chat_executor", "chat_verifier", "FINISH"), chatModel.decisions);
|
||||
assertEquals(List.of("chat_planner", "chat_executor", "chat_verifier"), chatModel.agentCalls);
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeChatComplexInvokesVerifierFallbackWhenSupervisorSkipsVerifier() throws Exception {
|
||||
void executeChatComplexRunsPlannerExecutorVerifierInFixedOrder() throws Exception {
|
||||
ChatService chatService = createChatService();
|
||||
ScriptedChatModel chatModel = new ScriptedChatModel(
|
||||
List.of("chat_planner", "chat_executor", "FINISH"),
|
||||
"""
|
||||
{
|
||||
"verdict": "PASS",
|
||||
"groundedness_score": 1.0,
|
||||
"critical_fact_count": 1,
|
||||
"facts_checked": [
|
||||
{
|
||||
"fact": "executor answer generated",
|
||||
"is_critical": true,
|
||||
"verification": "direct_evidence",
|
||||
"detail": "covered by fallback verifier",
|
||||
"evidence_refs": []
|
||||
}
|
||||
],
|
||||
"rationale": "fallback verifier pass"
|
||||
}
|
||||
"""
|
||||
);
|
||||
ScriptedChatModel chatModel = new ScriptedChatModel();
|
||||
|
||||
ChatService.ChatResult result = chatService.executeChatComplex(
|
||||
chatModel,
|
||||
new ToolCallback[0],
|
||||
"请分析订单支付超时的原因,并给出修复建议",
|
||||
List.of(),
|
||||
"supervisor-verifier-fallback-session"
|
||||
"sequential-workflow-session"
|
||||
);
|
||||
|
||||
assertEquals("EXECUTOR_FINAL_ANSWER", result.answer());
|
||||
assertEquals(List.of("chat_planner", "chat_executor", "FINISH"), chatModel.decisions);
|
||||
assertEquals(List.of("chat_planner", "chat_executor", "chat_verifier"), chatModel.agentCalls);
|
||||
assertTrue(chatModel.sawVerifierPrompt);
|
||||
}
|
||||
|
||||
@@ -190,15 +171,13 @@ class ChatServiceSupervisorAgentTest {
|
||||
}
|
||||
|
||||
private static final class ScriptedChatModel implements ChatModel {
|
||||
private final List<String> decisionScript;
|
||||
private final java.util.ArrayList<String> decisions = new java.util.ArrayList<>();
|
||||
private int decisionIndex;
|
||||
private final java.util.ArrayList<String> agentCalls = new java.util.ArrayList<>();
|
||||
private String promptText = "";
|
||||
private boolean sawVerifierPrompt;
|
||||
private final String verifierOutput;
|
||||
|
||||
private ScriptedChatModel() {
|
||||
this(List.of("chat_planner", "chat_executor", "chat_verifier", "FINISH"), """
|
||||
this("""
|
||||
{
|
||||
"verdict": "PASS",
|
||||
"groundedness_score": 1.0,
|
||||
@@ -218,11 +197,6 @@ class ChatServiceSupervisorAgentTest {
|
||||
}
|
||||
|
||||
private ScriptedChatModel(String verifierOutput) {
|
||||
this(List.of("chat_planner", "chat_executor", "chat_verifier", "FINISH"), verifierOutput);
|
||||
}
|
||||
|
||||
private ScriptedChatModel(List<String> decisionScript, String verifierOutput) {
|
||||
this.decisionScript = decisionScript;
|
||||
this.verifierOutput = verifierOutput;
|
||||
}
|
||||
|
||||
@@ -230,14 +204,14 @@ class ChatServiceSupervisorAgentTest {
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
promptText = prompt.getContents();
|
||||
String text;
|
||||
if (promptText.contains("Available options:")) {
|
||||
text = "{\"agent\":\"" + decisionScript.get(decisionIndex++) + "\"}";
|
||||
decisions.add(text.substring(10, text.length() - 2));
|
||||
} else if (promptText.contains("PLANNER_TEST_PROMPT")) {
|
||||
if (promptText.contains("PLANNER_TEST_PROMPT")) {
|
||||
agentCalls.add("chat_planner");
|
||||
text = "PLANNER_PLAN";
|
||||
} else if (promptText.contains("EXECUTOR_TEST_PROMPT")) {
|
||||
agentCalls.add("chat_executor");
|
||||
text = "EXECUTOR_FINAL_ANSWER";
|
||||
} else if (promptText.contains("VERIFIER_TEST_PROMPT")) {
|
||||
agentCalls.add("chat_verifier");
|
||||
sawVerifierPrompt = true;
|
||||
text = verifierOutput;
|
||||
} else {
|
||||
Reference in New Issue
Block a user