Class: ActiveHarness::Agent
- Inherits:
-
Object
- Object
- ActiveHarness::Agent
- Defined in:
- lib/active_harness/agent.rb
Overview
DSL base class for agents and guard agents — one class, two roles.
Main agent (class-method style — one-liner):
SupportAgent.call(input: "hi", context: {})
Main agent (instance style — store params, call later):
agent = SupportAgent.new(input: "hi", language: :ru, context: {})
result = agent.call
result = agent.call(constraints: { max_input_length: 200 }) # merge extra params
Guard agent (same class, called positionally by the engine guard chain):
class InjectionGuard < ActiveHarness::Agent
model { use provider: :openai, model: "gpt-4.1-mini" }
system_prompt MyGuardPrompt
system_language :en
end
Class Attribute Summary collapse
-
.last_run_prompt ⇒ Object
readonly
Debug info from the most recent guard-mode .call (class-level).
-
.last_run_response ⇒ Object
readonly
Debug info from the most recent guard-mode .call (class-level).
Class Method Summary collapse
- .after(hook = nil, guard_name = nil, &block) ⇒ Object
-
.agent_config ⇒ Object
Each subclass gets its own isolated config hash.
-
.before(hook = nil, guard_name = nil, &block) ⇒ Object
Callbacks ———————————————————— All callbacks receive TWO arguments — (payload, current_value) — and must return the new current_value.
-
.call(*args, input: nil, context: {}, constraints: {}, language: nil, translate: nil, options: {}) ⇒ Object
Entry point.
-
.constraint(name, value) ⇒ Object
Declares a default constraint for this agent.
-
.default_error_answer(text_or_callable) ⇒ Object
Accepts a String or a callable (proc/lambda).
-
.guard(klass, name: nil, **options) ⇒ Object
Registers a guard class for the safety chain (main agent only).
-
.guard_retries(n) ⇒ Object
How many times to retry when a guard returns invalid/unparseable JSON.
- .model(&block) ⇒ Object
- .output(type, schema: nil) ⇒ Object
- .param(name, required: false) ⇒ Object
- .risk_tolerance(level) ⇒ Object
-
.setup(&block) ⇒ Object
Initializer hook — runs once before the pipeline starts.
-
.system_language(lang) ⇒ Object
DSL ——————————————————————-.
- .system_prompt(text) ⇒ Object (also: prompt)
Instance Method Summary collapse
-
#call(**overrides) ⇒ Object
Execute the agent.
-
#initialize(input: nil, context: {}, constraints: {}, language: nil, translate: nil, options: {}) ⇒ Agent
constructor
Build an agent instance with preset call parameters.
Constructor Details
#initialize(input: nil, context: {}, constraints: {}, language: nil, translate: nil, options: {}) ⇒ Agent
Build an agent instance with preset call parameters. Any keyword accepted by .call is valid here.
agent = SupportAgent.new(input: "hello", language: :ru)
result = agent.call # use stored params
result = agent.call(constraints: { max_input_length: 200 }) # merge overrides
213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/active_harness/agent.rb', line 213 def initialize(input: nil, context: {}, constraints: {}, language: nil, translate: nil, options: {}) @stored_params = { input: input, context: context, constraints: constraints, language: language, translate: translate, options: } end |
Class Attribute Details
.last_run_prompt ⇒ Object (readonly)
Debug info from the most recent guard-mode .call (class-level).
169 170 171 |
# File 'lib/active_harness/agent.rb', line 169 def last_run_prompt @last_run_prompt end |
.last_run_response ⇒ Object (readonly)
Debug info from the most recent guard-mode .call (class-level).
169 170 171 |
# File 'lib/active_harness/agent.rb', line 169 def last_run_response @last_run_response end |
Class Method Details
.after(hook = nil, guard_name = nil, &block) ⇒ Object
156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/active_harness/agent.rb', line 156 def after(hook = nil, guard_name = nil, &block) if hook key = (hook == :guard && guard_name) ? :"after_guard_#{guard_name}" : :"after_#{hook}" agent_config[:callbacks] ||= {} agent_config[:callbacks][key] ||= [] agent_config[:callbacks][key] << block else agent_config[:guard_after_callbacks] ||= [] agent_config[:guard_after_callbacks] << block end end |
.agent_config ⇒ Object
Each subclass gets its own isolated config hash.
172 173 174 |
# File 'lib/active_harness/agent.rb', line 172 def agent_config @agent_config ||= {} end |
.before(hook = nil, guard_name = nil, &block) ⇒ Object
Callbacks ———————————————————— All callbacks receive TWO arguments — (payload, current_value) — and must return the new current_value. The payload is read-only context; current_value is what gets threaded through the pipeline stage.
Main-agent pipeline hooks:
before(:guards) { |payload, input| input.strip } # String → String
after(:guards) { |payload, result| result } # InputResult → InputResult
before(:guard, :injection_guard) { |payload, input| input.downcase }
after(:guard, :injection_guard) { |payload, result| result }
before(:request) { |payload, prompt| prompt } # Hash → Hash
after(:request) { |payload, response| response } # ModelResponse → ModelResponse
Guard-mode hooks (when this class acts as a guard):
before { |payload, input| input.strip } # String → String
after { |payload, result| result } # InputResult → InputResult
144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/active_harness/agent.rb', line 144 def before(hook = nil, guard_name = nil, &block) if hook key = (hook == :guard && guard_name) ? :"before_guard_#{guard_name}" : :"before_#{hook}" agent_config[:callbacks] ||= {} agent_config[:callbacks][key] ||= [] agent_config[:callbacks][key] << block else agent_config[:guard_before_callbacks] ||= [] agent_config[:guard_before_callbacks] << block end end |
.call(*args, input: nil, context: {}, constraints: {}, language: nil, translate: nil, options: {}) ⇒ Object
Entry point.
Main mode: MyAgent.call(input: “…”, context: {}, language: :en, translate: fn) Guard mode: MyGuard.call(payload) — called by the engine guard chain
MyGuard.call("raw string") — manual / test use
MyGuard.call(prev_input_result) — manual / test use
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/active_harness/agent.rb', line 26 def call(*args, input: nil, context: {}, constraints: {}, language: nil, translate: nil, options: {}) if args.any? first = args.first payload = first.is_a?(Payload) ? first : Payload.new( input: first.is_a?(InputResult) ? first.processed : first.to_s, context: context, language: language, translate: translate, options: ) call_as_guard(payload) else Engine.new(agent_config).call( input: input, context: context, constraints: constraints, language: language, translate: translate ) end end |
.constraint(name, value) ⇒ Object
Declares a default constraint for this agent. Call-time constraints (passed to .call) override agent-level defaults.
Supported constraints:
constraint :max_input_length, 500 # reject inputs longer than 500 chars
97 98 99 100 |
# File 'lib/active_harness/agent.rb', line 97 def constraint(name, value) agent_config[:constraints] ||= {} agent_config[:constraints][name] = value end |
.default_error_answer(text_or_callable) ⇒ Object
Accepts a String or a callable (proc/lambda). When a callable is given, it is called with the Payload at block time:
default_error_answer ->(payload) { payload.translate&.call("my.key") || "Fallback text" }
111 112 113 |
# File 'lib/active_harness/agent.rb', line 111 def default_error_answer(text_or_callable) agent_config[:default_error_answer] = text_or_callable end |
.guard(klass, name: nil, **options) ⇒ Object
Registers a guard class for the safety chain (main agent only). Optional per-registration options are forwarded to the guard at call time. If name: is not given, defaults to the class name as a symbol (e.g., :InjectionGuard).
Examples:
guard InjectionGuard
guard InjectionGuard, name: :injection_guard
guard TopicGuard, name: :topic, allowed_topics: [:ruby, :programming]
59 60 61 62 63 |
# File 'lib/active_harness/agent.rb', line 59 def guard(klass, name: nil, **) guard_name = (name || klass.name).to_sym agent_config[:guards] ||= [] agent_config[:guards] << { klass: klass, options: , name: guard_name } end |
.guard_retries(n) ⇒ Object
How many times to retry when a guard returns invalid/unparseable JSON. Overrides ActiveHarness.config.guard_retries for this specific guard.
104 105 106 |
# File 'lib/active_harness/agent.rb', line 104 def guard_retries(n) agent_config[:guard_retries] = n end |
.model(&block) ⇒ Object
65 66 67 68 69 |
# File 'lib/active_harness/agent.rb', line 65 def model(&block) config = ModelConfig.new config.instance_eval(&block) agent_config[:model] = config.to_h end |
.output(type, schema: nil) ⇒ Object
83 84 85 86 |
# File 'lib/active_harness/agent.rb', line 83 def output(type, schema: nil) agent_config[:output_type] = type agent_config[:output_schema] = schema end |
.param(name, required: false) ⇒ Object
71 72 73 74 75 76 |
# File 'lib/active_harness/agent.rb', line 71 def param(name, required: false) agent_config[:params] ||= [] agent_config[:required_params] ||= [] agent_config[:params] << { name: name, required: required } agent_config[:required_params] << name if required end |
.risk_tolerance(level) ⇒ Object
88 89 90 |
# File 'lib/active_harness/agent.rb', line 88 def risk_tolerance(level) agent_config[:risk_tolerance] = level end |
.setup(&block) ⇒ Object
Initializer hook — runs once before the pipeline starts. Receives the Payload and must return it (optionally modified).
Example:
setup do |payload|
payload.[:started_at] = Time.now
payload.context[:locale] = determine_locale(payload.language)
payload
end
124 125 126 |
# File 'lib/active_harness/agent.rb', line 124 def setup(&block) agent_config[:setup] = block end |
.system_language(lang) ⇒ Object
DSL ——————————————————————-
47 48 49 |
# File 'lib/active_harness/agent.rb', line 47 def system_language(lang) agent_config[:system_language] = lang end |
.system_prompt(text) ⇒ Object Also known as: prompt
78 79 80 |
# File 'lib/active_harness/agent.rb', line 78 def system_prompt(text) agent_config[:system_prompt] = text end |
Instance Method Details
#call(**overrides) ⇒ Object
Execute the agent. overrides is merged into the stored params —any key present in overrides wins.
227 228 229 230 231 232 233 234 |
# File 'lib/active_harness/agent.rb', line 227 def call(**overrides) params = @stored_params.merge(overrides) do |_key, stored, override| # For Hash values (context, constraints) do a shallow merge so callers # can add keys without replacing the whole hash. (stored.is_a?(Hash) && override.is_a?(Hash)) ? stored.merge(override) : override end self.class.call(**params) end |