Skip to main content
This tutorial walks you through building an intelligent customer support system powered by multi-agent collaboration, showcasing AgentScope’s multi-agent orchestration capabilities. You will learn the following core features step by step:
FeaturePurpose
Structured OutputStructured output for routing decisions
MsgHubMulti-agent message broadcasting and collaboration
HandoffsAgent task delegation and dynamic dispatching
HooksHuman-in-the-Loop review

Environment Setup

# Install AgentScope
pip install agentscope

# Set the API Key
export DASHSCOPE_API_KEY=your_api_key
import asyncio
import os
from typing import Any, Literal

from pydantic import BaseModel, Field

from agentscope.agent import AgentBase, ReActAgent, UserAgent
from agentscope.formatter import (
    DashScopeChatFormatter,
    DashScopeMultiAgentFormatter,
)
from agentscope.memory import InMemoryMemory
from agentscope.message import Msg, TextBlock
from agentscope.model import DashScopeChatModel
from agentscope.pipeline import MsgHub
from agentscope.tool import Toolkit, ToolResponse

Part 1: A Single Customer Support Agent

First, let’s create a basic customer support agent. This is the simplest scenario — a single agent handles all customer inquiries.
def create_support_agent() -> ReActAgent:
    """Create a basic customer support agent."""
    model = DashScopeChatModel(
        model_name="qwen-max",
        api_key=os.environ.get("DASHSCOPE_API_KEY"),
        stream=True,
    )

    return ReActAgent(
        name="CustomerSupport",
        sys_prompt="""You are a professional customer service representative \
responsible for helping customers resolve their issues.

You should:
1. Answer customer questions politely and patiently
2. Accurately understand customer needs
3. Provide clear and helpful responses
4. For issues you cannot resolve, explain why and offer alternatives""",
        model=model,
        formatter=DashScopeChatFormatter(),
        memory=InMemoryMemory(),
        toolkit=Toolkit(),
    )


async def demo_basic_support() -> None:
    """Demonstrate the basic customer support agent."""
    agent = create_support_agent()

    queries = [
        "When will my order arrive?",
        "I'd like to return an item. How do I do that?",
        "What benefits does your membership offer?",
    ]

    for query in queries:
        print(f"\nCustomer: {query}")
        response = await agent(Msg("Customer", query, "user"))
        print(f"Support: {response.get_text_content()}")


asyncio.run(demo_basic_support())

Limitations of a Single Agent

While a single agent can handle basic conversations, it has obvious limitations:
  • Cannot handle all types of issues (technical, order, and complaint inquiries each require specialized knowledge)
  • Complex problems require collaboration across multiple areas of expertise
  • Lacks routing and dispatching mechanisms
Next, we’ll implement intelligent routing using Structured Output.

Part 2: Intelligent Routing — Issue Classification

Use Structured Output to have the agent produce structured routing decisions. By defining the output format with a Pydantic BaseModel, the agent will return structured data according to the specified schema, making it easy for downstream processing.
Tip: When using the structured_model parameter, it is recommended to set the model’s stream to False to ensure the integrity of structured output.
class RouteDecision(BaseModel):
    """Structured output for routing decisions."""

    category: Literal["technical", "order", "complaint", "general"] = Field(
        description=(
            "Issue category: technical (technical issues), "
            "order (order issues), complaint (complaints), "
            "general (general inquiries)"
        ),
    )
    confidence: float = Field(
        description="Classification confidence, between 0 and 1",
        ge=0,
        le=1,
    )
    summary: str = Field(
        description="Brief summary of the issue",
    )
    priority: Literal["low", "medium", "high"] = Field(
        description="Issue priority",
    )


def create_router_agent() -> ReActAgent:
    """Create a router agent."""
    model = DashScopeChatModel(
        model_name="qwen-max",
        api_key=os.environ.get("DASHSCOPE_API_KEY"),
        stream=False,  # Recommended to disable stream for structured output
    )

    return ReActAgent(
        name="Router",
        sys_prompt="""You are an intelligent routing system responsible for \
analyzing customer issues and deciding which specialized team should \
handle them.

Classification rules:
- technical: Product usage issues, technical failures, feature inquiries
- order: Order status, logistics, returns and exchanges
- complaint: Dissatisfaction, complaints, compensation requests
- general: Membership, promotions, general inquiries

Priority rules:
- high: Complaints, urgent order issues
- medium: General order and technical issues
- low: Inquiry-type questions""",
        model=model,
        formatter=DashScopeChatFormatter(),
        memory=InMemoryMemory(),
        toolkit=Toolkit(),
    )


async def demo_routing() -> None:
    """Demonstrate intelligent routing classification."""
    router = create_router_agent()

    queries = [
        "My order shows shipped, but I haven't received it after three days",
        "The app keeps crashing. What should I do?",
        "This is terrible! I demand a full refund!",
        "Do you have student discounts?",
    ]

    for query in queries:
        print(f"\nCustomer: {query}")

        # Use structured_model to get structured output
        response = await router(
            Msg("Customer", query, "user"),
            structured_model=RouteDecision,
        )

        # Retrieve the structured result from metadata
        decision = response.metadata
        print(f"  → Category: {decision.get('category')}")
        print(f"  → Priority: {decision.get('priority')}")
        print(f"  → Confidence: {decision.get('confidence')}")
        print(f"  → Summary: {decision.get('summary')}")


asyncio.run(demo_routing())

Advantages of Structured Output

  • Output format is controllable, facilitating programmatic processing and downstream logic branching
  • Includes meta-information like confidence scores, which can be used for fallback strategies
  • Supports Pydantic model validation, automatically constraining data types and ranges
Structured output is stored in response.metadata, while response.get_text_content() still returns the text content.

Part 3: Multi-Agent Collaboration — MsgHub and Handoff

When a system involves multiple specialized agents, a mechanism is needed for them to collaborate efficiently. AgentScope provides multiple multi-agent collaboration patterns. Here we introduce two commonly used patterns:
  1. MsgHub — Message broadcasting mode where all participants share context
  2. Handoff — Task delegation mode where the Orchestrator dynamically creates Workers to complete sub-tasks
Below we implement both patterns and then provide a comparative analysis.

3.1 MsgHub — Message Broadcasting Collaboration

MsgHub is AgentScope’s message broadcasting hub that allows multiple agents to “hear” each other’s conversations within the same context, forming a natural multi-party discussion.
                    MsgHub

    ┌─────────────────┼─────────────────┐
    ↓                 ↓                 ↓
 TechAgent       OrderAgent      ComplaintAgent
    │                 │                 │
    └────────── Message Broadcast ──────┘
  • Agent calls within a MsgHub automatically broadcast messages
  • Every agent can “hear” other agents’ replies
  • This creates a natural multi-party conversation, ideal for scenarios that require collective discussion
def create_model() -> DashScopeChatModel:
    """Create a shared model instance."""
    return DashScopeChatModel(
        model_name="qwen-max",
        api_key=os.environ.get("DASHSCOPE_API_KEY"),
        stream=True,
    )


def create_specialist_agent(
    name: str,
    specialty: str,
    description: str,
) -> ReActAgent:
    """Create a specialized customer support agent.

    Args:
        name: Agent name.
        specialty: Area of expertise.
        description: Detailed description.
    """
    return ReActAgent(
        name=name,
        sys_prompt=f"""You are {name}, specializing in {specialty}.

{description}

When collaborating with other support agents:
- Listen carefully to your colleagues' opinions
- Provide insights within your area of expertise
- If the issue is outside your specialty, suggest handing it off to the \
appropriate colleague""",
        model=create_model(),
        # Use MultiAgentFormatter for multi-agent scenarios
        formatter=DashScopeMultiAgentFormatter(),
        memory=InMemoryMemory(),
        toolkit=Toolkit(),
    )


# Create the specialized agent team
tech_agent = create_specialist_agent(
    "TechSupport",
    "technical support",
    "You are an expert in product technical issues, troubleshooting, "
    "and feature usage guidance.",
)

order_agent = create_specialist_agent(
    "OrderSupport",
    "order services",
    "You handle order inquiries, logistics tracking, and returns/exchanges.",
)

complaint_agent = create_specialist_agent(
    "ComplaintHandler",
    "complaint handling",
    "You handle customer complaints, soothe emotions, and provide solutions.",
)


async def demo_msghub() -> None:
    """Demonstrate MsgHub multi-agent collaboration.

    All participants share context within the same MsgHub.
    Each agent's reply is automatically broadcast to other participants.
    """
    customer_issue = (
        "The app on my phone won't open, and the order shows it hasn't "
        "shipped yet. This is really disappointing!"
    )

    # Use MsgHub to enable multi-agent collaboration
    async with MsgHub(
        participants=[tech_agent, order_agent, complaint_agent],
    ):
        # Technical support analyzes the technical issue first
        await tech_agent(
            Msg(
                "Coordinator",
                f"Customer issue: {customer_issue}\n"
                "Please have technical support analyze the technical problem.",
                "user",
            ),
        )

        # Order support checks the order status (can see tech support's reply)
        await order_agent(
            Msg("Coordinator", "Please have order support check the "
                "order status.", "user"),
        )

        # Complaint handler synthesizes information and provides a solution
        # (can see the discussion from the previous two agents)
        await complaint_agent(
            Msg(
                "Coordinator",
                "Please synthesize all the above information and provide "
                "the customer with a satisfactory solution.",
                "user",
            ),
        )


asyncio.run(demo_msghub())

3.2 Handoff — Task Delegation Pattern

Handoff (task handover/delegation) is another important multi-agent collaboration pattern. Unlike MsgHub’s “broadcast discussion”, Handoff uses an Orchestrator-Workers architecture:
  • An Orchestrator is responsible for decomposing tasks
  • It dynamically creates Worker agents through tool calls
  • Each Worker independently completes a sub-task and returns the result to the Orchestrator
In AgentScope, Handoff is implemented by wrapping agent creation and invocation as tool functions — when the Orchestrator calls a tool, the tool function internally creates and runs a specialized Worker agent.
Orchestrator Agent
    ├── 🔧 create_tech_worker(task)   → TechWorker Agent
    ├── 🔧 create_order_worker(task)  → OrderWorker Agent
    └── 🔧 create_complaint_worker(task) → ComplaintWorker Agent
The core advantages of this pattern are:
  1. Dynamism: Workers can be created on-demand or pre-defined
  2. Context isolation: Workers do not share context with each other, reducing context pressure. The Orchestrator autonomously decides which tool to call (i.e., which specialized agent to delegate to).
First, let’s define the tool functions that Workers will use:
def query_order(order_id: str) -> ToolResponse:
    """Query order status.

    Args:
        order_id (``str``):
            The order ID.
    """
    # Simulate order data
    orders = {
        "12345": {"status": "shipped", "eta": "2024-01-20"},
        "67890": {"status": "processing", "eta": "2024-01-22"},
    }
    order = orders.get(order_id, {"status": "not_found"})
    return ToolResponse(
        content=[TextBlock(type="text", text=f"Order {order_id}: {order}")],
    )
Next, define the tool functions for creating each type of specialized Worker:
async def create_tech_worker(task_description: str) -> ToolResponse:
    """Create a technical support Worker agent to handle technical issues.

    Args:
        task_description (``str``):
            Description of the technical issue to handle.
    """
    worker = ReActAgent(
        name="TechWorker",
        sys_prompt=(
            "You are a technical support specialist, skilled in product "
            "technical issues, troubleshooting, and feature usage guidance. "
            "Please answer the user's technical questions professionally "
            "and accurately."
        ),
        model=DashScopeChatModel(
            model_name="qwen-max",
            api_key=os.environ.get("DASHSCOPE_API_KEY"),
            stream=False,
        ),
        formatter=DashScopeChatFormatter(),
        memory=InMemoryMemory(),
        toolkit=Toolkit(),
    )
    res = await worker(Msg("user", task_description, "user"))
    return ToolResponse(content=res.get_content_blocks("text"))


async def create_order_worker(task_description: str) -> ToolResponse:
    """Create an order service Worker agent to handle order-related issues.

    Args:
        task_description (``str``):
            Description of the order issue to handle.
    """
    # Equip the order Worker with the order query tool
    toolkit = Toolkit()
    toolkit.register_tool_function(query_order)

    worker = ReActAgent(
        name="OrderWorker",
        sys_prompt="You are an order service specialist, responsible for "
        "order inquiries, logistics tracking, and returns/exchanges.",
        model=DashScopeChatModel(
            model_name="qwen-max",
            api_key=os.environ.get("DASHSCOPE_API_KEY"),
            stream=False,
        ),
        formatter=DashScopeChatFormatter(),
        memory=InMemoryMemory(),
        toolkit=toolkit,
    )
    res = await worker(Msg("user", task_description, "user"))
    return ToolResponse(content=res.get_content_blocks("text"))


async def create_complaint_worker(task_description: str) -> ToolResponse:
    """Create a complaint handling Worker agent to handle customer complaints.

    Args:
        task_description (``str``):
            Description of the complaint to handle.
    """
    worker = ReActAgent(
        name="ComplaintWorker",
        sys_prompt=(
            "You are a complaint handling specialist, responsible for "
            "dealing with customer complaints, soothing emotions, and "
            "providing reasonable solutions. Your goal is to ensure "
            "customer satisfaction."
        ),
        model=DashScopeChatModel(
            model_name="qwen-max",
            api_key=os.environ.get("DASHSCOPE_API_KEY"),
            stream=False,
        ),
        formatter=DashScopeChatFormatter(),
        memory=InMemoryMemory(),
        toolkit=Toolkit(),
    )
    res = await worker(Msg("user", task_description, "user"))
    return ToolResponse(content=res.get_content_blocks("text"))
Now create the Orchestrator and register the Worker creation functions as tools:
async def demo_handoff() -> None:
    """Demonstrate the Handoff (Orchestrator-Workers) pattern.

    The Orchestrator autonomously decides which specialized Worker to
    delegate to via tool calls.
    """
    # Register the specialized Worker creation functions as tools
    toolkit = Toolkit()
    toolkit.register_tool_function(create_tech_worker)
    toolkit.register_tool_function(create_order_worker)
    toolkit.register_tool_function(create_complaint_worker)

    orchestrator = ReActAgent(
        name="Orchestrator",
        sys_prompt="""You are the Orchestrator of the customer support system.

Your responsibilities are:
1. Analyze customer issues and determine which specialized support is needed
2. Delegate sub-tasks to the appropriate Workers (via tool calls)
3. Summarize the results from each Worker and provide the final response

For complex issues, you can delegate to multiple Workers in parallel.""",
        model=DashScopeChatModel(
            model_name="qwen-max",
            api_key=os.environ.get("DASHSCOPE_API_KEY"),
            stream=False,
            generate_kwargs={"parallel_tool_calls": True},
        ),
        memory=InMemoryMemory(),
        formatter=DashScopeChatFormatter(),
        toolkit=toolkit,
        parallel_tool_calls=True,
    )

    # A complex customer issue involving multiple aspects
    customer_issue = (
        "The app on my phone won't open, and the order shows it hasn't "
        "shipped yet. This is really disappointing!"
    )

    print(f"Customer: {customer_issue}")
    response = await orchestrator(
        Msg("Customer", customer_issue, "user"),
    )
    print(f"\nFinal Response:\n{response.get_text_content()}")


asyncio.run(demo_handoff())

3.3 MsgHub vs Handoff — Comparison and Selection Guide

Below is a comparison of the core differences between the two multi-agent collaboration patterns:
FeatureMsgHub (Message Broadcasting)Handoff (Task Delegation)
Collaboration ModeEqual discussion, multi-party conversationHierarchical division, task dispatching
Context SharingAll participants share the complete conversation contextWorkers only receive the task description assigned by the Orchestrator
Execution FlowRequires explicit orchestration of call orderOrchestrator autonomously decides which Worker to invoke
Use CasesMulti-party brainstorming, cross-referencing opinions, debate and discussion scenariosClear task decomposition, independent sub-task processing, parallelizable scenarios
Communication OverheadHigh (each message is broadcast to all participants)Low (communication only between Orchestrator and Workers)
FlexibilityParticipants must be known in advanceNew Worker types can be created dynamically
Selection Guidelines:
  • If the problem requires multiple experts to discuss and reference each other’s opinions → Use MsgHub
  • If the problem can be decomposed into independent sub-tasks → Use Handoff
  • In complex systems, both can be used together: the Orchestrator delegates sub-tasks via Handoff, while certain sub-tasks internally use MsgHub for multi-agent collaborative discussion

Part 4: Human-in-the-Loop — Using Hooks for Manual Review

In critical business scenarios, AI-generated responses may need manual review before being sent to the customer. AgentScope’s Hook mechanism provides an elegant way to implement Human-in-the-Loop without modifying the agent’s core code. Hooks are extension features of the agent’s core functions, allowing custom logic to be injected before and after execution:
Agent.__call__()
  ├── pre_reply hooks   ← Review/modify input before agent replies
  ├── reply()            ← Agent core logic
  └── post_reply hooks  ← Review/modify output after agent replies
For ReActAgent, there are more fine-grained hooks:
ReActAgent.reply()
  ├── pre_reasoning / post_reasoning  ← Before/after reasoning
  └── pre_acting / post_acting        ← Before/after action execution
Tip: Hooks are implemented via metaclass and support inheritance. Subclasses automatically inherit Hook support from parent classes.
The most common Human-in-the-Loop scenario is: after the agent generates a response, a human reviewer confirms whether it is satisfactory. If not, the agent is asked to regenerate.
Important: Hook function signatures are fixed:
  • Pre-hook: (self, kwargs) -> dict | None
  • Post-hook: (self, kwargs, output) -> Any | None
Where self is the agent instance, kwargs is the function arguments dictionary, and output is the function return value. Returning None means no modification.
async def human_review_post_reply_hook(
    self: AgentBase,
    kwargs: dict[str, Any],
    output: Msg,
) -> Msg | None:
    """Post-reply hook: perform manual review after the agent replies.

    If the manual review does not pass, the review feedback is appended
    to the original input and the agent is called again to generate a
    new response.

    Args:
        self: The agent instance.
        kwargs: The argument dictionary of the reply function.
        output: The response message generated by the agent.

    Returns:
        Returns None if the review passes (keeping the original reply),
        otherwise returns the revised reply.
    """
    print("\n" + "=" * 50)
    print("[Manual Review] Agent response:")
    print(f"  {output.get_text_content()}")
    print("=" * 50)

    # Use UserAgent to get human input
    human = UserAgent(name="Reviewer")
    review_msg = await human(
        Msg(
            "System",
            "Please review the above response. Type 'ok' to approve, "
            "or enter your revision feedback:",
            "user",
        ),
    )

    review_text = review_msg.get_text_content().strip()

    if review_text.lower() == "ok":
        print("[Review Approved] Response confirmed.")
        return None  # Return None to keep the original output

    # Review rejected: append feedback to the original message and regenerate
    print(f"[Review Rejected] Feedback: {review_text}")
    original_msg = kwargs.get("msg")
    if original_msg is not None:
        # Temporarily remove the hook to avoid infinite loops
        self.clear_instance_hooks("post_reply")

        revised_msg = Msg(
            original_msg.name,
            f"{original_msg.get_text_content()}\n\n"
            f"[Review Feedback] Please revise based on the following "
            f"feedback: {review_text}",
            original_msg.role,
        )
        revised_output = await self.reply(revised_msg)

        # Re-register the hook
        self.register_instance_hook(
            hook_type="post_reply",
            hook_name="human_review",
            hook=human_review_post_reply_hook,
        )

        return revised_output

    return None


async def demo_human_review_hook() -> None:
    """Demonstrate using a post_reply Hook for manual review."""
    agent = create_support_agent()

    # Register the manual review hook
    agent.register_instance_hook(
        hook_type="post_reply",
        hook_name="human_review",
        hook=human_review_post_reply_hook,
    )

    response = await agent(
        Msg(
            "Customer",
            "I bought your product and the quality is terrible. "
            "I demand a refund!",
            "user",
        ),
    )
    print(f"\nFinal Response: {response.get_text_content()}")


asyncio.run(demo_human_review_hook())

Part 5: Complete Customer Support System

Integrate all components — routing, MsgHub collaboration, Handoff delegation, and Human-in-the-Loop Hooks — to build a complete multi-agent customer support system.
class ResolutionReport(BaseModel):
    """Structured output for resolution reports."""

    resolved: bool = Field(description="Whether the issue is resolved")
    solution: str = Field(description="The solution provided")
    follow_up: str = Field(description="Follow-up action items")


class CustomerSupportSystem:
    """Multi-agent customer support system.

    Integrates routing, multi-agent collaboration, and manual review.

    Attributes:
        enable_human_review: Whether to enable the manual review Hook.
    """

    def __init__(self, enable_human_review: bool = False) -> None:
        self.model = DashScopeChatModel(
            model_name="qwen-max",
            api_key=os.environ.get("DASHSCOPE_API_KEY"),
            stream=True,
        )
        self.enable_human_review = enable_human_review

        # Create the agents
        self.router = self._create_router()
        self.tech_agent = self._create_specialist(
            "technical support",
            "TechSupport",
        )
        self.order_agent = self._create_specialist(
            "order services",
            "OrderSupport",
        )
        self.complaint_agent = self._create_specialist(
            "complaint handling",
            "ComplaintHandler",
        )
        self.supervisor = self._create_supervisor()

        # If manual review is enabled, register the hook for the supervisor
        if self.enable_human_review:
            self.supervisor.register_instance_hook(
                hook_type="post_reply",
                hook_name="human_review",
                hook=human_review_post_reply_hook,
            )

    def _create_router(self) -> ReActAgent:
        """Create the router agent."""
        return ReActAgent(
            name="Router",
            sys_prompt="You are an intelligent routing system that analyzes "
            "customer issues and classifies them.",
            model=DashScopeChatModel(
                model_name="qwen-max",
                api_key=os.environ.get("DASHSCOPE_API_KEY"),
                stream=False,
            ),
            formatter=DashScopeChatFormatter(),
            memory=InMemoryMemory(),
            toolkit=Toolkit(),
        )

    def _create_specialist(
        self,
        specialty: str,
        name: str,
    ) -> ReActAgent:
        """Create a specialized customer support agent."""
        toolkit = Toolkit()
        if "order" in specialty:
            toolkit.register_tool_function(query_order)

        return ReActAgent(
            name=name,
            sys_prompt=f"You are a {specialty} specialist, professionally "
            f"handling related issues.",
            model=self.model,
            formatter=DashScopeMultiAgentFormatter(),
            memory=InMemoryMemory(),
            toolkit=toolkit,
        )

    def _create_supervisor(self) -> ReActAgent:
        """Create the supervisor agent."""
        return ReActAgent(
            name="Supervisor",
            sys_prompt="You are a customer service supervisor, responsible "
            "for monitoring service quality and summarizing results.",
            model=self.model,
            formatter=DashScopeMultiAgentFormatter(),
            memory=InMemoryMemory(),
            toolkit=Toolkit(),
        )

    async def handle_customer(
        self,
        customer_id: str,
        issue: str,
    ) -> str:
        """Main workflow for handling customer issues.

        Flow: Route classification → Assign specialist agent →
        MsgHub collaboration → Supervisor review

        Args:
            customer_id: The customer ID.
            issue: Description of the customer's issue.

        Returns:
            The final response text.
        """
        print(f"\n{'=' * 60}")
        print(f"[New Customer Issue] {customer_id}: {issue}")
        print("=" * 60)

        # Step 1: Route classification
        route_response = await self.router(
            Msg("System", f"Analyze this customer issue: {issue}", "user"),
            structured_model=RouteDecision,
        )
        decision = route_response.metadata
        category = decision.get("category", "general")
        priority = decision.get("priority", "medium")

        print(f"\n[Routing Decision] Category: {category}, "
              f"Priority: {priority}")

        # Step 2: Assign to a specialist agent
        specialist_map = {
            "technical": self.tech_agent,
            "order": self.order_agent,
            "complaint": self.complaint_agent,
            "general": self.tech_agent,
        }
        specialist = specialist_map.get(category, self.tech_agent)

        # Step 3: Multi-agent collaborative handling (MsgHub)
        async with MsgHub(participants=[specialist, self.supervisor]):
            # Specialist agent handles the issue
            await specialist(
                Msg(
                    "System",
                    f"Please handle this customer issue: {issue}",
                    "user",
                ),
            )

            # Supervisor reviews and summarizes (if the human_review hook
            # is enabled, manual review will be triggered automatically)
            final_response = await self.supervisor(
                Msg(
                    "System",
                    "Please review the handling result and provide "
                    "the final response.",
                    "user",
                ),
                structured_model=ResolutionReport,
            )

        return final_response.get_text_content()

Run the Complete System

async def main() -> None:
    """Run the complete multi-agent customer support system."""
    # Set enable_human_review=True to enable manual review
    system = CustomerSupportSystem(enable_human_review=False)

    # Simulate multiple customer issues
    customer_issues = [
        ("C001", "Your app keeps crashing. I can't use it at all!"),
        ("C002", "Has my order 12345 shipped? When will it arrive?"),
        (
            "C003",
            "I strongly protest! The product quality is terrible. "
            "I demand a refund!",
        ),
    ]

    for customer_id, issue in customer_issues:
        response = await system.handle_customer(customer_id, issue)
        print(f"\n[Final Response]\n{response}")
        print("\n" + "-" * 60)


asyncio.run(main())

Next Steps

Congratulations on completing this tutorial! You have mastered AgentScope’s multi-agent orchestration capabilities. Next, you can explore:
  • Hooking Functions — Detailed usage of agent hooks, including Human-in-the-Loop patterns
  • Orchestration — In-depth guide to routing strategies, MsgHub, and Orchestrator-Workers patterns