Caller Context Injection
When a voice agent answers an inbound call, Loquent injects caller context — the contact’s name and curated memory notes — into the AI session before the greeting. This lets the agent personalize the conversation without the caller knowing context is loaded.
How It Works
Section titled “How It Works”- Twilio sends a WebSocket
startevent with caller metadata - Loquent identifies the contact via phone number lookup
- If
use_contact_contextis enabled on the agent, Loquent fetches:- Contact name via
get_contact_name(contact_id, organization_id) - Memory notes via
get_contact_notes_for_agent(contact_id)— filtered toarea = "memory"only
- Contact name via
- A
## Caller Contextblock is injected as a conversation item before the AI greeting
If neither name nor notes exist, the context block is skipped entirely.
Context Block Format
Section titled “Context Block Format”The injected context follows this structure:
## Caller Context
Contact name: Sarah Johnson
(2026-03-15T10:30:00Z): Prefers morning appointments(2026-03-28T16:45:00Z): Interested in follow-up pricing for premium plan
Use this context to personalize the conversation naturally.If a contact name is provided, greet the caller by name and use itthroughout the conversation. Use memory notes to inform your responses— reference relevant facts when they come up naturally, but never say"according to my notes" or reveal that you have a file on them.Behave as if you simply know the caller well.The block adapts to what’s available:
| Contact has | Injected content |
|---|---|
| Name + memory notes | Both sections |
| Name only | Name section only |
| Memory notes only | Notes section only |
| Neither | Block skipped — no injection |
Note Filtering
Section titled “Note Filtering”Only notes with area = "memory" are injected. Other note types are excluded:
| Area | Included | Reason |
|---|---|---|
memory | ✅ | Curated facts about the contact |
call_annotation | ❌ | Transient call metadata |
sms_annotation | ❌ | Transient message metadata |
ai_enrichment | ❌ | System-generated enrichment |
alert | ❌ | Internal alerts |
general | ❌ | Free-form notes not meant for agent |
This filtering reduces token consumption significantly. A contact with 30+ annotations sends only the handful of memory notes that matter.
Key Services
Section titled “Key Services”get_contact_name
Section titled “get_contact_name”File: src/mods/contact/services/get_contact_name_service.rs
pub async fn get_contact_name( contact_id: uuid::Uuid, organization_id: uuid::Uuid,) -> Result<Option<String>, AppError>Returns the contact’s full name (first_name + last_name, trimmed) or None. Uses select_only() to fetch just the two name columns. Filters by organization_id for org-scoped security.
get_contact_notes_for_agent
Section titled “get_contact_notes_for_agent”File: src/mods/contact/services/notes/get_contact_notes_service.rs
pub async fn get_contact_notes_for_agent( contact_id: uuid::Uuid,) -> Result<Vec<AgentContactNote>, AppError>Returns memory notes ordered by created_at ascending. The ContactNoteArea::Memory filter is applied at the database query level.
Error Handling
Section titled “Error Handling”Both service calls use unwrap_or_else with structured logging:
- If
get_contact_namefails → logs withcontact_id, treats asNone, continues with notes - If
get_contact_notes_for_agentfails → logs withcontact_id, treats as emptyvec![], continues with name - Neither failure blocks the call — the agent proceeds with whatever context is available
Configuration
Section titled “Configuration”The use_contact_context flag is a per-agent setting stored in the database. When disabled, the entire context injection step is skipped. No environment variables control this behavior.