Class: ActionAgent::AgentScorecard
- Inherits:
-
Object
- Object
- ActionAgent::AgentScorecard
- Defined in:
- app/services/action_agent/agent_scorecard.rb
Overview
Per-agent scorecard stats for the dashboard's agent cards, computed with grouped queries (no per-agent N+1) over two sources:
- agent_runs — executions the platform itself ran, and
- active_agent_telemetry_traces — executions reported by an SDK in the customer's own app, attributed to an Agent by AgentRegistrar.
Agents discovered by observation (status: :observed) only ever have the second kind, so a runs-only scorecard reported 0 for every tile while the Traces view showed real traffic.
A platform run writes BOTH an AgentRun and a trace sharing a trace_id, so traces are counted only when no AgentRun claims the same trace_id — otherwise every platform execution would count twice.
Constant Summary collapse
- WINDOW =
30.days
Class Method Summary collapse
-
.for_agents(agents) ⇒ Hash{Integer => Hash}
Agent_id => stats.
Class Method Details
.for_agents(agents) ⇒ Hash{Integer => Hash}
Returns agent_id => stats.
23 24 25 26 27 28 29 30 31 32 33 34 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 |
# File 'app/services/action_agent/agent_scorecard.rb', line 23 def self.for_agents(agents) ids = agents.map(&:id) return {} if ids.empty? window_start = WINDOW.ago windowed = AgentRun.where(agent_id: ids, created_at: window_start..) run_counts = windowed.group(:agent_id).count completed_counts = windowed.where(status: :complete).group(:agent_id).count avg_durations = windowed.where.not(duration_ms: nil).group(:agent_id).average(:duration_ms) token_sums = windowed.group(:agent_id).sum("COALESCE(total_tokens, 0)") last_runs = AgentRun.where(agent_id: ids).group(:agent_id).maximum(:created_at) eval_runs = latest_evaluation_runs(ids) trace_stats = telemetry_stats(ids, window_start) trace_last = unclaimed_traces(ids, nil).group(:agent_id).maximum(:timestamp) costs = estimated_costs(ids, windowed, window_start) ids.index_with do |id| runs = run_counts[id].to_i stats = trace_stats[id] || {} traced = stats[:count].to_i total = runs + traced succeeded = completed_counts[id].to_i + stats[:ok].to_i eval_run = eval_runs[id] { window_days: (WINDOW / 1.day).to_i, runs: total, # Which sources contributed, so the UI can say where numbers came from. run_sources: run_sources(runs, traced), success_rate: total.positive? ? (succeeded * 100.0 / total).round(1) : nil, avg_duration_ms: blended_duration(avg_durations[id], runs, stats[:avg_duration], traced), tokens: token_sums[id].to_i + stats[:tokens].to_i, cost: costs[id], eval_score: eval_run&.average_score, eval_samples_passed: eval_run&.samples_passed, eval_samples_evaluated: eval_run&.samples_evaluated, last_run_at: [ last_runs[id], trace_last[id] ].compact.max&.iso8601 } end end |