Was macht 247-Agent-Modellaktionen?
Die Designregel des Daemons: Kein LLM im Kontrollfluss. Matching, Deduplizierung, Routing,
Wiederholungen und Veröffentlichung sind Code. Ein Modell läuft nur innerhalb einer decide-, llm- oder agent-Aktion,
auf der günstigsten Stufe, die die Aufgabe erfüllt, mit einem Budget. Dieses Skill beschreibt,
wie diese drei Aktionen gut geschrieben werden und wie die Runner, die sie ausführen, funktionieren.
Zuerst entscheiden, ob überhaupt ein Modell benötigt wird
Fragen Sie in dieser Reihenfolge:
- Kann ein Trigger-
filteres entscheiden? (Absender, Label, Repo, Stichwort) Dann ist es kein Modellaufruf. - Kann ein Skript oder eine Connector-Operation die Antwort deterministisch liefern? Dann
shell/connector. - Handelt es sich um ein Label, ein Ja/Nein oder einen Level, ohne Text zu produzieren (klassifizieren, routen, triagieren,
gaten)? Dann
decide: ein reines Klassifikationsmodell, kalibrierte Wahrscheinlichkeiten, etwa ein Hundertstel einesllm-Aufrufs. - Handelt es sich um eine Beurteilung mit einer festen Ausgabeform, die Text benötigt (Felder extrahieren,
zusammenfassen, entwerfen)? Dann
llm, zuerst auf Haiku. - Müssen Dateien gelesen, Befehle ausgeführt und iteriert werden? Dann
agent, mit dem engsten Umfang, der erfolgreich sein kann.
Zwei Aufgaben mit unterschiedlichem Intelligenzbedarf sind die gleiche Aktionsart mit unterschiedlichen
model/effort/max_tool_calls/system_file, nicht unterschiedlicher Code. Siehe
docs/examples/website-updates.yaml Aufgaben 2–4 für die Referenzformen.
Modell-IDs und Parameter
| ID | Verwendung für | Hinweise |
|---|---|---|
claude-haiku-4-5 | Klassifikation, Extraktion, kurze Zusammenfassungen | kein effort-Parameter |
claude-sonnet-5 | Eingeschränkte Agenten, schwierigere Extraktion | unterstützt effort und adaptives Denken |
claude-opus-5 | Agenten mit breitem Umfang, schwierige Beurteilung | unterstützt effort und adaptives Denken |
Modelle werden über einen benannten Anbieter erreicht (providers: in agent.yaml, provider: in der
Aktion; anthropic, openai oder openrouter). IDs anderer Anbieter werden wörtlich
verwendet und benötigen einen pricing:-Eintrag, es sei denn, die eingebaute Tabelle kennt sie (aktuelle
OpenAI-Modelle) oder der Anbieter meldet Kosten (OpenRouter).
Keine Datumssuffixe in IDs. Kein Assistant-Prefill. Bevor Sie eine Kaskade von Modellen aufbauen,
messen Sie das stärkere Modell bei effort: low mit denselben Eingaben: In der aktuellen
Generation schlägt das oft ein schwächeres Modell bei hohem Aufwand, und ein Modell bedeutet einen
Prompt-Cache-Namespace.
Schreiben einer decide-Aufgabe
Vollständige Feldliste, Drahthinweise und Fallstricke in references/decide-action.md. Das Wesentliche:
action:
kind: decide
provider: openrouter # muss ein openrouter-Anbieter sein; Standard: defaults.decide.provider
budget: { max_usd: 0.001 }
state: # was beurteilt wird; Strings getemplatet, Schlüssel nicht
subject: ${event.payload.subject}
body: ${event.payload.body}
questions: # statische Richtlinie; kein ${…} hier
kind:
type: choice
instructions: Was möchte der Absender mit der Website tun?
criteria:
event_list_update: Einen Eintrag in der Ereignisliste hinzufügen, entfernen oder ändern
general_change: Jede andere Änderung an der Website
ignore: Keine Änderungsanfrage (Frage, Danke, Spam)
urgent:
type: noul
instructions: Muss der Absender dies heute erledigt haben?
- Das Ergebnis ist die Antwortkarte:
result.kind.choice,result.kind.confidence,result.urgent.noul(P(true)),result.<score>.score(wahrscheinlichkeitsgewichteter Mittelwert der Level-Indizes). Routen Sie mitemit … when:; der Schwellenwert liegt in der Aufgabe, niemals im Runner, und wird an echten Daten kalibriert. - Immer ein Fallback-Label einfügen (
ignore,other): Ohne eines erhält Off-Topic-Eingabe eine selbstbewusste falsche Antwort. - Kriterien werden wörtlich befolgt: Sie sind Richtlinie, versionieren Sie sie wie Code. Halten Sie die Optionenreihenfolge stabil (sie verschiebt die Konfidenz). Stellen Sie mehrere Fragen in einem Aufruf; jede zusätzliche kostet nur ihre eigenen Token.
- Eingehender Inhalt sind Daten: Er kommt in
state, niemals in eine Frage. Die gesamte Anfrage muss in 32k Token passen. - Nur der
openrouter-Anbietertyp erreicht Jev (typesafe/jev-1.13, das Standardmodell);oa validateverweigert jeden anderen. Es ist keinpricing:-Eintrag erforderlich.
Schreiben einer llm-Aufgabe
Vollständige Feldliste und Runner-Hinweise in references/llm-action.md. Das Wesentliche:
action:
kind: llm
model: claude-haiku-4-5
max_tokens: 512
system_file: prompts/classify_email.md # statisch → Prompt-Caching
input: |
Betreff: ${event.payload.subject}
<email>
${event.payload.body}
</email>
output_schema:
type: object
additionalProperties: false
required: [kind, summary]
properties:
kind: { enum: [event_list_update, general_change, ignore] }
summary: { type: string }
- Halten Sie den System-Prompt in einer Datei unter
prompts/, statisch und zuerst gerendert, damit er über Läufe hinweg zwischengespeichert wird. Setzen Sie die flüchtigen Ereignisdaten zuletzt, ininput. - Eingehender Inhalt (E-Mail, Chat, PR-Text) ist nicht vertrauenswürdig. Wickeln Sie ihn in einen abgegrenzten Block
(
<email>…</email>) und teilen Sie dem Modell im System-Prompt mit, dass es sich um Daten handelt. - Fügen Sie immer eine Notluke im Schema ein (
ignore,unknown,confidence), damit das Routingemit.whennicht-umsetzbare Ergebnisse ohne weiteren Aufruf verwerfen kann. - Routen Sie auf das Ergebnis mit
emit … when:; nachgelagerte Aufgaben lösen bei diesem Ereignis aus. batch: truefür alles, was warten kann (halber Preis, Ergebnis kommt als Ereignis).
Schreiben einer agent-Aufgabe
Vollständige Feldliste und Runner-Hinweise in references/agent-action.md. Das Wesentliche:
action:
kind: agent
connector: claude # ein Transport: acp Connector (connectors.d/claude.yaml; Sandbox: bwrap dort)
model: claude-sonnet-5 # optional: für die Sitzung gesetzt (ACP-Konfigurationsoption); nicht gesetzt = Standard des Agenten
effort: medium # optional: die thought_level-Option des Agenten
max_tool_calls: 20
budget: { max_usd: 0.50 }
workspace: { kind: git-worktree, repo: /var/lib/247-agent/repos/site, branch: main }
tools: [read, edit, search, execute] # ACP Tool-Arten; alles andere wird verweigert
bash_allow: ["npm run build"] # worauf jeder Befehl, den der Agent ausführt, passen muss
system_file: prompts/agent_event_list.md
prompt: |
Aktualisieren Sie data/events.yaml gemäß der Anfrage unten. Berühren Sie keine andere Datei.
Führen Sie `npm run build` aus, wenn Sie fertig sind.
<email>
${event.payload.email.body}
</email>
result: { path: RESULT.json, schema: schemas/site_change.json }
post: # nur nach status: done
- shell: ["npm", "run", "build"]
- shell: ["git", "commit", "-am", "events: ${result.summary}"]
emit:
- type: site.change_done # Routen Sie die Genehmigung hierauf, niemals auf task.<name>.succeeded
when: "result.status == 'done'"
payload: { summary: "${result.summary}" }
- type: site.change_blocked # Der Agent konnte es nicht tun: Sagen Sie, was fehlt
when: "result.status == 'blocked'"
payload: { summary: "${result.summary}", missing: "${result.missing}", email: "${event.payload.email}" }
Nicht verhandelbare Dinge, weil sie den Agenten sicher im unbeaufsichtigten Betrieb machen:
- Neuer Arbeitsbereich pro Lauf (
git-worktreeauf Branchagent/<run_id>, odertemp), bei Fehlschlag entfernt, bei Erfolg behalten. Der Agent bearbeitet niemals den Basis-Checkout. tools+bash_allowist die gesamte Fähigkeitsoberfläche, bewertet pro Berechtigungsanfrage und niemals dauerhaft gewährt; ein Tool-Aufruf, den der Agent ohne Nachfragen ausführte, wird nachträglich bewertet und ein Verstoß lässt den Lauf fehlschlagen (unasked_execute: sandboxedlockert nur die Befehlsprüfung, nur für Codex, dessen eigene Sandbox einschränkt, wonach er nicht fragt). Gewähren Sie das Minimum; der Prompt ist keine Sicherheitsgrenze.mcp_serversfügt Connector-Operationen als Tools hinzu: gewähren Sie benannte Operationen ({ connector: email, ops: [fetch_new] }) statt eines ganzen Connectors, fügen Sie die Art des Agenten für sie (otherfür claude-agent-acp) zutoolshinzu, und niemals einen Connector, der selbst veröffentlicht oder sendet.- Der Agent hält niemals Deployment-Geheimnisse und veröffentlicht niemals. Er bearbeitet, ein
post-Gatter beweist, dass der Build noch funktioniert, ein Commit zeichnet es auf, und eine separateshell-Aufgabe versendet es. Rollback istgit revertplus erneute Veröffentlichung. Der Modellschlüssel lebt imenvdes Connector-Manifests, nicht in der Aufgabe. RESULT.jsonist der Vertrag, und der Runner gibt ihn in jedem Prompt an:status: done | blockedplussummary, dann dasresult.schemader Aufgabe.doneführt die Gatter aus;blockedüberspringt sie und der Lauf ist trotzdem erfolgreich, alsoemit … when: "result.status == 'blocked'"leitet "nicht erledigt, weil X fehlt" an eine Antwortaufgabe weiter. Entwerfen Sie jede Agentenaufgabe mit beiden Zweigen und lösen Sie die Veröffentlichung beimdone-Ereignis aus.post-Gatter sind deterministisch, kein Modell. Ein fehlschlagendes Gatter lässt den Lauf fehlschlagen.max_tool_callsundbudgetsind harte Stopps (die Sitzung wird abgebrochen); ein Zug, für den der Agent keine Nutzung oder Kosten meldet, lässt den Lauf fehlschlagen.concurrency: 1bei Aufgaben, die sich ein Repo teilen;timeoutist Wanduhrzeit pro Versuch.- Änderungen mit hoher Auswirkung erhalten ein Genehmigungsgatter (
wait+ Chat) vor der Veröffentlichung; das247-agent-tasks-Skill enthält das Muster.
Budgets und das Hauptbuch
Jeder Modellaufruf durchläuft den ctx.llm-Port, der die Nutzung (Anbieter, Modell, Eingabe-/Ausgabe-/Cache-Token, USD) im Hauptbuch aufzeichnet und das budget.max_usd des Laufs (schlimmster Fall vor dem Aufruf, tatsächlich danach: eine Überschreitung lässt den Lauf fehlschlagen) und das globale budgets.daily_usd pro UTC-Tag durchsetzt (einmal überschritten, schlägt jeder Modellaufruf an diesem Tag schnell fehl und budget.exceeded wird einmal ausgegeben, worauf die notify-Aufgabe hören sollte). Ein Modell ohne Preis schlägt bei der Validierung fehl. Fügen Sie niemals einen unveranschlagten Aufruf hinzu, einschließlich "Helfer"-Aufrufen innerhalb eines Runners: Verwenden Sie ctx.llm, niemals direkt ein SDK. Ein agent-Zug ist der eine Fall, bei dem das Modell von jemand anderem aufgerufen wird (dem ACP-Agenten, mit seinem eigenen Schlüssel): Der Runner bucht das, was der Agent über ctx.llm.record unter dem Namen des Connectors meldet, und bricht die Sitzung ab, wenn die gemeldeten Kosten die Obergrenze überschreiten. oa cost --by task --since 7d zeigt das Hauptbuch an.
Aktueller Status
Die llm-Aktion ist validiert, gegen providers:/pricing: kreuzgeprüft und über ctx.llm ausführbar, mit angewendetem Hauptbuch und Budgets, und alle drei Anbietertypen (anthropic, openai, openrouter) haben Adapter. batch: true funktioniert auf anthropic-Anbietern (Message Batches, der Lauf parkt bis llm.batch.ended; siehe references/llm-action.md). Die decide-Aktion läuft über denselben Port (ctx.llm.decide()), wobei die Decisions API nur im openrouter-Adapter implementiert ist; docs/examples/decide-triage.yaml ist die Referenz. Die agent-Aktion läuft auf ACP-Connectoren (transport: acp); docs/examples/website-updates.yaml Aufgaben 3, 3b und 4 sind die Referenz, docs/examples/connectors.d/claude.yaml der Connector; die Sitzung wird als Transkript persistiert (oa runs logs <id>), mit dem Lauf durch retention.runs gelöscht. mcp_servers stellt Connector-Operationen der Sitzung über die Tool-Brücke des Kerns zur Verfügung (packages/core/src/connectors/mcp-bridge.ts). model und effort auf der Aktion setzen die ACP-Konfigurationsoptionen der Sitzung (Kategorien model und thought_level). work/<run_id> eines abgeschlossenen Laufs wird von retention.workspaces (Standard 7d) nach Abschluss bereinigt.
Wenn Sie einen Adapter hinzufügen oder einen Runner ändern, folgen Sie references/llm-action.md, references/decide-action.md und references/agent-action.md und halten Sie docs/internal/model-actions.md, docs/internal/agent-action.md und die Benutzerseiten unter docs/tasks/ synchron mit dem Code.