How the Multi-Agent Architecture in MathModelAgent Works: Coordinator, Modeler, Coder, and Writer
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. 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) 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) 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) 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) 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. These Data-Transfer Objects (DTOs) enforce type safety and make the workflow deterministic:
- CoordinatorToModeler: Carries
questions(dict of sub-questions) andques_count - ModelerToCoder: Carries
questions_solution(the modeling plan) - CoderToWriter: Carries
coder_response(generated code) andcreated_images - WriterResponse: Carries
response_content(written sections) andfootnotes
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 within the MathModelWorkFlow class. Its execute coroutine sequences the agents and manages the sandboxed interpreter lifecycle:
# 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), 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:
COORDINATOR_PROMPTfor problem decompositionMODELER_PROMPTfor strategy designCODER_PROMPTfor code generationget_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:
- The agent publishes a status message to Redis indicating tool execution
- The tool executes (e.g.,
execute_codein the sandbox orsearch_papersfor literature) - The result appends as a
role: "tool"message to the chat history - 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
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
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
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
Agentbase class inbackend/app/core/agents/agent.py - DTOs defined in
backend/app/schemas/A2A.pyensure type-safe data transfer between pipeline stages - MathModelWorkFlow in
backend/app/core/workflow.pyorchestrates execution and manages the sandboxed interpreter viacreate_interpreterfrombackend/app/tools/interpreter_factory.py - Tool-calling loops enable
CoderAgentto execute and debug code iteratively usingexecute_code, whileWriterAgentsearches scholarly sources withsearch_papers - Real-time updates flow through Redis (
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) 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 for handoff data, and insert the agent call into the MathModelWorkFlow.execute method in 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) 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.
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 →