Class: Insika::Safety::Moderator
- Inherits:
-
Object
- Object
- Insika::Safety::Moderator
- Defined in:
- lib/insika/safety/moderator.rb
Overview
LLM content moderator (RFC-0009 Fase C / D2). The subtle tier the regex can't reach: social engineering (a fabricated prior promise), veiled hostility, tone.
Pure over an injected ask callable (prompt -> raw model text), exactly like
the eval Judge — so it is unit-testable with no LLM. The real ask (RubyLLM on
the utility_model, temp 0) is built by the Safety::Factory at wiring time.
FAIL-OPEN by construction: the deterministic layer already ran and caught the
gross cases, so an unparseable/failed moderator reply must NOT block a
legitimate customer — it degrades to allow. Blocking is the high-stakes
direction (RFC §6: a false positive turns away a real buyer).
Defined Under Namespace
Classes: Verdict
Constant Summary collapse
- CATEGORIES =
%w[injection abuse sexual self_harm off_topic safe].freeze
- ACTIONS =
%w[allow refuse escalate].freeze
Instance Method Summary collapse
-
#classify(message, context: nil) ⇒ Object
Classifies a user message.
-
#initialize(ask:) ⇒ Moderator
constructor
ask: ->(prompt) { "
" }.
Constructor Details
#initialize(ask:) ⇒ Moderator
ask: ->(prompt) { "
27 28 29 |
# File 'lib/insika/safety/moderator.rb', line 27 def initialize(ask:) @ask = ask end |
Instance Method Details
#classify(message, context: nil) ⇒ Object
Classifies a user message. Returns a Verdict; on ANY failure -> allow/safe
(fail-open). context is optional free text (e.g. the agent's domain) woven
into the prompt.
34 35 36 37 38 39 |
# File 'lib/insika/safety/moderator.rb', line 34 def classify(, context: nil) raw = @ask.call(build_prompt(.to_s, context)).to_s parse(raw) rescue StandardError Verdict.new(category: "safe", action: "allow", reason: "moderator error (fail-open)") end |