What Are Formatters in AgentScope and How Do They Shape Model Input/Output?
Formatters in AgentScope are adapter classes that serve as the bridge between AgentScope's internal message representation and provider-specific LLM APIs, converting Msg objects into JSON payloads and back while handling validation, multimodal content, tool calls, and token limits.
AgentScope represents conversations as structured Msg objects containing various content blocks, but each LLM provider (OpenAI, Anthropic, Gemini, etc.) expects a unique JSON schema. Formatters in the agentscope-ai/agentscope repository handle these transformations automatically, ensuring that the same agent logic can run across different models without code changes.
Core Responsibilities of AgentScope Formatters
AgentScope formatters perform six critical functions that determine how data flows between your application and the LLM:
- Message validation:
FormatterBase.assert_list_of_msgsverifies that inputs are valid lists ofMsgobjects, preventing malformed API requests. - Block conversion: Maps internal block types (text, image, audio, tool-use) to provider-specific schemas, such as OpenAI's
{type:"image_url",...}format. - Tool-call handling: Converts tool-use blocks into provider "function" formats and formats tool-result blocks, including optional promotion of result images to separate user messages.
- Multimodal support: Processes base64 or URL-based images and audio, saving base64 data locally when needed.
- Token management:
TruncatedFormatterBasecounts tokens viaTokenCounterBaseand truncates older messages to fit withinmax_tokenslimits. - Conversation grouping: Groups sequences of tool calls and results separately from normal messages in multi-agent scenarios.
Message Validation and Block Conversion
In src/agentscope/formatter/_formatter_base.py, the abstract FormatterBase class defines the interface for all formatters. Before conversion, formatters validate that the input is a proper list of Msg instances. Each Msg contains blocks representing text, images, or audio, and the formatter maps these to the target provider's expected JSON structure.
Tool-Call and Multimodal Handling
Formatters handle complex interactions like vision tasks and function calling. For example, OpenAIChatFormatter in src/agentscope/formatter/_openai_formatter.py converts tool-use blocks into OpenAI's function-calling format and can extract images from tool results, promoting them to standalone user messages when promote_tool_result_images=True.
Token Limit Enforcement and Truncation
When initialized with a token_counter and max_tokens, TruncatedFormatterBase (defined in src/agentscope/formatter/_truncated_formatter_base.py) automatically counts tokens and removes older messages from the conversation history until the request fits within the budget. This prevents API errors due to context window overflow.
How to Use AgentScope Formatters: Code Examples
The following examples demonstrate practical formatter usage based on the source implementation.
Formatting Messages for OpenAI Models
To convert AgentScope messages into OpenAI-compatible payloads, instantiate OpenAIChatFormatter and call its format method:
from agentscope.formatter._openai_formatter import OpenAIChatFormatter
from agentscope.message import Msg, TextBlock
# Build a simple message list
msgs = [
Msg(
role="user",
name="user",
content=[TextBlock(type="text", text="Tell me a joke.")],
),
]
# Initialize the formatter (no token limit here)
formatter = OpenAIChatFormatter(promote_tool_result_images=False)
# Convert to OpenAI-compatible payload
payload = await formatter.format(msgs)
print(payload)
# Output:
# [{'role': 'user', 'name': 'user', 'content': [{'type': 'text', 'text': 'Tell me a joke.'}]}]
This implementation references OpenAIChatFormatter.__init__ (lines 58-63) and the core _format logic (lines 9-12) in src/agentscope/formatter/_openai_formatter.py.
Handling Tool Results with Image Promotion
When tools return images, you can promote them to separate user messages for better model consumption:
formatter = OpenAIChatFormatter(promote_tool_result_images=True)
# Suppose a tool result returns an image block
tool_result_msg = Msg(
role="assistant",
name="assistant",
content=[
{
"type": "tool_result",
"id": "tool-1",
"name": "search_image",
"output": [
{"type": "image", "source": {"type": "url", "url": "https://example.com/cat.png"}}
],
}
],
)
payload = await formatter.format([tool_result_msg])
# The formatter inserts a new user message with the promoted image
The image promotion logic resides in OpenAIChatFormatter._format at lines 71-94.
Enforcing Token Budgets with TruncatedFormatter
To prevent requests from exceeding token limits, combine a formatter with a token counter:
from agentscope.token import OpenAITokenCounter
from agentscope.formatter._openai_formatter import OpenAIChatFormatter
counter = OpenAITokenCounter(model_name="gpt-3.5-turbo")
formatter = OpenAIChatFormatter(token_counter=counter, max_tokens=500)
payload = await formatter.format(msgs) # Truncates if >500 tokens
This leverages the truncation implementation in TruncatedFormatterBase.format (lines 47-55) and the _truncate helper method (lines 51-78) in src/agentscope/formatter/_truncated_formatter_base.py.
Formatter Architecture and Source Files
AgentScope organizes formatters in a class hierarchy under src/agentscope/formatter/. The base classes define the interface and common utilities, while subclasses implement provider-specific serialization logic.
src/agentscope/formatter/_formatter_base.py: DefinesFormatterBase, validation helpers, and theconvert_tool_result_to_stringutility.src/agentscope/formatter/_truncated_formatter_base.py: Implements token-aware formatting, message truncation, system-message handling, and conversation grouping for multi-agent scenarios.src/agentscope/formatter/_openai_formatter.py: OpenAI-specific implementation supporting chat completions, vision, audio, tool calls, and image promotion.src/agentscope/formatter/_ollama_formatter.py: Ollama-compatible formatter adapting the schema for local model deployment.src/agentscope/formatter/_gemini_formatter.py: Google Gemini-specific formatter handling multimodal payloads.src/agentscope/formatter/_anthropic_formatter.py: Anthropic Claude formatter managing Claude's unique tool-use format.src/agentscope/formatter/_dashscope_formatter.py: Formatter for Alibaba Cloud DashScope models.src/agentscope/formatter/_a2a_formatter.py: "Agent-to-Agent" formatter facilitating direct LLM-to-LLM conversations.
Summary
- Formatters act as the translation layer between AgentScope's internal
Msgformat and provider-specific LLM API schemas. - They handle validation, block conversion, tool calls, multimodal content, and token truncation automatically.
- The hierarchy extends from
FormatterBasetoTruncatedFormatterBaseand provider-specific implementations likeOpenAIChatFormatter. - By configuring
promote_tool_result_imagesandmax_tokens, you control how tool outputs and long conversations are processed. - Located in
src/agentscope/formatter/, these classes enable the same agent code to operate across OpenAI, Anthropic, Gemini, Ollama, and other providers.
Frequently Asked Questions
What is the difference between FormatterBase and TruncatedFormatterBase?
FormatterBase in src/agentscope/formatter/_formatter_base.py provides the abstract interface and validation logic for all formatters. TruncatedFormatterBase extends this class in src/agentscope/formatter/_truncated_formatter_base.py to add token-counting capabilities and automatic message truncation when conversations exceed max_tokens.
How do formatters handle multimodal inputs like images and audio?
Formatters inspect each block within a Msg object and convert media blocks to the provider's expected format. For example, OpenAIChatFormatter converts image blocks to OpenAI's image_url schema, while GeminiFormatter structures them for Google's multimodal API. Base64 data is saved locally when required.
Can I use the same AgentScope agent with different LLM providers?
Yes. Because formatters abstract the API-specific serialization, you can swap the formatter instance (e.g., from OpenAIChatFormatter to AnthropicFormatter) without changing your agent logic. The formatter ensures the correct JSON payload structure for each provider.
How does token truncation work in formatters?
When initialized with a token_counter and max_tokens, formatters inheriting from TruncatedFormatterBase count tokens in the message list and iteratively remove the oldest messages (while preserving system messages) until the total is below the limit. This prevents API errors from oversized context windows.
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 →