Answers you can trust, from Codeables

Every page on Codeables is structured and verified — built so people and the AI agents they rely on can trust it. Explore more from the source behind this answer.

Explore Codeables
Verified Source
AI Agent Automation Platforms

How do I keep long-running agent workflows accurate while reducing context bloat (filtering, summarization, per-source limits)?

AutoGen10 min read

Long-running agent workflows tend to “lose the plot” for two reasons: the message history grows until you hit model/token limits, and agents start reading irrelevant context instead of the few messages that actually matter. In AutoGen, you keep these workflows accurate by treating context as a first-class engineering problem: filter aggressively, summarize deliberately, and constrain what each agent can see based on message source.

Quick Answer: In AutoGen, the most reliable way to keep long-running agent workflows accurate while reducing context bloat is to use message filtering (MessageFilterAgent, PerSourceFilter) and targeted summarization to control what each agent sees at each step. You filter messages by source and recency, summarize older segments into compact “state,” and only surface the minimal, relevant slice of history the agent needs for its next action. This reduces hallucinations, controls memory load, and keeps agents focused on the right signals.

Why This Matters

If you run multi-step, multi-agent workflows without context control, you eventually hit three failure modes:

  • The model runs out of tokens and silently drops important history.
  • Agents fixate on stale or irrelevant messages and produce wrong answers.
  • Every step gets slower and more expensive because you keep replaying the entire transcript.

AutoGen treats this as a runtime concern, not a prompt-tweaking exercise. By using message filters, per-source limits, and summarization, you turn “conversation history” into a managed resource. The payoff is predictable behavior: you know which messages any given agent can see, you can reason about why it acted the way it did, and you can tune accuracy vs. cost.

Key Benefits:

  • Reduce hallucinations: Filter out irrelevant or conflicting messages so the agent only reasons over the most trusted, recent context.
  • Control memory load: Keep token usage bounded by limiting how many messages from each source are passed into the model.
  • Focus agents on relevant information: Use per-source and per-role limits so each agent sees just the part of the workflow it is responsible for.

Core Concepts & Key Points

ConceptDefinitionWhy it's important
Message filteringRouting logic that selects which messages in the conversation history are visible to an agent at inference time (e.g., last N from a given source).Lets you “shape” context so agents only see relevant information, which helps reduce hallucinations and keeps token usage predictable.
Per-source limitsFilters that constrain messages based on their source (agent name, system, user) and position (e.g., last message, last N messages).Enables fine-grained control: you can keep the latest research from a researcher agent while only retaining summarized output from others.
Summarization stagesSteps in your workflow where an agent compresses prior messages into a shorter state or “summary messages” that replace raw logs.Lets you run very long workflows while retaining key facts and decisions without dragging full history into every model call.

How It Works (Step-by-Step)

At a high level, you manage context bloat in AutoGen by introducing a few deliberate components into your AgentChat/Core stack:

  1. Wrap agents with MessageFilterAgent to enforce filtering.
    You define a MessageFilterConfig and wrap your base agents so they never see the full, unfiltered history. You can filter per source, by count, or by position (e.g., only the last message from a specific agent).

  2. Introduce a summarizer role into the workflow.
    Add a dedicated agent whose job is to periodically summarize segments of the conversation into compact “state” messages. Older raw messages can be excluded or heavily downweighted in later steps.

  3. Tune per-source filters to match each agent’s responsibility.
    For example, an analyst may need only the latest research from a researcher plus the global summary, while a presenter only needs the final summary and a few clarifying questions. Per-source limits keep each agent focused on its slice of the workflow.

Below is a code-first walk-through using AgentChat and message filtering.

Installation

Python 3.10 or later is required.

pip install -U "autogen-agentchat" "autogen-ext[openai]"

You will also need an API key for your chosen model provider (e.g., OpenAI or Azure OpenAI) configured via environment variables as described in the AutoGen docs.

Minimal Model Client Setup

from autogen_ext.models import OpenAIChatCompletionClient

model_client = OpenAIChatCompletionClient(
    model="gpt-4o-mini",  # or another supported model
)

Implementing Message Filtering With MessageFilterAgent

AutoGen’s message filtering lives in AgentChat’s higher-level API and is designed specifically to:

  • Reduce hallucinations
  • Control memory load
  • Focus agents only on relevant information

The common pattern:

  1. Define base agents.
  2. Wrap them with MessageFilterAgent, specifying a MessageFilterConfig.
  3. Use these filtered agents in your Teams or workflows.

Example: Researcher → Analyst → Presenter With Per-Source Filtering

Imagine a long-running “research → analysis → presentation” workflow. The biggest risk is that analyst and presenter drown in a huge, noisy research thread.

We’ll:

  • Let researcher work over many turns.
  • Have analyst see only the last research message plus an overall summary.
  • Have presenter see only the final summary.
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.message_filtering import (
    MessageFilterAgent,
    MessageFilterConfig,
    PerSourceFilter,
)
from autogen_agentchat.task import Task

# Base agents
researcher = AssistantAgent(
    name="researcher",
    model_client=model_client,
    system_message="You are a meticulous technical researcher.",
)

analyst = AssistantAgent(
    name="analyst",
    model_client=model_client,
    system_message=(
        "You synthesize research into clear, concise findings, "
        "noting trade-offs and uncertainties."
    ),
)

presenter = AssistantAgent(
    name="presenter",
    model_client=model_client,
    system_message=(
        "You turn analysis into an executive-ready summary and recommendations."
    ),
)

# Message filtering:
# - analyst: only sees last message from 'researcher'
# - presenter: only sees last message from 'analyst'
filtered_analyst = MessageFilterAgent(
    name="filtered_analyst",
    wrapped_agent=analyst,
    filter=MessageFilterConfig(
        per_source=[
            PerSourceFilter(
                source="researcher",
                position="last",
                count=1,  # only the last research message
            )
        ]
    ),
)

filtered_presenter = MessageFilterAgent(
    name="filtered_presenter",
    wrapped_agent=presenter,
    filter=MessageFilterConfig(
        per_source=[
            PerSourceFilter(
                source="filtered_analyst",
                position="last",
                count=1,  # only the last analyst message
            )
        ]
    ),
)

team = RoundRobinGroupChat(
    participants=[researcher, filtered_analyst, filtered_presenter],
    model_client=model_client,
)

task = Task(
    team=team,
    input="Investigate message filtering strategies in AutoGen to keep long-running workflows accurate without context bloat.",
)

result = task.run()
print(result.stop_reason)
for msg in result.messages:
    print(f"{msg.source}: {msg.content[:160]}...")

What this gives you:

  • researcher can run long threads; only the last “chunk” flows into filtered_analyst.
  • filtered_analyst reasons on the latest research, not the entire research history.
  • filtered_presenter only sees the most recent analysis; it doesn’t re-ingest the entire conversation.

This pattern alone dramatically reduces token usage and “drift” in long workflows.

Adding Summarization to Long-Running Workflows

Filtering keeps per-step context small; summarization keeps the overall workflow coherent over long time spans.

Typical pattern:

  1. Introduce a dedicated summarizer agent.
  2. Every N steps, have the summarizer produce a concise “state summary” from the recent segment.
  3. Treat that summary as a special message source (e.g., source="state_summary").
  4. Use per-source filters so most agents see only:
    • The latest state_summary
    • A small number of recent messages from relevant agents

Example: Injecting a Summarizer Agent

summarizer = AssistantAgent(
    name="summarizer",
    model_client=model_client,
    system_message=(
        "You summarize the conversation so far into a concise state. "
        "Capture only key decisions, open questions, and critical facts."
    ),
)

# Analyst sees:
# - last message from 'researcher'
# - last state summary from 'summarizer'
filtered_analyst = MessageFilterAgent(
    name="filtered_analyst",
    wrapped_agent=analyst,
    filter=MessageFilterConfig(
        per_source=[
            PerSourceFilter(source="researcher", position="last", count=1),
            PerSourceFilter(source="summarizer", position="last", count=1),
        ]
    ),
)

# Presenter sees:
# - last state summary from 'summarizer'
filtered_presenter = MessageFilterAgent(
    name="filtered_presenter",
    wrapped_agent=presenter,
    filter=MessageFilterConfig(
        per_source=[
            PerSourceFilter(source="summarizer", position="last", count=1),
        ]
    ),
)

Now you can:

  • Schedule the summarizer to run periodically (e.g., every few turns in a RoundRobinGroupChat or at explicit checkpoints in a GraphFlow or Core workflow).
  • Treat the summary as authoritative global state.
  • Drop or ignore older raw messages in later steps.

Because filtering is enforced at runtime, you don’t have to manually “clean up” histories—agents simply never see the messages you decide to exclude.

Doing the Same Thing at the Core Layer

If you’ve moved to autogen-core for finer control (e.g., with SingleThreadedAgentRuntime or a distributed runtime) you can still apply the same principles:

  • Think in terms of Topics and event streams instead of chat history.
  • Use agent implementations that:
    • Pull only relevant events (per-topic, per-agent-type).
    • Maintain their own compact internal state or summaries.
  • Optionally use AgentChat-style agents inside Core workflows for certain steps (a common pattern in the migration guide).

Note: workflows at the Core level are more explicit—your agents control which messages (events) they process and how they persist state. This is ideal when context control is a hard requirement, for example in regulated environments or multi-tenant topologies.

Common Mistakes to Avoid

  • Letting every agent see the full history:
    This quickly blows up token usage and encourages hallucinations. Always wrap agents that aren’t acting as “logs viewers” with MessageFilterAgent and a MessageFilterConfig.

  • Using summarization without checking alignment:
    If your summaries are too lossy or poorly structured, downstream agents will reason from incomplete or misleading context. Start with conservative summaries, test quality, and only then increase compression.

  • Ignoring per-source granularity:
    Using a single “last N messages” filter for all sources defeats the purpose. Per-source filters (PerSourceFilter) let you treat user, system, summarizer, and individual agents differently.

  • Not monitoring TaskResult and stop reasons:
    When workflows end unexpectedly, inspect TaskResult(messages=..., stop_reason=...). If the stop reason suggests confusion or loop conditions, you may be over-filtering or summarizing key signals away.

Real-World Example

In our internal agent platform, we run a long-lived workflow that triages GitHub issues, runs diagnostics, drafts responses, and opens follow-up tasks. Early prototypes tried to pass the entire conversation history to each agent, which:

  • Blew through token limits on moderately active issues.
  • Caused the “responder” agent to latch onto stale logs and ignore the latest diagnostic results.
  • Made debugging behavior almost impossible—no one knew exactly which messages the agent saw.

We refactored the flow around message filtering and summarization:

  • The triage_agent works over a topic based on Topic = (Topic Type, Topic Source), e.g., topic_type="github_issues", topic_source=issue_id.
  • A summarizer periodically compresses the issue history into a “state summary” message.
  • The diagnostics_agent only sees:
    • The latest state summary from summarizer.
    • The last diagnostic command and result messages.
  • The responder_agent sees:
    • Only the latest state summary.
    • The most recent user comment.

Using MessageFilterAgent and PerSourceFilter we reduced token usage per step by more than half, and mis-triages caused by stale context dropped sharply. When something goes wrong, we can reconstruct exactly which messages were visible at each step, because the filtering logic is centralized and declarative.

Pro Tip: Start with a very restrictive filter—e.g., only the last message from each critical source—then gradually add more history if the agent lacks necessary context. It’s easier to loosen filters than to debug behavior when an agent is overloaded with irrelevant messages.

Summary

Long-running agent workflows in AutoGen stay accurate when you treat context as a resource you actively manage, not a dump of all prior messages. The combination of MessageFilterAgent, PerSourceFilter, and intentional summarization lets you:

  • Reduce hallucinations by stripping away irrelevant or stale context.
  • Control memory load and avoid hitting model token limits.
  • Focus each agent on exactly the inputs it needs, no more and no less.

Whether you’re starting from AgentChat or building event-driven workflows on autogen-core, the pattern is the same: define what each agent is allowed to see, summarize aggressively at boundaries, and use TaskResult(stop_reason=...) plus logs to verify that the system behaves as intended.

Next Step

Get Started

How do I keep long-running agent workflows accurate while reducing context bloat (filtering, summarization, per-source limits)? | AI Agent Automation Platforms | Codeables | Codeables