Zum Inhalt

Der Capability-Katalog

Alles, was ein Agent tun kann, stammt aus einer von zwei Quellen: aus einer Capability, die im Code dieses Deployments registriert ist, oder aus einem MCP-Server, den jemand verbunden hat. Diese Seite ist die erste Liste.

Eine Capability ist die Einheit, die sich zu- oder abschalten lässt — eine Zeile im Builder, ein Eintrag im Spec. Sie ist bewusst nicht „ein Tool“: Die Wissenssuche ist eine einzige Entscheidung für die Person, die einen Agent konfiguriert, und ob sie heute eine Funktion bereitstellt und nächsten Monat drei, ist nicht deren Problem. Capabilities decken außerdem Dinge ab, die gar keine Tools sind — deshalb stehen thinking und clock hier ohne aufgeführte Tools.

Die API ist maßgeblich, diese Seite ist eine Momentaufnahme

GET /api/v1/agents/capabilities liefert die Registry so aus, wie sie im laufenden Deployment vorliegt, einschließlich allem, was seit dem Schreiben dieser Seite hinzugekommen ist. Der Builder zeichnet seine Auswahl und seine Konfigurationsformulare aus dieser Antwort. Widersprechen sich die beiden, hat die API recht.

Der Builder benennt jede Capability in der Sprache der Konsole und erklärt sie in einfachen Worten: was sie den Agenten tun lässt, zwei oder drei Beispiele, was sie braucht und was sie nie tut. Die Namen unten sind die deutschen der Konsole. Fünf Capabilities, die nur einstellen, wie ein Run getragen wird - lange Gespräche, Bilder auslagern, Erinnerung an Anweisungen, Grenzen für Tool-Ergebnisse und Tool-Suche -, sind unter Erweitert eingeklappt, bis jemand den Bereich öffnet, sucht oder eine davon einschaltet.

In den Einstellungen einer Capability liegen Grenzwerte, Cache-Lebensdauern, Prompts und andere Feinabstimmung eingeklappt unter Erweiterte Einstellungen, die sich von selbst öffnen, wo eines davon gesetzt wurde.

Die Capabilities, die der Starter-Agent bindet - die Vorlage AgenticOS, die eine neue Organisation beim ersten Start bekommt - tragen das Abzeichen Empfohlen, und ein mit New agent angelegter Agent startet mit genau diesen eingeschaltet, so konfiguriert wie in der Vorlage, dazu mit ihren Instruktionen, Skills, Plattform-Werkzeugen und ihrem Budget. Sein Autor schaltet ab, was der Agent nicht tun soll.

Mit welchen Capabilities ein Agent auch endet: Jeder Run erfährt in seinen Instruktionen unter Your setup, welche er hat und welche nicht - so beschreibt er nur, was er kann, und sagt, was für den Rest eingeschaltet werden müsste.

Braucht eine Anfrage eine Capability, die dem Agent fehlt, zeigt er im Chat eine Karte dafür, mit seinem Grund. Wer den Agent bearbeiten darf, schaltet sie auf der Karte ein - gebunden, wie der Starter-Agent sie bindet, und geprüft, wie eine Veröffentlichung prüft. Sofort veröffentlicht wird nur, wenn der Entwurf nichts Unveröffentlichtes enthielt und die Person veröffentlichen darf; sonst wartet sie im Entwurf auf den Builder. Alle anderen bitten auf der Karte den Besitzer des Agents, und der Besitzer erhält eine Benachrichtigung, die die Toolbox des Agents öffnet.

Zwei davon hängen davon ab, was die Installation betreibt. Der Arbeitsbereich ist ein Container auf dem eigenen Sandbox-Dienst der Installation, den Bootstrap und die erste Veröffentlichung als Standard der Organisation registrieren; ohne ihn hält er nur Dateien. Der Webbrowser zeigt auf den Browser-Dienst der Installation (BROWSER_SIGN_IN_CDP_URL) und entfällt, wo es keinen gibt.

Was ausgeliefert wird

id Name Kategorie Tools Scope Schlüssel Sprachanruf
knowledge Wissenssuche knowledge search_documents knowledge:read — unverändert
skills Skills knowledge read_skill_resource knowledge:read — angepasst
context Kontext knowledge list_context, read_context — — unverändert
memory_files Gedächtnis knowledge list_memory, read_memory, write_memory, edit_memory, delete_memory — — unverändert
memory_mem0 Gedächtnis (mem0) knowledge remember, recall — erforderlich unverändert
conversation_search Frühere Gespräche knowledge search_conversations, read_conversation conversations:read — unverändert
web_research Websuche research web_search web:read für kostenpflichtige Dienste unverändert
web_fetch Webseiten lesen research web_fetch web:fetch — unverändert
browser_use Webbrowser research browse_web, browser_open, browser_read, browser_click, browser_type, browser_select, browser_scroll, browser_back web:browse über das Extra browser übergeben
code_execution Berechnungen analysis run_python code:execute — unverändert
sandbox Sandbox analysis ls, read_file, glob, grep, write_file, edit_file, execute sandbox:execute für Daytona übergeben
charts Diagramme analysis create_chart — — unverändert
visuals Visuals analysis draw, revise_visual, read_visual — — unverändert
image_generation Bilderzeugung analysis generate_image — erforderlich unverändert
text_to_speech Sprachausgabe analysis generate_speech — erforderlich unverändert
video_generation Videoerzeugung analysis generate_video — erforderlich übergeben
coding_agent Coding Agent analysis run_coding_agent sandbox:execute erforderlich übergeben
subagents Subagents reasoning task, check_task, wait_tasks, list_active_tasks, answer_subagent, send_message_to_subagent, soft_cancel_task, hard_cancel_task, create_agent, delegate agents:delegate — übergeben
planning Planung reasoning write_plan, read_plan, add_task, update_task_status, update_task_statuses, remove_task, add_subtask, set_dependency, get_available_tasks — — unverändert
thinking Nachdenken reasoning keine, mit Absicht — — abgebildet
system_reminders Erinnerung an Anweisungen reasoning keine, mit Absicht — — ohne Wirkung
tool_search Tool-Suche utility keine, mit Absicht — — entfällt
clock Datum und Uhrzeit utility keine, mit Absicht — — unverändert
guardrails Schutzregeln utility keine, mit Absicht — — auf Transkripten
compaction Lange Gespräche utility keine, mit Absicht — — ohne Wirkung
media Bilder auslagern utility keine, mit Absicht — — ohne Wirkung
tool_output_limits Grenzen für Tool-Ergebnisse utility read_tool_result — — unverändert
artifacts Apps utility publish_artifact, read_artifact — — unverändert
channel_tools Infos zum Chat-Kanal channels get_channel_info, list_channel_members, search_channels, read_channel_history, send_channel_file — — nicht verfügbar
ask_user Rückfragen utility ask_user_question — — auf dem Bildschirm
follow_ups Nächste Schritte utility suggest_next_steps, propose_setup — — nicht verfügbar

Sieben davon haben absichtlich keine Tools. thinking verändert, wie das Modell arbeitet, statt was es erreichen kann, clock schreibt das Datum in die Instruktionen, tool_search steuert seine Suchfunktion erst bei, sobald es ein Toolset umschließt, das zurückgestellte Tools enthält — für sich allein deklariert es nichts —, guardrails prüft und überschreibt den Text, der durch einen Run fließt, compaction schreibt die Historie um, die eine Anfrage mitführt, media schreibt um, wie eine verdichtete Historie gespeichert wird, und system_reminders hängt steuernden Text an das Ende der Anfrage.

Keine der sieben lässt etwas übrig, das eine Person genehmigen müsste, also deklariert auch keine ein Tool. Eine Capability, die wirklich keine Tools hat, sagt das mit tools=(), statt das Argument wegzulassen; siehe Eine Capability hinzufügen.

Diese Spalte zeigt, was eine Capability deklariert, und das ist nicht immer das, was einem Modell angeboten wird. Die Delegation ist die einzige Stelle, an der beides auseinandergeht: create_agent und delegate erscheinen nur unter allow_dynamic, und answer_subagent erscheint überhaupt niemandem — beides wird unten unter Delegation erklärt.

Die Spalte Sprachanruf zeigt, was eine Capability in einem Sprachanruf tut. Unverändert läuft ohne Änderung. Angepasst: Skills werden bei Bedarf als Instruktionen geladen, ohne ihr Lese-Tool. Entfällt: Die Tool-Suche gibt dem Anruf seine MCP-Tools direkt, weil die Tools eines Anrufs beim Verbinden feststehen. Abgebildet: Das Nachdenken wird zur eigenen Reasoning-Einstellung des Realtime-Modells. Übergeben läuft im eigenen Textmodell des Agenten, dem der Anruf eine Aufgabe übergibt (Arbeit übergeben); die Karte Stimme (Voice) im Builder listet diese für einen Agent auf, der sie hat, ebenso das Arbeiten während Tools laufen, das ein übergebenes Tool nur in den Hintergrund schickt. Auf dem Bildschirm stellt seine Fragen auf der Karte der Anrufansicht in /chat. Die Chat-Kanal-Abfrage ist nicht verfügbar, weil ein Anruf nie über einen Kanal geführt wird, und ebenso wenig Nächste Schritte, deren Schaltflächen ein Anruf nirgends zeigen kann. Ohne Wirkung kennzeichnet drei, die auf den Modellanfragen eines Text-Runs und deren gespeicherter Historie arbeiten, die ein Anruf nicht sendet.

Guardrails prüfen die Transkripte des Anrufs. Das Ergebnis eines Tools wird geprüft und blockiert, bevor der Agent es liest; die Prüfung, bevor ein Tool läuft, ist die Freigabe, für Tools, die eine brauchen. Guardrails prüfen die Argumente eines Tools nicht. Die Katalog-API führt, wie jede Capability an einem Anruf teilnimmt, im Feld voice: live auf dem eigenen Modell des Anrufs, hand_off oder never.

Eine davon steht gar nicht in der Toolbox. channel_tools wird pro gebundenem Bot unter Where this agent is available gewählt, und die Veröffentlichung lehnt einen Spec ab, der diese Capability zu führen versucht — siehe Chat-Kanal-Abfrage.

search_documents — Durchsucht die Dokumente der Organisation nach Passagen, die für eine Frage relevant sind.

Durchsucht die Collections, die der Spec des Agents bindet, und belegt, was es verwendet hat. Das Modell bestimmt, was gesucht wird, niemals wo: Die Collections werden vor dem Run aus dem Spec aufgelöst und der Capability übergeben, sodass ein Agent keine Collection erreichen kann, die ihm niemand zugeordnet hat.

Jedes Ergebnis nennt sein Dokument und die Stelle des Treffers darin. Ein PDF, eine Präsentation oder eine Textverarbeitungsdatei wie .doc, .odt oder .rtf nennt die Seite. Eine Markdown-Datei, eine Tabelle, eine Webseite oder ein Bild hat keine Seite, die ein Leser öffnen kann. Sie nennt nur den Dokumentnamen, ebenso wie Textdateien und .docx-Dateien. Chunks bewahren keine verlässlichen Abschnittsgrenzen. Eine Zeile mit einer Raute wird deshalb nicht als Überschrift interpretiert: Sie kann ein Kommentar in einem Codeblock sein, der im vorherigen Chunk begann.

Das entscheidet der Dokumenttyp, wenn das Ergebnis angezeigt wird, sodass eine früher ingestierte Collection keine erneute Ingestion braucht.

Konfiguration Standard Bereich
default_top_k 5 1–50
self_query_enabled false an / aus
query_analysis_mode off off, multi_query, hyde
query_analysis_max_variants 3 1–5
parent_context off off, window, parent

default_top_k greift nur, wenn das Modell nicht selbst eine Anzahl verlangt.

parent_context schaltet Small-to-Big-Retrieval ein. Treffer und Ranking laufen immer über die präzisen kleinen Chunks; diese Option entscheidet nur, wie viel umgebenden Kontext jeder Treffer zurückgegeben bekommt, zusammengesetzt auf dem Rückweg:

Modus Was das Modell erhält
off Nur der getroffene Chunk — der Standard, unverändertes Verhalten
window Der getroffene Chunk mit dem Chunk davor und danach im selben Dokument
parent Der getroffene Chunk mit so viel seines Dokuments darum herum, wie passt, den nächsten Text zuerst

Der getroffene Chunk wird nie gekürzt. Nur der um ihn herum hinzugefügte Text zählt gegen zwei feste Grenzen - 8.000 Zeichen pro Ergebnis und 24.000 pro Suche -, sodass der Modus dem Modell nie weniger zeigt als off. Eine Passage bleibt zusammenhängend: Sie wächst vom Treffer nach außen und endet am ersten Chunk, der nicht passt oder schon mit einem früheren Ergebnis zurückgegeben wurde, sodass Text, der im Dokument nicht benachbart war, nie zusammengefügt wird.

Chunks werden nach ihrer Position um den Treffer gelesen, nie das ganze Dokument, und die Erweiterung bleibt im eigenen Mandanten- und Collection-Scope des Aufrufers. Sie ändert nie, welche Chunks getroffen wurden, deren Scores oder Zitate; ein Zitat sagt with surrounding text, wenn die Passage über den genannten Chunk hinausreicht.

self_query_enabled schaltet Self-Query ein, standardmäßig aus. Läuft eine Suche ohne einen vom Modell selbst genannten Filter, liest ein LLM die Frage — „PDFs vom letzten Monat über Onboarding“ — und leitet die geschäftlichen Filter ab, die sie impliziert: eine Quelle, einen Dokumenttyp, eine Organisationseinheit, einen Datumsbereich. Die eigenen, ausdrücklichen Filter des Modells gewinnen immer; Self-Query füllt nur die Lücke. Werden abgeleitete Filter angewendet, beginnt das Ergebnis mit einer Zeile, die sie nennt, und das Modell kann die Suche mit infer_filters=false ohne sie wiederholen.

Das abgeleitete Objekt ist derselbe validierte Filter, den ein Aufrufer liefert, trägt also kein Tenant- oder Autorisierungsfeld und kann den Zugriff nicht erweitern — es kann nur innerhalb des eigenen Tenants und der Collections des Agenten einschränken. Eine Organisationseinheit bleibt nur erhalten, wenn die gebundenen Collections sie tatsächlich tragen, gelesen im selben Bereich wie die Suche; Collections mit zusammen mehr als 200 Einheiten bieten keine zur Ableitung an. Eine Dokument-ID wird nie abgeleitet. Eine leere oder fehlgeschlagene Ableitung sucht ungefiltert innerhalb dieses weiterhin erzwungenen Bereichs.

Kosten: Jede Suche, die das Modell ohne eigene Filter ausführt, stellt eine zusätzliche Modellanfrage für die Ableitung (zwei, wenn deren Ausgabe korrigiert werden muss). Sie läuft auf dem eigenen Modell des Agenten, wird dem Lauf wie jede andere Anfrage angerechnet und vor dem Senden abgelehnt, wenn das Budget bereits ausgeschöpft ist. Sie wird wie die eigenen Anfragen des Agenten getraced, sodass ein Agent, der keine Inhalte aufzeichnet, die Frage auch hier aus seinen Traces heraushält.

Ohne gebundene Collections steuert diese Capability nichts bei — sie wird gar nicht erst angehängt. Ein Suchtool, das immer leer zurückkommt, ist schlimmer als gar kein Suchtool, denn das Modell versucht es weiter und schließt aus dem Schweigen.

Abfrageanalyse und -erweiterung

Kurze, unterspezifizierte oder im Vokabular abweichende Fragen finden zu wenig. Standardmäßig aus, erweitert query_analysis_mode die Abfrage optional vor dem Abruf:

Modus Was er tut Kosten
off Sucht die Abfrage so, wie sie geschrieben wurde keine
multi_query Das Modell des Runs schreibt bis zu query_analysis_max_variants Umformulierungen; das Original und die Varianten werden je einzeln gesucht und ihre Ergebnisse zusammengeführt ein Modellaufruf, plus ein Abruf je Abfrage
hyde Das Modell des Runs schreibt eine kurze hypothetische Antwort, und der Abruf läuft gegen deren Embedding ein Modellaufruf

multi_query und hyde fügen je einen Modellaufruf vor der Suche hinzu und tauschen so Latenz und ein wenig Ausgabe gegen bessere Trefferquote bei unscharfen Fragen. multi_query ruft außerdem einmal je Abfrage ab, eine nach der anderen, und jeder Abruf bettet seine eigene Abfrage ein — die Varianten werden nicht in einem Embedding-Aufruf gebündelt —, also halte query_analysis_max_variants niedrig, um die Auffächerung zu begrenzen.

Beide Modi nutzen das eigene Modell des Agents — es gibt kein separates Modell zu konfigurieren — und ihre Kosten werden gegen das Budget des Runs gemessen wie jeder andere Modellaufruf. Ein erschöpftes Budget überspringt die Erweiterung, ohne das Modell aufzurufen, und ebenso ein Modell, das scheitert oder keine einfache Anfrage beantworten kann: Die Suche läuft dann mit der Abfrage, wie sie geschrieben wurde, statt zu scheitern.

Erweiterung erhöht die Trefferquote, niemals den Zugriff. Jede von ihr erzeugte Abfrage wird unter demselben Mandanten-Scope und denselben Geschäftsfiltern wie das Original gesucht, sodass eine erweiterte Abfrage niemals ein Dokument einer anderen Organisation oder außerhalb des Scopes erreichen kann. Sie fügt sich mit dem Reranking zusammen: Erweiterung verbreitert die Kandidatenmenge und die Ergebnisse werden zusammengeführt, und ein Reranker würde umsortieren, was die Zusammenführung zurückgab. Mit eingeschaltetem parent_context wird der umgebende Text einmal an die zusammengeführten Ergebnisse angefügt, sodass seine Grenzen für die ganze Suche gelten und nicht für jede Abfrage einzeln.

Skills

read_skill_resource

Aufgeschriebenes Know-how, das der Agent nur lädt, wenn er es für relevant hält, und zwar einen Skill nach dem anderen — die Alternative wäre ein Instruktionsfeld, das so lange wächst, bis jeder Run für jede Prozedur bezahlt. Siehe Skills dafür, was ein Skill ist und wie einer in eine Organisation gelangt.

Jeder gebundene Skill ist eine eigene Capability. Sein Name und seine Beschreibung stehen im Katalog, den das Modell in jeder Runde liest, und den Textkörper holt sich das Modell mit load_capability — dem Tool des Agent-Frameworks selbst, weshalb es nicht in der Liste oben steht und weshalb ein Spec es weder gewähren noch absichern noch umbenennen kann. read_skill_resource ist das einzige Tool, das diese Capability beisteuert, und es erscheint nur, wenn mindestens ein gebundener Skill eine Datei neben seinen Instruktionen mitbringt.

Das Tool stammt aus pydantic-ai-skills, sein Name und seine Formulierungen liegen also in fremder Hand. Ein Drift-Test vergleicht, was die Registry deklariert, mit den Tools, die dem Modell tatsächlich angeboten werden — das ist es, was den Tag meldet, an dem das passiert.

run_skill_script ist abgeschaltet statt freigegeben: Die Dateien eines Skills erreichen einen Run unter /workspace/skills/, wo das eigene execute der Sandbox sie unter den Grenzen des Betreibers ausführt, und ein zweiter Ausführungsweg wäre ein zweiter Satz Regeln, den man falsch machen kann.

Kontext

list_context, read_context

Der ständige Kontext einer Organisation, der in den Run gelegt wird, statt erfragt werden zu müssen — ein Glossar, eine Markenstimme, eine Eskalationsmatrix. Jede gebundene Datei trägt einen mode: Eine inject-Datei wird wörtlich in die Instruktionen eingefügt, sodass das Modell sie einfach kennt; eine link-Datei bleibt aus dem Prompt heraus und wird über read_context erreicht, sodass eine große oder selten benötigte Datei nichts kostet, bis das Modell sie für relevant hält. list_context meldet, was verfügbar ist, ohne die Inhalte. Die beiden Tools erscheinen nur, wenn eine Datei im Modus link gebunden ist; ein Agent, dessen Dateien alle inject sind, steuert Instruktionen bei und keine Tools.

Eingefügter Inhalt wird als Referenzmaterial gerahmt — abgegrenzt und mit einer Zeile eingeleitet, die dem Modell sagt, es als Information zu behandeln und nicht als Instruktionen —, weil der Inhalt einer Datei von einer Person geschrieben ist und das Modell wörtlich erreicht. Die Abgrenzung ist ein Best-Effort gegen einen versehentlichen Ausbruch: Ein Inhalt, der selbst ein schließendes </context-file>- oder </context-files>-Tag enthält, oder ein Name oder Format mit einem " darin, wird neutralisiert, damit er keinen Text in die vertrauenswürdigen Instruktionen zurückspülen kann. Es ist keine Sicherheitsgrenze — wer context:edit hält, kann weiterhin absichtlich einfügen. Inhalt ist Text: Ein Dokument, das durchsucht werden soll, gehört in eine Wissens-Collection, nicht hierher.

Ohne etwas Nutzbares gebunden — keine Dateien, oder nur link-Dateien mit abgeschaltetem Lese-Tool — steuert diese Capability nichts bei und wird nicht angehängt, genauso wie knowledge ohne Collections nicht angehängt wird. Dateien werden unter /api/v1/context verwaltet und per id an einen Agent gebunden (AgentSpec.context_ids).

Gedächtnis

list_memory, read_memory, write_memory, edit_memory, delete_memory

Notizen, die ein Agent über Unterhaltungen hinweg selbst führt, indiziert durch eine, die er selbst pflegt. Wo context eine Bibliothek ist, die eine Person verfasst und an viele Agents bindet, ist das Gedächtnis das eigene des Agents: Er schreibt mitten im Run über Tools hinein, und sonst schreibt hier niemand. Es wird nicht per id gebunden — die Capability zu aktivieren, gibt dem Agent seine Notizen.

MEMORY.md ist der Index, und er wird dem Agent bei jeder Anfrage gezeigt. Er ist eine gewöhnliche Notiz, die der Agent mit denselben Tools schreibt und bearbeitet wie jede andere, und die Capability fügt ihn in die Instruktionen ein, so wie eine gebundene Kontextdatei eingefügt wird. So begegnet der Agent dem, was er gespeichert hat, bevor er irgendetwas entscheidet, und öffnet eine gelistete Notiz mit read_memory, wenn die Zeile sagt, dass sie lesenswert ist — statt sich entscheiden zu müssen, ein Auflistungs-Tool aufzurufen, das ein leichteres Modell selten aufruft.

Wessen Notizen, und wer sie hören darf

Eine Notiz gehört entweder einer Person oder einem Gruppenchat, und ein Run berührt genau einen Speicher: den der jeweiligen Unterhaltung. Welcher das ist, wird serverseitig daraus abgeleitet, wer die Antwort hören wird, niemals aus dem Modell — deshalb nimmt kein Tool einen Scope entgegen und es gibt nichts, was der Agent falsch machen könnte.

  • Eins zu eins — Webchat, eine Direktnachricht — gehören die Notizen dieser Person, und niemand sonst liest sie jemals. Dieselbe Person erreicht aus beiden denselben Speicher: Ein verknüpftes Chat-Konto löst auf ihr Konto auf und nicht auf die Oberfläche, über die sie gekommen ist.
  • In einem Gruppenchat gehören die Notizen dem Chat, und alle im Chat lesen sie. Die eigenen Notizen der sprechenden Person sind dort nicht erreichbar: Was unter vier Augen aufgeschrieben wurde, wird nicht dort vorgelesen, wo ein ganzer Kanal zusieht.
  • Auf einem öffentlichen Widget oder in einem Embed gibt es niemanden, dem sich etwas zuordnen ließe, also gibt es keinen Speicher, und die Tools sagen das, statt irgendwohin zu speichern. Ein mit einem API-Schlüssel gestarteter Run hat auch keinen: Der Schlüssel handelt für die ausstellende Person, aber wer ihn hält, ist nicht diese Person.
  • Bei einem Zeitplan oder einem Ereignis-Trigger gibt es ebenfalls keinen Speicher, obwohl der Run als Ersteller des Triggers ausgeführt wird. Der Ersteller leiht dem Run seine Befugnisse, nicht seine Identität, also hört niemand zu und die Tools verweigern. Legen Sie einen Fakt, den ein geplanter Run braucht, in den Prompt des Triggers oder in eine gebundene Kontextdatei. Oder lassen Sie den Trigger als seinen Ersteller laufen: Ein Zeitplan liest und schreibt dann den Speicher des Erstellers, ein Ereignis liest ihn nur.

Es gibt keinen organisationsweiten Speicher. Es gab einen, und er wurde entfernt: Er war ein zweiter Mechanismus für das, was Kontextdateien bereits leisten — ständiges Wissen, das eine Person verfasst und an Agents bindet —, und eine Aufgabe mit zwei Mechanismen ist der Weg, auf dem die beiden sich widersprechen. Gedächtnis ist das, was der Agent gelernt hat; alles, was ein Mensch schreibt, gehört in den Kontext.

Ein Schalter, Allow personal memory, streicht den personenbezogenen Speicher vollständig, aus Compliance- oder Datenschutzgründen; die in Gruppenchats geführten Notizen bleiben.

Was eingefügt wird, und was nur abgerufen wird

Ein Tool-Ergebnis ist etwas, das ein Modell abwägt; die Instruktionen sind das, was es befolgt. Deshalb erreicht der Index den Prompt nur dort, wo sein Inhalt niemanden außer der lesenden Person hätte steuern können: In einer Eins-zu-eins-Unterhaltung wird er eingefügt, in einem Gruppenchat bleibt er über read_memory erreichbar und wird nie eingefügt. Die Notizen eines Raums sind niemandem selbst zugeordnet, sonst käme der Satz einer Kollegin im selben Kanal als Instruktion eines Kollegen an.

Ein Index, der größer ist als etwa 6.000 Zeichen, wird weggelassen statt gekürzt. Ein halber Index — mitten in einer Zeile, mitten in einem Dateinamen endend — ist schlimmer als gar keiner.

Lesen und löschen

Niemand blättert kraft einer Organisationsrolle durch die Notizen eines anderen. Das war die gesamte frühere Antwort - Löschen und überhaupt keine Liste - und sie war halb richtig: eine Liste ist beim Speicher einer Kollegin ein Überwachungsmittel und beim eigenen das Gegenteil davon. Die Antwort hat deshalb jetzt drei Teile (#1594).

Den eigenen, immer, unter Einstellungen → Gedächtnis. Kein Recht sichert das ab, denn die Antwort ist für eine Viewerin dieselbe wie für eine Ownerin: was die Agenten hier über Sie aufgeschrieben haben, über alle Agenten hinweg, mit dem Agenten, der die jeweilige Notiz schrieb, und dem Zeitpunkt. Drei Dinge können Sie mit einer Notiz tun:

Nicht mehr verwenden Die Notiz wird nicht mehr gelistet, gelesen oder von einem Tool bearbeitet, erreicht das Modell also nicht mehr - und existiert weiter, zum Nachlesen und Wiederherstellen. Die mittlere Antwort für eine Notiz, die falsch oder zu persönlich ist und bei der Sie noch nicht sicher sind, ob sie weg soll.
Wieder verwenden Stellt sie wieder her.
Löschen Weg.

Der Name einer stillgelegten Notiz bleibt vergeben, ein Agent, der ihn erneut schreibt, belebt die Zeile also mit neuem Inhalt. Das hebt die Stilllegung nicht auf: was Sie stillgelegt haben, wird überschrieben, und die Zeile hält etwas, das der Agent seither gelernt hat. Die Alternative - ein dauerhaft unbrauchbarer Name - wäre ein Speicher, der stumm den Dienst verweigert.

Der Index zieht mit. MEMORY.md wird in die Instruktionen jeder Anfrage eingefügt; eine von Ihnen gestoppte Notiz, deren Indexzeile sie weiterhin beschreibt, erreicht das Modell also weiterhin. Eine Notiz stillzulegen oder zu löschen entfernt daher die Indexzeilen, die sie benennen. Eine Zeile, die die Notiz beschreibt, ohne sie zu benennen, bleibt - das Kürzen ist zeilenweise und am Namen orientiert, denn genau das ist der Index - und das Wiederherstellen setzt keine Zeile zurück: den Index schreibt der Agent, und dies ist nicht der Ort, in seiner Stimme zu formulieren.

Den einer anderen Person, nur eine Deployment-Administratorin. GET /memory/person/{id} mit Nennung des Tenants, und für alle anderen abgelehnt: nicht für eine Ownerin, nicht für eine Admina, nicht für jemanden mit einem Edit-Grant auf dem Agenten, der die Notiz schrieb. Die Begründung ist die obige - eine Organisationsrolle ist nicht die Stelle, die ein Auskunftsersuchen erreicht, und die Administratorin des Deployments, die ohnehin Konten über Tenants hinweg verwaltet, ist es. Der Zugriff steht mit Akteurin, Tenant, Person und Begründung im Audit-Trail, und ohne Inhalt: ein Eintrag, der festhielte, worauf er gesehen hat, wäre eine zweite Kopie des Geschützten.

Löschen, unverändert. Eine Person räumt aus ihrem eigenen Profil alles weg, woran ein Agent sich über sie erinnert, und eine Administratorin mit members:manage kann das für jemand anderen tun; beides löscht die Zeilen hier und die zugehörigen Erinnerungen in mem0 bei jedem Agenten, der es bindet. Das Gedächtnis eines Agenten vollständig zu leeren steht in seinem Werkzeugkasten, neben der Capability.

Was die Selbstauskunft nicht erreicht. Ein an mem0 gebundener Agent hält seine Erinnerungen im Dienst eines Dritten, und diese Seite listet sie nicht - die API von mem0 beantwortet, was zu einer Frage passt, nicht was ein Speicher enthält. Solche Agenten werden auf der Seite genannt statt weggelassen, denn eine Liste nativer Notizen, als vollständiges Inventar präsentiert, wäre schlimmer als eine, die sagt, was ihr fehlt. Das Löschen erreicht mem0; das Lesen nicht.

Das Löschen des Speichers prüft Entwürfe und alle aufbewahrten Agentenversionen, auch frühere mem0-Endpunkte. Das Leeren eines Agenten entfernt außerdem seine mem0-Namensräume für alle Besitzer. Fehlende Zugangsdaten, Anbieterfehler oder unvollständige Listen führen zu einem Fehler. Stellen Sie den Zugriff wieder her und wiederholen Sie den Vorgang, um die Löschung abzuschließen.

Gedächtnis (mem0)

remember, recall

Semantisches Gedächtnis, das in einem mem0-Dienst gehalten wird — in der Cloud oder selbst gehostet über base_url — statt in diesem Deployment. remember hält einen kurzen, in sich geschlossenen Satz fest; recall findet diejenigen, um die es in einer Frage geht, dem Sinn nach statt dem Namen nach. Es braucht einen API-Schlüssel aus dem Vault der Organisation.

Welche Erinnerungen ein Run erreichen kann, folgt genau der obigen Regel, denn mem0 bekommt den ganzen Scope als seine user_id: {org}:{agent}:{owner}. Ein mem0-Konto kann daher nicht die Erinnerungen zweier Organisationen, zweier Agents oder zweier Personen vermischen.

Zwei Unterschiede, die man vor der Wahl kennen sollte. Hier wird nichts gespeichert, deshalb erreicht das Löschen des Gedächtnisses einer Person mem0 über dessen eigene API und nicht über eine Zeile, die wir löschen. Und mem0 rechnet sein eigenes Embedding außerhalb ab, deshalb sieht das Ausgabenkonto des Deployments es nicht und ein Budget-Cap begrenzt es nicht.

Eine selbst gehostete base_url muss https sein und in MEM0_ALLOWED_HOSTS stehen. Eine leere Allowlist lehnt selbst gehostetes mem0 rundheraus ab, und das ist Absicht: Der Schlüssel reist in einem Authorization-Header, deshalb darf jemand, der einen geteilten Schlüssel binden, aber nicht lesen darf, ihn nicht auf einen eigenen Server richten können.

search_conversations, read_conversation

Findet eine frühere Unterhaltung danach, was darin gesagt wurde, und öffnet sie vollständig. Das Gedächtnis kann sich nur an das erinnern, was ein früherer Zug für aufschreibenswert hielt; alles andere wurde gesagt, gespeichert und war bis dahin unerreichbar — sodass „was haben wir zur Q3-Preisgestaltung entschieden“ mit „dazu liegt mir nichts vor“ beantwortet wurde, in einem Produkt, das den gesamten Austausch hält.

search_conversations liefert die am besten passenden Threads zurück, jeweils mit Titel, mit dem Zeitpunkt der letzten Aktivität, mit der Anzahl der passenden Züge und mit der stärksten Passage, in der die gefundenen Wörter fett gesetzt sind. read_conversation öffnet einen davon als Markdown, aufgeteilt in USER: und AI: in der Reihenfolge, in der die Züge geschrieben wurden, und benennt, wer gesprochen hat, wenn ein Raum mehrere Personen hat. Ein langer Thread kommt fensterweise, und die Antwort sagt, wie man das nächste Fenster anfordert.

Wessen Unterhaltungen

Die einer einzigen Person: derjenigen, der der Run antwortet. Drei Wege hinein, dieselben drei, die die Konsole erlaubt — Unterhaltungen, die ihr gehören, Unterhaltungen, die mit ihr geteilt wurden, und Kanal-Threads, an denen sie teilgenommen hat und in denen sie noch Mitglied ist, gegen die Chat-Plattform geprüft. Das Run-Log eines Triggers ist bewusst nicht dabei: Es ist ein Protokoll von Runs unter der Autorität einer anderen Person, und ein Agent, der im Auftrag einer Person sucht, hält keine ihrer Berechtigungen, mit der er es prüfen könnte.

Und nur dort, wo diese Person der einzige Zuhörer ist. In einem Gruppenchat verweigern beide Tools und sagen warum: Der Korpus ist persönlich, also würde eine Antwort daraus in einem Kanal die privaten Unterhaltungen einer Person allen im Raum vorlesen. Es ist dieselbe Linie, die der Gedächtnisindex zieht, eine Schicht weiter außen.

Ein Zeitplan oder ein Ereignis-Trigger antwortet niemandem, also verweigern beide Tools auch dort, aus demselben Grund wie die Gedächtnisdateien - und sie verweigern auch bei einem Trigger, der als sein Ersteller läuft: Der öffnet Gedächtnis und persönliche Verbindungen, nie aber die Unterhaltungen einer Person.

Wie gesucht wird

PostgreSQL-Volltextsuche — ein tsvector, den die Datenbank über jede Nachricht pflegt, ein GIN-Index, websearch_to_tsquery für die Anfrage und ts_rank_cd für die Reihenfolge. Zitierte "exakte Phrasen", or und ein führendes - zum Ausschließen eines Wortes funktionieren alle. Nicht ILIKE, das innerhalb von Wörtern trifft und nicht ranken kann; nicht Embeddings, denn das ist die Wissenssuche bereits und beantwortet eine andere Frage.

Wörter werden ganz und ohne Rücksicht auf Groß- und Kleinschreibung getroffen, aber nicht auf ihren Stamm zurückgeführt: meeting findet meetings nicht. Die Konfiguration ist in der Datenbank festgelegt, und english würde eine Sprache stemmen und jede andere verstümmeln — PostgreSQL bringt überhaupt kein polnisches Wörterbuch mit —, also wird die Gleichbehandlung aller Sprachen mit den Wortformen bezahlt. Die Tool-Beschreibung sagt das, damit ein Modell, das nichts findet, eine andere Wortform versucht, statt zu schließen, es sei nichts gesagt worden.

Ein Betreiber, der überhaupt nicht will, dass Agents Unterhaltungen lesen, gewährt den Scope conversations:read nicht, was es im gesamten Deployment abschaltet. Es gibt keine Einstellung, die den Korpus erweitert.

web_search — Durchsucht das öffentliche Web nach aktuellen Informationen.

Konfiguration Standard Werte
method duckduckgo duckduckgo, native, tavily, brave, exa
max_results 5 1–10, von native ignoriert

Die Konsole benennt jede Methode, statt den gespeicherten Wert auszugeben, und zeichnet die Marke des jeweiligen Dienstes daneben: Das Feld trägt x-enum-labels, und genau das liest das generierte Formular als Beschriftung. Ohne sie bot die Auswahl duckduckgo und exa in der Schreibweise an, in der sie gespeichert sind, was sich wie ein wiederzuerkennender Konfigurationsschlüssel liest statt wie ein Produkt, das man auswählt.

  • duckduckgo — kostenlos, kein Konto, Ergebnisse als anklickbare Quellen dargestellt.
  • native — der Modell-Provider sucht mit seinem eigenen Index und liefert seine eigenen Quellenangaben. Nur bei Modellen, die das unterstützen.
  • tavily — Ergebnisse, zusammengefasst für ein Modell zum Lesen.
  • brave — ein eigener Index.
  • exa — Suche nach Bedeutung statt nach Schlagwort.

Die drei kostenpflichtigen Methoden brauchen einen API-Schlüssel aus den Secrets der Organisation, benannt durch die secret_id der Bindung. Die Anforderung ist bedingt statt pauschal: Eine pauschale würde entweder den kostenlosen Standard hinter ein Konto sperren oder einen Tavily-Agent veröffentlichen lassen, der sich mit nichts authentifizieren kann und bei seiner ersten Suche scheitert.

Genehmigung und native lassen sich nicht kombinieren, aus dem Grund, den Webabruf weiter unten nennt: Das Genehmigungs-Gate umschließt die Tool-Ausführung, und eine native Suche wird vom Modell-Provider ausgeführt — deshalb wird eine Bindung, die für web_search eine Genehmigung verlangt und method auf native setzt, beim Veröffentlichen abgelehnt, statt ein Gate zu bekommen, das nie auslöst. Wählen Sie eine Methode, die dieses Deployment selbst ausführt, oder verzichten Sie auf die Genehmigungspflicht.

Die Suche findet eine Seite; sie liest keine. Das Lesen ist der Webabruf weiter unten, und er ist eine eigene Capability mit einem eigenen Scope.

Webseiten lesen

web_fetch — Liest die vollständige Seite unter einer URL, als Markdown.

Konfiguration Standard Werte
method local local, native, auto
max_content_chars 50000 1000–200000, von native ignoriert
allowed_domains — reine Hostnamen, die der Agent abrufen darf; null bedeutet beliebige
blocked_domains — reine Hostnamen, die er nie abrufen darf
  • local — dieses Deployment ruft die Seite ab. Der Standard, weil es die einzige Methode ist, die sich bei jedem Modell identisch verhält.
  • native — der Modell-Provider ruft sie ab, mit seinem eigenen Egress und seinen eigenen Quellenangaben. Nur bei Modellen, die das unterstützen; bei den übrigen wirft Pydantic AI einen Fehler.
  • auto — nativ, wo das Modell es hat, sonst überall local. Genau eine der beiden wird jemals angeboten, ein Run kann also nicht pro Aufruf zwischen ihnen wählen.

Der Abruf selbst ist das web_fetch_tool von Pydantic AI über dessen SSRF-geschütztes safe_download, und das ist der Grund, warum dies kein Code von uns ist.

Was aus einer Antwort wird, entscheidet sich hier:

  • Eine Seite (HTML, Markdown, JSON, reiner Text) kommt als Markdown zurück.
  • Ein Bild kommt als Bild zurück, für ein Modell, das Bilder liest.
  • Ein PDF oder ein Office-Dokument kommt als sein extrahierter Text zurück, gelesen vom selben Parser, durch den ein Chat-Anhang geht, und wie eine Seite bei max_content_chars abgeschnitten. Die Bibliothek würde die rohen Bytes zurückgeben, damit das Modell sie nativ liest. Ein Modell hinter einem OpenAI-kompatiblen Endpunkt, das das nicht kann, lehnt die ganze Anfrage ab (Unsupported chat content part type: 'file'), und der Agent ruft dasselbe Dokument dann erneut ab. Extrahiert wird nur der Text bis max_content_chars, und ein PDF wird höchstens 2.000 Seiten weit gelesen: Ein längeres endet mit einem Hinweis, der die Seite nennt, an der das Lesen stoppte, und wenn die gelesenen Seiten keinen Text hatten, kommt nur dieser Hinweis zurück.
  • Eine Binärdatei ohne lesbaren Text (ein gescanntes PDF, ein Archiv) erreicht das Modell als wiederholbarer Fehler, der nennt, was zurückkam.

Die URL kommt vom Modell und wird von innerhalb des Containers dereferenziert, deshalb reicht es nicht aus, sie vorab zu validieren — so wie es app.core.sanitize.validate_webhook_url für einen von jemandem übergebenen Callback tut. httpx löst den Hostnamen ein zweites Mal auf und folgt Redirects, ohne noch einmal zu fragen, deshalb kann ein Name, der vor einem Moment öffentlich antwortete, jetzt 169.254.169.254 antworten, und eine öffentliche URL kann auf eine solche umleiten.

safe_download heftet die aufgelöste Adresse an die Anfrage und validiert jeden Sprung erneut, die Domain-Filter eingeschlossen. Es begrenzt außerdem den Body während des Streamens und lehnt die Komprimierungsverfahren ab, die sich so nicht begrenzen lassen.

Private, Loopback-, Link-Local- und Cloud-Metadaten-Adressen werden abgelehnt, und die Ablehnung erreicht das Modell als wiederholbarer Fehler statt als leere Seite — eine Ablehnung in der Form eines Ergebnisses ist eine, um die das Modell herumantwortet, ohne zu sagen, dass es das musste. Der Bibliothek lässt sich sagen, dass sie lokale Adressen zulassen soll; hier legt nichts das offen.

Jeder dieser Fehlschläge ist, wie jeder andere (ein 403, eine Zeitüberschreitung), nur so lange eine Wiederholung, wie das Tool noch einen Versuch hat. Pydantic AI zählt Wiederholungen pro Tool, nicht pro URL, sodass die zweite Seite, die den Agenten abweist, sonst den ganzen Run beenden würde; beim letzten Versuch kommt dieselbe Meldung stattdessen als Ergebnis des Tools zurück, und das Modell wechselt zu einer anderen Quelle oder antwortet ohne sie.

Die Domain-Filter sind nicht die Sicherheitsgrenze — safe_download ist es

Sie treffen den Hostnamen exakt, ohne Wildcards und ohne implizite Subdomains, beantworten also welche Seiten dieser Agent lesen darf und nicht kann dieser Agent unser Netzwerk erreichen.

Ein Eintrag, der niemals treffen könnte, wird beim Veröffentlichen abgelehnt: eine Wildcard, ein Schema, ein Pfad, ein Port oder eine leere Allowlist. Jeder davon würde sonst eine Denylist still nichts verweigern lassen oder eine Allowlist still alles verweigern lassen.

Eine leere Denylist verweigert nichts, und genau das bedeutet es bereits, sie nicht zu setzen — sie wird daher als nicht gesetzt gelesen statt abgelehnt: Ein importierter Spec, der „keine verbotenen Hosts“ als [] schreibt, sagt damit etwas Wahres.

Ein Eintrag, der treffen kann, wird in der einen Schreibweise gespeichert, nach der DNS gefragt würde: klein geschrieben, ohne abschließendes Root-Label, IDNA-kodiert. Ein Name hat mehr als eine Schreibweise, und ein exakter Abgleich gegen eine davon ist ein Filter mit einem Loch darin — https://exämple.com/ erreicht den Vergleich so, wie es getippt wurde, sodass eine Denylist, die nur xn--exmple-cua.com hält, es durchlassen würde, während getaddrinfo die beiden identisch auflöst. Jede äquivalente Schreibweise wird dem Filter beim Aufbau übergeben; der Spec speichert eine.

Genehmigung und native lassen sich nicht kombinieren

Das Genehmigungs-Gate umschließt die Tool-Ausführung, und das ist die einzige Stelle, an der ein Aufruf angehalten werden kann — ein Abruf, den der Modell-Provider auf seiner eigenen Seite ausführt, erreicht sie also nie.

Eine Bindung, die für web_fetch eine Genehmigung verlangt und method auf native setzt — oder auf auto, wo es eine Eigenschaft des Modellprofils ist, welche der beiden läuft, und sich ohne erneutes Veröffentlichen ändert —, wird beim Veröffentlichen abgelehnt, statt ein Gate zu bekommen, das still niemals auslöst.

Setzen Sie method auf local, oder verzichten Sie auf die Genehmigungspflicht. Beides sind legitime Agents, und welcher davon gewollt ist, ist keine Entscheidung, die man stellvertretend für den Autor trifft.

Eine Version, die vor dieser Ablehnung veröffentlicht wurde, wird erneut abgelehnt, wenn sie zusammengebaut wird, denn nichts validiert eine eingefrorene Version noch einmal. Ein solcher Agent läuft also nicht weiter, bis er bearbeitet wird, statt weiterhin ohne Genehmigung abzurufen.

Eine Seite kommt als Markdown an, bei max_content_chars abgeschnitten; ein PDF oder ein Bild kommt als Binärinhalt an, den das Modell nativ liest. Nichts fasst es zusammen — was mit einer Seite zu tun ist, gehört in die Instruktionen des Agents.

Webbrowser

browse_web — Eine Aufgabe in einem echten Webbrowser erledigen, Schritt für Schritt.

Eine Aufgabe und eine Start-URL. Die Capability öffnet die Seite in einem Chromium, den Sie betreiben, und wiederholt drei Dinge: die Seite in eine nummerierte Tabelle der Elemente lesen, auf die ein Mensch reagieren könnte, das Modell des Runs um einen Schritt bitten, den Schritt ausführen. Ein Schritt beurteilt, ob die letzte Aktion gewirkt hat, hält ein kurzes Gedächtnis für den Rest der Aufgabe, sagt, wozu die nächste Aktion dient, und nennt genau eine Aktion: klicken, tippen, auswählen, scrollen, eine URL öffnen, zurückgehen, warten, mit einer Antwort enden, einen Menschen bitten oder melden, dass die Seite blockiert.

Greifen Sie zu ihr, wenn eine Seite bedient werden muss — ein Formular, ein Filter, eine Anmeldung, für die Zugangsdaten vorliegen, eine Suche, deren Ergebnisse einen Klick brauchen, ein Ablauf über mehrere Seiten. Für eine Seite, die nur gelesen werden muss, ist Web-Abruf schneller und ohne Nebenwirkungen.

Oder der Agent steuert ihn selbst. browser_open, browser_read, browser_click, browser_type, browser_select, browser_scroll und browser_back sind derselbe Tab in den Händen des Agents: eine Aktion je Aufruf, gewählt vom Modell des Runs in seinem eigenen Zug, und jede antwortet mit der Seite danach - Adresse, nummerierte Elemente und Text. Keine zweite Anfrage pro Schritt, und der Agent sagt zwischen den Aktionen im Chat, was er tut. Die Schritte erscheinen in einem Live-Fenster unter der Nachricht des Agents, dem, das das erste browser_open geöffnet hat, und eine Person kann den Tab zwischen zwei beliebigen Schritten übernehmen. Gemessen mit Claude über OpenRouter: ein Schritt von browse_web dauert 3,5-5 s, fast ganz die eigene Modellanfrage des Schritts; eine Aufgabe mit zwei Klicks, die der Agent selbst steuerte, dauerte 13 s, mit browse_web etwa 20. Die Tools eignen sich für kurze Aufgaben oder solche, die jemand verfolgen will, browse_web für eine lange Aufgabe, die ganz abgegeben wird.

Eine Aktion nennt eine Zeile, nie einen Selektor. Jedes Element, auf das das Modell wirken kann, ist eine Zeile einer Tabelle, die auf dem Server aus dem lebenden DOM gebaut wird, also kann eine Seite keine Aktion anbieten, indem sie eine beschreibt. Das ist nicht dasselbe wie sicher: „Konto löschen“ ist eine Aktion, die eine Seite wirklich anbietet, deshalb ist die Capability side_effecting, und browse_web lässt sich hinter eine Freigabe stellen.

Standardmäßig wartet sie nicht auf eine Freigabe, wie jedes Tool. Eine Freigabe für ein Browsen kommt, bevor die erste Seite geladen ist, auf eine Aufgabe in natürlicher Sprache, und bittet damit um Zustimmung zu Aktionen, die noch niemand sehen kann. An ihre Stelle tritt das Zusehen: Die Konsole zeichnet das Browsen live, jeder Schritt nennt, was er gedrückt hat und wozu, und allowed_domains begrenzt, wohin es überhaupt darf. Wer die Sperre will, setzt tool_approval am Binding.

Ein Schrittlimit ist eine Pause, kein Ende. Der Tab bleibt für den Rest des Turns offen. Ein Browsen, das max_steps erreicht, sagt das zusammen mit dem, was es gelernt hat, und der Agent macht weiter, indem er browse_web erneut ohne start_url aufruft — auf derselben Seite, angemeldet, mit demselben Gedächtnis. Ein Aufruf mit start_url beginnt eine neue Aufgabe im selben Tab. Vier Ergebnisse, alle gewöhnlich: erledigt, von der Seite blockiert, am Schrittlimit pausiert, und der Browser war nicht erreichbar. Das letzte ist eine Antwort, kein gescheiterter Run: Der Agent erfährt, dass der Browser nicht antwortet, und sagt es, zusammen damit, wer es beheben kann.

Ein Mensch kann den Browser übernehmen. Jedes Browsen eines Runs hat in der Konsole eine Live-Ansicht unter /browse/{run_id}/{call_id}: die Seite, wie sie gerade ist, und die bisherigen Schritte. Die Karte im Chat verlinkt sie, und in Slack, Telegram und Mattermost postet der Schritt den Link. Die Person, für die der Run handelt, oder wer den Agent bearbeiten darf, kann übernehmen: Der Agent pausiert nach seinem laufenden Schritt, und die Seite gehört dieser Person — klicken, tippen, scrollen —, bis sie sie zurückgibt; der Agent macht auf der Seite weiter, wie sie sie hinterlassen hat. Mitglieder, die Runs sehen dürfen, können zusehen, aber nicht steuern. Was jemand tippt, geht an die Seite und wird nirgends gespeichert; der Verlauf des Runs hält nur fest, dass jemand übernommen hat.

Die Seite nimmt die Größe des Fensters an, das sie zeigt. Hat jemand, der den Durchlauf steuern darf, ihn geöffnet, legt der Browser die Seite zwischen den Schritten für dieses Fenster an - das eigene Layout der Seite für diese Breite, in der Dichte dieses Bildschirms -, sodass die Live-Ansicht die Seite ist und kein in eine Spalte verkleinerter Laptop-Bildschirm. Das Modell sieht dann dieselbe Seite in dieser Größe.

Eine schon erledigte Aufgabe geht beim zweiten Mal schneller, für dieselbe Person. Wenn ein Browse, der für eine Person handelt - ihr eigener Chat, ihre Direktnachricht, ihr eigener Zeitplan - fertig wird, werden die Schritte, die dorthin geführt haben, nur für diese Person gespeichert: die Adresse und die Beschriftung von allem, was gedrückt wurde, nie das Getippte (die Aufzeichnung endet am ersten ausgefüllten Feld). Fragt sie dieselbe Aufgabe vom selben start_url erneut an, laufen diese Schritte ohne das Modell, solange die Seite sie noch anbietet, und das Modell übernimmt beim ersten Unterschied. Eine Wiederholung, die nicht fertig wird, wird verworfen. Niemand sonst in der Organisation liest sie, und Redis hält sie zwei Wochen nach der letzten Erledigung.

Wenn nur ein Mensch weiterkommt — eine Anmeldung ohne Zugangsdaten, ein Captcha, eine Entscheidung, die jemand anderem gehört —, bittet das Modell um einen Menschen, statt aufzugeben. Karte und Kanal-Thread sagen, dass der Agent einen Menschen braucht, und wofür. Das Browsen wartet bis zu zehn Minuten auf eine Übernahme und endet blockiert, wenn niemand kommt; ein Mensch hält den Browser höchstens fünfzehn Minuten. Die Live-Ansicht liegt in Redis, braucht also eines, und bleibt eine Stunde nach der letzten Aktion des Browsens bestehen.

Konfiguration Standard Werte
cdp_url leer ein Chromium-DevTools-Endpunkt; Pflicht, und sein Host muss auf BROWSER_CDP_ALLOWED_HOSTS stehen
allowed_domains null Hosts, auf denen der Browser sein darf; Muster wie *.example.com erlaubt; null heißt keine Einschränkung im Web. Jede Anfrage, die ins Netz der Installation zeigt (private, Loopback-, Link-Local- oder Metadaten-Adresse, auch nach einer Weiterleitung), wird abgelehnt, außer ihr Host steht hier - tragen Sie ein internes Werkzeug ein, um es zu besuchen (und hinter BROWSER_EGRESS_PROXY auch in die Ausnahmen des Proxys)
max_steps 50 1–200; Schritte pro Aufruf, jeder eine Modellanfrage
vision false sendet dem Modell bei jedem Schritt auch einen Screenshot; das Modell muss Bilder lesen
candidate_cap 60 2–200; wie viele Elemente in einem Schritt angeboten werden dürfen
preview true sendet die Ansicht in den Chat, während das Browsen läuft
preview_width 1024 320–1920; die Breite der Ansicht und damit der Vorschaubilder
step_model_profile_id leer ein Modellprofil, das browse_web bei jedem Schritt statt des Modells des Agents fragt - ein günstigeres Modell macht einen langen Browse günstiger und nur dort schneller, wo sein Anbieter schnell antwortet (Claude Haiku über OpenRouter brauchte pro Schritt so lange wie Sonnet); leer heißt das Modell des Agents, das seine eigenen Browser-Tools immer nutzen
connection built_in built_in, proxy oder hosted: wie der Browser ins Netz geht
proxy_url leer bei proxy: http://, https:// oder socks5:// mit Host und Port; das Secret des Bindings, falls vorhanden, ist sein username:password
hosted_url leer bei hosted: die wss://-CDP-Adresse des Dienstes; das Secret des Bindings ist sein Schlüssel und Pflicht
hosted_key_param apiKey wie der gehostete Browser seinen Schlüssel in der Query der Adresse nennt
timezone leer eine IANA-Zeitzone, die der Browser meldet - passend zur Region des Proxys
locale leer ein Sprach-Tag wie pl-PL, gesendet als Accept-Language und navigator.language

Den Browser betreiben Sie, und der Betreiber sagt welchen. Im API-Image ist kein Chromium: cdp_url zeigt auf einen Browser-Dienst, den ein Betreiber betreibt und isoliert. Die Compose-Dateien enthalten einen hinter dem Profil browser — docker compose --profile browser up -d browser, erreichbar als http://browser:9222. Der Host muss auf BROWSER_CDP_ALLOWED_HOSTS stehen, und eine leere Liste — der Standard — lehnt Browser-Automatisierung bei der Veröffentlichung ganz ab.

Der compose-Browser liegt in einem eigenen Docker-Netz, dem nur die API und der Worker beitreten, um ihn zu erreichen: Eine Seite, die er öffnet oder auf die er umgeleitet wird, erreicht weder die Datenbank noch Redis oder MinIO. Er erreicht weiterhin, was das Netz des Hosts erlaubt, etwa die Metadaten-Adresse einer Cloud - wo das zählt, sperre sie auf dem Host.

Ein Agent wählt, wie seine Browses ins Netz gehen (#2130), in den Einstellungen des Browsers selbst, mit demselben edit auf den Agent wie der Rest. connection ist built_in - der Browser, den cdp_url nennt, von der eigenen Adresse des Deployments - proxy oder hosted, und timezone und locale sagen, welche Zeitzone und Sprache der Browser meldet, gleich in welchem Modus.

Ein Proxy (proxy_url) schickt den Verkehr dieses Browsers über eine http://-, https://- oder socks5://-Adresse - eine private oder regionale - und meldet sich mit dem Secret des Bindings als username:password an, wenn es eines hat.

Einen http://- oder socks5://-Proxy bekommt der Browser unter der geprüften Adresse, sodass ein Name, der sich zwischen Prüfung und Verbindung ändert, ihn nicht ins Netz lenkt; ein https://-Proxy behält seinen Namen, auf den sein Zertifikat lautet.

Ein gehosteter Browser (hosted_url) verbindet sich stattdessen über wss:// mit einem Browser-Dienst - Browserbase, Steel oder einem eigenen - mit dem Secret des Bindings als Schlüssel, an die Query der Adresse unter hosted_key_param angehängt (standardmäßig apiKey).

Ein gehosteter Browser startet nie mit den gespeicherten Anmeldungen einer Person: Seine Adresse wählt der Autor des Agents, nicht ein Operator. Auch dort bekommt jeder Durchlauf einen eigenen Browser-Kontext, und ein Dienst, der ihn verweigert, wird abgelehnt: Sein Standardprofil gilt nicht als privat, und was ein Durchlauf dort schreibt - Cookies, Local Storage - überdauerte ihn.

Eine Adresse im Netz des Deployments wird beim Veröffentlichen und erneut beim Start eines Browses abgelehnt, es sei denn, der Operator hat den Host in BROWSER_CDP_ALLOWED_HOSTS eingetragen. Chromium kann sich nicht bei einem SOCKS-Proxy anmelden, also muss ein Proxy mit Passwort HTTP sein. Eine Website, die nach einem Passwort fragt, bekommt nie das des Proxys. Der Anmelde-Browser läuft immer im eigenen Browser des Deployments.

Der Compose-Browser wirkt wie ein Desktop-Browser. Ein vollständiges Chromium mit Fenster auf einem virtuellen Bildschirm (docker/browser), mit den Schriften eines Desktops und ohne die Flags, die einer Seite sagen, dass sie gesteuert wird - die Headless-Shell, die er ersetzt, hatte HeadlessChrome im User-Agent, und Seiten, die Bots ablehnen, lehnten sie sofort ab. Die Capability entfernt auch, was eine Seite noch über CDP lesen könnte: Sie schaltet die Domäne Runtime nie ein und ersetzt Headless im User-Agent, navigator.webdriver und einen WebGL-Renderer SwiftShader, wo ein Browser sie zeigt - so wirkt auch eine Headless-Shell, die ein Betreiber behält, wie ein Desktop-Browser.

Geprüft gegen die Bot-Erkennung von BrowserScan (Normal in jedem Punkt) und die Tabelle von Sannysoft (alles bestanden). BROWSER_TZ und BROWSER_LANG in der Umgebung legen fest, wo und in welcher Sprache er sich ausgibt; Standard sind UTC und en-US. Eine Bot-Prüfung ist keine Nutzungsbedingung: Was eine Seite an automatisiertem Zugriff erlaubt, liest der Betreiber, bevor er Agents darauf ansetzt.

Das ist eine Allowlist und nicht die SSRF-Prüfung, durch die andere vom Mandanten gelieferte URLs gehen, denn cdp_url steht in einer Spec, die jeder mit edit schreibt, und die SSRF-Sperre ist hier in beide Richtungen falsch: Sie lehnt den isolierten Dienst im eigenen Netz ab und lässt einen ins Internet offenen Debugger zu. Chromium beantwortet DevTools nur unter einer Adresse, also wird ein einfacher http- oder ws-Endpunkt mit Namen einmal aufgelöst und über seine Adresse angesprochen; ein TLS-Endpunkt behält seinen Namen für das Zertifikat.

Jedes Browsen bekommt einen eigenen Browser-Kontext, der am Ende des Runs verworfen wird. Auf einem von vielen geteilten Browser würde der Standardkontext ein Cookie behalten, das der Agent einer Person gesetzt hat, und der nächste Aufrufer käme als diese Person angemeldet an.

Jeder Schritt geht an das eigene Modell des Agenten. URL, Titel, die Beschriftungen der Elemente und bis zu 4.000 Zeichen sichtbarer Text der Seite, mit vision auch ein Screenshot — an denselben Anbieter wie das Gespräch und nirgendwo sonst. Ein vom Agenten getippter Wert wird nicht wiederholt: Der Verlauf hält fest, dass ein Feld gefüllt wurde, nicht womit. Jeder Schritt wird vor dem Senden gegen das Budget des Runs geprüft, also hält ein aufgebrauchtes Budget das Browsen an, statt den nächsten Schritt zu bezahlen.

cdp-use kommt mit dem Extra browser, das eine Standardinstallation nicht hat. Wer die Capability will, installiert agenticos[browser]; ein Agent auf einem Deployment ohne es scheitert an diesem einen Tool laut, mit der Installationszeile.

Specs vor Version 13, die den Browser Schritt für Schritt (browser_choice) gebunden haben, laden als diese Capability; Freigabe und Umbenennung von browse_page gehen auf browse_web über, die Einstellungen des Entscheidungsmodells und der TypeSafe-Schlüssel entfallen.

Berechnungen

run_python — Führt ein kleines Python-Programm aus, um etwas zu berechnen.

Eine eingeschränkte Sandbox ohne Netzwerk und ohne Dateisystem — deshalb sind Zeit und Speicher die einzigen Grenzen, die zu setzen sich lohnt.

Konfiguration Standard Bereich
timeout_secs 10 > 0, ≤ 120
max_memory_mb 256 16–4096

Pro Agent, nicht pro Deployment

Wer eine Grenze für einen datenintensiven Agent anhebt, sollte dafür weder einen Betreiber noch ein erneutes Deployment brauchen — und die Obergrenzen sind gedeckelt statt offen.

Sandbox

ls, read_file, glob, grep — Lesen. write_file, edit_file, execute — Schreiben und Ausführen.

Ein Workspace, der zwischen den Zügen überdauert. code_execution rechnet und vergisst; dieser merkt sich, und auf einem containergestützten Backend hat er eine echte Shell. Ein Agent, dem beides gewährt ist, rechnet mit dem einen und bewahrt seine Arbeit im anderen auf — was die übliche Paarung auf dem state-Backend ist, denn dieses hat gar keine Shell.

Konfiguration Standard Werte
backend state state, service
connection_id null eine registrierte Sandbox-Verbindung; null nimmt die Standardverbindung der Organisation. Nur service
session_scope conversation run, conversation, channel, user, agent
runtime null ein Alias, den der Dienst dieser Verbindung zulässt; nur service
include_execute true entfernt die Shell vollständig, wenn ausgeschaltet, statt sie zu gaten

Es gibt kein docker- oder daytona-Backend zur Auswahl. Wo eine Sandbox läuft, ist eine Eigenschaft der Verbindung, die ein Betreiber registriert hat — Sandboxes in der App —, also heißt die Verbindung zu benennen, die Art zu benennen. Beides getrennt zu wählen, machte es möglich, zwei Dinge zu wählen, die einander widersprechen.

Ein Deployment, das seinen eigenen Sandbox-Dienst betreibt - sandboxd, gestartet aus der Compose-Datei dieses Projekts, mit dem Token, das make sandbox-token erzeugt hat -, muss nichts registrieren. Wenn eine Organisation ohne Standardverbindung zum ersten Mal ihre Sandboxes auflistet oder einen Agenten ausführt, der eine braucht, registriert die Plattform This deployment's sandbox als Standard, im Namen des Eigentümers der Organisation, mit dem Token in dessen Tresor und einem Audit-Eintrag wie bei jeder anderen Verbindung. Auf dieselbe Weise bekommt der Architekt Dateien und Code, wenn er auf einem solchen Deployment installiert wird.

backend ist Infrastruktur; session_scope ist eine Richtlinie zur Datenteilung. Das Erste falsch zu setzen, kostet eine Funktion. Das Zweite falsch zu setzen, zeigt einer Person die Dateien einer anderen — es lohnt sich also, zweimal zu lesen:

Scope Wer den Workspace teilt
run Niemand — jeder Zug bekommt einen frischen
conversation Alle in diesem Chat. In Slack ist ein Thread ein Chat, Threads teilen sich also nichts
channel Jeder Thread in einem Kanal. Eine Direktnachricht hat eine eigene Chat-id, Personen bekommen also weiterhin eigene
user Eine Person, über jede Oberfläche hinweg, auf der sie diesen Agent erreicht
agent Alle, die mit diesem Agent sprechen, organisationsweit

conversation und channel gibt es als getrennte Antworten, weil eine Chat-Plattform daraus verschiedene Dinge macht. SlackAdapter faltet thread_ts in die Chat-id, deshalb bedeutet conversation in Slack einen Workspace pro Thread — fünfzig Threads in einem belebten Kanal sind fünfzig Container und ein 429 für die einundfünfzigste Person, die antwortet.

Der Scope im Spec ist der Standard. Jeder Kanal, in dem der Agent veröffentlicht ist, kann ihn überschreiben, auf der Exposure: Ein Agent, der im Webchat und auf einem Slack-Bot erreicht wird, ist ein Agent in zwei Situationen, und ein Wert für beide war die falsche Form. Der user-Scope ist das, was einen Workspace über Oberflächen hinweg trägt — dieselbe Person, die in Slack eine Unterhaltung fortsetzt, die sie im Webchat begonnen hat, findet dort ihre Dateien.

agent ist derjenige, der eine Grenze zwischen Personen überschreitet. Der Builder warnt am Feld, das Dateipanel benennt, wessen Workspace es ist, statt ihn „die Dateien dieser Unterhaltung“ zu nennen, und die Einstellung wird im Audit-Log festgehalten — denn wer eine Datei sieht, die er nicht angelegt hat, sollte herausfinden können, warum.

Das Backend oder die Verbindung zu wechseln, startet einen frischen Workspace, statt sich wieder an den alten zu hängen. Ein gespeichertes Dokument, das Volume eines Containers und eine Daytona-Sandbox sind drei verschiedene Dinge, und zwei sandboxd-Installationen sind zwei verschiedene Dinge — also bekommt jede ihren eigenen Workspace, und der vorherige bleibt, wo er ist, weiterhin gelistet und weiterhin lesbar. Einen laufenden Agent umzuziehen, ist daher kein Weg, seine Dateien mitzunehmen; der Agent findet auf dem neuen Host einen leeren Workspace vor. Da connection_id: null „die Standardverbindung der Organisation“ bedeutet, hat es denselben Effekt, eine andere Verbindung als Standard zu markieren, ohne dass sich ein Spec ändert.

Ein Spec wählt eine Verbindung und nie ein Image, einen Mount, einen Netzwerkmodus oder eine Obergrenze. Diese gehören denen, die das Deployment betreiben: Ein Spec wird im Browser von allen verfasst, die edit auf dem Agent halten, und einer, der ein Container-Image benennen könnte, könnte eines benennen, dessen Entrypoint den Host einhängt. runtime ist ein Alias, und der Builder bietet nur die Aliasse an, die der Dienst dieser Verbindung meldet — live gelesen, denn eine gespeicherte Kopie würde einen anbieten, den der Dienst inzwischen nicht mehr zulässt.

Was jedes Backend im Betrieb kostet:

Backend Braucht Shell Wo die Dateien liegen
state nichts nein diese Datenbank, gedeckelt bei SANDBOX_STATE_MAX_BYTES
service eine registrierte Verbindung ja ein Container auf diesem Host, oder Daytonas Cloud auf dem eigenen Konto der Organisation

Ein Betreiber kann sehen, was läuft: Sandboxes listet die offenen Sandboxes dieser Organisation auf ihrem Standard-Host mit ihren Runtimes, Leerlaufzeiten und Speicher, sowie das Aktivitätsprotokoll pro Sandbox. Siehe Konfiguration.

Die Veröffentlichung wird für einen service-Workspace abgelehnt, wenn die Organisation keine Verbindung registriert hat, wenn die benannte verschwunden ist oder wenn diese Verbindung keine Zugangsdaten hat — jeweils namentlich, denn alle drei sind Zustände, die ein Deployment nach der Veröffentlichung eines Agents erreicht, und die Behebung liegt bei einem Betreiber statt beim Autor.

Nur execute ist als nebenwirkend deklariert, und nichts fragt, bis ein Autor es hinter ein Gate stellt. Von den sieben wirkt nur das Ausführen eines Befehls auf die Welt ein: Ein Workspace ist Notizraum, der mit der Unterhaltung gelöscht wird, zu der er gehört, deshalb ist eine Datei darin zu schreiben nicht dieselbe Art von Handlung wie eine E-Mail zu senden — und ein Agent, der vor jedem write_file fragen muss, kann mehrstufige Arbeit gar nicht erledigen, was dazu führt, dass ein Autor das Gate ganz abschaltet und das eine verliert, auf das es ankam. execute führt beliebige Befehle auf dem Host von jemandem aus, deshalb ist es das, das ein Gate lohnt.

Eine Bindung, die das strengere Verhalten will, setzt es pro Tool: tool_approval: {"write_file": "required"}. Siehe Governance dafür, wie eine Genehmigung einer Person vorgelegt wird — und für die zwei Dinge, die eine einzelne Chat-Sitzung zusätzlich zum Spec sagen kann: für diese Unterhaltung auf jeden gegateten Aufruf verzichten, oder bei jedem Tool nachfragen, das der Agent hat, einschließlich der MCP-Tools, die das spec-gesteuerte Gate bewusst in Ruhe lässt (agenticos#925).

Manche Pfade werden abgelehnt, was auch immer die Genehmigungsrichtlinie sagt. Zugangsdaten (**/.env, **/*.pem, **/*.key, **/credentials*, **/.ssh/**, **/.aws/**) und der Systembaum (/etc/**, /usr/**, /proc/** und ihre Geschwister) können weder gelesen noch geschrieben noch bearbeitet werden — der Agent bekommt eine lesbare Ablehnung und kann weitermachen. grep wird gefiltert statt abgelehnt, denn ein Muster über / deckt legitimerweise den Workspace ab: Treffer innerhalb einer gesperrten Datei werden verworfen, sodass eine Suche keine Zeile daraus zurückgeben kann. Namen sind nicht geheim, deshalb zeigen ls und glob weiterhin, was da ist; nur die Inhalte werden vorenthalten.

Ein Befehl, der einen dieser Pfade nennt, wird ebenfalls abgelehnt, sodass cat /etc/shadow die Regel nicht dadurch umgeht, dass es ein anderes Tool fragt. Das ist Verteidigung in der Tiefe und keine Grenze, und der Unterschied zählt: Eine Shell erreicht eine Datei auf Wegen, die eine String-Prüfung nicht sehen kann, deshalb ist das, was die Ausführung tatsächlich sicher macht, die Isolation des Containers und der Netzwerkmodus des Betreibers. Es gibt keine Allowlist von Befehlszeichenfolgen, denn eine solche wird von sh -c ausgehebelt.

Und nichts davon ersetzt das Genehmigungs-Gate: Die Ablehnung hier ist das schlichte Nein des Codes, während execute, das eine Person fragt, die Entscheidung ist, die ein Betreiber verantwortet.

Dateien, die jemand an eine Nachricht anhängt, landen in /uploads — siehe Dateiverarbeitung.

Auch Skills werden zu Dateien. Ein Agent, der sowohl einen Workspace als auch Skills hat, bekommt jeden Skill als /workspace/skills/<name>/SKILL.md mit seinen Ressourcen daneben, und genau das macht das Skript eines Skills überhaupt ausführbar: Es liegt auf der Platte, neben der Shell, die es ausführen kann. Es gibt bewusst kein run_skill_script — execute hat bereits das Genehmigungs-Gate und die Obergrenzen des Betreibers hinter sich, und ein zweiter Ausführungspfad wäre ein zweiter Satz Regeln, den man falsch machen kann.

Diese Dateien sind beschreibbar, und was der Agent schreibt, wird nicht zu einem Skill. Ein Skill sind Instruktionen, denen jeder daran gebundene Agent bei jedem Run folgt, deshalb wird eine Änderung als Vorschlag festgehalten und jemand mit skills:edit nimmt sie an oder verwirft sie — siehe Skills.

Diagramme

create_chart — Zeichnet ein Diagramm aus Zahlen, die Sie bereits haben, damit der Nutzer sie sehen kann.

Stellt Zahlen dar, die das Modell bereits hat. Es ruft nichts ab, rechnet nichts und aggregiert nichts — paaren Sie es dafür mit code_execution oder knowledge. Keine Konfiguration.

Die Zahlen kommen als Spalten an — eine x_values-Liste für die Achse, eine values-Liste pro Reihe —, denn ein freiformatiges data-Argument ist nichts, was ein JSON Schema beschreiben kann, und ein Modell, dem ein Array von Objekten ohne deklarierte Eigenschaften gegeben wurde, schickte ein einzelnes leeres zurück.

Ein Diagramm ohne Inhalt ist jetzt nicht mehr ausdrückbar, statt lediglich abgelehnt zu werden. Eine Achse ohne Punkte, ein Diagramm ohne Reihen oder eine Reihe mit weniger Zahlen, als die Achse Punkte hat, kommen alle als Wiederholung zurück, die benennt, was fehlt.

Ein Rahmen, der um keine Daten gezeichnet ist, liest sich als „es gibt keinen Trend“ statt als Fehler — und er wird gespeichert und bei jeder Wiedergabe der Unterhaltung neu gezeichnet.

Bilderzeugung

generate_image — Erzeugt ein Bild aus einer schriftlichen Beschreibung.

Zeichnet ein Bild mit einem eigenen Bildmodell — getrennt vom Modell des Agents —, sodass es funktioniert, auf welchem Modell der Agent auch läuft. create_chart trägt Zahlen auf; dies zeichnet Bilder.

Konfiguration Standard Werte
provider openai Die Provider, deren Modellklasse das Bild-Tool unterstützt und einen API-Schlüssel entgegennimmt - heute zwei
model gpt-image-2 Die Modelle dieses Providers, aus app/core/catalog/image_models.json
quality Provider-Standard low, medium, high, auto
size Provider-Standard auto, 1024x1024, 1024x1536, 1536x1024, 512, 1K, 2K, 4K
background Provider-Standard transparent, opaque, auto
output_format Provider-Standard png, webp, jpeg
aspect_ratio Provider-Standard 16:9, 1:1, 9:16, …

Welche Provider zeichnen können, beantwortet das SDK, keine Liste. Model.supported_native_tools() ist eine Klassenmethode auf jeder Modellklasse, die Pydantic AI ausliefert, also fragt die Plattform danach: OpenAIResponsesModel, GoogleModel und GoogleModel über Vertex unterstützen ImageGenerationTool, sonst niemand, und ein Upgrade, das einem vierten das beibringt, braucht hier keinen Code. Die Bildmodelle von Together und Fireworks existieren tatsächlich und würden beim ersten Aufruf mit „not supported by this model“ scheitern, weshalb sie nicht angeboten werden.

Zeichnen zu können und konfigurierbar zu sein, sind zwei Fragen, und bei Vertex AI trennen sie sich. Die Capability versiegelt einen API-Schlüssel und baut jeden Provider damit, wo Vertex ein Dienstkonto will — ein Vertex-Eintrag wäre also eine Auswahlmöglichkeit, für die niemand Zugangsdaten liefern kann, und er entfällt zusammen mit den nicht zeichnenden. Ihn anzubieten, hieße der Capability provider-spezifische Formen von Zugangsdaten beizubringen, und das ist eine Änderung an der Capability statt eine am Katalog.

Welche Modelle jeder Provider anbietet, sind Daten, in app/core/catalog/image_models.json: eine id, ein Name und ein Satz dazu, wann man danach greift. Kein Listing-Endpunkt beantwortet diese Frage - /v1/models liefert Chat-Modelle -, deshalb ist ein heute Morgen erschienenes Modell ein Katalogeintrag statt ein Release. Ein Provider-Eintrag, den das SDK nicht ansteuern kann oder dessen Zugangsdaten diese Capability nicht bauen kann, wird beim Lesen der Datei verworfen - die Absicherung dagegen, dass in der Datei etwas Unzeichenbares oder Unkonfigurierbares wächst.

Die beiden Provider benennen das Bildmodell an verschiedenen Stellen, und der Katalog trägt auch das. Bei Google ist das gewählte Modell das Bildmodell. Bei OpenAI wird das Tool von einem Responses-Modell aufgerufen und zeichnet mit dem gewählten, deshalb benennt der Eintrag diesen Aufrufer und die Wahl reist als das eigene model des Tools. Keines von beidem ist eine Frage, die dem Autor gestellt wird.

Ein Spec, der vor dem Bestehen des Paares veröffentlicht wurde, läuft weiterhin. Früher war dies ein einziger aufgezählter String, der das SDK-Präfix trug (openai-responses:gpt-5.4), und ein gespeicherter Spec hält das noch. Gegen die beiden Felder gelesen, ist das ein unbekanntes Modell, deshalb normalisiert die Konfiguration die alte Form beim Einlesen: Das Präfix benennt den Provider, und ein Name, der der Aufrufer statt eines zeichnenden Modells ist, löst auf das erste Bildmodell dieses Providers auf. Nur dort, wo kein provider gespeichert war — eine Bindung, die einen benennt, sagt beide Hälften.

model entscheidet außerdem, zu welchem Provider der API-Schlüssel gehört. Der Schlüssel ist erforderlich — einen Agent zu veröffentlichen, der dies ohne Schlüssel bindet, wird abgelehnt — und kommt aus den Secrets der Organisation, benannt durch die secret_id der Bindung. Jede andere Einstellung ist optional; ungesetzt wendet der Provider seinen eigenen Standard an, sodass es genügt, die Capability einzuschalten, um zu erzeugen.

Sie hat Seiteneffekte. Ein Bild zu zeichnen, gibt echtes Geld auf einem Provider-Schlüssel aus und erzeugt Inhalte, die eine Person veröffentlichen kann, deshalb ist jeder Aufruf ein Kandidat für das Genehmigungs-Gate und kann pro Bindung gegatet werden.

Ihre Ausgaben werden erfasst. Das Bildmodell läuft als Subagent, dessen Verbrauch auf das Konto des Runs gebucht wird, sodass Bildkosten genauso gegen ein Budget zählen wie eine Modellanfrage. Bildmodelle sind in der Preis-Momentaufnahme oft nicht bepreist; in dem Fall verbucht der Run den Aufruf mit null und markiert seine Summe als unvollständig (cost_is_partial), statt die Ausgaben zu verbergen.

Wohin das Bild geht. Jedes erzeugte Bild wird pro Organisation gespeichert und über GET /api/v1/generated/{filename} zurückgeliefert, begrenzt auf die eigene Organisation der aufrufenden Person — eine weitere Grenze als bei einem Chat-Upload, das einem Nutzer gehört. Es wird bei der Conversation erfasst, in der es entstand, und mit ihr gelöscht, gegen das Kontingent der Organisation für erzeugte Medien gezählt und nach der Aufbewahrungsfrist gelöscht (Konfiguration). Über dem Kontingent antwortet das Tool das, bevor es etwas ausgibt. Wenn der Agent zusätzlich einen Workspace hat (die sandbox-Capability), wird dasselbe Bild dort unter /output abgelegt, sodass ein späterer execute-Schritt damit bauen kann — ein PDF, eine Folie, eine Seite zusammensetzen. Ein Agent ohne Workspace erzeugt und zeigt Bilder trotzdem; er hat nur nichts, womit er damit bauen könnte.

Sprachausgabe

generate_speech — Liest einen Text als Audioclip vor.

Liest einen Text über einen Sprach-Provider vor, den der Autor wählt, und verhält sich wie die Bilderzeugung: Der Clip wird gespeichert, im Gespräch als Player gezeigt und unter output/ abgelegt, wenn ein Workspace offen ist (#2259).

Konfiguration Standard Werte
provider openai openai, elevenlabs, google
model gpt-4o-mini-tts Die Modelle dieses Providers aus app/core/catalog/text_to_speech_models.json
voice die erste des Modells Die Voreinstellung, mit der der Agent spricht; er darf pro Clip innerhalb von voices wechseln. Eine Katalogstimme für OpenAI und Gemini; eine ID aus Ihrer eigenen Stimmenbibliothek für ElevenLabs
voices keine: jede Stimme des Modells Bis zu 20 Stimmen, zu denen der Agent pro Clip wechseln darf, jede als id und, für eine Kontostimme, eine note mit bis zu 120 Zeichen, die sagt, wer es ist. Leer erlaubt jede Stimme des Modells - bei ElevenLabs jede Stimmen-ID im Konto
output_format das erste des Providers mp3, wav (Gemini liefert nur wav)
max_characters 5000 1–10 000; der meiste Text, den ein Aufruf sprechen darf

Provider, Modelle, Stimmen und Preise sind Daten in der Katalogdatei. Ein Provider ist ein Secret-Zweck mit einem einfachen API-Schlüssel - ElevenLabs betreibt kein Chatmodell und ist deshalb ein Dienst im Vault statt ein Modell-Provider -, und ein Eintrag ohne Zweck oder mit einer API-Form ohne Client wird verworfen. OpenAIs ältere tts-1 und tts-1-hd sprechen mit neun der dreizehn Stimmen, deshalb werden Stimmen pro Modell aufgeführt.

ElevenLabs-Stimmen gehören zum Konto. Die gemeinsamen Standardstimmen laufen am 31.12.2026 ab, also führt der Katalog keine auf, und ein Binding nennt eine der eigenen Stimmen des Kontos per ID; Veröffentlichen ohne sie wird abgelehnt.

Die Stimme ist eine Voreinstellung; die erlaubten Stimmen sind die Grenze. voice ist die Stimme, mit der der Agent spricht, solange er keine andere verlangt, und voices nennt die einzigen übrigen, zu denen er wechseln darf - für einen Dialog oder für Erzähler und Figur. Leer erlaubt jede Stimme des Modells; bei ElevenLabs jede Stimmen-ID im Konto, geklonte eingeschlossen, wovor der Builder warnt. Die Stimmen erreichen das Modell als enum am Parameter voice des Tools, mit der üblichen als Voreinstellung und der Beschreibung jeder Stimme aus dem Katalog oder, für eine Kontostimme, der Notiz des Autors. Eine offene Kontostimme ist freier Text, dessen Form geprüft wird. Eine aufgelistete Stimme, die das Modell nicht hat, wird beim Veröffentlichen abgelehnt, und eine Stimme außerhalb der Liste wird beim Aufruf an das Modell zurückgelenkt.

Die Schlüsselauswahl zeigt die Schlüssel des gewählten Providers. Das Secret ist für jeden Provider erforderlich und als erforderlich deklariert, wenn provider einer von ihnen ist - das sagt dem Builder, dass der Provider der Zweck des Schlüssels ist.

Ein langer Text ist ein Clip. Ein Text über dem Limit des Modells pro Anfrage wird an einem Absatz, einem Satz oder einem Wort geschnitten und der Reihe nach gesprochen. MP3-Stücke werden aneinandergehängt; WAV wird von jedem Provider als PCM mit 24 kHz angefordert und in einen Header gepackt. instructions - Ton, Tempo, Akzent - erreichen die Modelle, die sie lesen (OpenAIs gpt-4o-mini-tts, Gemini), und entfallen bei den übrigen.

Das Budget wird mit dem Preis des Clips gefragt. Sprache wird pro Zeichen verkauft, also hält der Katalog einen datierten Preis pro Million Zeichen, und die Kosten sind vor dem Aufruf bekannt. Ein Run, den der Clip über eine Grenze tragen würde, wird abgelehnt; danach werden die Kosten gebucht, und bei einem Fehler mittendrin auch die bereits gesprochenen Stücke. Token-abgerechnete Modelle werden zu einem Preis pro Zeichen typischer Sprache geprüft. Gemini, das sein Audio als rohes PCM liefert, wird danach aus dem Audio gebucht, das jedes Stück zurückgab - Text-Tokens hinein und 25 Audio-Tokens pro Sekunde heraus -, sodass eine langsame Sprechweise kostet, was der Provider dafür abrechnet. Ein datierter Preis gilt ab seinem Tag - so ist die Verdopplung der Gemini-Preise am 01.01.2027 erfasst.

Gebucht beim Sprechen, ein Clip nach dem anderen. Jedes Stück eines langen Textes wird gebucht, sobald sein Audio ankommt, sodass ein Fehler oder ein abgebrochener Run mittendrin die zurückgekommenen Stücke trotzdem bucht. Die Budget-Prüfung reserviert nichts, also führt das Tool einen Aufruf nach dem anderen aus. instructions ist auf 1.000 Zeichen begrenzt, geht mit jedem Stück an den Provider und wird nicht bepreist.

Sie hat Nebenwirkungen, aus denselben Gründen wie die Bilderzeugung: Sie gibt Geld aus und erzeugt eine Aufnahme, die jemand veröffentlichen kann.

Fehler gehen an den, der sie beheben kann. Leerer Text, Text über max_characters oder vom Provider abgelehnter Text geht an das Modell zurück, ebenso ein Provider, der drosselt oder ausgefallen ist. Ein abgelehnter Schlüssel, eine unbekannte Stimme und ein ausgeschöpftes Budget werden als Ergebnis beantwortet, weil nur der Autor oder der Betreiber sie beheben kann; eine vom Modell gewählte Stimme, die der Provider nicht kennt, geht an das Modell zurück. Protokolliert wird nur der Fehlercode des Providers; sein Fehlertext erreicht weder die Logs noch das Modell.

Videoerzeugung

generate_video — Erzeugt ein kurzes Video aus einer schriftlichen Beschreibung.

Erzeugt einen kurzen Clip über einen Video-Provider, den der Autor wählt, und verhält sich wie die Bilderzeugung: Der Clip wird gespeichert, im Gespräch als Player gezeigt und unter output/ abgelegt, wenn ein Workspace offen ist (#2260). Er kann mit einem Bild beginnen - dem filename, den generate_image im selben Run zurückgab, oder einem Pfad im Workspace, wo ein erzeugtes Bild auch unter output/ liegt -, erkannt an seinen Bytes, nicht am Namen. Ein Dateiname allein erreicht nie die erzeugten Dateien der Organisation: Der Run muss das Bild selbst gemacht haben.

Konfiguration Standard Werte
provider google google, byteplus, xai, alibaba
model gemini-omni-1.1-flash Die Modelle dieses Providers aus app/core/catalog/video_models.json
resolution die erste des Modells Die Voreinstellung; der Agent darf pro Clip eine andere verlangen, bis max_resolution
max_resolution keine: jede, die das Modell rendert Die höchste, die der Agent verlangen darf; mindestens resolution
aspect_ratio das erste des Modells Die Voreinstellung; der Agent darf jedes Seitenverhältnis verlangen, das das Modell rendert
seconds 5 oder der nächste Wert des Modells Im Bereich des Modells; für Gemini Omni abgelehnt, das selbst entscheidet
max_seconds der längste Clip des Modells Der längste Clip, den der Agent verlangen darf; für Gemini Omni abgelehnt
audio Voreinstellung des Providers Die Voreinstellung, an oder aus; der Agent darf pro Clip beides verlangen. Abgelehnt, wo der Ton des Modells feststeht
alibaba_workspace keiner: der gemeinsame DashScope-Endpunkt Nur Alibaba: die Workspace-ID in Model Studio, ein Host-Label; Wan wird dann unter https://{workspace}.{region}.maas.aliyuncs.com/api/v1 aufgerufen
alibaba_region ap-southeast-1 Nur Alibaba: ap-southeast-1, us-east-1, eu-central-1, ap-northeast-1, cn-beijing, cn-hongkong
timeout_seconds 600 60–1800

Was jedes Modell erzeugen kann, sind Daten. Der Katalog nennt für jedes Modell den Längenbereich, die Seitenverhältnisse, die Auflösungen, ob seine Clips Ton haben und ob es mit einem Bild beginnen kann, und der Builder bietet nur das an. BytePlus ist ein Secret-Zweck im Vault; Google, xAI und Alibaba Cloud sind bereits Modell-Provider. OpenAI fehlt: Seine Video-API wurde am 24.09.2026 abgeschaltet.

Die Einstellungen sind Voreinstellungen; die höchste Auflösung ist die Grenze. Auflösung, Seitenverhältnis, Länge und Ton eines Bindings bekommt ein Clip, sofern der Agent nicht pro Aufruf etwas anderes verlangt. Die Länge endet bei max_seconds, das Seitenverhältnis bei dem, was das Modell rendert, und die Auflösung bei max_resolution: leer darf der Agent jede Auflösung des Modells verlangen, wovor der Builder warnt, weil die Auflösung den Preis einer Sekunde bestimmt. Das Budget wird mit dem Preis der gewählten Auflösung geprüft. Seitenverhältnis und Auflösung erreichen das Modell als enum mit der üblichen als Voreinstellung, und Ton wird nur angeboten, wo das Modell ihn optional macht.

Ein Clip ist ein Job, auf den gewartet wird wie auf ein Bild. Gemini antwortet in der Anfrage selbst; BytePlus, xAI und Alibaba nehmen einen Job an, der abgefragt wird. In beiden Fällen wartet der Aufruf eine Minute oder mehrere, höchstens timeout_seconds. Ein Job darüber hinaus wird abgebrochen, wo der Provider es erlaubt - bei BytePlus, solange er wartet -, und der Aufruf wird beantwortet statt wiederholt, weil xAI und Alibaba keinen Abbruch dokumentieren und den Job noch fertigstellen und abrechnen können. Der Clip wird heruntergeladen, bevor die Adresse des Providers abläuft, und der Schlüssel geht nie an einen Speicher-Host.

Das Budget wird mit dem Preis des Clips gefragt. Jeder Provider rechnet pro Sekunde ab, zu einem Satz, den die Auflösung bestimmt, also hält der Katalog einen datierten Preis pro Sekunde und Auflösung. Ein Run, den der Clip über eine Grenze tragen würde, wird abgelehnt; danach wird die Länge gebucht, die der Provider meldet. Gemini Omni bestimmt seine Länge selbst und meldet keine, also wird ein Clip dort zur längsten Länge des Modells bepreist und gebucht.

Sie hat Nebenwirkungen, aus denselben Gründen wie die Bilderzeugung, und stärker: Ein Clip ist der teuerste einzelne Aufruf, den ein Agent macht.

Fehler gehen an den, der sie beheben kann. Ein leerer Prompt, eine Länge oder ein Seitenverhältnis, die das Modell nicht macht, und ein Bild, das sich nicht lesen lässt, gehen mit einem Hinweis an das Modell zurück, ebenso ein Provider, der drosselt oder ausgefallen ist. Ein abgelehnter Schlüssel, ein aufgebrauchtes Budget, ein Prompt, den die Inhaltsrichtlinie des Providers abgelehnt hat, und ein Job über dem Zeitlimit werden als Ergebnis beantwortet: ein Wiederholungshinweis bei einer Ablehnung würde zu ihrer Umgehung einladen. Ein Job über dem Zeitlimit oder einer, der mit dem Lauf abgebrochen wird, wird mit seiner Schätzung gebucht, sobald seine Anfrage den Provider erreicht haben kann, weil der Provider ihn noch beenden und abrechnen kann. Ein Clip, der erstellt, aber nicht abgerufen werden konnte, wird ebenfalls gebucht und als Ergebnis beantwortet statt an das Modell zurückgegeben: ein zweiter Versuch würde erneut abgerechnet.

Coding Agent

run_coding_agent — Hand a software task to the coding agent, which works on it in the sandbox until done.

Übergibt Softwarearbeit an Claude Code oder Codex, die im eigenen Sandbox des Agent laufen. Ein gewöhnlicher Agent mit Shell kann Code bearbeiten; ein Coding-Harness kann es besser, und so erreicht ein Agent eines. Das Harness ist ein eigener Agent: Der Agent übergibt ihm eine ganze Aufgabe, es arbeitet, bis sie erledigt ist, und der Chat zeigt es bei der Arbeit. Ein anderer Agent erreicht einen Coding Agent über gewöhnliche Delegation, wie jeden Spezialisten.

Konfiguration Standard Werte
harness claude_code claude_code (ein Anthropic-API-Key oder ein Claude-Abo-Token), codex (ein OpenAI-API-Key oder eine ChatGPT-Anmeldung)
access edit edit ändert Dateien und führt Befehle aus; read_only liest den Code und antwortet (nur Claude Code)
model Standard des Harness Claude Opus, Sonnet oder Haiku 5.5 für Claude Code; GPT-5.6 Sol, Terra, Luna oder GPT-5.3 Codex für Codex
base_url direkt Anthropic Nur Claude Code: ein Gateway, das die API von Anthropic spricht, etwa https://openrouter.ai/api, dessen Key dann im Vault liegt. Entfällt, sobald Codex gewählt ist
timeout_minutes 15 1–60, pro Aufgabe

Ein Tool, und das Binding entscheidet, was es darf. Bei edit bearbeitet das Harness jede Datei und führt jeden Befehl im Sandbox aus, mit abgeschalteten eigenen Rückfragen (--dangerously-skip-permissions, --dangerously-bypass-approvals-and-sandbox): Niemand sitzt an seinem Terminal, und die Grenze ist der Container, für den beide Schalter dokumentiert sind. Bei read_only läuft Claude Code im Planmodus ohne jedes Tool, das schreibt - ohne seine Bearbeitungstools und ohne Shell - und liest mit Read, Grep und Glob; es weiß, dass nichts anderes freigegeben werden kann. Nicht einmal lesende Befehle: Keine Liste von Befehlspräfixen hält eine Shell schreibgeschützt, denn git log --output=<Datei> passt auf git log wie jeder Lesezugriff. Codex hat kein read_only: Sein Nur-Lesen-Modus ist ein eigener Sandbox, der im Container des Agent nicht startet, und das Veröffentlichen lehnt die Kombination ab. Ob ein Mensch vor jeder Aufgabe Ja sagt, ist die Freigabe durch einen Menschen der Capability, auf Immer fragen gesetzt — wie jede Sperre ist sie aus, bis jemand sie setzt. Es gibt keinen Tools-Tab: Das eine Tool ist die Capability.

Man sieht ihm bei der Arbeit zu. Das Harness wird im Sandbox im Hintergrund gestartet (claude -p --output-format stream-json oder codex exec --json), und seine Ereignisdatei wird alle anderthalb Sekunden gelesen. Jeder Schritt erreicht den Chat, während er geschieht, in einem eigenen Panel unter dem Zeichen von Claude Code oder Codex, wie die einer Delegation: die gelesene Datei, die Bearbeitung, der ausgeführte Befehl, was es dazwischen sagt. Am Ende enthält das Ergebnis des Aufrufs die Antwort, jeden Schritt und die geänderten Dateien. Bei einer Aufgabe über timeout_minutes und einer, die ein Mensch stoppt, wird die ganze Prozessgruppe gestoppt. Im Hintergrund heißt auch, dass eine Aufgabe nicht an der Obergrenze des Sandbox-Dienstes für einen Befehl abgeschnitten wird.

Die Aufgaben einer Unterhaltung setzen eine Sitzung fort. Die Sitzungs-ID des Harness liegt im Sandbox neben der Arbeit, unter .agenticos/coding-agent/, eine je Unterhaltung, sodass die zweite Aufgabe in einem Chat weiß, was die erste gelesen hat - und ein Chat setzt nie die Sitzung eines anderen fort, in einem Sandbox, den mehrere teilen: dem eines Projekts oder einem, der einer Person oder einem Agent gehört. Das Modell übergibt fresh_session, um neu zu beginnen, und eine Sitzung, die der Sandbox nicht mehr hat, beginnt von selbst neu, einmal.

Was es braucht. Die Capability sandbox auf dem Backend service mit erlaubten Shell-Befehlen — alles andere lehnt das Veröffentlichen ab — und eine Runtime mit installiertem Harness: Die Runtime workbench fixiert Claude Code 2.1.288 und Codex 0.160.0. Die Zugangsdaten kommen über die secret_id des Bindings aus dem Vault und erreichen den Befehl über seine Umgebung, nie auf dessen Befehlszeile. In einem Projekt, das seine Dateien ohne Container hält, gibt es keine Shell; dort wird der Coding Agent in Runs weggelassen, und der Agent erfährt, warum.

Ein Key oder ein Abo. Claude Code nimmt einen Anthropic-API-Key oder das Token, das claude setup-token für ein Claude-Pro- oder -Max-Abo ausgibt, als Key gespeichert — erkannt an seinem Präfix sk-ant-oat; es meldet sich nur bei Anthropic an, nicht bei einem Gateway. Codex nimmt einen OpenAI-API-Key oder eine ChatGPT-Anmeldung über Sign in with ChatGPT, dieselbe, die ein Modellprofil nutzt. Für eine Aufgabe wird die Anmeldung in eine auth.json in einem eigenen Codex-Verzeichnis dieser einen Aufgabe (CODEX_HOME) außerhalb des Workspace geschrieben und danach entfernt - zwei gleichzeitig laufende Aufgaben teilen sich nie eines, und die Sitzungen, die Codex fortsetzt, werden je Unterhaltung daneben gehalten. Von Codex erneuerte Tokens werden über dasselbe Secret zurückversiegelt. Alles, was Codex ausführt, kann diese Datei lesen, darum wird die Anmeldung nur für eine Person übergeben, die sie selbst im Vault sehen darf - den Agent auszuführen ist nie ein Weg zum Abo eines anderen -, und nur in einer Sandbox, die keine Aufgaben anderer teilen: Im Container eines Projekts, einer Sandbox auf Agent-Ebene oder einem jemandem zum Bearbeiten geteilten Chat läuft die Aufgabe einer anderen Person als derselbe Benutzer und könnte sie lesen. Ein API-Key ist etwas anderes: Er gehört dem Agent, wie das Token eines Bots in Slack, also nutzt ihn jeder Run, wer auch immer gefragt hat - und alles, was eine Aufgabe ausführt, kann ihn lesen. Binde einen Key, den alle teilen dürfen, die den Agent ausführen dürfen. Die Arbeit auf einem persönlichen Abo unterliegt den Bedingungen von Anthropic und OpenAI für diese Pläne, die das auf einem geteilten Deployment womöglich nicht erlauben.

Der Key passt zum Harness. Die Auswahl bietet Claude Code nur Keys an, die für Anthropic gespeichert sind, und Codex nur OpenAI-Keys und ChatGPT-Anmeldungen. Ein als custom gespeicherter Key passt zu beiden, ebenso der schon gebundene. Die Veröffentlichung lehnt einen Key ab, der für einen anderen Anbieter gespeichert ist, und nennt die, die das Harness annimmt: Ein OpenAI-Key an Claude Code würde veröffentlicht und dann bei jeder Aufgabe scheitern. Ist base_url gesetzt, gehört der Key dem Gateway - OpenRouter, LiteLLM oder einem anderen -, daher nimmt Claude Code dort jeden Key an.

An einen Coding Agent delegieren. Ein Delegierter, der sandbox selbst bindet, läuft in einem frischen Workspace im Speicher ohne Shell, sodass ein per Delegation erreichter Coding Agent sein Harness nirgends starten kann. Geben Sie dem delegierenden Agent die Capability sandbox auf einer Sandbox-Verbindung und nehmen Sie sandbox in Capabilities, die Delegaten erben der Delegation auf: Der Coding Agent arbeitet dann im Container des Elternteils, und die Dateien, die er schreibt, sind die, die der Elternteil sieht.

Seine Kosten werden gemessen. Beide Harnesses melden ihre Tokens pro Modell; sie werden wie jede Modellanfrage im Ledger des Runs gebucht, und das Budget wird vor jeder Aufgabe geprüft. Codex nennt in seinen Ereignissen kein Modell, daher wird seine Nutzung ohne gesetztes model ohne Preis gebucht und die Kosten des Runs als unvollständig markiert. Ebenso gebucht wird eine Aufgabe, die endet, bevor ihr Harness meldet, was sie verbraucht hat - fehlgeschlagen, abgelaufen oder gestoppt.

Visuals

draw — Zeichnet im Gespräch: ein Diagramm, eine Karte, einen Ablauf, eine Erklärung, eine 3D-Szene. revise_visual — Ändert ein Visual durch exakte Ersetzungen, ohne es neu zu zeichnen. read_visual — Liest ein Visual dieses Gesprächs, wie es jetzt ist, mit angewendeten Überarbeitungen.

Zeichnet im Chat selbst. kind ist svg für Diagramme und Illustrationen, mermaid für Flussdiagramme, Sequenzen und ER-Diagramme oder html für alles Interaktive - Schieberegler, <canvas>, three.js, eine Karte. Keine Konfiguration; der mitgelieferte Skill visual-design trägt den Stil und fünf Vorlagen, und der Builder bindet ihn, zusammen mit Charts, wenn Visuals eingeschaltet wird.

Es wählt zuerst die Form. Nicht jede Antwort ist eine Zeichnung. Der Text des Tools und der Skill sagen dem Modell, eine Tatsache als Text zu beantworten, genaue Werte als Markdown-Tabelle und Zahlen zum Ansehen mit create_chart - sodass „jetzt als Diagramm“ eine Tabelle in ein natives Diagramm verwandelt - und zu zeichnen, wenn die Person verstehen soll, wie etwas funktioniert, ein Werkzeug für den Moment nutzen oder Orten auf einer Karte folgen will. Der Skill sagt auch, wie eine Zeichnung aussehen soll, damit sie für die Antwort gemacht und nicht generiert wirkt: ein großes Thema, Raum statt Kästen, nur die Farben des Themes, keine Pills, Schatten oder Verläufe.

Eine Zeichnung kann das Gespräch fortsetzen. agenticos.followUp(text) in einer Zeichnung bittet darum, die Wahl der Person als ihre nächste Nachricht zu senden - Diesen Stopp planen, Diese Zahlen verwenden. Die Konsole zeigt den Text unter der Zeichnung und sendet ihn erst, wenn die Person auf Senden klickt; eine Bitte ohne Klick dahinter wird verworfen. Siehe Folgenachrichten.

Karten. lib/agenticos-map-2.js zeichnet eine Reiseroute - nummerierte Pins der Reihe nach und die Linie dazwischen, sofort alle gezeigt und auf Wunsch einzeln gesetzt, auf einer Karte im Verhältnis 16:9 zur Breite der Seite, und eine Karte pro Stopp - auf dem Kachelserver, den VISUAL_MAP_TILES nennt, oder auf einem Deployment ohne einen als Umriss der Stopps. Die Anweisungen der Capability sagen dem Modell, welcher Fall vorliegt, damit es keine Straßen verspricht, die es nicht zeigen kann.

Es erscheint, während es geschrieben wird. Die Zeichnung ist das Argument content des Aufrufs. Die Konsole liest es aus den gestreamten Argumenten und gibt es in einen Rahmen unter der Nachricht, sodass SVG und HTML auf dem Bildschirm wachsen und Mermaid bei jeder vollständigen Zeile neu zeichnet. Skripte laufen einmal, wenn der Aufruf vollständig ist: bis dahin wird das Markup ohne Skripte und on*-Attribute gezeigt, sodass eine halb geschriebene Szene nie startet.

Es lebt im Gespräch. Gespeichert wird nur der Tool-Aufruf, den das Transkript ohnehin hält, sodass ein Neuladen und ein geteilter Thread es wieder zeichnen. Ein früherer Turn erreicht das Modell nur als Text, deshalb nennt die Capability die früheren Visuals des Gesprächs in ihren Anweisungen, mit ID und Titel, und read_visual holt eines vor einer Überarbeitung zurück.

Das Modell erfährt, wie es gerendert wurde. Während jemand im Web-Chat zusieht, meldet die Konsole die Skriptfehler, fehlgeschlagenen Ladevorgänge und abgelehnten Anfragen jeder Zeichnung, und draw wartet bis zu zehn Sekunden auf diesen Bericht, bevor es antwortet - so behebt das Modell eine kaputte Zeichnung mit revise_visual im selben Turn. Sieht niemand zu, antwortet es sofort und sagt, dass die Zeichnung nicht geprüft wurde.

Überarbeitungen sind exakte Ersetzungen, dieselbe Regel wie bei den Edits von publish_artifact, und jede ist ein eigenes Visual unter der Nachricht, die sie gemacht hat. Eine Zeichnung, die für eine App zu groß ist (ARTIFACT_MAX_BYTES), ein SVG, das nicht ein einzelnes <svg>-Element ist, oder eine, die etwas von einer anderen Website lädt, ist ein Retry, der sagt, was zu ändern ist.

Bilder. images bringt bis zu sechs Bilder in eine Zeichnung, jedes unter einem Namen, den die Zeichnung als {{image:NAME}} verwendet: ein an das Gespräch angehängtes Bild, eine Datei im Workspace oder eine https://-Adresse. Eine Adresse funktioniert nur für einen Agent, der das Web bereits liest - mit eingeschaltetem web_fetch oder browser_use - und nur auf einem Host, den einer von beiden lesen würde: ihre allowed_domains und blocked_domains begrenzen auch die Bilder, und der Download darf nicht auf einen anderen Host umgeleitet werden. Jedes Bild hat höchstens 2 MiB, und ein Visual trägt nur die Bilder, die seine Zeichnung noch verwendet, über alle Revisionen höchstens sechs.

Es hat die Isolation einer App. Der Rahmen ist die App-Sandbox - ein opaker Origin, connect-src 'none', nichts Entferntes außer dem Bibliothekssatz des Deployments, der jetzt three.js, Mermaid und Leaflet enthält, und dem eigenen Kachelpfad des Deployments. Siehe Visuals.

Apps

publish_artifact — veröffentlicht eine fertige Seite — einen Bericht, ein kleines Dashboard, eine Zusammenfassung — unter einem stabilen Link. read_artifact — liest die aktuelle Version einer Seite, die dieser Agent veröffentlicht hat, so wie sie geschrieben wurde.

Veröffentlicht ein in sich geschlossenes HTML- oder Markdown-Dokument als App: eine geteilte Ressource mit einem Besitzer, einer Sichtbarkeit und Grants, die im Browser unter einem Link geöffnet wird, der bleibt. Keine Konfiguration.

Der Name ist die Identität. (organization, agent, environment, name) wählt die App aus, sodass der nächste Run desselben Agents, der weekly-report veröffentlicht — aus einem Chat, einem Zeitplan oder der API —, derselben App eine Version hinzufügt, statt einen zweiten Link anzulegen. Die Umgebung kommt vom Run, also veröffentlicht ein Run in staging eine eigene Seite. Identische Bytes fügen keine Version hinzu und antworten mit unchanged.

Woher die Seite kommt. path liest eine Datei aus dem Workspace des Runs über dessen eigenes Backend, funktioniert also überall, wo die Capability sandbox funktioniert; content nimmt die Seite inline für einen Agent ohne Workspace; edits sind exakte Ersetzungen, angewendet auf die Version, die read_artifact geliefert hat, und eine Veröffentlichung dazwischen lehnt sie ab, statt sie zu überschreiben. Genau eines der drei. Ein falscher Aufruf — mehr als eines, keines, ein Name außerhalb von ^[a-z0-9][a-z0-9-]{0,63}$, eine unbekannte Endung, eine leere oder zu große Seite, eine Ersetzung, die keine oder mehrere Stellen trifft — ist ein Retry, der nennt, was zu ändern ist. Ein Lesen, das die Berechtigungsregeln des Workspace ablehnen, und eine Seite, die die Person des Runs nicht öffnen darf, sind Ergebnisse, keine Retries.

Zurücklesen. read_artifact liefert eine Kopfzeile (Version, Format, Größe) und den Quelltext, bei 100.000 Zeichen abgeschnitten, was die Kopfzeile sagt. Es öffnet nur, was die Person des Runs in der Konsole öffnen darf, und nichts in einem öffentlichen Widget, einem Embed, einem Zeitplan oder einem Ereignis-Trigger, wo keine identifizierte Person zuhört.

Ohne Seiteneffekte. Eine erste Veröffentlichung ist privat für die Person, für die der Run lief, und nur eine Person erweitert, wer sie liest; das Genehmigungs-Gate würde also nur den geplanten Bericht parken, für den das hier existiert. Ein Autor, der jede erneute Veröffentlichung einer geteilten Seite genehmigt haben möchte, setzt tool_approval auf publish_artifact.

Die Seite hat kein Netzwerk. Sie wird in einem opaken Origin unter einer sandbox-Policy mit connect-src 'none' ausgeliefert, und der Tool-Text sagt dem Modell, seine eigenen Daten einzubetten und Diagramme aus dem Bibliothekssatz des Deployments statt aus einem CDN zu laden. Siehe wie die Seite isoliert wird.

Delegation

task — übergibt ein in sich geschlossenes Stück Arbeit an einen der Spezialisten dieses Agents. check_task, wait_tasks, list_active_tasks — eine laufende verfolgen. send_message_to_subagent, soft_cancel_task, hard_cancel_task — eine steuern oder stoppen. Diese sechs werden nur angeboten, wenn eine Delegation im Hintergrund erreichbar ist — ein Agent mit reinem sync bekommt keine davon. create_agent, delegate — ein Spezialist, den das Modell sich selbst schreibt, wenn der Autor es erlaubt. answer_subagent — deklariert, und keinem Modell angeboten.

Ein Agent übergibt einen Teil einer Aufgabe an einen anderen, jeder auf seinem eigenen Modell mit seinem eigenen Wissen und seinem eigenen Schrittlimit, namentlich angesprochen. Es gibt zwei Formen von Delegierten, und der Unterschied entscheidet, wie geprüft, versioniert und abgerechnet wird — Konzepte ist der Ort, an dem das erklärt wird. An welche veröffentlichten Agents dieser delegieren darf, steht nicht in dieser Konfiguration: Es ist subagents auf der obersten Ebene des Specs, wo die Veröffentlichungsprüfung, der YAML-Export und das Berechtigungsmodell es alle sehen können.

Konfiguration Standard Bereich
inline keine Spezialisten, die in diesem Agent definiert sind
mode sync sync, async, auto
allow_questions false eine synchrone Delegierte darf die Person des übergeordneten Agents fragen
allow_dynamic false
max_depth 1 1–3
max_fanout 3 1–10
max_result_chars 2000 200–20000
share_with_delegates keine Capability-ids, an die dieser Agent selbst gebunden ist, außer subagents

Der Modus ist die Entscheidung des Autors, nicht die des Modells.

Das task-Tool der Bibliothek nimmt ein mode-Argument entgegen, das auf sync voreingestellt ist, deshalb sind „das Modell hat sich zum Warten entschieden“ und „das Modell hat nichts gesagt“ derselbe Aufruf. Es gibt keine Möglichkeit, sowohl eine Einstellung als auch eine Wahl zu ehren, und die Einstellung wurde geprüft.

Also wird das Argument unterwegs ersetzt, und auto ist die Art, wie ein Autor die Entscheidung bewusst abgibt. auto wird vor dem Start der Delegation aufgelöst, denn ob ein Panel offen bleibt, nachdem der übergeordnete Agent geantwortet hat, hängt von der Antwort ab.

Eine angeheftete Delegierte oder ein Spezialist darf den Modus für sich selbst überschreiben — ein langsamer Rechercheur ist der Fall, der sich im Hintergrund zu laufen lohnt. Die Instruktionen markieren diese Delegierte dann neben ihrem Namen: Ein einzelner Satz, der den konfigurierten Modus angibt, war ein Versprechen, das die überschreibende Delegierte dann brach, indem er dem Modell eine Antwort ankündigte und ihm eine Task-id gab.

Einem Agent mit reinem sync wird keines der sechs Task-Lebenszyklus-Tools angeboten. Jedes von check_task, wait_tasks, list_active_tasks, send_message_to_subagent und den beiden Abbrüchen nimmt eine Task-id entgegen oder berichtet über sie, und eine sync-Delegation liefert die Antwort und sonst nichts — es gibt keine id zum Weitergeben. Sie werden also nur angeboten, wenn eine Hintergrund-Delegation erreichbar ist: der Modus async oder auto, eine Delegierte, die eines von beiden bevorzugt, oder die Erlaubnis, Spezialisten zu erfinden. sync ist der Standard, das ist also die übliche Konfiguration, und sechs zurückgehaltene Tool-Beschreibungen sind sechs, für die das Modell nicht mehr in jedem Zug bezahlt. task bleibt — ein sync-Agent delegiert weiterhin.

Fan-out und Verschachtelung sind Obergrenzen, keine Fehler.

Jenseits von max_fanout kommt die nächste Delegation als Tool-Ergebnis zurück, auf das das Modell reagieren kann — warten oder die Arbeit selbst erledigen —, denn eine Taktgrenze sollte keinen Run beenden.

max_depth zählt Delegationsebenen einschließlich der des konfigurierten Agents selbst: 1 heißt, dieser Agent delegiert und seine Delegierten nicht; 2 erlaubt eine verschachtelte Ebene.

An der Grenze wird eine Delegierte ohne die Delegations-Capability gebaut statt mit einer, die nur ablehnen kann. Ein Tool, das immer „keine Delegierten verfügbar“ antwortet, ist eine Beschreibung, für die das Modell in jedem Zug bezahlt und die es trotzdem versucht.

Es gibt bewusst keine 0. Delegation abzuschalten heißt, die Bindung zu deaktivieren, und eine zweite Schreibweise desselben Schalters ist eine, die der ersten widerspricht.

Und jeder Agent im Baum wird an seinem eigenen max_depth gemessen, nicht an dem der Wurzel. Eine Delegierte bekommt den niedrigeren Wert aus dem, was der Baum noch übrig hat, und dem, was ihr eigener Spec erlaubt — eine für drei Ebenen konfigurierte Wurzel, die an einen Agent delegiert, dessen Autor 1 gewählt hat, bekommt eins: Diese Delegierte delegiert und ihre Delegierten nicht, genau so, wie ihre eigenen Prüfer es gelesen haben. Eine Obergrenze, die ein Aufrufer erweitern könnte, wäre keine, und der Grund, eine Delegierte auf eine Version anzuheften, ist, dass die Entscheidungen ihres Autors gelten, wenn jemand anderes sie aufruft.

Eine synchrone Delegation kann anhalten, um eine Person zu fragen, und wird an Ort und Stelle fortgesetzt. Ein gegatetes Tool darin parkt den gesamten Run; es zu genehmigen, setzt diese Delegierte dort fort, wo sie stehen geblieben ist, statt erneut zu delegieren — und genau das lässt die Genehmigung für den Aufruf gelten, den die prüfende Person tatsächlich gesehen hat. Governance beschreibt die Form des gespeicherten Zustands und warum ein erneuter Lauf anders antworten würde.

Eine Hintergrund-Delegation kann nicht anhalten, um eine Person zu fragen.

Ein gegatetes Tool darin wird abgelehnt statt geparkt, und die Ablehnung sagt dem Modell, es solle diese Arbeit stattdessen mit mode="sync" delegieren.

Der Grund ist keine Richtlinie, sondern die Lebensdauer. Der Genehmigungskanal schließt über die Datenbanksitzung der Anfrage, und eine Hintergrund-Delegation überdauert den Tool-Aufruf, der sie gestartet hat — bis sie fragen wollte, ist also nichts mehr da, womit sich die Frage schreiben ließe.

Eine Hintergrund-Delegation, die trotzdem aussetzt — eine Form, die die Bibliothek als nicht zustellbar dokumentiert —, wird mit derselben Meldung als failed festgehalten. Die Alternative wäre eine Task, die „läuft noch“ meldet, solange der Prozess lebt: ihre Ausgaben niemandem zugeordnet, ihr Fan-out-Platz nie freigegeben und das von einer Oberfläche geöffnete Panel nie geschlossen.

Eine synchrone Delegierte darf die Person des übergeordneten Agents fragen, wenn allow_questions gesetzt ist.

Standardmäßig aus: Ein Spezialist arbeitet autonom und sagt es, wenn er nicht konnte.

Eingeschaltet bekommt eine Delegierte, deren Modus sync ist, das ask_parent-Tool der Bibliothek, und eine Frage, die sie stellt, wird über den eigenen ask_user-Kanal des Runs beantwortet — von der Person, die den Tool-Aufruf des übergeordneten Agents ohnehin schon hält — und niemals vom Modell.

Es ist die Entscheidung des Autors, weil die Frage einen Namen trägt, den der Autor veröffentlicht hat. Ein Spezialist, den das Modell erfindet, fragt nie, was auch immer hier steht: Instruktionen, die ein Modell vor einem Moment geschrieben hat, sind nicht die des Autors, um sie einer Person vorzulegen.

Nur sync. Eine Hintergrund-Delegation hat eine Task-id zurückgegeben, und es ist niemand mehr da, der antworten könnte, und auto kann zu einer solchen werden.

Eine vorgefertigte Delegierte zu erreichen, brauchte eine Änderung stromaufwärts. subagents-pydantic-ai#76 ehrt can_ask_questions für einen vom Aufrufer gelieferten Agent, was hier jede Delegierte ist — damit ist die synchrone Hälfte von #184 erledigt.

answer_subagent wird keinem Modell angeboten.

Es beantwortet eine Frage, auf der eine Hintergrund-Delegierte geparkt hat, und keine Delegierte hier parkt auf einer: Eine synchrone Frage geht über ask_user an eine Person und nie über dieses Tool, und einer asynchronen Delegierten wird ask_parent gar nicht erst gegeben. Die einzig mögliche Antwort des Tools ist daher „diese Delegation wartet auf keine Antwort“.

Es bleibt deklariert, denn ein Tool, das in der Deklaration fehlt, kann weder von der Genehmigungsrichtlinie gegatet noch von einer Bindung umbenannt werden, und diese Hälfte des Versagens ist still.

Es wird aus der angebotenen Menge herausgefiltert, denn die andere Hälfte ist eine Beschreibung im Kontext jedes Zugs, die eine Aktion beschreibt, die nicht stattfinden kann — und Tool-Beschreibungen sind der stärkste Prompt in diesem Produkt.

Das Tool wird erst erreichbar, wenn die Hintergrund-Hälfte von #184 beantwortet ist: dort, wo das eigene Modell des übergeordneten Agents antwortet, während nichts es verpflichtet hinzusehen, und die Delegierte auf einem Fan-out-Platz blockiert, den das Ende des Zuges abbricht.

wait_tasks kürzt, und sagt das. Das Ergebnis einer abgeschlossenen Task wird bei max_result_chars abgeschnitten, mit einer ausdrücklichen Markierung, die auf check_task verweist, das immer den vollständigen Text zurückgibt. Die Markierung ist die tragende Hälfte: Ein stiller Schnitt liest sich als kurze Antwort, und ein Orchestrator, dem ein halber Bericht gereicht wurde, delegiert Arbeit erneut, die er schon hat.

Die Delegation abzuschalten heißt, die Bindung zu deaktivieren, nicht eine Zahl zu senken. Eine deaktivierte Bindung ist keine Delegation: Es wird nichts gebaut, also liest nichts die Pins oder die Spezialisten, die sie mitführt — und die Veröffentlichung wird dann für einen Agent abgelehnt, der immer noch Delegierte benennt, denn ein Pin, den nie jemand aufrufen wird, ist Konfiguration, die sich wie eine Entscheidung liest und nichts tut.

Ohne jede gebundene Delegierte steuert diese Capability nichts bei — sie wird nicht angehängt, genauso wie knowledge ohne Collections nicht angehängt wird. Zehn Tools, die nur ablehnen können, sind zehn Tools im Kontext jedes Zuges.

Drei sind als handelnd deklariert, und ein Autor stellt sie mit tool_approval hinter ein Gate: send_message_to_subagent, soft_cancel_task und hard_cancel_task. Das Steuern ändert mitten im Run, was eine Delegierte tut, und jeder der beiden Abbrüche vernichtet Arbeit, die bezahlt und nicht geliefert wurde. task hat bewusst keine Seiteneffekte, was sich einen Moment lang falsch liest: Was eine Delegierte tut, wird vom eigenen Spec der Delegierten gegatet, über dasselbe Genehmigungs-Gate, das dieser Run benutzt — die Delegation zusätzlich zu gaten hieße also, jemanden um Genehmigung zu bitten, bevor die Arbeit, die vielleicht Genehmigung braucht, überhaupt vorgeschlagen wurde. Wer das trotzdem will, hat eine tool_approval-Überschreibung.

Einer Delegierten werden die Capabilities des übergeordneten Agents nicht geliehen. Sie läuft auf ihrem eigenen Spec plus dem, was share_with_delegates benennt, eine id nach der anderen — ein Spezialist, der still die Zugangsdaten des übergeordneten Agents erhielte, wäre der leise Weg um das herum, was diesem gewährt wurde. Die Veröffentlichung lehnt eine geteilte id ab, an die der übergeordnete Agent selbst nicht gebunden ist, denn zu verleihen, was man nicht hält, ist eine Konfigurationszeile, die sich wie eine Entscheidung liest und nichts tut. In der Praxis existiert das für sandbox: Es zu teilen, ist die Art, wie ein Rechercheur /workspace/notes.md schreibt und ein Autor es liest. Eine Delegierte, die sandbox bindet, ohne dass ihr der des übergeordneten Agents geteilt wurde, bekommt den In-Memory-Workspace, denn nur der Run öffnet einen.

subagents kann nicht geteilt werden, und es ist die eine id, bei der „hält der übergeordnete Agent es“ niemals ablehnen könnte — ein Agent, der irgendetwas teilt, hält es per Definition.

Geteilt landet die Bindung des übergeordneten Agents bei einer Delegierten, die keine bindet, und die Laufzeit liest dann die Spezialisten, allow_dynamic, max_fanout, max_depth und die Teilungsliste des übergeordneten Agents, als hätte der Autor der Delegierten sie gewählt.

Die Veröffentlichung lehnt das ab, und die Laufzeit streicht es außerdem aus der Teilungsliste, sodass auch ein vor dieser Regel gespeicherter Spec eine Delegierte nicht erweitern kann.

Ob eine Delegierte überhaupt delegieren darf, beantwortet ihr eigener Spec, und wie tief sie gehen darf, ebenfalls — begrenzt durch das, was der Baum über ihr übrig hat.

Das Teilen ist außerdem der einzige Weg zu einer MCP-Verbindung für einen Inline-Spezialisten, der selbst gar keine binden kann: Eine Verbindung ist organisationsweite Konfiguration, und sie über einen Spezialisten zu erreichen, den niemand veröffentlicht hat, ist die falsche Tür. Binden Sie sie am übergeordneten Agent und benennen Sie sie hier.

create_agent und delegate werden nur unter allow_dynamic angeboten. Ein Tool, das in der Deklaration einer Capability fehlt, kann weder von der Genehmigungsrichtlinie gegatet noch von einer Bindung umbenannt werden, und die gefährliche Hälfte davon ist still — deshalb sind alle zehn deklariert, und eine Standardkonfiguration bietet sieben an.

Was der Schalter einbringt, ist ein Spezialist, den das Modell selbst schreibt: Instruktionen und ein Modell, und sonst nichts.

Er wird über dasselbe build_agent gebaut, über das ein Inline-Spezialist kommt, auf dem gemeinsamen Budget-Guard des Runs und dessen Genehmigungskanal, sodass seine Anfragen bepreist und gegen das von jemandem gesetzte Cap gezählt werden.

Das ist der ganze Grund, warum es dafür eine Factory brauchte statt eines Schalters. Ein Spezialist, den die Bibliothek für sich selbst gebaut hätte, säße außerhalb des Modellkatalogs dieses Deployments, seines Vaults und seines Budget-Guards — eine nicht erfasste Anfrage, womöglich an einen Provider, für den die Organisation keinen Schlüssel hält. Die Factory ist das, was sie stattdessen durch diese Plattform zurückleitet.

Ein Spezialist, der kein Modell benennt, wird abgelehnt

Vor subagents-pydantic-ai 0.2.18 führte die Bibliothek einen Standard-Modellstring mit, aus dem ein modellloser Spezialist kompiliert wurde. 0.2.18 hat diesen Rückfall entfernt, und diese Plattform lehnt es noch früher ab, in DelegatingToolset._refuse_dynamic.

Das Modell darf nur ein Modell benennen, für das die Organisation ein Profil hat, und die Ablehnung nennt die Liste. Es darf keine Capabilities anhängen: Einem Modell zu erlauben, seinem eigenen Kind eine Capability zu gewähren, ist das Versagen des nicht gewährten Scopes mit einem neuen Hut. Er bekommt kein Wissen, keine eigenen Delegierten, und nichts wird über Runs hinweg gespeichert — einen Spezialisten zu behalten heißt, einen Agent zu veröffentlichen, und das ist die Handlung einer Person. MAX_DYNAMIC_SPECIALISTS begrenzt, wie viele ein Run behalten darf.

Dass ein Spezialist nicht gespeichert wird, ist Absicht, und es hat einen Ausgang statt einer Sackgasse: Eine Person kann einen solchen zu einem Entwurfs-Agent befördern.

Seine Definition reist auf dem eröffnenden SubagentStarted-Frame mit — der einen Stelle, an der sie lesbar ist, nachdem das Modell sie geschrieben hat und bevor der Zug endet —, sodass das Delegationspanel im Chat anbieten kann, sie zu behalten, während der Run noch auf dem Bildschirm ist, und der Builder bietet dasselbe bei einem Inline-Spezialisten an.

Die Beförderung erzeugt einen Entwurf, der demjenigen gehört, der befördert hat, gegatet auf agents:edit, und hört dort auf: Sie veröffentlicht nicht, heftet den neuen Agent nicht als Delegierten an und entfernt auch nicht den Spezialisten, aus dem er hervorging.

Siehe Konzepte dafür, warum die Speicherregel der Grund für den Ausgang ist statt eine Einschränkung, die er umgeht.

Ein behaltener hält den gesamten Run, in dem er erfunden wurde, eine Genehmigungsparkung eingeschlossen: Die Registrierung lebt in einer Registry, die die Delegationsbibliothek pro gebautem Agent aufbaut, und ein Run, der parkt, wird bei der Fortsetzung erneut gebaut — sie ging also über die Parkung hinweg verloren, bis die Registrierungen in PausedRunState mitgeführt und beim erneuten Abspielen neu registriert wurden (#175). Er überdauert nicht in den nächsten Zug der Unterhaltung, der ein frischer Aufbau ohne pausierten Zustand ist — ein in einer Antwort erzeugter Name ist in der nächsten unbekannt, und die Beschreibung von create_agent sagt dem Modell, es solle ihn erneut erzeugen, wenn task das nahelegt.

Die eigene unspezialisierte Delegierte der Delegationsbibliothek wird gar nicht angeboten, und es gibt keine Einstellung dafür.

Vor subagents-pydantic-ai 0.2.18 wäre sie auf einem Modell gelaufen, das dieses Deployment nicht konfiguriert hat — kompiliert aus dem bibliothekseigenen Standard-Modellstring, außerhalb der Profile der Organisation, ihres Vaults und des Budget-Guards des Runs. Genau wie der Laufzeit-Spezialist oben, bevor es dafür eine Factory gab.

Ein Auffangbecken ist ein legitimer Wunsch. Schreiben Sie es als Inline-Spezialisten, wo Sie lesen können, was er tut, und er wie alles andere bepreist wird.

Die Delegierte der Bibliothek ist seit 0.2.18 behoben (#174): Ohne Standardmodell und ohne Factory weigert sie sich nun, die Delegierte zu bauen, statt ein Modell auszuwählen.

Was dem Modell über all das gesagt wird, wird hier geschrieben statt von der Bibliothek: die Delegierten mit Namen und Beschreibung, der Modus, den dieser Run tatsächlich verwenden wird, und die Fan-out-Obergrenze, die es sonst dadurch entdecken würde, dass es abgelehnt wird. Zwei Listen derselben Delegierten in einem System-Prompt sind doppelt bezahlter Kontext, und nur eine davon kann sagen, was das Deployment durchsetzt.

Remote Agents über A2A

Ein Agent kann auch an den Agent eines anderen Anbieters delegieren, der das Agent2Agent-Protokoll spricht. Er wird einmal unter Connections → Remote agents über die Adresse seiner Agent Card registriert, die dabei gelesen und geprüft wird, optional mit einem API-Key aus dem Vault, der als Bearer-Token mitgeht. Die Spec eines Agent nennt Registrierungen in external_agents, und die Delegationseinstellungen zeigen sie neben den eigenen Agents der Organisation. Der Key einer Registrierung wird jedem Run geliehen, wie der einer Capability, darum lehnt das Veröffentlichen eine Registrierung ab, deren Key die veröffentlichende Person im Vault nicht sehen kann.

Jeder Agent, der an eine Registrierung delegiert, hängt von ihr ab, darum darf sie nur aktualisieren oder entfernen, wer sie registriert hat, oder wer jeden Agent bearbeitet - und aktualisieren, was bestimmt, wohin der Key geht, nur wer diesen Key noch verwenden darf. Eine Aktualisierung, deren Karte den Agent auf einen anderen Host verlegt, wird auditiert, denn dieser Host bekommt nun die Anfragen und den Key. Die Liste zeigt die Adresse einer Karte und ihren Endpoint ohne Query und Userinfo, in denen eine signierte Adresse ihr Credential trägt.

Wird der Key gelöscht, den eine Registrierung sendet, wird die Registrierung mit gelöscht, statt ohne Key weiter aufzurufen. Ein Key geht nur an einen HTTPS-Endpoint, darum wird eine Karte mit reinem HTTP für eine Registrierung mit Key abgelehnt. Eine Adresse mit Benutzername oder Passwort wird gleich abgelehnt: Ein Credential gehört in den Vault.

Für das delegierende Modell ist ein Remote Agent ein weiterer Delegierter, über seinen Namen mit demselben Tool task aufgerufen. Jeder Aufruf ist ein A2A message/send mit der jüngsten Anfrage, und die contextId des Remote Agent wird innerhalb derselben Delegation von Aufruf zu Aufruf weitergegeben: Eine Rückfrage zu einer Aufgabe setzt deren entferntes Gespräch fort, und zwei gleichzeitig gesendete Aufgaben haben jeweils ein eigenes.

Eine Antwort input-required oder auth-required kommt als Text zurück, der das sagt, denn ein Delegierter, der auf seinen eigenen Nutzer wartet, ist eine Antwort, die das Modell lesen muss. Ein Daten- oder Dateiteil einer Antwort wird nicht gelesen und erscheint als Zeile, die sagt, dass einer gesendet wurde. Ein unerreichbarer Remote Agent antwortet genau damit, und der Run geht weiter.

Das Budget wird vor jedem Aufruf geprüft. Was ein Aufruf kostet, bepreist der entfernte Anbieter, und niemand veröffentlicht es, daher wird jeder Aufruf ohne Preis gebucht und die Kosten des Runs als unvollständig ausgewiesen. Jede Anfrage läuft durch die Adressprüfungen des Deployments, sodass weder eine Karte noch ein Endpunkt die Plattform auf ihr eigenes Netz richten kann. Das Veröffentlichen lehnt einen Verweis auf eine nicht vorhandene Registrierung ab, und ein Run, dessen Registrierung inzwischen entfernt wurde, scheitert und nennt sie.

Was eine Delegation kostet und welche Run-Zeile sie festhält, steht unter Governance. Wer an wen delegieren darf, steht unter Berechtigungen.

Planung

write_plan — legt die ganze Checkliste an oder ersetzt sie. read_plan — zeigt die Schritte und ihre ids vor einer feinkörnigen Änderung. add_task, update_task_status, update_task_statuses, remove_task — ändern einen Schritt oder eine Gruppe, ohne den Plan zu ersetzen. add_subtask, set_dependency, get_available_tasks — abhängigkeitsbewusste Planung, nur unter enable_subtasks angeboten.

Eine Checkliste, die das Modell während der Arbeit für sich selbst führt: was erledigt ist, was in Arbeit ist, was übrig ist. Bei mehrstufiger Arbeit ist ein Modell besser, wenn es die Schritte zuerst aufschreibt und sie sich vor Augen hält, deshalb wird der aktuelle Plan in jedem Zug als cache-sichere Erinnerung am Ende zurückgespielt — nach einem Cache-Breakpoint angehängt, sodass das stabile Prompt-Präfix bytegleich bleibt und nur der veränderliche Plan in jedem Zug neu gelesen wird. Der Plan landet nie im System-Prompt.

Sie überschneidet sich mit der Delegation so, wie sich ein Plan mit einem Team überschneidet: Die Planung entscheidet, was die Schritte sind, die Delegation entscheidet, wer sie macht. Sie sind orthogonal — ein Plan ist ein Toolset plus eine Erinnerung, Delegation ist ein Toolset plus eine Run-Hülle —, deshalb darf ein Agent beides binden, eines oder keines.

Konfiguration Standard Werte
enable_subtasks false fügt die drei Subtask-/Abhängigkeits-Tools und den Status blocked hinzu
cache_ttl 5m 5m, 1h — wie lange das Präfix vor der Erinnerung cachen darf

Keines der neun Tools wirkt auf die Welt. Jedes verändert eine Checkliste, die das Modell für sich selbst führt, deshalb gibt es hier nichts, was eine Person genehmigen müsste, und die Capability deklariert side_effecting=False. Die drei Subtask-Tools sind auch dann deklariert, wenn eine flache Checkliste sie nicht anbietet, denn ein Tool, das in der Deklaration fehlt, kann weder von der Genehmigungsrichtlinie gegatet noch von einer Bindung umbenannt werden.

Der Plan gehört der Unterhaltung, nicht einem einzelnen Zug davon.

Die Checkliste ist Zustand, und jede Grenze, die ein Run hat, würde sie sonst verlieren. Ein Run, der mitten im Plan auf einer Genehmigung parkt, wird als frischer Run fortgesetzt — und eine Chat-Nachricht ist hier ein Run, deshalb begann die nächste Nachricht mit einem leeren Speicher.

Ein Agent schrieb drei Schritte, wurde gebeten, mit dem ersten zu beginnen, und antwortete, es gebe keinen Plan und er habe nie einen erstellt (agenticos#1077).

Der Speicher gehört also dem Runner, nicht der Capability. Er wird aus dem gespeicherten Plan der Unterhaltung befüllt, oder bei einer Fortsetzung aus paused_state, was die neuere Kopie ist, und beim Anhalten des Runs in die Unterhaltung zurückgeschrieben.

Eine Oberfläche ohne Unterhaltung — ein reiner API-Aufruf — hält einen Plan für die Dauer ihres Runs, und das ist alles, was sie hat.

Eine fertige Checkliste ist Geschichte, und ein neuer Zug beginnt nicht mit ihr. Die Zeile behält sie — nichts wird gelöscht —, aber ein Plan, dessen Schritte alle completed oder cancelled sind, wird nicht in den nächsten Zug übernommen: Die Erinnerung am Ende würde eine Aufgabe, die niemand erledigt, „Ihren aktuellen Plan“ nennen, und read_plan würde damit antworten (agenticos#1221).

Die Zeile zu behalten, braucht eine weitere Regel, denn ein Zug schreibt seinen Speicher am Ende zurück: Der Zug, dessen Speicher über einem fertigen Plan leer geöffnet wurde, schreibt nichts. Er hat nichts an der Checkliste getan, und ein leerer Dump würde die Zeile schon bei der nächsten gewöhnlichen Nachricht löschen. Ein Agent, der neue Arbeit beginnt, legt wie üblich einen Plan an und ersetzt ihn.

Der Filter sitzt beim Befüllen, nicht beim Abhaken des letzten Schritts, und darin besteht die ganze Entscheidung. Innerhalb des Zuges, der einen Plan abschließt, hält der Speicher ihn noch, sodass der Agent zusammenfassen kann, was er gerade getan hat, und nichts dem Transkript widerspricht. Erst die nächste Frage beginnt sauber — und die abgehakte Checkliste steht weiterhin in den Nachrichten darüber, wo sie sich als das liest, was getan wurde, statt als das, was gerade getan wird. Ein blocked-Schritt ist ausstehende Arbeit, deshalb wird ein Plan, der einen hält, übernommen: Irgendetwas muss ihn noch entsperren. Eine Fortsetzung wird aus paused_state befüllt und bleibt unangetastet, da sie konstruktionsbedingt mitten im Plan steht.

Ein Run, der endet, ohne fertig zu werden, legt seinen Schritt ab. Ein fehlgeschlagener, gestoppter oder über das Budget gelaufener Run arbeitet an nichts mehr, also hat der Plan, den er zurückschreibt, keinen Schritt in_progress: Dieser Schritt geht zurück auf pending — noch zu tun, aber nicht in Arbeit. Unverändert behalten, sagte die Erinnerung des nächsten Zuges einem frischen Run, er erledige ihn bereits (agenticos#2149). Ein Zug, der absichtlich mitten im Plan endet — mit einer Rückfrage an die Person —, und ein geparkter Run behalten ihren Schritt in Arbeit.

Ein Agent, der die Capability nicht bindet, zahlt nichts: keine Tools, keine Erinnerung und nichts Gespeichertes, denn eine leere Checkliste gegen eine Spalte, die null ist, ist keine Änderung, die geschrieben werden müsste.

Sie gibt keine eigenen Tokens aus. Die Tools sind lokale Änderungen an einer Checkliste, hinter denen keine Modell- oder Embedding-Anfrage steht, deshalb gibt es anders als bei Wissen oder Delegation keinen Umgebungsverbrauch zu erfassen. Die Runden, die das Modell macht, um sie aufzurufen, sind seine eigenen, und die zählt der Budget-Guard bereits.

Weiterarbeiten, während Tools laufen

Keine eigenen Tools. Ist sie gebunden, bekommt ein langsames Tool, das eine andere Capability markiert - heute browse_web des Webbrowsers -, die Wahl run_in_background, und das Modell entscheidet pro Aufruf: Auf einen Durchlauf, ohne dessen Ergebnis es nichts sagen kann, wartet es; einer, um den es herumarbeiten kann, läuft weiter, während der Agent den Rest der Anfrage beantwortet, und sein Ergebnis kommt als Nachricht, sobald es fertig ist. Gebaut auf BackgroundTools aus pydantic-ai-harness.

  • Genauso geprüft. Ein Ergebnis aus dem Hintergrund durchläuft die Regeln des Agenten für Tool-Ergebnisse - Guardrails, Tool-Ausgabelimits - genau wie eines im Vordergrund; die Bibliothek allein würde es an ihnen vorbei zustellen.
  • Erst die Freigabe. Ein Tool, das auf einen Menschen wartet, parkt für seine Freigabe, bevor irgendetwas im Hintergrund läuft.
  • Budget und Lebensdauer des Runs. Was ein Hintergrund-Tool ausgibt, gehört dem Run, und wer den Run stoppt, stoppt es. Ein Run, der parkt - eine Freigabe, eine Frage -, bricht die noch laufenden Hintergrund-Tools ab, und ihre Ergebnisse kommen nach dem Fortsetzen nicht an.

Nachdenken

Keine Tools. Bittet das Modell, vor der Antwort nachzudenken: langsamer und teurer, besser bei Arbeit, die mehrere Schritte gleichzeitig im Kopf braucht.

Konfiguration Standard Werte
effort ungesetzt minimal, low, medium, high, xhigh, max

Ungesetzt bedeutet den eigenen Standardaufwand des Providers. Eine Stufe, die ein Provider nicht hat, wird auf die nächstliegende abgebildet, sodass ein Spec über einen Modellwechsel hinweg portabel bleibt.

Erinnerung an Anweisungen

Keine Tools. Wiederholt steuernde Hinweise mitten im Run, damit eine lange Sitzung nicht von ihren Instruktionen abdriftet — das Versagen, das dies behebt, ist das Verblassen der Instruktionen, bei dem ein Modell nach vielen Tool-Zügen die Hinweise, mit denen es begonnen hat, zunehmend ignoriert. Es ist eine Portierung von SystemReminders aus pydantic-ai-harness.

Es gibt drei Arten von Erinnerungen, jede mit ihrem eigenen Takt:

Art Kosten Text, den sie einfügt
reminders[] keine Eine feste Zeile, die Sie schreiben
goal_reanchor keine Die erste Nutzeranfrage des Runs, als Anker wiederholt
llm_reminder ein Modellaufruf pro Auslösung Ein kurzer Hinweis, den ein Modell aus dem jüngsten Transkript schreibt

Jede Art nimmt interval (bei jeder N-ten Modellanfrage auslösen), first_after (die Nummer der Anfrage, bei der zum ersten Mal ausgelöst wird) und max_fires (die Obergrenze über die Unterhaltung hinweg) entgegen; cache_ttl auf der Capability legt die Lebensdauer des Cache-Breakpoints fest. Mindestens eine Art muss gesetzt sein, sonst steuert die Capability nichts bei und wird aus dem Run gestrichen — und genau das bedeutet eine leere Konfiguration.

Der Takt zählt über die gesamte Unterhaltung, und er ist dauerhaft. Eine Erinnerung löst bei Modellanfrage Nummer N aus, und dieser Zähler wird auf der Unterhaltung gespeichert und im nächsten Zug zurückgeladen — eine Erinnerung, die alle zehn Anfragen auslösen soll, zählt also dort weiter, wo der letzte Zug aufgehört hat, statt auf null zurückzuspringen, und eine Unterhaltung zu verlassen und neu zu laden, setzt sie fort (#787). Gespeichert werden nur die Zähler; der Erinnerungstext wird pro Anfrage eingefügt und gelangt nie ins Transkript.

Das Einfügen ist cache-sicher. Eine ausgelöste Erinnerung wird an das Ende der Anfrage als flüchtiger Nutzer-Prompt hinter einem Cache-Breakpoint angehängt, nachdem der Kern die dauerhafte Historie gespeichert hat — sie erreicht also das Modell, gelangt aber nie in message_history, es häufen sich keine veralteten Erinnerungen an, und das gecachte Präfix (Tools, System, die echte Unterhaltung) bleibt Zug um Zug bytegleich, während nur die kleine Erinnerung außerhalb des Caches liegt. In den System-Prompt einzufügen, würde stattdessen bei jeder Auslösung das gecachte Präfix sprengen und veraltete Erinnerungen ansammeln.

Eine LLM-Erinnerung wird erfasst und erbt das Modell des Runs.

Sie schreibt ihren Text über einen Agent, den sie selbst baut und den kein Budget-Guard umschließt — ihre Ausgaben werden daher auf das Konto des Runs gebucht, so wie bei einer Zusammenfassung, und sie läuft unter den Verbrauchsgrenzen des Runs abzüglich einer reservierten Anfrage, sodass sie den Run nie über sein eigenes Schrittlimit hinausschieben kann.

Sie verwendet das eigene Modell des Runs — dasjenige, dessen Zugangsdaten der Vault aufgelöst hat — statt eines Namens aus der Konfiguration, dieselbe Entscheidung, die die Kontextverwaltung für ihren Zusammenfasser trifft.

Bei jedem Fehler, oder wenn das reservierte Budget bereits verbraucht ist, fällt sie auf die Zielanker-Zeile zurück. Eine fehlgeschlagene Erzeugung blockiert den Run nie.

Datum und Uhrzeit

Keine Tools. Legt das aktuelle Datum und die aktuelle Uhrzeit in die Instruktionen des Agents, damit er aufhört, eines anzunehmen — das Versagen, das dies behebt, ist ein Agent, der selbstbewusst über „dieses Quartal“ von seinem Trainingsstand aus nachdenkt.

Konfiguration Standard
timezone UTC ein beliebiger IANA-Name, z. B. Europe/Warsaw

Lange Gespräche

Keine Tools. Stutzt die Nachrichtenhistorie eines langen Runs vor jeder Anfrage, sodass ein Run, der sonst an die Grenze des Modells gestoßen wäre, weiterarbeitet. Die Strategien stammen aus pydantic-ai-harness.

Konfiguration Standard
strategy summarize summarize, tiered, clear_tool_results, sliding_window
max_fraction 0.9 0,05–0,95 des Fensters, ab dem die Compaction beginnt
keep_messages 20 jüngste Nachrichten, die eine Zusammenfassung oder ein Fenster überleben
keep_tool_pairs 3 jüngste Tool-Aufrufe, die ihre Ergebnisse behalten
summary_prompt der bibliothekseigene was dem zusammenfassenden Modell gesagt wird; muss {messages} enthalten
context_window ungesetzt überschreibt das Fenster — das, wogegen dies auslöst, und das, wodurch die Anzeige im Chat teilt
fallback_context_window 200000 Fenster, das anzunehmen ist, wenn das des Modells nicht aufgelöst werden kann

summarize ist der Standard, weil es die einzige Strategie ist, die bewahrt, was die älteren Züge gesagt haben. Die Strategien ohne LLM sind billiger, weil sie Information wegwerfen — ein gleitendes Fenster verwirft die ältesten Nachrichten rundheraus, ein geleertes Tool-Ergebnis löscht eine Antwort, die der Agent noch brauchen könnte —, und ein Agent, der mitten im Run still vergisst, was ihm gesagt wurde, ist ein schlimmeres Versagen als eine Zusammenfassung, um die niemand gebeten hat. Sie löst aus demselben Grund bei 0,9 des Fensters aus: Bei der Compaction beginnt ein Run, Details zu verlieren, deshalb wird sie aufgeschoben, bis das Fenster nahezu voll ist.

tiered ist die sparsame Wahl und eine Bindung entfernt: Es leert zuerst alte Tool-Ergebnisse und bezahlt eine Zusammenfassung nur, wenn das nicht gereicht hat. Zusammenfassen macht aus Eingabe-Tokens Ausgabe-Tokens, die mit einem Aufschlag abgerechnet und seriell erzeugt werden, deshalb ist ein Agent, dessen Runs von großen Tool-Ergebnissen dominiert werden, meist mit tiered besser bedient.

Sie reicht über einen Run, nicht über eine Unterhaltung. Zwischen den Zügen wird die Historie aus dem Transkript als Nutzer- und Assistententext neu aufgebaut, deshalb sind Tool-Aufrufe und ihre Ergebnisse nicht mehr da, um komprimiert zu werden, und keine hier vorgenommene Änderung überdauert eine Zuggrenze. Die Historie, deren Compaction sich lohnt, ist die lange Tool-Schleife innerhalb eines einzelnen Runs, wo eine Verzeichnisauflistung oder eine Wissenssuche Zehntausende Tokens groß ist.

Der Auslöser ist ein Bruchteil, weil eine absolute Zahl nur für ein Modell stimmt, und derselbe Agent läuft auf dem Profil, auf das sein Spec gerade zeigt. Das Fenster kommt aus dem Modellprofil, das es aus dem eigenen Listing des Providers übernommen hat, als jemand das Modell hinzugefügt hat — siehe Welche Modelle ein Provider anbietet.

Wo das Profil nichts festgehalten hat, wird das Fenster stattdessen aus der mitgelieferten Preis-Momentaufnahme aufgelöst, und zwei Fälle lösen falsch auf — beide in die Richtung, die einen Run kaputt macht, statt in die, die eine Zusammenfassung verschwendet: Ein Spec mit Fallbacks baut ein FallbackModel, dessen zusammengesetzte id auf nichts auflöst, und genai-prices hält für anthropic:claude-sonnet-4-5 1.000.000 fest gegenüber echten 200.000, wo max_fraction=0.9 den Auslöser auf 900.000 legt und die Compaction nie auslöst. context_window überschreibt alles und ist die Antwort auf beides — ein Provider veröffentlicht das Maximum, das ein Modell annehmen kann, und ein Deployment mit Beta- oder Tier-Beschränkung bekommt weniger.

Der Auslöser berücksichtigt, was jede Anfrage mitführt. Er misst die Nachrichtenteile; eine Anfrage trägt außerdem die Instruktionen und jedes Tool-Schema. Bei einem echten Agent sah der Schätzer 60 Tokens, wo der Provider 3.865 abrechnete — der Overhead wird deshalb an jeder Antwort gemessen und das Fenster des Auslösers um ihn nach unten verschoben, und genau das lässt die Anzeige und den Auslöser eine Obergrenze beschreiben statt zwei.

Er wartet auf eine Antwort, an der er messen kann, deshalb löst die erste Anfrage eines Runs allein anhand der Nachrichten aus. Und er gibt auf, wenn der Overhead allein bereits jenseits des Auslösers liegt: Keine Zusammenfassung kommt darunter, die Schemata stehen nicht in der Historie, und ein korrigiertes Fenster würde für immer bei jeder Anfrage eine Zusammenfassung einbringen.

Wenn sie aufgibt, sagt sie das. Ein context_window, das kleiner ist als der Overhead des Agents selbst, ist dieser Fall, und nichts dagegen zu tun, ist auf dem Bildschirm nicht von einer funktionierenden Einstellung zu unterscheiden — deshalb zeigt der Chat, wie hoch der Overhead ist und gegen welches Fenster er gemessen wurde, und das ist das Paar, das jemand braucht, um eine funktionierende Zahl zu wählen. Einmal pro Run, weil es eine Konfiguration beschreibt statt eines Ereignisses, und es wird in dem Moment verdrängt, in dem tatsächlich eine Zusammenfassung läuft.

Eine Zusammenfassung sagt, dass sie stattfindet. Sie ist eine ganze Modellanfrage zwischen zwei eigenen des Zuges, bei der sonst nichts streamt — der Chat blieb früher für ihre Dauer stehen, was sich wie ein kaputter Bildschirm liest und dazu führt, dass die Seite neu geladen und der Zug abgebrochen wird. Der Chat zeigt jetzt, was gerade zusammengefasst wird, während es geschieht. Nur die zusammenfassende Strategie: Die anderen bearbeiten eine Liste und kehren zurück.

Eine Zusammenfassung wird erfasst. Die Strategie schreibt sie über einen Agent, den sie selbst baut und den kein Budget-Guard umschließt, deshalb misst die Capability den Verbrauch des Runs über den Hook hinweg und bucht die Differenz auf das Konto des Runs. Sie wird festgehalten statt verhindert: Der Guard lehnt bei der nächsten Anfrage ab, deshalb stoppt eine Compaction, die ein Cap überschreitet, den Run danach und nicht während ihrer selbst.

Die Anzeige daneben gehört nicht zu dieser Bindung. Wie voll das Fenster war, meldet jeder Agent, ob er komprimiert oder nicht — siehe wie voll das Kontextfenster ist. Die Warnung zählt am meisten für den Agent, der nicht komprimieren wird, denn das ist der, der an die Decke stößt und abgelehnt wird.

Bilder auslagern

Keine Tools. Schreibt die großen Teile einer verdichteten Konversation in den Speicher und hinterlässt in der gespeicherten Historie eine media+sha256://…-Referenz. Die inhaltsadressierten Speicher und die Walker stammen aus pydantic-ai-harness.

Konfiguration Standard
threshold_bytes 32768 1 KiB–10 MiB; Teile ab dieser Größe werden ausgelagert

Es gibt eine Stelle, an der sich Medien wirklich anhäufen, und das ist diese. Ein Anhang erreicht das Modell einmal, in der Runde, in der er angehängt wurde: die gewöhnliche Historie wird aus dem Text des Transkripts aufgebaut, ein Bild wird also nicht erneut gesendet. Die Ausnahme ist eine verdichtete Konversation — dann wird der bibliothekseigene Dump der Run-Nachrichten vollständig gespeichert und exakt so wiedergegeben, wie das Modell ihn zuletzt gesehen hat, Base64 inklusive, bis die nächste Zusammenfassung ihn ersetzt. Dieser Blob sind Zeilen in Postgres und Bytes auf der Leitung, in jeder Runde dazwischen.

Wohin die Bytes gehen, und wie lange sie leben. In den eigenen Dateispeicher des Deployments, nach media/<Organisation>/<Konversation>/<Digest>. Die Organisation ist die Isolation: eine Medien-URI ist ein Inhalts-Hash, zwei Mandanten mit demselben Bild berechnen dieselbe URI, und die Organisation kommt aus dem Run und nicht aus der URI. Die Konversation ist die Lebensdauer — ein Inhalts-Hash hält nicht fest, wer ihn noch referenziert, also verschwindet das Präfix des Threads mit dem Thread und das des Mandanten mit dem Mandanten. Der Speicher gibt keine öffentliche URL aus; eine URL, die ein Modellanbieter abrufen kann, kann jeder abrufen.

Auslagern ist optional, Wiederherstellen nicht. Die Capability zu binden ist die Entscheidung auszulagern. Das Wiedereinsetzen geschieht für jede Konversation, ob sie noch gebunden ist oder nicht: eine Konversation, deren Agent danach entbunden wurde, hat weiterhin Marker in ihrer Historie — und ein Marker, den niemand auflöst, ist ein Bild, das dem Modell in einer Sprache gereicht wird, die es nicht liest.

Beide Richtungen scheitern weich. Eine Zusammenfassung zu verlieren, für die ein Modell bezahlt wurde, weil der Speicher gestockt hat, ist schlimmer als eine Historie, die größer ist als nötig. Ein Fehler wird geloggt und die Historie so verwendet, wie sie ist.

Es reduziert nicht, was das Modell bekommt: die Teile werden vor dem Absenden wieder eingesetzt, und genau das hält den Run korrekt. Sie in eine URL umzuschreiben, die das Modell selbst abruft, ist eine andere Funktion und bräuchte die öffentliche URL, die dieser Speicher bewusst nicht ausgibt.

Grenzen für Tool-Ergebnisse

Ein Tool, read_tool_result. Wo compaction die Historie innerhalb des Fensters zwischen zwei Anfragen stutzt, verhindert dies, dass eine übergroße Tool-Rückgabe überhaupt dorthin gelangt. Ein ToolReturnPart bleibt bestehen, deshalb wird ein grep über ein großes Repository oder eine geschwätzige API-Antwort bei jeder späteren Anfrage des Runs vollständig erneut gesendet — code_execution schneidet genau aus diesem Grund bereits bei 8.000 Zeichen ab, was der richtige Standard und die falsche Obergrenze ist: Der Teil, auf den es ankam, ist aus dem Blick des Modells verschwunden, und es bleibt nichts, worauf es reagieren könnte. Dies verkleinert eine Rückgabe einmal, wenn sie entsteht, und lässt die verkleinerte Form bestehen. Die Verkleinerung selbst ist ToolOutputLimits aus pydantic-ai-harness.

Konfiguration Standard
action spill spill, truncate, summarize
threshold 60000 Größe, ab der eine Rückgabe verkleinert wird; 15000, wenn over_tokens gesetzt ist
over_tokens false die Schwelle in geschätzten Tokens messen, nicht in Zeichen
max_chars 20000 Zeichen, die beim Abschneiden einer Rückgabe erhalten bleiben, oder wenn ein Spill darauf zurückfällt; nicht gesetzt, nie mehr als der Schwellenwert
truncation_strategy head_tail head, tail, head_tail — welches Ende bzw. welche Enden behalten werden
strip_ansi false Terminal-Farbcodes vor dem Messen und Verkleinern entfernen
summary_prompt der bibliothekseigene was dem zusammenfassenden Modell gesagt wird; muss {tool_name} und {output} enthalten

spill ist der Standard und der einzige verlustfreie: Die vollständige Rückgabe wird in das Backend des Agents geschrieben und durch ein Handle, eine Vorschau und eine Strukturskizze ersetzt, und das Modell liest bei Bedarf Ausschnitte davon über read_tool_result(handle, offset, limit, from_end, pattern) — dasselbe Muster zum Durchblättern, das ihm read_file über den Workspace gibt. truncate ist die billige, verlustbehaftete Klammer mit einer Markierung, die sagt, was abgeschnitten wurde; summarize ersetzt die Rückgabe durch eine LLM-Zusammenfassung und ist die teure Variante.

Ein Spill geht in das eigene Backend des Agents.

Ein Agent, der sandbox bindet, hat bereits ein Dateisystem — state, einen Docker-Container, Daytona —, das der Runner für den Run geöffnet und der Organisation zugeordnet hat. Der Spill lebt dort, unter einem tool_output/-Präfix, teilt also die Lebensdauer dieses Workspace, und der Agent kann ihn sogar über sein eigenes read_file und grep erreichen.

Beim standardmäßigen Sitzungs-Scope run ist diese Lebensdauer der Run, und genau das verlangt die Anforderung „darf den Run nicht überdauern“.

Ein Spill ist ein Artefakt innerhalb eines Runs und überdauert den Run auch auf einem länger gefassten Workspace (conversation, user, agent) nie:

  • bei einem state-Workspace wird das reservierte Präfix beim Schreiben entfernt, sodass angesammelte Spills ihn nicht mehr in Richtung seiner Byte-Obergrenze drücken und die eigenen Schreibvorgänge des Agents ablehnen lassen können;
  • bei einem Container-Workspace werden die Spills des Runs beim Schließen des Workspace von dessen Dateisystem gelöscht — anhand genau der Handles, die der Run festgehalten hat, nie durch ein Abräumen des Präfixes, sodass zwei gleichzeitige Runs, die sich einen Workspace teilen, einander die Spills nicht mitten im Flug wegnehmen können (#803).

Ein Agent ohne Backend bekommt ein In-Memory-Backend, das für den Run gebaut und mit ihm verworfen wird, sodass der Spill nie auf eine geteilte Platte geschrieben wird.

Ein Spill, den das Backend ablehnt — ein state-Workspace, der bereits an seiner Byte-Obergrenze ist —, fällt auf ein Abschneiden zurück statt still verworfen zu werden. Ebenso ein summarize, dessen Modellaufruf scheitert: summarize → spill → truncate.

Eine Zusammenfassung wird dem Run berechnet. Wie bei compaction läuft der zusammenfassende Aufruf über einen Agent, den der Harness selbst baut, außerhalb des Budget-Guards, deshalb werden seine Tokens über denselben Pfad für Umgebungsverbrauch auf das Konto des Runs gebucht — siehe wie die Kosten eines Runs gezählt werden. spill und truncate rufen kein Modell auf und kosten nichts.

Der Harness setzt Verkleinerungen aus einer geordneten Liste von Größen-Bändern zusammen; hier wählt ein Autor eine action bei einer threshold, denn das Builder-Formular kann keine verschachtelte Liste zeichnen — aus demselben Grund wählt compaction eine Strategie, statt Stufen zusammenzusetzen.

Keine eigenen Tools. Lässt den Agent ein Tool aus einer großen Menge finden, statt bei jeder Anfrage das Schema jedes Tools in seinem Kontext mitzuführen. Das zählt am meisten für MCP: Ein Agent darf beliebig viele Server binden, und jedes Tool, das ein Server bereitstellt, ist ein Schema, für das das Modell in jedem Zug bezahlt, ob es es jemals aufruft oder nicht.

Konfiguration Standard Werte
strategy auto auto, keywords, bm25, regex
max_results 10 1–50, von der nativen Suche ignoriert
  • auto — native Tool-Suche, wo der Provider sie anbietet (Anthropic BM25 oder regex, OpenAI serverseitig), sonst überall der lokale Schlagwortalgorithmus.
  • keywords — immer lokal abgleichen, bei jedem Provider.
  • bm25 / regex — einen Anthropic-nativen Algorithmus erzwingen; ein Run auf einem Provider ohne native Tool-Suche wirft einen Fehler, statt still einen anderen einzusetzen. Das Modell wird getrennt vom Spec aufgelöst, das ist also eine Laufzeitkosten, die der Autor akzeptiert, indem er einen benennt — auto scheitert nie auf diese Weise.

Sie einzuschalten, ist das, was die MCP-Toolsets zurückstellt. Die Capability und die Zurückstellung sind zwei Hälften einer Entscheidung: Das ToolSearch der Bibliothek ist ohne Zurückgestelltes wirkungslos, und ein zurückgestelltes Tool ohne eine Suche, die es findet, ist ein Tool, das das Modell nie aufrufen kann. tool_search zu binden ist also das, was die Toolsets der verbundenen Server zum zurückgestellten Laden markiert — die Tools der Registry selbst bleiben sichtbar, da sie wenige und pro Agent gewählt sind. Ein Agent, der es nicht bindet, zahlt nichts und sieht jedes Tool wie zuvor.

Der Builder sagt, wann es sich lohnt. Sobald die MCP-Bindungen eines Agenten zusammen mehr als 30 Tools ergeben - nach der eigenen Positivliste jeder Bindung, sonst nach dem, was ihr Server bei der letzten Prüfung gelistet hat -, zeigt der MCP-Tab einen Hinweis mit einer groben Token-Schätzung und eine Schaltfläche, die die Tool-Suche bindet. Von selbst bindet er sie nie: Die Zurückstellung ändert, was das Modell sieht, also bleibt es die Entscheidung der bearbeitenden Person.

Die Zurückstellung ändert, was das Modell sieht, nie die Identität eines Tools. Ein gefundenes MCP-Tool kommt unter seinem echten, präfigierten Namen an, sodass das Genehmigungs-Gate weiterhin darauf passt und die Umbenennung einer Bindung es weiterhin erreicht; ToolSearch sitzt ganz außen und liest die Namen, die eine Umbenennung bereits angewandt hat.

Sie braucht keine Erfassung. Die beiden lokalen Strategien laufen in Python und geben keine Tokens aus; die native Suche läuft innerhalb der Anfrage des Providers selbst, deren Verbrauch der Budget-Guard bereits erfasst; und die Runden zur Entdeckung sind gewöhnliche Modellanfragen, die derselbe Guard umschließt. Die eine Form, die ihm entgehen würde — eine eigene Suchfunktion, die selbst ein Modell oder ein Embedding aufruft —, ist bewusst nicht freigegeben.

Schutzregeln

Keine Tools. Prüft den Text, der durch einen Run fließt, an vier Kanten und schwärzt entweder einen Treffer oder blockiert den Run. Die Prüfungen sind fertige Detektoren aus pydantic-ai-harness und ein Detektor für Telefonnummern, den der Harness nicht mitbringt; ein Agent ist Daten, deshalb wählt und parametrisiert die Konfiguration sie, statt eine Python-Prüfung mitzuführen.

Kante Liest Schwärzen Blockieren
Eingabe den Prompt des Nutzers redact_secrets_in, redact_pii_in blocked_keywords_in
Ausgabe die Antwort des Agents redact_secrets_out, redact_pii_out blocked_keywords_out
Tool-Ergebnis was ein Tool zurückgab, bevor das Modell es liest redact_secrets_tool, redact_pii_tool blocked_keywords_tool
Tool-Argumente was der Agent einem Tool übergibt, bevor das Tool läuft redact_secrets_args, redact_pii_args blocked_keywords_args
Konfiguration Standard
redact_secrets_* false API-Schlüssel, Tokens, JWTs und PEM-Blöcke entfernen
redact_pii_* false E-Mail, Telefonnummer (gültig in ihrem Nummerierungsplan), IBAN (mod-97), Karte (Luhn) und US-SSN entfernen
blocked_keywords_* "" durch Komma oder Zeilenumbruch getrennte Begriffe; ein Treffer beendet den Run
phone_regions "US, GB, DE, PL" durch Komma oder Zeilenumbruch getrennte ISO-3166-Codes, deren nationale Telefonformate das PII-Schwärzen liest, höchstens 16

Jedes Kantenfeld ist standardmäßig aus, und eine Capability, die ohne konfigurierte Kante aktiviert wird, hängt nichts an — ein Agent, der sie nicht nutzt, zahlt nichts.

Eine Telefonnummer wird nur geschwärzt, wenn sie eine echte Nummer ist. Der Detektor stammt aus libphonenumber und läuft mit der Stufe STRICT_GROUPING: Ein Kandidat wird nur angenommen, wenn er zum Nummerierungsplan seines Landes passt und seine Trennzeichen dort stehen, wo dieses Land Ziffern gruppiert, sodass ein Datum, ein Betrag oder eine Bestellnummer, die eine Regel nach Ziffernzahl treffen würde, durchkommt. Eine mit + geschriebene Nummer nennt ihr Land selbst und wird geschwärzt, gleich was phone_regions aufführt. Eine nationale Nummer wie 415-555-0132 wird gegen jedes aufgeführte Land gelesen, und jedes weitere Land erweitert, was eine bloße Ziffernfolge sein kann: 123456789 ist eine gültige polnische Festnetznummer, also wird mit PL in der Liste auch eine neunstellige Bestellnummer geschwärzt. Die Gruppierung wirkt in beide Richtungen: eine US-Postleitzahl im Format ZIP+4, die mit 0 beginnt, etwa 02134-1234, ist wie eine deutsche Vorwahl mit Rufnummer gruppiert und wird mit DE in der Liste geschwärzt. Führen Sie die Länder auf, die der Agent bedient. Ein unbekannter Code (meist UK statt GB) oder eine Liste mit mehr als 16 Codes wird beim Veröffentlichen abgelehnt. Bei bis zu vier Ländern wird ein Text mit mehr als 200.000 Zeichen oder mehr als 10.000 Ziffern gar nicht gelesen. Jedes Land über vier ist ein weiterer Durchlauf über den Text, daher sinken beide Grenzen im selben Verhältnis, bei sechzehn auf 50.000 Zeichen und 2.500 Ziffern. Ein Text über der Grenze beendet den Run mit guardrail_blocked, denn ihn ungelesen weiterzugeben, gäbe jede Nummer darin weiter.

Das Schwärzen schreibt um; eine Blockade ist ein Run-Ergebnis. Ein Schwärzer entfernt den Treffer, und der Run läuft zu Ende — eine Antwort, die einen Schlüssel zurückzitiert hat, hat die Arbeit trotzdem getan. Eine Schlagwort-Blockade beendet den Run stattdessen mit dem Status guardrail_blocked, einem eigenen Ergebnis neben budget_exceeded, denn eine Ablehnung ist die Plattform bei der Arbeit, und ein Betreiber, der nach Problemen filtert, sollte sie finden können, statt dass sie sich wie jede abgeschlossene Antwort liest. Siehe Governance.

Eine Ausgabeprüfung greift, bevor jemand die Antwort sieht. Jede Oberfläche streamt: Der Web-Chat und das eingebettete Widget senden die Antwort, während sie geschrieben wird, und ein Channel-Bot bearbeitet seine Antwort, während der Text ankommt. Ist irgendeine Ausgabeprüfung konfiguriert, wird jedes Stück Text und Reasoning zurückgehalten, bis es vollständig ist, mit denselben Detektoren geprüft und erst dann gesendet. Ein Schlüssel, der auf zwei Chunks verteilt ist, wird trotzdem erkannt. Einer, der auf zwei Teile verteilt ist, etwa Text vor einem Tool-Aufruf und Text danach oder Reasoning und dann die Antwort, beendet den Run, bevor seine zweite Hälfte gesendet wird, weil die erste schon angezeigt wird. Ebenso Text, den das Modell schreibt, bevor es ein Tool aufruft — er gehört nicht zur endgültigen Antwort, wird aber trotzdem angezeigt und gespeichert. Der Preis ist, dass die Antwort eines solchen Agents Schritt für Schritt statt Wort für Wort ankommt. Ein Agent ohne Ausgabeprüfung streamt wie bisher. Eine Schlagwort-Blockade in der Antwort beendet den Run, bevor irgendetwas vom blockierten Text angezeigt oder gespeichert wird. Reasoning ist nicht die Antwort, daher beendet ein blockiertes Schlagwort dort den Run nicht: Dieser Reasoning-Schritt zeigt stattdessen [reasoning withheld by the output guardrail].

Tool-Argumente sind eine eigene Kante. Jeder Text, den der Agent einem Tool übergibt, wird geprüft, sobald die Antwort des Modells eintrifft, bevor das Tool läuft. Den geschwärzten Wert erhält das Tool, speichert das Transkript und zeigt der Stream, und ein gesperrtes Stichwort beendet den Run, bevor ein Tool aufgerufen wird. Getrennt von der Ausgabeprüfung, weil das Schwärzen eines Arguments ändert, was das Tool tut: Eine E-Mail geht mit einem Platzhalter statt des Schlüssels hinaus. Solange sie aktiv ist, werden die Argumente eines Aufrufs zurückgehalten, bis der Aufruf vollständig ist, wie die Antwort.

Ein Delegat streamt unter der Ausgabeprüfung des delegierenden Runs. Text und Reasoning, die ein Delegat im Delegationspanel schreibt, durchlaufen dieselbe Prüfung wie die Antwort des Runs, auf jeder Delegationsebene. Ein gesperrtes Stichwort beendet diese Delegation, bevor etwas davon gezeigt wird, und der delegierende Agent erfährt, dass sie fehlgeschlagen ist.

Der Stream-Filter erbt die Größengrenzen des Telefonnummern-Detektors, sodass ein Antwortteil, der dafür zu lang ist, den Run so beendet, wie es die endgültige Antwort täte. Weil sich der Filter in den Event-Stream des Runs einhängt, streamen die Modellanfragen eines Agents mit Guardrail auch über die HTTP-API, sein Modell muss also Streaming unterstützen.

Die Eingabekante ändert, was das Modell liest, nicht das Transkript. Ein geschwärzter Prompt erreicht das Modell bereinigt, aber die Unterhaltung speichert die Nachricht so, wie die Person sie eingegeben hat, einschließlich personenbezogener Daten. Wer die Unterhaltung lesen kann, kann auch diese Nachricht lesen.

Die Prüfung von Tool-Ergebnissen ist der Grund, warum diese Kante am meisten zählt. Sie ist die einzige Absicherung gegen nicht vertrauenswürdige Inhalte, die in die Schleife gelangen — eine abgerufene Seite, eine Datei, die Antwort eines MCP-Servers —, wo eine Prompt-Injection-Nutzlast das Modell sonst ungelesen erreichen würde.

Zwei Dinge stehen bewusst außerhalb des Umfangs. Tool-Argumente sind eine strukturierte Abbildung ohne Textdetektor, deshalb sind sie keine Kante. Und das approve-Urteil des Harness für ein Tool ist nicht portiert: Genehmigungen parken einen Run bereits pro Tool für eine menschliche Entscheidung, und ein zweiter, regelgetriebener Pfad zu demselben Mechanismus ist das, was eine einzige Tür vermeidet.

Infos zum Chat-Kanal

get_channel_info — Beschreibt den Kanal, in dem diese Unterhaltung stattfindet. list_channel_members — Listet die Personen in diesem Kanal auf. search_channels — Findet andere Kanäle nach Namen oder Zweck, ohne sie zu lesen. read_channel_history — Liest die jüngsten Nachrichten in diesem Kanal, die neuesten zuletzt. send_channel_file — Angeforderte Workspace-Datei senden.

Verbinden Sie das Konto mit einer direkten Nachricht an den Bot: nur link ohne Schrägstrich. Öffnen Sie den privaten Verbindungslink nach der Anmeldung bei AgenticOS und kehren Sie zum Channel zurück. Benötigen frühere Thread-Dateien eine Kontoverbindung, erklärt der Bot dies ohne Download und versucht es nach der Verbindung erneut. Channel-Bilder verwenden die doppelte CSS-Auflösung; sehr breite horizontale Mermaid-Flussdiagramme erhalten nur für das PNG eine vertikale Anordnung.

Die eine Capability, die der Spec eines Agents nicht binden darf. Sie wird pro Bindung gewährt, im Builder unter Where this agent is available, denn eine Organisation kann einen Agent an zwei Mattermost-Server und drei Slack-Workspaces binden — und „darf er lesen, was in diesem Kanal gesagt wurde“ hat beim internen eine andere Antwort als beim Kundenkanal. Ein Feld im Spec hätte eine Antwort für alle fünf, deshalb lehnt die Veröffentlichungsprüfung channel_tools in einem Spec ab, und der Run baut die Bindung aus der Zeile zusammen, die die Nachricht zugelassen hat — genau so, wie er den Prompt dieser Zeile an die Instruktionen anhängt.

Sie ist trotzdem eine gewöhnliche Registry-Capability, und das ist der Sinn dieses Vorgehens statt des Einschleusens eines Toolsets: Ihre Tools können durch tool_approval gegatet und durch tool_overrides umbenannt werden, und beides liest den Spec.

Konfiguration Standard Bereich
tools [] beliebige der vier ids
default_limit 20 1–200

Standardmäßig wird nichts gewährt, und was eine Plattform nicht beantworten kann, wird nicht angeboten: Telegram gibt einem Bot kein Verzeichnis von Chats zum Suchen und keine Möglichkeit, Nachrichten zu lesen, die ihm nicht gesendet wurden. docs/channels.md hat die Tabelle pro Plattform und die Begründung.

Drei Eigenschaften gelten auf jeder Plattform:

  • Die Mitgliedschaft des Bots ist die gesamte Berechtigungsgrenze. Jeder Aufruf verwendet das Token des Bots, der Agent sieht also genau das, was der Bot sieht.
  • Das Modell benennt nie einen Kanal. Die Tools sind serverseitig an den Kanal gebunden, in dem die Nachricht eingegangen ist — in einem Thread an den Kanal, der ihn enthält.
  • Außerhalb eines Kanals steuert sie nichts bei. Ein Run vom Dashboard, aus der API oder aus einem Zeitplan hat kein Verzeichnis, deshalb wird die Capability gar nicht erst angehängt — aus demselben Grund, aus dem knowledge ohne Collections nicht angehängt wird.

Rückfragen

Braucht ein Agent eine Entscheidung, die nur der Mensch treffen kann, fragt er, statt zu raten: ask_user_question nimmt eine bis zehn Multiple-Choice-Fragen, jede mit einer kurzen Überschrift, zwei bis sechs Optionen samt Bedeutung und der Angabe, ob mehrere gewählt werden dürfen. Die Konsole zeigt sie als Karte, eine Frage nach der anderen, mit Platz für eine eigene Antwort und einer Übersicht vor dem Senden; der Agent erhält die Auswahl nach Überschrift oder erfährt, dass die Person abgelehnt hat, und macht weiter.

Das Tool und sein Schema stammen aus AskUser in pydantic-ai-harness. Es ist in jedem neuen Agent und jeder Vorlage standardmäßig aktiv und lässt sich abschalten. Wo noch niemand antworten kann — ein Zeitplan, ein Webhook, die API —, erfährt der Agent, dass die Person abgelehnt hat.

Eine unbeantwortete Frage beendet die Arbeit des Agents nicht endgültig. Verlässt die Person die offene Karte, parkt der Run darauf — im Run-Verlauf als Wartet auf eine Antwort — und die Karte kehrt zurück, sobald sie die Unterhaltung wieder öffnet, auf jedem Gerät. Die Antwort setzt den Run dort fort, wo er stehen blieb. Antworten kann nur die gefragte Person, über POST /runs/{run_id}/answers; GET /runs/{run_id}/questions liest die Karte zurück. Nach einem Tag (QUESTION_EXPIRY_HOURS, 24) endet der Run als abgebrochen, und der Schritt hält fest, dass die Person nicht rechtzeitig geantwortet hat.

Nächste Schritte

Schaltflächen werden im Konsolenchat und den genannten Nachrichtenkanälen unterstützt. Öffentliche Widgets, gehostete Seiten und reine Embed-Sockets verarbeiten keine Vorschlagskarten und starten daher keinen kostenpflichtigen Follow-up-Lauf. Vor Generierung und Zustellung prüfen Kanalaufgaben die aufgezeichnete Absenderidentität, Bot-Bindung, aktuelle Zugriffsrichtlinie und Raummitgliedschaft erneut. Zugelassene Raumteilnehmer ohne verknüpftes Konto bleiben vom Herausgeber getrennt, dem die Kosten zugerechnet werden.

Die Schaltflächen für nächste Schritte erstellt ausschließlich ein separates Modell, ausgewählt unter Modell für nächste Schritte (model_profile_id). Ohne Auswahl sind Vorschläge deaktiviert. Wählen Sie nach dem Upgrade ein Profil in Follow-ups aus, um die Schaltflächen weiter zu nutzen. Das Hauptmodell erhält suggest_next_steps nicht und wartet weder beim Abschluss seiner Antwort noch bei der Annahme der nächsten Nachricht auf Vorschläge.

Nachdem die Antwort gespeichert und die Transaktion bestätigt wurde, liest eine optionale Hintergrundaufgabe die Frage dieses Runs und die gespeicherte Antwort. Sie liefert bis zu drei kurze Schritte in der Sprache der Person oder keine. Die Konsole erhält später ein Ereignis mit der genauen Unterhaltung und Antwort; ein neuerer Turn bleibt unberührt. Gespeicherte Schaltflächen bleiben nach dem Wiederöffnen erhalten. Slack, Teams, Telegram und Mattermost beginnen diese Arbeit nach dem Senden der Hauptantwort und senden die Schaltflächen separat.

Das Hilfsmodell hat keine Tools und keine Anweisungen des Hauptagenten. Sein eigener Run prüft Agenten-, Abteilungs- und Organisationsbudgets, übernimmt den Datenschutz beim Tracing und erfasst seine Kosten separat, auch bei Fehlern. Die Kosten der Hauptantwort bleiben unverändert. Jeder Prozess erlaubt vier solche Aufgaben gleichzeitig; weitere werden übersprungen. Die Modellarbeit ist auf 15 Sekunden, höchstens zwei Anfragen und 512 Ausgabetokens begrenzt. Es gibt keine automatische Wiederholung. Beim Herunterfahren oder Verbindungsverlust können Vorschläge verloren gehen; die Antwort wird niemals durch einen Fehler ersetzt.

Activity kennzeichnet den Hilfs-Run als Nächste Schritte. Er behält die Leseberechtigungen der ursprünglichen Unterhaltung und zählt zu deren Gesamtkosten, ohne sie als beschäftigt zu markieren oder ihren letzten Haupt-Run zu ersetzen.

suggest_next_steps bleibt für Verlauf und bestehende Spec-Einstellungen die gespeicherte Identität der Schaltflächen; das Hauptmodell kann es nicht aufrufen.

propose_setup schlägt bis zu drei Dinge zum Einrichten vor, als Karten unter der Antwort: einen eigenen Agent der Person, eine Routine oder einen Dienst zum Verbinden. Eine Karte wirkt mit einem Klick - eine Routine öffnet den Routinendialog ausgefüllt, auf dem Agent, der sie vorgeschlagen hat; ein Agent ist eine Frage an den Architect.

Eine Verbindung, die ihren Server mit dem Katalogschlüssel nennt, ist die Karte dieses Dienstes - mit seinem Logo und seinem Verbinden, das direkt zur Anmeldung beim Anbieter und zurück ins Gespräch führt -, und eine ohne Schlüssel öffnet die Seite der MCP-Server an ihrem Hinzufügen-Button. Der AgenticOS-Agent beendet sein Kennenlerngespräch damit und bietet eine Verbindungskarte an, wann immer eine Anfrage einen Dienst braucht, den die Person noch nicht verbunden hat. Die Karten gibt es nur in der Konsole; eine Chat-App zeichnet sie nicht.

Sie ist im Starter-Agent und im Architect eingeschaltet, und ein Autor kann sie abschalten. Ein Sprachanruf lässt sie weg.

Was eine Bindung ändern darf

Der Katalog ist die Antwort des Deployments auf „was existiert“. Ein capabilities[]-Eintrag in einem Spec ist die Antwort eines Agents auf „wie benutze ich es“, und er darf vier Dinge ändern:

Feld Wirkung
config Gegen das Schema dieser Capability validiert, beim Veröffentlichen, nicht zur Laufzeit
approval default | required | never für jedes Tool, das die Capability beisteuert
tool_approval Dasselbe, pro Tool, überschreibt approval
tool_overrides Der name und die description, die das Modell sieht, pro Tool
secret_id Welches Secret der Organisation eine deklarierte Schlüsselanforderung erfüllt
enabled Aus, ohne die Konfiguration zu verlieren

Die Genehmigung ist der Grund, warum eine Capability ihre Tools überhaupt deklariert. „Darf dieser Agent Dateien schreiben“ und „darf er sie lesen“ sind zwei Entscheidungen, obwohl eine Capability beide beantwortet — deshalb bleibt das Aktivieren pro Capability, während das Genehmigen pro Tool geschieht. default fragt niemanden; side_effecting sagt dem Builder nur, welche Tools auf die Welt einwirken.

Die Beschreibung eines Tools ist der Prompt mit der größten Hebelwirkung im Produkt

Sie ist das, was das Modell liest, bevor es sich zum Aufruf entscheidet, und sein Name steuert genauso stark: search_refund_policy ist nicht search_documents. Ein Agent, der von demselben Tool ein anderes Verhalten braucht, braucht meist eine andere Formulierung dieser beiden und keine zweite geschriebene Capability.

Am stabilen id des Tools verschlüsselt, nie am Namen, den das Modell sieht

Das ist es, was ein Genehmigungs-Gate an einem umbenannten Tool hängen lässt. Es am sichtbaren Namen zu verschlüsseln, hieße, dass eine Umbenennung das Gate still entfernt und ein Aufruf mit Seiteneffekten dann unbeaufsichtigt läuft, ohne dass etwas es meldet. Eine id, die keine solche Capability bereitstellt,

wird beim Veröffentlichen abgelehnt, und ebenso ein Name, den kein Modell aufrufen könnte.

Scopes

Eine Capability darf Scopes deklarieren, die die Organisation gewährt haben muss; geprüft wird, wenn der Agent zusammengebaut wird:

Scope Deklariert von
knowledge:read knowledge, skills
conversations:read conversation_search
web:read web_research
web:fetch web_fetch
web:browse browser_use
code:execute code_execution
sandbox:execute sandbox
agents:delegate subagents

Alle acht werden heute standardmäßig gewährt

DEFAULT_GRANTED_SCOPES in app/services/agent_registry.py. Die Verwaltung von Scopes pro Organisation ist Roadmap-Arbeit; die Prüfung ist in der Zwischenzeit aktiv und ehrlich statt abgeschaltet und vergessen.

agents:delegate ist nicht das Tor dafür, an wen delegiert werden darf

Das ist agents:run, geprüft an der veröffentlichenden Person gegen die Zeile jeder Delegierten. Dieser Scope beantwortet eine Frage, die keine Berechtigung beantworten kann: ob dieses Deployment Agents überhaupt erlaubt, Agents aufzurufen. Nehmen Sie ihn aus dieser Menge heraus, und die Delegation ist mit einer Änderung überall aus.

Ein Betreiber, der keine verschachtelten Runs oder Fan-out-Abrechnung will, entfernt ihn, und jeder Spec, der delegiert, sagt das dann beim Veröffentlichen statt um drei Uhr nachts. conversations:read ist derselbe Hebel für die Unterhaltungssuche: Eine Änderung hält jeden Agent davon ab, vergangene Unterhaltungen zu lesen, für ein Deployment, das ein Transkript für zu sensibel hält, um durchsuchbar zu sein, wie eng der Korpus auch gefasst ist.

Was ein Tool dem Modell sagt

Eine Tool-Definition ist ein Prompt. Das Modell wählt ein Tool aus und füllt seine Argumente aus, und zwar aus nichts als dem Text, der daran hängt — deshalb trägt jedes Tool hier vier Dinge, und das vierte ist das, was üblicherweise weggelassen wird:

  1. Was es tut, in einem Satz. Das ist auch das, was der Builder neben dem Genehmigungs-Häkchen zeigt, es wird also einmal geschrieben und von beiden gelesen.
  2. Wann man es benutzt und wann etwas anderes. create_chart sagt, dass es für Zahlen ist, die man bereits hat, und generate_image für etwas, das gezeichnet werden soll; glob sagt, dass es rekursiv sucht, wo ls das nicht tut.
  3. Was jedes Argument bedeutet, einschließlich seines Standards und seiner Obergrenze.
  4. Was zurückkommt — die Form der Antwort, wie ein Fehlschlag aussieht und wo das Ergebnis ein abgeschnittener Ausschnitt statt der ganzen Menge ist. Ein Modell, das nicht weiß, dass grep je nach output_mode in drei verschiedenen Formen antwortet oder dass glob bei 100 Pfaden aufhört, schließt aus einem Ausschnitt, als wäre er alles.

Alle vier erreichen das Modell in einer Form, und es ist die von pydantic-ai selbst: die Prosa in <summary>, die Beschreibung der Rückgabe in <returns>.

Ein hier geschriebenes Tool bekommt das umsonst — das Framework baut es aus dem Returns:-Abschnitt des Docstrings.

Ein Tool, das aus einer Bibliothek kommt, wird mit einer ausdrücklichen Beschreibung registriert, was diesen Weg abschneidet. Sein Text läuft deshalb durch ToolText in app/agents/capabilities/_tool_text.py, das rendert, was das Framework gerendert hätte.

Zwei Konventionen in einer Tool-Liste sind eine Sache mehr, die das Modell zusammenbringen muss, und tests/test_tool_text_shape.py ist das, was dafür sorgt, dass es eine bleibt: Es pinnt ToolText gegen ein Tool, das pydantic-ai selbst baut, und prüft, dass die Tools jeder Capability eine Rückgabeform mitführen.

Das deckt auch die Tools ab, die dieses Deployment nicht geschrieben hat: planning und die Delegations-Tools bekommen den Text dieses Repositories, web_fetch und search_tools werden dort neu beschrieben, wo sie gebaut werden, und read_tool_result sowie read_skill_resource werden direkt auf dem Toolset der Bibliothek neu beschrieben. Eines davon war den Aufwand über die Konsistenz hinaus wert — der Satz der Bibliothek für read_tool_result sagte nichts darüber, womit ein Handle antwortet, und das ist das Einzige, was ein Modell mit einem Handle braucht.

Ein Tool aus einer Bibliothek, für das dieses Repository keinen Text hat, behält den der Bibliothek, und das ist der richtige Standard: run_skill_script wird ausgeschlossen statt beschrieben, und falls es je ankommt, kommt es mit dem an, was sein Autor geschrieben hat.

Ein Fehler, ein Ergebnis und eine Ablehnung

Wie ein Tool über Probleme berichtet, entscheidet, was das Modell als Nächstes tut, und die drei sind nicht austauschbar.

Der Fehlschlag Was das Tool tut Warum
Die eigenen Argumente des Modells — eine Diagrammreihe mit der falschen Anzahl Werte, eine Kontextdatei, die es nicht gibt, ein NameError in selbst geschriebenem Python Wiederholungsaufforderung Ein anderer Aufruf ist eine plausible Behebung, und die Meldung sagt, wie ein korrekter Aufruf aussieht
Ein vorübergehender Fehlschlag dessen, was hinter dem Tool steht — ein Suchanbieter ist ausgefallen, eine Wissensdatenbank läuft in einen Timeout Wiederholungsaufforderung Ein Fehler in der Form eines Ergebnisses liest sich als „nichts gefunden“, und das Modell antwortet dann selbstbewusst aus dem Gedächtnis, ohne zu sagen, dass es das musste
Ein Ergebnis, das schlicht eine schlechte Nachricht ist — ein Befehl mit Exit-Code ungleich null, eine Suche ohne Treffer, ein Kanal, den dieser Bot nicht sehen kann Als Text zurückgegeben Es ist die Antwort. Das Modell denkt darüber nach und macht weiter
Eine Ablehnung — eine Berechtigungsregel, eine Capability, die das Deployment nicht anbietet Als Text zurückgegeben Eine Wiederholungsaufforderung lädt das Modell ein, nach einem Weg daran vorbei zu suchen

Wiederholungen sind budgetiert: Ein Tool-Aufruf bekommt einen Versuch, sich selbst zu korrigieren, und eine jenseits dieses Budgets ausgelöste Wiederholung beendet den ganzen Run statt des Aufrufs. Der letzte Versuch gibt daher seine Meldung zurück, statt zu werfen — gesteuert, solange Budget dafür da ist, und nie schlechter als die Antwort, die er ohnehin gegeben hätte. Der eine Helfer, der das entscheidet, ist app/agents/capabilities/_failures.py; pydantic-ai-backend hält dieselbe Regel für die Workspace-Tools.

Diese Liste erweitern

Capabilities sind Code — nichts, was ein Betreiber tippt, bringt eine neue hervor, und genau das macht die Menge dessen, was ein Agent tun kann, prüfbar. Siehe Eine Capability hinzufügen für eine neue, oder Einer Capability ein Tool hinzufügen, wenn die Capability bereits existiert.

Für Tools, die hier niemand schreiben muss, siehe MCP.