Skip to content

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 ​

FieldSourceShown to user?
analysisuser-defined schema featuresYes
conversation_summarycombined LLM callYes
quality_evaluationcombined LLM callYes (quality)
agenticSignalscombined LLM callNo (internal)
industrySignalscombined LLM call, from schema's industryNo (internal)
conversationFactscombined LLM call, dynamic key/valueNo (internal)
embedding_narrativecombined LLM callNo (internal)
embeddingtext-embedding-3-small on the narrativeNo (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):

bash
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):

bash
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:

bash
# 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 ​

FieldDefaultMeaning
dryRuntruetrue = report only, no calls. false = re-trigger.
onlyMissingBlockstrueOnly contacts without agenticSignals (avoid re-paying).
analysisSchemaIdScope to one schema's contacts.
customerUIDScope to one customer.
campaignUIDScope to one campaign.
limitallCap number of contacts.
concurrency5Parallel re-trigger requests (1–20).

Standalone CLI (from backend/client_backend) ​

bash
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=8

Response shape ​

json
{
  "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": []
}

Last updated: