Files
unibots/dev/issues/completed/0008-knowledge_por_agente.md
T
agent fc644ecd6e feat: import agents_and_robots platform as unibots (Matrix-out, unibus transport)
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.
2026-06-07 11:50:13 +02:00

10 KiB

Tarea 08 — Knowledge por agente

Objetivo

Cada agente tiene una carpeta knowledge/ donde almacena documentos de conocimiento (markdown). El agente puede buscar, leer, escribir y mejorar su propio conocimiento usando tools siempre disponibles. El conocimiento es archivos reales — inspeccionables por humanos, editables, y se pueden sembrar con contenido inicial.

Diseño

Almacenamiento híbrido: archivos + índice FTS5

agents/<id>/knowledge/          ← archivos .md reales (human-readable)
├── go-patterns.md
├── user-preferences.md
└── matrix-tips.md

agents/<id>/data/knowledge.db   ← índice SQLite FTS5 (búsqueda rápida)
  • Los documentos viven como archivos .md en knowledge/.
  • Un índice FTS5 en SQLite permite búsqueda full-text instantánea.
  • Al iniciar, se sincroniza: archivos → índice (detecta nuevos, modificados, eliminados).
  • Al escribir via tool, se actualiza archivo + índice atómicamente.

Por qué archivos y no solo SQLite

  1. Sembrables: se puede crear knowledge/ con documentos iniciales antes de arrancar
  2. Inspeccionables: un humano puede leer/editar el conocimiento del agente
  3. Git-friendly: opcionalmente trackeable en el repo
  4. Naturales: el agente "escribe documentos", no inserta rows

Arquitectura (pure core / impure shell)

1. Pure core: pkg/knowledge/

// pkg/knowledge/types.go
package knowledge

import "time"

// Document represents a knowledge document.
type Document struct {
    Slug      string    // filename sin extensión, e.g. "go-patterns"
    Title     string    // primera línea H1 del markdown, o slug humanizado
    Content   string    // contenido completo del archivo
    UpdatedAt time.Time // mtime del archivo
}

// SearchResult is a document matched by a search query.
type SearchResult struct {
    Slug    string
    Title   string
    Snippet string  // fragmento relevante con match highlights
    Rank    float64 // relevancia FTS5
}
// pkg/knowledge/store.go
package knowledge

import "context"

// Store is the pure interface for knowledge operations.
// Implemented by shell/knowledge.
type Store interface {
    // Search performs full-text search across all documents.
    Search(ctx context.Context, query string, limit int) ([]SearchResult, error)

    // Get retrieves a document by slug.
    Get(ctx context.Context, slug string) (*Document, error)

    // Put creates or updates a document (file + index).
    Put(ctx context.Context, doc Document) error

    // Delete removes a document (file + index).
    Delete(ctx context.Context, slug string) error

    // List returns all document slugs with titles.
    List(ctx context.Context) ([]Document, error)

    // Sync re-indexes all files from disk. Called on startup.
    Sync(ctx context.Context) error

    // Close releases resources.
    Close() error
}

2. Impure shell: shell/knowledge/

// shell/knowledge/store.go
package knowledge

// FileStore implements knowledge.Store using files + SQLite FTS5.
type FileStore struct {
    dir    string   // path a agents/<id>/knowledge/
    dbPath string   // path a agents/<id>/data/knowledge.db
    db     *sql.DB
    logger *slog.Logger
}

Schema SQLite:

CREATE VIRTUAL TABLE IF NOT EXISTS documents USING fts5(
    slug,
    title,
    content,
    updated_at UNINDEXED
);

Operaciones:

Método Archivos SQLite FTS5
Sync() Lee todos los .md del dir Reconstruye índice completo
Search() SELECT slug, title, snippet(...) FROM documents WHERE documents MATCH ?
Get() Lee {slug}.md
Put() Escribe {slug}.md Upsert en FTS5
Delete() Borra {slug}.md Delete en FTS5
List() SELECT slug, title FROM documents

Sync al startup:

  1. Listar *.md en el directorio
  2. Para cada archivo: leer contenido, extraer título (primer # ...), calcular mtime
  3. DELETE FROM documents + re-insertar todo (rebuild completo, simple y correcto)
  4. Log: knowledge_sync count=N

Slug rules:

  • Solo [a-z0-9-], máximo 64 chars
  • Derivado del nombre de archivo sin .md
  • El tool valida antes de escribir

3. Tools: tools/knowledge.go

Cuatro tools que el agente siempre tiene disponibles cuando knowledge está habilitado:

Nombre: knowledge_search
Descripción: Search your knowledge base for relevant documents. Returns matching snippets ranked by relevance.
Parámetros:
  - query (string, required): Search terms or phrase
  - limit (integer, optional): Max results, default 5
Retorna: Lista de resultados con slug, título y snippet

knowledge_read

Nombre: knowledge_read
Descripción: Read the full content of a knowledge document by its slug.
Parámetros:
  - slug (string, required): Document slug (e.g. "go-patterns")
Retorna: Contenido completo del documento

knowledge_write

Nombre: knowledge_write
Descripción: Create or update a knowledge document. Use this to save new knowledge or improve existing documents.
Parámetros:
  - slug (string, required): Document slug (lowercase, hyphens, e.g. "matrix-tips")
  - content (string, required): Full markdown content of the document
Retorna: Confirmación con slug y tamaño

knowledge_list

Nombre: knowledge_list
Descripción: List all documents in your knowledge base with their titles.
Parámetros: ninguno
Retorna: Lista de slugs con títulos y fecha de última actualización

Nota: No incluyo knowledge_delete por ahora. Los agentes deberían mejorar y ampliar, no borrar. Si se necesita, se añade después.

4. Config: internal/config/schema.go

type KnowledgeCfg struct {
    Enabled bool   `yaml:"enabled"`
    Dir     string `yaml:"dir"`  // default: "./knowledge" (relativo al dir del agente)
}

Añadir a ToolsCfg:

type ToolsCfg struct {
    // ... existentes ...
    Knowledge KnowledgeCfg `yaml:"knowledge"`
}

Config de ejemplo en config.yaml:

tools:
  knowledge:
    enabled: true
    dir: "./knowledge"   # opcional, default relativo al agente

5. Registro en runtime: agents/runtime.go

En buildToolRegistry(), después de los memory tools:

if cfg.Tools.Knowledge.Enabled {
    knowledgeDir := resolveKnowledgeDir(cfg)  // resolve relative to agent dir
    knowledgeDBPath := filepath.Join(cfg.Storage.DataDir, "knowledge.db")
    kStore, err := shellknowledge.New(knowledgeDir, knowledgeDBPath, logger)
    if err != nil {
        logger.Error("knowledge_store_init_failed", "err", err)
    } else {
        // Sync on startup
        if err := kStore.Sync(ctx); err != nil {
            logger.Error("knowledge_sync_failed", "err", err)
        }
        reg.Register(tools.NewKnowledgeSearch(kStore))
        reg.Register(tools.NewKnowledgeRead(kStore))
        reg.Register(tools.NewKnowledgeWrite(kStore))
        reg.Register(tools.NewKnowledgeList(kStore))
        logger.Debug("registered knowledge tools")
    }
}

Plan de implementación (orden)

Paso 1 — Pure types (pkg/knowledge/)

  • pkg/knowledge/types.go — Document, SearchResult
  • pkg/knowledge/store.go — Store interface

Paso 2 — Config

  • Añadir KnowledgeCfg a internal/config/schema.go dentro de ToolsCfg

Paso 3 — Shell store (shell/knowledge/)

  • shell/knowledge/store.go — FileStore con FTS5
    • Constructor New(dir, dbPath, logger)
    • Sync(), Search(), Get(), Put(), Delete(), List(), Close()
    • Validación de slugs
    • Extracción de título del markdown (primer # )

Paso 4 — Tools (tools/knowledge.go)

  • tools/knowledge.go — NewKnowledgeSearch, NewKnowledgeRead, NewKnowledgeWrite, NewKnowledgeList
  • Interface KnowledgeStore en tools (subset de knowledge.Store, como se hizo con MemoryStore)

Paso 5 — Registro en runtime

  • Modificar buildToolRegistry() en agents/runtime.go
  • Resolver directorio de knowledge relativo al agente

Paso 6 — Activar en agentes existentes

  • Crear agents/assistant-bot/knowledge/ con un documento semilla
  • Crear agents/asistente-2/knowledge/ con un documento semilla
  • Actualizar config.yaml de ambos agentes: tools.knowledge.enabled: true
  • Actualizar system prompts para que el agente sepa que tiene knowledge tools

Paso 7 — Tests

  • Test de shell/knowledge/ — sync, search, put, get, list
  • Test de tools/knowledge.go — validación de slugs, parámetros
  • Build completo: go build -tags goolm ./...

Ejemplo de uso por el agente

Un usuario le dice al bot: "¿Cómo configuro un webhook en Gitea?"

  1. El agente llama knowledge_search(query="gitea webhook")
  2. Encuentra gitea-admin.md con snippet relevante
  3. Llama knowledge_read(slug="gitea-admin") para leer el documento completo
  4. Responde al usuario con la info
  5. Si descubre info nueva en la conversación, llama knowledge_write(slug="gitea-webhooks", content="# Gitea Webhooks\n\n...") para ampliar su base

Diferencia con memory tools

Aspecto Memory (facts) Knowledge (documents)
Granularidad Key-value individual Documentos completos
Búsqueda Por subject exacto Full-text search (FTS5)
Formato Tripla (subject, key, value) Markdown libre
Propósito Datos puntuales sobre users/temas Base de conocimiento estructurada
Persistencia SQLite rows Archivos .md + índice FTS5
Editable por humanos No (solo via SQL) Sí (archivos normales)

Notas de implementación

  • FTS5 y modernc/sqlite: modernc.org/sqlite soporta FTS5 nativamente, no necesita CGO.
  • Slugs: validar con regexp ^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$ (min 2 chars).
  • Título: extraer primera línea que empiece con # . Si no hay, usar slug humanizado.
  • Tamaño máximo por documento: 64 KB (consistente con read_file tool).
  • Directorio knowledge/ en .gitignore: decisión del usuario. Se puede trackear o no.
  • No embeddings: FTS5 keyword search es suficiente para v1. Embeddings es extensión futura.