[{"content":" Oversigt # Engestofte Gods afholder hvert år et julemarked med over 60 stadeholdere fordelt på 5 zoner. Event-koordinator Lise opdaterede tidligere stadeholderlisten på hjemmesiden manuelt ved at skrive HTML direkte i Squarespace — tidskrævende, fejlbehæftet og kræver teknisk viden.\nDette projekt løser problemet med en simpel webapp: Lise udfylder en formular med stadeholderoplysninger, AI genererer korrekt formateret HTML, og hun copy-paster det direkte ind på hjemmesiden.\nFunktioner # Formular med felterne: virksomhedsnavn, beskrivelse, hjemmeside og zone 5 faste zoner som dropdown: Den gamle avlsgård, Hestestalden, Laden, Kostalden, Jagtstuen Claude AI genererer Squarespace-kompatibel HTML med korrekt centrering og formatering \u0026ldquo;Kopiér HTML\u0026rdquo;-knap til hurtig copy-paste Ingen login, ingen database — ren input → AI → output Tech Stack # Frontend: HTML, CSS, vanilla JavaScript\nBackend: Python, Flask\nAI: Claude Sonnet via Anthropic API\nProblemet der løses # Felterne i webapp\u0026rsquo;en er direkte mapnet fra Engestoftes officielle ansøgningsskema — særligt beskrivelsesfeltet, som ansøgerne selv udfylder og som Lise modtager og skal publicere. Det eliminerer dobbeltarbejdet med at genskrive information i HTML.\nHvad jeg lærte # Prompt engineering: hvordan man specificerer præcist HTML-format til et AI-output uden at modellen blander markdown ind Flask som letvægts API-proxy der holder API-nøglen ude af frontend Vigtigheden af server-side output-rensning når AI genererer struktureret tekst Links # 🚀 Prøv live demo · Kildekode på GitHub\n","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/projects/project-one/","section":"Projects","summary":"","title":"AI-drevet stadeholder-generator","type":"projects"},{"content":" Overview # Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat duis aute irure dolor.\nFeatures # Consectetur adipiscing elit sed do eiusmod tempor Incididunt ut labore et dolore magna aliqua ut enim Ad minim veniam quis nostrud exercitation ullamco Laboris nisi ut aliquip ex ea commodo consequat Tech Stack # Frontend: Amet, Consectetur\nBackend: Adipiscing, Elit\nTools: Sed, Eiusmod, Tempor\nWhat I Learned # Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n","date":"15 March 2026","externalUrl":null,"permalink":"/Portfolio/projects/project-two/","section":"Projects","summary":"","title":"Project Two","type":"projects"},{"content":" Overview # Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nFeatures # Ullamco laboris nisi ut aliquip ex ea commodo Duis aute irure dolor in reprehenderit in voluptate Velit esse cillum dolore eu fugiat nulla pariatur Excepteur sint occaecat cupidatat non proident Tech Stack # Language: Dolor, Sit\nFramework: Amet, Consectetur, Adipiscing\nInfrastructure: Elit, Sed, Do\nWhat I Learned # Sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium totam rem.\n","date":"1 February 2026","externalUrl":null,"permalink":"/Portfolio/projects/project-three/","section":"Projects","summary":"","title":"Project Three","type":"projects"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/tags/ai/","section":"Tags","summary":"","title":"Ai","type":"tags"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/series/ai-drevne-applikationer/","section":"Series","summary":"","title":"AI-Drevne Applikationer","type":"series"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/","section":"Ayman Chaykh","summary":"","title":"Ayman Chaykh","type":"page"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/posts/","section":"Blog","summary":"","title":"Blog","type":"posts"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/tags/claude/","section":"Tags","summary":"","title":"Claude","type":"tags"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/tags/flask/","section":"Tags","summary":"","title":"Flask","type":"tags"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/tags/html/","section":"Tags","summary":"","title":"Html","type":"tags"},{"content":" Projektet kort fortalt # Jeg har bygget en AI-drevet webapp til Engestofte Gods julemarked. Event-koordinator Lise opdaterede tidligere stadeholderlisten på hjemmesiden manuelt — hun skrev HTML direkte i Squarespace for hver enkelt stadeholder. Det er tidskrævende, kræver teknisk viden og er nemt at lave fejl i.\nLøsningen: en simpel formular hvor Lise udfylder stadeholderoplysninger, Claude AI genererer korrekt formateret HTML, og hun copy-paster det direkte ind på hjemmesiden. Ingen HTML-kendskab nødvendigt.\nLive demo: stadeholder-generator.onrender.com\nKildekode: github.com/Zuhami/stadeholder-generator\nHvad jeg har bygget # MVP — formulargeneratoren # Webapp\u0026rsquo;en består af tre lag:\nFrontend (index.html + style.css + app.js)\nEn formular med fire felter: virksomhedsnavn, beskrivelse, hjemmeside (valgfri) og zone (dropdown med de 5 faste bygninger på Engestofte). Når Lise klikker \u0026ldquo;Generer HTML\u0026rdquo; sendes data til backend, og resultatet vises i en tekstboks med en \u0026ldquo;Kopiér\u0026rdquo;-knap.\nBackend (server.py — Flask)\nEn letvægts Python-server der modtager formulardata og proxyer det til Anthropic API. API-nøglen lever på serveren og eksponeres aldrig i browseren.\nAI-laget (Claude Sonnet via Anthropic API)\nClaude modtager strukturerede stadeholderdata og en præcis system-prompt der specificerer det eksakte HTML-format. Den returnerer én klar HTML-blok klar til copy-paste.\nBatch-scriptet # Udover webapp\u0026rsquo;en har jeg bygget batch.py — et CLI-script der læser en hel CSV-fil med stadeholdere og genererer HTML for dem alle på én gang, grupperet per zone. Til slutningen af ansøgningsperioden, hvor Lise har 60+ stadeholdere at behandle, sparer det hende for timevis af arbejde.\nErfaringer og udfordringer # Prompt engineering er sværere end det lyder # Den største tekniske udfordring var ikke koden — det var at få AI\u0026rsquo;en til at returnere præcist det format jeg ville have, hver gang. Min første system-prompt var for løs, og AI\u0026rsquo;en blandede HTML med markdown-syntax i outputtet.\nLøsningen var to ting:\nGive et konkret eksempel på korrekt output direkte i prompten Tilføje server-side rensning med regex der fjerner markdown-elementer selv hvis de dukker op Det lærte mig at AI-output aldrig er 100% deterministisk — selv en præcis prompt kan give lidt varierende resultater. Defensiv rensning er nødvendig i produktion.\nFelternes oprindelse # En vigtig indsigt kom da jeg analyserede det rigtige ansøgningsskema fra Engestofte. Felterne i webapp\u0026rsquo;en er direkte mapnet fra skemaet — særligt beskrivelsesfeltet (\u0026ldquo;Kort og fængende beskrivelse af jeres forretning/stand\u0026rdquo;) som ansøgerne selv udfylder. Det betyder Lise kan copy-paste fra ansøgningen direkte ind i formularen. Ingen genskrivning.\nZoner er faste — en antagelse der holdt # Jeg antog at zonerne (Den gamle avlsgård, Hestestalden, Laden, Kostalden, Jagtstuen) var faste fra år til år. Det bekræftede stadeplaner og historisk data. Dropdownen er derfor hardcoded — det er den rigtige beslutning for MVP.\nDeployment lærte mig forskellen på dev og produktion # Lokalt kørte appen med python3 server.py. I produktion (Render.com) kræves en rigtig webserver — Gunicorn — foran Flask. Det er den klassiske forskel på en udviklingsserver og noget der kan håndtere rigtig trafik. En lille men vigtig detalje.\nHvad der mangler til version 2 # Jeg har bevidst afgrænset MVP\u0026rsquo;en. Disse ting er ikke bygget endnu:\nDirekte Squarespace-integration — i stedet for copy-paste, post HTML automatisk via Squarespace API Database over historiske stadeholdere — genbrug data fra tidligere år Login/brugerstyring — så kun Lise kan tilgå systemet Batch-upload via UI — upload en CSV direkte i webapp\u0026rsquo;en i stedet for CLI Samlet vurdering # Projektet har vist mig at den sværeste del ved AI-drevne applikationer ikke er at kalde et API — det er at designe et system der er pålideligt nok til at en ikke-teknisk bruger kan stole på outputtet. Prompt engineering, output-validering og brugergrænseflade skal alle stemme overens.\nDet er stadig et lille projekt, men det løser et reelt problem for en reel bruger. Det er et godt udgangspunkt.\n","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/posts/projekt-fremdrift-del-1/","section":"Blog","summary":"","title":"Projektfremdrift del 1 — AI-drevet stadeholder-generator","type":"posts"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/tags/refleksion/","section":"Tags","summary":"","title":"Refleksion","type":"tags"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"29 May 2026","externalUrl":null,"permalink":"/Portfolio/tags/webapp/","section":"Tags","summary":"","title":"Webapp","type":"tags"},{"content":"","date":"13 May 2026","externalUrl":null,"permalink":"/Portfolio/tags/api/","section":"Tags","summary":"","title":"Api","type":"tags"},{"content":" Projektet # Denne gang tog vi fat i et konkret, virkeligt problem — ikke et tutorial-eksempel, men noget der faktisk irriterer et rigtigt menneske i det daglige arbejde.\nArbejdstitlen: AI-drevet stadeholder-generator til Engestofte julemarked.\nEngestofte Gods afholder julemarked hvert år med en lang liste af stadeholdere. Koordinatoren Lise opdaterer listen på hjemmesiden manuelt — hun skriver HTML direkte i Squarespace. Det er tidskrævende, kræver teknisk viden hun ikke har, og det er nemt at lave fejl, særligt når der er mange stadeholdere.\nProblemet # Lises nuværende arbejdsgang ser sådan her ud:\nModtager stadeholderinfo via mail Skriver eller retter HTML manuelt i Squarespace Publicerer siden Ingen standardiseret format, ingen historik, ingen genbrug af data fra tidligere år. Hver opdatering er en manuel operation der kræver at hun husker HTML-strukturen og ikke laver slåfejl i tags.\nDet er præcis den slags opgave AI er god til: struktureret, gentagende, formatfølsom output fra varierende input.\nDen foreslåede løsning # En simpel webapp hvor Lise udfylder en formular med stadeholderinfo. Klikker hun på en knap, sender appen dataene til Claude (Anthropic API), som genererer færdig, korrekt formateret Squarespace-HTML — klar til copy-paste direkte ind på hjemmesiden.\nIngen kode. Ingen HTML. Ingen teknisk viden nødvendig.\nAI-funktionen: Claude modtager strukturerede data (navn, beskrivelse, hjemmeside, zone) og en system-prompt der specificerer præcis det Squarespace-format siden bruger — inkl. korrekte sektioner per zone (Laden, Kostalden, udendørs m.fl.). Output er færdig HTML der matcher den eksisterende sidestruktur.\nMVP # Formular med fire felter:\nFelt Type Navn Tekstfelt Beskrivelse Tekstområde Hjemmeside URL-felt Zone Dropdown Knap → Claude API-kald → HTML vises i en tekstboks klar til copy-paste.\nIngen login. Ingen database. Bare input → AI → output.\nHvad vi ikke bygger (endnu) # Vi afgrænser projektet bevidst. Version 1 inkluderer ikke:\nDirekte Squarespace-integration Login eller brugerstyring Database over historiske stadeholdere Plantegning over markedet Trello-integration Det er version 2-opgaver. MVP skal bevise konceptet, ikke løse alt på én gang.\nÅbne spørgsmål # Der er tre ting vi endnu ikke har svar på fra kunden:\nHvilke felter indeholder ansøgningsskemaet præcist? Vi kender ikke det fulde datasæt endnu. Hvilket HTML-format bruger Squarespace-siden i dag? Det skal reverse-engineers fra den live side. Er zonerne faste hvert år, eller ændrer de sig? Det har betydning for dropdown-designet. Næste skridt # Hent og analyser HTML-strukturen fra engestofte.com/da/stadeholderliste — vi skal kende det præcise format Claude skal generere. Byg formular-interface med de kendte felter og koblet til Anthropic API. Test output mod den rigtige side og finpuds system-prompten til den genererer korrekt HTML hver gang. Refleksion # Det interessante ved dette projekt er ikke teknologien — det er problem-fittet. Claude kan sagtens generere HTML. Udfordringen er at få system-prompten til at generere præcis den rigtige HTML til præcis dette Squarespace-setup. Den del kræver at vi forstår formatet til bunds inden vi begynder at kode.\nDet er også et godt eksempel på hvornår AI faktisk giver mening: ikke som erstatning for noget komplekst, men som erstatning for noget kedeligt og fejlprone — der er en stor forskel.\n","date":"13 May 2026","externalUrl":null,"permalink":"/Portfolio/posts/gang-3-ai-stadeholder-generator/","section":"Blog","summary":"","title":"Projektopgave — AI-drevet stadeholder-generator til Engestofte julemarked","type":"posts"},{"content":" Hello, I\u0026rsquo;m Ayman 👋 # Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nWhat I Do # Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nSkills # Languages: Lorem, Ipsum, Dolor, Sit Frameworks: Amet, Consectetur, Adipiscing Tools: Elit, Sed, Eiusmod, Tempor Other: Incididunt, Labore, Dolore Education # Lorem Ipsum University — B.Sc. in Dolor Sit Amet\n2022 – 2026\nConsectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nGet in Touch # Feel free to reach out via LinkedIn or email — I\u0026rsquo;m always open to new opportunities and collaborations.\n","date":"27 April 2026","externalUrl":null,"permalink":"/Portfolio/about/","section":"Ayman Chaykh","summary":"","title":"About Me","type":"page"},{"content":"","date":"27 April 2026","externalUrl":null,"permalink":"/Portfolio/projects/","section":"Projects","summary":"","title":"Projects","type":"projects"},{"content":" Hvad er en kodeagent? # En kodeagent er en LLM der ikke bare besvarer spørgsmål, men aktivt løser programmeringsopgaver — den kan læse filer, skrive kode, køre tests og iterere på resultatet. Den arbejder som en kollega der kender kodebasen, ikke som en søgemaskine der returnerer snippets.\nVærktøjer vi kiggede på:\nAgent Hvad den gør Claude Code Kørende i terminalen, kan læse og redigere hele projekter OpenAI Codex GitHub Copilot-lignende integrationer Gemini CLI Googles CLI-agent, tæt integreret med Google-stack Opgaven vi løste # Vi tog udgangspunkt i det eksisterende stadeholder-generator projekt og udvidede det med en ny funktion:\nOpgave: Byg et batch-script der læser en liste af stadeholdere fra en CSV-fil og genererer samlet Squarespace-HTML for alle på én gang — uden at Lise skal udfylde formularen manuelt for hver enkelt.\nDet er en reel use case: ved slutningen af ansøgningsperioden har Lise 60+ stadeholdere at behandle. Et batch-script sparer hende for timevis af arbejde.\nWorkflowet med kodeagenten # 1. Opgaveformulering # Det første skridt var at give agenten en præcis opgavebeskrivelse med kontekst:\nHvad eksisterer allerede (Flask-app, system-prompt, API-opsætning) Hvad det nye script skal gøre (læse CSV, kalde API per række, gruppere output per zone) Hvilke edge cases der skal håndteres (manglende hjemmeside, ukendte zoner) Læring: En god opgaveformulering til en kodeagent minder mere om en teknisk spec end et spørgsmål. Jo mere kontekst, jo færre iterationer.\n2. Første implementation # Agenten genererede batch.py med:\nCSV-læsning via csv.DictReader Generer HTML per stadeholder via Anthropic API Gruppering af output per zone med HTML-kommentarer som separatorer # Kernefunktionen agenten genererede def generer_html(client, navn, beskrivelse, hjemmeside, zone): besked = f\u0026#34;Zone: {zone}\\nNavn: {navn}\\nBeskrivelse: {beskrivelse}\u0026#34; if hjemmeside: besked += f\u0026#34;\\nHjemmeside: {hjemmeside}\u0026#34; response = client.messages.create( model=\u0026#34;claude-sonnet-4-6\u0026#34;, max_tokens=512, system=SYSTEM_PROMPT, messages=[{\u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: besked}], ) return response.content[0].text.strip() 3. Test afslører en fejl # Vi testede mod en CSV med 3 stadeholdere. Output for den tredje var pakket ind i markdown kodeblokke:\n```html \u0026lt;p style=\u0026#34;text-align: center;\u0026#34;\u0026gt;\u0026lt;strong\u0026gt;Casa Italy\u0026lt;/strong\u0026gt;\u0026lt;/p\u0026gt; AI\u0026#39;en returnerede markdown i stedet for ren HTML — samme problem vi stødte på i webapp\u0026#39;en. Agenten identificerede mønsteret og tilføjede rensning: ```python # Agentens fix — regex der stripper markdown kodeblokke html = re.sub(r\u0026#39;```(?:html)?\\n?(.*?)```\u0026#39;, r\u0026#39;\\1\u0026#39;, html, flags=re.DOTALL).strip() 4. Verificeret output # Efter rettelsen kørte scriptet rent mod alle 3 testdatalinjer og producerede korrekt formateret HTML grupperet per zone.\nRefleksion # Hvad gik godt\nAgenten forstod konteksten fra det eksisterende projekt uden at jeg skulle forklare strukturen fra bunden. Den valgte de rigtige biblioteker, håndterede edge cases og fulgte samme kodningsmønstre som resten af projektet.\nHvad krævede iteration\nOutput-rensning var ikke perfekt første gang. LLM\u0026rsquo;er er ikke deterministiske — samme prompt kan give lidt forskelligt output. Det betød at fejlen ikke altid reproducerede sig, hvilket gør det sværere at spotte under udvikling. Agenten løste det ved at tilføje defensiv rensning der fanger alle varianter.\nDen vigtigste observation\nKodeagenter er ikke bedst til at skrive kode fra ingenting — de er bedst til at udvide eksisterende kode de har kontekst på. Jo mere agenten ved om projektet, jo mere præcist og konsistent bliver resultatet.\nKildekode # github.com/Zuhami/stadeholder-generator — se batch.py\n","date":"20 April 2026","externalUrl":null,"permalink":"/Portfolio/posts/gang-4-kodeagenter/","section":"Blog","summary":"","title":"Gang 4 — Kodeagenter i softwareudvikling","type":"posts"},{"content":"","date":"17 April 2026","externalUrl":null,"permalink":"/Portfolio/tags/dify/","section":"Tags","summary":"","title":"Dify","type":"tags"},{"content":" Hvad er RAG? # En normal LLM ved kun det den er trænet på. RAG — Retrieval Augmented Generation — løser det ved at give modellen en videnskilde den kan søge i, når der kommer et spørgsmål. Flowet ser nogenlunde sådan her ud:\nSpørgsmål → embed spørgsmålet → søg i vector store → hent de mest relevante chunks → smid dem ind i prompten → LLM svarer\nDet giver to fordele: modellen kan svare på spørgsmål om dokumenter den aldrig har set, og den kan pege på konkrete passager frem for at finde på et svar der lyder rigtigt.\nRAG giver bedst mening når kildematerialet skifter tit, er privat, eller er for stort til at putte direkte i en prompt. Til generelle spørgsmål hvor modellen allerede ved nok er det bare unødigt komplekst.\nTre værktøjer vi kiggede på # Værktøj Godt Skidt ChatGPT CustomGPT Nul opsætning, man kender interfacet Låst til OpenAI, ingen reel kontrol customGPT.ai Nem at embed\u0026rsquo;e på en side Betalt fra start, black-box Dify.ai Open source, fuld kontrol over pipelinen Lidt mere at sætte op Vi endte med at bruge Dify.ai til den praktiske del. Det er det eneste af de tre der lader dig se og justere hvad der faktisk sker — chunk-størrelse, embedding-model, retrieval-indstillinger og det hele.\nHvad vi byggede # Vi lavede en chatbot der kan svare på spørgsmål om vores studieordning. Det er et tæt PDF-dokument fyldt med regler og formuleringer som ingen gider læse igennem manuelt — perfekt use case.\nDataforberedelse\nInden vi uploadede dokumentet konverterede vi det fra PDF til Markdown. Rå PDF-udtrækning er ret rodet — overskrifter blandes med brødtekst, tabeller går i stykker, sidetal dukker op midt i sætninger. Markdown giver chunker\u0026rsquo;en noget rent at arbejde med, og det mærkes på kvaliteten af svarene.\nChunking og indeksering\nDify deler dokumentet op i overlappende chunks og kører dem igennem en embedding-model der laver dem om til vektorer. De vektorer gemmes og søges via cosine similarity når der kommer et spørgsmål.\nChatbotten\nNår man stiller et spørgsmål henter chatbotten de mest relevante chunks og putter dem ind i prompten. System-prompten siger til modellen at den kun må svare ud fra det dokument — det reducerer hallucination og holder den on-topic.\nRefleksion # Det mest interessante var ikke selve chatbotten — det var at se hvor meget dataforberedelsen betød. Det samme spørgsmål mod rå PDF og renset Markdown gav mærkbart forskellige svar. Garbage in, garbage out gælder åbenbart lige så meget her som alle andre steder.\nSammenligningen mellem værktøjerne viste også noget ret tydeligt: jo nemmere det er at komme i gang, jo mindre kan du se hvad der sker indeni. CustomGPT virker på fem minutter, men når det fejler ved du ikke hvorfor. Dify tager længere tid, men du kan faktisk debugge det. Til noget der skal bruges i produktion vil jeg altid foretrække at kunne se hvad der foregår.\n","date":"17 April 2026","externalUrl":null,"permalink":"/Portfolio/posts/gang-2-rag/","section":"Blog","summary":"","title":"Gang 2 — Retrieval Augmented Generation (RAG)","type":"posts"},{"content":" Dagens mål # Denne gang gik fra teori til praksis. Vi skulle bygge en RAG-løsning helt fra bunden — vælge en case, forberede data, sætte retrieval op og tune promptdesignet til noget der faktisk virker.\nFokuspunkterne var:\nKonverter rå data til markdown for bedre chunking Optimer datakilder og dokumentstruktur Tune retrieval og promptdesign for bedre svarkvalitet Deploy en chatbot direkte på portfolio-sitet Hvad vi byggede # Vi brugte Dify.ai som platform — det giver fuld kontrol over hele pipelinen uden at man skal bygge embedding-infrastruktur fra bunden.\nDataforberedelse\nDet første og vigtigste skridt: konverter kildematerialet fra PDF til Markdown. Rå PDF-udtrækning er fyldt med støj — sidetal midt i sætninger, ødelagte tabeller, manglende overskriftshierarki. Markdown giver chunker\u0026rsquo;en rent input, og det mærkes direkte på kvaliteten af svarene.\nChunking og indeksering\nDify splitter dokumentet op i overlappende chunks og embedder dem som vektorer. Størrelsen på chunks har stor betydning — for store chunks giver for meget irrelevant kontekst, for små chunks mister sammenhæng. Vi testede os frem.\nPromptdesign\nSystem-prompten instruerer modellen i kun at svare ud fra det hentede materiale. Det reducerer hallucination og holder svarene relevante. Vi itererede på formuleringen for at finde den rette balance mellem præcision og fleksibilitet.\nRefleksion # Det mest overraskende var igen dataforberedelsen. Samme spørgsmål mod rå PDF og renset Markdown gav mærkbart forskellige svar — ikke fordi modellen er bedre, men fordi den har bedre kontekst at arbejde med.\nPromptdesign viste sig at være ligeså vigtigt som selve retrieval-opsætningen. En uklar systemprompt giver upræcise svar selv med perfekte chunks. Det er to separate problemer der begge skal løses.\nLive demo # Prøv chatbotten herunder — den er bygget med Dify og deployede direkte på dette site:\n","date":"17 April 2026","externalUrl":null,"permalink":"/Portfolio/posts/gang-3-rag-praktisk/","section":"Blog","summary":"","title":"Gang 3 — RAG i praksis: Vi bygger fra bunden","type":"posts"},{"content":"","date":"17 April 2026","externalUrl":null,"permalink":"/Portfolio/tags/llm/","section":"Tags","summary":"","title":"Llm","type":"tags"},{"content":"","date":"17 April 2026","externalUrl":null,"permalink":"/Portfolio/tags/rag/","section":"Tags","summary":"","title":"Rag","type":"tags"},{"content":" Hvad vi lavede # Første gang gik med at få styr på hvad kurset egentlig handler om — AI-drevne applikationer, hvordan LLM\u0026rsquo;er bruges i rigtig software, og hvad eksamen kommer til at se ud. Ret hurtigt efter gik vi i gang: alle skulle have et portfolio-site op at køre, som vi kan bruge til at dokumentere det vi bygger hen ad vejen.\nStacken:\nHugo — statisk site-generator, hurtige builds og nemt at deploye Blowfish — et Hugo-tema med dark mode og et fedt profilayout GitHub Pages — gratis hosting direkte fra repo\u0026rsquo;ets docs/-mappe Fra nul til live site tog ca. 30 minutter. Installer Hugo, klon Blowfish, konfigurer params.toml, kør hugo, push til GitHub og slå Pages til. Ret simpelt.\nRefleksion — Hvad jeg håber at få ud af det her # Inden kurset har jeg mest brugt LLM\u0026rsquo;er som en lidt klogere autocomplete — få svar på spørgsmål, generere boilerplate, forstå fejlmeddelelser. Det er praktisk, men det er bare et lag ovenpå den samme arbejdsgang som altid.\nDet jeg synes er interessant er skridtet videre: AI ikke bare som hjælp mens man koder, men som en del af selve applikationen. En LLM der selv læser data, tager beslutninger og kalder andre systemer — det er en anden slags arkitektur end det vi normalt arbejder med.\nEt par ting jeg er nysgerrig på:\nPrompt engineering som design. Måden man formulerer en opgave til en LLM har lige så meget at sige for resultatet som koden rundt om. Det lyder mere som at skrive specifikationer end instruktioner.\nHvornår skal man ikke bruge en LLM. Ikke alt skal være AI. Jeg tror den svære del er at finde ud af præcis hvor det giver mening og hvor det bare er unødigt komplekst.\nHvordan tester man det her. Normal software har unit tests. LLM-output er ikke deterministisk. Jeg aner ikke rigtigt hvordan man definerer \u0026ldquo;korrekt\u0026rdquo; i det setup, og det vil jeg gerne finde ud af.\nDen ting jeg er mest skeptisk over for er hype. Mange \u0026ldquo;AI-drevne\u0026rdquo; produkter er bare et tyndt lag oven på en API. Jeg håber kurset giver mig nok indsigt til at se forskel på det og noget der faktisk er gennemtænkt.\n","date":"10 April 2026","externalUrl":null,"permalink":"/Portfolio/posts/gang-1-kursusintro-portfolio/","section":"Blog","summary":"","title":"Gang 1 — Kursusintro \u0026 Portfolio-opsætning","type":"posts"},{"content":"","date":"10 April 2026","externalUrl":null,"permalink":"/Portfolio/tags/hugo/","section":"Tags","summary":"","title":"Hugo","type":"tags"},{"content":"","date":"10 April 2026","externalUrl":null,"permalink":"/Portfolio/tags/portfolio/","section":"Tags","summary":"","title":"Portfolio","type":"tags"},{"content":"","date":"15 March 2026","externalUrl":null,"permalink":"/Portfolio/tags/dolor/","section":"Tags","summary":"","title":"Dolor","type":"tags"},{"content":"","date":"15 March 2026","externalUrl":null,"permalink":"/Portfolio/tags/ipsum/","section":"Tags","summary":"","title":"Ipsum","type":"tags"},{"content":"","date":"15 March 2026","externalUrl":null,"permalink":"/Portfolio/tags/mobile/","section":"Tags","summary":"","title":"Mobile","type":"tags"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/Portfolio/tags/lorem/","section":"Tags","summary":"","title":"Lorem","type":"tags"},{"content":"","externalUrl":null,"permalink":"/Portfolio/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/Portfolio/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"}]