// EKSEMPLER
Eksempler på projekter og proof-of-concepts.
Besøg min FoGit-side for offentligt tilgængelige repositories.
Alle disse småprojekter/POC'er er hostet på en gammel Lenovo-kasse hjemme hos mig sammen med en masse private whitelisted ting. Foran den står en Ubuntu VPS, som kun fungerer som en fast IP-adresse og reverse proxy via Caddy til Lenovo-kassen gennem en sikker WireGuard-protokol.
- Beskrivelse
-
Generativ AI er fremragende til at opsummere tekst, billeder og video. Jeg har dog et problem; jeg stoler overhovedet ikke på Anthropic og OpenAI. Altså overhovedet ikke. Og, det burde andre heller ikke gøre, hvis du spørger mig. Jeg tror, at små lokale modeller inden længe vil blive integreret med strukturerede og ustrukturerede data, så de kan søge gennem store dokumentsamlinger og levere private og koncise svar. Det kunne for eksempel være store lovsamlinger, gamle kort- og billedarkiver og nyhedsarkiver, hvor man kan få svar på tværs af jpg, mp4, pdf, xlsx, csv, txt, ppt og så videre og så videre. Her kan der være tale om tusindvis af filer, som generativ AI kan svare på baggrund af gennem en vektordatabase.
Der er nogle åbenlyse grunde til, at vi ikke kan stole på nogle få virksomheder til at håndtere alle vores data, og der er også fagområder, hvor det slet ikke kan komme på tale at sende data og forespørgsler til Anthropic eller OpenAI. Men det er en politisk diskussion, som jeg ikke vil gå ind i her.
Anyways, derfor har jeg udviklet en generativ AI RAG-prototype, der kan gøre noget af dette. Applikationen hedder RAGGo, RAG fordi den bruger Retrieval Augmented Generation, og den er skrevet i Go. Originalt, hva'? RAG-Go. RAGGo. Nå, men RAGGo er hostet på en gammel stationær computer hjemme hos mig. Den indekserer dokumenter og billeder og giver mulighed for at føre en samtale med dem ved hjælp af naturligt sprog. Det hele kan køre på en (air-gapped, uden internetforbindelse) Linux-server på ens egen IT-infrastruktur. Data forlader aldrig din server, går aldrig gennem en tredjeparts cloud-udbyder og er fuldstændig i dine hænder. Der er ingen ukendte månedlige API-regninger eller skjulte licenser. Når RAGGo er sat op, kører systemet uden ekstra omkostninger. Her kører vi en lille embedding-model og Gemma 4 lokalt.
RAGGo understøtter følgende dokumentformater:
pdf, docx, odt, xlsx, pptx, html, htm, epub, txt, mdRAGGo understøtter følgende billedformater:
png, jpg, jpeg, webp, gifDokumenter med struktur (såsom pdf, docx, odt, xlsx, pptx, html, htm, epub) analyseres semantisk og bevarer sidetal, overskrifter, tabeller og lister som metadata, der bevarer sammenhængen. Flade tekstfiler og markdown-filer opdeles i semantisk meningsfulde stykker og bliver ikke skåret over midt i en sætning. Billeder behandles via AI-captioning, som beskriver deres indhold, og indekseres derefter sammen med de øvrige dokumenter, så der også kan søges i visuelle data ved hjælp af naturligt sprog.
Bygget på åbne værktøjer som Go, PostgreSQL med pgvector og Ollama er opsætningen meget enkel og nem at reproducere og vedligeholde. En webside giver adgang til chat og dokumenthåndtering. For virksomheder og institutioner, der håndterer store dokumentsamlinger, skaber dette en søgbar vidensbase, som fortsat er nem at administrere med forudsigelige omkostninger og uden at være afhængig af eksterne AI-udbydere.
Jeg har lagt et par filer ind fra hjemmesiderne snar.fo (alt under kvæde, eventyr og sagn) og Friends Wikipedia, så kan du prøve dig frem :-)
- Vejledning
-
Log ind – For at bruge RAGGo skal du logge ind via linket nedenfor.
Brugernavn: raggo
Adgangskode: oKfE!DHecTgJM9zPjjgfqL#mt59#n5G4C3gsjF4fTilføj dokumenter – Klik på +-knappen under inputfeltet. Vælg en understøttet fil (PDF, DOCX, ODT, XLSX, PPTX, HTML, EPUB, TXT eller Markdown). Filen analyseres og indekseres automatisk og bliver søgbar i løbet af få sekunder. Du får en bekræftelse med filnavnet og antallet af oprettede tekststykker.
Tilføj billeder – Klik på billedikonet ved siden af +-knappen. Vælg en billedfil (PNG, JPG, JPEG, WEBP eller GIF), og skriv en kort beskrivelse af billedet. Beskrivelsen gør billedet søgbart. Hvis en vision-model er konfigureret, kan du klikke på Auto-caption for automatisk at generere en beskrivelse.
Stil spørgsmål – Skriv dit spørgsmål i inputfeltet, og tryk på Enter (brug Shift+Enter for linjeskift). RAGGo finder de mest relevante tekstuddrag fra de indekserede dokumenter og billeder og genererer et svar baseret på dette materiale. Hvert svar angiver kildefilen og, hvor det er relevant, sidetallet for strukturerede dokumenter.
Vis indekserede filer – Klik på Documents i navigationsmenuen for at se en opdateret tabel over alt indhold i din samling: filnavne, filtyper, antal tekststykker, tidspunkt for import og registrerede strukturelle elementer som tabeller og lister. Listen opdateres automatisk.
- Teknologi
-
▸ Go – med tredjepartsbibliotekerne tsawler/tabula, openai/openai-go og fsnotify/fsnotify
▸ Postgres 18 + pgvector – PostgreSQL-backend med pgvector som vector store
▸ Taskfile – bruges til build-, deployment- og orkestreringskommandoer
▸ Ollama – inferensserver
▸ AI-komponenter – samtalemodel som primært chatinterface; embedding-model, der konverterer tekststykker til vektorer til lighedssøgning; multimodal model, der tager billeder som input og producerer tekst som output; query rewriter, der omskriver tidligere brugerforespørgsler til en optimeret søgeforespørgsel
▸ AI-modeller – gemma4, nomic-embed-text
▸ CSS
▸ HTML
▸ JavaScript
- Kildekode
- Privat
- Brugernavn
- raggo
- Adgangskode
- oKfE!DHecTgJM9zPjjgfqL#mt59#n5G4C3gsjF4f
- Link
- RAGGo
- Beskrivelse
-
Som den gamle mand sagde: Det er jo meget fint at vise, at man kan hente data fra et API og visualisere dem på en hjemmeside, hvor brugerne kan filtrere visningen. Her bruger vi fiskeristatistik, som bliver opdateret én gang om måneden. Så kan vi ligesom se, hvad der sker, ikke? Jeg kalder denne applikation 'hagfisk'. Altså statistik (FO = hagtøl) + fisk. Hagfisk. Jeg har fundet ud af, at hagfisk også er en helt utrolig modbydelig og slimet fisk.
Men ja, jeg gik med navnet alligevel.
Anyways, der er ikke så meget mere at sige: Hent data fra Hagstova-API'et, gem dem i en lokal DuckDB-database på serveren, eksponér dem via et API til webfrontend'en, og visualisér dataene.
- Vejledning
-
Gå ind på siden, kig dig omkring og filtrér.
- Teknologi
-
▸ Rust - med biblioteker axum, serde, tokio, tower-http, reqwest, rust-embed
▸ DuckDB - lokal OLTP-database
▸ HTML
▸ JavaScript
▸ CSS
▸ ECharts - visualiseringsbibliotek
- Kildekode
- FoGit Repos
- Link
- Hagfisk
- Beskrivelse
-
Webapp til tidsregistrering og fakturagenerering til PDF. Jeg bruger selv denne app i mit daglige arbejde som konsulent. Jeg byggede den, fordi der ikke er nogen app, der gør præcis dette på det færøske marked. Derudover er jeg også for nærig til at betale en virksomhed et månedligt gebyr for dette. Denne prototype kører kun i browseren uden nogen backend, så alle data bliver slettet hvert 20. minut.
Ellers er der i den version, jeg bruger, tilknyttet et backend-API til en VPS, som reverse proxy'er kald gennem Caddy til min hjemmeserver, hvor en SQLite-database tager imod alle CRUD-operationer. Denne version er selvfølgelig IP-whitelistet, så du kan ikke bruge den ;-)
- Vejledning
-
1. Indtast information om din virksomhed på [SETTINGS]-siden (der er default values, hvis du ikke gider det her)
2. På [TIMER]-siden opretter du Company, som er den virksomhed, du skal arbejde for. Indtast navn, adresse, e-mail, telefonnummer og standard timepris. Alt dette bliver brugt til at regne den endelige pris ud og som information til fakturagenerering.
3. Opret derefter en Assignment, som er en større klump arbejde. Det kan for eksempel være Udvikle backend til den nye hjemmeside hos example.com. Et Company kan have flere Assignments. Her kan du også give en længere beskrivelse af opgaven og en særlig pris for netop denne Assignment. Hvis der ikke bliver angivet nogen pris, er det standardprisen hos den virksomhed, der lige er oprettet.
4. Opret derefter en Task. Dette er en underklump af arbejde under en Assignment. En Assignment kan eje mange Tasks. Så vi opretter en Task, der hedder Udvikle GET customer endpoint eller noget.
5. Herfra kan du så vælge at knytte tid til en Task, enten ved at starte Timer eller ved at indtaste en Manual entry.
6. Nu er opgaven færdig, og du skal sende faktura til Company. Tryk på Generate invoice (PDF). På denne side ser du timer, beløb og pris. Skriv en kort beskrivelse af denne Assignment i Comment. Her kan du også knytte fakturalinjer på, som ikke er timer, men en eller anden enhed. Så du kan f.eks. sætte en router på til 500 kr. stykket eller noget. Nå, men når du er klar, tryk på Generate PDF, og tada, en smuk faktura, klar til at sende til Company. Pyha, det var meget, men prøv det?
- Teknologi
-
▸ Go - webserveren, backendkode, templating language osv.
▸ HTMX - brugt til at lave CRUD-operationer
▸ GNU Make - brugt til at automatisere bygning af binary og deployment til remote server via ssh med én kommando.
▸ CSS
▸ HTML
▸ JavaScript
▸ SQLite
- Kildekode
- FoGit repos
- Link
- zeitkort
- Beskrivelse
-
Jeg har længe haft lyst til at lave et projekt, der streamer hændelser fra en database til en event-consumer. Altså ved hjælp af det, man kalder CDC (Change Data Capture), hvor ændringer hentes fra databasens transaktionslog eller replication-mekanisme og sendes videre til en consumer.
CDC/log-based replication bygger på, at databasen allerede registrerer ændringer for at kunne håndtere recovery og replication. I PostgreSQL er det f.eks. WAL og logical replication/logical decoding, i MariaDB/MySQL er det binlog, og i SQL Server findes CDC som en indbygget funktion. Disse mekanismer kan fange INSERT, UPDATE og DELETE i tabeller, og derfor er det en effektiv måde at streame ændringer fra en database på, uden at applikationen selv skal sende ekstra events.
Jeg mener, at Debezium er en af de stærkeste løsninger, når det handler om at streame data mellem en database og en consumer, såsom Apache Kafka eller NATS. Debezium kan læse ændringer fra databasen og sende dem videre som events med metadata. Det er skrevet i Java og er let at deploye som en container på en Linux-server.
Jeg har ofte rodet med Apache Kafka, og ofte står jeg tilbage uden resultat og med hovedpine efter en lang dag med trial and error. Derfor valgte jeg denne gang at bruge NATS i stedet. NATS er skrevet i Go og er en binary, ikke som det Kafka-Java-monster, der har tusind bevægelige dele.
Nå, men kort fortalt: dette er et inventory management-system. Inde på siden kan man lave CUD-operationer, som resulterer i SQL DML mod PostgreSQL-databasen. CDC fanger ændringerne fra loggen, Debezium sender ændringen sammen med metadata til NATS, og derfra kunne flere systemer subscribe til real-time data fra databasen.
Fordelen ved dette er, at det operative system bliver decoupled fra downstream-brugen af live-data. Inventory-systemet skal bare gøre sit normale arbejde mod databasen, mens andre systemer kan bruge ændringerne uden at være direkte bundet til selve applikationen. Det kunne for eksempel være et BI-dashboard, som viser live-data fra inventory, eller andre systemer og medarbejdere, som skal kunne træffe beslutninger ud fra live operative data.
I dette tilfælde bliver data flowet blot vist tilbage på skærmbilledet.
- Vejledning
-
I demoen ser du inventory-systemet til venstre og CDC-trace til højre. Vælg en vare og lav en ændring, f.eks. sell, restock, rename/move eller delete. Når ændringen er udført, kan du til højre se, hvordan den bevæger sig gennem flowet: PostgreSQL -> Debezium -> NATS JetStream -> browser. Nederst vises selve eventet som raw JSON og audit trail, så du kan se både tilstanden før og efter ændringen.
- Teknologi
-
▸ Go - webserver, API-endpoints, NATS-consumer og SSE event stream.
▸ PostgreSQL - inventory-database med logical replication slået til.
▸ Debezium Server - fanger ændringer fra PostgreSQL og publisher dem som events.
▸ NATS JetStream - event streaming-lag mellem Debezium og Go-backend.
▸ Taskfile - brugt til build, deployment og orchestration-kommandoer.
▸ Podman - kører development- og production-infrastruktur som containers (NATS, PostgreSQL, Debezium).
▸ Server-Sent Events - streamer CDC-events live fra Go-backend til browseren.
▸ HTML
▸ CSS
▸ JavaScript
- Kildekode
- FoGit repos
- Link
- inventory-fló
- Beskrivelse
-
Web API til at hente data fra backend, skrevet i Go. Dette er et tænkt eksempel på registrering af vold til et API-endpoint, både som registreringsværktøj og til senere analyse.
Bliver deployed som kun én binary uden dependencies eller runtime environment. Hele frontend'en er embedded i binary. Projektet bruger kun Go standard library, ingen ekstern dependency.
- Vejledning
-
Se den tekniske vejledning på linket nedenfor.
- Teknologi
-
▸ Go - 100% Golang std lib: html/template, net/http, flag, crypto
▸ Taskfile - procesautomatisering, builds og deployment
▸ Reflex - hurtig genopbygning og start af binary ved ændring af filer i repoet
▸ Rsync - til at sync'e til produktion
▸ CSS
▸ HTML
▸ JavaScript
- Kildekode
- FoGit repos
- Link
- Abuse Registration API
- Beskrivelse
-
Webapp udviklet for Landsverk. Brugt til jobmatch og lignende.
Gratis hosting og deployment til GitHub Pages med Hugo/Go som static site generator, der er integreret med en CI/CD pipeline. Et PR ind på main opdaterer hele siden.
- Vejledning
- Find ud af, hvilken profil du er, ved at klikke dig gennem Starvskumpass.
- Teknologi
-
▸ Go - Hugo bruger Go html/template som templating language
▸ Hugo - Static Site Generator skrevet i Go
▸ JavaScript - quizlogik
▸ CSS
▸ HTML
- Kildekode
- Github repos
- Link
- Starvskumpass-link
- Beskrivelse
-
Et simpelt POC af en chatfunktion skrevet i Rust. Rust fascinerer, så jeg ville prøve at bygge og deploye et Rust-projekt.
Vi har back office, hvor en supporter venter på beskeder, mens han spiser chips.
Så skriver to brugere ind i chatten, og supporteren kan chatte med begge brugere på én gang.
Fremadrettet har jeg tænkt mig at knytte back office til en backend, hvor en supporter kan knytte metadata til chats, så man i fremtiden kan lave kvantitative analyser. Hvis man også gemmer samtalerne, kan man lave sentimentanalyser eller kvalitative analyser.
Men jeg har børn og et hus og et arbejde, jeg skal tage mig af, så...
- Vejledning
-
Dette POC sletter al data/chats hvert 20. minut og har rate limiting på req/resp, så min hjemmeserver ikke bliver smadret.
Gå ind på linket nedenfor. Vælg først supporteren, gå tilbage til forsiden i en ny fane, og vælg derefter bruger.
Giv brugeren et eller andet navn, og chatten starter. Skriv noget, og supporteren ser, at en samtale begynder. Nu kan du chatte med dig selv.
Men hvad hvis du vil chatte endnu mere med dig selv? Opret en ny bruger, og nu har supporteren to chats åbne.
- Teknologi
-
-
▸ Axum - web framework i Rust. Tager sig af routing og middleware og alt det der. Nogenlunde det samme som Gin i Golang og FastAPI i Python
▸ Tokio - det man altid bruger, når man skal have async await i Rust
▸ Serde - serialisering/deserialisering fra JSON til Rust structs
▸ GNU Make - brugt til at automatisere bygning af binary og deployment til remote server via ssh med én kommando.
▸ CSS
▸ HTML
▸ JavaScript
- Kildekode
- Privat
- Link
- Bruger til support chat
- Beskrivelse
-
Denne hjemmeside er hostet på en Lenovo-kasse hjemme hos mig.
- Vejledning
- N/A
- Teknologi
-
▸ Go - 100% Golang std lib, bortset fra en rimelig standard dot-env-læser, joho/godotenv
▸ Taskfile - procesautomatisering, builds og deployment
▸ Reflex - hurtig genopbygning og start af binary ved ændring af filer i repoet
▸ Rsync - til at sync'e til produktion
▸ CSS
▸ HTML
▸ JavaScript
- Kildekode
- Privat
- Link
- fló.fo