Most agent projects hit the same wall. A request arrives that the agent can’t handle, so someone writes a new tool. Repeat. The cost of keeping up is O(number of requests), and requests combine combinatorially — so you never catch up.
The fix isn’t a better framework. It’s seeing that autonomous composition isn’t one mechanism — it’s a stack of composition types plus two layers of glue. A real agent layers these and reaches for whichever the task needs, instead of betting one mechanism is the silver bullet.
Four kinds of composition
- Sequential —
Thought → Action → Observation, each result deciding the next step. This is the ReAct loop, the base layer. Good for a few steps where semantics drive the path; bad at bulk. - Batch / compute — N rows, filter, join, aggregate. Don’t make the model pick tools one per round — let it write code that calls the primitives, loop and all, and run once. Standard tool-calling can’t express a
forloop; code execution can. - Decomposition — hand a big chunk to a sub-agent with its own context, get back a summary. The value is context isolation and narrowed attention. The cost is orchestration and debugging — so don’t pay for complexity that isn’t there.
- Knowledge — make retrieval a tool the loop calls when it decides it needs to. The retrieval method is itself a sub-choice: plain vector RAG, graph RAG for global or multi-hop questions, tree navigation for long documents.
ReAct is the base. Everything else — code execution, sub-agents, retrieval — is a tool hanging off it. Without that “decide → call → observe → decide” loop driving them, they’re just inert functions lying around.
Two layers of glue
The stack only composes because of two things underneath it:
- A unified tool schema — local tools, MCP endpoints, sub-agents, and code execution all look like the same function-call schema to the model, so it can mix them freely in one turn. Adding a capability is registering one more schema; the loop logic doesn’t change.
- State and memory — multi-step composition needs results to carry across steps: a scratchpad within a task, a workspace within a session, durable memory across sessions.
The discipline is to let no single mechanism swallow everything. ReAct orchestrates, code execution computes, sub-agents decompose, retrieval fetches. Move the combinatorial explosion off your maintenance backlog and onto the model’s runtime — and your cost drops from O(requests) to O(primitives).