# What Are Formatters in AgentScope and How Do They Shape Model Input/Output?

> Explore AgentScope formatters, adapter classes that transform Msg objects into LLM API payloads. Learn how they manage input/output, validation, multimodal content, and token limits.

- Repository: [AgentScope-AI/agentscope](https://github.com/agentscope-ai/agentscope)
- Tags: deep-dive
- Published: 2026-03-09

---

**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`](https://github.com/agentscope-ai/agentscope/blob/main/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`](https://github.com/agentscope-ai/agentscope/blob/main/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`](https://github.com/agentscope-ai/agentscope/blob/main/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:

```python
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`](https://github.com/agentscope-ai/agentscope/blob/main/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:

```python
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:

```python
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`](https://github.com/agentscope-ai/agentscope/blob/main/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`](https://github.com/agentscope-ai/agentscope/blob/main/src/agentscope/formatter/_formatter_base.py)**: Defines `FormatterBase`, validation helpers, and the `convert_tool_result_to_string` utility.
- **[`src/agentscope/formatter/_truncated_formatter_base.py`](https://github.com/agentscope-ai/agentscope/blob/main/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`](https://github.com/agentscope-ai/agentscope/blob/main/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`](https://github.com/agentscope-ai/agentscope/blob/main/src/agentscope/formatter/_ollama_formatter.py)**: Ollama-compatible formatter adapting the schema for local model deployment.
- **[`src/agentscope/formatter/_gemini_formatter.py`](https://github.com/agentscope-ai/agentscope/blob/main/src/agentscope/formatter/_gemini_formatter.py)**: Google Gemini-specific formatter handling multimodal payloads.
- **[`src/agentscope/formatter/_anthropic_formatter.py`](https://github.com/agentscope-ai/agentscope/blob/main/src/agentscope/formatter/_anthropic_formatter.py)**: Anthropic Claude formatter managing Claude's unique tool-use format.
- **[`src/agentscope/formatter/_dashscope_formatter.py`](https://github.com/agentscope-ai/agentscope/blob/main/src/agentscope/formatter/_dashscope_formatter.py)**: Formatter for Alibaba Cloud DashScope models.
- **[`src/agentscope/formatter/_a2a_formatter.py`](https://github.com/agentscope-ai/agentscope/blob/main/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 `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`](https://github.com/agentscope-ai/agentscope/blob/main/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`](https://github.com/agentscope-ai/agentscope/blob/main/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.