How the `send_message_to_user` Tool Enables Proactive Agent Messaging in AstrBot
The send_message_to_user tool is a function-tool that allows AstrBot's LLM-driven agents to push messages to users outside the normal request-response flow, supporting rich media, file attachments, and sandbox file resolution.
This tool is essential for building proactive AI agents in AstrBot that need to notify users when background tasks complete, cron jobs finish, or long-running operations yield results. According to the AstrBot source code, it serves as the sanctioned channel for all outbound proactive messaging, centralizing session management, file handling, and platform-specific delivery.
Core Architecture of send_message_to_user
The tool is implemented as a first-class citizen in AstrBot's agent framework, with explicit registration, schema validation, and execution paths that bridge LLM decisions to concrete message delivery.
Tool Definition and Schema
The SendMessageToUserTool class in astrbot/core/astr_main_agent_resources.py defines the contract that the LLM sees when deciding to contact a user. It declares the tool name, description, and a strict JSON schema for the messages parameter—an ordered list of message components that can include text, images, files, mentions, and other media types.
The class exposes a call() method (lines 70-84 and 92-124 in the same file) that performs the heavy lifting: validating the payload, resolving file paths (including automatic sandbox retrieval), constructing MessageComponent objects (Plain, Image, Record, Video, File, At), and ultimately dispatching the MessageChain to the target session.
Execution Flow
When the LLM decides to send a message, FunctionToolExecutor._execute_local in astrbot/core/astr_agent_tool_exec.py (lines 70-85) detects that the tool overrides call() and runs it synchronously within the agent's coroutine loop. This bridges the LLM-generated tool call JSON to the Python implementation without blocking the main event loop.
Registration and Availability
The tool is automatically registered with every main-agent run. In astrbot/core/astr_main_agent.py, the system adds the tool to the provider request via req.func_tool.add_tool(SEND_MESSAGE_TO_USER_TOOL) (around lines 46-48). This ensures that both interactive sessions and background/cron agents can access proactive messaging capabilities.
The system prompt explicitly instructs the LLM to use this specific tool for immediate delivery:
req.prompt = (
"… If you need to deliver the result to the user immediately, "
"you MUST use `send_message_to_user` tool to send the message directly to the user, "
"otherwise the user will not see the result."
)
Source: astrbot/core/astr_agent_tool_exec.py lines 10-13
How the Tool Works Step-by-Step
The execution path from LLM decision to user notification follows a precise sequence:
-
LLM Tool Call Generation – The model returns JSON matching the schema, optionally specifying a target
sessionor defaulting to the current session:{ "name": "send_message_to_user", "arguments": { "session": "12345", "messages": [ {"type": "plain", "text": "Your job finished!"}, {"type": "image", "path": "/tmp/result.png"} ] } } -
Executor Invocation –
FunctionToolExecutorroutes the call toSendMessageToUserTool.call(context, **arguments). -
Payload Validation – The
call()method verifies thatmessagesis a non-empty list and that each entry contains a validtypefield. -
Component Resolution – For each message component:
- Plain → Creates
Comp.Plain(text=...) - Media (Image/Video/Record/File) → If a local
pathis provided,_resolve_path_from_sandbox()checks the host filesystem; if missing, it attempts to download from the sandbox environment. If aurlis supplied, the component is created directly from the URL. - Mention → Creates
Comp.At(qq=...)for user mentions.
- Plain → Creates
-
Chain Construction – The resolved components are wrapped in a
MessageChainobject. -
Session Resolution – The target session is determined via
MessageSession.from_str(session)or the currentunified_msg_origin. -
Platform Delivery – The tool invokes
await context.context.context.send_message(target_session, MessageChain(chain=components)), which routes through the appropriate platform adapter (e.g., Telegram, OneBot/QQ) to convert the chain into platform-specific API calls. -
Status Return – The tool returns a confirmation string (
"Message sent to session ...") to the LLM.
Practical Implementation Examples
Direct Python Usage in Plugins
For custom plugin development, you can import the singleton tool instance directly:
from astrbot.core.astr_main_agent_resources import SEND_MESSAGE_TO_USER_TOOL
from astrbot.core.astr_agent_context import AgentContextWrapper
async def proactive_notify(context: AgentContextWrapper):
await SEND_MESSAGE_TO_USER_TOOL.call(
context,
session="user12345",
messages=[
{"type": "plain", "text": "⚡️ Your background job is done!"},
{"type": "image", "url": "https://example.com/report.png"},
{"type": "mention_user", "mention_user_id": "user12345"},
],
)
Key implementation details: Tool definition and call() logic reside in astrbot/core/astr_main_agent_resources.py (lines 70-84 and 92-124).
LLM-Driven Tool Invocation
When the LLM autonomously decides to notify a user, it emits JSON similar to:
{
"name": "send_message_to_user",
"arguments": {
"messages": [
{"type": "plain", "text": "Your data export is ready, see the file below."},
{"type": "file", "path": "/sandbox/exports/report.csv"}
]
}
}
The executor automatically resolves /sandbox/exports/report.csv through the sandbox retrieval mechanism, builds a File component with the downloaded temporary path, and delivers it to the current user session without requiring the LLM to know the underlying filesystem details.
Proactive Cron Job Messaging
In scheduled background tasks, the tool is injected into the agent's toolset to enable proactive notifications:
# Inside cron job initialization (astrbot/core/cron/manager.py, lines 278-342)
req.func_tool.add_tool(SEND_MESSAGE_TO_USER_TOOL)
# Later, after job completion
await SEND_MESSAGE_TO_USER_TOOL.call(
run_context,
messages=[{"type": "plain", "text": "Daily summary generated successfully!"}]
)
This pattern ensures that cron jobs and background agents can push results to users even when no active conversation context exists.
Summary
send_message_to_useris the sanctioned channel for proactive outbound messaging in AstrBot, enabling LLM agents to push notifications outside the request-response cycle.- The tool supports rich multi-component messages including Plain text, Image, Video, Record, File (with sandbox resolution), and At mentions.
- Core implementation resides in
astrbot/core/astr_main_agent_resources.py, with execution handled byFunctionToolExecutorinastrbot/core/astr_agent_tool_exec.py. - Automatic registration in
astrbot/core/astr_main_agent.pyensures availability for all main-agent runs, including cron jobs managed byastrbot/core/cron/manager.py. - The tool centralizes file path resolution, sandbox handling, and platform-specific delivery through the
MessageChainabstraction.
Frequently Asked Questions
Can send_message_to_user target any user, or only the current session?
The tool accepts an optional session parameter. If provided, it resolves the target via MessageSession.from_str(session). If omitted, it defaults to the current unified_msg_origin context. This allows agents to message specific users when they have the session identifier, or default to the active conversation.
How does the tool handle files created in the sandbox environment?
The _resolve_path_from_sandbox() method in astrbot/core/astr_main_agent_resources.py checks if the provided path exists on the host filesystem. If the file is missing locally but exists in the sandbox, the method automatically downloads it from the sandbox environment to a temporary location before constructing the message component.
Is this tool available to all AstrBot agents by default?
Yes. According to astrbot/core/astr_main_agent.py (lines 46-48), the tool is automatically added to every main-agent provider request via req.func_tool.add_tool(SEND_MESSAGE_TO_USER_TOOL). This includes both interactive chat agents and background/cron agents initialized through astrbot/core/cron/manager.py.
What happens if the LLM tries to send an invalid message format?
The call() method validates that the messages argument is a non-empty list and that each entry contains a valid type field. Invalid payloads are rejected before any platform API calls are made, preventing malformed messages from reaching the user.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →