Skip to main content
LLM Call Hooks provide fine-grained control over language model interactions during agent execution. These hooks allow you to intercept LLM calls, modify prompts, transform responses, implement approval gates, and add custom logging or monitoring.

Overview

LLM hooks are executed at two interception points: Write them with the @on decorator. The legacy @before_llm_call / @after_llm_call decorators keep working unchanged — both styles register on the same engine and run in one ordered chain.

Hook Signature

Unlike the boundary and step points, the model-call points pass the rich LLMCallHookContext directly as the hook argument (there is no separate ctx.payload): mutate ctx.messages in place before the call, and return a string to replace the response after it. Blocking a call raises ValueError("LLM call blocked by before_llm_call hook") inside the executor; the HookAborted reason and source are recorded in telemetry.

LLM Hook Context

The LLMCallHookContext object provides comprehensive access to execution state:
The context also exposes request_human_input(prompt, default_message), which pauses live console updates and collects input from the terminal — useful for approval gates.

Modifying Messages

Important: Always modify messages in-place:

Registration Methods

1. Global Hooks

Apply to all LLM calls across all crews. Use the agents= filter to scope a hook to specific agent roles:

2. Crew-Scoped Hooks

Apply the same decorator to a method inside a @CrewBase class to scope the hook to that crew only:

Common Use Cases

1. Iteration Limiting

2. Human Approval Gate

3. Adding System Context

4. Response Sanitization

5. Debug Logging

Hook Management

The legacy management API (register_before_llm_call_hook, unregister_before_llm_call_hook, clear_before_llm_call_hooks, clear_all_llm_call_hooks, get_before_llm_call_hooks, and their after_ counterparts) operates on the same underlying registries, so either API can manage hooks registered by the other.

Legacy Decorators

The original per-point decorators keep working unchanged and run in the same registration-order chain as @on hooks:
Differences from @on:
  • Blocking is return False from a before hook — equivalent to raising HookAborted, but without a custom reason or source for telemetry.
  • Signatures are point-specific: before hooks return bool | None, after hooks return str | None. The context object is the same LLMCallHookContext.
  • Filters and crew-scoping work the same way: @before_llm_call(agents=[...]), and applying the decorator to a @CrewBase method scopes it to that crew.
Prefer @on for new code; keep the legacy style where it is already in use — there is no behavioral penalty.

Best Practices

  1. Keep hooks focused and fast — they run on every LLM call
  2. Modify in-place — always mutate ctx.messages, never replace the list
  3. Use type hints — annotate with LLMCallHookContext for IDE support
  4. Abort loudly — raise HookAborted with a meaningful reason and source; any other exception is swallowed (fail-open)
  5. Clear hooks in tests — call clear_all_hooks() between test runs

Troubleshooting

Hook Not Executing

  • Verify the hook is registered before crew execution
  • Check whether an earlier hook aborted (subsequent hooks don’t run)

Message Modifications Not Persisting

  • Use in-place modifications: ctx.messages.append(...)
  • Don’t replace the list: ctx.messages = []

Response Modifications Not Working

  • Return the modified string from a POST_MODEL_CALL hook
  • Returning None keeps the original response