Laravel AI SDK 1.1 bringt Agent Skills zur Befehlsgliederung
Mit wachsenden Agent-Anweisungen geht viel unnötiger Kontext an das Modell verloren, da jede Anfrage alles mitschickt. Laravel AI SDK 1.1 fuehrt dafuer Agent Skills ein, bei deren Einrichtung einige Stolperfallen zu beachten sind.

Veröffentlicht am 24. Oktober • Zuerst erschienen auf dev-talk.com
Ein Skill ist ein Ordner mit einer SKILL.md Datei darin. Die Datei enthält oben eine kurze Beschreibung und darunter die vollständigen Anweisungen. Der Agent sieht immer die Liste der Namen und Beschreibungen. Den vollständigen Text liest er erst, wenn er entscheidet, dass ein Skill relevant ist. Man kann sich das wie ein Inhaltsverzeichnis vorstellen, das das Modell Kapitel für Kapitel öffnen kann.
composer require laravel/ai:^1.1 Enter fullscreen mode Exit fullscreen mode Die composer.json des Pakets verlangt PHP 8.3 oder neuer sowie illuminate/support 12 oder 13, und sie benötigt symfony/yaml, um die Skill-Dateien zu lesen.
Lege einen Ordner pro Skill unter resources/skills ab:
resources/skills/ └── refund-policy/ ├── SKILL.md └── templates/ └── reply.md Enter fullscreen mode Exit fullscreen mode --- name: refund-policy description: " Use when a customer asks for a refund or a partial credit." --- # Refund policy Refunds are allowed within 30 days. See templates/reply.md for the wording. Enter fullscreen mode Exit fullscreen mode Die Beschreibung ist Pflicht. Der Name ist optional und fällt andernfalls auf den Ordnernamen zurück. Schreibe die Beschreibung als Auslöser, beginnend mit "Use when...", denn sie ist das Einzige, was das Modell sieht, bis es den Skill lädt.
Implementiere den HasSkills Contract und gib zurück, wo die Skills liegen:
use Laravel\Ai\Contracts\Agent; use Laravel\Ai\Contracts\HasSkills; use Laravel\Ai\Promptable; use Stringable; class SupportAgent implements Agent, HasSkills { use Promptable; public function instructions (): Stringable | string { return 'You are a support assistant for Acme.'; } public function skills (): iterable { return [ resource_path ( 'skills' )]; } } Enter fullscreen mode Exit fullscreen mode 4. Prompte ihn wie gewohnt $response = ( new SupportAgent ) -> prompt ( 'Can I get a refund for order 1042?' ); Enter fullscreen mode Exit fullscreen mode Das ist alles. Im Hintergrund gibt das SDK dem Agenten ein zusätzliches Werkzeug namens LoadSkill, sodass du es nie von Hand hinzufügen musst.
Ich habe das echte LoadSkill Tool gegen eine Reihe von Test-Skill-Ordnern laufen lassen (die zusätzlichen Namen unten, custom-name und secret, sind nur meine Test-Fixtures). Die Beschreibung des Tools, die das Modell vorab liest, sieht so aus:
Load a skill's instructions before performing a task matching the skill's description. Pass a path to read one of the skill's bundled files instead. Available skills: - custom-name: Folder differs from name. - refund-policy: Use when a customer asks for a refund or a partial credit. - secret: Secret skill. - shipping: Use for shipping and delivery questions. Enter fullscreen mode Exit fullscreen mode Die Skill-Namen werden außerdem als feste Liste erlaubter Werte für das name-Argument des Tools angeboten. Wenn das Modell refund-policy lädt, erhält es den Inhalt der Datei plus eine Liste der anderen Dateien im Ordner:
<skill_content name="refund-policy"> # Refund policy Refunds are allowed within 30 days. See templates/reply.md for the wording. <skill_resources> templates/reply.md </skill_resources> Read one of these files by calling this tool again with the skill name and the file's path. </skill_content> Enter fullscreen mode Exit fullscreen mode Um templates/reply.md zu lesen, ruft das Modell das Tool ein zweites Mal mit einem Pfad auf. Deine Tokens werden also in drei Schichten ausgegeben: Namen und Beschreibungen immer, Anweisungen bei Bedarf, zusätzliche Dateien nur, wenn die Anweisungen das Modell dorthin schicken.
skills() kann eine Mischung aus Verzeichnissen, in PHP erzeugten Skill-Objekten und Closures zurückgeben. Eine Closure läuft beim ersten Mal, wenn das Tool die Skills benötigt, sodass du sie zum Beispiel aus einer Datenbank laden kannst, etwa pro Nutzer oder pro Tenant:
use Laravel\Ai\Skills\Skill; public function skills (): iterable { return [ resource_path ( 'skills' ), new Skill ( 'brand-voice', 'Use when writing customer-facing text.', 'Be friendly and brief.' ), fn () => TeamSkill:: query () -> where ( 'team_id', $this -> teamId ) -> get () -> map ( fn ( $row ) => new Skill ( $row -> name, $row -> description, $row -> body ) ), ]; } Enter fullscreen mode Exit fullscreen mode Der Skill-Konstruktor ist (name, description, instructions, files = [], path = null). Ich habe die Closure und den In-Memory-Skill mit einer gebündelten Datei getestet. Die Datenbankabfrage oben ist nur eine Illustration, wo eine Closure Daten herziehen kann (TeamSkill steht für ein eigenes Eloquent Modell von dir); es ist mein Beispiel, nicht aus der SDK-Dokumentation.
Ich habe versucht, Dateien zu lesen, die das Modell niemals erhalten sollte. Alle wurden mit einer schlichten Meldung abgewiesen:
Eines decken die Beschränkungen nicht ab: Ein Skill ist ein Text, dem das Modell folgen wird. Behandle Skill-Dateien wie Code und sei vorsichtig mit Closures, die Skills aus nutzerbearbeitbaren Inhalten laden, denn jeder, der diesen Text bearbeiten kann, kann deinen Agenten steuern.
Das Release fügt außerdem Cohere Textgenerierung, ein MoveFile Tool, Freigaben für eingebaute Tools und einen Fix hinzu, durch den Failover Provider-Tools überspringt, die der Fallback-Provider nicht unterstützt. Die Änderung, die dich aber überraschen kann, sind die Standardmodelle. Ich habe den Quellcode von 1.0.1 und 1.1.0 verglichen: Der Anthropic-Standard wechselte von claude-sonnet-5 zu claude-sonnet-5-5 und der Gemini-Standard von gemini-3.6-flash zu gemini-3.8-flash. Wenn du nie ein Modell auswählst, wechselt deine App beim Upgrade das Modell. Um die Kontrolle zu behalten, übergib ein Modell explizit:
( new SupportAgent ) -> prompt ( 'Hello', model: 'claude-sonnet-5' ); Enter fullscreen mode Exit fullscreen mode Die Provider lesen models.text.default aus ihrem Eintrag in config/ai.php, dort kannst du also auch ein Standardmodell festlegen. Das habe ich im Quellcode gelesen, aber nicht ausgeführt.
Halte die kurzen, immer relevanten Regeln in instructions(). Verschiebe themenspezifisches Material (eine Richtlinie, eine Prozedur, einen langen Stilguide) in Skills, damit es nur dann Tokens kostet, wenn es gebraucht wird.
Nein. 1.3.0 ist das neueste Tag, das ich gefunden habe. Der Skills-Code (LoadSkill, Skill und HasSkills) ist zwischen 1.1.0 und 1.3.0 identisch, daher gilt hier alles für beide.
Skills werden über einen normalen Tool-Aufruf bereitgestellt, daher müssen Modell und Provider, die du verwendest, Tool Calling unterstützen. Ich habe es nicht mit einem Live-Modell getestet.
Automatisch übersetzt. Das Original auf Englisch ist unten aufklappbar.
Original (Englisch) anzeigen
Posted on Oct 24 • Originally published at dev-talk.com
A skill is a folder with a SKILL.md file in it. The file has a short description at the top and the full instructions below. The agent always sees the list of names and descriptions. It reads the full text only when it decides a skill is relevant. Think of a table of contents that the model can open chapter by chapter.
composer require laravel/ai:^1.1 Enter fullscreen mode Exit fullscreen mode The package's composer.json asks for PHP 8.3 or newer and illuminate/support 12 or 13, and it needs symfony/yaml to read the skill files.
Put one folder per skill under resources/skills:
resources/skills/ └── refund-policy/ ├── SKILL.md └── templates/ └── reply.md Enter fullscreen mode Exit fullscreen mode --- name: refund-policy description: " Use when a customer asks for a refund or a partial credit." --- # Refund policy Refunds are allowed within 30 days. See templates/reply.md for the wording. Enter fullscreen mode Exit fullscreen mode The description is required. The name is optional and falls back to the folder name. Write the description as a trigger, starting with "Use when...", because it is the only thing the model sees until it loads the skill.
Implement the HasSkills contract and return where the skills live:
use Laravel\Ai\Contracts\Agent; use Laravel\Ai\Contracts\HasSkills; use Laravel\Ai\Promptable; use Stringable; class SupportAgent implements Agent, HasSkills { use Promptable; public function instructions (): Stringable | string { return 'You are a support assistant for Acme.'; } public function skills (): iterable { return [ resource_path ( 'skills' )]; } } Enter fullscreen mode Exit fullscreen mode 4. Prompt it as usual $response = ( new SupportAgent ) -> prompt ( 'Can I get a refund for order 1042?' ); Enter fullscreen mode Exit fullscreen mode That is all. Behind the scenes the SDK gives the agent one extra tool, LoadSkill, so you never add it by hand.
I ran the real LoadSkill tool against a set of test skill folders (the extra names below, custom-name and secret, are just my fixtures). The tool's description, which is what the model reads up front, looks like this:
Load a skill's instructions before performing a task matching the skill's description. Pass a path to read one of the skill's bundled files instead. Available skills: - custom-name: Folder differs from name. - refund-policy: Use when a customer asks for a refund or a partial credit. - secret: Secret skill. - shipping: Use for shipping and delivery questions. Enter fullscreen mode Exit fullscreen mode The skill names are also offered as a fixed list of allowed values for the tool's name argument. When the model loads refund-policy, it gets the body of the file plus a list of the other files in the folder:
<skill_content name="refund-policy"> # Refund policy Refunds are allowed within 30 days. See templates/reply.md for the wording. <skill_resources> templates/reply.md </skill_resources> Read one of these files by calling this tool again with the skill name and the file's path. </skill_content> Enter fullscreen mode Exit fullscreen mode To read templates/reply.md the model calls the tool a second time with a path. So your tokens are spent in three layers: names and descriptions always, instructions when needed, extra files only if the instructions send the model there.
skills() can return a mix of directories, Skill objects built in PHP, and closures. A closure runs the first time the tool needs the skills, so you can load them from a database, for example per user or per tenant:
use Laravel\Ai\Skills\Skill; public function skills (): iterable { return [ resource_path ( 'skills' ), new Skill ( 'brand-voice', 'Use when writing customer-facing text.', 'Be friendly and brief.' ), fn () => TeamSkill:: query () -> where ( 'team_id', $this -> teamId ) -> get () -> map ( fn ( $row ) => new Skill ( $row -> name, $row -> description, $row -> body ) ), ]; } Enter fullscreen mode Exit fullscreen mode The Skill constructor is (name, description, instructions, files = [], path = null). I tested the closure and the in-memory Skill with a bundled file. The database query above is only an illustration of where a closure can pull from ( TeamSkill stands for an Eloquent model of your own); it is my example, not from the SDK docs.
I tried to read files the model should never get. All were refused with a plain message:
One thing the limits do not cover: a skill is text that the model will follow. Treat skill files like code, and be careful with closures that load skills from user-editable content, because anyone who can edit that text can steer your agent.
The release also adds Cohere text generation, a MoveFile tool, approval for built-in tools, and a fix so failover skips provider tools the fallback provider does not support. But the change that can surprise you is the default models. I compared the source of 1.0.1 and 1.1.0: the Anthropic default moved from claude-sonnet-5 to claude-sonnet-5-5, and the Gemini default from gemini-3.6-flash to gemini-3.8-flash. If you never choose a model, your app changes model when you upgrade. To keep control, pass a model explicitly:
( new SupportAgent ) -> prompt ( 'Hello', model: 'claude-sonnet-5' ); Enter fullscreen mode Exit fullscreen mode The providers read models.text.default from their entry in config/ai.php, so you can also set a default model there. I read that in the source and did not run it.
Keep the short, always-relevant rules in instructions(). Move topic-specific material (a policy, a procedure, a long style guide) into skills so it only costs tokens when it is needed.
No. 1.3.0 is the newest tag I found. The skills code ( LoadSkill, Skill and HasSkills ) is identical between 1.1.0 and 1.3.0, so everything here applies to both.
Skills are delivered through a normal tool call, so the model and provider you use need to support tool calling. I did not test with a live model.