Agent Memory Management and Summarization in MathModelAgent: Implementation and Code

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. 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:

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:

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

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 handles the actual LLM call:

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

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_historyclear_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:

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:

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:

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, 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, 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →