Zum Inhalt springen

Models

Ein Agent braucht ein Sprachmodell, um zu arbeiten. OpenBot enthält kein Modell. Du verbindest eins. OpenBot speichert dafür zwei Arten von Einträgen:

  • Ein Model beschreibt ein Sprachmodell, zum Beispiel gpt-5: seinen Namen, seine Optionen und seine Grenzen. Ein Model gehört nicht zu einem Anbieter.
  • Ein Model Provider (Modell-Anbieter) ist ein Dienst für Modelle mit seinem API-Schlüssel. Ein Anbieter verknüpft die Models, die er anbietet. Eine Verknüpfung kann manche Werte des Model nur für diesen Anbieter ändern.

Du fügst beide über die API hinzu. OpenBot speichert sie in der Datenbank. So nutzt jede Instanz dieselben.

Die Platform oder eine Organization besitzt jedes Model und jeden Anbieter:

Besitzer Wer es hinzufügt Wer es nutzen kann
Organization Eine Organization, mit ihrem eigenen Schlüssel Nur diese Organization. Das heißt “Bring your own key” (bring deinen eigenen Schlüssel mit).
Platform Ein Admin der Platform, mit dem Schlüssel der Platform Die Organizations in ihrer availability. Siehe Platform.

Eine Organization kann ein Model eines Anbieters nur nutzen, wenn sie sowohl den Anbieter als auch das Model nutzen darf.

Jedes Model hat einen von zwei Typen:

  • Ein language-Model schreibt Text und ruft Werkzeuge auf. Es führt Sessions aus, beurteilt Rules und lernt Agent-Memory.
  • Ein decision-Model wählt nur eine Antwort aus einer festen Liste. Es schreibt keinen Text. Jev von TypeSafe ist ein Beispiel. Siehe Decision-Model.

Wenn du OpenBot aus Code startest, kannst du auch eingebaute Models angeben. Siehe Eingebaute Models.


Ein Model hinzufügen

POST /v1/organization/models fügt deiner Organization ein Model hinzu. Es braucht model_providers.write.

Terminal window
curl -H "Authorization: Bearer $OPENBOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "id": "qwen3-32b", "name": "Qwen3 32B", "reasoningEfforts": [], "serviceTiers": [], "contextWindow": 131072 }' \
https://api.example.com/v1/organization/models
Feld Regeln
id Pflicht. 1 bis 200 Zeichen. Sie ist für ihren Besitzer eindeutig, und du kannst sie nicht ändern. Ein Anbieter schickt sie als Namen des Modells, außer die Verknüpfung setzt upstream.
name Pflicht. Ein Name für Menschen.
reasoningEfforts Pflicht. Die Werte für reasoningEffort, die das Model annimmt. Kann leer sein.
serviceTiers Pflicht. Die Werte für serviceTier, die das Model annimmt. Kann leer sein.
contextWindow Optional. Die Größe des Kontextfensters, in Tokens.
maxOutputTokens Optional. Die meisten Tokens, die das Model in einer Antwort schreiben kann.

Wenn deine Organization schon ein Model mit dieser id hat, gibt OpenBot 409 mit ModelConflict zurück.

Route Was sie tut
GET /v1/organization/models Listet die Models deiner Organization auf. Geteilte Models sind nicht in dieser Liste.
GET /v1/organization/models/:modelId Liest ein Model.
PATCH /v1/organization/models/:modelId Ändert ein Model. Der Body braucht expectedVersion. Schicke null für eine Grenze, um sie zu entfernen.
DELETE /v1/organization/models/:modelId Entfernt ein Model.

Lesen braucht model_providers.read. Änderungen brauchen model_providers.write. Du kannst ein Model nicht entfernen, solange einer deiner Anbieter es verknüpft. OpenBot gibt 409 mit ModelInUse zurück. Entferne zuerst die Verknüpfung.

Einen Anbieter hinzufügen

POST /v1/organization/model_providers fügt deiner Organization einen Anbieter hinzu. Es braucht model_providers.write.

Terminal window
curl -H "Authorization: Bearer $OPENBOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kind": "openai-compat",
"name": "Local vLLM",
"endpoint": "https://llm.example.com/v1",
"apiKey": "sk-...",
"models": [
{ "model": "qwen3-32b", "modelOwner": "organization", "upstream": "Qwen/Qwen3-32B" }
]
}' \
https://api.example.com/v1/organization/model_providers
Feld Regeln
kind Pflicht. Die API, die der Dienst spricht: openai, anthropic oder openai-compat. Siehe die Tabelle unten.
name Pflicht. 1 bis 120 Zeichen.
endpoint Pflicht für openai-compat. Optional für die anderen. Eine Basis-URL mit http oder https, oft mit /v1 am Ende. Kein Nutzername, kein Passwort, keine Query und kein Teil mit #.
apiKey Pflicht. OpenBot speichert ihn verschlüsselt und schickt ihn nur an den Endpoint.
models Pflicht. 0 bis 200 Verknüpfungen. Die Reihenfolge ist die Reihenfolge der Liste der Models.
kind API
openai OpenAI Responses API.
anthropic Anthropic Messages API.
openai-compat OpenAI Chat Completions API und Dienste, die sie nachbauen.

Jede Verknüpfung hat diese Felder:

Feld Regeln
model Pflicht. Die id des Model. Ein Anbieter verknüpft jede Model-ID nur einmal.
modelOwner Pflicht. organization für ein Model deiner Organization oder platform für ein geteiltes Model.
upstream Optional. Der Name des Modells, den der Dienst bekommt, wenn er nicht die id des Model ist.
reasoningEfforts Optional. Ersetzt die reasoningEfforts des Model für diesen Anbieter.
serviceTiers Optional. Ersetzt die serviceTiers des Model für diesen Anbieter.
contextWindow Optional. Ersetzt das contextWindow des Model für diesen Anbieter.
maxOutputTokens Optional. Ersetzt maxOutputTokens des Model für diesen Anbieter.

Ein Anbieter deiner Organization kann deine eigenen Models verknüpfen und die geteilten Models, die die Platform dir zur Verfügung stellt. Für jedes andere Model gibt OpenBot 404 mit ModelNotFound zurück.

OpenBot antwortet mit dem Anbieter. Seine id beginnt mit prov_. Eine Anbieter-ID ist in der ganzen Platform eindeutig. Eine Auswahl eines Model nennt sie als provider. Der API-Schlüssel kommt nie zurück. Die Antwort zeigt stattdessen apiKey: { set, hint }. Siehe Platform.

Einen Anbieter ändern oder entfernen

Route Was sie tut
GET /v1/organization/model_providers Listet die Anbieter auf.
GET /v1/organization/model_providers/:providerId Liest einen Anbieter.
PATCH /v1/organization/model_providers/:providerId Ändert einen Anbieter.
DELETE /v1/organization/model_providers/:providerId Entfernt einen Anbieter.

Lesen braucht model_providers.read. Änderungen brauchen model_providers.write.

Der Body von PATCH braucht expectedVersion, also die version, die du gelesen hast. Wenn jemand den Anbieter geändert hat, nachdem du ihn gelesen hast, gibt OpenBot 409 mit ModelProviderConflict zurück. Lass apiKey weg, um den aktuellen Schlüssel zu behalten. Schicke models, um alle Verknüpfungen des Anbieters zu ersetzen.

Terminal window
curl -X PATCH -H "Authorization: Bearer $OPENBOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"expectedVersion":0,"apiKey":"sk-new..."}' \
https://api.example.com/v1/organization/model_providers/prov_4c1e...

Der nächste Aufruf eines Modells nach einer Änderung nutzt den neuen Schlüssel, Endpoint oder die neuen Verknüpfungen. OpenBot baut jedes Model einmal und baut es neu, wenn sich sein Anbieter ändert.

Du kannst einen Anbieter nicht entfernen, solange rulesModel, agentMemoryModel oder decisionModel in den Einstellungen der Organization ihn nennt. OpenBot gibt 409 mit ModelProviderInUse zurück. Ändere zuerst die Einstellung.


Ein Model auswählen

GET /v1/organization/model_catalog listet die Models, die deine Organization jetzt nutzen kann. Es braucht models.use. Jeder Eintrag ist ein Model eines Anbieters, mit den Werten der Verknüpfung schon angewendet. Ein Model, das zwei Anbieter verknüpfen, erscheint zweimal.

Terminal window
curl -H "Authorization: Bearer $OPENBOT_TOKEN" \
https://api.example.com/v1/organization/model_catalog
{
"providers": [
{
"id": "prov_4c1e...",
"owner": "organization",
"kind": "openai-compat",
"name": "Local vLLM"
}
],
"models": [
{
"provider": "prov_4c1e...",
"id": "qwen3-32b",
"owner": "organization",
"name": "Qwen3 32B",
"reasoningEfforts": [],
"serviceTiers": [],
"contextWindow": 131072,
"price": {
"model": "qwen3-32b",
"timeZone": "UTC",
"tiers": [
{
"upToInputTokens": null,
"inputUncached": 400000,
"inputCacheRead": 100000,
"inputCacheWrite": 0,
"output": 1200000,
"outputReasoning": null
}
],
"periods": [],
"effectiveFrom": "2026-10-20T08:00:00.000Z"
}
}
],
"decisionModel": null
}

price ist der aktuelle Preis des Model bei diesem Anbieter. Er fehlt, wenn niemand einen Preis gesetzt hat. Siehe Preise.

Eine Auswahl eines Model nennt den Anbieter und die Model-ID:

{ "_tag": "Config.Selection", "provider": "prov_4c1e...", "model": "qwen3-32b" }

OpenBot wählt nie ein Model für dich. Es gibt kein Standard-Model. Jede Nutzung nennt ihr eigenes Model:

  • Eine neue Session nutzt settings.model aus der Anfrage. Ohne diesen Wert nutzt die Session das model ihres Agents. Wenn keiner von beiden ein Model gibt, gibt OpenBot 400 mit SessionModelInvalid und dem Grund missing zurück. Dein Main Agent hat am Anfang kein Model. Setze also zuerst sein model mit PATCH /v1/agents/:id.
  • Das Prüf-Modell für Rules nutzt rulesModel in den Einstellungen der Organization. Wenn es null ist, gibt das Prüf-Modell keine Antwort. Dann entscheidet eine Person oder ein Agent.
  • Agent-Memory nutzt agentMemoryModel in denselben Einstellungen. Wenn es null ist, lernen Agents nichts.

rulesModel und agentMemoryModel müssen language-Models sein. Der Katalog nennt den type jedes Model, und eine Session kann nur language-Models wählen.

Ältere Versionen von OpenBot hatten defaultModel und helperModel. Beim Update gibt OpenBot das alte defaultModel jedem Agent, der kein Model hat. rulesModel und agentMemoryModel bekommen das alte Helfer-Modell: helperModel, sonst defaultModel. Wenn beide null waren, bleiben die neuen Werte leer, und du musst sie setzen.

So gibst du zum Beispiel deinem Main Agent ein Model. Schicke seine aktuelle revision als expected:

Terminal window
curl -X PATCH -H "Authorization: Bearer $OPENBOT_TOKEN" -H 'Content-Type: application/json' \
-d '{"expected":0,"model":{"_tag":"Config.Selection","provider":"prov_4c1e...","model":"qwen3-32b"}}' \
https://api.example.com/v1/agents/$AGENT_ID

Eine Session kann ihre eigene Auswahl speichern. Schicke die aktuelle Revision der Session als expected:

Terminal window
curl -X PUT -H "Authorization: Bearer $OPENBOT_TOKEN" -H 'Content-Type: application/json' \
-d '{"expected":3,"selection":{"_tag":"Config.Selection","provider":"prov_4c1e...","model":"qwen3-32b"}}' \
https://api.example.com/v1/sessions/$SESSION_ID/model

GET /v1/sessions/:id/models zeigt dieselben Models für eine Session. Alle Felder findest du in der API-Referenz für Sessions.

Um festzulegen, welche Anbieter ein Member auswählen darf, nutze models.use auf openbot.org.<org>.model.<providerId>. Siehe Berechtigungen.

Decision-Model

decisionModel in den Einstellungen der Organization nennt das Decision-Model (Entscheidungs-Model) deiner Organization. Es muss ein decision-Model sein, das einer deiner Anbieter anbietet. Sonst gibt OpenBot 400 mit SettingsInvalid zurück. null heißt: Deine Organization hat kein Decision-Model.

Um zum Beispiel Jev über TypeSafe zu nutzen, füge das Model hinzu, dann einen Anbieter, der es verknüpft, und dann die Einstellung:

Terminal window
curl -H "Authorization: Bearer $OPENBOT_TOKEN" -H "Content-Type: application/json" \
-d '{ "id": "jev-latest", "type": "decision", "name": "Jev", "reasoningEfforts": [], "serviceTiers": [] }' \
https://api.example.com/v1/organization/models
curl -H "Authorization: Bearer $OPENBOT_TOKEN" -H "Content-Type: application/json" \
-d '{
"kind": "typesafe",
"name": "TypeSafe",
"endpoint": "https://typesafe.example.com/v1",
"apiKey": "...",
"models": [{ "model": "jev-latest", "modelOwner": "organization" }]
}' \
https://api.example.com/v1/organization/model_providers
curl -X PATCH -H "Authorization: Bearer $OPENBOT_TOKEN" -H "Content-Type: application/json" \
-d '{"expectedVersion":3,"decisionModel":{"_tag":"Config.Selection","provider":"prov_9a2b...","model":"jev-latest"}}' \
https://api.example.com/v1/organization/settings

GET /v1/organization/model_catalog zeigt die Einstellung als decisionModel.

OpenBot ruft einen typesafe-Anbieter mit dem TypeSafe-Client von Effect (@effect/ai-typesafe) auf. Es schickt jede Anfrage an <endpoint>/systemone, mit dem API-Schlüssel als Bearer-Token, und schickt upstream als Namen des Model. OpenBot baut den Client einmal für jeden Anbieter und jedes Model und baut ihn neu, wenn sich der Anbieter ändert. OpenBot kann ein Decision-Model noch nicht über einen openai-compat-Anbieter fragen. Du kannst es dort verknüpfen, aber nutze einen typesafe-Anbieter, um es zu fragen.

Noch kein Teil von OpenBot fragt das Decision-Model.

Ohne Model arbeiten

Eine Organization ohne Anbieter kann ihre Sessions trotzdem lesen, umbenennen, forken und löschen. Sie kann keine neue Session erstellen, weil eine neue Session ein Model braucht. Siehe Ein Model auswählen.

Arbeit, die ein Modell braucht, schlägt mit dem Status 503 und dem Fehler SessionExecutionUnavailable fehl. Dazu gehört:

  • einen Turn starten mit POST /v1/sessions/:id/turns,
  • ein Model auswählen mit PUT /v1/sessions/:id/model,
  • einen Turn nach einer Entscheidung oder nach delegierter Arbeit fortsetzen.
{ "_tag": "SessionExecutionUnavailable", "session": "..." }

Eingebaute Models

Wenn du OpenBot aus deinem eigenen Programm startest, kannst du mit der Option builtinModels Models im Code angeben. Siehe Aus Code starten.

  • Jede Organization kann sie nutzen.
  • Die API kann sie nicht ändern.
  • Sie erscheinen in der Liste der Models neben den gespeicherten Anbietern.

builtinModels hat drei Felder:

Feld Was es ist
providers Die Anbieter, die erscheinen sollen, zum Beispiel { id, name }.
models Die Models, die erscheinen sollen, als Werte vom Typ Config.Model.
resolve Eine Funktion, die eine Auswahl bekommt und ein Effect mit einem LanguageModel zurückgibt.

Dieses Beispiel gibt ein Anthropic-Modell an, mit einem Schlüssel aus der Umgebung deines Programms:

import { NodeRuntime } from "@effect/platform-node"
import { Config, Model, Provider } from "@openbot-build/core"
import {
AnthropicProvider,
EnvironmentConfiguration,
Server,
} from "@openbot-build/openbot"
import { Context, Effect, Layer, Redacted } from "effect"
import { LanguageModel } from "effect/ai"
import { FetchHttpClient } from "effect/http"
const selection = Config.Selection.make({
provider: Provider.Id.make("house"),
model: Model.Id.make("claude-sonnet-4-5"),
})
NodeRuntime.runMain(
Effect.scoped(
Effect.gen(function* () {
const settings = yield* EnvironmentConfiguration.load
// Baue das Modell einmal. Es lebt so lange wie das Programm.
const layer = Layer.unwrap(
AnthropicProvider.make({ max_tokens: 4096 }).model(selection),
).pipe(
Layer.provide(
AnthropicProvider.layerClient({
apiKey: Redacted.make(process.env.ANTHROPIC_API_KEY ?? ""),
}),
),
Layer.provide(FetchHttpClient.layer),
)
const claude = Context.get(
yield* Layer.build(layer),
LanguageModel.LanguageModel,
)
const builtinModels = {
providers: [{ id: selection.provider, name: "House models" }],
models: [
Config.Model.make({
provider: selection.provider,
id: selection.model,
name: "Claude Sonnet 4.5",
reasoningEfforts: [],
serviceTiers: [],
}),
],
resolve: (requested: Config.Selection) =>
requested.model === selection.model
? Effect.succeed(claude)
: Effect.fail(
new Config.ModelConfigurationError({
reason: "model-not-found",
message: "This model is not available",
}),
),
}
return yield* Layer.launch(
Server.layer({ ...settings, builtinModels }).pipe(
Layer.provide(FetchHttpClient.layer),
),
)
}),
),
)

Wenn resolve fehlschlägt, gibt das Starten eines Turn 503 zurück. Der Einstiegspunkt für die Entwicklung nutzt builtinModels für seine Test-Modelle. Ein vollständiges Beispiel findest du in packages/openbot/src/app/DevelopmentModel.ts.

Anbieter-Module

Ein Anbieter-Modul macht aus einem Modell-Namen ein LanguageModel für eine Art von API. @openbot-build/openbot exportiert drei davon. Jedes hat dieselben Teile:

Teil Was er tut
make(defaults?) Erstellt einen Anbieter. defaults sind Request-Optionen für jeden Aufruf.
provider.model(selection) Gibt den Model-Layer für eine Auswahl zurück. Er schickt keine Requests.
layerClient(options) Erstellt den HTTP-Client. Gib apiKey an, und apiUrl für eine andere Adresse.
layer(tag, defaults?) Legt den Anbieter in einen Service, den du benennst.
Modul Anbieter-ID API
OpenAiProvider openai OpenAI Responses API. ResponsesCompaction fügt die eingebaute Kontext-Verdichtung hinzu.
OpenAiCompatProvider openai-compat OpenAI Chat Completions API und Dienste, die sie nachbauen.
AnthropicProvider anthropic Anthropic Messages API.

OpenBot nutzt diese Module auch für die gespeicherten Anbieter. Kontext-Verdichtung (Context Compaction) macht einen langen Verlauf kürzer, damit er in das Kontextfenster des Modells passt. Ein kurzes Beispiel für jeden Anbieter findest du unter Pakete.


Models für die Entwicklung

Der Server für die Entwicklung (devenv shell -- pnpm dev) ruft kein echtes Modell auf. Er nutzt stattdessen Test-Modelle. Sie geben jedes Mal dieselbe Antwort. So kannst du Clients ohne Kosten testen.

Die Anbieter-ID ist development. Die Model-IDs sind:

Model Was es tut
mock-text Der Standard. Gibt sofort einen festen Text zurück.
mock-slow Gibt Text langsam über zehn Sekunden zurück. Nutze es, um Streams und Unterbrechungen zu testen.
mock-tool Ruft ein harmloses Test-Tool auf, das auf einen Request wartet.
mock-app Ruft ein Tool der Test-App auf und gibt ihren echten Inhalt zurück.
mock-failure Schickt etwas Text und schlägt dann absichtlich mit einem Fehler des Modells fehl.

Die Token-Zahlen in ihren Antworten sind nicht echt. Um eins auszuwählen, speichere es in einer Session:

Terminal window
curl -X PUT -H "Authorization: Bearer $OPENBOT_TOKEN" -H 'Content-Type: application/json' \
-d '{"expected":1,"selection":{"_tag":"Config.Selection","provider":"development","model":"mock-slow"}}' \
https://localhost:8443/v1/sessions/$SESSION_ID/model

Der Einstiegspunkt für die Entwicklung gibt diese Models mit builtinModels an. So kann jede Organization der Platform für die Entwicklung sie nutzen. Das Programm für den Betrieb nutzt sie nie.