Dynamic agents, and the catalogue
Dynamic means the agent reconfigures itself per turn rather than being fixed at construction. There is no parameter for it, which is the point: wrap_model_call already holds the call, so it is the general mechanism, and every built-in that switches models or filters tools is built on it.
The shape
@wrap_model_call
def escalate_on_complexity(request, handler):
if looks_hard(request):
request = request.override(model=BIG_MODEL)
return handler(request)The hook receives the request, can rewrite the model, the prompt or the tool list, and hands it on. Because it wraps rather than observes, it can also inspect what came back and call the handler again, which is how fallbacks and retries work.
The sixteen built-ins
SummarizationMiddleware HumanInTheLoopMiddleware
ModelCallLimitMiddleware ToolCallLimitMiddleware
ModelFallbackMiddleware PIIMiddleware
TodoListMiddleware LLMToolSelectorMiddleware
ToolErrorMiddleware ToolRetryMiddleware
ModelRetryMiddleware LLMToolEmulator
ContextEditingMiddleware ProviderToolSearchMiddleware
ShellToolMiddleware FilesystemFileSearchMiddlewareGrouped by what they protect you from, sixteen stops being sixteen things:
- Context pressure:
SummarizationMiddleware,ContextEditingMiddleware. - Runaway cost:
ModelCallLimitMiddleware,ToolCallLimitMiddleware. - Flaky infrastructure:
ModelFallbackMiddleware,ModelRetryMiddleware,ToolRetryMiddleware,ToolErrorMiddleware. - Safety and oversight:
HumanInTheLoopMiddleware,PIIMiddleware. - Tool surface shaping:
LLMToolSelectorMiddleware,LLMToolEmulator,ProviderToolSearchMiddleware,ShellToolMiddleware,FilesystemFileSearchMiddleware. - Planning:
TodoListMiddleware.
Two are easy to misread from the name. LLMToolEmulator fakes a tool rather than calling it, which makes it a testing instrument for exercising an agent’s reasoning before a tool exists. ProviderToolSearchMiddleware deals with tools the model provider hosts, not ones you registered.
Defaults worth having exactly
A few more that are hard to search for under pressure. Both retry middlewares default to max_retries=2, backoff_factor=2.0, initial_delay=1.0, max_delay=60.0, jitter on, and on_failure="continue". PIIMiddleware defaults to strategy="redact" with apply_to_input=True but apply_to_output and apply_to_tool_results both off, which is a narrower default guard than most people assume.
The TypeScript gap, stated once
ShellToolMiddleware and FilesystemFileSearchMiddleware are genuinely absent from TypeScript. Everything else in the list exists in both, spelled camelCase.
That plus the authoring difference from lesson 1 is the whole gap: PascalCase classes with decorators in Python, camelCase factories in TypeScript, two missing entries. The hooks, the ordering rule, the jump targets and every threshold are identical.
Try it yourself
Two limits, two different defaults
ModelCallLimitMiddleware and ToolCallLimitMiddleware look like siblings and their exit_behavior defaults differ.
Show answer
Correct answer: C — ModelCallLimit defaults to end; ToolCallLimit defaults to continue
ModelCallLimitMiddleware defaults to end and ToolCallLimitMiddleware defaults to continue. The asymmetry is deliberate rather than an oversight: hitting a model-call ceiling means the agent cannot think any further, so ending is the only honest outcome, whereas a tool being exhausted still leaves the agent able to reason and answer without it. Assuming parity is the natural error and it is why this makes a good question.
Choosing from the catalogue
An agent is looping, calling the same tool repeatedly, and burning budget. You want a hard stop rather than a nudge.
Show answer
Correct answer: A — ToolCallLimitMiddleware, because it caps the number of tool calls outright
ToolCallLimitMiddleware is the hard stop. LLMToolSelectorMiddleware is the tempting one because narrowing the tool list feels like it addresses the cause, but it shapes what the model chooses rather than bounding how many times it may choose, so a determined loop still runs forever. When the requirement says hard stop, the answer is a limit middleware.
How an agent becomes dynamic
Dynamic behaviour is not a create_agent parameter, which is the structural point of this lesson.
How do you change the model, the system prompt, or the available tools at runtime rather than at construction?
Reveal answer
Through a wrap_model_call middleware. It receives the request on its way to the model, so it can rewrite the model, the prompt or the tool list per turn based on state or context, then hand the modified request to the handler. This is why there is no dynamic_model or dynamic_prompt parameter on create_agent: the wrap hook already holds the call and is the general mechanism, and the built-in middlewares that switch models or filter tools are all built on exactly this hook.
What PIIMiddleware guards by default
PIIMiddleware is added with only a pii_type and no other arguments. Which direction is protected?
Show answer
Correct answer: D — Input only; output and tool results are off by default
apply_to_input defaults to True while apply_to_output and apply_to_tool_results both default to False, and the default strategy is redact. The third option is the intuitive one, because leaking is something you picture happening on the way out; the default guards what you send to the provider, which is a different threat model, and tool results being unguarded is the gap most likely to surprise you.
Absent from TypeScript
Three of these ship in both languages. One is genuinely missing from the TypeScript package.
Show answer
Correct answer: D — ShellToolMiddleware
ShellToolMiddleware is genuinely absent from TypeScript, alongside FilesystemFileSearchMiddleware. The other three are core and exist in both. The trap is that shell execution feels like the most universal capability imaginable, so its absence reads as impossible, which is precisely why it is worth knowing rather than guessing.
Sort the catalogue by job
In a scratch file, group the sixteen built-ins under four headings - context, cost, reliability, and tool surface - without looking back at the list.
You placed at least twelve of the sixteen, and you can say in one sentence what each group is protecting you from.