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_msgs verifies that inputs are valid lists of Msg objects, 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: TruncatedFormatterBase counts tokens via TokenCounterBase and truncates older messages to fit within max_tokens limits.
  • 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.

Summary

  • Formatters act as the translation layer between AgentScope's internal Msg format and provider-specific LLM API schemas.
  • They handle validation, block conversion, tool calls, multimodal content, and token truncation automatically.
  • The hierarchy extends from FormatterBase to TruncatedFormatterBase and provider-specific implementations like OpenAIChatFormatter.
  • By configuring promote_tool_result_images and max_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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →