Hans Kristian lar AI anbefale deg vin fra polet – uten å ende opp med skyhøy regning

Gjorde grep i hobbyprosjektet Vinvenn som viste seg å være «helt kritisk for å unngå en skyhøy regning fra OpenAI».

Du finner Hans Kristian Sandes hobbyprosjekt Vinvenn på Vinvenn.no. Her lar han OpenAIs språkmodeller anbefale deg vin til mat. 📸: Ole Petter Baugerød Stokke
Du finner Hans Kristian Sandes hobbyprosjekt Vinvenn på Vinvenn.no. Her lar han OpenAIs språkmodeller anbefale deg vin til mat. 📸: Ole Petter Baugerød Stokke Vis mer

Lurer du på hva du skal servere ved siden av pavlova eller rømmegrøten på 17. mai? 🇳🇴

Eller hvilke bobler som passer best til frokosten? 🍾

Da vil jeg anbefale å gi Vinvenn et forsøk.

Hva er Vinvenn? 🍇

Vinvenn er en mobile-first web app som fungerer som en slags AI-sommelier. 🤖 Den hjelper deg å finne ulike mat- og vinkombinasjoner (såkalte pairings) med vin du finner på ditt nærmeste vinmonopol. 🍷

Til tross for at praksisen å kombinere vin og mat har eksistert i flere tusener av år, så er det dessverre fortsatt en ganske så vanskelig greie for de fleste av oss. Dessuten blir man ikke akkurat noe klokere når man leser ekspertene beskrive vinen i anmeldelsene sine med begreper som «petroleum», «nyklipt gress» og «våt stein» ... hva skal det bety liksom?

Neida, de har jo naturligvis sitt eget fagspråk – på lik linje som min kollega Sindre påpeker gjennom sin tekniske ordbok at vi i IT-bransjen også har vårt eget fagspråk. Samtidig tør jeg påstå at til tross for mangel på forståelse (eller kanskje nettopp derfor 🤔) for hva disse fancy begrepene betyr, så ville nok de fleste av oss likevel stolt på hva en vinkelner anbefalte oss til maten ute på restaurant, selv uten å ha snøring på hva eksempelvis «våt stein» skal formidle.

Summa summarum: Vi anerkjenner vinkelneres talent og deres ekspertise selv uten å skjønne helt hva de sier, og ønsker derfor likevel deres anbefalinger og råd!

Det er her Vinvenn kommer inn, som altså er et forsøk på å gjøre det enklere å forstå hvorfor ting passer sammen med et enklere språk, samtidig som man kan dra nytte av kunnskapen disse ekspertene besitter.

Hans Kristian Sande, mannen bak Vinvenn.no. 📸: Bekk
Hans Kristian Sande, mannen bak Vinvenn.no. 📸: Bekk Vis mer

Hvor kom ideen fra? 🍽️

Jeg skulle invitere noen venner til middag med «ølbreserte svinekjaker» på en oppskrift jeg hadde fått fra min onkel, men jeg var usikker på hvilken vin jeg skulle servere til maten.

Jeg forsøkte først med Vinmonopolets drikkevelger, men kategorien «svin» var veldig lite beskrivende, da det kunne tilsi alt fra sursøt asiatisk svinewok, til feit juleribbe, grillet spareribs, og helt til servelat, liksom?

Jeg valgte derfor heller å ty til mer klassiske hjelpemidler, altså bøker, og omsider fant jeg noe som passet bra i boken What To Drink With What You Eat. Deretter måtte jeg «mappe» den anbefalte vinen fra boken til en konkret vin på vinmonopolets sider. I alt ble det en vellykket sammensetning, men det var jo litt tungvint (hvis du er like lat som meg) synes jeg ...

Nevnte middag var omtrent samtidig som OpenAI hadde sluppet sitt Assistants-API.

I dagene etter middagen fant jeg ovennevnte bok på PDF hos Scribd, og dermed hadde jeg plutselig en konkret produktidé til å utforske denne teknologien nærmere med.

Hvordan fungerer Vinvenn? 🧃

Vinvenn har som nevnt to ulike bruksområder:

  1. Enten så ønsker bruker å få anbefalt en vin til maten sin, eller...
  2. ...så ønsker bruker å få anbefalt en matrett til vinen sin.

I begge tilfellene benytter jeg meg av to modeller; der den første er en såkalt Assistant-modell (fortsatt i beta), mens den andre er en såkalt Chat-modell.

I Assistant-modellen har jeg lastet opp et datasett med en rekke utvalgte vinbøker, -journaler og -artikler i tekstlig format. Fordelen med tekstdata er at det enkelt kan vektoriseres, noe som gjør det mulig å indeksere og dermed effektivt søke gjennom (relativt) store datamengder på en strukturert måte. I tillegg kan man instruere modellen med såkalte system prompts eller instruksjoner, som lar deg manipulere hvordan den oppfører seg.

På bakgrunn av hvordan mitt datasett ser ut, så har jeg eksempelvis for use-case #1 instruert modellen min til å alltid returnere tre forskjellige type anbefalinger som output per matrett den får som input. Det er veldig enkelt å komme i gang med OpenAI sine Assistant-modeller via deres playground, og som man ser i Figur 1 under så er det kjapt gjort å legge til og iterere på systeminstruksjonene (instructions).

Hvordan det ser ut i playground-miljøet til OpenAI. Merk Instructions på venstre side hvor man mater modellen med systeminstruksjoner.
Hvordan det ser ut i playground-miljøet til OpenAI. Merk Instructions på venstre side hvor man mater modellen med systeminstruksjoner. Vis mer

Outputten fra Assistant-modellen mates så videre i Chat-modellen, som egentlig bare benyttes til å formatere det tekstlige svaret fra Assistant-modellen til et JSON-objekt som jeg klarer å håndtere i koden.

På dette tidspunktet hadde ikke OpenAI lansert sin nye feature “JSON mode”, så jeg måtte bruke en del tid på å få modellen til å alltid returnere dataen på riktig format. For disse Chat-modellene testet jeg derfor en del frem og tilbake med prompt engineering vs. fine-tuning av GPT-modellene.

Jeg besluttet til slutt å heller prompte, som vil si å mate modellen med mye kontekst, enn å gå for fine-tuning. Med nok justeringer på prompts så klarte omsider en ordinær modell å “alltid” returnere dataen på riktig format. Det er også blitt billigere å bruke standard modellene, og bare siden jeg startet på sideprosjektet har modellene jeg opprinnelig brukte falt mer enn 50% i pris.

Det skal dog sies at modeller med fine-tuning ga betydelig raskere responser, og de var samtidig mer nøyaktig. Figur 2 viser hva som skjer bak kulissene når jeg sender inn en matrett til API-et.

«Jeg måtte bruke en del tid på å få modellen til å alltid returnere dataen på riktig format.»
Hvordan en respons flyter fra Assistant- og til Chat-modellene.
Hvordan en respons flyter fra Assistant- og til Chat-modellene. Vis mer

Merk integrasjonen mot Firebase som benyttes til å mellomlagre output fra modellene per vin eller matrett, slik at jeg på den måten ikke må generere et svar hver bidige gang en bruker gjør et søk. Denne modelleringen er helt kritisk for å unngå en skyhøy regning fra OpenAI, og jeg mener det dessuten er noe jeg kan tillate uten at det går på bekostning av kvaliteten i løsningen, ettersom en «barbera d’alba» (for eksempel) neppe vil slutte å passe til en pizza margherita (for eksempel) med det første. Med andre ord så er behovet for generering i sanntid ikke like viktig slik Vinvenn er skrudd sammen.

Når Chat-modellene har returnert dataen på et format jeg kan håndtere i backend er det enkelt å filtrere og tilpasse hvilke produkter som skal returneres til frontend gitt kriteriene satt av bruker.

Jeg har lagt ved noen flytdiagrammer som illustrerer litt nærmere hvordan ting flyter for hvert use-case. I begge use-cases (1) og (2) så sjekker jeg først om det finnes data på nevnte matrett eller vin i Firebase, hvis nei, så generér en ny beskrivelse via OpenAI:

Hva som skjer når en bruker søker opp en matrett i Finn vin.
Hva som skjer når en bruker søker opp en matrett i Finn vin. Vis mer
Hva som skjer når en bruker søker opp en vin i Finn matrett.
Hva som skjer når en bruker søker opp en vin i Finn matrett. Vis mer

Teknologier ⚙️

Vinvenn består i hovedsak av en backend og en frontend.

  • Backenden er en Python Flask app hostet på GCP som har integrasjon mot OpenAI.
  • I frontend har jeg benyttet tailwind i det som er en Next.js 14 app.

Hvis du liker bakgrunnen så kan jeg røpe at det også var noe billige AI-generering ved hjelp av Dall-E 2. 😉 Ellers er frontend-appen er for øvrig hostet på Vercel.

Fremtidig arbeid 🔮

Hvis du selv er mat- og vininteressert så tar jeg imot feature-requests med åpne armer!

Jeg har noen idéer om hva jeg kan utvide tjenesten med, men jeg prøver samtidig ikke å lage en blåkopi av Vivino. Det hadde eksempelvis vært kult å utvide med alternativer til alkoholholdige drikkevarer. Tanken er uansett at tjenesten skal være en “educational experience”, heller enn et produkt som skal maksimere salg og konsum.

Så hvis du feirer den store dagen med bobler eller what not i glasset – vær ansvarlig og snill, og ellers ha en riktig god 17. mai! 🇳🇴😄