Class: Insika::Refinement::EvidenceCollector
- Inherits:
-
Object
- Object
- Insika::Refinement::EvidenceCollector
- Includes:
- Coercion
- Defined in:
- lib/insika/refinement/evidence_collector.rb
Overview
Reads a window of an agent's real traffic and emits RANKED FINDINGS — "here is what broke, how often, and in which conversations". No model runs here and nothing is written to the agent: this is the evidence half of the loop, and it is deliberately useful on its own.
It reads ONLY durable data the engine already records:
TaskStore — the turn, its Command (which carries the agent) and the
executions (a failed turn keeps its error)
SessionStore — the transcript (repetition, canned safe replies)
ToolTraceStore — per-session tool calls with ok/args/result (already masked
and clipped by the store itself)
Two signals of are NOT computed here, and that is a finding about
the engine rather than about an agent: guardrail decisions and edge-limit hits
are emitted as EVENTS and never persisted, so the only durable footprint they
leave is the canned safe reply in the transcript — which is exactly what the
safe_reply finding matches. Attributing one to a specific rule needs a write
path that does not exist yet.
The agent of a turn comes from the Task's Command payload, not from the Session: a Session does not stamp which agent produced it.
Defined Under Namespace
Constant Summary collapse
- DEFAULT_WINDOW =
distinct sessions, most recent first
200- DEFAULT_MAX_FINDINGS =
20- MAX_PROVENANCE =
session ids kept per finding
5- SNIPPET_CHARS =
160- REPETITION_JACCARD =
0.6- REPETITION_MIN_WORDS =
"oi"/"sim" repeated is not a defect
3- INJECTED_FRAGMENT_RE =
LEGACY FALLBACK, for transcripts written before messages carried an origin.
A message the engine wrote itself — an injected context fragment, a delegation result delivered as a new turn — is persisted with
role: userlike any other, because it is what the model saw. Counting those as the customer repeating themselves turned the first production run into 219 false positives, every one of them the engine reading its own<store_cep_required>back.MessageOriginis the structural answer and is preferred whenever a message carries it. This regex stays for everything written before that field existed (the pilot's database is full of it) and for consumers that have not started declaringorigin— it is a guess, and it only ever runs on messages that made no claim about themselves. /\A<[a-z][a-z0-9_:.-]*>/i- SEVERITY =
Weight of a finding kind when ranking (count × severity).
{ tool_error: 3, task_failed: 3, safe_reply: 2, repetition: 2, tool_unused: 1 }.freeze
Constants included from Coercion
Instance Method Summary collapse
-
#collect(agent_id:, last_sessions: DEFAULT_WINDOW, since: nil, max_findings: DEFAULT_MAX_FINDINGS, exclude_sessions: []) ⇒ Object
-> Report.
-
#initialize(task_store:, session_store:, tool_trace_store:, profiles:, settings_store: nil) ⇒ EvidenceCollector
constructor
A new instance of EvidenceCollector.
Methods included from Coercion
blank?, deep_stringify, presence, present?, truthy?, utf8
Constructor Details
#initialize(task_store:, session_store:, tool_trace_store:, profiles:, settings_store: nil) ⇒ EvidenceCollector
Returns a new instance of EvidenceCollector.
70 71 72 73 74 75 76 77 |
# File 'lib/insika/refinement/evidence_collector.rb', line 70 def initialize(task_store:, session_store:, tool_trace_store:, profiles:, settings_store: nil) @task_store = task_store @session_store = session_store @tool_trace_store = tool_trace_store @profiles = ProfileSource.coerce(profiles) @settings_store = settings_store end |
Instance Method Details
#collect(agent_id:, last_sessions: DEFAULT_WINDOW, since: nil, max_findings: DEFAULT_MAX_FINDINGS, exclude_sessions: []) ⇒ Object
-> Report. since (ISO8601) wins over last_sessions when both are given:
an incremental run ("what happened since the last one") is the common case.
exclude_sessions drops sessions whose id starts with any of the given
prefixes. It defaults to NOTHING — a report must not decide on its own what
counts as real traffic — but a deployment that replays load tests or debug
conversations into the same store needs it: on the pilot, loadtest- sessions
outnumbered real ones and drowned every genuine finding.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/insika/refinement/evidence_collector.rb', line 87 def collect(agent_id:, last_sessions: DEFAULT_WINDOW, since: nil, max_findings: DEFAULT_MAX_FINDINGS, exclude_sessions: []) agent = agent_id.to_s profile = @profiles[agent] || (raise Insika::NotFoundError, "agent '#{agent}' not configured") tasks, excluded = window_tasks(agent, last_sessions: last_sessions, since: since, exclude_sessions: Array(exclude_sessions)) session_ids = tasks.filter_map { |t| presence(t.session_id) }.uniq traces = session_ids.to_h { |sid| [sid, @tool_trace_store.for_session(sid)] } findings = [ *tool_error_findings(traces), *task_failed_findings(tasks), *repetition_findings(session_ids), *safe_reply_findings(session_ids, profile), # An EMPTY window says nothing about a tool being unused — it says the agent # did not run. Without this guard every incremental run over quiet traffic # would report the whole tool list as "never called". *(tasks.empty? ? [] : tool_unused_findings(profile, traces)) ] Report.new( agent_id: agent, window: since ? { "since" => since.to_s } : { "last_sessions" => last_sessions }, findings: rank(findings).first(max_findings), sessions_seen: session_ids.size, turns_seen: tasks.size, excluded: excluded ) end |