Qontak | AI Agent | AI Agent Memory — Phase 1: Read-side CEBE Memory
Phase 1 of the AI Agent Memory initiative. Phase Goal (verbatim from the ANCHOR Phase Index): The autonomous AI Agent reads the customer's cross-module CEBE profile and history (keyed
qontak_customer_id) at conversation start and uses it in responses — org-flag-gated, with graceful degradation whenever CEBE data, identity resolution, or the latency budget is unavailable. Deferred to future (non-committed) work: agent-authored write-back and tenant memory controls.
HEADER BLOCK
| Field | Value |
|---|---|
| PM | Dimas Fauzi Hidayat (Product Manager, Mekari Qontak) |
| PRD Version | 1.0 |
| Status | DRAFT |
| PRD Type | NEW |
| Epic | TBD — created at grooming |
| Squad | BOT — Chatbot Squad |
| RFC Link | N/A — pending; gated on the DSAI contract + the CEBE joint Eng+Data RFC (see Dependencies) |
| Figma Master | N/A — no UI surface in this phase (flag-only rollout) |
| Anchor | Yes — AI Agent Memory — ANCHOR |
| Labels | epic:qontak-chat | module:ai-agent | feature:ai-agent-memory |
| Last Updated | 2026-07-22 |
Status values:
DRAFT→READY→BUILD→SHIPPED
Table of Contents
- HEADER BLOCK
- 2. One-liner + Problem
- 3. Target Users + Persona Context
- 4. Non-Goals
- 5. Constraints
- 6. API & Webhook Behavior
- 7. System Flow + User Stories
- 8. Rollout
- 9. Observability
- 10. Success Metrics
- 11. Launch Plan & Stage Gates
- 12. Dependencies
- 13. Key Decisions + Alternatives Rejected
- 14. Open Questions
- PRD CHANGELOG
2. One-liner + Problem
One-liner: The AI service reads the customer's cross-module CEBE profile before the agent's first response and answers with context — org-flag-gated, never blocking the reply.
Problem:
The autonomous AI Agent answers every conversation context-blind. The chatbot BE sends the AI
service only the current message plus identifiers (send_message_with_resolve.rb —
question: last_history&.input_content; the customer reference is
qontak_chat_customer_id = contact.channel_contact_id, a per-channel id, not the unified
qontak_customer_id). Conversation state lives in the AI-service thread keyed by
room_id/conversation_id and dies with the conversation — there is no contact-level memory
anywhere. Meanwhile the business's own systems already know this customer: open and past tickets,
orders and LTV, campaign engagement and opt-outs, loyalty tier, prior conversations. CEBE
(Customer Event-Based Engine, TF-3302) is
consolidating exactly this into a Central DB with a Retriever/Metrics layer — and its ANCHOR
names this phase as the consumer: "Agentic AI reads CEBE memory — Eng (Chatbot) — Q4 2026."
Without this phase, returning customers are greeted as strangers and re-asked known facts,
containment and CSAT stay capped, and CEBE's flagship consumer metric (≥50% of conversations by
end of Q4 2026) has no owner.
3. Target Users + Persona Context
| Persona | Role | Goal | Pain | Workaround |
|---|---|---|---|---|
| Primary — Business Admin / CS Lead | Owns the org's production AI Agent and its containment/CSAT outcomes | The agent recognizes returning customers and answers with account context, lifting containment without adding human agents | Agent re-asks known facts and treats every conversation as first contact; frustrated repeat customers escalate to humans | Longer static instructions/KB entries; routing known customers straight to humans |
| Secondary — AI Agent (system actor) | The autonomous agent acting in live conversations | Read the customer's cross-module profile/history before the first response | Receives only the current message + thread; cannot see tickets, orders, campaigns, loyalty | None — the data sits in silos the agent cannot reach |
| Tertiary — End Customer | The business's customer on WhatsApp/webchat | Continuous service without repeating their history each session | Repeats context every conversation; generic answers despite a long relationship | Asks for a human agent |
(See Constraints for plan availability and feature-flag scope.)
4. Non-Goals
- No agent-authored memory write-back — the agent reads CEBE; it never writes events, attributes, or learned facts back. Write-back requires net-new AI event types in the standardized CEBE schema and is a future, non-committed phase (see ANCHOR Phase Index).
- No tenant memory controls — no admin UI to view/edit/delete a customer's memory, no retention settings, no per-customer opt-out surface in this phase.
- No reuse of
omnichannel_room_summariesas a memory source — per-conversation summaries are not read into the agent in this phase (evaluated and rejected as v1, see ANCHOR §7b). - No human-agent-facing memory panel — surfacing the CEBE profile to human agents in the inbox is the Report/Command Center consumer's scope, not this initiative's.
- No Ask Airene / copilot consumption — only the autonomous customer-facing agent reads memory in this phase.
- No new FE surface — no config toggle, no UI change; rollout is controlled by the org-level feature flag operated internally. A V2 agent-config toggle is future work gated on design.
- No CEBE-side build — the Central DB, Retriever/Metrics layer, event ingestion, and PII masking are CEBE (CDP squad) deliverables; this phase consumes them and contributes requirements only.
- No per-turn re-query — memory is read once at conversation start (first agent response) and cached in the thread; continuous mid-conversation refresh is out of scope.
5. Constraints
| Field | Value |
|---|---|
| Platform | Backend + AI-service only (chatbot Rails BE + DSAI). No new screen; no FE change. |
| Performance | The CEBE read must never block or break the reply: p95 added latency to the agent's first response ≤ 2s, enforced by a hard timeout of 2,000 ms on the retriever query — on timeout the agent answers without memory (auto-skip). Subsequent turns add 0 ms (context cached in the thread; Non-Goal #8). |
| Feature flag | ai_agent_memory | default: OFF. Org-level, BE system_preferences rollout-group pattern. Flag state is resolved by the chatbot BE per request and passed to the AI service — DSAI must not read memory for orgs where the flag is off. |
| Plan scope | Production autonomous-agent tiers only — Plus / Ultimate / 360 (the tiers already entitled for the AI Agent). Not Starter/Free. |
| Identity | Memory is keyed on qontak_customer_id only. If the room's contact does not resolve to a qontak_customer_id, memory is skipped for that conversation (no fuzzy matching, no phone/email lookup fallback in this phase). |
| Tenancy & access | The retriever read is org-scoped: DSAI authenticates service-to-service with a credential bound to the requesting company_id; a query can never return another org's data. Data minimization: DSAI receives only the field set allow-listed in the CEBE PII-masking policy for the AI consumer role (see Dependencies) — never raw payment data, government IDs, or masked-by-policy fields. |
| Data limits | The injected profile is bounded: the retriever response consumed per conversation is capped by the DSAI contract (target ≤ 8 KB serialized; history entries truncated oldest-first beyond the cap). Telemetry rows carry no content and are fixed-width (S5.1). |
| Testing mode | is_testing_mode conversations behave identically to production (memory read when flag on) so admins can validate memory behavior before go-live; testing-mode reads are tagged in telemetry and excluded from the ⭐ adoption metric. |
| Read/write | Read-only end to end: DSAI reads CEBE; nothing in this phase writes to CEBE, the CRM, or contact records. |
5.1. Data Lifecycle
Phase 1 persists no new customer data. The CEBE profile fetched at conversation start lives only in the AI-service thread context and expires with the thread (existing DSAI thread retention policy applies — confirmed in the DSAI contract, see Dependencies). The only new durable artifact is the per-response memory-read telemetry row (hit/miss/skip reason, latency, modules present — no customer profile content) written to the AI Activity Log datamart, subject to that datamart's existing retention. Customer-data retention/erasure remains governed by CEBE (source) and the DSAI thread policy (cache) — nothing new to clean up in the chatbot BE.
6. API & Webhook Behavior
| # | Behavior | Entity Affected | Triggered By | Expected Behavior | Failure Behavior |
|---|---|---|---|---|---|
| 1 | Identity + flag handoff in the prediction request | AI-service request payload (chatbot BE → DSAI) | Every AI Agent prediction request for a conversation's first agent response | Chatbot BE resolves the room's contact to qontak_customer_id and includes it plus the org's ai_agent_memory flag state in the existing predict payload (alongside today's qontak_chat_customer_id) | Contact lookup fails or no qontak_customer_id exists → field sent as null, flag logic unchanged; the reply path is never blocked or delayed by identity resolution |
| 2 | CEBE retriever read at conversation start | Customer profile/history (CEBE Central DB, via Retriever/Metrics layer) | DSAI receiving a first-response prediction with flag ON and a non-null qontak_customer_id | DSAI queries the CEBE retriever (org-scoped auth, qontak_customer_id key) for the aggregated customer profile + cross-module history within the 2,000 ms budget, and injects the result into the agent's reasoning context before generating the response | Timeout (> 2,000 ms), CEBE 4xx/5xx, or empty profile → agent answers exactly as today (no memory), failure reason recorded in the response telemetry; never an error to the customer |
| 3 | Thread-cached memory on subsequent turns | AI-service thread context | Second and later turns of a memory-enabled conversation | DSAI reuses the context fetched at conversation start from the thread; no re-query | Thread context lost (thread rebuilt or cache evicted) → treat as memory-miss for remaining turns; malformed/corrupt cached context → discarded and treated as memory-miss; no mid-conversation re-query in this phase |
| 4 | Per-response memory telemetry | AI Activity Log datamart row | Every AI Agent response in a flag-ON org | Response telemetry carries memory_status (hit / miss_no_identity / miss_empty_profile / skipped_flag_off / skipped_timeout / error), retriever latency ms, and the list of CEBE modules present in the profile | Telemetry write fails → response is unaffected; failure logged for reconciliation (metric completeness monitored in Observability) |
7. System Flow + User Stories
7.1. System Flow
Flow: Conversation start → memory read → memory-informed response Type: API Sequence
- A customer message opens (or reopens) a conversation and the room is assigned to the AI Agent.
- The chatbot BE builds the prediction request (
send_message_with_resolve/_get_answer_ai_agent): it resolves the room's contact toqontak_customer_idand adds it, plus the org'sai_agent_memoryflag state, to the payload. - Decision: flag OFF for the org → DSAI processes the request exactly as today; telemetry
records
skipped_flag_off; flow ends (no memory path). - Decision: flag ON but
qontak_customer_idis null (identity unresolved) → DSAI answers without memory; telemetry recordsmiss_no_identity. - Flag ON + identity present + first agent response of the conversation → DSAI queries the CEBE
Retriever/Metrics layer (org-scoped service auth, keyed
qontak_customer_id) with a 2,000 ms budget. - Decision: retriever returns within budget with a profile → DSAI injects the profile
(aggregated metrics + cross-module history summary, PII-masked per the AI consumer policy)
into the agent's reasoning context; telemetry will record
hit+ latency + modules present. - Decision: timeout / CEBE error / empty profile → DSAI generates the response without
memory; telemetry records
skipped_timeout/error/miss_empty_profile. The customer experience is exactly today's — never an error, never added waiting beyond the budget. - The agent generates the response. With memory: it acknowledges the returning customer, does not re-ask facts present in the profile, and references open items (e.g. an unresolved ticket or undelivered order) where relevant to the customer's question. Without memory: behavior is unchanged from today.
- On second and later turns, DSAI reuses the thread-cached context — no re-query (Non-Goal #8).
- Every response's telemetry row (memory_status, latency, modules) lands in the AI Activity Log datamart — the system of record for the ⭐ adoption metric and the memory-vs-non-memory outcome comparison.
- Failure branch (identity chain): the contact-service lookup errors mid-request → the BE
sends the payload with
qontak_customer_id: null(step 4 path); the reply is never blocked. - Failure branch (telemetry): the telemetry write fails → the response is unaffected; the gap is logged and surfaced by the metric-completeness monitor (Observability).
📊 System Flow — Read-side CEBE Memory
sequenceDiagram
participant Cust as Customer
participant BE as Chatbot BE (predict payload)
participant AI as DSAI (AI service)
participant CEBE as CEBE Retriever (org-scoped)
participant ADL as AI Activity Log
Cust->>BE: Message (conversation start, room on AI Agent)
BE->>BE: Resolve contact → qontak_customer_id + flag state
BE->>AI: Predict request (+qontak_customer_id, +ai_agent_memory flag)
alt Flag OFF
AI-->>Cust: Response as today
AI->>ADL: memory_status = skipped_flag_off
else Flag ON, identity null
AI-->>Cust: Response as today
AI->>ADL: memory_status = miss_no_identity
else Flag ON, identity present (first response)
AI->>CEBE: Query profile (qontak_customer_id, 2000ms budget)
alt Profile returned in budget
CEBE-->>AI: Masked profile + history
AI->>AI: Inject into reasoning context (thread-cached)
AI-->>Cust: Memory-informed response
AI->>ADL: memory_status = hit (+latency, modules)
else Timeout / error / empty
CEBE-->>AI: (late / error / empty)
AI-->>Cust: Response as today (auto-skip)
AI->>ADL: memory_status = skipped_timeout / error / miss_empty_profile
end
end
Note over AI: Later turns reuse thread-cached context — no re-query
7.2. User Stories
[MEM-S01] — Identity + flag handoff in the prediction request (chatbot BE)
| User Story | As the AI Agent (system actor), I want every prediction request to carry the customer's unified qontak_customer_id and the org's memory-flag state, so that the AI service can key a memory read to the right customer — or skip it cleanly. |
| Before State | The predict payload identifies the customer only as qontak_chat_customer_id = contact.channel_contact_id (send_message_with_resolve.rb, _get_answer_ai_agent) — a per-channel id CEBE cannot be keyed on. No flag state is passed. |
| After Delta | The BE resolves room → contact → qontak_customer_id and adds qontak_customer_id (nullable) + ai_agent_memory_enabled (boolean) to the existing payload. Resolution failures degrade to null — never block or delay the reply. |
| Importance | Must Have |
| Mockup / Technical Notes | Figma: N/A — backend-only. Data Fields (payload additions): • qontak_customer_id (uuid, nullable) — source: room's contact record via the existing contact-service lookup; null when unresolved• ai_agent_memory_enabled (boolean, required) — source: org system_preferences flag ai_agent_memoryTechnical Notes: Extends the existing _get_answer_ai_agent payload (which already carries room_id, conversation_id, company_id, qontak_chat_customer_id); the legacy field is kept unchanged for backward compatibility. Business rule: flag resolution per S5 §Feature flag (ai_agent_memory | default OFF). |
| Acceptance Criteria | — Happy Path — • AC-1: Given a room whose contact resolves to a qontak_customer_id in an org with ai_agent_memory ON, when the BE builds the prediction request, then the payload contains that qontak_customer_id and ai_agent_memory_enabled: true.• AC-2: Given the org's flag is OFF, when the BE builds the prediction request, then ai_agent_memory_enabled: false is sent and the AI service performs no memory read (verified via memory_status = skipped_flag_off telemetry).• AC-3 (boundary): Given the room's contact has no qontak_customer_id (identity unresolved), when the request is built, then qontak_customer_id: null is sent and the reply proceeds without memory (miss_no_identity).— Error / Unhappy Path — • ERR-1: Given the contact-service lookup errors or times out, when the request is built, then the BE sends qontak_customer_id: null, logs the resolution failure, and the customer reply is neither blocked nor delayed.— Permission Model — • CAN: chatbot BE (system) resolves and attaches identity/flag on every AI Agent request; internal ops toggle the org flag. • CANNOT: org admins, human agents, and end customers cannot toggle or override the flag (no FE surface — Non-Goal #6); no manual per-conversation trigger exists. • Unauthorized: N/A (system-to-system; no user-invocable path). — UI States — • Loading/Empty/Error/Success: N/A (backend payload change; no UI). — Negative Scenarios — (from Non-Goals) • NEG-1: Given any conversation in this phase, when the BE builds requests, then it performs no fuzzy identity matching (phone/email lookup fallback) — unresolved stays null (S5 §Identity). • NEG-2: Given a Starter/Free org, when its bot answers, then no memory fields drive a CEBE read (plan scope — S5). |
Dependencies: None (internal BE change; consumed by [MEM-S02]).
🧪 Test Coverage Matrix — [MEM-S01]
| Dimension | Coverage | Notes |
|---|---|---|
| Boundary values | ✅ defined | AC-3 covers null identity; NEG-2 covers plan boundary |
| State transitions | ✅ defined | AC-1/AC-2 cover flag ON/OFF request shapes |
| Data validation | ⚠️ TBD | ⚠️ QA: contact resolving to a malformed/legacy qontak_customer_id value |
| Concurrency | ⚠️ TBD | ⚠️ QA: flag toggled between two requests of one conversation — which state wins per turn |
| Network/timeout | ✅ defined | ERR-1 (lookup failure degrades to null, never blocks the reply) |
[MEM-S02] — CEBE retriever read + context injection at conversation start (DSAI)
| User Story | As the AI Agent (system actor), I want the customer's CEBE profile fetched once at conversation start and injected into my reasoning context, so that my responses use the customer's real cross-module history without slowing the conversation down. |
| Before State | DSAI receives only the current message + identifiers; its thread holds intra-conversation state only. It has no CEBE client and no cross-module customer context. |
| After Delta | On the first agent response of a conversation with flag ON + identity present, DSAI queries the CEBE Retriever/Metrics layer (org-scoped auth, keyed qontak_customer_id, 2,000 ms budget), injects the masked profile into the reasoning context, and caches it in the thread for later turns. Every degradation path answers exactly as today. |
| Importance | Must Have |
| Mockup / Technical Notes | Figma: N/A. Data Fields (retriever response consumed): • profile (object) — aggregated per-customer metrics from CEBE (e.g. total conversations, avg CSAT, open/resolved tickets, orders/LTV, loyalty tier, campaign opt-in status) — exact field set = the CEBE AI-consumer allow-list (Dependencies)• history (array) — cross-module event summary entries, per the CEBE query approach• modules_present (array) — which source modules had data for this customerTechnical Notes: Injection mechanics (system-context block vs. retrieval tool) are the RFC's decision (§14) — this PRD fixes the product behavior: read once at conversation start, hard 2,000 ms budget, thread-cached, org-scoped, masked fields only (S5 §Tenancy & access). |
| Acceptance Criteria | — Happy Path — • AC-1: Given flag ON and a non-null qontak_customer_id, when the first agent response of a conversation is generated, then DSAI queries the CEBE retriever exactly once and the returned profile is available to the agent's reasoning for that response.• AC-2: Given the retriever responds within 2,000 ms, when the response is generated, then telemetry records memory_status = hit with the retriever latency and modules_present.• AC-3: Given a memory hit on turn 1, when turns 2..n are generated, then no further retriever query is made and the cached context remains available (hit telemetry carries cached: true).• AC-4 (boundary): Given CEBE has data from only some modules for this customer (e.g. CDP + Marketing connected, tickets not yet), when the profile is injected, then the agent uses the partial profile and never fabricates data for absent modules. — Error / Unhappy Path — • ERR-1: Given the retriever exceeds 2,000 ms, when the budget expires, then DSAI abandons the read, answers without memory, and records skipped_timeout — total added first-response latency never exceeds the budget.• ERR-2: Given CEBE returns an error (4xx/5xx) or an empty profile, when the response is generated, then the agent answers exactly as today and telemetry records error / miss_empty_profile — the customer never sees an error.• ERR-3: Given the thread cache is lost mid-conversation, when later turns are generated, then they proceed as memory-miss (no re-query, no error). — Permission Model — • CAN: DSAI reads the CEBE retriever with a service credential bound to the requesting company_id.• CANNOT: DSAI cannot query across orgs, cannot query without a qontak_customer_id, cannot receive fields outside the AI-consumer PII allow-list, and cannot write anything to CEBE (read-only credential).• Unauthorized: an auth failure on the retriever is treated as ERR-2 (answer without memory; error telemetry) — never a customer-visible failure.— UI States — • N/A (no UI); degradation states are defined per ERR-1..3. — Negative Scenarios — (from Non-Goals) • NEG-1: Given any turn of any conversation, when the agent runs, then no write of any kind is issued to CEBE (Non-Goal #1 — read-only). • NEG-2: Given a customer resolved for org A, when org B's agent converses with a different customer, then no data from org A's profile can appear in org B's context (tenancy isolation — S5). • NEG-3: Given an Ask Airene / copilot request, when it is processed, then no CEBE read occurs (Non-Goal #5 — autonomous agent only). |
Dependencies: [MEM-S01] (identity + flag in payload); CEBE retriever + AI-consumer allow-list + DSAI contract (Section 12).
🧪 Test Coverage Matrix — [MEM-S02]
| Dimension | Coverage | Notes |
|---|---|---|
| Boundary values | ✅ defined | AC-4 (partial modules), ERR-2 (empty profile), 2,000 ms budget edge in ERR-1 |
| State transitions | ✅ defined | AC-1→AC-3 (first read → cached turns); ERR-3 (cache loss mid-conversation) |
| Data validation | ⚠️ TBD | ⚠️ QA: profile containing unexpected/renamed fields vs. the allow-list contract |
| Concurrency | ⚠️ TBD | ⚠️ QA: two first-turn messages racing on one new conversation — exactly-one retriever query |
| Network/timeout | ✅ defined | ERR-1 (hard budget), ERR-2 (CEBE error), auth failure path under Unauthorized |
[MEM-S03] — Memory-informed response behavior
| User Story | As an End Customer returning to a business I've bought from and contacted before, I want the AI agent to recognize me and use what the business already knows, so that I don't repeat my history and get answers that reflect my actual situation. |
| Before State | The agent treats every conversation as first contact: generic greeting, re-asks name/order/issue details the business holds, unaware of open tickets or orders. |
| After Delta | With a memory hit, the agent: (a) acknowledges the returning customer in its greeting, (b) does not re-ask facts present in the profile, (c) references relevant open items (unresolved ticket, undelivered order) when the customer's question touches them, and (d) treats profile data as context — the customer's current message always takes precedence when they conflict. Memory-miss conversations behave exactly as today. |
| Importance | Must Have |
| Mockup / Technical Notes | Figma: N/A — behavior is conversational, no UI. Data Fields: consumes the injected profile from [MEM-S02] — no new fields. Technical Notes: Behavior is specified at the outcome level; prompt-side implementation is DSAI's (RFC). Guardrails: the agent never recites the raw profile or enumerates PII unprompted (data minimization — it uses context, it doesn't dump it); profile fields are already masked per the AI-consumer allow-list (S5 §Tenancy & access). |
| Acceptance Criteria | — Happy Path — • AC-1: Given a memory hit whose profile shows prior resolved conversations, when the agent greets the customer, then the greeting acknowledges the returning relationship and does not re-ask identity facts present in the profile (e.g. name).• AC-2: Given the profile shows an open item (e.g. an unresolved ticket or an undelivered order) and the customer's message relates to it, when the agent responds, then the response references that open item's state instead of asking the customer to re-explain it from scratch. • AC-3: Given the customer's current message contradicts the profile (e.g. "this is about a different order"), when the agent responds, then the current message wins and the agent follows the customer's correction without arguing the profile. • AC-4 (boundary): Given a memory miss of any kind ( miss_no_identity / miss_empty_profile / skipped_* / error), when the agent responds, then behavior is byte-for-byte today's memoryless behavior — no reference to memory, no apology about missing context.— Error / Unhappy Path — • ERR-1: Given the injected profile contains a field the agent cannot interpret, when the agent responds, then it ignores that field rather than exposing raw data or erroring. — Permission Model — • CAN: the autonomous AI Agent uses injected memory in flag-ON orgs. • CANNOT: the agent cannot enumerate the customer's full profile on request ("tell me everything you know about me" → answers with a brief summary of relationship state only, no raw field dump); human agents and Ask Airene get no memory surface (Non-Goals #4, #5). • Unauthorized: N/A (no user-invocable path). — UI States — • N/A — conversational behavior only. — Negative Scenarios — (from Non-Goals) • NEG-1: Given the customer states a new durable preference mid-conversation, when the conversation ends, then the agent has not persisted it anywhere (no write-back — Non-Goal #1); it may use it within the conversation only (thread state, as today). • NEG-2: Given a flag-OFF org, when its agent responds, then no memory-informed behavior of any kind appears. |
Dependencies: [MEM-S02].
🧪 Test Coverage Matrix — [MEM-S03]
| Dimension | Coverage | Notes |
|---|---|---|
| Boundary values | ✅ defined | AC-4 covers every miss/skip class collapsing to today's behavior |
| State transitions | ✅ defined | AC-3 (profile vs. live-message precedence), NEG-1 (in-thread only) |
| Data validation | ✅ defined | ERR-1 (uninterpretable field ignored, never exposed) |
| Concurrency | ⚠️ TBD | ⚠️ QA: profile fetched at start goes stale mid-conversation (e.g. ticket resolves while chatting) — agent references start-of-conversation state |
| Network/timeout | ✅ defined | Inherited from [MEM-S02] ERR-1/ERR-2 — misses collapse to AC-4 |
[MEM-S04] — Per-response memory telemetry (adoption + outcome system of record)
| User Story | As the Business Admin / CS Lead (and the BOT squad reviewing rollout), I want every AI response to record whether memory was read, why not, and at what cost, so that the ≥50% adoption metric and the memory-vs-non-memory outcome comparison are measurable from day one. |
| Before State | The autonomous agent persists no per-response AI artifacts on the chatbot side; the AI Activity Log datamart is being enhanced for the agent but has no memory dimension. |
| After Delta | Every AI Agent response in a flag-ON org lands a telemetry row carrying memory_status (hit / miss_no_identity / miss_empty_profile / skipped_flag_off / skipped_timeout / error), retriever latency ms, modules_present, cached (boolean), and is_testing_mode — joinable to the room's outcome (rooms.closed_reason) for the containment/CSAT delta. |
| Importance | Must Have |
| Mockup / Technical Notes | Figma: N/A. Data Fields (telemetry row): • memory_status (enum, required) — the six values above• retriever_latency_ms (integer, nullable) — null unless a query was attempted• modules_present (string[], nullable) — CEBE modules with data on hit• cached (boolean, required) — thread-cache reuse vs. fresh read• is_testing_mode (boolean, required) — testing-mode rows excluded from the ⭐ metric• room_id, conversation_id, company_id, agent_id (required) — join keysTechnical Notes: Lands in the AI Activity Log datamart (the row-grain telemetry layer already being enhanced for the autonomous agent); no profile content is ever written — status, latency, and module names only (S5.1 Data Lifecycle). Ownership of the emit point (DSAI response envelope → datamart) is fixed in the RFC. |
| Acceptance Criteria | — Happy Path — • AC-1: Given any AI Agent response in a flag-ON org, when the response completes, then exactly one telemetry row is written with a valid memory_status and the join keys above.• AC-2: Given a hit, when the row is written, then it carries retriever_latency_ms, modules_present, and cached.• AC-3: Given rows accumulated over a period, when the adoption metric is computed, then % hit of all non-testing-mode AI responses in flag-ON orgs is derivable from this table alone (no joins outside the datamart + rooms).— Error / Unhappy Path — • ERR-1: Given the telemetry write fails, when the response completes, then the customer-facing response is unaffected and the failure is logged; sustained write-failure rate > 1% of responses over 1h raises an internal alert (metric completeness — §9). — Permission Model — • CAN: the AI pipeline writes rows; BOT squad + BI read the datamart (existing org-locked access model). • CANNOT: customers and org users have no access to raw telemetry; no PII/profile content exists in the rows to expose. • Unauthorized: N/A (internal pipeline). — UI States — • N/A. — Negative Scenarios — (from Non-Goals) • NEG-1: Given a hit row, when it is inspected, then it contains no customer profile values — only status, latency, module names, and join keys. |
Dependencies: [MEM-S02]; AI Activity Log datamart enhancement (Section 12).
🧪 Test Coverage Matrix — [MEM-S04]
| Dimension | Coverage | Notes |
|---|---|---|
| Boundary values | ✅ defined | AC-2 nullable fields by status; testing-mode exclusion in AC-3 |
| State transitions | ✅ defined | One row per response across hit/miss/skip/error classes (AC-1) |
| Data validation | ⚠️ TBD | ⚠️ QA: unknown future memory_status value rejected vs. stored — schema evolution rule |
| Concurrency | ⚠️ TBD | ⚠️ QA: burst traffic — row-per-response holds with no dedup/loss at volume |
| Network/timeout | ✅ defined | ERR-1 (write failure never touches the response; completeness alert) |
8. Rollout
| Stage | Audience | What's true before advancing |
|---|---|---|
| 1. Internal validation | Mekari internal orgs + testing-mode conversations on staging, flag ON | All four stories pass QA; timeout auto-skip verified under injected CEBE latency; zero cross-tenant reads in penetration checks |
| 2. Design partners | 5–10 production orgs on Plus/Ultimate/360 whose CEBE-connected modules give real profiles (CDP + Marketing at minimum), flag ON per org | ≥ 2 weeks of clean telemetry: p95 added latency ≤ 2s, error+timeout rate < 5% of attempted reads, no tenancy or PII incidents; qualitative transcript review confirms memory-informed behavior (MEM-S03) reads naturally |
| 3. Staged GA | Progressive flag enablement across all autonomous-agent orgs (Plus/Ultimate/360), batched weekly | Stage-2 gates hold at each batch; ⭐ adoption tracking live on the datamart dashboard |
No data migration is involved — the feature reads CEBE at runtime and persists only telemetry. Old and new conversations coexist trivially: any conversation started before its org's flag was enabled simply never read memory; the next conversation does. Rollback: turning the org flag OFF (or a global kill) reverts the agent to today's memoryless behavior on the next conversation — no state to unwind.
Semantic regression rollback (AI-output change): memory changes response content, so a
structurally-valid-but-worse regression is possible. Per-org flag ai_agent_memory is the revert
mechanism; the named trigger is the Observability thresholds (§9.1): containment delta of the
memory cohort turning negative (< 0 pp vs. control over a rolling 14 days) or a CSAT drop > 0.2
in the cohort → freeze rollout, flag OFF for affected orgs, investigate before resuming.
9. Observability
| # | Event / Metric | Trigger | Properties / Definition |
|---|---|---|---|
| 1 | ai_agent_memory_read | Every AI response in a flag-ON org (the MEM-S04 row) | memory_status, retriever_latency_ms, modules_present, cached, is_testing_mode, join keys |
| 2 | Adoption % (⭐ feed) | Derived — daily | hit / all non-testing AI responses in flag-ON orgs |
| 3 | Latency p50/p95 | Derived — daily | retriever_latency_ms distribution on attempted reads; p95 threshold 2,000 ms |
| 4 | Skip/error rate | Derived — daily | (skipped_timeout + error) / attempted reads; alert > 5% sustained 1h, routed to the BOT squad on-call channel |
| 5 | Identity-coverage % | Derived — daily | non-null qontak_customer_id / AI conversations in flag-ON orgs (feasibility signal for the ≥50% target) |
| 6 | Telemetry completeness | Write-failure log vs. response count | Alert when gap > 1% over 1h (MEM-S04 ERR-1) |
| 7 | Outcome deltas | Derived — weekly | Containment (RESOLVE_AI share via rooms.closed_reason) and CSAT: memory cohort vs. control |
Dashboard owner: BOT — Chatbot Squad, on the AI Activity Log datamart (Metabase); CEBE-side retriever health (availability, upstream latency) is monitored by the CDP squad per the CEBE SLA.
9.1. Post-Launch Monitoring Cadence
Weekly review by the BOT squad (PM + BE) for the first 4 weeks post-GA, then monthly. Investigation triggers: adoption % drops > 10% week-over-week; skip/error rate > 5% sustained; p95 latency > 2,000 ms on two consecutive daily reads; containment delta < 0 pp or CSAT delta < −0.2 on the rolling 14-day cohort comparison → semantic-regression rollback per §8.
10. Success Metrics
| Category | Metric | Definition | Baseline | Target |
|---|---|---|---|---|
| Adoption & Usage | ⭐ AI Agent uses CEBE memory | % of non-testing AI-handled conversations with memory_status = hit in flag-ON orgs (delivers the CEBE ANCHOR metric) | 0% (no memory exists) | ≥ 50% of conversations by end of Q4 2026 |
| Quality & Impact | Containment delta | RESOLVE_AI share of AI-handled rooms, memory cohort vs. control (rooms.closed_reason spine) | Cohort baseline at Stage 2 | +3 pp within 60 days of GA (reviewed at baseline read) |
| Quality & Impact | CSAT delta | Avg CSAT, memory cohort vs. control, where collected | Cohort baseline at Stage 2 | +0.2 within 60 days of GA |
| Efficiency & Guardrail | Added first-response latency | p95 retriever_latency_ms on attempted reads | N/A — new | ≤ 2,000 ms p95; timeout auto-skip caps worst case |
| Quality & Guardrail | Tenancy/PII incidents | Cross-org reads or out-of-allow-list fields observed | 0 | 0 — any incident halts rollout |
11. Launch Plan & Stage Gates
| Gate | Stage | Success gate (measurable) | Duration |
|---|---|---|---|
| G0 — BUILD entry | Pre-build | CEBE retriever + query approach available in staging and the signed DSAI contract (interface, latency SLO, timeline) exists — neither is waivable | — |
| G1 — Internal validation | Stage 1 (§8) | 100% story ACs pass; injected-latency test proves auto-skip at 2,000 ms; 0 cross-tenant reads in isolation tests | ~2 weeks |
| G2 — Design partners | Stage 2 (§8) | p95 ≤ 2s; skip+error < 5%; adoption (hit-rate) ≥ 40% within partner cohort; transcript review sign-off; 0 PII incidents | ≥ 2 weeks |
| G3 — Staged GA | Stage 3 (§8) | Stage-2 gates hold per weekly batch; ⭐ trajectory on track for ≥50% by end of Q4 | Q4 2026 |
12. Dependencies
| Dependency | Owner | What this phase needs | Blocking? |
|---|---|---|---|
| CEBE core infrastructure (TF-3302) | CDP squad (Zhelia Alifa) | Central DB + Retriever/Metrics layer + query approach live in staging; per-customer aggregated query ≤ 1–2s | YES |
| CEBE RFC requirements injection (time-critical — the joint Eng+Data RFC is being authored now and is escalated in the CDP sprint) | CDP squad, input from BOT | This phase's consumer requirements land in the CEBE RFC: (a) per-customer aggregated query ≤ 1–2s, (b) granular event-level query for AI, (c) service-to-service auth model for DSAI as a consumer, (d) PII-masking allow-list for the AI consumer role, (e) the "AI-ready layer / feature store" item in CEBE §13 made concrete | YES |
| DSAI: retriever client + context injection + memory telemetry in the response envelope | DSAI squad | Signed contract before BUILD (G0): interface to the retriever, 2,000 ms budget behavior, thread-caching, injection mechanics, telemetry fields, delivery timeline. Avoidability check recorded in ANCHOR §7 — the BE-side alternative was rejected with eyes open; this gate is the compensating control for the known DSAI delivery-risk precedent | YES |
| Chatbot BE: identity chain + flag in payload (MEM-S01) | BOT squad (this phase) | Room → contact → qontak_customer_id resolution + ai_agent_memory flag in the predict payload | YES (internal) |
| AI Activity Log datamart enhancement | BOT + BI/Data | memory_status + latency + modules dimensions on the per-response row (MEM-S04) | YES — the ⭐ metric is unmeasurable without it (GA-blocking, not build-blocking) |
| Q4 module coverage in CEBE (Comms/Ticket/Loyalty land during Q4 — Phase 2 of CEBE) | CDP squad + source squads | Richer profiles as modules connect; not required to ship (AC MEM-S02/AC-4 handles partial profiles) | NO |
📊 Dependency Graph — Read-side CEBE Memory
graph LR
MEM[Phase 1 — Read-side CEBE Memory]
CEBE[CEBE core infra - retriever + query layer]
RFC[CEBE RFC - consumer requirements injected]
DSAI[DSAI contract - client + injection + telemetry]
BE[Chatbot BE - identity + flag in payload]
ADL[AI Activity Log - memory dimensions]
MODS[CEBE Q4 module coverage - Comms/Ticket/Loyalty]
MEM -->|BLOCKING| CEBE
MEM -->|BLOCKING| RFC
MEM -->|BLOCKING| DSAI
MEM -->|BLOCKING - internal| BE
MEM -->|BLOCKING - for GA metric| ADL
MEM -->|non-blocking| MODS
13. Key Decisions + Alternatives Rejected
13a — Decisions Made
| Date | Decision | Rationale |
|---|---|---|
| 2026-07-22 | Read once at conversation start, thread-cached (not per-turn) | One bounded read caps latency and CEBE load; within a conversation, freshness rarely changes materially; per-turn re-query multiplies cost for marginal gain |
| 2026-07-22 | Hard 2,000 ms budget with auto-skip | The reply path is sacrosanct: a memoryless answer on time beats a memory-informed answer late; matches CEBE's own ≤1–2s aggregated-query target with headroom |
| 2026-07-22 | qontak_customer_id-only keying, no fuzzy fallback | Wrong-customer memory is worse than no memory; identity confidence below "resolved unified id" is a safety boundary, not an optimization target |
| 2026-07-22 | Flag-only rollout, no FE surface | No memory design exists in the design system prototype; a config toggle adds a design+FE dependency with zero benefit during staged rollout |
| 2026-07-22 | Telemetry carries status + latency only, never profile content | Keeps the datamart PII-clean and the erasure story trivial (S5.1) |
13b — Alternatives Rejected
| Alternative | Why Rejected | Date |
|---|---|---|
| Chatbot BE assembles context and injects via predict payload / thread push | Initiative-level decision — see ANCHOR §7b; DSAI-direct chosen with a contract gate | 2026-07-22 |
| Per-turn CEBE re-query for maximum freshness | Multiplies latency exposure and CEBE load per message; staleness within one conversation is acceptable (flagged as a QA scenario in MEM-S03) | 2026-07-22 |
Fuzzy identity fallback (phone/email match) when qontak_customer_id is unresolved | Wrong-customer context is a trust and privacy hazard; identity resolution belongs upstream in CEBE's ContactResolver, not improvised here | 2026-07-22 |
| Ship with a partner-visible config toggle in the V2 agent config | Gates Q4 on a design dependency (no memory design exists); revisit as an FE follow-up post-GA | 2026-07-22 |
14. Open Questions
| # | Type | Question | Owner | Deadline |
|---|---|---|---|---|
| 1 | Risk | CEBE slips Q3 (RFC escalated) → G0 unreachable in time for Q4. Mitigation: requirements injected into the CEBE RFC now (§12); BUILD phase-gated on staging availability; descope fallback = launch with CDP+Marketing-only profiles | Dimas Fauzi Hidayat + Zhelia Alifa | 2026-08-15 |
| 2 | Risk | DSAI does not commit capacity for Q4. Mitigation: G0 contract gate — no build without a signed interface + timeline; escalation path is the task-force steering forum | Dimas Fauzi Hidayat + DSAI lead | 2026-08-31 |
| 3 | Open Question | Injection mechanics — system-context block vs. retrieval tool call inside DSAI's reasoning. Product behavior is fixed by this PRD; the mechanism is the RFC's decision with DSAI | RFC (BOT + DSAI) | 2026-09-15 (RFC review) |
| 4 | Open Question | Exact AI-consumer PII allow-list — which profile fields DSAI may receive, per CEBE's masking policy; UU PDP review of cross-module personal-data use in AI responses | CDP squad + Legal/Privacy | 2026-09-15 |
| 5 | Assumption | Identity coverage is sufficient — enough AI conversations resolve to a qontak_customer_id to make ≥50% adoption reachable (Observability #5 measures this from Stage 1) | BOT BE + CDP | 2026-08-15 |
| 6 | Assumption | CEBE profile fields available by GA match what MEM-S03 behavior needs (open tickets/orders require Comms/Ticket events landing in CEBE Q4 — until then profiles are CDP+Marketing-weighted) | Dimas Fauzi Hidayat + Zhelia Alifa | 2026-09-30 |
| 7 | Open Question | DSAI thread retention policy for the cached profile — confirm the cache expiry satisfies the privacy note in S5.1 | DSAI + Privacy | 2026-09-15 |
PRD CHANGELOG
| Version | Date | By | Section | Type | Summary |
|---|---|---|---|---|---|
| 1.0 | 2026-07-22 | Dimas Fauzi Hidayat / Claude | All | ADDED | Initial Phase-1 PRD: read-side CEBE memory consumer — identity handoff, DSAI-direct retriever read, memory-informed behavior, telemetry, flag-gated rollout. |