Lead-Call Agentic Analysis (Step 1)
This step makes lead web and phone-to-phone calls select a mandatory analysis schema, and enriches every real (non-test) call's analysis with internal agentic blocks used for automation, dashboards, and insights.
What a real call's analysis now stores on Contact
| Field | Source | Shown to user? |
|---|---|---|
analysis | user-defined schema features | Yes |
conversation_summary | combined LLM call | Yes |
quality_evaluation | combined LLM call | Yes (quality) |
agenticSignals | combined LLM call | No (internal) |
industrySignals | combined LLM call, from schema's industry | No (internal) |
conversationFacts | combined LLM call, dynamic key/value | No (internal) |
embedding_narrative | combined LLM call | No (internal) |
embedding | text-embedding-3-small on the narrative | No (internal) |
All produced in one combined LLM call (no extra call) + one cheap embedding call.
agenticSignals (industry-independent, drives the future cron)
call_outcome, answered_but_no_audio, reachability, next_action, callback_requested, callback_time_raw, callback_at (ISO-8601 with +05:30 IST offset), callback_window, preferred_channel, follow_up_priority, user_sentiment, engagement_score, interest_level, contact_restriction, complaint_flag.
industrySignals (per AnalysisSchema.industry)
Chartable dimensions per industry: real_estate, healthcare, education, hospitality, travel, legal, generic. Defined in Analysis/src/pipeline/internal_analysis.py.
conversationFacts (fully dynamic)
Array of { key, value, type, confidence }. The model chooses keys and values, capturing everything actually discussed only-discussed, nothing invented.
Test calls
Test calls extract only analysis (user features). No quality/summary/ agentic/industry/facts/embedding saves tokens. Authority: the request test_call flag and Contact.isTestCall (whichever is true).
UI
On the lead Call tab, Web and Phone-to-phone now require an analysis schema (dropdown, mandatory). The last selection is remembered in localStorage (oktivo:lastLeadCallSchema). Industry is taken from the selected schema no separate industry picker. AI-agent mode keeps deriving the schema from the agent.
Backfill: re-analyze existing contacts
Re-runs analysis for contacts that already completed analysis under the old pipeline, to populate the new blocks. Non-destructive (re-trigger only). Test calls are always skipped.
HTTP (curl)
Dry run (default reports what WOULD be re-analyzed, makes no calls):
curl -X POST "$BASE_URL/internal/scripts/reanalyze-contacts" \
-H "Content-Type: application/json" \
-H "x-api-secret: $SUPERADMIN_API_SECRET" \
-d '{ "dryRun": true }'Real run (actually re-triggers analysis):
curl -X POST "$BASE_URL/internal/scripts/reanalyze-contacts" \
-H "Content-Type: application/json" \
-H "x-api-secret: $SUPERADMIN_API_SECRET" \
-d '{ "dryRun": false }'Scoped / capped examples:
# Only a specific analysis schema, first 100 contacts
curl -X POST "$BASE_URL/internal/scripts/reanalyze-contacts" \
-H "Content-Type: application/json" \
-H "x-api-secret: $SUPERADMIN_API_SECRET" \
-d '{ "dryRun": false, "analysisSchemaId": "anl_xxx", "limit": 100 }'
# One customer, higher concurrency, include even already-backfilled contacts
curl -X POST "$BASE_URL/internal/scripts/reanalyze-contacts" \
-H "Content-Type: application/json" \
-H "x-api-secret: $SUPERADMIN_API_SECRET" \
-d '{ "dryRun": false, "customerUID": "cust_xxx", "concurrency": 8, "onlyMissingBlocks": false }'Body options
| Field | Default | Meaning |
|---|---|---|
dryRun | true | true = report only, no calls. false = re-trigger. |
onlyMissingBlocks | true | Only contacts without agenticSignals (avoid re-paying). |
analysisSchemaId | Scope to one schema's contacts. | |
customerUID | Scope to one customer. | |
campaignUID | Scope to one campaign. | |
limit | all | Cap number of contacts. |
concurrency | 5 | Parallel re-trigger requests (1–20). |
Standalone CLI (from backend/client_backend)
npx tsx src/scripts/reanalyze-contacts.ts --dry=true
npx tsx src/scripts/reanalyze-contacts.ts --dry=false --limit=100
npx tsx src/scripts/reanalyze-contacts.ts --dry=false --analysisSchemaId=anl_xxx
npx tsx src/scripts/reanalyze-contacts.ts --dry=false --customerUID=cust_xxx --concurrency=8Response shape
{
"success": true,
"dryRun": true,
"matched": 1234,
"skippedTestCalls": 20,
"skippedNoData": 5,
"triggered": 0,
"failed": 0,
"byCallType": { "ai_agent": 1000, "human": 150, "phone_to_phone": 84 },
"sampleContactUIDs": ["api_...", "crm_..."],
"errors": []
}