# How to Add New Agents to the MathModelAgent Multi-Agent System: A Step-by-Step Guide

> Learn how to add new agents to the MathModelAgent multi-agent system. Follow this step-by-step guide to subclass, register, and integrate your custom agents easily.

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

---

**To add a new agent to the MathModelAgent system, subclass the `Agent` base class in [`app/core/agents/agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/agents/agent.py), register it in [`app/core/agents/__init__.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/agents/__init__.py), and wire it into the `MathModelWorkFlow.execute` method in [`app/core/workflow.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/workflow.py).**

The MathModelAgent repository (`jihe520/mathmodelagent`) orchestrates a pipeline of specialized agents including the **Coordinator**, **Modeler**, **Coder**, and **Writer**. Each agent inherits from a common base class that handles chat history, memory pruning, and LLM interaction. Adding new agents to this existing multi-agent system follows a standardized four-step procedure that maintains architectural consistency.

## Step 1 – Subclass the Agent Base Class

All agents in the system inherit from the **`Agent`** base class located in [`app/core/agents/agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/agents/agent.py). This base provides essential infrastructure including `self.chat_history` for message tracking, `self.append_chat_history` for automatic memory pruning, `self.clear_memory` for history compression, and a `run` helper for LLM interaction.

### Understanding the Agent Base Interface

The base class manages stateful conversations and memory constraints through the following key components:

- **`max_chat_turns`** – Limits the number of conversation turns before termination
- **`max_memory`** – Triggers automatic pruning when history exceeds this threshold
- **`is_first_run`** – Boolean flag to handle system prompt initialization
- **`append_chat_history`** – Method that adds messages and triggers memory management
- **`model.chat`** – Coroutine for LLM inference with history context

### Implementing a New Agent

Create a new file in `app/core/agents/` and implement the required `__init__` and `run` coroutine. The example below demonstrates an `AnalyzerAgent` that processes modeler output:

```python

# backend/app/core/agents/analyzer_agent.py

from app.core.agents.agent import Agent
from app.core.llm.llm import LLM
from app.core.prompts import ANALYZER_PROMPT
from app.schemas.response import SystemMessage
from app.services.redis_manager import redis_manager

class AnalyzerAgent(Agent):
    """Analyzes modeler results and produces a concise summary."""

    def __init__(
        self,
        task_id: str,
        model: LLM,
        max_chat_turns: int = 20,
        max_memory: int = 15,
    ) -> None:
        super().__init__(task_id, model, max_chat_turns, max_memory)
        self.system_prompt = ANALYZER_PROMPT

    async def run(self, modeler_output: str) -> str:
        # Initialize system prompt on first call

        if self.is_first_run:
            self.is_first_run = False
            await self.append_chat_history(
                {"role": "system", "content": self.system_prompt}
            )

        await self.append_chat_history(
            {"role": "user", "content": f"请为下面的模型输出生成要点摘要:\n\n{modeler_output}"}
        )

        # Query the LLM

        response = await self.model.chat(
            history=self.chat_history,
            agent_name=self.__class__.__name__,
        )
        summary = response.choices[0].message.content

        # Store assistant response

        self.chat_history.append({"role": "assistant", "content": summary})

        # Broadcast via Redis for front-end monitoring

        await redis_manager.publish_message(
            self.task_id,
            SystemMessage(content=f"{self.__class__.__name__} 完成分析")
        )

        return summary

```

**Key implementation details:**
- Always call `super().__init__` to initialize inherited attributes including `is_first_run`, `chat_history`, and memory management
- Use `append_chat_history` rather than direct list manipulation to ensure automatic memory pruning via `clear_memory`
- Follow the established pattern of publishing status updates through `redis_manager` for real-time front-end visibility

## Step 2 – Register the Agent in the Package

Expose the new class by importing it in [`app/core/agents/__init__.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/agents/__init__.py) and adding it to the `__all__` list:

```python

# backend/app/core/agents/__init__.py

from .coder_agent import CoderAgent
from .writer_agent import WriterAgent
from .coordinator_agent import CoordinatorAgent
from .modeler_agent import ModelerAgent
from .analyzer_agent import AnalyzerAgent   # New import

__all__ = [
    "CoderAgent",
    "WriterAgent",
    "CoordinatorAgent",
    "ModelerAgent",
    "AnalyzerAgent",   # New export

]

```

This registration makes the agent available for import throughout the backend, particularly in the workflow orchestrator.

## Step 3 – Integrate the Agent into the Workflow

Instantiate and invoke the agent within `MathModelWorkFlow.execute` in [`app/core/workflow.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/workflow.py). The workflow orchestrator sequences agent execution and manages data flow between pipeline stages:

```python

# Inside MathModelWorkFlow.execute (backend/app/core/workflow.py)

from app.core.agents import AnalyzerAgent   # Import new agent

# ... after existing agent execution

coder_response = await coder_agent.run(...)

# Instantiate new agent with task context

analyzer_agent = AnalyzerAgent(
    task_id=problem.task_id,
    model=writer_llm,  # Reuse existing LLM or instantiate dedicated model

)

# Execute analysis on previous agent output

analysis = await analyzer_agent.run(coder_response.code_response)

# Store results for downstream consumption

user_output.set_res(f"{key}_analysis", analysis)

```

**Integration best practices:**
- Pass the `task_id` to maintain conversation isolation across concurrent tasks
- Reuse existing LLM instances from `LLMFactory` unless the agent requires specific model capabilities
- Store results in `UserOutput` using `set_res` to preserve data for subsequent pipeline stages or final output generation

## Step 4 – Define Prompts and Schemas (Optional)

### Adding Custom Prompts

If the agent requires specialized system instructions, define them in [`app/core/prompts.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/prompts.py) and reference them in the agent class:

```python

# backend/app/core/prompts.py

ANALYZER_PROMPT = """
你是一个数学建模结果分析助手。请根据提供的模型输出，提取关键结论、重要数值和潜在风险点，输出条理清晰的要点列表。返回的文字应简洁明了，适合作为论文或报告的摘要。
"""

```

### Creating Structured Output Schemas

For agents returning structured data rather than plain strings, define Pydantic models in [`app/schemas/A2A.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/schemas/A2A.py):

```python

# backend/app/schemas/A2A.py

from pydantic import BaseModel

class AnalyzerResult(BaseModel):
    summary: str
    key_metrics: list[str] = []

```

Update the agent's type hints to return `AnalyzerResult` and parse the LLM output accordingly. This pattern is consistent with existing schemas like `CoderToWriter` and `WriterResponse`.

## Testing Your New Agent

Verify integration by running the backend test suite or executing a minimal async script:

```bash
cd backend
uv run pytest   # Run existing test suite

```

For manual verification:

```python
python -c "
import asyncio
from app.core.agents import AnalyzerAgent
from app.core.llm.llm_factory import LLMFactory

async def demo():
    llm = (await LLMFactory('demo').get_all_llms())[0]
    agent = AnalyzerAgent('demo', llm)
    result = await agent.run('示例模型输出')
    print(result)

asyncio.run(demo())

```

## Summary

Adding new agents to the MathModelAgent multi-agent system requires four specific actions:

- **Subclass `Agent`** in a new file under `app/core/agents/`, implementing `__init__` and an async `run` method that handles chat history via `append_chat_history`
- **Register the class** in [`app/core/agents/__init__.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/agents/__init__.py) by importing it and adding it to `__all__`
- **Wire into workflow** by instantiating the agent in [`app/core/workflow.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/workflow.py) within `MathModelWorkFlow.execute` and calling its `run` coroutine
- **Define supporting assets** such as prompts in [`app/core/prompts.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/prompts.py) and Pydantic schemas in [`app/schemas/A2A.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/schemas/A2A.py) when structured I/O is required

This procedure ensures new agents leverage the existing memory management, Redis notification, and LLM abstraction layers while maintaining consistency with the established Coordinator-Modeler-Coder-Writer pipeline.

## Frequently Asked Questions

### What base class should new agents inherit from?

All agents must inherit from the **`Agent`** class defined in [`app/core/agents/agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/agents/agent.py). This base class provides the `chat_history` management, automatic memory pruning through `clear_memory`, and the `is_first_run` flag for system prompt initialization. Inheriting from this class ensures your agent integrates seamlessly with the existing memory-aware chat infrastructure.

### Where should I instantiate new agents in the codebase?

Instantiate new agents within the **`MathModelWorkFlow.execute`** method in [`app/core/workflow.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/workflow.py). This orchestrator coordinates the multi-agent pipeline and manages the flow of data between specialized agents. Create the instance using the current `task_id` and an LLM instance, then call the `run` method with the appropriate input from the previous pipeline stage.

### How does memory management work for custom agents?

The base class automatically handles memory through the **`max_memory`** parameter passed to `__init__`. When `append_chat_history` is called and the history exceeds `max_memory` entries, the system invokes `simple_chat` to compress older messages. Agents should always use `append_chat_history` (which triggers this pruning) rather than directly appending to `self.chat_history` to ensure memory constraints are respected.

### Can I use a dedicated LLM configuration for a specific agent?

Yes. While you can reuse existing LLM instances passed to other agents, you may configure a dedicated model by extending **`LLMFactory`** in [`app/core/llm/llm_factory.py`](https://github.com/jihe520/mathmodelagent/blob/main/app/core/llm/llm_factory.py). Create a new factory method or configuration profile that returns a specialized LLM instance, then pass this to your agent's `model` parameter during instantiation in the workflow. This is useful when an agent requires specific capabilities like extended context windows or particular API endpoints.