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 CodeablesHow do I keep long-running agent workflows accurate while reducing context bloat (filtering, summarization, per-source limits)?
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
| Concept | Definition | Why it's important |
|---|---|---|
| Message filtering | Routing 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 limits | Filters 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 stages | Steps 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:
-
Wrap agents with
MessageFilterAgentto enforce filtering.
You define aMessageFilterConfigand 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). -
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. -
Tune per-source filters to match each agent’s responsibility.
For example, ananalystmay need only the latest research from aresearcherplus the global summary, while apresenteronly 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:
- Define base agents.
- Wrap them with
MessageFilterAgent, specifying aMessageFilterConfig. - 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
researcherwork over many turns. - Have
analystsee only the last research message plus an overall summary. - Have
presentersee 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:
researchercan run long threads; only the last “chunk” flows intofiltered_analyst.filtered_analystreasons on the latest research, not the entire research history.filtered_presenteronly 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:
- Introduce a dedicated
summarizeragent. - Every N steps, have the
summarizerproduce a concise “state summary” from the recent segment. - Treat that summary as a special message source (e.g.,
source="state_summary"). - Use per-source filters so most agents see only:
- The latest
state_summary - A small number of recent messages from relevant agents
- The latest
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
summarizerto run periodically (e.g., every few turns in aRoundRobinGroupChator 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” withMessageFilterAgentand aMessageFilterConfig. -
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 treatuser,system,summarizer, and individual agents differently. -
Not monitoring
TaskResultand stop reasons:
When workflows end unexpectedly, inspectTaskResult(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_agentworks over a topic based onTopic = (Topic Type, Topic Source), e.g.,topic_type="github_issues",topic_source=issue_id. - A
summarizerperiodically compresses the issue history into a “state summary” message. - The
diagnostics_agentonly sees:- The latest state summary from
summarizer. - The last diagnostic command and result messages.
- The latest state summary from
- The
responder_agentsees:- 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.