docs: document the gateway agent cache memory budget
Record why the cache needs a third bound and what the pressure pass will and will not shed, so an operator tuning agent.agent_cache knows which knob to reach for. Adds the config keys to the session-lifecycle appendix and a user guide section covering the "auto" cgroup-derived budget.
This commit is contained in:
parent
6bbe55dd09
commit
2d0c2682c7
|
|
@ -557,10 +557,14 @@ The `_session_expiry_watcher` task runs in the gateway event loop every 300 seco
|
|||
session with its full history (#61220, #61993, #63539).
|
||||
|
||||
2. **Sweep idle cached agents** — Calls `_sweep_idle_cached_agents()` to evict agents that
|
||||
have been idle beyond `_AGENT_CACHE_IDLE_TTL_SECS` (3600s / 1h), regardless of session
|
||||
have been idle beyond the idle TTL (3600s / 1h by default), regardless of session
|
||||
reset policy. This prevents unbounded memory growth in gateways with long-lived sessions.
|
||||
|
||||
3. **Prune stale entries** — Calls `session_store.prune_old_entries()` hourly based on
|
||||
3. **Sweep under memory pressure** — Calls `_sweep_agent_cache_under_pressure()` to shed
|
||||
least-recently-used transcripts once the process's anonymous RSS is over budget. See
|
||||
§11.
|
||||
|
||||
4. **Prune stale entries** — Calls `session_store.prune_old_entries()` hourly based on
|
||||
`config.session_store_max_age_days`. Prevents `sessions.json` from growing unbounded.
|
||||
|
||||
### Failure Handling
|
||||
|
|
@ -578,11 +582,44 @@ preserve prompt caching across turns.
|
|||
|
||||
### Cache Properties
|
||||
|
||||
- **Max size:** 128 entries (`_AGENT_CACHE_MAX_SIZE`).
|
||||
- **Max size:** 128 entries (`agent.agent_cache.max_size`, default `_AGENT_CACHE_MAX_SIZE`).
|
||||
- **Eviction policy:** Least-recently-used (LRU via `OrderedDict`).
|
||||
- **Idle TTL:** 3600s (1h) — enforced by `_session_expiry_watcher`.
|
||||
- **Idle TTL:** 3600s (1h) — `agent.agent_cache.idle_ttl_secs`, enforced by
|
||||
`_session_expiry_watcher`.
|
||||
- **Memory budget:** `agent.agent_cache.memory_high_mb` (default `auto`) — see below.
|
||||
- **Lock:** `_agent_cache_lock` (threading) for thread safety.
|
||||
|
||||
### Memory-Pressure Eviction
|
||||
|
||||
A cached agent pins `_session_messages`, the full live transcript including tool
|
||||
outputs — tens of MB on a session with 100+ tool calls. The entry cap and the idle
|
||||
TTL are both blind to that: a gateway serving many chats keeps every warm transcript
|
||||
resident (agents that took a turn within the TTL are never idle-swept, and the idle
|
||||
sweep additionally defers finalizable sessions until they expire), so RSS climbs until
|
||||
the cgroup throttles and SIGTERM can no longer flush inside systemd's stop timeout
|
||||
(#80764).
|
||||
|
||||
`_sweep_agent_cache_under_pressure()` is the valve. Each watcher tick it compares the
|
||||
process's anonymous RSS against `memory_high_mb`; over budget, it evicts LRU agents
|
||||
through the same soft path the cap enforcer uses (`_commit_then_release_soft`), then
|
||||
runs `malloc_trim` so the freed arenas actually return to the OS. Evicted sessions
|
||||
rebuild their transcript from the persisted session on the next turn.
|
||||
|
||||
Three classes of session are never shed:
|
||||
|
||||
- agents currently mid-turn (their clients and sandboxes are in use);
|
||||
- the `protect_recent` most-recently-used sessions (their prompt cache is worth the
|
||||
most);
|
||||
- any session whose live transcript has not finished reaching disk —
|
||||
`transcript_persistence_caught_up()` compares `_last_flushed_db_idx` against
|
||||
`len(_session_messages)`, the same divergence the FTS write-corruption guard reacts
|
||||
to when it preserves live history over a lagging transcript.
|
||||
|
||||
`memory_high_mb: auto` derives the budget from the cgroup limit the gateway runs under
|
||||
(`memory.high`, then `memory.max`, then cgroup v1), falling back to total RAM when
|
||||
uncapped. Set a number to pin it, or `0`/`off` to disable the pass entirely. Helpers
|
||||
live in `gateway/agent_cache_pressure.py`.
|
||||
|
||||
### Cache Lifecycle
|
||||
|
||||
```
|
||||
|
|
@ -623,6 +660,11 @@ When a session expires:
|
|||
| `session_store_max_age_days` | `int` | `0` | Prune sessions older than N days (0=disabled) |
|
||||
| `agent.gateway_auto_continue_freshness` | `int` | `3600` | Seconds for resume freshness window |
|
||||
| `agent.gateway_timeout` | `int` | `1800` | Agent turn timeout (30 min default) |
|
||||
| `agent.agent_cache.max_size` | `int` | `128` | LRU entry cap on cached AIAgents |
|
||||
| `agent.agent_cache.idle_ttl_secs` | `int` | `3600` | Evict agents idle this long |
|
||||
| `agent.agent_cache.memory_high_mb` | `int`/`str` | `auto` | Anon-RSS budget above which LRU transcripts are shed |
|
||||
| `agent.agent_cache.max_evictions_per_pass` | `int` | `16` | Cap on sessions shed per pressure pass |
|
||||
| `agent.agent_cache.protect_recent` | `int` | `8` | MRU sessions the pressure pass never touches |
|
||||
|
||||
### Reset Policy (per-platform/type, in config.yaml)
|
||||
|
||||
|
|
|
|||
|
|
@ -920,6 +920,30 @@ agent:
|
|||
session_stall_timeout: 300 # seconds; 0 disables the watchdog
|
||||
```
|
||||
|
||||
## Gateway Agent Cache
|
||||
|
||||
The gateway keeps one agent per session so a conversation reuses its cached prompt prefix instead of rebuilding the system prompt every turn. That cached agent also holds the session's full transcript — tool output included, which is tens of megabytes on a session with a hundred tool calls. On a busy multi-platform gateway the cache is therefore the largest single consumer of memory in the process.
|
||||
|
||||
```yaml
|
||||
agent:
|
||||
agent_cache:
|
||||
max_size: 128 # LRU entry cap
|
||||
idle_ttl_secs: 3600 # evict an agent idle this long
|
||||
memory_high_mb: auto # anon-RSS budget; number, "auto", or 0/off
|
||||
max_evictions_per_pass: 16
|
||||
protect_recent: 8
|
||||
```
|
||||
|
||||
`max_size` and `idle_ttl_secs` bound the cache by count and by time. Neither knows how many bytes it holds, so `memory_high_mb` adds a third bound: once the gateway's own anonymous resident memory crosses the budget, it sheds least-recently-used transcripts, which reload from the stored session on the next turn. Lower it if the gateway is competing for memory with other services; raise it (or set `0` to switch the pass off) if you would rather keep every prefix warm.
|
||||
|
||||
`auto` derives the budget from the memory limit the gateway actually runs under — the cgroup limit for a container or systemd unit, total RAM otherwise — so a `MemoryMax`/`MemoryHigh` on the unit is respected without a second number to keep in sync.
|
||||
|
||||
Sessions that are mid-turn, the `protect_recent` most recently used ones, and any session whose transcript has not finished being written to disk are never shed. Eviction is logged at WARNING with the measured RSS and the sessions dropped:
|
||||
|
||||
```
|
||||
Agent cache pressure: anon RSS 6802MB over budget 6656MB — evicting 5 LRU session(s): ...
|
||||
```
|
||||
|
||||
## Context Engine
|
||||
|
||||
The context engine controls how conversations are managed when approaching the model's token limit. The built-in `compressor` engine uses lossy summarization (see [Context Compression](/developer-guide/context-compression-and-caching)). Plugin engines can replace it with alternative strategies.
|
||||
|
|
|
|||
Loading…
Reference in New Issue