tech-architecture
Dette dokumentet er auto-synket fra kildefilene i boligassistent-repoet. Endringer her vil overskrives ved neste sync. Rediger kildefilen direkte.
Begrunnelse per komponent
Section titled “Begrunnelse per komponent”Turborepo + pnpm
Section titled “Turborepo + pnpm”Monorepo gir felles typer, delt konfigurasjon og effektiv bygging. pnpm er raskere og mer diskeffektiv enn npm/yarn. Turborepo cacher builds og kjører tasks parallelt.
React + Vite
Section titled “React + Vite”React er en moden, fleksibel UI-plattform med stort økosystem. Vite gir rask utvikling og hot reload. shadcn/ui gir ferdige, tilpassbare komponenter uten lock-in — komponentene kopieres inn i prosjektet og eies fullt ut.
Express.js
Section titled “Express.js”Enkel og veldokumentert Node.js-ramme. Gir full kontroll over routing, middleware og API-design uten magi. Passer for et prosjekt der utvikleren ønsker å forstå hva som skjer.
Express 5 — breaking changes fra v4 (kjente gotchas):
Express 5 er en major versjon med breaking changes. De viktigste for dette prosjektet:
- Routing wildcards:
path-to-regexpv8 tillater ikke bare/*. Bruk eksplisitt path-prefix (app.use('/prefix', handler)) eller navngitt wildcard (/*splat). Seengineering-standards.mdseksjon 9 for detaljer. - Auth.js-montering:
app.use('/api/auth', ExpressAuth(config))— ikke'/api/auth/*'.app.usemed prefix-path håndterer sub-ruter automatisk. - Async handlers: Express 5 håndterer async route handlers native — ingen
express-async-errors-pakke nødvendig. server.close()callback: Er sync — ikke async. Bruk Promise-chaining inne i callback.
PostgreSQL + Drizzle ORM
Section titled “PostgreSQL + Drizzle ORM”PostgreSQL er den mest fullverdige åpen kildekode-databasen og støtter pgvector som utvidelse — viktig for semantisk søk. Drizzle ORM gir typesikker spørring, migrations og full SQL-kontroll uten overhead fra tyngre ORM-er som Prisma.
pgvector
Section titled “pgvector”Nødvendig for AI-kontekstbygging. Lagrer vektor-embeddings av entiteter (rom, avvik, møbler) slik at AI-assistenten kan finne semantisk relevante data til et brukerspørsmål via cosine similarity-søk. Alternativet ville vært ekstern vector store (Pinecone, Weaviate), men pgvector holder alt i én database.
Auth.js (self-hosted)
Section titled “Auth.js (self-hosted)”Gir fleksibel autentisering med e-post/passord og potensielt sosiale logins uten ekstern avhengighet. Self-hosted betyr full kontroll over brukerdata. OAuth-providere kan legges til uten arkitekturendring.
S3-kompatibel objektlagring som kjører i Docker — ingen ekstern avhengighet i MVP. Kan erstattes av AWS S3 eller Cloudflare R2 uten kodeendringer (identisk API).
Claude API
Section titled “Claude API”Anthropic Claude er best egnet for kontekstuell rådgivning, strukturert output og reasoning over kompleks boligdata. Støtter lange kontekstvinduer som trengs for å sende inn romdata, manualinnhold og designretning i samme prompt.
DALL-E 3
Section titled “DALL-E 3”Brukes for romvisualisering. Ikke i MVP, men arkitekturelt planlagt. Generering av bilder basert på stilbeskrivelser og rombeskrivelser.
Brave Search API
Section titled “Brave Search API”Gir nettbasert produktsøk uten avhengighet til Google. Brukes av AI-assistenten til å finne konkrete møbler og produkter basert på stil og funksjon. Ikke i MVP.
Monorepo-struktur
Section titled “Monorepo-struktur”boligassistent/├── apps/│ ├── api/ — Express.js backend│ │ ├── src/│ │ │ ├── routes/ — HTTP route handlers (tynne)│ │ │ ├── services/ — Forretningslogikk│ │ │ ├── db/ — Drizzle-klient og migrasjoner│ │ │ ├── ai/ — Claude og DALL-E integrasjoner│ │ │ └── ingestion/ — Ingestion Engine (PDF-parsing, ekstraksjon, jobbkø)│ │ └── Dockerfile│ └── web/ — React + Vite frontend│ ├── src/│ │ ├── components/│ │ ├── pages/│ │ ├── hooks/│ │ └── lib/│ └── Dockerfile├── packages/│ ├── types/ — Delte TypeScript-typer│ ├── auth/ — Auth.js-konfigurasjon│ └── db/ — Drizzle-skjema og migrations├── docker-compose.yml├── turbo.json└── pnpm-workspace.yamlAI-arkitektur og kontekstbygging
Section titled “AI-arkitektur og kontekstbygging”Problem
Section titled “Problem”En AI-assistent som gir generiske råd er lite nyttig. For å gi kontekstuell rådgivning om én spesifikk bolig trenger Claude tilgang til riktige data om den boligen.
Løsning: Strukturert kontekstbygging + pgvector RAG
Section titled “Løsning: Strukturert kontekstbygging + pgvector RAG”Steg 1 — Strukturert kontekst (alltid med) Når brukeren stiller et spørsmål, bygges en strukturert kontekstblokk:
Eiendom: Hellebakken 9, enebolig, byggeår 1975Valgt rom: Stue (34 m², 1. etasje)Designretning interiør: nordisk, naturlig, varmLayoutIntent stue: salong + arbeidshjørne, behov for mykt lysSteg 2 — Semantisk søk (pgvector) Brukerens spørsmål embeddes og søkes mot:
- Rom-beskrivelser
- Issue-tekster
- Observation-tekster
- Manualinnhold (chunked)
- DesignDirection-tekster
De mest relevante entitetene inkluderes i konteksten.
Steg 3 — Prompt til Claude Kontekstblokk + relevante data + brukerens spørsmål sendes til Claude API. Claude svarer basert på faktiske data om boligen, ikke generiske råd.
Embedding-strategi
Section titled “Embedding-strategi”- Entiteter embeddes ved opprettelse og oppdatering
- Embedding-modell:
text-embedding-3-small(OpenAI) eller tilsvarende - Embeddings lagres i
embedding vector(1536)kolonne på relevante tabeller
Ingestion Engine
Section titled “Ingestion Engine”Formål
Section titled “Formål”Gjør det mulig å onboarde en ny bolig på 5–10 minutter ved å laste opp salgsoppgave og/eller takstrapport. AI ekstraherer strukturerte data, bruker godkjenner, systemet lagrer.
Kritisk prinsipp: AI fyller aldri databasen direkte. Flyten er alltid: AI foreslår → bruker godkjenner → system lagrer.
Pipeline
Section titled “Pipeline”1. Upload — Bruker laster opp PDF → lagres i MinIO → Document opprettet2. Queue — ExtractionJob opprettes med status=queued3. Parse — pdf-parse ekstraherer tekst fra PDF (side-for-side)4. Extract — Claude API med structured output identifiserer fakta: rom, arealer, systemer, avvik, tilstandsgrader5. Structure — Fakta mappes til entitets-felt via field_path6. Store — ExtractedFact-rader lagres med status=pending7. Review — Bruker ser gjennom fakta, godkjenner/endrer/forkaster8. Persist — Godkjente fakta skrives til de ordinære tabelleneTekniske komponenter
Section titled “Tekniske komponenter”| Komponent | Valg | Begrunnelse |
|---|---|---|
| PDF-tekstekstraksjon | pdf-parse (MIT) | Enkel, veldokumentert, ingen ekstern avhengighet |
| Jobbkø | pg-boss (MIT) | Bruker eksisterende PostgreSQL, ingen ny infrastruktur |
| AI-ekstraksjon | Claude API med tool use / structured output | Gir typesikre JSON-responser |
| Gjennomgangs-UI | React + shadcn/ui | Inline redigering, diff-visning, godkjennknapper |
Claude API-bruk i ekstraksjon
Section titled “Claude API-bruk i ekstraksjon”Pipelinen sender dokumenttekst i bolker og ber Claude returnere strukturert JSON:
// Forenklet eksempel på extraction-promptconst systemPrompt = `Du er en ekspert på norske takstrapporter og salgsoppgaver.Ekstraher strukturerte data og returner dem som JSON etter dette skjemaet: [schema]For hvert faktum, oppgi: verdi, sidenummer, tekstutdrag, confidence (0.0-1.0).Sett lav confidence på usikre tolkninger.`Kostnad
Section titled “Kostnad”En takstrapport på 60 sider ≈ 80 000 tokens → ~$0.25 per dokument (Sonnet 4.6). Onboarding med 2–3 dokumenter totalt ≈ $0.50–0.75 (engangsbeløp).
Deployment
Section titled “Deployment”Lokal drift med nettilgang (MVP)
Section titled “Lokal drift med nettilgang (MVP)”docker-compose.yml inneholder: - PostgreSQL 16 + pgvector - MinIO - API (Express.js) - Web (Vite dev / statisk bygg) - Caddy eller Nginx for reverse proxy med HTTPSTilgjengelig fra nett og telefon via lokal IP eller domene med TLS. Anbefalt: sett opp Tailscale eller Cloudflare Tunnel for sikker tilgang utenfra uten å åpne porter i hjemmeruter.
Fremtidig cloud-deployment
Section titled “Fremtidig cloud-deployment”Arkitekturen er container-basert og kan flyttes til:
- Fly.io, Railway, Render (enkelt)
- AWS / GCP / Azure (mer komplekst)
Ingen arkitekturendringer er nødvendig — kun miljøvariabler og CI/CD.