Asistenți
Un asistent e un pachet versionat de comportament: un prompt, un transcriber, un model, o voce, tools-urile pe care le poate apela și planurile care decid când vorbește, când se oprește și ce înregistrează. E unitatea pe care o rulează un apel.
Fă câte unul per treabă, nu per client. Diferențele de la un apelant la altul stau în variabile, nu într-un al doilea asistent.
Draft, versiune, apel
Când editezi un asistent modifici draft-ul lui. publish îngheață draft-ul într-un rând de versiune imutabil și îndreaptă asistentul spre el. Un apel fixează versiunea cu care a pornit, deci o republicare în timpul apelului nu poate ajunge la o sesiune vie.
GET /assistants/{id} raportează has_unpublished_changes, pe care îl citește badge-ul din dashboard. version e 0 până la prima publicare.
POST /api/v1/assistants/{id}/publish → 201 assistant_version
POST /api/v1/assistants/{id}/unpublish
GET /api/v1/assistants/{id}/versions
GET /api/v1/assistants/{id}/versions/{version}
POST /api/v1/assistants/{id}/versions/{version}/restoreO versiune publicată poartă un config_hash, tool_refs și knowledge_base_refs — așa îți dai seama dacă două apeluri chiar au rulat aceeași configurație.
Creare și editare
API-ul de asistenți e snake_case. Un PATCH schimbă doar câmpurile pe care le numește.
curl -X PATCH "$CALLAGENT_API_URL/assistants/$ASSISTANT_ID" \
-H "Authorization: Bearer $CALLAGENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": { "provider": "openai", "model": "gpt-4o-mini", "temperature": 0.4 },
"silence_timeout_seconds": 20
}'PUT și PATCH ajung la același handler. În documentul OpenAPI e doar PUT; PATCH funcționează și nu e documentat acolo.
Câmpurile de nivel superior
| Câmp | Observații |
|---|---|
name | Obligatoriu, ≤255. |
description | ≤255. |
mode | pipeline (implicit) sau realtime. Vezi avertismentul de la Speech to speech. |
language | ≤16, de ex. ro, en. Plus additional_languages[]. |
first_message | ≤5000. |
first_message_mode | assistant_speaks_first · assistant_speaks_first_with_model_generated_message · assistant_waits_for_user. |
system_prompt | ≤100000. |
template_strictness | lenient · warn (implicit) · strict — ce se întâmplă când un template numește o variabilă pe care nu o dă nimeni. |
silence_timeout_seconds | 1–600, implicit 30. |
max_duration_seconds | de la 10 până la plafonul platformei, implicit 1800. |
transcriber | { provider, model, language, … }. |
model | { provider, model, temperature, … }. |
voice | { provider, voice_id, … }. |
speech_to_speech_model | Se salvează și e ignorat — vezi mai jos. |
end_call_phrases[], idle_messages[], background_messages[], pronunciations[], custom_keywords[] | |
server | { url, … } — serverul de rezervă pentru tools-uri function și pentru hook-uri. server.url trebuie să fie un URL valid. |
hooks[] | 14 puncte de declanșare. |
fallbacks | { model: [], transcriber: [], voice: [] }, maximum două fiecare, indexul 0 primul. |
variables[], variable_values{} | Vezi Variabile. |
knowledge_only, knowledge_only_plan | |
metadata{} | Cheile tale, transmise neatinse. |
tools[], tool_ids[], knowledge_base_ids[], structured_output_ids[] | Atașamente; fiecare id e verificat față de organizația ta. |
Blocurile de plan
Treisprezece blocuri de plan sunt acceptate ca obiecte libere, și fiecare e plat, în snake_case. Un bloc camelCase imbricat, din @callagent/contracts, se salvează curat și nu ajunge niciodată la gateway.
| Bloc | Chei |
|---|---|
start_speaking_plan | wait_seconds, smart_endpointing_provider, completion_threshold, smart_endpointing_ms, on_punctuation_seconds, on_no_punctuation_seconds, on_number_seconds, custom_rules[] |
stop_speaking_plan | enabled, num_words, voice_seconds, backoff_seconds, acknowledgement_phrases[], interruption_phrases[] |
artifact_plan | recording_enabled, recording_format, stereo_recording, video_enabled, transcript_enabled, messages_enabled, events_enabled, logs_enabled, debug_json_enabled, retention_days, pii_redaction, post_call_transcription.{enabled,provider,model,language} |
analysis_plan | summary_enabled, summary_prompt, sentiment_enabled, topics_enabled, success_evaluation_enabled, success_evaluation_prompt, success_evaluation_rubric, model, timeout_seconds |
message_plan | idle_timeout_seconds, idle_max_count, idle_message_reset_count_on_user_speech |
keypad_input_plan | enabled, delimiters[], timeout_seconds, max_digits, mute_assistant_while_typing |
voicemail_detection_plan | enabled, strategy, beep_max_await_seconds, message |
recording_consent_plan | required, mode, prompt, audio_url, on_refusal, regions[] |
observability_plan | provider, endpoint, project, sample_rate, include_prompts, include_transcripts, tags, metadata |
compliance_plan | hipaa, pci, zero_data_retention |
transfer_plan | destinations[] |
knowledge_only_plan | action, message, min_passages, min_score |
background_speech_denoising | enabled, mode, aggressiveness |
Plus background_sound ({ type, volume } sau { type: "custom", url, volume }; tipurile sunt off, office, call-center, cafe, street, nature) și voice_formatting (enabled, min_characters, max_wait_ms, expand_numbers, expand_currency, expand_dates, expand_abbreviations, read_emails_naturally, strip_markdown).
Unele dintre astea fac mai puțin decât par
Câmpuri pe care nu le citește nimic la runtime, verificate prin căutare în sursa gateway-ului:
analysis_planconfigurează un pipeline care nu rulează niciodată. Rezumatul, sentimentul, subiectele și evaluarea de succes nu se produc pe un apel viu.compliance_planajunge în snapshot-ul gateway-ului și nu e citit niciodată.observability_planaplică tag-uri și sampling; nu se trimit span-uri.model.num_fast_turnse convertit pe fir, iar gateway-ul nu îl citește niciodată.fast_modelnici măcar nu e convertit — există doar în contract. Primele tururi nu sunt trimise către un model mai ieftin.model.emotion_recognitione convertit și niciodată citit.voice_formattingdevinechunkPlandin contract, pe care gateway-ul nu îl citește niciodată. Normalizarea textului înainte de sinteză nu e configurabilă azi.pronunciationsdevinepronunciationDictionary, pe care gateway-ul nu îl citește niciodată.artifact_plan.storage_path_templatee ignorat; recorderul își scrie calea hardcodat.
Ce este citit: max_tool_iterations, context_window_messages, background_sound, keypad_input_plan, voicemail_detection_plan, message_plan, end_call_phrases, idle_messages, background_messages, fallbacks, knowledge_base_ids, variable_values și cele două planuri de vorbire.
Vezi Ce nu este construit.
Vorbire la intrare, gândire, vorbire la ieșire
{
"transcriber": {
"provider": "deepgram",
"model": "nova-3",
"language": "ro",
"smart_format": true,
"numerals": true,
"diarization": false,
"profanity_filter": false,
"auto_language_detection": false
},
"model": {
"provider": "openai",
"model": "gpt-4o-mini",
"temperature": 0.4,
"max_tokens": 400,
"tool_choice": "auto",
"max_tool_iterations": 5,
"context_window_messages": 60,
"timeout_seconds": 30
},
"voice": {
"provider": "elevenlabs",
"voice_id": "rachel",
"speed": 1.0
}
}Valori implicite care merită știute: temperature 0.4, max_tokens 400, max_tool_iterations 5, context_window_messages 60, timeout_seconds 30 pe model, speed 1.0 pe voce.
API-ul ciuntește blocurile astea, iar una dintre victime strică orice apel
POST și PATCH /assistants folosesc $request->validated() din Laravel, care întoarce doar cheile care au avut o regulă de validare. În interiorul lui transcriber, model, voice și server, clasa de request declară reguli pentru exact cinci sub-chei:
transcriber.provider · model.provider · model.model · voice.provider · server.url
Orice altă sub-cheie din cele patru blocuri e aruncată în tăcere. Trimisă, acceptată cu 201, și dispărută:
# trimis
"voice": { "provider": "elevenlabs", "voice_id": "rachel", "speed": 1.1 }
"model": { "provider": "openai", "model": "gpt-4o-mini", "temperature": 0.9 }
# salvat
"voice": { "provider": "elevenlabs" }
"model": { "provider": "openai", "model": "gpt-4o-mini" }Deci voice_id, speed, stability, temperature, max_tokens, tool_choice, num_fast_turns, context_window_messages, transcriber.model, transcriber.language, smart_format și restul nu pot fi setate deloc prin API.
Ar fi doar enervant dacă s-ar opri aici. Nu se oprește: voice.voice_id e unul dintre cele cinci câmpuri fără de care configurația de apel a gateway-ului nu poate fi construită. Fără el, convertorul nu întoarce nimic, apelul nu are configurație, iar apelantul aude o linie moartă. validate le verifică pe celelalte patru și pe ăsta nu, deci asistentul se validează și se publică fără reproș.
Un asistent al cărui bloc de voce a fost scris ultima dată prin API-ul REST nu poate porni un apel. CLI-ul trimite către același endpoint și moștenește problema.
Blocurile de plan nu sunt afectate — nu au sub-reguli, deci start_speaking_plan, artifact_plan, metadata, variable_values și background_sound trec întregi. Iar dashboard-ul scrie direct în model, nu prin clasa asta de request, deci un asistent configurat acolo e complet.
Setează vocea din dashboard până se repară asta.
Cuvintele-cheie de transcriber nu sunt transcriber.keywords. Vin din custom_keywords, de la nivelul de sus al asistentului, care acceptă șiruri simple sau obiecte { "term": …, "boost": … }. E cea mai valoroasă setare de transcriber pentru numele de brand și de produs pe care transcriber-ul le-ar auzi altfel greșit.
num_fast_turns și fast_model ar trebui să trimită primele tururi ale unui apel către un model mai mic, pe teoria că „Bună, cu ce te pot ajuta?" nu are nevoie de cel scump. Niciunul nu e citit de gateway. Fiecare tur folosește model.model.
Transcriber-ul trebuie să fie unul pentru care gateway-ul are un adaptor live. validate impune asta la publicare, pentru că un transcriber pe care gateway-ul nu îl poate numi invalidează toată configurația apelului, nu doar câmpul acela, iar apelantul aude o linie moartă.
Încrederea în furnizori
Sunt declarați nouăsprezece furnizori de voce; doi sunt marcați ca revizuiți față de documentația vendorului, și niciunul nu a fost rulat pe un vendor live în CI. Vezi Furnizori și Ce nu este construit.
Fallback-uri
{ "fallbacks": { "voice": [ { "provider": "azure", "voice_id": "ro-RO-AlinaNeural" } ] } }Ordonate, maximum două fiecare, indexul 0 se încearcă primul. Vezi Furnizori.
Speech to speech
speech_to_speech_model acceptă un furnizor și un model, le salvează, le versionează și le publică.
E ignorat la runtime
Sesiunea live de apel nu citește niciodată câmpul. Un asistent configurat pentru speech-to-speech rulează pipeline-ul obișnuit transcriere → model → sinteză. Setarea mode: "realtime" nu schimbă asta; schimbă doar ce câmpuri cere validate.
Variabile
Două jumătăți. variables[] le declară; variable_values{} dă valorile implicite.
{
"variables": [
{ "name": "company_name", "source": "static", "default_value": "Example SRL", "required": true },
{ "name": "plan_tier", "source": "call" }
],
"variable_values": { "company_name": "Example SRL" },
"system_prompt": "Ești agent de suport pentru {{company_name}}. Apelantul e pe planul {{plan_tier}}."
}name trebuie să se potrivească cu ^[A-Za-z_][A-Za-z0-9_]*$. source e una dintre static, call, contact, webhook, tool, handoff, environment.
Sintaxa și ce e disponibil
Sintaxa e {{cale.cu.puncte}}. Nu e Liquid; nu există filtre, nu există condiționale, nu există bucle. O cale nerezolvată se randează ca șir gol.
Substituția rulează pe system prompt, pe primul mesaj, pe mesajele de fundal, pe mesajele de inactivitate, pe anunțul de consimțământ la înregistrare și pe mesajul de căsuță vocală.
Variabilele interne disponibile la runtime:
| Cale | Valoare |
|---|---|
{{call.id}} | ULID-ul apelului. |
{{call.from}} | Numărul apelantului. |
{{customer.name}}, {{customer.number}}, {{customer.email}}, {{customer.language}}, {{customer.id}}, {{customer.external_id}}, {{customer.attributes.*}} | Contextul clientului, când a fost dat unul. |
Precedență: variable_values de pe asistent, apoi variables trimise per apel către POST /calls, care câștigă.
Nu există {{now}} sau {{date}} la runtime. Resolverul de preview din dashboard expune un namespace env cu now, date, time, timezone, region și environment, și e o listă curată de valori permise, nu mediul procesului — dar resolverul acela randează preview-uri, nu apeluri vii.
POST /assistants/{id}/render-prompt randează prompt-ul pe un context pe care îl dai tu și întoarce missing, ceea ce e cel mai rapid mod de a găsi o greșeală de tipar într-o cale.
Hooks
hooks[] ar trebui să declanșeze o acțiune într-un punct numit. Paisprezece triggere:
call.start · call.end · call.ending · speech.update · transcript.final · tool.calls · tool.completed · customer.speech.timeout · customer.speech.interrupted · assistant.speech.interrupted · phone.call.control · silence.timeout · handoff · transfer.failed
Fiecare hook are filters opționale (oneOf, regex, equals) și cel puțin o acțiune: say, tool, transfer, handoff, endCall, setVariable, webhook.
Se declanșează doar hook-urile handoff
Hook-urile sunt convertite pe fir și trimise gateway-ului. Sursa gateway-ului nu conține nicio referință la ele. Control plane-ul rulează hook-uri pentru exact un eveniment — handoff, prin runnerul lui. Celelalte treisprezece triggere sunt salvate, publicate, transmise și ignorate.
Folosește un webhook sau un tool pentru orice plănuiai să faci cu restul.
Un hook cu o acțiune nerecunoscută e aruncat la conversie, nu respins. Supraviețuiesc doar say, transfer, tool și end_call; handoff, setVariable și webhook nu ajung deloc pe fir.
Inspectare
GET /api/v1/assistants/{id}/validate verificări de publicare, fără publicare
GET /api/v1/assistants/{id}/preview snapshot-ul rezolvat
POST /api/v1/assistants/{id}/render-prompt randează template-urile pe un contextvalidate verifică structura și disponibilitatea adaptoarelor. Nu verifică dacă există sau funcționează o credențială, și nici dacă tools-urile atașate se rezolvă. Vezi Pornire rapidă, pasul 4 pentru lista exactă.
preview întoarce snapshot-ul rezolvat în forma snake_case a API-ului. Nu e formatul de pe fir al gateway-ului — acela e o a doua conversie, care în plus aruncă tools-urile pe care nu le poate mapa.
Ca fișier
name: Support Line
firstMessageMode: assistant-speaks-first
firstMessage: Bună, ai sunat la suport. Cu ce te pot ajuta?
systemPrompt: |
Ești agent de suport pentru {{company_name}}.
Fii scurt. Pune o singură întrebare pe rând.
transcriber: { provider: deepgram, model: nova-3, language: ro }
model: { provider: openai, model: gpt-4o-mini, temperature: 0.4 }
voice: { provider: elevenlabs, voiceId: rachel }
tools:
- lookupOrder
knowledgeBases:
- Manual de produsCLI-ul vorbește vocabularul contractelor — camelCase, valori de enum cu cratimă — și convertește singur în ortografia API-ului. tools și knowledgeBases numesc resurse, iar CLI-ul le rezolvă. Vezi Config as code.
API
GET/POST /api/v1/assistants
GET/PATCH/DELETE /api/v1/assistants/{id}
POST /api/v1/assistants/{id}/duplicate
POST /api/v1/assistants/{id}/publish
POST /api/v1/assistants/{id}/unpublish
GET /api/v1/assistants/{id}/versions
GET /api/v1/assistants/{id}/versions/{version}
POST /api/v1/assistants/{id}/versions/{version}/restore
GET /api/v1/assistants/{id}/preview
POST /api/v1/assistants/{id}/render-prompt
GET /api/v1/assistants/{id}/validateParametri, body-uri și răspunsuri complete: referința asistenților.