Conversation History Context & Dynamic Variable Memory
Two independent, per-agent features that give a voice agent memory of a contact's earlier calls: Conversation History Context (injects past conversation summaries into the agent's context) and Dynamic Variable Memory (reuses variables such as the caller's name captured on previous outbound calls). Both are OFF by default and fully optional.
Terminology
- contactUID: identifier of a single call record in the
Contactcollection. A number called multiple times produces multiple records.- conversation_summary: a 3–4 sentence summary of a call produced by the Analysis service and stored on the
Contactrecord.- dynamicVariables: per-contact template values (e.g.
{ name: "Daivik" }) used to render greetings/prompts likeHello {{name}}.- E.164: international phone format, e.g.
+919876543210(reference).
Architecture Overview
How It Works
Conversation History Context
- Trigger: at call start, if
history_context_enabledand a phone number is present. - Match:
Contactwhere phone (last-10-digit match) ANDAgentID ∈ {current} ∪ linkedAgentIds, with a non-emptyconversation_summarywhose call time is within the configured window. - Select: dedup by
contactUID(latest per contact), take the most recentmaxConversations(1–10), order oldest → newest. - Inject: append a labeled block to
agent_meta.Context(knowledge base is preserved, never overwritten). Per-call only; nothing is persisted back.
Dynamic Variable Memory
- Trigger: at call start, if
variable_memory_enabledand a phone number is present. - Match: latest
Contactwhere phone matches ANDAgentID ∈ {current} ∪ {variableMemory.linkedAgentId}ANDcallDirection == "outbound"ANDdynamicVariablesis non-empty. - Merge: fill only missing/empty keys in the call's
dynamic_values(explicit live/campaign values always win). Applied before the greeting is rendered.
Why outbound-only? Only outbound calls carry dynamicVariables. Sourcing exclusively from outbound calls means repeated inbound calls always reach back to the originating outbound call rather than a prior variable-less inbound call so no write-back/persistence is needed and the chain never degrades.
Call-type behavior
- Outbound: has its own variables; memory only fills gaps. History applied before the assistant is built.
- Inbound: caller phone is only known inside
InboundCallHandler, so both features are applied there (before the greeting) and the live session agent is rebuilt. - Web: no phone number → neither feature finds anything (no-op).
File Reference
| File | Role | Key items |
|---|---|---|
backend/Agent/src/schemas/agent_meta_schema.py | Runtime settings | history_context_*, variable_memory_* fields |
backend/Agent/src/services/conversation_history.py | Core logic | resolve_history_block, resolve_remembered_variables, maybe_apply_history_context, maybe_apply_variable_memory, _phone_last10, _agent_id_set |
backend/Agent/agent.py | Entry point wiring (outbound/web) | applies both before VoiceAssistant |
backend/Agent/src/handlers/inbound_call_handler.py | Inbound wiring | applies both after caller phone resolved; rebuilds session agent |
backend/Agent/src/handlers/call_handler.py | Handoff wiring | applies both on agent handoff |
backend/client_backend/src/models/Agent.ts | Mongoose schema | historyContext sub-document |
backend/client_backend/src/types/index.ts | Types | IHistoryContext, IVariableMemory |
backend/client_backend/src/utils/dispatch.utils.ts | Validate + flatten | validateHistoryContext, buildHistoryContextConfig |
backend/client_backend/src/controllers/agent.controller.ts | API | validateLinkedAgentOwnership on create/update |
app/src/components/features/HistoryContextSettings.tsx | UI | two independent sections |
app/src/types/campaign.ts, app/src/lib/agentFormUtils.ts | FE types/defaults | HistoryContext, VariableMemory |
Data Model
Stored on the Agent document as historyContext:
historyContext:
enabled: boolean # history feature (default false)
maxConversations: number # 1..10 (default 5)
window:
type: "days"|"weeks"|"months"|"quarters"|"years"|"custom" # default "months"
value: number # default 1
startDate: Date | null # only for type="custom", ≤ 1 year ago
linkedAgentIds: string[] # extra agents whose history is pooled
variableMemory: # independent sub-feature
enabled: boolean # default false
linkedAgentId: string | null # a SINGLE extra agentFlattened into agent_config for the Agent runtime: history_context_enabled, history_context_max, history_context_window_type, history_context_window_value, history_context_window_start, history_context_linked_agent_ids, variable_memory_enabled, variable_memory_linked_agent_id.
Read from Contact: phone / originalPhone, AgentID, callDirection, conversation_summary, dynamicVariables, callStartedAt / createdAt.
API Endpoints
Handled by the existing agent endpoints (no new routes):
- Create:
POST /api/v2/agentsacceptshistoryContext; validates ranges and linked-agent ownership. - Update:
PUT /api/v2/agents/:agentIdsame validation.
Validation rejects: maxConversations outside 1–10; custom startDate in the future or > 1 year ago; non-array linkedAgentIds; non-string variableMemory.linkedAgentId; and any linked agent not owned by the requester.
How to Test (End-to-End)
Prerequisites: an agent, a phone number, and a contact with a completed conversation_summary (let the Analysis service finish after a prior call).
- In the agent editor, enable Use previous conversation history (set max + window; optionally link the outbound agent). Optionally enable Remember caller details and pick a linked agent. Save.
- Place an outbound call to a number, converse, and hang up. Wait for analysis to store
conversation_summaryand confirm the contact hasdynamicVariables(e.g.name). - Call the inbound number from the same phone. Verify in the Agent logs:
Injecting N prior conversation summary(ies) into agent Context.Applied N remembered dynamic variable(s) from prior calls.(if variable memory is on and the greeting uses{{name}}).
- Confirm the greeting renders the remembered name and the agent references prior context. Confirm the knowledge base text in
Contextis unchanged (history is appended under## Previous Conversation History with This Contact).
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Log: No eligible conversation history found | Prior call's summary not generated yet, or outside window | Wait for analysis to finish; widen the window |
Log: No usable phone number for history lookup | Web call or missing caller number | Expected for web; for phone calls verify SIP caller number |
Greeting still shows raw {{name}} | Variable memory off, first-time caller, or no prior outbound contact with variables | Enable variable memory; ensure a prior outbound call captured the variable |
| Inbound doesn't match outbound (different agents) | Agents not linked | Add the outbound agent to the inbound agent's linked list (history and/or variable) |
| Save rejected: "Invalid linked agents" | A linked agent id isn't owned by the customer | Only link your own agents |
Changelog
- Initial: conversation history context (per phone + agent, windowed, top-N, appended to Context).
- Enhancement: linked agents (pool history across agents), and Dynamic Variable Memory (single linked agent, outbound-only source, fills only missing keys).