Back to blog

Product · Jul 27, 2026

Facio's Backpressure Discipline: How AI Agents Adjust to Capacity Without Drowning the System

AI agents produce work. The downstream — databases, APIs, humans, dependent agents — has capacity limits. The naive approach floods downstream regardless, causing cascading errors, latency spikes, and human review burnout. Facio's backpressure discipline gives agents mechanisms to sense downstream capacity and adjust production rate: real-time capacity sensing, adaptive throttling, queue management with overflow strategies, flow control signals, and backpressure-aware retries. The agent flows at the pace the system can absorb.

BackpressureFlow ControlCapacity SensingCircuit BreakerProduction Discipline

Facio's Backpressure Discipline: How AI Agents Adjust to Capacity Without Drowning the System

AI agents produce work. The work flows downstream — to databases, to APIs, to humans, to other agents. The downstream systems have capacity limits. The databases can handle N writes per second. The APIs throttle at M requests per minute. The human reviewers can review K items per hour. The dependent agents can process P tasks per minute.

The naive approach floods the downstream regardless. The agent produces work as fast as it can. The downstream gets backed up. The timeouts start. The errors cascade. The system collapses.

Facio's backpressure discipline gives AI agents the mechanisms to sense downstream capacity and adjust their production rate. The agent doesn't outrun the downstream. The agent doesn't outrun the humans. The agent flows at the pace the system can absorb.

Here's how the discipline works, what backpressure it includes, and why backpressure discipline is what makes AI agents sustainable in production where downstream capacity is finite.

The Backpressure Reality

Production AI agents face a backpressure problem:

Problem 1: Downstream is finite. Every downstream system has limits. Databases have write throughput. APIs have rate limits. Humans have review capacity. The agent ignores the limits at the system's peril.

Problem 2: Errors cascade under overload. When the downstream is overloaded, it returns errors. The agent sees errors, retries, makes the overload worse. The cascade spreads.

Problem 3: Latency degrades. When the downstream is at capacity, latency degrades. The agent waits. The user's request takes longer. The user is unhappy.

Problem 4: Data loss in queues. When queues are full, the oldest items get dropped. The agent's work is lost. The user is affected.

Problem 5: Human review burnout. When humans are flooded with review requests, the quality of review drops. The humans approve things they shouldn't. The system becomes unsafe.

The naive approach — produce as fast as possible — fails every test. The team ends up with a system where the agent is the bottleneck for cascading failures.

The Backpressure Discipline

Facio's backpressure discipline has five pillars. Each addresses a different aspect of capacity-aware flow.

Pillar 1: Capacity Sensing (Know the Downstream)

The agent senses the capacity of downstream systems:

# Capacity sensing
downstream_capacity = {
    "postgres.production_db": {
        "current_writes_per_second": 1200,
        "max_sustained_writes_per_second": 1500,
        "current_utilization_pct": 80,
        "queue_depth": 45,
        "p99_latency_ms": 120,
        "degraded": False,
    },
    "stripe.api": {
        "current_requests_per_second": 95,
        "max_requests_per_second": 100,
        "current_utilization_pct": 95,
        "queue_depth": 230,
        "p99_latency_ms": 850,
        "degraded": True,
        "rate_limit_remaining": 50,
    },
    "human_review.queue": {
        "pending_items": 1247,
        "max_pending_items": 1500,
        "current_utilization_pct": 83,
        "avg_review_time_seconds": 240,
        "reviewers_available": 8,
        "reviewers_at_capacity": False,
    },
    "elasticsearch.index": {
        "current_writes_per_second": 4500,
        "max_writes_per_second": 5000,
        "current_utilization_pct": 90,
        "queue_depth": 1200,
        "p99_latency_ms": 450,
        "degraded": True,
    },
}

def get_downstream_capacity(service):
    return downstream_capacity.get(service, {"utilization_pct": 0, "degraded": False})

The sensing provides real-time visibility into downstream capacity. The agent knows what's possible.

Pillar 2: Adaptive Throttling (Adjust Production Rate)

The agent adjusts its production rate based on downstream capacity:

# Adaptive throttling
def calculate_production_rate(agent_id, targets):
    rates = {}
    for target in targets:
        capacity = get_downstream_capacity(target)
        if capacity["degraded"]:
            utilization = capacity["current_utilization_pct"]
            if utilization > 95:
                rates[target] = {"rate_per_second": 10, "reason": "near_capacity"}
            elif utilization > 80:
                rates[target] = {"rate_per_second": 50, "reason": "high_load"}
            else:
                rates[target] = {"rate_per_second": 100, "reason": "normal"}
        else:
            rates[target] = {"rate_per_second": 500, "reason": "healthy"}
    return rates

# Example: agent producing data to three downstream systems
production_rates = calculate_production_rate(
    agent_id="agent-data-pipeline-007",
    targets=["postgres.production_db", "stripe.api", "elasticsearch.index"]
)
# Result:
# {
#     "postgres.production_db": {"rate_per_second": 500, "reason": "healthy"},
#     "stripe.api": {"rate_per_second": 10, "reason": "near_capacity"},
#     "elasticsearch.index": {"rate_per_second": 50, "reason": "high_load"},
# }
# The agent's effective rate is the minimum: 10/second

The adaptive throttling ensures the agent doesn't outrun the slowest downstream. The flow is balanced.

Pillar 3: Queue Management (Buffer Without Drowning)

When production temporarily exceeds capacity, queues buffer the work:

# Queue management
queue_policies = {
    "postgres.write_queue": {
        "max_size": 10000,
        "overflow_strategy": "reject_oldest",
        "overflow_action": "alert_AND_fail_fast",
        "drain_strategy": "fifo_with_priority",
        "estimated_drain_time_seconds": 8,
    },
    "human_review.queue": {
        "max_size": 1500,
        "overflow_strategy": "reject_new",
        "overflow_action": "alert_AND_route_to_secondary_reviewer",
        "drain_strategy": "priority_based",
        "estimated_drain_time_seconds": 1800,
    },
    "stripe.api_queue": {
        "max_size": 500,
        "overflow_strategy": "block_producer",
        "overflow_action": "agent_throttles_AND_reports",
        "drain_strategy": "fifo_with_retry_backoff",
        "estimated_drain_time_seconds": 5,
    },
    "elasticsearch.ingest_queue": {
        "max_size": 50000,
        "overflow_strategy": "drop_lowest_priority",
        "overflow_action": "log_drops_AND_alert",
        "drain_strategy": "bulk_optimized",
        "estimated_drain_time_seconds": 12,
    },
}

def enqueue_with_backpressure(queue_name, item):
    queue = queue_policies[queue_name]
    if queue_size(queue_name) >= queue["max_size"]:
        if queue["overflow_strategy"] == "reject_oldest":
            evict_oldest(queue_name)
        elif queue["overflow_strategy"] == "reject_new":
            return {"rejected": True, "reason": "queue_full"}
        elif queue["overflow_strategy"] == "block_producer":
            wait_for_drain(queue_name)
        elif queue["overflow_strategy"] == "drop_lowest_priority":
            drop_lowest_priority(queue_name)
    return enqueue(queue_name, item)

The queue management prevents catastrophic overload. The strategies differ by downstream characteristics.

Pillar 4: Flow Control Signals (Communicate Pressure)

The agent and downstream exchange flow control signals:

# Flow control signals
flow_control_signals = {
    "ready_to_receive": {
        "description": "Downstream is ready for more work",
        "signal": "actor_ready",
        "trigger": "queue_depth < 50% of max",
        "agent_action": "increase_production_rate",
    },
    "slow_down": {
        "description": "Downstream is approaching capacity",
        "signal": "actor_slow_down",
        "trigger": "queue_depth > 70% of max OR utilization > 80%",
        "agent_action": "reduce_production_rate",
    },
    "near_capacity": {
        "description": "Downstream is near capacity",
        "signal": "actor_near_capacity",
        "trigger": "queue_depth > 90% of max OR utilization > 95%",
        "agent_action": "minimal_production_rate",
    },
    "overflow_imminent": {
        "description": "Downstream is about to overflow",
        "signal": "actor_overflow_imminent",
        "trigger": "queue_depth > 95% of max",
        "agent_action": "stop_production_AND_alert",
    },
    "recovered": {
        "description": "Downstream has recovered from pressure",
        "signal": "actor_recovered",
        "trigger": "queue_depth < 30% of max AND utilization < 50% for 5 minutes",
        "agent_action": "gradually_increase_production_rate",
    },
}

The signals allow the downstream to communicate pressure. The agent responds to the actual state, not a stale measurement.

Pillar 5: Backpressure-Aware Retries (Don't Make Overload Worse)

Retries are backpressure-aware:

# Backpressure-aware retries
def retry_with_backpressure(action, max_attempts=5):
    for attempt in range(max_attempts):
        try:
            return execute_action(action)
        except CapacityError as e:
            if attempt == max_attempts - 1:
                raise
            if e.signal == "actor_overflow_imminent":
                wait_time = exponential_backoff(attempt) * 2
            elif e.signal == "actor_near_capacity":
                wait_time = exponential_backoff(attempt) * 1.5
            else:
                wait_time = exponential_backoff(attempt)
            time.sleep(wait_time)
        except RateLimitError as e:
            time.sleep(e.retry_after_seconds)
        except TransientError as e:
            time.sleep(exponential_backoff(attempt))
    raise RetryExhaustedError(action)

The retries respect the downstream's capacity. Retries don't make overload worse.

The Backpressure Patterns

Several patterns emerge from disciplined backpressure.

Pattern 1: Circuit Breaker Pattern

When downstream is consistently failing, the circuit breaker trips:

# Circuit breaker pattern
circuit_breaker_states = {
    "stripe.api": {
        "state": "open",
        "last_failure_timestamp": "2026-07-27T09:23:45Z",
        "consecutive_failures": 12,
        "failure_threshold": 10,
        "open_until": "2026-07-27T09:33:45Z",
        "half_open_attempts": 0,
    },
    "postgres.production_db": {
        "state": "closed",
        "consecutive_failures": 0,
        "failure_threshold": 10,
        "last_failure_timestamp": None,
    },
}

def execute_with_circuit_breaker(service, action):
    breaker = circuit_breaker_states[service]
    if breaker["state"] == "open":
        if time.now() < breaker["open_until"]:
            raise CircuitBreakerOpenError(service)
        else:
            breaker["state"] = "half_open"
            breaker["half_open_attempts"] = 0

    try:
        result = execute_action(action)
        if breaker["state"] == "half_open":
            breaker["state"] = "closed"
            breaker["consecutive_failures"] = 0
        return result
    except Exception as e:
        breaker["consecutive_failures"] += 1
        if breaker["consecutive_failures"] >= breaker["failure_threshold"]:
            breaker["state"] = "open"
            breaker["open_until"] = time.now() + 60
        raise

The circuit breaker prevents the agent from hammering a failing downstream. The downstream gets room to recover.

Pattern 2: Load Shedding

When the system is overloaded, low-priority work is shed:

# Load shedding
load_shedding_policy = {
    "priority_levels": {
        "critical": {"sheddable": False, "examples": ["payment.processing", "auth.login"]},
        "high": {"sheddable": False, "examples": ["customer.support_response", "order.processing"]},
        "medium": {"sheddable": True, "examples": ["email.notification", "analytics.event"]},
        "low": {"sheddable": True, "examples": ["report.generation", "log.aggregation"]},
    },
    "shedding_strategy": {
        "healthy_load": {"shed": [], "process": "all"},
        "moderate_load": {"shed": ["low"], "process": ["critical", "high", "medium"]},
        "high_load": {"shed": ["low", "medium"], "process": ["critical", "high"]},
        "critical_load": {"shed": ["low", "medium", "high"], "process": ["critical"]},
    },
}

def should_process(work_item, system_load):
    priority = work_item.priority
    shed_list = load_shedding_policy["shedding_strategy"][system_load]["shed"]
    return priority not in shed_list

The load shedding protects critical work. The system stays functional under stress.

Pattern 3: Bulk Operations When Possible

The agent batches work to reduce downstream pressure:

# Bulk operations
bulk_patterns = {
    "postgres.bulk_insert": {
        "max_batch_size": 500,
        "trigger": "queue_depth >= 50 OR scheduled_flush",
        "benefit": "100x throughput vs single inserts",
    },
    "elasticsearch.bulk_index": {
        "max_batch_size": 1000,
        "trigger": "queue_depth >= 200 OR 5 second elapsed",
        "benefit": "100x throughput vs single indexes",
    },
    "email.bulk_send": {
        "max_batch_size": 100,
        "trigger": "queue_depth >= 20 OR 30 second elapsed",
        "benefit": "10x throughput vs single sends",
    },
    "s3.bulk_put": {
        "max_batch_size": 1000,
        "trigger": "queue_depth >= 500 OR 60 second elapsed",
        "benefit": "50x throughput vs single puts",
    },
}

The bulk operations reduce the per-item overhead. The downstream gets fewer, larger requests.

Pattern 4: Workload Smoothing

Bursts are smoothed to avoid rapid capacity swings:

# Workload smoothing
workload_smoothing = {
    "detect_burst": {
        "window_seconds": 60,
        "burst_threshold": "production_rate > 3x average_for_5_minutes",
        "action": "smooth_to_average_rate",
    },
    "smoothing_strategies": {
        "leaky_bucket": {
            "description": "Drip work at constant rate",
            "use_case": "API calls with strict rate limits",
        },
        "token_bucket": {
            "description": "Burst within quota, drip otherwise",
            "use_case": "Mixed workloads with quota",
        },
        "jitter": {
            "description": "Add random delay to spread load",
            "use_case": "Periodic batch jobs",
        },
    },
}

def smooth_workload(work_items, strategy="token_bucket"):
    if strategy == "leaky_bucket":
        return drip_at_constant_rate(work_items, rate=10)
    elif strategy == "token_bucket":
        return allow_burst_within_quota(work_items, quota=100, refill_rate=10)
    elif strategy == "jitter":
        return add_random_delay(work_items, max_delay_seconds=30)

The smoothing prevents bursts that overwhelm downstream. The work flows steadily.

Pattern 5: Backpressure Observability

Backpressure is observable:

# Backpressure observability
backpressure_metrics = {
    "downstream_utilization_pct": {
        "postgres.production_db": 78,
        "stripe.api": 92,
        "human_review.queue": 83,
        "elasticsearch.index": 90,
    },
    "queue_depths": {
        "postgres.write_queue": 245,
        "stripe.api_queue": 230,
        "human_review.queue": 1247,
        "elasticsearch.ingest_queue": 1200,
    },
    "circuit_breaker_states": {
        "stripe.api": "open",
        "postgres.production_db": "closed",
        "auth.service": "closed",
    },
    "throttled_agents": 4,
    "load_shedding_active": True,
    "avg_wait_time_seconds": 23,
    "max_wait_time_seconds": 180,
    "backpressure_events_per_hour": 47,
    "dashboard": "https://internal-dashboard/facio/backpressure",
}

The observability surfaces backpressure patterns. The team sees what's stressed.

The Backpressure Discipline Doesn't Do

Honest limitations:

  • It doesn't eliminate capacity limits. The downstream still has limits. The discipline works within the limits, not beyond them.
  • It requires downstream cooperation. The downstream must communicate state. Some systems don't.
  • It can be too aggressive. Sometimes the agent should push through. The discipline needs tuning.
  • It adds latency. Waiting for capacity adds latency. The discipline trades latency for stability.
  • It can drop work. Some load shedding drops work. The discipline accepts that some work is non-essential.

The Backpressure as Operational Practice

Backpressure discipline is operational practice:

Capacity monitoring. The team monitors downstream capacity. They see trends before they become problems.

Threshold tuning. The team tunes throttling thresholds. They balance throughput and stability.

Circuit breaker review. The team reviews circuit breaker events. They identify systematic downstream issues.

Load shedding review. The team reviews load shedding events. They ensure critical work isn't shed.

The practice is what makes the discipline sustainable. Without it, the discipline is too rigid or too loose. With it, the discipline is calibrated.

The Compound Effect of Backpressure Discipline

Backpressure discipline compounds:

  • Higher availability. The system doesn't crash under load. The availability is higher.
  • Better latency under stress. The system degrades gracefully. The latency is bounded.
  • Lower error rates. Cascading failures are prevented. The errors are contained.
  • Better human review quality. Humans aren't flooded. The review quality is higher.
  • Lower operational cost. The team isn't firefighting. The focus is on improvement.

The undisciplined approach has the opposite trajectory. Cascading failures, latency spikes, high error rates, review burnout, constant firefighting.

Bottom Line

AI agents produce work. The downstream has capacity. Without backpressure discipline, the agent overwhelms the downstream. With backpressure discipline, the agent flows at the pace the system can absorb.

Facio's backpressure discipline provides capacity sensing, adaptive throttling, queue management, flow control signals, and backpressure-aware retries. The discipline makes AI agents sustainable in production.

The agent without backpressure discipline is a liability that overwhelms the system. The agent with it is a managed flow. The team trusts the managed one. The downstream accepts the managed one.

Because AI agents in production produce work. The question is whether the work is absorbed or floods. The backpressure discipline is what makes the answer absorbed.


See the backpressure documentation for capacity sensing, throttling configuration, and circuit breaker tuning.

Keep reading

More on Product

View category
Jul 26, 2026Product

Facio's Rollback Discipline: How AI Agents Undo Their Own Mistakes Without Making Things Worse

AI agents take actions. Some actions are wrong: a customer is charged twice, an email goes to the wrong person, a deployment breaks production. The naive approach accepts the bad state and moves on. Facio's rollback discipline gives AI agents structured mechanisms to undo their own actions: reversible action design, automatic rollback triggers, compensating transactions for multi-step workflows, confirmation and verification, and learning loops that improve behavior. Bad state is reversed quickly; the system recovers cleanly.

Jul 25, 2026Product

Facio's Tool Surface Discipline: How AI Agents Get Exactly the Capabilities They Need Without Exposing the Entire System

AI agents gain power through tools. The naive approach exposes every tool to every agent. The risk: an agent that's supposed to handle customer support questions can also trigger a payment refund; an agent compromised via prompt injection has the keys to the kingdom. Facio's tool surface discipline gives every agent a controlled capability set: per-agent allowlisting, argument scoping, context-aware dynamic loading, complete invocation logging, and anomaly detection. The agent is powerful enough to do its work; constrained enough to not be a liability.

Jul 24, 2026Product

Facio's Memory Hierarchy Discipline: How AI Agents Decide What to Remember, What to Forget, and What to Surface at the Right Moment

AI agents accumulate state: conversation history, tool results, intermediate reasoning, user preferences, learned facts. The naive approach stores everything and hopes the model sorts it out. The disciplined approach treats memory as a hierarchy with explicit retention policies, decay functions, relevance scoring, and surfacing mechanisms. Facio's memory hierarchy discipline gives AI agents the structural framework to remember what matters, forget what doesn't, and surface the right information at the right moment without overwhelming the context window.