Prompt Middleware Deep Dive¶
Overview¶
Mozaiks uses middleware.yaml for AG2 1.0 prompt injection. Runtime execution is AG2 1.0 middleware, not prior hook registration style.
Canonical declarations live in:
workflows/{workflow}/middleware.yaml
Builder workflows use the same contract under factory_app/workflows/{workflow}/middleware.yaml.
Workflow middleware JSON files are not part of the runtime contract.
Supported Entries¶
middleware.yaml supports prompt middleware declarations only.
Declarative Contract¶
Fields are required per entry:
agentfilenamefunction
Runtime Registration Model¶
load_prompt_middleware_entries()reads and validatesmiddleware.yaml.create_agents()resolves prompt middleware functions before beta agent construction.build_prompt_middleware()registers aMozaiksPromptMiddlewarefactory on the betaAgent.- The middleware runs during
on_llm_calland mutatesContext.promptfor the current turn. - If middleware calls
agent.update_system_message(...), that message becomes the prompt for the current model call.
Call Shape Contract¶
The runner calls every prompt middleware function positionally, as fn(agent, messages):
agentis a capture object exposingname,context_variables(the liveContextVariablesBridge, whose reads are frozen),system_message, andupdate_system_message(message).messagesis the conversation history.- The return value is ignored. The only effect that reaches the model is a call to
agent.update_system_message(...). - Exceptions are swallowed and logged at DEBUG.
So a hook must name its first two parameters agent and messages. A hook written as (agent_name, context_variables) binds without error, receives the capture and the history in the wrong slots, and never injects anything; that is how the brownfield hook stayed inert on six registrations (#723). tests/test_registered_hooks_match_their_runners.py pins this contract for every entry in every middleware.yaml and calls each hook once the way the runner does. A hook should also log whether it acted (see #720), because a registration that fails silently cannot be verified from a live run.
Execution Timing¶
Prompt middleware runs before the beta model call for an agent turn. It is for context injection, prompt guards, and deterministic runtime guidance. Message transforms, output validation, persistence, and side effects belong in structured outputs, lifecycle tools, runtime validators, or ordinary tools.
Troubleshooting¶
If middleware does not fire:
- Verify the
middleware.yamlentry validates. - Confirm
agentexactly matches the runtime agent name. - Confirm
filenameandfunctionresolve to an importable callable. - Confirm the middleware function calls
agent.update_system_message(...)when it needs to change the current-turn prompt.