grötfluence.se är en grötblogg. Det kräver inte en sofistikerad teknisk infrastruktur. Eller?
Det som började som ett manuellt arbetsflöde — fotografera skålen, öppna appen, skriva bildtexten, välja taggar — har automatiserats till en pipeline som involverar två lokalt körda språkmodeller, en Telegram-bot, en Docker-containeriserad FastAPI-tjänst och en dedikerad AI-server med 96 GB/s minnesbandbredd. Grötens kvalitet har inte förbättrats. Tekniken är rimlig.
Arkitektur
Den fullständiga pipelinen aktiveras genom att skicka ett foto till en Telegram-bot:
- Telegram levererar fotot till en webhook-endpoint
- Qwen2.5-VL 3B analyserar bilden och genererar en saklig engelsk beskrivning av innehållet
- Mistral Small 3.1 24B tar beskrivningen och genererar svensk rubrik, bildtext och metadata i ett enda API-anrop
- Bilden och inlägget publiceras till Directus via REST API
- Boten svarar med den publicerade URL:en
Från början till slut tar processen ungefär 30–60 sekunder. Det mesta av det är Mistral.
Hårdvara: AMD Ryzen AI MAX+ 395 w/ Radeon 8060S
All inferens körs lokalt på frmwrk-ai — en Framework Desktop med en AMD Ryzen AI MAX+ 395. Det intressanta med den processorn är att CPU och GPU delar samma minnespool: 64 GiB LPDDR5x med 96 GB/s bandbredd. Det innebär att en 13 GiB stor modell kan laddas in i minnet och köras på GPU:n utan att det uppstår någon flaskhals mellan separata minnesbussar.
GPU:n är en integrerad Radeon 8060S (gfx1151, RDNA 3.5). ROCm fungerar via en egenkompilerad llama.cpp med HIP-stöd. Maskinen kör Fedora 43 och hanteras via Tailscale, vilket gör den nåbar från var som helst i nätverket.
Att hålla beräkningen lokal är ett aktivt val: latensen är förutsägbar, det finns inga kostnader per token, och frukostfoton hör hemma på den egna hårddisken.
Visionsmodell: Qwen2.5-VL 3B
Qwen2.5-VL 3B (Q4_K_M, ~1.8 GiB) är en multimodal modell som körs via llama.cpp med ROCm HIP-backend. Eftersom llama.cpp:s bildavkodare inte stöder WebP körs ett tunt proxylager som konverterar inkommande bilder till JPEG innan de vidarebefordras — Telegram skickar foton i JPEG, så i praktiken är det sällan relevant, men det är bra att ha.
Modellen genererar ~77 token/sekund med en svarstid på ungefär 725 ms per anrop. Uppgiften är avsiktligt smal: beskriv maten objektivt — utseende, färger, konsistens, toppings. Ingen svenska, ingen humor, ingen tolkning. Bara fakta som nästa modell kan arbeta utifrån.
POST /v1/chat/completions
{
"model": "qwen2.5vl:3b-gpu",
"messages": [{
"role": "user",
"content": [
{ "type": "text", "text": "Describe the food in this image objectively..." },
{ "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,<base64>" } }
]
}]
}
Språkmodell: Mistral Small 3.1 24B
Mistral Small 3.1 24B (Q4_K_M, ~13.4 GiB) körs via llama.cpp med ett kontextfönster på 131 072 token — väl tilltaget för en grötbeskrivning. Genereringshastigheten är ~14 token/sekund, vilket är acceptabelt för ett icke-interaktivt flöde.
Den engelska bildbeskrivningen skickas som kontext och ett enda API-anrop returnerar ett JSON-objekt med rubrik, bildtext, kategori och taggar. Systempromten är det som faktiskt gör jobbet: grammatiskt korrekt svenska är trivialt för en modell i den här storleksklassen — problemet är ton. Bloggens röst är torr, måttligt absurd och skriven på naturlig talad svenska. Prompten innehåller explicita stilexempel och explicita förbud (inga hashtags, inga emojis, ingen uppmuntrande ton) och instruerar modellen att använda svenska idiom snarare än direktöversättningar från engelska.
Att dela upp pipelinen i två modeller är ett medvetet val. Qwen2.5-VL 3B är kompakt och snabb på visuell förståelse. Mistral Small 3.1 24B är bättre på nyanserad textgenerering på andra språk än engelska. Varje modell gör det den faktiskt är bra på.
Applikationen: FastAPI i Docker
Orkestreringslagret är en FastAPI-applikation som körs i Docker på en separat server. Python 3.11, httpx för all utgående HTTP.
Telegram webhook-endpointen returnerar HTTP 200 omedelbart och skickar pipelinen vidare som en FastAPI BackgroundTask. Det är nödvändigt eftersom Telegram kräver ett svar inom 60 sekunder och den fullständiga pipelinen tar längre tid än så.
Directus-integration
Publicering sker via Directus REST API med en statisk token. Bilden laddas upp till Directus filhantering och inlägget skapas med titel, brödtext, kategori och taggar i ett enda anrop. Kategorier och taggar skapas automatiskt om de inte redan finns — taxonomin bygger upp sig själv över tid.
Källkod
Den fullständiga källkoden finns på GitHub: martindebruin/grotfluence-image-describer.
Det är en grötblogg. Tekniken är oproportionerlig. Det är lugnt.