Class: Insika::Commands::BackfillKnowledge
- Inherits:
-
Object
- Object
- Insika::Commands::BackfillKnowledge
- Defined in:
- lib/insika/commands/backfill_knowledge.rb
Overview
The recovery path (extraction is best-effort — a crash between a turn's
terminal and the write loses that turn's concepts, not the conversation
nor a previously learned one). Replays one agent's stored sessions
through the SAME Knowledge::Extractor the per-turn hook uses.
Synchronous — the CLI's own path (insika knowledge:backfill), same
"no app boot" discipline as RunHarvest/RunDistillation: no task, no
turn, nothing written but the concepts themselves.
Constant Summary collapse
- DEFAULT_MIN_MESSAGES =
2
Instance Method Summary collapse
-
#call(command) ⇒ Object
payload: { agent:, since?: ISO8601 } -> { backfilled: true, sessions: N, concepts: N, conflicts: N, dropped: ... } | { backfilled: false, skipped: "disabled|no_model|no_sessions" }.
-
#initialize(profiles:, knowledge_store:, session_store:, task_store:, settings_store: nil, event_stream:, extractor_factory: nil, consolidator_factory: nil) ⇒ BackfillKnowledge
constructor
A new instance of BackfillKnowledge.
Constructor Details
#initialize(profiles:, knowledge_store:, session_store:, task_store:, settings_store: nil, event_stream:, extractor_factory: nil, consolidator_factory: nil) ⇒ BackfillKnowledge
Returns a new instance of BackfillKnowledge.
18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/insika/commands/backfill_knowledge.rb', line 18 def initialize(profiles:, knowledge_store:, session_store:, task_store:, settings_store: nil, event_stream:, extractor_factory: nil, consolidator_factory: nil) @profiles = ProfileSource.coerce(profiles) @knowledge_store = knowledge_store @session_store = session_store @task_store = task_store @settings_store = settings_store @extractor_factory = extractor_factory || ->(config) { Knowledge::ExtractorFactory.build(config, utility_model: utility_model) } @consolidator_factory = consolidator_factory || ->(config) { Knowledge::ConsolidatorFactory.build(config, utility_model: utility_model) } @event_stream = event_stream end |
Instance Method Details
#call(command) ⇒ Object
payload: { agent:, since?: ISO8601 } -> { backfilled: true, sessions: N, concepts: N, conflicts: N, dropped: ... } | { backfilled: false, skipped: "disabled|no_model|no_sessions" }
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/insika/commands/backfill_knowledge.rb', line 35 def call(command) p = AgentPayload.symbolize(command.payload) agent = AgentPayload.presence(p[:agent]) raise Insika::ValidationError, "agent is required" if agent.nil? profile = @profiles[agent] || (raise Insika::NotFoundError, "agent '#{agent}' not configured") config = Coercion.deep_stringify(profile.knowledge) return skip("disabled") if config.nil? || !Coercion.truthy?(config["extract"]) extractor = @extractor_factory.call(config) return skip("no_model") if extractor.nil? # Same resolved model as the extractor. nil (unresolvable) is still # meaningful: Knowledge.write_concept's conservative default. consolidator = @consolidator_factory.call(config) sessions = resolve_sessions(agent, p) return skip("no_sessions") if sessions.empty? concepts = 0 conflicts = 0 dropped = Hash.new(0) sessions.each do |s| result = extractor.extract(prompt: build_prompt(config, s[:messages])) result[:concepts].each do |c| case write_concept(profile, s[:id], c, consolidator)[:verdict] when :new, :related then concepts += 1 when :contradicting then conflicts += 1 end end result[:dropped].each { |k, v| dropped[k] += v } end emit(:knowledge_backfilled, agent: agent, sessions: sessions.size, concepts: concepts, conflicts: conflicts, dropped: dropped.to_h) { backfilled: true, sessions: sessions.size, concepts: concepts, conflicts: conflicts, dropped: dropped.to_h } end |