Note
Go to the end to download the full example code.
Agent¶
In this tutorial, we first focus on introducing the ReAct agent in AgentScope, then we briefly introduce how to customize your own agent from scratch.
ReAct Agent¶
In AgentScope, the ReActAgent
class integrates various features into a final implementation, including
Feature |
Reference |
---|---|
Support realtime steering |
|
Support parallel tool calls |
|
Support structured output |
|
Support fine-grained MCP control |
|
Support agent-controlled tools management (Meta tool) |
|
Support self-controlled long-term memory |
|
Support automatic state management |
Due to limited space, in this tutorial we only demonstrate the first three
features of ReActAgent
class, leaving the others to the corresponding sections
listed above.
import asyncio
import json
import os
from datetime import datetime
import time
from pydantic import BaseModel, Field
from agentscope.agent import ReActAgent
from agentscope.formatter import DashScopeChatFormatter
from agentscope.memory import InMemoryMemory
from agentscope.message import TextBlock, Msg
from agentscope.model import DashScopeChatModel
from agentscope.tool import Toolkit, ToolResponse
Realtime Steering¶
The realtime steering allows user to interrupt the agent’s reply at any time, which is implemented based on the asyncio cancellation mechanism.
Specifically, when calling the interrupt
method of the agent, it will
cancel the current reply task, and execute the handle_interrupt
method
for postprocessing.
Hint
With the feature of supporting streaming tool results in
Tool, users can interrupt the tool execution if it takes too long or
deviates from user expectations by Ctrl+C in the terminal or calling the
interrupt
method of the agent in your code.
The interruption logic has been implemented in the AgentBase
class as a
basic feature, leaving a handle_interrupt
method for users to customize the
post-processing of interruption as follows:
# code snippet of AgentBase
class AgentBase:
...
async def __call__(self, *args: Any, **kwargs: Any) -> Msg:
...
reply_msg: Msg | None = None
try:
self._reply_task = asyncio.current_task()
reply_msg = await self.reply(*args, **kwargs)
except asyncio.CancelledError:
# Catch the interruption and handle it by the handle_interrupt method
reply_msg = await self.handle_interrupt(*args, **kwargs)
...
@abstractmethod
async def handle_interrupt(self, *args: Any, **kwargs: Any) -> Msg:
pass
In ReActAgent
class, we return a fixed message “I noticed that you have
interrupted me. What can I do for you?” as follows:

Example of interruption¶
You can override it with your own implementation, for example, calling the LLM to generate a simple response to the interruption.
Parallel Tool Calls¶
ReActAgent
supports parallel tool calls by providing a parallel_tool_calls
argument in its constructor.
When multiple tool calls are generated, and parallel_tool_calls
is set to True
,
they will be executed in parallel by the asyncio.gather
function.
# prepare a tool function
def example_tool_function(tag: str) -> ToolResponse:
"""A sample example tool function"""
start_time = datetime.now().strftime("%H:%M:%S.%f")
# Sleep for 3 seconds to simulate a long-running task
time.sleep(3)
end_time = datetime.now().strftime("%H:%M:%S.%f")
return ToolResponse(
content=[
TextBlock(
type="text",
text=f"Tag {tag} started at {start_time} and ended at {end_time}. ",
),
],
)
toolkit = Toolkit()
toolkit.register_tool_function(example_tool_function)
# Create an ReAct agent
agent = ReActAgent(
name="Jarvis",
sys_prompt="You're a helpful assistant named Jarvis.",
model=DashScopeChatModel(
model_name="qwen-max",
api_key=os.environ["DASHSCOPE_API_KEY"],
),
memory=InMemoryMemory(),
formatter=DashScopeChatFormatter(),
toolkit=toolkit,
parallel_tool_calls=True,
)
async def example_parallel_tool_calls() -> None:
"""Example of parallel tool calls"""
# prompt the agent to generate two tool calls at once
await agent(
Msg(
"user",
"Generate two tool calls of the 'example_tool_function' function with tag as 'tag1' and 'tag2' AT ONCE so that they can execute in parallel.",
"user",
),
)
asyncio.run(example_parallel_tool_calls())
Jarvis: {
"type": "tool_use",
"id": "call_62c5acb20db34d1686ee7f",
"name": "example_tool_function",
"input": {
"tag": "tag1"
}
}
system: {
"type": "tool_result",
"id": "call_62c5acb20db34d1686ee7f",
"name": "example_tool_function",
"output": [
{
"type": "text",
"text": "Tag tag1 started at 09:50:27.181639 and ended at 09:50:30.184701. "
}
]
}
Jarvis: {
"type": "tool_use",
"id": "call_cbe813bd39044af4aeb0b2",
"name": "example_tool_function",
"input": {
"tag": "tag2"
}
}
system: {
"type": "tool_result",
"id": "call_cbe813bd39044af4aeb0b2",
"name": "example_tool_function",
"output": [
{
"type": "text",
"text": "Tag tag2 started at 09:50:33.106928 and ended at 09:50:36.109987. "
}
]
}
Jarvis: The 'example_tool_function' has been called with two different tags, 'tag1' and 'tag2', in a way that they can execute in parallel. Here are the results:
- For 'tag1': It started at 09:50:27.181639 and ended at 09:50:30.184701.
- For 'tag2': It started at 09:50:33.106928 and ended at 09:50:36.109987.
Please note that the actual execution in parallel may depend on the environment and system capabilities where these functions are being run. If you need further assistance or have any other requests, feel free to let me know.
Structured Output¶
To generate a structured output, the ReActAgent
instance receives a child class
of the pydantic.BaseModel
as the structured_model
argument in its __call__
function.
Then we can get the structured output from the metadata
field of the returned message.
Taking introducing Einstein as an example:
# Create an ReAct agent
agent = ReActAgent(
name="Jarvis",
sys_prompt="You're a helpful assistant named Jarvis.",
model=DashScopeChatModel(
model_name="qwen-max",
api_key=os.environ["DASHSCOPE_API_KEY"],
# Preset the generation kwargs to enable parallel tool calls
generate_kwargs={
"parallel_tool_calls": True,
},
),
memory=InMemoryMemory(),
formatter=DashScopeChatFormatter(),
toolkit=Toolkit(),
parallel_tool_calls=True,
)
# The structured model
class Model(BaseModel):
name: str = Field(description="The name of the person")
description: str = Field(
description="A one-sentence description of the person",
)
age: int = Field(description="The age")
honor: list[str] = Field(description="A list of honors of the person")
async def example_structured_output() -> None:
"""The example structured output"""
res = await agent(
Msg(
"user",
"Introduce Einstein",
"user",
),
structured_model=Model,
)
print("\nThe structured output:")
print(json.dumps(res.metadata, indent=4))
asyncio.run(example_structured_output())
Jarvis: Albert Einstein was a theoretical physicist who developed the theory of relativity, one of the two pillars of modern physics. He is best known for his mass–energy equivalence formula E = mc2.
The structured output:
{
"name": "Albert Einstein",
"description": "Theoretical physicist who developed the theory of relativity",
"age": 76,
"honor": [
"Nobel Prize in Physics (1921)",
"Copley Medal (1925)",
"Max Planck Medal (1929)"
]
}
Customizing Agent¶
AgentScope provides two base classes, AgentBase
and ReActAgentBase
, which
differ in the abstract methods they define and the hooks they support.
Specifically, the ReActAgentBase
extends AgentBase
with additional _reasoning
and _acting
abstract methods, as well as their pre- and post- hooks.
Developers can choose to inherit from either of these base classes based on their needs.
We summarize the agent under agentscope.agent
module as follows:
Class |
Abstract Method |
Support Hooks |
Description |
---|---|---|---|
|
reply observe print handle_interrupt |
pre_/post_reply
pre_/post_observe
pre_/post_print
|
The base class for all agents, providing the basic interface and hooks. |
|
reply observe print handle_interrupt _reasoning _acting |
pre_/post_reply
pre_/post_observe
pre_/post_print
pre_/post_reasoning
pre_/post_acting
|
The abstract class for ReAct agent, extending |
|
- |
pre_/post_reply
pre_/post_observe
pre_/post_print
pre_/post_reasoning
pre_/post_acting
|
An implementation of |
|
A special agent that represents the user, used to interact with the agent |
Further Reading¶
Total running time of the script: (0 minutes 26.046 seconds)