# Agent Memory Management and Summarization in MathModelAgent: Implementation and Code

> Discover how MathModelAgent manages its memory with a rolling window and LLM summarization. Learn about its efficient bounded memory system and code implementation.

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

---

**MathModelAgent implements a bounded memory system using a rolling window approach where older dialogue is automatically summarized via LLM calls when `chat_history` exceeds `max_memory`, while strictly preserving tool call/response pairs to prevent execution errors.**

MathModelAgent provides a robust **agent memory management and summarization** architecture centered on the base `Agent` class in [`backend/app/core/agents/agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/agent.py). The system maintains conversation context within configurable token limits by compressing historical messages into structured summaries, ensuring that critical tool interactions remain intact even during aggressive memory pruning.

## How the Base Agent Class Handles Memory

The core mechanism resides in the `Agent` class, which orchestrates message storage, overflow detection, and safe truncation. Every agent instance maintains a `self.chat_history` list and a `self.max_memory` threshold that dictates when summarization must occur.

### Appending Messages and Automatic Triggers

When new messages enter the system via `Agent.append_chat_history`, the method immediately evaluates whether memory management is required. The critical logic resides in [`backend/app/core/agents/agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/agent.py):

```python
async def append_chat_history(self, msg: dict) -> None:
    self.chat_history.append(msg)
    if msg.get("role") != "tool":
        await self.clear_memory()

```

**Key implementation detail:** The `clear_memory` call is skipped only when the message role is `"tool"`. This ensures that tool responses remain paired with their original requests, preventing orphaned context that could break the agent's execution flow.

### Detecting Memory Overflow

The `clear_memory` method acts as the gatekeeper for the **agent memory management and summarization** pipeline. It first checks if the history length exceeds the instance's `max_memory` limit:

```python
if len(self.chat_history) <= self.max_memory:
    return

```

Standard agents default to `max_memory=12`, while the `WriterAgent` subclass extends this to `25` to accommodate longer writing sessions. If the threshold is not exceeded, the method returns immediately without invoking the LLM summarizer.

### Finding Safe Cut-Points for Tool Calls

Because tool interactions consist of paired messages—a request containing `tool_calls` and a subsequent response with `role="tool"`—the system must never truncate between these pairs. The `_find_safe_preserve_point` method handles this by walking backwards from the last `N` messages (default `N=3`) and validating potential cut points via `_is_safe_cut_point`:

- **Safe point criteria:** No dangling `tool` message can remain after truncation
- **Fallback behavior:** If no valid cut point exists within the search window, the system defaults to preserving only the most recent message
- **Implementation location:** [`backend/app/core/agents/agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/agent.py)

This protective logic ensures that the agent's ability to execute multi-step tool workflows remains unbroken even during aggressive memory compression.

### LLM-Driven Summarization Process

Once a safe preservation point is identified, all messages preceding that index are fed to the summarization engine. The `simple_chat` helper function in [`backend/app/core/llm/llm.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/llm/llm.py) handles the actual LLM call:

```python
summary = await simple_chat(self.model, summarize_history)

```

The reconstructed `chat_history` follows a strict three-part structure:

1. **System message** (if present in original history)
2. **Summary message** with the label `[历史对话总结]` followed by the compressed context
3. **Preserved recent messages** including any pending tool interactions

This architecture allows the agent to retain semantic context from earlier conversation turns while keeping the token count within operational limits.

### Robust Fallback Mechanisms

If the LLM summarization call fails due to network errors or model unavailability, the system invokes `_get_safe_fallback_history`. This method guarantees continuity by preserving:

- The system message (if any)
- A minimal set of recent non-tool messages
- Any active tool call pairs

This fallback ensures that the agent remains functional even when the **agent memory management and summarization** pipeline encounters unexpected failures.

## WriterAgent-Specific Implementation

The `WriterAgent` class in [`backend/app/core/agents/writer_agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/writer_agent.py) extends the base memory architecture for long-form content generation tasks. It configures a larger memory window (`max_memory=25`) to maintain extended context during report writing.

Additionally, it exposes a public `summarize` method for task-level summarization:

```python
async def summarize(self) -> str:
    await self.append_chat_history(
        {"role": "user", "content": "请简单总结以上完成什么任务取得什么结果:"}
    )
    response = await self.model.chat(...)

```

This method leverages the same `append_chat_history` → `clear_memory` pipeline, ensuring that even manual summarization requests respect the memory bounds. The Chinese prompt requests a brief summary of completed tasks and results, producing a final deliverable while the underlying system manages token limits automatically.

## Practical Code Examples

### Configuring a WriterAgent with Extended Memory

Instantiate a `WriterAgent` with custom memory limits to handle extended writing sessions without premature context loss:

```python
from app.core.llm.llm import LLM
from app.core.agents.writer_agent import WriterAgent
from app.tools.openalex_scholar import OpenAlexScholar

# Initialize the underlying LLM

llm = LLM(
    api_key="YOUR_API_KEY",
    model="gpt-4o-mini",
    base_url=None,
    task_id="task-1234",
)

# Optional scholar helper for paper search

scholar = OpenAlexScholar()

writer = WriterAgent(
    task_id="task-1234",
    model=llm,
    max_chat_turns=10,
    max_memory=25,          # Extended window for long writing sessions

    scholar=scholar,
)

# Execute a writing turn—memory handling is automatic

response = await writer.run(
    prompt="请撰写关于城市交通拥堵的建模思路。"
)
print(response.response_content)

```

Each call to `run` appends the user prompt and assistant response to `chat_history`, triggering `clear_memory` automatically if the history exceeds 25 messages.

### Triggering Manual Task Summarization

Generate final task summaries before ending a session:

```python
summary_text = await writer.summarize()
print("Task summary:", summary_text)

```

The `summarize` method injects a summary request into the history, generates the LLM response, and stores it—still protected by the standard memory-clearing routine.

### Inspecting Internal Chat History

Debug the memory state to verify summarization behavior:

```python
print("Current history length:", len(writer.chat_history))
for msg in writer.chat_history:
    print(msg["role"], "→", msg["content"][:80])

```

When automatic summarization occurs, you will observe a message with the role `assistant` or `system` containing `[历史对话总结]`, indicating that older context has been compressed.

## Summary

- **Bounded memory architecture:** The `Agent` class enforces a strict `max_memory` limit (default 12, configurable per subclass) to prevent token overflow.
- **Tool-call safety:** The `_find_safe_preserve_point` mechanism ensures that tool request/response pairs are never split during truncation, maintaining execution integrity.
- **LLM-driven compression:** Historical messages are summarized via `simple_chat` in [`backend/app/core/llm/llm.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/llm/llm.py), with results prefixed by `[历史对话总结]`.
- **Automatic triggers:** Every non-tool message appended via `append_chat_history` invokes `clear_memory`, making memory management transparent to agent operations.
- **Robust fallbacks:** The `_get_safe_fallback_history` method preserves critical context if the summarization LLM call fails.
- **Subclass flexibility:** `WriterAgent` demonstrates extended memory configuration (`max_memory=25`) and manual summarization capabilities for specialized workflows.

## Frequently Asked Questions

### How does MathModelAgent prevent tool call failures during memory summarization?

The system employs `_is_safe_cut_point` validation within `_find_safe_preserve_point` to ensure that truncation never occurs between a tool request and its corresponding response. Because `append_chat_history` skips `clear_memory` when the message role is `"tool"`, active tool workflows remain intact even when the preceding dialogue is summarized.

### What is the default memory limit for agents in MathModelAgent?

The base `Agent` class defaults to `max_memory=12` conversation turns. The `WriterAgent` subclass overrides this default to `25` to accommodate longer content generation sessions without losing contextual continuity.

### How does the WriterAgent differ from the base Agent in memory management?

According to the source code in [`backend/app/core/agents/writer_agent.py`](https://github.com/jihe520/mathmodelagent/blob/main/backend/app/core/agents/writer_agent.py), `WriterAgent` sets a higher `max_memory` threshold and exposes a public `summarize` method for task-level summarization. However, it inherits the same core **agent memory management and summarization** logic, including the safe cut-point algorithms and fallback mechanisms defined in the base class.

### What happens if the LLM summarization call fails?

If `simple_chat` raises an exception during the summarization phase, the agent falls back to `_get_safe_fallback_history`, which guarantees retention of the system message and the most recent non-tool messages. This ensures the agent remains operational with minimal context loss rather than crashing or corrupting the conversation state.