# How CoordinatorAgent Breaks Down User Problems into Actionable Subtasks

> Learn how the CoordinatorAgent breaks down user problems into actionable subtasks by using an LLM to extract questions and structure responses. Discover the process today.

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

---

**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`](https://github.com/jihe520/mathmodelagent/blob/main/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`](https://github.com/jihe520/mathmodelagent/blob/main/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.

```python
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.

```python
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.

```python
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 subtasks
- `ques1`, `ques2`, ... `quesN`: Individual question strings

Parsing logic resides in lines 36-45 of [`coordinator_agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/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.

```python

# 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`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/schemas/A2A.py)). This object contains the `questions` dictionary and `ques_count`, forming the contractual handoff to the **Modeler Agent**.

```python
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:

```json
{
  "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 `````json````` markers 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`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/utils/log_util.py)

## Implementation Example

### Instantiating and Running the Coordinator

```python
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

```json
{
  "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 **`CoordinatorToModeler`** schema, providing a type-safe contract for the Modeler Agent.
- The **`ques_count`** field and enumerated **`quesN`** entries 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`](https://github.com/jihe520/mathmodelagent/blob/main/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`](https://github.com/jihe520/mathmodelagent/blob/main/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`](https://github.com/jihe520/mathmodelagent/blob/main/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.