Zum Inhalt springen

Plattform erweitern

ollama-agent ist als Basis gedacht, die man für einen speziellen Workflow übernimmt und erweitert. Es gibt drei Erweiterungsebenen — von “kein Code” bis “eigenes Binary”.

Ein Skill ist ein Ordner mit einer SKILL.md:

skills/
└── mein-skill/
├── SKILL.md
└── scripts/ # optional
└── helper.sh
---
name: mein-skill
description: Ein kurzer Satz, den das Modell liest.
triggers: [stichwort, anderes stichwort]
---
# Anleitung
Was das Modell tun soll, wenn der Skill geladen ist ...
  • Die Skill-Liste (nur Name + Beschreibung) steht kompakt im System-Prompt; der Body wird erst per load_skill-Tool in den Kontext geholt — wichtig für kleine Modelle.
  • triggers laden den Skill automatisch, wenn ein Stichwort im Input vorkommt (skills.auto_load_on_trigger).
  • Scripts sind nur ausführbar, wenn skills.allow_scripts: true gesetzt ist, und nur wenn sie bei der Discovery im scripts/-Ordner gefunden wurden.

Beliebige MCP-Server (stdio oder HTTP) in der Config eintragen — ihre Tools stehen dem Agent sofort zur Verfügung, mit Namen mcp_<server>_<tool>:

"mcp_servers": [
{ "name": "files", "transport": "stdio",
"command": "mcp-filesystem-server", "args": ["/pfad"],
"tool_tags": ["tool_calling", "code"] }
]

tool_tags steuert, bei welchen Task-Kategorien die Tools dem Modell angeboten werden (Tool-Subsetting hält die Prompts klein).

Der gesamte Kern liegt unter pkg/ und ist importierbar. Minimalbeispiel für einen eigenen Workflow mit eigenem nativen Tool:

package main
import (
"context"
"encoding/json"
"log/slog"
"gitlab.techeve.de/techeve/ollama-agent/pkg/agent"
"gitlab.techeve.de/techeve/ollama-agent/pkg/config"
"gitlab.techeve.de/techeve/ollama-agent/pkg/pool"
"gitlab.techeve.de/techeve/ollama-agent/pkg/router"
"gitlab.techeve.de/techeve/ollama-agent/pkg/tools"
)
func main() {
cfg, _ := config.Load("config.json")
ctx := context.Background()
p := pool.New(cfg, slog.Default())
p.Start(ctx)
defer p.Close()
reg := tools.NewRegistry()
reg.Register(&tools.Func{
ToolName: "ticket_lookup",
Desc: "Look up a support ticket by id.",
ArgsSchema: json.RawMessage(`{
"type":"object",
"properties":{"id":{"type":"string"}},
"required":["id"]
}`),
ToolTags: []string{"tool_calling"},
Fn: func(ctx context.Context, args json.RawMessage) (string, error) {
// eigene Logik ...
return "Ticket 42: offen, Priorität hoch", nil
},
})
a := agent.New(agent.Options{
Pool: p,
Tools: reg,
Router: router.New(p, cfg.Models, nil),
Config: cfg.Agent,
})
res, _ := a.Run(ctx, "Wie ist der Status von Ticket 42?", agent.RunOptions{})
println(res.Output)
}

Bausteine und ihre Aufgaben:

PaketAufgabe
pkg/poolEndpoint-Pool: Health-Checks, modell-bewusste Auswahl, Failover
pkg/routerTask-Kategorie → Modell (Heuristik + LLM-Classify + Config)
pkg/agentTool-Calling-Loop, Salvage-Parser, History-Kompaktierung, Sessions
pkg/toolsTool-Interface + Registry mit Kategorie-Tags
pkg/skillsSKILL.md-Discovery und -Matching
pkg/mcpclientMCP-Server anbinden, Tools adaptieren
pkg/schedCron-Jobs, die Agent-Läufe triggern
pkg/apiHTTP-Daemon über allem

Eigene Tools implementieren das Interface tools.Tool (oder nutzen tools.Func). Wichtig für kleine Modelle:

  • Description: genau ein kurzer Satz.
  • Schema: flach halten, wenige Pflichtfelder.
  • Tags: Kategorien angeben, damit das Tool nur bei passenden Tasks im Prompt landet.
  • Ergebnis: kompakt zurückgeben; der Loop truncated bei max_tool_result_chars.