Callbacks
ActiveAgent provides callbacks for four different lifecycles:
- Generation callbacks (
before_generation,after_generation,around_generation) - Wrap both prompting and embedding operations for rate limiting, authentication, and logging - Prompting callbacks (
before_prompt,after_prompt,around_prompt) - Specific to prompt execution - Embedding callbacks (
before_embed,after_embed,around_embed) - Specific to embedding operations - Streaming callbacks (
on_stream_open,on_stream,on_stream_close) - Handle real-time streaming responses as they arrive
Use generation callbacks for cross-cutting concerns, prompting/embedding callbacks for operation-specific behavior, and streaming callbacks for processing responses in real-time.
Generation Callbacks
Generation callbacks wrap both prompting and embedding operations for rate limiting, authentication, and resource management.
Before Generation
Runs before any generation executes. Use for setup, rate limiting, or validation:
class MyAgent < ApplicationAgent
before_generation :load_context
def chat
prompt(message: params[:message])
end
private
def load_context
@user_data = User.find(params[:user_id])
end
endAfter Generation
Runs after any generation completes. Use for logging, usage tracking, or cleanup:
class LoggingAgent < ApplicationAgent
after_generation :log_completion
def chat
prompt(message: params[:message])
end
private
def log_completion
Rails.logger.info "Completed generation"
end
endAfter callbacks are skipped if the callback chain is terminated with throw :abort.
Around Generation
Wraps the entire generation process. Use for timing, transactions, or resource management:
class TimingAgent < ApplicationAgent
around_generation :measure_time
def chat
prompt(message: params[:message])
end
private
def measure_time
start = Time.current
yield
duration = Time.current - start
Rails.logger.info "Generation took #{duration}s"
end
endRate Limiting Example
class RateLimitedAgent < ApplicationAgent
before_generation :check_rate_limit
after_generation :record_usage
def chat
prompt(message: params[:message])
end
private
def check_rate_limit
if RateLimiter.exceeded?(params[:user_id])
throw :abort
end
end
def record_usage
RateLimiter.increment(params[:user_id])
end
endPrompting Callbacks
Prompting callbacks are specific to prompt execution:
class MyAgent < ApplicationAgent
before_prompt :load_context
after_prompt :cache_response
around_prompt :measure_time
def chat
prompt(message: params[:message])
end
private
def load_context
@user_data = User.find(params[:user_id])
end
def cache_response
Rails.cache.write("response_#{params[:id]}", "cached")
end
def measure_time
start = Time.current
yield
Rails.logger.info "Prompt took #{Time.current - start}s"
end
endEmbedding Callbacks
Embedding callbacks are specific to embedding operations:
class MyAgent < ApplicationAgent
before_embed :validate_input
after_embed :store_embedding
around_embed :measure_time
def process_text
embed(input: params[:text])
end
private
def validate_input
raise "Input too long" if params[:text].length > 8000
end
def store_embedding
VectorDatabase.store(params[:text])
end
def measure_time
start = Time.current
yield
Rails.logger.info "Embedding took #{Time.current - start}s"
end
endStreaming Callbacks
Streaming callbacks handle real-time streaming responses as they arrive:
class StreamingAgent < ApplicationAgent
on_stream_open :initialize_stream
on_stream :process_chunk
on_stream_close :finalize_stream
def chat
prompt(message: params[:message], stream: true)
end
private
def initialize_stream
@start_time = Time.current
@chunk_count = 0
end
def process_chunk(chunk)
@chunk_count += 1
# Process each chunk as it arrives
broadcast_chunk(chunk)
end
def finalize_stream
duration = Time.current - @start_time
Rails.logger.info "Streamed #{@chunk_count} chunks in #{duration}s"
end
def broadcast_chunk(chunk)
# Broadcast implementation
end
endUse on_stream_open for initialization, on_stream to process each chunk, and on_stream_close for cleanup. See Streaming for complete documentation.
Multiple and Conditional Callbacks
Register multiple callbacks and apply them conditionally with :if and :unless:
class AdvancedAgent < ApplicationAgent
before_generation :load_context
before_generation :check_rate_limit, if: :rate_limiting_enabled?
after_generation :log_response
def chat
prompt(message: params[:message])
end
private
def load_context
@user_data = User.find(params[:user_id])
end
def check_rate_limit
raise "Rate limit exceeded" if rate_limited?
end
def log_response
Rails.logger.info "Completed generation"
end
def rate_limiting_enabled?
Rails.env.production?
end
def test_environment?
Rails.env.test?
end
def rate_limited?
false
end
endCallbacks execute in registration order (before/around) or reverse order (after).
Callback Control
Use prepend_*, skip_*, and append_* variants for all callback types:
class BaseAgent < ApplicationAgent
before_generation :base_setup
after_prompt :log_response
def chat
prompt(message: params[:message])
end
private
def base_setup
# Base setup logic
end
def log_response
# Log response
end
end
class ChildAgent < BaseAgent
prepend_before_generation :critical_auth # Runs before inherited callbacks
skip_after_prompt :log_response # Remove inherited callback
append_before_prompt :final_setup # Same as before_prompt
private
def critical_auth
# Critical authentication logic
end
def final_setup
# Final setup logic
end
endRelated Documentation
- Agents - Understanding the agent lifecycle
- Generation - Execution patterns and response objects
- Instructions - System prompts that guide behavior
- Actions - Define agent capabilities
- Messages - Work with conversation context
- Error Handling - Handle failures in callbacks
- Testing - Test callback functionality
- Instrumentation - Monitor callback execution