Skip to content
Agents: The Core LoopAgent state and concurrency

Senior · 6 chapters

Agent state and concurrency

Parallel tool calls, race conditions, shared-resource conflicts, per-agent workspace isolation

Concurrency bugs silently corrupt state, so agent systems need explicit isolation and synchronization rather than trusting the model or tool provider.

Read these first

What you will know

  • Run tool calls in parallel without corrupting shared state

    Stress the workflow with repeated overlapping calls and verify invariants remain intact after retries, timeouts, and partial failures.

  • Reproduce a race between two agents on one resource

    A controlled lost-update or stale-read failure makes the timing window observable instead of leaving the defect dependent on chance.

  • Isolate each agent's workspace

    Separate directories, namespaces, or records must also cover temporary files and cleanup paths, not just the primary output.

  • Explain why parallel tool calls are not automatically safe

    Provider concurrency says nothing about whether the underlying API, filesystem, database transaction, or external side effect is atomic.

  • Serialise the operations that must not interleave

    Locks, queues, or critical sections can prevent corruption, but introduce deadlocks, contention, and throughput costs that require measurement.


Chapters

  1. 1

    Concurrent dispatch is a protocol feature, not a safety property

    Your provider can emit several tool calls in one assistant turn and your framework can run them at once; between them they have promised only to invoke your functions. Whether the database transaction, the filesystem write, the third-party API or the outbound side effect tolerates two of those at the same instant is a question about your systems, which nobody in the stack has asked.

  2. 2

    'Create folder Q3' and 'upload into Q3' arrive in one turn

    The model had no way to say that the second call needs the first to have finished, because nothing in a tool schema expresses ordering, and the dispatcher happily starts both. Sometimes the upload lands first and fails; sometimes it creates a second folder. This chapter covers encoding dependencies where they can be enforced: combined operations, explicit inputs that only exist after the prior call, or refusing the parallel emission.

  3. 3

    A race becomes a test when you hold one run between read and write

    One in three hundred in production and never locally is not a timing problem, it is a control problem: a loop with more threads can pass all afternoon and prove nothing. Insert a barrier that pauses one run after its read, let the other complete its write, release the first, then assert on the record rather than on the absence of an exception. Now the lost update is a failing test with a name.

  4. 4

    Serialising buys correctness and pays in contention and deadlocks

    Once the race is reproducible, the fix is to stop the interleaving: a row lock, a single-writer queue, a critical section, or an atomic compare-and-set that fails loudly. Each one converts a correctness bug into a throughput and liveness problem, and agents hold their locks across model calls, which are slow and can time out. Measure wait time and lock hold time before deciding how much you serialised.

  5. 5

    Per-run directories still share the cache, the temp dir and the config

    Ten runs installing packages and editing files each get their own working directory, and then collide in a shared package cache, a global config file, a fixed temp path, and a cleanup routine that deletes by pattern rather than by owner. This chapter isolates the defaults nobody set: environment variables, home directory, lock files and ports, and treats teardown as part of the boundary rather than an afterthought.

  6. 6

    A concurrency test asserts invariants after retries and timeouts

    'It didn't crash' is not a result. The test that means something runs overlapping tool calls repeatedly against the same resources, injects retries, timeouts and partial failures in the middle, and then checks domain invariants: no duplicate record, balances that still sum, no orphaned file, no work silently dropped. Run it in CI with the barriers from earlier chapters so failures are reproducible rather than seasonal.

External resources