mirror of
https://github.com/BaaRai/ARTEX.git
synced 2026-11-13 11:40:53 +08:00
GET /api/skill-gaps aggregates skill_usage rows with found=false by skill, agent, and task category. Hits are returned for the same key so a miss rate needs no second query; hit-only skills are excluded. The source rows already existed: a miss is written when the model names a skill the platform does not provide. This is the evidence-backed capability backlog, not an auto-fixer: it creates no skill and changes no prompt. Motivated by the harness and skill-evolution sections of awesome-rsi (lobehub).
52 lines
1.6 KiB
Go
52 lines
1.6 KiB
Go
package db
|
|
|
|
import "context"
|
|
|
|
// SkillGap is one skill the agent asked for but the platform did not provide,
|
|
// aggregated by skill, agent, and task category. A miss row is written when the
|
|
// model names a skill that does not exist (skill_usage.found=false), so this
|
|
// report is the evidence-backed capability backlog rather than a guess.
|
|
type SkillGap struct {
|
|
Skill string `json:"skill"`
|
|
AgentKey string `json:"agent_key"`
|
|
CategoryID int64 `json:"category_id"`
|
|
Misses int `json:"misses"`
|
|
Hits int `json:"hits"`
|
|
LastSeen int64 `json:"last_seen"`
|
|
}
|
|
|
|
// SkillGaps returns the aggregated miss report, highest miss count first.
|
|
// Hits are the successful calls for the same key, so callers can compute a
|
|
// miss rate without a second query.
|
|
func (d *DB) SkillGaps(ctx context.Context, limit int) ([]SkillGap, error) {
|
|
if limit <= 0 {
|
|
limit = 50
|
|
}
|
|
rows, err := d.QueryContext(ctx, `
|
|
SELECT su.skill,
|
|
COALESCE(su.agent_key,''),
|
|
COALESCE(t.category_id,0),
|
|
COUNT(*) FILTER (WHERE NOT su.found) AS misses,
|
|
COUNT(*) FILTER (WHERE su.found) AS hits,
|
|
EXTRACT(EPOCH FROM MAX(su.ts))::bigint
|
|
FROM skill_usage su
|
|
LEFT JOIN tasks t ON t.id=su.task_id
|
|
GROUP BY 1,2,3
|
|
HAVING COUNT(*) FILTER (WHERE NOT su.found) > 0
|
|
ORDER BY misses DESC, 6 DESC, 1
|
|
LIMIT $1`, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
out := []SkillGap{}
|
|
for rows.Next() {
|
|
var g SkillGap
|
|
if err := rows.Scan(&g.Skill, &g.AgentKey, &g.CategoryID, &g.Misses, &g.Hits, &g.LastSeen); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, g)
|
|
}
|
|
return out, rows.Err()
|
|
}
|