Agent.bx
Die eine erforderliche Datei - eine Klasse, die bx-ais eigenes AiAgent erweitert, sodass sie DER Agent IST.
On this page
Agent.bx
Agent.bx ist die einzige erforderliche Datei in einem BX-Agents-Projekt. Sie erweitert bx-ais eigenes AiAgent, sie ist also der Agent - der Build instanziiert sie, statt eine aus einer Konfigurationsstruktur neu aufzubauen, sodass das, was geschrieben wird, auch läuft. Erben und hinzufügen, was gebraucht wird: private Hilfsmethoden, überschriebene Methoden, im Code registrierte Tools. Weil es sich um eine echte Klasse statt um einen struct-zurückgebenden Deskriptor handelt, kann eine IDE sie wie jede andere BoxLang-Klasse introspizieren - Sprung zur Definition, Autovervollständigung auf geerbten Methoden, alles.
class extends="bxModules.bxai.models.runnables.AiAgent" {
function init() {
super.init(
name : "my-agent",
description : "A helpful assistant",
instructions: "You are a helpful assistant.",
model : aiModel( provider: "openai", params: { model: "gpt-5" } )
)
return this
}
}
bxAgents new erzeugt genau diese Form als Gerüst. instructions.md ist optional - instructions direkt in super.init() setzen, oder die Datei neben Agent.bx ablegen und sie vom Build einbinden lassen (siehe Tabelle unten).
Was der Build über die Klasse legt
Die Regel: eine explizit deklarierte Konvention gewinnt; ansonsten hat die Klasse das letzte Wort.
Ein Agent, der alles in seiner eigenen init() setzt, bekommt also nichts aufgezwungen, während einer, der nur das Minimum setzt, trotzdem die Konventionen übernimmt, für die er nichts erklärt hat. Alles Folgende ist optional - eine configure() deklarieren, die einen dieser Schlüssel zurückgibt, um zu überschreiben, was die Klasse selbst gesetzt hat, oder den passenden Konventionsordner instructions.md/tools//subagents/:
| Was deklariert wird | Was der Build erzeugt | Wenn nicht deklariert |
|---|---|---|
instructions.md | withInstructions( fileRead( ... ) ) | die eigenen Instruktionen der Klasse bleiben bestehen |
model in configure() | setModel( aiModel( ... ) ) | das eigene Modell der Klasse bleibt bestehen |
name / description in configure() | setName() / setDescription() | die eigenen Werte der Klasse bleiben bestehen |
memory in configure() | setMemory( ... ) | der eigene Wert der Klasse bleibt bestehen |
| (nichts zu deklarieren) | withTools( aiToolRegistry().getAll() ) | immer - withTools() hängt an in bx-ai statt zu ersetzen, sodass von der Klasse selbst registrierte Tools erhalten bleiben und die entdeckten tools/ hinzugefügt werden |
subAgents an der Klasse, oder subagents/ auf der Platte | addSubAgent( ... ) pro Kind | wird auf dieselbe Weise angehängt |
checkpointer in configure() | withCheckpointer( ... ) | wird trotzdem eingesetzt, falls die Klasse keinen gesetzt hat - siehe unten |
Der Checkpointer ist das Einzige, das der Build ungefragt einsetzt. Ein von einem Gateway aus erreichbarer Agent ohne Checkpointer hat stillschweigend kaputtes Human-in-the-Loop, also erhält eine Klasse, die keinen gesetzt hat, trotzdem den cache-Standard. Eine Klasse, die einen eigenen setzt, wird unangetastet gelassen.
Bewusst nicht implementiert durch den Vergleich der eigenen Instanz mit bx-ais DEFAULT_AGENT_*-Werten. "Wollte die Autorin das so, oder ist das nur der Standardwert?" ist unbeantwortbar, und eine Autorin, die tatsächlich den Standardnamen wollte, würde ihn stillschweigend ersetzt vorfinden. Das Vorhandensein einer externen Deklaration ist eine Tatsache; die Absicht hinter einem Standardwert ist es nicht.
Die Klasse wird in die generierte App nach agent/classes/ kopiert und dort über ihren eigenen absoluten Dateipfad instanziiert (nie eine relative Lookup, die von einer registrierten Zuordnung abhängen würde), genau wie tools/, skills/ und mcp/ kopiert werden - sodass eine paketierte .bxa sie mitführt, und chat/invoke/serve sie alle auf dieselbe Weise instanziieren, egal ob ein echter ColdBox-Container bootet oder nicht.
configure() (optional) - überschreibt, was die Klasse gesetzt hat
Eine configure()-Methode ist vollständig optional. Sie nur deklarieren, um bestimmte Felder von außerhalb der Klasse zu überschreiben - nützlich, um deploymentspezifische Werte (z. B. ein anderes Modell pro Umgebung) aus dem Klassenkörper selbst herauszuhalten:
class extends="bxModules.bxai.models.runnables.AiAgent" {
function init() {
super.init(
name : "with-mcp-servers-agent",
model : aiModel( provider: "openai", params: { model: "gpt-5" } )
)
return this
}
function configure() {
return {
mcpServers : [
"https://example.com/mcp",
{ url : "https://other.com/mcp", name : "other" }
]
};
}
}
| Feld | Typ | Hinweise |
|---|---|---|
name | string | Wird zur Build-Zeit auch zum Bindungsschlüssel dieses Agenten in config/WireBox.bx (getInstance( name )) - siehe schedules/ - muss also im gesamten Projekt eindeutig sein (Root + jeder Subagent); build schlägt fehl, falls zwei Agenten sich einen Namen teilen. |
model | string | Ein provider/model-Slug, ein bloßer Provider-Name, oder ein Name, der zu einem models/-Eintrag passt. Siehe unten. |
description | string | Optional. |
subAgents | array of strings | Namen von Geschwister-Ordnern unter dem subagents/ des Root-Projekts. Siehe subagents/. |
mcpServers | array | Remote-MCP-Server - jeder Eintrag ein URL-String oder { url, name }. Siehe mcp/. |
security | struct | Wird unverändert in die bxai-Moduleinstellungen der generierten App weitergereicht; bx-ais eigener SecurityDirector macht daraus Guardrail-Middleware. Nur Durchreichung - BX Agents hat keine eigene Guardrails-Konvention. |
memory | string oder struct | Das Konversationsgedächtnis des Agenten. Ein bloßer String ist eine Kurzform für den Typ ("cache"); eine Struktur ist { type, ...config } und wird unverändert an aiMemory() weitergereicht - z. B. { type: "cache", maxMessages: 50 }, oder mit summaryProvider/summaryModel/summaryThreshold, damit /compact in der Web-UI funktioniert. Gilt pro Knoten, ein Subagent kann also einen eigenen deklarieren. |
checkpointer | struct | { type: "cache"\|"file"\|"jdbc", ...config }. Fällt bei Weglassen auf { type: "cache" } zurück. Immer angewendet - ohne einen scheitern Human-in-the-Loop-Genehmigungsabläufe über jedes Gateway außer cli vollständig. |
gatewaySession | struct | { policy, maxQueueDepth }, beide optional (Standard "queue" / 50). Nur relevant, falls das Projekt mindestens einen Push-Style-Gateway-Eintrag hat - steuert die Richtlinie der generierten GatewaySession für eine zweite eingehende Nachricht, die auf einem Thread eintrifft, auf dem bereits ein Turn läuft. policy muss reject/queue/steer/interrupt sein. |
| jeder andere Schlüssel | any | Wird gemergt und ist in der aufgelösten Konfigurationsstruktur verfügbar, wird aber von BX Agents selbst nicht interpretiert. |
The model slug
model ist BX Agents' eigene Konvention - bx-ai selbst nimmt provider und model als zwei separate Argumente an aiModel(). BX Agents teilt den Slug nur am ersten /, sodass ein Provider, der selbst einen Schrägstrich enthält (wie OpenRouters openrouter/anthropic/claude-x), trotzdem korrekt geparst wird:
model-Wert | provider | model |
|---|---|---|
openai/gpt-5 | openai | gpt-5 |
openrouter/anthropic/claude-x | openrouter | anthropic/claude-x |
mock/mock-model | mock | mock-model |
Hat model überhaupt keinen /, muss es entweder ein bekannter Core-Provider-Name sein oder zum Namen eines models/-Eintrags passen - die Validierung lehnt alles andere ab. Die erkannten Core-Provider sind: bedrock, claude, cohere, deepseek, docker, elevenlabs, gemini, grok, groq, huggingface, minimax, mistral, mock, ollama, openai, openai-compatible, openrouter, perplexity, voyage (mit bx-ais eigenem CORE_PROVIDERS synchron gehalten). mock ist ein echter Provider, nützlich für Tests und CI - er macht nie einen Netzwerkaufruf.
Diese Slug-Aufteilungskonvention durchläuft ein per configure() deklarierter model-String - super.init()s eigenes model-Argument nimmt hingegen direkt eine echte AiModel-Instanz (aiModel( provider: "...", params: { model: "..." } )), da die Klasse bereits bx-ais eigene API spricht.
Umgebungs-Overrides
Agent.bx kann eine nach einer Umgebung benannte Methode deklarieren (z. B. production(), development(), oder ein beliebiger eigener Name), die eine Struktur von Overrides zurückgibt - das funktioniert unabhängig davon, ob die Klasse auch eine configure() deklariert:
class extends="bxModules.bxai.models.runnables.AiAgent" {
function init() {
super.init(
name : "override-agent",
description : "An agent with an environment override",
model : aiModel( provider: "openai", params: { model: "gpt-5" } )
)
return this
}
function production() {
return {
model : "openai/gpt-5-mini"
};
}
}
Die aktive Umgebung wird nach dieser Rangfolge aufgelöst (höchste gewinnt):
--environment-CLI-Flag (bxAgents build --environment=production)- Umgebungsvariable
BX_AGENTS_ENV "development"(Standard)
Das ist eine reine Build-Zeit-Entscheidung, unabhängig von ColdBoxs eigener Laufzeit-Umgebungserkennung (die generierte App liest getSetting("environment") selbst, gemäß ColdBoxs environments-Konvention) - diese Rangfolge entscheidet nur, welche environment()-Override-Methode an Agent.bx, und welche boxlang-{env}.json/miniserver-{env}.json-Dateien, die Build-Pipeline anwendet.
Existiert keine zur aktiven Umgebung passende Methode, wird kein Override angewendet.
Merge-Semantik
Die vollständige Auflösungsreihenfolge (niedrigste zu höchste Priorität) ist:
configure()(optional)- die passende Umgebungs-Override-Methode, falls vorhanden
boxlang.jsonboxlang-{environment}.jsonminiserver.jsonminiserver-{environment}.json
Struct-Schlüssel werden rekursiv gemergt - eine verschachtelte Struktur aus einer höher priorisierten Quelle überschreibt nur die Schlüssel, die sie tatsächlich setzt, und lässt Geschwisterschlüssel aus einer niedriger priorisierten Quelle unangetastet. Arrays und alle skalaren Werte werden vollständig ersetzt, nie angehängt oder verkettet.
boxlang.json/boxlang-{env}.json/miniserver.json/miniserver-{env}.json sind allesamt optionale JSON-Dateien im Projekt-Root, nützlich für Konfiguration, die sich leichter als Daten denn als BoxLang-Code ausdrücken lässt (z. B. Modell-Standardwerte):
// boxlang.json
{
"modelDefaults": { "temperature": 0.7, "maxTokens": 1000 }
}
// boxlang-production.json
{
"modelDefaults": { "temperature": 0.2 }
}
Ein Build mit --environment=production ergibt hier modelDefaults: { temperature: 0.2, maxTokens: 1000 } - das rekursive Merge behielt maxTokens aus der Basisdatei, da boxlang-production.json es nie erwähnte.
Secrets (API-Schlüssel, Tokens) werden von BX Agents zur Build-Zeit nie gelesen oder gemergt - sie bleiben extern (eine OS-Umgebungsvariable, .env, ein Plattform-Secret-Manager) und werden von bx-ai selbst live zur Laufzeit aufgelöst. Siehe Deployment & Secrets.