Introduce multiagent pattern practices and docs.#910
Introduce multiagent pattern practices and docs.#910chickenlj wants to merge 8 commits intoagentscope-ai:mainfrom
Conversation
|
|
There was a problem hiding this comment.
Pull request overview
This PR introduces seven multi-agent pattern examples for AgentScope (Supervisor, Pipeline, Routing, Skills, Subagent, Handoffs, and Workflow), along with corresponding documentation updates in both English and Chinese.
Changes:
- Added seven new Spring Boot example modules under
agentscope-examples/multiagent-patterns/demonstrating various multi-agent orchestration patterns built on Spring AI Alibaba Graph and AgentScope. - Added new documentation pages in
docs/en/multi-agent/anddocs/zh/multi-agent/covering each pattern, plus a newagent-as-tool.mdfeature doc. - Updated
docs/_toc.ymlto reorganize the Multi Agent section and add the new pattern pages, and corrected cross-reference links in existing docs.
Reviewed changes
Copilot reviewed 137 out of 139 changed files in this pull request and generated 29 comments.
Show a summary per file
| File | Description |
|---|---|
agentscope-examples/pom.xml |
Registers the 7 new multiagent-patterns submodules |
agentscope-examples/multiagent-patterns/supervisor/ |
Supervisor pattern: central ReActAgent delegates calendar/email tasks as sub-agent tools |
agentscope-examples/multiagent-patterns/pipeline/ |
Pipeline patterns: SequentialAgent, ParallelAgent, LoopAgent with AgentScopeAgent sub-agents |
agentscope-examples/multiagent-patterns/routing/ |
Routing pattern: classifies queries, invokes GitHub/Notion/Slack specialist agents in parallel |
agentscope-examples/multiagent-patterns/skills/ |
Skills (progressive disclosure) pattern: SQL assistant loads skill schemas on demand |
agentscope-examples/multiagent-patterns/subagent/ |
Subagent pattern: orchestrator delegates to Markdown/API-defined sub-agents via Task tools |
agentscope-examples/multiagent-patterns/workflow/ |
Custom workflow pattern: RAG and SQL agents using StateGraph with H2 in-memory DB |
agentscope-examples/multiagent-patterns/handoffs/ |
Handoffs pattern: sales/support agents with state-driven routing between graph nodes |
agentscope-examples/quarkus/src/main/resources/application.properties |
Adds native build configuration |
docs/_toc.yml |
Adds new multi-agent pattern pages to TOC, reorganizes the Multi Agent section |
docs/en/task/agent-as-tool.md |
New English documentation for Agent as Tool feature |
docs/en/multi-agent/supervisor.md, skills.md |
New English pattern documentation |
docs/zh/multi-agent/supervisor.md, skills.md |
New Chinese pattern documentation |
docs/en/multi-agent/multiagent-debate.md, docs/zh/multi-agent/multiagent-debate.md |
Updated links to new patterns |
docs/en/task/tool.md, docs/zh/task/tool.md |
Fixed relative links to agent-as-tool.md |
docs/en/task/msghub.md, docs/zh/task/msghub.md |
Fixed cross-directory links to pipeline/multiagent-debate |
docs/en/intro.md, docs/zh/intro.md |
Removed MsgHub from top-level multi-agent section |
| /* | ||
| * Copyright 2025-2026 the original author or authors. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| package com.alibaba.cloud.ai.examples.multiagents.workflow.sqlagent; | ||
|
|
||
| import com.alibaba.cloud.ai.graph.CompiledGraph; | ||
| import com.alibaba.cloud.ai.graph.OverAllState; | ||
| import com.alibaba.cloud.ai.graph.exception.GraphRunnerException; | ||
| import org.springframework.ai.chat.messages.AssistantMessage; | ||
| import org.springframework.ai.chat.messages.Message; | ||
| import org.springframework.ai.chat.messages.UserMessage; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Optional; | ||
|
|
||
| /** | ||
| * Service that invokes the SQL agent graph. | ||
| */ | ||
| public class SqlAgentService { | ||
|
|
||
| private final CompiledGraph graph; | ||
|
|
||
| public SqlAgentService(CompiledGraph graph) { | ||
| this.graph = graph; | ||
| } | ||
|
|
||
| /** | ||
| * Run the SQL agent with the given question. | ||
| */ | ||
| public SqlAgentResult run(String question) throws GraphRunnerException { | ||
| Map<String, Object> inputs = Map.of("messages", List.of(new UserMessage(question)), "question", question); | ||
| Optional<OverAllState> resultOpt = graph.invoke(inputs); | ||
|
|
||
| if (resultOpt.isEmpty()) { | ||
| return new SqlAgentResult(question, null, null); | ||
| } | ||
|
|
||
| OverAllState state = resultOpt.get(); | ||
| @SuppressWarnings("unchecked") | ||
| List<Message> messages = (List<Message>) state.value("messages").orElse(List.of()); | ||
| String answer = messages.stream() | ||
| .filter(m -> m instanceof AssistantMessage) | ||
| .map(m -> m instanceof org.springframework.ai.chat.messages.AssistantMessage am ? am.getText() : "") | ||
| .reduce((a, b) -> b) | ||
| .orElse(null); | ||
|
|
||
| return new SqlAgentResult(question, answer, state); | ||
| } | ||
|
|
||
| public record SqlAgentResult(String question, String answer, OverAllState state) { | ||
| } |
There was a problem hiding this comment.
The SqlAgentService and RagAgentService have nearly identical implementations with the same run method pattern, result record structure, and state extraction logic. The only differences are the graph instance and the input map construction. This duplication could be refactored into a shared base class or utility, following the DRY principle.
| ## Related Documentation | ||
|
|
||
| - [Agent as Tool](../task/agent-as-tool.md) - Sub-agent registration and SubAgentConfig | ||
| - [Subagents](./subagent.md) - Orchestrator with Task/TaskOutput and Markdown/API sub-agents | ||
| - [Pipeline](./pipeline.md) - Sequential and parallel agent composition |
There was a problem hiding this comment.
The en/multi-agent/supervisor.md and zh/multi-agent/supervisor.md files both end without a final newline (no line after line 57 in the English version). The related documentation pages should have a closing newline for proper markdown rendering, but more importantly, the English supervisor.md doesn't end with a newline character at line 57. Additionally, the same issue applies to skills.md for both languages. Consider also that these files lack a handoffs.md link in their "Related Documentation" sections even though multiagent-debate.md links to handoffs—and supervisor/skills are all part of the same multiagent patterns family.
| name: skills-sql-assistant | ||
|
|
||
| # Set to true to run the skills (progressive disclosure) demo on startup. | ||
| skills.runner.enabled: true |
There was a problem hiding this comment.
The skills/src/main/resources/application.yml also defaults skills.runner.enabled: true. This will cause the skills demo to run automatically on startup with real LLM API calls. Consider defaulting to false.
| # Set to true to run the AgentScope multi-agent handoffs demo on startup. | ||
| agentscope.runner.enabled: true |
There was a problem hiding this comment.
The pipeline.runner.enabled and workflow.runner.enabled flags are used, but handoffs/src/main/resources/application.yml defaults agentscope.runner.enabled: true, which also defaults pipeline.runner.enabled: true in pipeline/src/main/resources/application.yml. These are fine if intentional for demos, but are inconsistent with the PR description which says these flags are "optional". However note: both supervisor.run-examples (not runner.enabled) inconsistency makes it harder for users to understand which property controls what.
| - [subagents.md](../../../multiagents/subagents.md) - Subagent architecture documentation | ||
| - [spring-ai-agent-utils subagent-demo](../../../multiagents/spring-ai-agent-utils/examples/subagent-demo) - Similar pattern with Spring AI community tools |
There was a problem hiding this comment.
The subagent/README.md at line 113 references ../../../multiagents/subagents.md and ../../../multiagents/spring-ai-agent-utils/examples/subagent-demo, which appear to be relative paths to files that are not part of this repository. These references will be broken links in the rendered README.
| URL u = new URL(url); | ||
| try (InputStream in = u.openStream(); | ||
| Scanner scanner = new Scanner(in, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) { | ||
| String content = scanner.hasNext() ? scanner.next() : ""; | ||
| if (content.length() > 15000) { | ||
| content = content.substring(0, 15000) + "\n...[truncated]"; | ||
| } | ||
| return content; | ||
| } |
There was a problem hiding this comment.
The WebFetchTool.webFetch method uses new URL(url) and u.openStream() which does not set connection or read timeouts. A slow or non-responding URL will block the calling thread indefinitely, which in a reactive/agentic context can cause the entire agent invocation to hang. Consider using HttpURLConnection with explicit timeout settings, or a proper HTTP client library with timeout support.
| if (query.toUpperCase().contains("INSERT") || query.toUpperCase().contains("UPDATE") | ||
| || query.toUpperCase().contains("DELETE") || query.toUpperCase().contains("DROP")) { | ||
| return "Error: Only SELECT queries are allowed."; | ||
| } |
There was a problem hiding this comment.
The SqlTools.runQuery method performs a keyword-based blocklist check to prevent write operations (INSERT, UPDATE, DELETE, DROP), but this approach can be bypassed. For example, a query like SELECT * FROM (DELETE FROM table RETURNING *) t or a comment injection may evade the string-contains check. Also the check is case-insensitive via .toUpperCase() but the check for "DROP" does not cover "TRUNCATE" or "ALTER" statements. Since this is a demo/example, this is a moderate concern rather than a critical one, but it should at least be documented as a limitation.
| } | ||
| catch (Exception e) { | ||
| return true; | ||
| } | ||
| } |
There was a problem hiding this comment.
The BackgroundTask.waitForCompletion method at line 114 catches a generic Exception (which would include ExecutionException from completed-exceptionally futures) and returns true. This is misleading: it signals the caller that the task completed, even though it completed with an error. The caller in TaskOutputTool then calls bgTask.isCompleted() but the distinction between completed-normally and completed-exceptionally is lost for the wait return value. The ExecutionException case should either be documented explicitly or return true but the method name better reflects "isDone" rather than "completed successfully".
| toolkit.registration() | ||
| .subAgent(() -> calendarAgent) | ||
| .apply(); | ||
| toolkit.registration() | ||
| .subAgent(() -> emailAgent) | ||
| .apply(); |
There was a problem hiding this comment.
The supervisor/SupervisorConfig.supervisorAgent method registers calendarAgent and emailAgent as sub-agents via lambda: () -> calendarAgent and () -> emailAgent. These lambdas always return the same instance (the Spring-managed bean). According to the documentation in agent-as-tool.md (line 49), it's noted that lambdas must create a new instance for each call. However, here the same bean instance is returned, which means the sub-agent's InMemoryMemory will accumulate conversation history across multiple supervisor calls, potentially causing context confusion or memory growth.
| import com.alibaba.cloud.ai.graph.agent.tools.task.BackgroundTask; | ||
|
|
||
| import java.util.function.Supplier; | ||
|
|
||
| /** | ||
| * Repository for managing background tasks. | ||
| * <p> | ||
| * Inspired by spring ai TaskRepository, enables the main agent to | ||
| * launch sub-agents in the background and retrieve results later via TaskOutputTool. | ||
| * | ||
| */ | ||
| public interface TaskRepository { | ||
|
|
||
| /** | ||
| * Get a background task by its ID. | ||
| * @param taskId the task identifier | ||
| * @return the background task, or null if not found | ||
| */ | ||
| BackgroundTask getTask(String taskId); | ||
|
|
||
| /** | ||
| * Add a new background task to the repository. | ||
| * @param taskId the task identifier | ||
| * @param taskExecution the supplier that executes the task and returns its output | ||
| * @return the created background task | ||
| */ | ||
| BackgroundTask putTask(String taskId, Supplier<String> taskExecution); |
There was a problem hiding this comment.
The TaskRepository interface at lines 18-19 imports com.alibaba.cloud.ai.graph.agent.tools.task.BackgroundTask, but the BackgroundTask class in this module is defined in io.agentscope.examples.subagent.tools.task.BackgroundTask. This import of a different BackgroundTask from com.alibaba.cloud.ai.graph in the interface but a locally-defined BackgroundTask in DefaultTaskRepository suggests a class naming conflict. The DefaultTaskRepository uses the local BackgroundTask, while the interface signature imports from com.alibaba.cloud.ai.graph. This is a potential compilation error or type mismatch at runtime.
Multi-agent systems coordinate specialized agents or components to handle complex workflows. Not every complex task needs multiple agents—a single agent with the right tools and prompt can often suffice.
This pull request introduces several supervisor, handoffs, routing, etc. multiagent patterns that are built on Spring AI Alibaba Graph and AgentScope.
Examples
1. Supervisor
agentscope-examples/multiagent-patterns/supervisorschedule_event,manage_email), invokes them from user requests, and combines results. Specialist capabilities are implemented as AgentScope ReActAgents with Model and registered viaToolkit.registration().subAgent()../mvnw -pl agentscope-examples/multiagent-patterns/supervisor spring-boot:run. Optionally setsupervisor.run-examples=trueto run two sample conversations (calendar + email) on startup.2. Pipeline
agentscope-examples/multiagent-patterns/pipelinesequential_sql_agent): Natural language → SQL generation → SQL scoring.parallel_research_agent): One topic researched in parallel from technology, finance, and market angles; results merged into one report.loop_sql_refinement_agent): Loops “generate SQL → score” until score > 0.5../mvnw -pl agentscope-examples/multiagent-patterns/pipeline spring-boot:run. Optionally setpipeline.runner.enabled=trueto run all three pipeline demos on startup.3. Routing
agentscope-examples/multiagent-patterns/routingAgentScopeRoutingAgent+RouterService(invoke router then synthesize)../mvnw -pl agentscope-examples/multiagent-patterns/routing spring-boot:run. Optionally setrouting.runner.enabled=trueorrouting-graph.runner.enabled=truefor the corresponding demo.4. Skills (progressive disclosure)
agentscope-examples/multiagent-patterns/skillsskills/(e.g. sales_analytics, inventory_management) via AgentScope ClasspathSkillRepository and SkillBox. The model sees only skill descriptions first and loads full SKILL.md on demand with the read_skill tool../mvnw -pl agentscope-examples/multiagent-patterns/skills spring-boot:run. Optionally setskills.runner.enabled=trueto run one sample query on startup.5. Subagent
agentscope-examples/multiagent-patterns/subagent./mvnw -pl agentscope-examples/multiagent-patterns/subagent spring-boot:run. Optionally setsubagent.run-interactive=truefor interactive chat.6. Handoffs
agentscope-examples/multiagent-patterns/handoffstransfer_to_support,transfer_to_sales) update state (e.g.active_agent); the graph routes between nodes based on that state for role handoffs over multiple turns../mvnw -pl agentscope-examples/multiagent-patterns/handoffs spring-boot:run. Optionally setagentscope.runner.enabled=truefor the demo. Default port 8089; can be used with the chat UI.7. Workflow (custom)
agentscope-examples/multiagent-patterns/workflowworkflow.rag.enabled=true): Query → rewrite → retrieve → prepare → Agent (ReActAgent + context) → response.workflow.sql.enabled=true): list_tables → get_schema → generate_query (AgentScopeAgent + SQL tools), using H2 in-memory with a Chinook-like schema../mvnw -pl agentscope-examples/multiagent-patterns/workflow spring-boot:run -Dspring-boot.run.arguments="--workflow.rag.enabled=true --workflow.runner.enabled=true"--workflow.sql.enabled=trueinstead of--workflow.rag.enabled=true.