fc644ecd6e
Reemplaza el scaffold del echobot por la plataforma completa de bots traida desde ~/DataProyects/Github/agents_and_robots tras la operacion Matrix-out: los bots ya no hablan por Matrix sino por el bus unibus (modelo todo-rooms + E2E via shell/transportunibus sobre github.com/enmanuel/unibus/pkg/client). - go.mod: replace de unibus -> ../unibus y de fn-registry -> ../../../.. (paths relativos reajustados a la nueva ubicacion dentro de fn_registry). - app.md: bump a 0.2.0, descripcion + arquitectura + comandos + gotchas reales. - modulo Go conservado como github.com/enmanuel/agents (sin reescribir imports). agents_and_robots queda archivado como museo de la era Matrix.
104 lines
2.5 KiB
Go
104 lines
2.5 KiB
Go
package skills
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Match retorna las skills mas relevantes para una query dada.
|
|
// Implementacion inicial: keyword matching simple contra name + description.
|
|
// La query y las skills son procesadas en lowercase para matching case-insensitive.
|
|
//
|
|
// El scoring es basico:
|
|
// - Match exacto en name: 1.0
|
|
// - Match parcial en name: 0.8
|
|
// - Match en description: 0.6 * (palabras coincidentes / palabras totales)
|
|
// - Sin match: 0.0
|
|
//
|
|
// Retorna las skills ordenadas por confidence descendente.
|
|
func Match(query string, skills []SkillMeta) []SkillMatch {
|
|
query = strings.ToLower(strings.TrimSpace(query))
|
|
if query == "" {
|
|
return nil
|
|
}
|
|
|
|
queryWords := strings.Fields(query)
|
|
var matches []SkillMatch
|
|
|
|
for _, skill := range skills {
|
|
confidence := scoreSkill(queryWords, skill)
|
|
if confidence > 0 {
|
|
matches = append(matches, SkillMatch{
|
|
Skill: skill,
|
|
Confidence: confidence,
|
|
})
|
|
}
|
|
}
|
|
|
|
sort.Sort(ByConfidence(matches))
|
|
return matches
|
|
}
|
|
|
|
// scoreSkill calcula el score de relevancia de una skill para las palabras de query.
|
|
func scoreSkill(queryWords []string, skill SkillMeta) float64 {
|
|
nameLower := strings.ToLower(skill.Name)
|
|
descLower := strings.ToLower(skill.Description)
|
|
|
|
// Match exacto en name
|
|
queryStr := strings.Join(queryWords, " ")
|
|
if nameLower == queryStr {
|
|
return 1.0
|
|
}
|
|
|
|
// Match parcial en name (todas las palabras de query aparecen en name)
|
|
nameMatches := 0
|
|
for _, word := range queryWords {
|
|
if strings.Contains(nameLower, word) {
|
|
nameMatches++
|
|
}
|
|
}
|
|
if nameMatches == len(queryWords) {
|
|
return 0.8
|
|
}
|
|
|
|
// Match en description (contar palabras coincidentes)
|
|
descWords := strings.Fields(descLower)
|
|
descMatches := 0
|
|
for _, qword := range queryWords {
|
|
for _, dword := range descWords {
|
|
if strings.Contains(dword, qword) || strings.Contains(qword, dword) {
|
|
descMatches++
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if descMatches > 0 {
|
|
ratio := float64(descMatches) / float64(len(queryWords))
|
|
return 0.6 * ratio
|
|
}
|
|
|
|
return 0.0
|
|
}
|
|
|
|
// FilterByCategory retorna solo las skills que pertenecen a las categorias especificadas.
|
|
// Si categories esta vacio, retorna todas las skills sin filtrar.
|
|
func FilterByCategory(skills []SkillMeta, categories []string) []SkillMeta {
|
|
if len(categories) == 0 {
|
|
return skills
|
|
}
|
|
|
|
catSet := make(map[string]bool)
|
|
for _, cat := range categories {
|
|
catSet[strings.ToLower(cat)] = true
|
|
}
|
|
|
|
var filtered []SkillMeta
|
|
for _, skill := range skills {
|
|
if catSet[strings.ToLower(skill.Category)] {
|
|
filtered = append(filtered, skill)
|
|
}
|
|
}
|
|
return filtered
|
|
}
|