Navigent. smart chat
Jeg byggede en chat-først AI til B2B cold outreach, over-engineerede den grundigt, overlevede et rigtigt hackforsøg — og slettede så det hele. Det her er hvad jeg lærte, og hvorfor det næste bliver bygget helt anderledes.
Idé til live kampagne på ti beskeder.
Du åbnede en chat, skrev noget i stil med “find Heads of Marketing i Berlin og send dem en cold email”, og en state machine på serveren kørte resten: find leads, skriv emails, vælg tone, planlæg, launch. Hvert trin kom tilbage som et interaktivt kort inde i chatten — målgruppe-preview, redigerbar email, schedule-editor. Det virkede. Det var også det mest over-engineerede jeg nogensinde har bygget.
Briefen sagde LangGraph. Det gjorde jeg ikke.
Jeg håndlavede en state machine i stedet. “Grafen” er i virkeligheden bare et register af felter plus rene funktioner der regner næste trin ud — rækkefølgen i et array er flowet. 1.339 linjer i én fil.
LangGraph føltes som overhead for et flow på ~10 mostly-lineære trin. Det var en forsvarlig beslutning dengang — men det løste ikke det egentlige problem, nemlig at jeg over-byggede selve flowet. (Mere om det i “Hvad jeg fik galt”.)
Fire skills og en model-split
Goal-klassifikator
En billig model router intaket til det rette mål — find_leads, email_campaign, undecided.
Multi-intent nav
Ét LLM-kald pr. tur returnerer både primær intent og side-flags: off-topic, injection, spørgsmål.
Concierge (Q&A)
Svarer fra et videns-dokument og afviser off-topic og prompt-injection deterministisk — uden et modelkald.
Audience-architect
Naturligt sprog → Prospeo-filtre. Forfin, udvid eller bekræft målgruppen direkte i chatten.
Strategist
Vælger persona og skriver selve email-copyen — den dyre model, fordi det er det eneste brugeren bedømmer.
Pointen i model-splittet: brug den billige model til alt det kedelige — routing, parsing, Q&A — og gem den dyre til selve email-copyen. Ingen beundrer en klassifikators prosa; emailen en lead læser er det eneste output et menneske rent faktisk bedømmer.
Det kedelige forsvar var det bedste jeg lavede
- Billig/dyr model-split — pengene brugt der hvor brugeren faktisk kan smage det.
- Rate limits + cost caps. (De reddede min pengepung under et rigtigt hack.)
- Hvert kort valideret af et schema — klienten stoler aldrig blindt på serveren.
- En kill switch der fandtes før jeg fik brug for den.
- Audit-logs der overlevede selv en slettet chat.
Jeg byggede til fleksibilitet ingen brugte
- En 1.339-linjers state machine til at gå 10 trin i rækkefølge. En wizard havde gjort det.
- Chatten var den forkerte indpakning — kortene var værdien, ikke samtalen.
- 3–5 LLM-kald før brugeren så et eneste ord tilbage. Langsomt og dyrt.
- For meget magt gemt bag ét enkelt admin-flag (“god mode”).
- Row-Level Security slået fra by default — og jeg tjekkede det ikke.
Så prøvede nogen rent faktisk.
Mens den var live, kom en rigtig bruger ind i et superadmin-view, slog “god mode” til og begyndte at spamme chatten for at brænde mit OpenAI-budget af. Jeg sad ved skærmen og så det ske live. Mine rate limits og spend caps holdt, jeg trak kill switchen, smed ham ud — og brugte aftenen på at lukke døren han kom ind ad.
Det rigtige hul var ikke AI’en — det var databasen. Supabase serverer hver tabel gennem et auto-genereret, offentligt API, og Row-Level Security var slået fra. Jeg slog RLS til på hver eneste tabel samme aften. Lektien: giv ikke din AI — eller dit admin-flag — mere magt end opgaven kræver, og regn med at nogen prøver hver dør du glemmer at låse.
Så slettede jeg det hele.
788 filer. 181.757 linjer. Én commit. Plus ~800 linjers SQL der tømte databasen. Ingen kørende demo i dag — og det er lidt pointen. At bygge det var læringen; at beholde det havde været fejlen.
Hvad nu: LinkedIn, bygget mere skånsomt
Hvad ændrer sig
- LinkedIn, ikke email — deliverability er et mareridt af spamfiltre og 20-sends-om-dagen warm-up.
- En wizard, ikke et metro-net.
- Færre LLM-kald, flere knapper, mindre gætteri.
- Least privilege fra linje ét.
Hvad følger med
- Audience-søgning via Prospeo.
- AI-personalisering af copy.
- Scheduling og opfølgnings-sekvenser.
- Alle sikkerhedslektionerne.
De tre næste opgaver
LinkedIn-kanal
Connect-flow via Unipile + send/læs beskeder end-to-end, så en personaliseret besked kan afsted fra chatten.
Wizard, ikke chat
Knap-drevet flow gennem de samme trin. Klassificér kun når en knap ikke kan svare — minimér LLM-kald pr. tur.
Least privilege fra start
RLS slået til på hver tabel, ingen genvejs-“god mode”, og spend caps fra dag ét frem for som eftertanke.