How to Add New Agents to the MathModelAgent Multi-Agent System: A Step-by-Step Guide
To add a new agent to the MathModelAgent system, subclass the Agent base class in app/core/agents/agent.py, register it in app/core/agents/__init__.py, and wire it into the MathModelWorkFlow.execute method in 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. 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 terminationmax_memory– Triggers automatic pruning when history exceeds this thresholdis_first_run– Boolean flag to handle system prompt initializationappend_chat_history– Method that adds messages and triggers memory managementmodel.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:
# 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 includingis_first_run,chat_history, and memory management - Use
append_chat_historyrather than direct list manipulation to ensure automatic memory pruning viaclear_memory - Follow the established pattern of publishing status updates through
redis_managerfor 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 and adding it to the __all__ list:
# 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. The workflow orchestrator sequences agent execution and manages data flow between pipeline stages:
# 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_idto maintain conversation isolation across concurrent tasks - Reuse existing LLM instances from
LLMFactoryunless the agent requires specific model capabilities - Store results in
UserOutputusingset_resto 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 and reference them in the agent class:
# 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:
# 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:
cd backend
uv run pytest # Run existing test suite
For manual verification:
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
Agentin a new file underapp/core/agents/, implementing__init__and an asyncrunmethod that handles chat history viaappend_chat_history - Register the class in
app/core/agents/__init__.pyby importing it and adding it to__all__ - Wire into workflow by instantiating the agent in
app/core/workflow.pywithinMathModelWorkFlow.executeand calling itsruncoroutine - Define supporting assets such as prompts in
app/core/prompts.pyand Pydantic schemas inapp/schemas/A2A.pywhen 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. 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. 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. 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.
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 →