Class: Insika::Context::Providers::SkillTrigger

Inherits:
Insika::ContextProvider show all
Defined in:
lib/insika/context/providers/skill_trigger.rb

Overview

Level-2 skill BODIES in the prompt, with two selection modes — both deterministic, neither asks the model:

profile.skills_eager  -> the agent's eager set (all, or a named list),
                       every turn. No decision at all, so no miss rate.
                       Costs the bodies' tokens.
`triggers:` in the    -> only the skills whose trigger matches this
frontmatter              message (whole word, accent- and case-insensitive).

Model-invoked loading (load_skill) stays the fallback for everything else.

A trigger only belongs on a skill that can COMPLETE the turn by itself. Injecting a reference table whose companion skill holds the procedure is worse than injecting nothing: the model has a plausible half-recipe in the prompt, so it never calls load_skill for the other half. Measured on a real pack — the line map arrived, the query-construction rules did not, and the searches came out malformed. Activation here is NOT a tool call, so nothing in the transcript would show it. The fragment's labels carry the name AND THE REASON, and the EXECUTOR announces them (:skill_activated) — not this provider. A provider only has the ContextRequest, which has no task, and the Studio's SSE drops an event whose meta lacks task_id when the subscriber is task-scoped (Subscription#matches?): emitting from here produced an event that was correct and never arrived.

Instance Method Summary collapse

Methods inherited from Insika::ContextProvider

#enabled_for?, #id, #required?

Constructor Details

#initialize(catalog:) ⇒ SkillTrigger

Returns a new instance of SkillTrigger.



30
31
32
# File 'lib/insika/context/providers/skill_trigger.rb', line 30

def initialize(catalog:)
  @catalog = catalog
end

Instance Method Details

#call(request) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/insika/context/providers/skill_trigger.rb', line 34

def call(request)
  matched = select(request)
  return [] if matched.empty?

  content = matched.map do |skill, _reason|
    %(<active_skill name="#{skill.name}">\n#{skill.body}\n</active_skill>)
  end.join("\n\n")

  labels = matched.map { |skill, reason| { "name" => skill.name, "reason" => reason } }
  [ContextFragment.build(content: content, placement: :system,
                         priority: Context::Priority::SKILL_BODY, source: id,
                         labels: labels)]
end