Sånn bygde han Partichat.no, uten hallusinering
Partichat.no skal vise deg hva partiprogrammene sier, ingenting annet. – Avgjørende for demokratiet, mener Henrik Halvorsen Kvamme (20), og forklarer hvordan.
Som førstegangsvelger synes jeg det er vanskelig å få oversikt over alle de politiske partiene og hva de mener. Derfor bygget jeg like før valget Partichat.no – på bare tre dager.
Problemet med å bruke for eksempel ChatGPT før valget er at den lett kan hallusinere. Hva partier mener endrer seg, og ChatGPT er gjerne ikke trent på de nyeste partiprogrammene. I tillegg er det alltid en risiko for at språkmodeller hallusinerer uten mer kontekst.
Partichat løser dette ved å bruke RAG, partiprogrammene som eneste kilde, og en streng systemprompt som nekter å gjette.
Resultatet er en politisk chatbot med nær null hallusinasjoner – noe jeg mener er avgjørende for demokratiet.
Hvorfor dette er viktig nå
- Valget: Politiske standpunkter bør beskrives slik partiene selv formulerer dem. I en valgsituasjon kan hallusinerte eller “beste gjetning”-svar skape feilinntrykk.
- Etterrettelighet: Når teknologien formidler politikk, må standarden være “kontekst eller stillhet”. Enten finnes det dekning i programmet, eller så sier boten “Ikke omtalt”.
- For meg personlig: Jeg var usikker på hva jeg skulle stemme. Ved å spørre om sakene jeg bryr meg om og se nøytrale, side om side svar basert på partiprogrammene, fikk jeg oversikt og tok et mer velinformert valg.
Nesten null hallusinasjoner
Nesten null hallusinasjoner handler mindre om fancy prompts – og mer om datadisiplin:
- Én ren kilde
- Streng retrieval
- En prompt som nekter å spekulere
Tre grep senket hallusinasjonene:
#1. Kildepolicy: kun partiprogram
Jeg ingester kun offisielle partiprogram PDF-er fra partienes nettsider. Ingen debattinnlegg, nyheter eller blogginnhold. Alle var maskinlesbare, så pdf-parse holdt – ingen OCR.
Metadata per chunk: parti, år, sidetall, kapittel. Dette muliggjør sporbarhet og kommende kildevisning i UI.
#2. Retrieval med likhetsterskel – per parti
- Ingen global miksing. Spør du om Høyre, søker jeg bare i Høyres program.
- Cosine likhet med minimumsterskel. Under terskel? Da får du “Ikke omtalt”.
#3. Systemprompt som nekter å gjette
Jeg instruerer modellen strengt til å alltid søke i partiprogrammet, og ellers svare “Ikke omtalt”.
system: `Du er en nyttig assistent som svarer basert
på ${party.name}s partiprogram.
Bruk verktøyet for å søke etter relevant informasjon i partiprogrammet
før du svarer.
Svar kun basert på informasjon fra partiprogrammet.
Hvis ingen relevant informasjon finnes, svar
"Ikke omtalt i ${party.name}s partiprogram."`
Stack og utvikling
Siden jeg fikk idéen litt sent før valget, måtte jeg lage nettsiden kjapt. For å spare tid brukte jeg Vercel AI SDK v5, som gjør det utrolig enkelt å komme i gang med en fullstack chat applikasjon. De har også en egen RAG Guide.
Frontend ble raskt utformet med hjelp av Claude Code. For å bevare kodekvaliteten bruker jeg streng typesjekking med TypeScript og Biome med Ultracite for formatering og linting. Ultracite setter opp strenge regler for deg - og brukes blant annet av Shadcn (utvikleren bak de kjente komponent biblioteket).
Stacken min:
- Next.js (frontend/backend)
- Shadcn
- Tailwind
- Bun
- Postgres med pgvector
- Drizzle ORM
- oRPC
Hvordan RAG fungerer – kort forklart
I stedet for å la svaret til en språkmodell bare basere seg på treningsdata, kan man feste relevant kontekst til hvert spørsmål ved bruk av RAG.
Nøkkelen ligger i embeddings og vector similarity search:
- Embeddings er vektorer som fanger semantikken i en tekst.
- Tekster med lik betydning får embeddings som ligger nær hverandre.
- Brukerens spørsmål gjøres om til en embedding, og sammenlignes med embeddings fra partiprogrammene.
- Treffer vi over en viss terskel, brukes det som kontekst. Hvis ikke → “Ikke omtalt”.
Et datapunkt som bygde tillit
Jeg testet “Hva mener partiet om formueskatt?” på alle partier.
Alt var greit – bortsett fra Arbeiderpartiet, som ga “Ikke omtalt i partiprogrammet”.
Først trodde jeg det var en bug. Det viste seg å være korrekt.
Den typen “nei svar” er akkurat det du vil ha når kilden ikke dekker spørsmålet.