# How the Multi-Agent Architecture in MathModelAgent Works: Coordinator, Modeler, Coder, and Writer

> Discover how MathModelAgent's multi-agent architecture with Coordinator, Modeler, Coder, and Writer agents transforms math problems into code and papers using DTOs.

- Repository: [Sanjin/mathmodelagent](https://github.com/jihe520/mathmodelagent)
- Tags: deep-dive
- Published: 2026-03-04

---

**MathModelAgent orchestrates a four-stage pipeline where CoordinatorAgent, ModelerAgent, CoderAgent, and WriterAgent communicate through typed Pydantic DTOs to transform raw mathematical competition problems into executable code and polished academic papers.**

The **multi-agent architecture in MathModelAgent** (from the `jihe520/mathmodelagent` repository) implements a structured workflow that mimics the human approach to mathematical modeling competitions. Each specialized LLM-driven agent handles a distinct phase of the problem-solving process, passing structured data through well-defined interfaces to ensure type-safe handoffs and deterministic execution.

## The Four-Agent Pipeline Architecture

The system processes every problem through a sequential pipeline where each agent inherits from a common `Agent` base class defined in [`backend/app/core/agents/agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/agent.py). This base class provides shared functionality including chat history management, memory compression, and a unified `run` contract.

### CoordinatorAgent: Problem Decomposition

The **CoordinatorAgent** ([`backend/app/core/agents/coordinator_agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/coordinator_agent.py)) serves as the entry point. It parses the raw user problem description, extracts individual sub-questions, and returns a structured `CoordinatorToModeler` DTO containing the `questions` dictionary and `ques_count`. This agent injects the `COORDINATOR_PROMPT` system prompt to guide the LLM in proper problem decomposition.

### ModelerAgent: Strategy Design

The **ModelerAgent** ([`backend/app/core/agents/modeler_agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/modeler_agent.py)) receives the parsed questions via the DTO and designs a mathematical modeling strategy. Using the `MODELER_PROMPT`, it generates a modeling plan that maps to the `ModelerToCoder` DTO, which contains the `questions_solution` field carrying the solution sketch for the next stage.

### CoderAgent: Code Generation and Execution

The **CoderAgent** ([`backend/app/core/agents/coder_agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/coder_agent.py)) takes the modeling sketch and generates executable Python code within a sandboxed Jupyter-like interpreter. This agent implements a tool-calling loop using `execute_code` to iterate until the code runs without errors. It produces a `CoderToWriter` DTO containing the `coder_response` (final code) and `created_images` (any generated visualizations).

### WriterAgent: Academic Paper Composition

The **WriterAgent** ([`backend/app/core/agents/writer_agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/writer_agent.py)) consumes the code and images alongside a writing prompt generated by `get_writer_prompt`. It queries scholarly sources via the `search_papers` tool when needed and returns a `WriterResponse` DTO containing `response_content` (written text) and `footnotes` for academic citations.

## Inter-Agent Communication and DTOs

Agents exchange data through typed Pydantic models defined in [`backend/app/schemas/A2A.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/schemas/A2A.py). These **Data-Transfer Objects (DTOs)** enforce type safety and make the workflow deterministic:

- **CoordinatorToModeler**: Carries `questions` (dict of sub-questions) and `ques_count`
- **ModelerToCoder**: Carries `questions_solution` (the modeling plan)
- **CoderToWriter**: Carries `coder_response` (generated code) and `created_images`
- **WriterResponse**: Carries `response_content` (written sections) and `footnotes`

This DTO-based architecture ensures that each agent receives exactly the data structure it expects, eliminating schema mismatches between pipeline stages.

## Workflow Orchestration with MathModelWorkFlow

The central orchestrator lives in [`backend/app/core/workflow.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/workflow.py) within the `MathModelWorkFlow` class. Its `execute` coroutine sequences the agents and manages the sandboxed interpreter lifecycle:

```python

# 1. Coordinator stage

coordinator_response = await coordinator_agent.run(problem.ques_all)

# 2. Modeler stage

modeler_response = await modeler_agent.run(coordinator_response)

# 3. Sandbox preparation

code_interpreter = await create_interpreter(...)

# 4. Coder stage (iterates over sub-questions)

coder_response = await coder_agent.run(
    prompt=value["coder_prompt"], 
    subtask_title=key
)

# 5. Writer stage

writer_response = await writer_agent.run(
    writer_prompt, 
    available_images=coder_response.created_images, 
    sub_title=key
)

```

The workflow publishes real-time status updates to a Redis channel via `redis_manager.publish_message` (implemented in [`backend/app/services/redis_manager.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/services/redis_manager.py)), allowing the frontend to display progress as each agent completes its task.

## Agent Communication Patterns

### System Prompt Injection

Each agent injects its specific system prompt before the first user message. The prompts are defined in [`backend/app/core/prompts.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/prompts.py):

- `COORDINATOR_PROMPT` for problem decomposition
- `MODELER_PROMPT` for strategy design
- `CODER_PROMPT` for code generation
- `get_writer_prompt()` for academic writing

### Chat History and Memory Management

The base `Agent` class maintains conversation state in `self.chat_history`. After every LLM response, the history appends the new messages. A **memory-compression** mechanism (`clear_memory`) summarizes older turns to stay within the `max_memory` token limit, preventing context window overflow during long modeling sessions.

### Tool Calling Loops

`CoderAgent` and `WriterAgent` declare tool schemas (`coder_tools`, `writer_tools`) that enable the LLM to request external actions:

1. The agent publishes a status message to Redis indicating tool execution
2. The tool executes (e.g., `execute_code` in the sandbox or `search_papers` for literature)
3. The result appends as a `role: "tool"` message to the chat history
4. The agent continues the chat loop until no tool calls remain

### Error Handling and Reflection

When tool execution fails, `CoderAgent` triggers a reflection mechanism using `get_reflection_prompt`. It retries the operation up to `max_retries`, appending error context to guide the LLM toward a correct solution. The `WriterAgent` implements similar retry logic for failed scholarly searches.

## Practical Implementation Examples

### Running the Complete Pipeline

```python
import asyncio
from backend.app.schemas.request import Problem
from backend.app.core.workflow import MathModelWorkFlow

async def run_example():
    problem = Problem(
        task_id="demo-001",
        ques_all="请用微分方程描述某城市的交通流量，并提供数值仿真代码。",
        comp_template="default",
        format_output="Markdown",
    )
    workflow = MathModelWorkFlow()
    await workflow.execute(problem)

asyncio.run(run_example())

```

### Direct Agent Invocation

```python
from backend.app.core.agents.coordinator_agent import CoordinatorAgent
from backend.app.core.llm.llm_factory import LLMFactory

# Build LLM for coordinator

llm_factory = LLMFactory(task_id="demo-001")
coordinator_llm = llm_factory.get_llm("coordinator")

# Instantiate and run

coordinator = CoordinatorAgent(
    task_id="demo-001", 
    model=coordinator_llm
)
dto = await coordinator.run("请对以下问题进行分解……")
print(dto.questions)   # Structured sub-questions

print(dto.ques_count)  # Total count

```

### Coder Agent with Tool Execution

```python
from backend.app.core.agents.coder_agent import CoderAgent
from backend.app.core.llm.llm_factory import LLMFactory
from backend.app.tools.interpreter_factory import create_interpreter

llm = LLMFactory(task_id="demo-001").get_llm("coder")
interpreter = await create_interpreter(
    kind="local", 
    task_id="demo-001", 
    work_dir="/tmp/work"
)

coder = CoderAgent(
    task_id="demo-001",
    model=llm,
    work_dir="/tmp/work",
)

response = await coder.run(
    prompt="请在 Jupyter 环境实现上述微分方程的数值求解代码。",
    subtask_title="traffic_flow_simulation",
)

print(response.coder_response)  # Generated Python code

print(response.created_images)  # Visualization paths

```

## Summary

- **Four specialized agents** (Coordinator, Modeler, Coder, Writer) inherit from a common `Agent` base class in [`backend/app/core/agents/agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/agent.py)
- **DTOs** defined in [`backend/app/schemas/A2A.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/schemas/A2A.py) ensure type-safe data transfer between pipeline stages
- **MathModelWorkFlow** in [`backend/app/core/workflow.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/workflow.py) orchestrates execution and manages the sandboxed interpreter via `create_interpreter` from [`backend/app/tools/interpreter_factory.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/tools/interpreter_factory.py)
- **Tool-calling loops** enable `CoderAgent` to execute and debug code iteratively using `execute_code`, while `WriterAgent` searches scholarly sources with `search_papers`
- **Real-time updates** flow through Redis ([`backend/app/services/redis_manager.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/services/redis_manager.py)) to provide frontend visibility into agent progress

## Frequently Asked Questions

### How does the CoordinatorAgent decide how to split a problem into sub-questions?

The CoordinatorAgent uses the `COORDINATOR_PROMPT` system prompt (defined in [`backend/app/core/prompts.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/prompts.py)) to guide the LLM in parsing the raw problem text. It analyzes the semantic structure of the input to identify distinct mathematical questions, then returns a `CoordinatorToModeler` DTO containing a dictionary of sub-questions indexed by task keys and a total count. This structured decomposition ensures downstream agents receive discrete, manageable units of work.

### What happens if the CoderAgent generates code that fails to execute?

When the `execute_code` tool returns an error, the CoderAgent invokes its reflection mechanism using `get_reflection_prompt`. It appends the error message to the chat history as a tool response and retries the generation, incorporating the failure context to guide the LLM toward a corrected solution. This loop continues until the code executes successfully or reaches the configured `max_retries` limit.

### Can additional agents be inserted into the pipeline without modifying existing code?

Yes. The architecture supports extensibility through inheritance from the base `Agent` class and the DTO pattern. To add a new stage, create a new agent class, define a corresponding DTO in [`backend/app/schemas/A2A.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/schemas/A2A.py) for handoff data, and insert the agent call into the `MathModelWorkFlow.execute` method in [`backend/app/core/workflow.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/workflow.py). The existing agents remain unchanged due to the loose coupling provided by the DTO-based communication protocol.

### How does the WriterAgent handle academic citations and references?

The WriterAgent ([`backend/app/core/agents/writer_agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/writer_agent.py)) declares `search_papers` in its `writer_tools` schema. When the LLM determines that scholarly support is needed, it invokes this tool to query academic databases. The results append to the chat history, and the agent incorporates the findings into the paper text, tracking sources in the `footnotes` field of the `WriterResponse` DTO returned to the workflow.