Skip to content

Analytics și costuri ​

Două întrebări, un singur set de date: cum merg apelurile și cât au costat. Ambele sunt răspunse din aceleași fapte per apel, deci o cifră de cost poate fi mereu urmărită înapoi până la secunda de apel care a produs-o.

Interogare ​

POST /api/v1/analytics/query        metrici, dimensiuni, filtre, granularitate
GET  /api/v1/analytics/overview     cifrele de titlu ale dashboard-ului
GET  /api/v1/analytics/vocabulary   ce ai voie să ceri

Începe cu vocabulary. Întoarce metricile, dimensiunile și filtrele pe care depozitul le suportă efectiv, o listă mai scurtă decât ai crede, și îți economisește o rundă de încercări.

bash
curl -X POST "$CALLAGENT_API_URL/analytics/query" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "metrics": ["call_count", "avg_duration_seconds"],
    "dimensions": ["assistant_id"],
    "granularity": "day",
    "from": "2026-01-01",
    "to": "2026-01-31"
  }'
ts
const report = await fetch(`${process.env.CALLAGENT_API_URL}/analytics/query`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    metrics: ['call_count', 'avg_duration_seconds'],
    dimensions: ['assistant_id'],
    granularity: 'day',
  }),
}).then((response) => response.json());
python
import os, requests

report = requests.post(
    f"{os.environ['CALLAGENT_API_URL']}/analytics/query",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    json={
        "metrics": ["call_count", "avg_duration_seconds"],
        "dimensions": ["assistant_id"],
        "granularity": "day",
    },
    timeout=30,
).json()

Formele exacte de request și de răspuns sunt în referința analytics.

Două căi, un singur răspuns ​

Depozitul citește din rollup-uri materializate unde poate și din faptele brute de apel unde trebuie — percentilele, histogramele, funnels-urile și filtrele pe mai multe dimensiuni nu pot fi calculate dintr-un rollup. Percentilele sunt percentile percentile_cont reale, nu o medie îmbrăcată în percentilă.

Rollup-urile se construiesc pe schedule: din oră în oră, plus o trecere zilnică la 02:30. O interogare care acoperă ultimele minute cade deci pe calea brută, care e mai lentă și corectă, în loc să întoarcă un rollup învechit.

Dashboard-ul, board-urile și API-ul REST citesc toate prin același depozit, deci un număr de pe un wallboard și același număr din API nu pot fi în dezacord.

Contabilitatea costurilor ​

Banii sunt contorizați per componentă, nu per apel.

GET /api/v1/calls/{call}/costs      un apel, defalcat
GET /api/v1/billing/usage           evenimente de consum
GET /api/v1/billing/usage/summary   agregate

Fiecare rând reține componenta, furnizorul, modelul, cantitatea în unitatea proprie a furnizorului, costul unitar și atât costul furnizorului, cât și suma facturată clientului.

Două vocabulare de componente, iar API-ul îl folosește pe cel din PHP

call_costs.component — ce întoarce GET /calls/{id}/costs — ia aceste zece valori:

stt · llm · tts · s2s · telephony · recording · storage · tool · mcp · platform

CostComponentSchema din @callagent/contracts e un set diferit: transcriber, model, voice, speech_to_speech, telephony, storage, tool, mcp, knowledge, platform. Observă llm față de model, stt față de transcriber, recording prezent doar în PHP și knowledge doar în TypeScript. Citește cele zece din API.

Blocul summary din răspunsul de costuri le grupează și mai departe, în stt, llm, tts, telephony și other.

Sunt recunoscute douăsprezece unități: input_tokens, cached_input_tokens, output_tokens, reasoning_tokens, characters, audio_seconds, audio_minutes, call_minutes, requests, messages, gigabytes, gigabyte_months.

Sumele sunt decimal(20,8). Un token de LLM costă o fracțiune de milionime de dolar, iar rotunjirea devreme produce un ledger care nu poate fi reconciliat.

Ce înseamnă „priced" ​

E partea care merită înțeleasă înainte să construiești un raport de marjă pe ea.

O etapă e tarifată doar dintr-un rând de registry marcat pricing_verified. Unde nu există un preț verificat, rândul de cost se scrie oricum — deci cantitatea nu se pierde — dar cu cost = 0 și metadata.priced = false. Fiecare cantitate își reține și quantity_source, deci poți deosebi un număr de tokeni măsurat de unul estimat.

Defalcarea costului apelului scoate la suprafață acoperirea parțială, în loc să prezinte un sub-total ca total.

Valuta se convertește doar dacă e configurat un curs; altfel cifrele rămân în USD, în loc să fie convertite la un curs pe care nu l-a ales nimeni.

Calculul costurilor e idempotent — o cheie deterministă per componentă per apel — deci o finalizare reîncercată nu poate factura de două ori.

Margin floor ​

O marjă minimă de platformă pe minut facturabil. Formula e țintă = prag pe minut × minute facturabile, câștigat = Σ(taxare − cost furnizor), iar completarea e max(0, țintă − câștigat), postată ca un singur eveniment de consum platform_margin și idempotentă per apel.

E un rând de preț pe produsul de catalog platform.margin_floor, cu dată de intrare în vigoare și suprascriibil per client — nu o constantă dintr-un fișier de configurare. Valoarea implicită din seed e 0,10 EUR și 0,10 USD pe minut, ca prețuri de listă independente, nu unul convertit la un curs.

Dacă timpul de vorbire al operatorului contează sau nu e configurabil: valoarea implicită ai_only îl exclude din baza de secunde facturabile și nu lasă marja liniei de operator să acopere pragul; whole_call le include pe amândouă.

Costul furnizorului față de preț ​

Costul furnizorului e ce a plătit CallAgent; suma facturată e ce plătești tu; marja se reține alături. Cu chei proprii, furnizorul te facturează direct, iar CallAgent taxează doar taxa de platformă — ambele cifre se rețin, pentru că „de ce e factura mea numărul ăsta" are nevoie de amândouă.

Analytics de latență ​

GET /calls/{call}/metrics întoarce cele 25 de nume de metrici, per tur. Agregate, ele îți spun dacă o schimbare de furnizor a ajutat. Cele cinci cu ținte de platformă sunt listate pe Cum funcționează un apel.

Monitoare și incidente ​

GET  /api/v1/monitors
POST /api/v1/monitors
POST /api/v1/monitors/{monitor}/evaluate
GET  /api/v1/issues
POST /api/v1/issues/{issue}/acknowledge
POST /api/v1/issues/{issue}/resolve

Un monitor urmărește o metrică față de un comparator și un prag, pe o fereastră, cu o dimensiune minimă de eșantion, un cooldown și o perioadă de auto-rezolvare. Depășirea deschide un incident cu o severitate și un contor de apariții; incidentele se confirmă și se rezolvă, nu doar se notifică, deci o problemă recurentă e un singur rând, nu patru sute de alerte. Monitoarele sunt evaluate din cinci în cinci minute.

Scorecards, eval-uri și simulări ​

GET  /api/v1/scorecards        POST /api/v1/scorecards
GET  /api/v1/eval-suites       POST /api/v1/eval-suites/{evalSuite}/run
GET  /api/v1/simulation-suites POST /api/v1/simulation-suites/{simulationSuite}/run

Un scorecard e un set de metrici ponderate cu un prag de trecere, delimitat la asistenți sau la campanii. O suită de eval reia cazuri fixe și verifică rezultatul. O suită de simulare rulează persoane împotriva asistentului, cu tools mock și criterii de succes.

Unde rulează azi scorecards

Motorul de scorecards e real și e folosit când rulează o simulare. Scorarea unui apel viu depinde de pipeline-ul de analiză de după apel de mai jos, care nu e dispecerizat în momentul de față.

Analiza de după apel ​

analysis_plan de pe un asistent configurează un rezumat, o citire de sentiment, extragerea de subiecte, o evaluare de succes cu o grilă și orice structured outputs de extras.

Nu e produsă în momentul de față de control plane

Planul e salvat, versionat și livrat gateway-ului, iar coloanele și evenimentul analysis.completed care ar purta rezultatele există toate. Dar pipeline-ul de analiză al control plane-ului nu e dispecerizat nicăieri după terminarea unui apel — finalizatorul rulează redactarea, artefactele, transcrierea de după apel și retenția, și se oprește.

În practică, calls.summary e populat doar dacă gateway-ul îl furnizează în payload-ul de finalizare. Nu construi un raport care presupune că rezumatul, sentimentul, evaluarea de succes sau subiectele sunt prezente. Urmărit pe Stadiul implementării.

Transcriere de după apel ​

artifact_plan.post_call_transcription retranscrie înregistrarea după apel, ceea ce îți cumpără o acuratețe pe care un transcriber de streaming optimizat pentru latență nu ți-o poate da.

Un singur furnizor

Formularul de asistent oferă un furnizor din registry, dar Sonix e singurul adaptor implementat. Orice altă alegere nu are niciun client în spate, iar interfața o spune, în loc să eșueze la runtime.

Export de observabilitate ​

observability_plan configurează un furnizor (langfuse, opentelemetry, custom_http), un endpoint, tag-uri, metadate și un sample_rate, plus include_prompts și include_transcripts.

Span-urile nu sunt trimise

Sampling-ul și tag-urile își fac efectul, iar în cod există exportere pentru Langfuse și OTLP — dar nu le invocă nimic. Gateway-ul o spune explicit în propriile log-uri: „observability provider is configured but this gateway exports no traces to it; tags and sampling are applied, spans are not shipped."

Nu-ți planifica încă o integrare cu Langfuse sau OpenTelemetry în jurul ăsteia. Folosește în schimb fluxul de evenimente, GET /calls/{call}/metrics și GET /calls/{call}/debug — alea sunt reale.

Mai departe ​

Scrisă după cod. Ce nu este încă implementat este marcat ca atare, niciodată descris ca funcțional.