Skip to content

agent_api: de API voor de Freeze AI-agent

De AI-agent op de Freeze AI-VM (offerte-assistent, later de chat) leest via een eigen, alleen-lezen API producten, pagina's, prijzen en offerteaanvragen. Hij komt nooit via de gewone shop-API en kan niets wijzigen.

Ontwerp: repo Voorman/watchtower, docs/plans/2026-11-02-freeze-ai-ontwerp.md §4.1. Todo: .planning/todos/pending/agent-api.md. Code: backend/apps/agent_api/.

Endpoints (/api/agent/v1/)

Scope Endpoint Inhoud
catalog:read GET products/, GET products/{id}/ actieve, geliste producten (zoals de shop, zonder inkoopprijs)
catalog:read GET categories/ de categorieboom met intro en FAQ
catalog:read GET decoration-methods/ actieve bedrukkingsmethoden, zonder tarieven
content:read GET pages/, GET pages/{slug}/ actieve CMS-pagina's
pricing:read POST pricing/quote-estimate/ prijsindicatie via de prijsmotor (simulate_price); met product_id telt de basisprijs van dat product
quotes:read GET quotes/{id}/ één open offerteaanvraag (status requested) met de vraag van de klant, zonder e-mail, telefoon, adres, bedrijfsnaam, interne notities of de namen en nummers voor op de kleding (alleen has_personalization)

Beveiliging (drie lagen)

  1. nginx: location /api/agent/ laat alleen 100.110.246.113 toe (de VM in het tailnet); de rest krijgt 403. Op staging zonder basic auth, want de agent stuurt die niet.
  2. Django: AGENT_API_ALLOWED_IPS (komma-gescheiden IP's of netwerken; leeg = niemand), via get_client_ip.
  3. Sleutel: header Authorization: Api-Key fai_<prefix>.<geheim>, met scopes. Alleen de SHA-256 staat in de database. Een sessie-login telt niet.

Daarnaast een throttle per sleutel (AGENT_API_THROTTLE_RATE, standaard 600/uur) en een auditlog per call (Django-admin → Agent-API-calls).

Een sleutel maken

Per omgeving en per toepassing een eigen sleutel. De sleutel verschijnt één keer, zonder andere tekst, dus stuur hem direct door naar de VM of 1Password:

python manage.py create_agent_key staging-offertes --scope catalog:read --scope pricing:read --scope quotes:read

Intrekken: Django-admin → Agent-API-sleutels → "Is active" uit. Toevoegen via de admin kan niet, zodat een sleutel nooit in de admin of in een log staat.

Een nieuw VM-adres

Verandert het tailnet-IP van de VM (tailscale ip -4 op de VM), pas dan zowel de allow in nginx/nginx.conf en nginx/nginx.staging.conf aan als AGENT_API_ALLOWED_IPS in de env-bestanden van productie en staging.

Offerte-assistent (concepten)

Bij elke nieuwe offerteaanvraag vraagt de shop de agent om een beoordeling (apps/agent_api/review.py: na de commit een Celery-taak agent.request_quote_review, tot 3 keer opnieuw). De agent leest de aanvraag via deze API en slaat één concept op via POST suggestions/ (scope suggestions:write; payload strikt gecontroleerd, alleen voor open aanvragen). Het eerste concept wint: elke volgende post voor dezelfde offerte krijgt 409, zodat een retry of een run met een prompt-injectie een concept nooit vervangt.

  • Aan de kant van de agent: het offertenummer staat vast in de deps van de run (geen vrije ID's); de agent zet target_id daaruit, nooit uit de uitvoer van het model. Het endpoint /v1/quotes/{id}/review antwoordt direct 202 en werkt op de achtergrond.
  • Retries: bij een 5xx of verbindingsfout probeert Celery het tot 3 keer opnieuw; een 4xx (bv. een verkeerde token) wordt gelogd en niet herhaald. Een storing bij het inplannen (broker weg) wordt gelogd en raakt de klant nooit.

  • Admin: op de offertepagina staat rechts "Concept van de assistent", als platte tekst, met de knoppen Bruikbaar / Niet bruikbaar (voor de evaluatie; er gebeurt verder niets). Ook in de Django-admin onder Agent-concepten.

  • Melding: ntfy-topic freeze-approve, met alleen het offertenummer en een klik naar de admin. Geen naam, e-mail of bedrag. De ntfy-gebruiker webshop mag al schrijven op freeze-*; abonneer je in de ntfy-app op freeze-approve.
  • Instellingen: FREEZE_AI_AGENT_URL (http://100.110.246.113:8000), FREEZE_AI_AGENT_TOKEN (1Password "ntfy Freeze AI — tokens", veld agent-bearer) en FREEZE_AI_ENV (staging of prod). Zonder URL of token gebeurt er niets.
  • De sleutel van de agent heeft voor deze flow quotes:read, catalog:read, pricing:read en suggestions:write nodig.