How CoordinatorAgent Breaks Down User Problems into Actionable Subtasks
The CoordinatorAgent breaks down user problems into actionable subtasks by prompting an LLM to extract and enumerate individual questions from raw text, then parsing the response into a structured JSON payload that downstream agents consume.
In the jihe520/mathmodelagent repository, the CoordinatorAgent serves as the entry point of a multi-agent pipeline designed for mathematical modeling tasks. This agent transforms free-form problem descriptions into discrete, machine-readable subtasks that enable parallel or sequential processing by specialized downstream agents.
Step-by-Step Task Decomposition Workflow
The decomposition process follows a deterministic six-step pipeline implemented in backend/app/core/agents/coordinator_agent.py.
Step 1: System Prompt Initialization
When the agent instantiates, it loads the COORDINATOR_PROMPT defined in backend/app/core/prompts.py. This system prompt instructs the LLM to verify that the input constitutes a mathematical-modeling problem and to output a strict JSON document listing every individual question separately.
self.system_prompt = COORDINATOR_PROMPT # Line 18 in coordinator_agent.py
Step 2: Chat History Assembly
The agent constructs a conversation history by appending the system prompt followed by the user’s raw input. This structured context ensures the LLM understands its role as a task parser rather than a problem solver.
append_chat_history(...) # Lines 22-26 in coordinator_agent.py
Step 3: LLM Invocation
The assembled chat history is passed to the underlying language model via self.model.chat(), which returns a raw text response intended to contain the JSON-formatted problem breakdown.
await self.model.chat(...) # Lines 30-34 in coordinator_agent.py
Step 4: JSON Cleaning and Parsing
The raw LLM output undergoes aggressive sanitization to ensure valid JSON parsing. The agent strips Markdown fences (json) and removes non-printable control characters using the regex pattern re.sub(r"[\x00-\x1F\x7F]", "", json_str) before attempting json.loads().
From this cleaned JSON, the agent extracts:
ques_count: An integer indicating the total number of subtasksques1,ques2, ...quesN: Individual question strings
Parsing logic resides in lines 36-45 of coordinator_agent.py.
Step 5: Retry Mechanism with Error Feedback
If JSON decoding fails or validation checks fail, the agent appends a specific error prompt to the chat history (⚠️ 上次响应格式错误: … 请严格输出JSON格式) and retries the LLM call. This cycle repeats up to three times before raising a fatal exception, ensuring robustness against hallucinated formatting or partial outputs.
# Retry loop handles malformed JSON (Lines 48-62)
Step 6: Structured Data Return
Upon successful parsing, the agent returns a CoordinatorToModeler Pydantic model (defined in backend/app/schemas/A2A.py). This object contains the questions dictionary and ques_count, forming the contractual handoff to the Modeler Agent.
return CoordinatorToModeler(...) # Lines 45-46
JSON Schema for Subtask Extraction
The COORDINATOR_PROMPT embeds a template requiring the LLM to output JSON matching this exact shape:
{
"title": "<problem title>",
"background": "<any extra context>",
"ques_count": 3,
"ques1": "<first sub-question>",
"ques2": "<second sub-question>",
"ques3": "<third sub-question>"
}
Each quesN field becomes an independent modeling subtask. The ques_count field enables downstream agents to iterate over tasks deterministically, while the atomic nature of each question allows the Modeler Agent and subsequent Coder Agent to generate specific modeling plans and implementations for discrete problem components.
Error Handling and Robustness Features
The CoordinatorAgent implements multiple safeguards to handle real-world LLM inconsistencies:
- Markdown fence stripping: Removes stray
jsonmarkers that corrupt parsers - Control character sanitization: Strips invisible ASCII characters (
\x00-\x1F\x7F) that break JSON decoders - Explicit error injection: Feeds parsing failure details back to the LLM context for self-correction
- Observability: Logs all retry attempts and fatal parsing errors via the centralized logger in
backend/app/utils/log_util.py
Implementation Example
Instantiating and Running the Coordinator
from backend.app.core.agents.coordinator_agent import CoordinatorAgent
from backend.app.core.llm.llm import LLM
import asyncio
async def demo():
# Initialize with your configured LLM backend
llm = LLM(model_name="gpt-4o-mini")
# Create coordinator instance
coordinator = CoordinatorAgent(task_id="demo-001", model=llm)
# Raw user input containing multiple implicit questions
raw_text = """
研究城市交通拥堵的原因并给出改进建议。请回答以下问题:
1. 交通流量随时间的变化规律是什么?
2. 哪些路段的拥堵最严重?
3. 提出三套缓解拥堵的策略并进行成本-效益分析。
"""
# Execute decomposition
result = await coordinator.run(raw_text)
print(f"Subtask count: {result.ques_count}")
print(f"Structured tasks: {result.questions}")
asyncio.run(demo())
Expected LLM Output Structure
{
"title": "城市交通拥堵分析",
"background": "",
"ques_count": 3,
"ques1": "交通流量随时间的变化规律是什么?",
"ques2": "哪些路段的拥堵最严重?",
"ques3": "提出三套缓解拥堵的策略并进行成本‑效益分析。"
}
The resulting CoordinatorToModeler object now holds atomic subtasks ready for independent processing.
Summary
- The CoordinatorAgent operates as a structured extraction layer between raw user input and the modeling pipeline.
- Task decomposition relies on prompt engineering (
COORDINATOR_PROMPT) rather than hardcoded rules, enabling flexibility across problem domains. - JSON validation and retry logic (maximum three attempts) ensure pipeline stability despite LLM output variance.
- Output conforms to the
CoordinatorToModelerschema, providing a type-safe contract for the Modeler Agent. - The
ques_countfield and enumeratedquesNentries create explicit boundaries for parallel or sequential subtask execution.
Frequently Asked Questions
What happens if the LLM returns invalid JSON after three retries?
If JSON parsing fails after three retry cycles, the CoordinatorAgent logs the fatal error via log_util.py and raises an exception, halting the pipeline to prevent corrupted data from reaching downstream agents.
How does the CoordinatorAgent determine the number of subtasks?
The agent does not calculate this internally; rather, it relies on the LLM to populate the ques_count field in the JSON response. The LLM analyzes the user text to count distinct questions or modeling requirements, which the agent then validates against the actual number of quesN keys present.
What is the relationship between CoordinatorAgent and the ModelerAgent?
The CoordinatorAgent produces a CoordinatorToModeler Pydantic object containing structured questions, which serves as the sole input for the ModelerAgent. This creates a strict agent-to-agent (A2A) contract defined in backend/app/schemas/A2A.py, decoupling task decomposition from modeling strategy generation.
Can the CoordinatorAgent handle non-mathematical problems?
According to the source code in backend/app/core/prompts.py, the COORDINATOR_PROMPT specifically instructs the LLM to verify that the input is a mathematical-modeling problem. While the underlying mechanics could theoretically parse other structured outputs, the system prompt is optimized for mathematical and statistical problem decomposition.
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 →