MuninMunin
Logga inKom igång gratis
Home/Journal/Din innehållsmodell är en prompt.
Engineering · 8 min read

Din innehållsmodell är en prompt.

Flytta en artikeltext från ett markdown-fält till en ordnad lista med typade block, och frontenden märker det knappt. Författaren märker det — och författaren är i allt högre grad en agent. Vad ett schema kan upprätthålla, vad det inte kan, och vad varje blocktyp kostar dig.

A small cobalt blue moulded drawer unit on a dark desk at night with exactly one of its identical drawers pulled open and empty.
Lådorna avgör vad som kan arkiveras.

Deklarera femton blocktyper för en artikeltext, och en författare får fullt lagligt använda en av dem femton gånger i rad. En vägg av prose validerar. Schemat har ingenting att säga om det, eftersom ingenting är fel — monotoni är ett giltigt dokument i ett blockbaserat CMS på samma sätt som en funktion på tusen rader är giltig C. Det är feltillståndet du ärver när författaren är en agent, och det är den intressanta hälften av det här inlägget. Börja med hälften som fungerade.

Varför ersätta ett markdown-fält med typade block?

För att författaren har blivit någon annan. Standardinnehållsmodellen för en artikel är ett fält som håller en markdown-sträng. Alternativet är en ordnad lista med typade block — en ingress, ett utdragscitat, en faktaruta, ett kodblock, en uppsättning huvudpoänger — vart och ett med deklarerade fält och sin egen validering. Byt det ena mot det andra, och frontenden märker det knappt. Det som skriver in i den märker det omedelbart.

Det gedigna argumentet för strukturerat innehåll handlar om utdata: du vill rendera samma artikel till webb, till e-post, till en app, till tryck, och en markdown-sträng bakar in antaganden om HTML. Det argumentet är verkligt och det är inte det jag driver. Fallet jag bryr mig om är indatasidan. När det som sätter samman en artikel slutar vara en person med en textredigerare och blir en process som håller ett verktygsschema, slutar innehållsmodellen vara lagring och börjar vara instruktioner. Jag har drivit det argumentet om gränssnitt tidigare; det här är samma skifte när det når innehållslagret, där det är avsevärt mindre abstrakt.

Vad låter en markdown-sträng egentligen en författare göra?

Vad som helst. John Gruber släppte Markdown 2004 som ett sätt för en människa att skriva något som till slut skulle bli HTML, och dess definierande egenskap är tillåtenhet — syntax som inte går ihop degraderar till bokstavlig text i stället för att fallera. En markdown-kolumn accepterar en tom sträng, en roman, sex råa <div>-ar, och ett utdragscitat förfalskat med ett blockquote. Allt är giltigt, eftersom kolumnen inte har någon åsikt.

Den tillåtenheten är en fördel när en människa skriver och en redaktör läser. Den är en risk när författaren är en process som gör exakt det som det senaste den läste antydde, och ingen människa ser resultatet innan det landar i en databas.

jsonformen på det
// ett fält, en sträng, allt går
{ "body": "## Varför vi gjorde det\n\nDe flesta B2B-plattformar växer genom förvärv…" }

// en ordnad lista med typade block
{ "body": [
    { "key": "lead",  "type": "lead",  "props": { "text": "…" } },
    { "key": "why",   "type": "prose", "props": { "markdown": "## Varför…" } },
    { "key": "quote", "type": "pull_quote",
      "props": { "quote": "…", "source": "…" } },
    { "key": "costs", "type": "editorial_list",
      "props": { "items": ["01 | …", "02 | …"] } }
] }

Varför betyder textens form mer när författaren är en agent?

För att schemat är den enda brief författaren garanterat läser. En mänsklig skribent plockar upp husstilen från en stilguide, en redaktör och tre års läsning av publikationen. En agent får innehållsmodellen — fältnamn, typer och beskrivningar — inläst i sin kontext, och inte mycket mer. Det som inte finns i schemat finns inte i briefen.

När du väl accepterar det slutar fältets description vara dokumentation och blir den text som har störst genomslag i hela systemet. De användbara är inte beskrivningar av data. De är redaktionella regler, skrivna där författaren inte kan undgå att möta dem.

Ett exempel. Säg att du deklarerar två likartade blocktyper: en för nyckel/värde-rader där nyckeln är en kort symbol, en annan för rader där nyckeln är en hel fras. En beskrivning som säger rader med nyckel och värde är ett bortkastat fält. En beskrivning som säger att symbolvarianten är rätt när nyckeln är en tvåteckens etikett mot en eller två rader, att frasvarianten är rätt när värdet sträcker sig till ett kort stycke, och att om värdena går förbi fyra rader ska du inte använda någon av dem — brödtext med mellanrubriker, eller en riktig tabell — är ett stycke redaktionellt omdöme som sitter inuti ett JSON-schema. Det är skillnaden mellan en författare som vet när blocket ska användas och en författare som använder det för att det stod i listan.

Det finns ett mindre fall som jag gillar bättre, eftersom regeln det kodar inte står nedskriven någon annanstans. Ge schemat både en title och en display_title, och lägg regeln på den andra: sätt den när titeln går förbi ungefär femtio tecken. Det fältet finns för att en titel numera har två läsare. Långformen måste bära den fråga någon skulle skriva in eller ställa till en assistent; kortformen måste sitta under en vinjett utan att brytas till tre rader. Det är inte en stilanteckning i ett dokument som någon underhåller. Det är ett fält.

En stilguide är ett dokument någon kanske läser. Ett schema är ett dokument författaren inte kan undvika.

Vad kan en typad text upprätthålla som en stilguide inte kan?

Fyra saker, och de är värda att hålla isär, eftersom bara en av dem handlar om validering. Ett schema gör en hel felklass strukturellt omöjlig, ger varje innehållsenhet en stabil adress, flyttar renderingsbeslut ur händerna på författaren, och bär sina egna instruktioner. En stilguide kan bara be.

  • Strukturell omöjlighetEtt pull_quote utan quote är inte ett dåligt formaterat utdragscitat. Det klarar inte valideringen och når aldrig databasen. Felklassen försvinner i stället för att fångas i granskning — vilket betyder mest för de fel ingen granskar, som ett stats-block med två värden där layouten väntar sig tre.
  • Varje block har en adressBlock bär en stabil keyintro, costs, faq. Det gör redigering till en patch i stället för en omskrivning: en agent som rättar ett pris i ett avsnitt skickar det blocket ensamt, inte hela artikeln, och kan inte tyst tappa ett stycke den glömde ta med i omsändningen.
  • Renderingen avgörs någon annanstansFörfattaren väljer callout med tonen warn. Den väljer inte en färg, en ram, en ikon eller en bredd. Presentationen bor i rendreraren, där ett designbeslut kan ändras en gång i stället för att tas upp på nytt i varje artikel som råkar innehålla en varning.
  • Instruktionen följer med fältetEn beskrivning fäst vid ett fält anländer till författarens kontext varenda gång, i samma nyttolast som det den beskriver. En wikisida om tonalitet anländer aldrig.

Vad ett schema inte kan upprätthålla

Smak, vilket är det jag inledde med. En text som helt består av prose-block är giltig, och det är modellens förstaval: given en lista över blocktyper och ett resonemang att driva kommer en språkmodell att gripa efter den mest generella behållare som finns, och fortsätta gripa efter den. Typsystem har aldrig kunnat tala om för dig att ett korrekt program är ett tråkigt program.

Lösningen är inte ett strängare schema, och jag vill vara tydlig med det, eftersom instinkten är att lägga till en begränsning. Du kan inte uttrycka använd utdragscitatet när meningen gör mer arbete än stycket runt den i JSON Schema, och en regel som högst tre prose-block i följd producerar en författare som stoppar in en avdelare vart tredje stycke för att uppfylla den. Goodhart gäller även innehållsmodeller. Det som faktiskt fungerar är att skriva in kravet på uttrycksfullhet i de instruktioner agenten kör under, och sedan läsa resultatet.

Det andra ett schema inte kan kontrollera är om något i det är sant. Ett stats-block deklarerar tre strängar. Det deklarerar inte tre fakta. En strukturerad text gör en påhittad siffra bättre formaterad, mer framträdande och mer sannolik att citeras tillbaka av något som läser den senare. Struktur höjer priset på en hallucination i stället för att sänka det.

Beskrivningar är kod utan tester

I samma ögonblick som en fältbeskrivning slutar stämma med den verkliga redaktionella regeln har du byggt en självsäker lögnare som levereras vid varje förfrågan. Säg att en beskrivning talar om för författaren att numreringen på ett listblock genereras och aldrig får skrivas för hand. Om en ändring i rendreraren någon gång gör det osant kastas inget fel — varje framtida artikel handnumrerar tyst sina listor, och den förste som märker det är en läsare. Fältbeskrivningar är den del av schemat som saknar typkontroll, saknar testsvit och har störst spridningsradie. Granska dem som kod, för det är vad de är.

Hur lär sig agenten egentligen formen?

Genom att hämta den. Blocktyperna är definierade på samlingen, och agenten läser dem som den läser allt annat — ett anrop som returnerar innehållsmodellen som data. Själva skrivverktyget tar ett ganska ogenomskinligt data-objekt, och servern validerar det som kommer in mot samlingen. Briefen når alltså författaren som en nyttolast den slår upp vid körning, inte som en typ deklarerad i verktygets signatur.

Det finns bara två ställen ett schema kan bo, och valet betyder mer än det ser ut.

Lägg det i verktygets inputSchema och det är deklarerat en gång, i katalogen, där varje anropare ser det innan den gör ett anrop. Modellen kan inte skriva utan att ha fått formen visad för sig. Lägg det i data som returneras av ett läsverktyg och det kan ändras utan att något driftssätts, det kan versioneras tillsammans med innehållet, och — den avgörande faktorn — det kan skilja sig per tenant.

Den sista avgör saken för en driftad plattform. En endpoint betjänar många organisationer, och deras samlingar är inte desamma. Du kan inte baka in en tenants innehållsmodell i en verktygssignatur som alla tenanter läser. Kostnaden är verklig och värd att nämna: ingenting tvingar modellen att hämta samlingen innan den skriver. En författare som hoppar över uppslagningen gissar, och det enda som står mellan en gissning och databasen är validering på serversidan. Vilket är ett argument för att validera strängt, och mot att behandla verktygsbeskrivningen som om den vore kontraktet.

jsonen blockunion, i helt vanlig JSON Schema
{
  "type": "object",
  "properties": {
    "body": { "type": "array", "items": { "$ref": "#/$defs/block" } }
  },
  "$defs": {
    "block": {
      "oneOf": [
        { "$ref": "#/$defs/lead" },
        { "$ref": "#/$defs/prose" },
        { "$ref": "#/$defs/pull_quote" }
      ]
    },
    "pull_quote": {
      "type": "object",
      "required": ["type", "props"],
      "properties": {
        "type": { "const": "pull_quote" },
        "props": {
          "type": "object",
          "required": ["quote"],
          "properties": {
            "quote":  { "type": "string" },
            "source": { "type": "string" }
          }
        }
      }
    }
  }
}

Om din innehållsmodell är fast vid byggtid blev det andra alternativet bättre med revisionen 2026-07-28, och det är värt att känna till. SEP-2106, i 2026-07-28-MCP-specifikationen, lyfter verktygens inputSchema och outputSchema till fullt JSON Schema 2020-12: indataschemata behåller rotkravet type: "object" men tillåter nu komposition genom oneOf, anyOf och allOf, plus villkor och $ref/$defs. En diskriminerad union som den ovan går att uttrycka i en verktygssignatur, vilket betyder att en server med en enda tenant kan lägga hela formen i katalogen och låta protokollet bära den. Det är inte den affär vi står i, men det är den bättre när du kan ta den.

Hur står sig det här mot Portable Text, Contentful Rich Text och Strapi dynamic zones?

Ingen här har uppfunnit strukturerat innehåll. Block är inga nyheter i headless CMS-världen; det nya är vem som har nytta av dem. Var och en av de mogna implementationerna gör en annan avvägning, och avvägningarna läses annorlunda när din författare är en maskin.

Sanitys Portable Text kom först och har åldrats bäst. Det modellerar rik text som en lista med block med stilar och markdefinitioner, uttryckligen designat för att serialiseras till vilket människoläsbart format som helst i stället för att anta HTML, och det tillåter godtyckliga egna objekt i rotlistan. Det är ett genuint välgjort format och värt att läsa innan du designar ditt eget — Sanity är en innehållsplattform, så innehållsmodellen är produkten där på ett sätt den inte är någon annanstans i den här listan.

Contentful Rich Text lagrar ett nästlat JSON-dokumentträd som du går igenom för att rendera. Det stöder inbäddade poster och tillgångar ordentligt, vilket markdown inte kan. Kostnaderna är ett rendrerarbibliotek, en egen rendrerare för varje inbäddad typ, och nyttolaster betydligt större än motsvarande markdown. För en generator är djupet den knöliga delen: en platt lista med typade block är enklare att producera korrekt än ett träd, eftersom författaren aldrig behöver hålla en nästlingsposition i huvudet.

Strapi dynamic zones ligger närmast modellen som beskrivs här — en deklarerad uppsättning komponenter som författaren väljer bland, returnerad som strukturerad JSON. Kostnaden dyker upp i hämtningen snarare än i skrivandet: Strapi 5 tog bort den delade populeringsstrategin, så du deklarerar populering per komponent med on-fragment i stället för att luta dig mot populate=*.

Payload lagrar Lexical-redigerarens tillstånd som JSON, med block som lever som noder som bär ett fields-objekt, och ger dig konverterare för att ta sig till HTML.

Så varför skulle du använda Munins CMS i stället?

Inte för redigeringsupplevelsen. Munin levereras utan visuell sidbyggare, utan WYSIWYG-redigerare för rik text och utan plugin-marknadsplats. De fyra produkterna är byggda kring precis det, och om en mänsklig redaktör som sitter i en webbläsare hela dagen är centrum för din innehållsverksamhet är någon av dem det rätta köpet, och jag skulle inte försöka prata dig ur det.

Använd Munin när det du faktiskt vill veta är vilken artikel som stängde affären.

Ett headless CMS håller dokument. Det har ingen åsikt om vem som läste dem, eftersom människorna ligger i ett annat system med en annan idé om vad en person är, och kopplingen mellan dem är en integration någon underhåller. Munins CMS är en av sex moduler på en enda Postgres, så artikeln, besökaren som läste den, konversationen de öppnade två dagar senare och affären som kom ur det är rader i samma databas mot en enda kontaktpost, med radnivåsäkerhet som bär tenancy — vilket är hela det arkitektoniska vadet. Frågorna som normalt är ett dataprojekt är queries: vad någon läste innan de hörde av sig, vilket stycke supporten ständigt skickar, vilket ämne som producerar konversationer som konverterar.

Den andra halvan är att samma 200-plus MCP-verktyg som skrev artikeln kan göra allt runt den. En agent skriver utkastet, genererar omslagsbilden (via Munin Media-verktygen), kollar vad den förra drog in, hittar kontakterna som läste den, och lämnar en uppföljning i en granskningskö för att en människa ska godkänna eller döda den. Ingen orkestrering mellan de stegen, eftersom det inte finns något att orkestrera — det är en endpoint över ett schema, och verktygskatalogen är ytan. Det är inget du skruvar fast på ett headless CMS med tre integrationer och en schemaläggare. Det är vad du får när CMS:et aldrig var en separat produkt. Hur den rundan kör i praktiken — läs hyllan, verifiera, skriv utkast in i schemat, omslag, stopp — är beskrivet steg för steg.

Och det är MIT rakt igenom — ingen enterprise/-katalog som håller på de funktioner du faktiskt skulle behöva. Hela saken kör på din egen hårdvara direkt från repot, eller på Munin Cloud i EU om du hellre slipper köra den alls. Utgången är en sekvens verktygsanrop och varje modul har en matchande export. Vi vill hellre att du ska kunna lämna.

Vad det kostar

  • 01Varje blocktyp är för alltid. Femton typer är femton rendrerare, femton valideringsvägar och femton fall varje framtida migrering måste hantera. Att lägga till en är en schemaändring på fem minuter och ett permanent åtagande, vilket är samma räknestycke som att lägga till en publik API-metod.
  • 02Diffar blir sämre. En markdown-text diffar som brödtext i vilket verktyg som helst skrivet de senaste fyrtio åren. En blocklista diffar som JSON — en rättning på ett ord dyker upp som en ändrad rad inuti en citerad sträng, och en omordning dyker upp som en omskrivning. Stabila blocknycklar och versionering per post gör granskningen uthärdlig, och den är fortfarande sämre än git diff på en textfil.
  • 03Människor är långsammare i det. Ingen har någonsin skrivit snabbare i en strukturerad redigerare än i en ren textfil. Om de flesta av dina författare är människor och de flesta av dina artiklar är vanliga essäer är en markdown-kolumn med en bra stilguide det korrekta tekniska beslutet, och du ska inte läsa resten av den här listan som ett argument emot det.
  • 04Schemat blir taket. När rätt form för en tanke inte finns i listan griper författaren efter prose, och tanken kommer ut plattare än den borde ha gjort. En markdown-sträng har inget tak — den låter en skribent uppfinna en form på stället. Den friheten är precis det du ger upp, och det är den kostnad jag lägger märke till oftast.

Titta på formen på den räkningen, dock. Varje rad på den betalas en gång, i det ögonblick du deklarerar en typ eller kopplar in en rendrerare, och inget av det återkommer per artikel. Den återkommande kostnaden sitter på andra sidan av affären.

En markdown-kolumn ger dig exakt en redaktionell kontroll som körs på varje stycke, och det är en människa som läser stycket. Det fungerar, och det är rätt svar för ett team av mänskliga skribenter. Det slutar fungera i samma ögonblick som artikelproduktionen automatiseras, eftersom "en bra stilguide" då är ett dokument som ingen i loopen öppnar — författaren är en process, och en process läser den nyttolast den fick överlämnad. Schemat är den enda brief som anländer tillsammans med arbetet, varenda gång, oavsett om någon kom ihåg att skicka den eller inte.

Så de fyra kostnaderna ovan är ändliga, uppräkningsbara och mestadels engångs. Alternativets kostnad är ett granskningssteg du måste bemanna för alltid, på varje artikel, i vilken volym du än publicerar. Det är räknestycket som avgjorde det här, och det är räknestycket värt att göra innan du avgör det för egen del.

Vanliga frågor

Ska jag lagra markdown eller strukturerade block i mitt CMS? Lagra markdown om dina författare är människor och din utdata är en webbplats. Lagra strukturerade block om du renderar till mer än en yta, eller om något annat än en person sätter samman texten. Den avgörande frågan är inte hur innehållet visas — det är vem eller vad som skriver det, och om någon läser resultatet innan det publiceras.

Vad är Portable Text och hur skiljer det sig från Markdown? Portable Text är Sanitys öppna format för rik text som en lista med typade block, vart och ett med en stil och en uppsättning markdefinitioner, och det tillåter godtyckliga egna objekt i listan. Markdown är en klartextsyntax från 2004 som förutsätter en konvertering till HTML så småningom. Det ena är data du frågar mot och transformerar; det andra är en sträng du parsar.

Kan en AI-agent skriva tillförlitligt in i en strukturerad innehållsmodell? Ja för struktur, nej för omdöme. Schemavalidering förhindrar tillförlitligt felformade block, saknade obligatoriska fält och fel typer, så de felen slutar nå produktion. Den förhindrar inte monotoni, dålig avsnittsordning eller påhittade siffror, eftersom inget av det är typfel. Struktur är ett golv under kvaliteten, inte en ersättning för granskning.

Hur upptäcker en AI-agent innehållsmodellen i ett CMS? Antingen är formen deklarerad i verktygssignaturen som modellen ser innan den anropar något, eller så returneras den som data av ett läsverktyg som modellen måste komma ihåg att anropa. Flerhyresplattformar pressas mot det senare, eftersom olika kunder har olika samlingar och en signatur inte kan beskriva dem alla. Avvägningen är att ingenting framtvingar en uppslagning, så servern måste validera strängt.

Vad är ett agentiskt CMS? Ett som en AI-agent kan sköta direkt, snarare än ett med en AI-skrivassistent fastskruvad i redigeraren. I praktiken betyder det att varje handling är ett verktyg agenten kan anropa, att innehållsmodellen går att upptäcka, och att skrivningar landar i ett granskningsbart tillstånd i stället för att gå rakt ut. Munin exponerar mer än 200 sådana verktyg på en endpoint och håller varje post versionerad, så att en publicering kan rullas tillbaka.

Kan ett CMS tala om för mig vilken artikel som genererade en kund? Bara om innehållet och kunden bor i samma databas. Ett headless CMS håller dokument och vet ingenting om läsare, så att svara på det innebär en integration mellan CMS:et, analysverktyget och CRM:et. Munin kör innehåll, analys, konversationer och CRM som sex moduler på en enda Postgres mot en enda kontaktpost, vilket gör det till en query.

Är ett blockbaserat CMS värt det för ett litet team? Bara om agenten står för skrivandet. För ett tvåmannateam som publicerar en handfull inlägg för hand kostar ett markdown-fält mindre att bygga, mindre att rendera och mindre att redigera. Ekonomin vänder när artikelproduktionen automatiseras, för då är schemat den enda redaktionella kontroll du har som körs på varje enskilt stycke.

Hur hindrar man en agent från att använda samma blocktyp till allt? Inte med schemat — en vägg av brödtext är giltigt innehåll. Det måste hanteras i de instruktioner agenten kör under, formulerat som ett krav på att använda typregistret uttrycksfullt, och sedan kontrolleras genom att läsa resultatet. Det finns inget sätt att koda det här som en begränsning som inte producerar sämre skrivande än den förhindrar.

Om du modellerar innehåll för en agentförfattare

  • När något annat än en person sätter samman texten slutar innehållsmodellen vara lagring och blir briefen — behandla schemat som en prompt, för det är så det konsumeras.
  • Fältbeskrivningar är den text med störst genomslag du kommer att skriva. De anländer till författarens kontext varje gång; en stilguide anländer aldrig.
  • Typade block köper fyra saker: omöjliga fel, en stabil adress per block så att redigeringar blir patchar, renderingsbeslut hållna borta från författaren, och instruktioner som följer med fälten.
  • De köper inte smak eller sanning. En vägg av en enda blocktyp validerar, och ett stats-block kräver tre strängar, inte tre fakta.
  • Bestäm var schemat ska bo. I verktygssignaturen är det omöjligt att missa men fast; i hämtad data är det per tenant och versionerbart, men ingenting tvingar författaren att läsa det — så validera på servern oavsett.
  • Känn villkoren innan du tar affären: varje blocktyp du deklarerar är permanent, JSON-diffar behöver stabila nycklar och versionering per post för att gå att granska, och den deklarerade uppsättningen avgränsar de former som finns. De kostnaderna betalas en gång, vid deklarationen, och de återkommer inte per artikel.
  • Välj Munin när du vill ha artikeln, läsaren och affären i en databas och 200+ MCP-verktyg en agent kan köra — MIT, självhostbart, gratis att börja med.

Blocktyperna den här artikeln är skriven i är deklarerade på själva samlingen, läsbara i repot eller genom att anropa cms_list_collections mot en egen organisation.

Din stilguide är ett förslag. Din innehållsmodell är prompten.

Notes

  1. Fullt JSON Schema 2020-12 för verktygsscheman landade i SEP-2106, annonserat i utgåvenoteringarna för 2026-07-28-specifikationen.
  2. Sanitys argument för strukturerad text framför markdown: Why Portable Text is awesome och introduktionen till Portable Text.
  3. Contentfuls dokumentmodell för rik text finns dokumenterad under What is rich text?; Strapis komponentbaserade texter under Dynamic Zones; Payloads Lexical-JSON under Rich Text.
Kjell Rune Monsø, grundare.