From f01866c1a25a340c457062e60391eb7432e6f06b Mon Sep 17 00:00:00 2001 From: zhuyongxin Date: Fri, 3 Jul 2026 18:03:06 +0800 Subject: [PATCH] refactor: use sequential agent for chat workflow --- .../superbiz/agent/service/ChatService.java | 43 ++++--------- ...va => ChatServiceSequentialAgentTest.java} | 60 ++++++------------- 2 files changed, 30 insertions(+), 73 deletions(-) rename src/test/java/com/superbiz/agent/service/{ChatServiceSupervisorAgentTest.java => ChatServiceSequentialAgentTest.java} (80%) diff --git a/src/main/java/com/superbiz/agent/service/ChatService.java b/src/main/java/com/superbiz/agent/service/ChatService.java index 1bfa5fe..50415ac 100644 --- a/src/main/java/com/superbiz/agent/service/ChatService.java +++ b/src/main/java/com/superbiz/agent/service/ChatService.java @@ -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> 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 stateOptional = supervisor.invoke(supervisorInput, config); + String workflowInput = buildWorkflowInput(question, retryContext); + Optional 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 stateOptional, String key) { if (stateOptional.isEmpty()) { return null; diff --git a/src/test/java/com/superbiz/agent/service/ChatServiceSupervisorAgentTest.java b/src/test/java/com/superbiz/agent/service/ChatServiceSequentialAgentTest.java similarity index 80% rename from src/test/java/com/superbiz/agent/service/ChatServiceSupervisorAgentTest.java rename to src/test/java/com/superbiz/agent/service/ChatServiceSequentialAgentTest.java index d61ff98..a3c5dad 100644 --- a/src/test/java/com/superbiz/agent/service/ChatServiceSupervisorAgentTest.java +++ b/src/test/java/com/superbiz/agent/service/ChatServiceSequentialAgentTest.java @@ -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 decisionScript; - private final java.util.ArrayList decisions = new java.util.ArrayList<>(); - private int decisionIndex; + private final java.util.ArrayList 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 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 {