Skip to main content
Message and Event are the two fundamental data structures in AgentScope.
  • Message — the unit of inter-agent communication and persistence. Each Msg represents a complete conversation turn that is stored in context and exchanged between agents.
  • Event — the unit of frontend interaction and streaming. Events carry incremental progress updates (text tokens, tool call fragments, permission requests) and drive real-time UIs and human-in-the-loop workflows.
A sequence of events produced during a single reply call accumulates into exactly one assistant Msg. This guarantees that the complete message state is always recoverable from its event stream.

Message

An instance of the Msg class in AgentScope contains a complete conversation turn — a user input, or a complete assistant response, organized through different types of content blocks (Blocks).
  1. Running an agent’s reply_stream once produces a complete Msg instance, containing all information such as multiple rounds of reasoning, tool calls, and execution results.
  2. During frontend rendering, a Msg instance corresponds to a single complete message bubble.

Structure

The Msg class has the following core fields:
FieldTypeDescription
idstrUnique message identifier
namestrName of the sender
role"user" | "assistant" | "system"The sender’s role
contentlist[ContentBlock]Ordered list of content blocks
metadatadictArbitrary key-value metadata
created_atstrISO 8601 timestamp of creation
finished_atstr | NoneISO 8601 timestamp when the message was finalized
usageUsageToken usage statistics (for assistant messages)

Content Blocks

Message content is composed of typed blocks. Each block represents a distinct piece of information:
Block TypeDescription
TextBlockPlain text content
DataBlockBinary data (images, audio, video, etc.), either via base64 or URL
ThinkingBlockModel reasoning (chain-of-thought)
ToolCallBlockTool call, containing name, input, and state
ToolResultBlockTool execution result
HintBlockHint information (e.g., scheduled-task triggers, team messages, background tool results); supports multimodal data and uses source to identify the hint’s origin.
Role constraints are enforced at construction:
  • msg.role=="user" messages can only contain TextBlock and DataBlock;
  • msg.role=="system" messages can only contain TextBlock;
  • msg.role=="assistant" messages can contain all block types.
These content blocks carry different data information, and their detailed fields are described below:
FieldTypeDescription
typestrFixed as "text".
textstrThe actual text content.
idstrThe unique identifier of the content block (auto-generated UUID by default).
This content block allows passing through custom metadata defined by the model provider (such as the signature in Anthropic models, etc.).
FieldTypeDescription
typestrFixed as "thinking".
thinkingstrThe reasoning or chain-of-thought text of the model.
idstrThe unique identifier of the content block (auto-generated UUID by default).
FieldTypeDescription
typestrFixed as "data".
idstrThe unique identifier of the content block (auto-generated UUID by default).
sourceBase64Source | URLSourceIdentifies the data source. Supports Base64 input or URL input.
namestr | NoneOptional field, indicating the name of this content asset.
Data Source Configuration:
  • Base64Source:
    • type: Fixed as "base64".
    • data: Base64-encoded binary data.
    • media_type: Media type (e.g., "image/png", "audio/mpeg", "video/mp4", etc.).
  • URLSource:
    • type: Fixed as "url".
    • url: A valid URI/URL string satisfying the RFC 3986 standard.
    • media_type: Media type (e.g., "image/png", "audio/wav", etc.).
When finally passed to the LLM API, HintBlock is also converted to a standard user message (User message).To avoid confusion with user inputs, it is recommended to use XML tags (e.g., <system-reminder>...</system-reminder>) to wrap the hint content.
FieldTypeDescription
typestrFixed as "hint".
hintstr | list[TextBlock | DataBlock]The hint content — supports either plain text or a list of compound multimodal blocks.
idstrThe unique identifier of the content block (auto-generated UUID by default).
sourcestr | NoneSender/origin label of the hint (can be a JSON string for frontend parsing and rendering).
FieldTypeDescription
typestrFixed as "tool_call".
idstrUnique identifier for this tool call.
namestrName of the tool to be called.
inputstrTool call parameters in JSON string format.
stateToolCallStateTool call state:
"pending": Pending, has not passed verification and permission checks.
"asking": Suspended, waiting for user authorization.
"allowed": Approved by the user or permission rules, waiting to execute.
"submitted": Submitted externally and waiting for execution results.
"finished": Execution completed (whether successful or failed).
suggested_ruleslist[PermissionRule]Suggested authorization rules attached when the user confirms.
The id of a ToolResultBlock must match the id of the launching ToolCallBlock. Supports multimodal data.
FieldTypeDescription
typestrFixed as "tool_result".
idstrUnique ID matching the corresponding tool call.
namestrName of the tool called.
outputstr | list[TextBlock | DataBlock]Execution output — supports either plain text or multimodal data.
stateToolResultStateTool execution state:
"running": Tool executing.
"success": Finished successfully.
"error": Finished with error.
"interrupted": Interrupted by the user.
"denied": Denied execution by the user or security rules.

Create Messages

AgentScope provides three shortcut methods to construct Msg objects to avoid repeatedly setting the role parameter, and supports building TextBlocks from strings:
Factory FunctionRole
UserMsg(name, content)user
AssistantMsg(name, content)assistant
SystemMsg(name, content)system
When the content parameter is a string, it is automatically wrapped into a TextBlock.
from agentscope.message import UserMsg, SystemMsg, AssistantMsg

# User message
user_msg = UserMsg(
    name="user",
    content="What's in this image?"
)

# System message, used only for system prompts
system_msg = SystemMsg(
    name="system",
    content="You are an AI assistant named Friday."
)

# Assistant message
assistant_msg = AssistantMsg(
    name="Friday",
    content="Hello, how can I help you today?"
)

Access Content

Msg provides helper methods to extract specific block types:
MethodReturns
get_text_content(separator="\n")Concatenated text from all TextBlocks, or None
get_content_blocks(block_type)Filtered list of blocks by type
has_content_blocks(block_type)True if blocks of the given type exist
# Get all text content
text = msg.get_text_content()

# Get all tool calls
tool_calls = msg.get_content_blocks("tool_call")

# Check if message has tool results
if msg.has_content_blocks("tool_result"):
    ...

Event

Events are the streaming counterpart of messages. While the agent executes, it yields a sequence of AgentEvent objects that represent incremental progress — text tokens arriving, tool calls being constructed, results streaming back. Each event is lightweight and self-contained.

Event Lifecycle

Every event carries a reply_id that links it to the message being constructed. Within a reply, block_id or tool_call_id identifies which content block an event belongs to. Events follow a start → delta → end pattern for each content block: All events within the same reply share the same reply_id. Within a reply, use block_id to correlate text/thinking/data block events, and tool_call_id to correlate tool call and tool result events.

Event Types

All events inherit from EventBase which provides common fields:
FieldTypeDescription
idstrUnique event identifier
created_atstrISO 8601 timestamp
Events are grouped by category below. Every event also carries a reply_id field (except where noted) that links it to the message being constructed.
ReplyStartEvent — Agent begins a new reply.
FieldTypeDescription
reply_idstrID of the reply message
session_idstrID of the session
namestrAgent name
rolestrAgent role (default "assistant")
ReplyEndEvent — Agent finishes the reply.
FieldTypeDescription
reply_idstrID of the reply message
session_idstrID of the session
ExceedMaxItersEvent — Agent reached the maximum reasoning-acting iterations.
FieldTypeDescription
reply_idstrID of the reply message
namestrAgent name
TextBlockStartEvent — A new text block begins.
FieldTypeDescription
reply_idstrID of the reply message
block_idstrUnique identifier of the text block
TextBlockDeltaEvent — Incremental text content arrives.
FieldTypeDescription
reply_idstrID of the reply message
block_idstrUnique identifier of the text block
deltastrIncremental text content
TextBlockEndEvent — The text block is complete.
FieldTypeDescription
reply_idstrID of the reply message
block_idstrUnique identifier of the text block
ThinkingBlockStartEvent — A new thinking block begins.
FieldTypeDescription
reply_idstrID of the reply message
block_idstrUnique identifier of the thinking block
ThinkingBlockDeltaEvent — Incremental thinking content arrives.
FieldTypeDescription
reply_idstrID of the reply message
block_idstrUnique identifier of the thinking block
deltastrIncremental thinking text
ThinkingBlockEndEvent — The thinking block is complete.
FieldTypeDescription
reply_idstrID of the reply message
block_idstrUnique identifier of the thinking block
DataBlockStartEvent — A new data block begins (image, audio, etc.).
FieldTypeDescription
reply_idstrID of the reply message
block_idstrUnique identifier of the data block
media_typestrMIME type (e.g. "image/png")
DataBlockDeltaEvent — Incremental binary data arrives.
FieldTypeDescription
reply_idstrID of the reply message
block_idstrUnique identifier of the data block
datastrIncremental base64-encoded data
media_typestrMIME type
DataBlockEndEvent — The data block is complete.
FieldTypeDescription
reply_idstrID of the reply message
block_idstrUnique identifier of the data block
ToolCallStartEvent — The agent begins a tool call.
FieldTypeDescription
reply_idstrID of the reply message
tool_call_idstrUnique identifier of the tool call
tool_call_namestrName of the tool being called
ToolCallDeltaEvent — Incremental tool call input arrives.
FieldTypeDescription
reply_idstrID of the reply message
tool_call_idstrUnique identifier of the tool call
deltastrIncremental JSON fragment of tool input
ToolCallEndEvent — The tool call input is complete.
FieldTypeDescription
reply_idstrID of the reply message
tool_call_idstrUnique identifier of the tool call
ToolResultStartEvent — Tool execution begins.
FieldTypeDescription
reply_idstrID of the reply message
tool_call_idstrID of the corresponding tool call
tool_call_namestrName of the tool
ToolResultTextDeltaEvent — Incremental text output from the tool.
FieldTypeDescription
reply_idstrID of the reply message
tool_call_idstrID of the corresponding tool call
deltastrIncremental text content
ToolResultDataDeltaEvent — Binary data output from the tool.
FieldTypeDescription
reply_idstrID of the reply message
tool_call_idstrID of the corresponding tool call
block_idstrUnique identifier of the data block
media_typestrMIME type of the content
datastr | NoneBase64-encoded data (mutually exclusive with url)
urlstr | NoneURL pointing to the content (mutually exclusive with data)
ToolResultEndEvent — Tool execution is complete.
FieldTypeDescription
reply_idstrID of the reply message
tool_call_idstrID of the corresponding tool call
stateToolResultStateFinal state: SUCCESS, ERROR, INTERRUPTED, DENIED, or RUNNING
ModelCallStartEvent — A model API call begins.
FieldTypeDescription
reply_idstrID of the reply message
model_namestrName of the model being called
ModelCallEndEvent — A model API call completes.
FieldTypeDescription
reply_idstrID of the reply message
input_tokensintNumber of input tokens consumed
output_tokensintNumber of output tokens generated
RequireUserConfirmEvent — Agent pauses for user confirmation.
FieldTypeDescription
reply_idstrID of the reply message
tool_callslist[ToolCallBlock]Tool calls pending user confirmation
RequireExternalExecutionEvent — Agent pauses for external execution.
FieldTypeDescription
reply_idstrID of the reply message
tool_callslist[ToolCallBlock]Tool calls to be executed externally
UserConfirmResultEvent — User provides confirmation results (input event).
FieldTypeDescription
reply_idstrID of the reply message
confirm_resultslist[ConfirmResult]Confirmation results for each pending tool call
ExternalExecutionResultEvent — External system provides execution results (input event).
FieldTypeDescription
reply_idstrID of the reply message
execution_resultslist[ToolResultBlock]Results returned by the external executor
Unlike text / thinking / data / tool blocks, these events do not follow the start → delta → end pattern. The full payload arrives in a single event because it is known up-front rather than streamed.HintBlockEvent — A HintBlock is injected into the agent’s context (e.g. a scheduled-task trigger, a team message, a result returned by an offloaded background tool).
FieldTypeDescription
reply_idstrID of the reply message
block_idstrUnique identifier of the hint block
hintstr | list[TextBlock | DataBlock]The hint payload — plain text or a list of multimodal blocks
sourcestr | NoneOptional sender / origin tag (typically a small JSON object describing how the frontend should label this hint)
CustomEvent — Generic extensible event used by service-layer middleware to notify subscribers about state changes (task progress, team membership, permission updates, …) without polluting the core agent event enum.
FieldTypeDescription
reply_idstrID of the reply message
namestrThe signal name (e.g. "tasks_context", "team_updated")
valuedictArbitrary JSON-serialisable payload for this signal

Reconstruct Messages from Events

Events and messages are not independent — they are two views of the same data. Every event produced by reply_stream can be applied to a Msg via append_event(), reconstructing the complete message incrementally. This guarantees that the final message state is fully recoverable from the event stream alone.
from agentscope.message import Msg, AssistantMsg

msg = None

# Accumulate events into the message
async for event in agent.reply_stream(user_msg):
	if isinstance(event, ReplyStartEvent):
		# Create a new message when the reply starts
		msg = AssistantMsg(name=event.name, content=[], id=event.reply_id)

	else:
		# For all other events, append to the message to reconstruct its state
        msg.append_event(event)
The append_event method handles all event types:
Event TypeEffect on Msg
ReplyEndEventSets finished_at timestamp
TextBlockStartEventAppends a new empty TextBlock
TextBlockDeltaEventConcatenates delta to the block’s text
DataBlockStartEventAppends a new empty DataBlock
DataBlockDeltaEventConcatenates data to the block’s base64 content
ThinkingBlockStartEventAppends a new empty ThinkingBlock
ThinkingBlockDeltaEventConcatenates delta to the block’s thinking text
ToolCallStartEventAppends a new ToolCallBlock with empty input
ToolCallDeltaEventConcatenates delta to the tool call’s input
ToolResultStartEventAppends a new ToolResultBlock with empty output
ToolResultTextDeltaEventAppends text to the tool result’s output
ToolResultDataDeltaEventAppends a binary data block to the tool result’s output
ToolResultEndEventSets the tool result’s final state
HintBlockEventAppends a HintBlock to content (carrying the event’s hint and source) so the hint is persisted and replayable
RequireUserConfirmEventUpdates tool call states to ASKING
ExternalExecutionResultEventAppends ToolResultBlocks to content
This design makes deployment more flexible: the backend can stream events via SSE to the frontend, which reconstructs and renders the message in real time. Even if the connection is interrupted, replaying the event sequence from any checkpoint can restore the exact message state.

TypeScript Support

AgentScope provides TypeScript versions of messages and event primitives, so frontends can use the exact same appendEvent API to reconstruct messages from the event stream. Install the TypeScript version of AgentScope:
pnpm install @agentscope-ai/agentscope
Example of receiving and reconstructing messages on the frontend:
import { Msg, AssistantMsg, EventType } from "@agentscope-ai/agentscope/message";

let msg: Msg | null = null;
for await (const event of stream) {
    if (event.type === EventType.REPLY_START) {
        msg = new AssistantMsg({
			name: event.name,
			content: [],
			id: event.reply_id
		});
    } else {
        msg?.appendEvent(event);
    }
}

Example: Streaming UI

A typical pattern for building a streaming interface:
from agentscope.message import AssistantMsg, UserMsg
from agentscope.event import (
    ReplyStartEvent,
    TextBlockDeltaEvent,
    ToolCallStartEvent,
    ToolResultEndEvent,
    ReplyEndEvent,
)

msg = None

async for event in agent.reply_stream(UserMsg("user", "Fix the bug")):
    if isinstance(event, ReplyStartEvent):
        msg = AssistantMsg(name=event.name, content=[], id=event.reply_id)

    elif isinstance(event, TextBlockDeltaEvent):
        print(event.delta, end="", flush=True)

    elif isinstance(event, ToolCallStartEvent):
        print(f"\n[Calling {event.tool_call_name}...]")

    elif isinstance(event, ToolResultEndEvent):
        print(f"[Tool finished: {event.state}]")

    elif isinstance(event, ReplyEndEvent):
        print("\n[Done]")

    # Always accumulate into the message
    if msg is not None:
        msg.append_event(event)

# msg now contains the complete reply

Further Reading

Agent

How the agent produces events and messages in the ReAct loop

Context

How messages are stored, compressed, and offloaded