Bygg robusta LLM-applikationer med rätt prompt-mönster.
De flesta LLM-appar börjar med en lång sträng i en Python-fil. Den växer, någon lägger till ett undantag, en annan klistrar in ett exempel, och plötsligt är prompten 400 rader som ingen vågar röra. När en ny modellversion släpps eller en kund klagar på ett konstigt svar finns det ingen som vet vilken mening som faktiskt styr beteendet. Det är inte en modellfråga - det är en arkitekturfråga.
Jag har byggt prompt-lager för svenska bolag inom juridik, support och e-handel, och mönstren nedan är de som håller när trafiken och teamet växer. Jag behandlar prompten som det den är: ett gränssnitt mot en icke-deterministisk komponent. Och precis som med ett API-gränssnitt vill jag ha tydliga kontrakt, versioner och tester - inte en sträng som muteras på känsla.
Separera system, instruktion och kontext
Den första uppdelningen jag alltid gör är att skilja på tre saker som ofta blandas ihop i en enda sträng. Systemprompten beskriver roll, ton och hårda regler och ändras sällan. Uppgiftsinstruktionen beskriver vad som ska göras just nu. Kontexten är det dynamiska: hämtade dokument, användarens historik, verktygssvar. När dessa tre ligger separat blir det möjligt att testa och versionera dem var för sig.
Uppdelningen har en konkret bieffekt som ofta glöms bort: den gör prompten begriplig för fler än den som skrev den. När roll, uppgift och data ligger blandat i en sträng blir varje ändring en chansning, eftersom ingen vet vad som hänger ihop med vad. När delarna är separerade kan en kollega läsa systemprompten och förstå appens grundbeteende på trettio sekunder, utan att vada genom inklistrade exempel och dynamisk text.
- System: identitet och invarianter - det som ska gälla oavsett fråga.
- Instruktion: uppgiften och dess format, gärna med ett par konkreta exempel.
- Kontext: data som injiceras vid körning, tydligt avgränsad så modellen inte förväxlar den med instruktioner.
Avgränsa kontext för att stoppa prompt injection
Så fort du injicerar text du inte själv skrivit - ett mejl, en PDF, en webbsida - riskerar du att den texten innehåller instruktioner som modellen lyder. Jag ramar alltid in extern kontext med tydliga avgränsare och en mening i systemprompten om att innehållet mellan avgränsarna är data, inte kommandon. Det stoppar inte allt, men det höjer ribban betydligt. Hör texten hemma i ett RAG-flöde är detta en del av en bredare hämtningspipeline, vilket jag går djupare på i artikeln om RAG i produktion.
Det viktiga att förstå är att en LLM inte har någon inbyggd skillnad mellan dina instruktioner och den data du matar in - allt är text för modellen. Därför är avgränsningen inte en formalitet utan en faktisk säkerhetsåtgärd, och den blir bara viktigare ju mer av appens kontext som kommer från otrodda källor. I appar där användare kan ladda upp egna dokument behandlar jag allt sådant innehåll som potentiellt fientligt och kombinerar inramningen med validering av svaret efteråt, så att en lyckad injektion ändå inte slinker förbi i utdata.
Versionera prompts som kod
En prompt som ändras utan spårbarhet är en regression som väntar på att hända. Jag lägger prompts i versionshanterade filer eller i ett litet register med id och version, aldrig hårdkodade mitt i affärslogiken. Varje ändring går via samma granskning som vanlig kod. Det gör att jag kan svara på frågan "vad ändrades mellan förra veckan och nu" - en fråga som är omöjlig att besvara om prompten bara muteras direkt i koden.
Just den här disciplinen är något jag ofta inför inom AI Engineering, eftersom det är förutsättningen för allt annat: utan versioner går det inte att utvärdera, och utan utvärdering vet man inte om en ändring hjälpte.
Few-shot, format och när exempel skadar
Exempel i prompten är kraftfulla men inte gratis. Varje exempel kostar tokens vid varje anrop och kan låsa modellen vid ett mönster den inte borde generalisera från. Min tumregel: börja utan exempel, lägg till dem först när du ser ett konkret fel de rättar, och mät kostnaden. För strikt format är exempel ofta sämre än att be om ett JSON-schema direkt - vilket jag beskriver i artikeln om structured outputs.
Hantera fel och tomma svar
En produktionsprompt måste ha en plan för när modellen svarar fel, vägrar, eller returnerar något oväntat. Jag bygger alltid in en valideringsnivå efter modellen: kontrollera att svaret matchar förväntat format, och ha en tydlig fallback - ett omförsök med justerad prompt, en degradering till en enklare modell, eller ett ärligt felmeddelande. Det värsta är en app som tyst visar nonsens för användaren.
Komponera prompts istället för att duplicera dem
När en app växer får den ofta flera prompts som delar stora delar: samma rollbeskrivning, samma tonregler, samma formatkrav. Frestelsen är att kopiera. Problemet är att en ändring då måste göras på fem ställen, och i praktiken blir det fyra - den femte glöms bort och börjar bete sig annorlunda. Jag bygger därför prompts av återanvändbara delar: en gemensam systembas, formatregler som modul, och uppgiftsspecifika tillägg ovanpå. Då ändrar jag tonen på ett ställe och alla prompts följer med. Det är samma princip som god kodstruktur, applicerad på text.
Mät innan du finslipar
Det är frestande att justera ord i prompten tills ett enskilt testfall ser bra ut. Problemet är att man då optimerar mot ett exempel och försämrar tio andra utan att märka det. Jag kopplar prompt-ändringar till en utvärderingssvit som körs vid varje förändring, så att jag ser den faktiska effekten över ett representativt urval. Hur jag bygger sådana sviter beskriver jag i artikeln om evals. En annan sak jag lärt mig: skriv ner varför en prompt ser ut som den gör. En kommentar om att en viss formulering löste ett konkret fel sparar nästa person från att "förenkla" bort något som faktiskt fyllde en funktion.
Relaterat
- Multi-agent systems: Orchestration, delegation och konfliktlösning
- AI-gatewayar: Kong AI Gateway, Portkey och LiteLLM i produktion
- Streaming i LLM-appar: SSE, WebSockets och backpressure-hantering
Vill du se hur det här ser ut i en skarp leverans finns ett konkret exempel bland mina kundcase.
Vill du ta det vidare?
Om ni har en prompt som vuxit sig ohanterlig eller vill lägga en stabil grund från start hjälper jag gärna till. Boka ett förutsättningslöst samtal så går vi igenom ert upplägg tillsammans.
“En prompt utan versioner är en regression som väntar på att hända - behandla den som kod, inte som en kommentar.”
- Simon Axelsson
Vanliga frågor
- Var ska prompts ligga i kodbasen?
- I versionshanterade filer eller ett litet promptregister med id och version, separat från affärslogiken. Då kan du granska, testa och rulla tillbaka ändringar precis som med vanlig kod, och du slipper hårdkodade strängar utspridda i appen.
- Hjälper few-shot-exempel alltid?
- Nej. Exempel kostar tokens vid varje anrop och kan låsa modellen vid ett mönster den inte borde generalisera från. Börja utan exempel, lägg till dem när du ser ett konkret fel de rättar, och mät effekten över ett urval snarare än ett enskilt testfall.
- Hur skyddar jag mig mot prompt injection?
- Ram in all extern text med tydliga avgränsare och förklara i systemprompten att innehållet är data och inte instruktioner. Validera dessutom modellens svar efteråt. Det eliminerar inte risken helt men höjer ribban väsentligt jämfört med att klistra in rådata direkt.
Simon Axelsson är senior IT-konsult och grundare av SIAX Technology AB. Han hjälper nordiska företag med molninfrastruktur, dataplattformar och AI-automation.
Fler artiklar