Skip to main content
Memory GrainMemory Grain
GitHub
All articles
customer-servicepersonalizationportabilityindustry

Agent Memory for Customer Service: How OMS Captures, Structures, and Ports Support Knowledge

Customer service AI agents lose context between sessions, across channels, and when switching platforms. The Open Memory Specification provides a structured memory model — Episodes for raw transcripts, Facts for customer knowledge, Goals for issue tracking, Workflows for escalation — with GDPR-ready PII handling and cross-platform portability via .mg files.

12 min read

A customer calls in about a billing discrepancy. They explain the issue in detail. The agent resolves it. Three days later, the same customer reaches out over chat about a related problem — and has to explain everything from scratch. The chat agent has no memory of the phone call. The customer switches to a competitor's platform, and years of accumulated service history vanishes entirely.

This is the state of customer service memory today. Context is trapped in silos: per-session, per-channel, per-platform. AI agents that should be getting smarter with every interaction instead start from zero each time.

The Open Memory Specification provides a structured approach to this problem. By mapping customer service operations onto OMS memory types, agents can capture raw interactions, extract structured customer knowledge, track issue resolution, learn escalation procedures, and — critically — port all of this across platforms using the .mg file format.

The context loss problem

Customer service suffers from three distinct forms of memory failure:

Between sessions. A customer contacts support, explains their issue, gets a partial resolution, and follows up the next day. The follow-up agent has no structured record of what happened — just maybe a ticket ID and some notes if the previous agent bothered to write them.

Across channels. A customer starts on the web chat, switches to email for a detailed explanation, then calls in for urgent follow-up. Each channel operates with its own context. The phone agent cannot see the chat transcript. The email thread is disconnected from the ticket.

When switching platforms. An organization migrates from one customer service platform to another. Customer interaction history, preference data, resolution patterns, and learned workflows are locked in the old vendor's proprietary format. Migration is lossy at best, impossible at worst.

OMS addresses each of these through immutable, content-addressed memory grains with a portable container format.

Memory type mapping for customer service

Events for conversation capture

Every customer interaction — chat transcript, phone call summary, email thread — enters the system as an Event grain (Section 8.2). Episodes capture raw, unstructured text with minimal required fields: type, content, and created_at.

{
  "type": "event",
  "content": "Customer Jane Smith called regarding invoice #789. States she was charged $499 for a service she canceled last month. Requested immediate credit. Mentioned she has been a customer for 6 years and is considering switching providers.",
  "created_at": 1768471200000,
  "user_id": "customer-456",
  "namespace": "support:billing",
  "importance": 0.7,
  "structural_tags": ["customer-support", "billing", "escalation-risk"]
}

The user_id field ties this Episode to the specific customer for GDPR scoping (Section 12.4). The namespace partitions it into the billing support domain. The raw transcript is preserved exactly as captured — no interpretation, no summarization, just faithful recording.

For phone calls, the content_refs field (Section 7.1) can reference the original audio recording:

{
  "type": "event",
  "content": "Phone call summary: Customer discussed billing discrepancy for invoice #789",
  "created_at": 1768471200000,
  "user_id": "customer-456",
  "namespace": "support:billing",
  "content_refs": [
    {
      "uri": "cas://sha256:a4f8c2...",
      "modality": "audio",
      "mime_type": "audio/wav",
      "checksum": "sha256:a4f8c2...",
      "metadata": {"sample_rate_hz": 48000, "channels": 1, "duration_ms": 482000}
    }
  ]
}

Beliefs for customer preferences

As interactions accumulate, the consolidation pipeline (Section 8.1) extracts structured knowledge. Customer preferences become Belief grains with the semantic triple model:

{
  "type": "belief",
  "subject": "user",
  "relation": "prefers",
  "object": "dark mode",
  "confidence": 0.9,
  "source_type": "user_explicit",
  "created_at": 1768471200000,
  "namespace": "support",
  "user_id": "customer-456"
}

This follows the exact pattern from the spec's Test Vector 1 (Section 21.1). The source_type of "user_explicit" means the customer directly stated this preference, not that it was inferred. Language preferences work the same way:

{
  "type": "belief",
  "subject": "customer-456",
  "relation": "preferred_language",
  "object": "Spanish",
  "confidence": 0.95,
  "source_type": "user_explicit",
  "created_at": 1768471200000,
  "namespace": "support"
}

Beliefs for account knowledge

Account-level information uses the same Fact structure but adds temporal validity for accuracy over time:

{
  "type": "belief",
  "subject": "customer-456",
  "relation": "subscription_tier",
  "object": "enterprise",
  "confidence": 1.0,
  "source_type": "imported",
  "created_at": 1768471200000,
  "valid_from": 1735689600000,
  "valid_to": 1767225600000,
  "namespace": "support:billing"
}

The valid_from and valid_to fields (Section 15.1) mark when this subscription tier was active. When the customer upgrades or downgrades, the old Fact is superseded by a new one — the supersession chain preserves the full history. Bi-temporal queries (Section 15.2) can answer both "What is the customer's current tier?" and "What tier were they on when they filed this complaint?"

The source_type of "imported" indicates this data came from an external system (the billing database), not from a conversation or agent inference.

Goals for issue resolution

Open issues become Goal grains (Section 8.7), which have dedicated lifecycle semantics that Facts lack:

{
  "type": "goal",
  "subject": "support-agent-12",
  "description": "Resolve billing discrepancy for invoice #789",
  "goal_state": "active",
  "source_type": "user_explicit",
  "created_at": 1768471200000,
  "criteria": [
    "customer confirms resolution",
    "credit applied to account"
  ],
  "priority": 2,
  "namespace": "support:billing",
  "user_id": "customer-456",
  "valid_to": 1768903200000
}

The goal_state field cycles through "active", "satisfied", "failed", and "suspended" as the issue progresses. Each state transition creates a new immutable grain in the supersession chain (Section 8.7), preserving the complete resolution timeline. The priority field (integer 1-5, where 1 is critical) enables scheduling across the support queue. The valid_to field serves as a resolution deadline.

The criteria array lists human-readable success conditions: "customer confirms resolution" and "credit applied to account". Both must be verified before the Goal can transition to "satisfied".

Actions for system interactions

Every API call the agent makes — CRM lookups, ticket updates, payment processing — is recorded as a Action grain (Section 8.5):

{
  "type": "action",
  "tool_name": "crm_lookup_customer",
  "input": {"customer_id": "customer-456", "fields": ["subscription", "history"]},
  "content": {"subscription_tier": "enterprise", "tenure_years": 6, "lifetime_value": 28400},
  "is_error": false,
  "duration_ms": 145,
  "created_at": 1768471200000,
  "namespace": "support:billing",
  "user_id": "customer-456"
}
{
  "type": "action",
  "tool_name": "apply_account_credit",
  "input": {"customer_id": "customer-456", "amount": 499.00, "invoice": "INV-789", "reason": "erroneous_charge"},
  "content": {"credit_id": "CR-20260115-001", "new_balance": 0.00},
  "is_error": false,
  "duration_ms": 312,
  "created_at": 1768471500000,
  "namespace": "support:billing",
  "user_id": "customer-456"
}

The success field, duration_ms, and full input/content capture create a complete audit trail. When something goes wrong — a payment fails, a CRM call times out — the error field records what happened, and is_error: true flags the failure.

States for multi-session issues

Complex issues that span multiple interactions use State grains (Section 8.3) to snapshot the full state:

{
  "type": "state",
  "context": {
    "issue": "billing_discrepancy_inv_789",
    "customer": "customer-456",
    "status": "awaiting_credit_confirmation",
    "credit_applied": "true",
    "credit_amount": "499.00",
    "sessions_count": "3",
    "escalated": "false"
  },
  "created_at": 1768471800000,
  "plan": [
    "Follow up with customer in 24 hours",
    "Verify credit appears on next statement",
    "Close ticket if confirmed"
  ],
  "user_id": "customer-456"
}

When the next agent picks up this case, the Checkpoint provides the complete state: what has been done, what is pending, and what the plan is. The history field (optional, array of maps) can record the sequence of actions taken across sessions.

Workflows for escalation procedures

Learned escalation procedures are captured as Workflow grains (Section 8.4):

{
  "type": "workflow",
  "trigger": "customer_satisfaction_below_3",
  "steps": [
    "transfer_to_senior",
    "offer_discount",
    "schedule_callback"
  ],
  "created_at": 1768471200000,
  "importance": 0.7,
  "namespace": "support:escalation"
}

Workflows are procedural memory — learned sequences of actions that should be triggered under specific conditions. The trigger field defines when the workflow activates. The steps array defines the ordered sequence. As agents observe which escalation patterns work and which do not, the success_count and failure_count fields on related Facts track effectiveness.

Episode-to-Fact consolidation

The real power of OMS in customer service comes from the consolidation pipeline. After many conversations with the same customer, the system does not just have a pile of transcripts — it has structured knowledge extracted from them.

The consolidation_level field (Section 6.1) tracks the depth of extraction:

LevelMeaningCustomer service example
0RawCustomer said "I always have trouble with invoices"
1FrequencyCustomer has mentioned billing issues in 5+ episodes
2PatternBilling issues correlate with subscription renewals
3SequenceCustomer contacts support before renewal, then disputes charge after

A consolidation level 0 Fact is directly extracted from a single Episode. A level 1 Fact emerges from frequency analysis across multiple Episodes. Level 2 identifies co-occurrence patterns. Level 3 detects temporal sequences.

Each consolidated Fact carries derived_from (an array of content addresses pointing to source Episodes) and source_type: "consolidated", creating a complete provenance chain from structured knowledge back to raw interaction data.

Namespace partitioning

The namespace field partitions customer memory into logical domains. For customer service, a hierarchical approach works well:

  • "support:billing" — billing-related interactions and knowledge
  • "support:technical" — technical support issues
  • "support:returns" — return and refund requests
  • "support:general" — general inquiries

The first two bytes of SHA-256(namespace) are stored in the grain's fixed header (bytes 3-4) as a routing hint (Section 3.1.1), enabling efficient namespace-based filtering without payload deserialization. An agent handling a billing inquiry can quickly filter to "support:billing" grains without decoding every grain in the customer's memory.

Namespaces can also partition by customer: "customer:customer-456" would scope all memory for a single customer. The choice between per-department and per-customer namespacing depends on the access patterns — cross-customer queries prefer department namespaces, while customer-centric views prefer per-customer namespaces.

Cross-platform portability

When an organization switches customer service platforms, OMS provides a clean migration path through the .mg file format (Section 11).

A customer's complete service memory — every Episode, Fact, Goal, ToolCall, Checkpoint, and Workflow — can be exported as a single .mg file. The file structure (Section 11.2) includes:

  • A 16-byte header with magic bytes "MG\x01", flags, grain count, and compression settings
  • An offset index enabling random access to individual grains
  • The grain data itself (each grain is a complete .mg blob with its 9-byte header and MessagePack payload)
  • A 32-byte SHA-256 footer checksum for integrity verification

The receiving platform reads the .mg file, verifies the checksum, deserializes each grain, and indexes them into its own storage. Because every grain is self-describing (type byte in the header, full schema in the payload) and content-addressed (SHA-256 hash as identity), no platform-specific metadata is required. The grains are the data.

This portability has a practical consequence: customer service memory is no longer vendor lock-in. A customer's preference data, interaction history, and resolution patterns move with them — or with the organization — across platforms.

PII handling and GDPR compliance

Customer service data is inherently personal. OMS provides multiple layers of PII protection.

The user_id field (Section 12.4) ties grains to their data subject. Every grain containing customer data carries user_id: "customer-456", enabling GDPR Article 17 erasure by destroying the per-user encryption key (crypto-erasure). Per-user key derivation via HKDF-SHA256 from a master key plus the user_id means that destroying a single key renders all of that customer's grains unrecoverable (Section 20.3).

Sensitivity bits in the header flags (Section 13.1) classify grains at the binary level. Customer data grains carry sensitivity bits 10 (PII) in byte 1, bits 6-7. This enables O(1) routing to encrypted storage — a system can filter PII grains before deserialization.

Header flags byte: 0b10000000 = 0x80
                     ^^
                     PII sensitivity bits

Structural tags provide finer classification. A grain containing a customer's email might carry structural_tags: ["pii:email"]. One with their payment history might include structural_tags: ["pii:name", "reg:pci-dss"]. The reg:pci-dss tag signals that PCI-DSS storage requirements apply.

Selective disclosure (Section 10) enables sharing customer insights across teams without exposing PII. A billing team might share a Fact about a customer's payment pattern with the product team, eliding the user_id and subject fields:

{
  "type": "belief",
  "relation": "payment_pattern",
  "object": "consistently_late_by_3_days",
  "confidence": 0.88,
  "source_type": "consolidated",
  "created_at": 1768471200000,
  "_elided": {
    "subject": "sha256:7f3a...",
    "user_id": "sha256:b2c4..."
  },
  "_disclosure_of": "sha256:original_grain_hash..."
}

The product team can see the pattern (late payments) and the confidence level, but not who the customer is. The elided fields can be verified if the values are later revealed — the SHA-256 hash of the canonical MessagePack encoding of each field value (Section 10.1.1) proves authenticity without exposure.

Tracking what works: success and failure counts

The success_count and failure_count fields on Belief grains (Section 6.1) track which pieces of customer knowledge actually help resolve issues. When an agent uses a Fact — say, that a customer prefers email over phone — and the interaction goes well, success_count increments. When a Fact leads to a poor outcome, failure_count increments.

Over time, this creates a feedback loop. Facts with high success counts become more reliable. Facts with rising failure counts signal outdated or inaccurate knowledge that needs reverification. Combined with the confidence field (which represents the initial credibility of the knowledge claim), success_count and failure_count provide empirical validation of customer knowledge.

For example, a Fact recording that a customer prefers Spanish might start with confidence: 0.95 (high, because the customer explicitly stated it) and accumulate success_count: 12 over subsequent interactions where routing to Spanish-speaking agents produced positive outcomes. If the customer later switches language preferences and the agent does not catch the update, failure_count would start rising — a signal that the knowledge needs refreshing.

Putting it all together

Consider the full lifecycle for the billing discrepancy scenario:

  1. Capture: The phone call is recorded as an Episode with user_id: "customer-456" and namespace: "support:billing".

  2. Extract: Consolidation produces Facts — the customer's subscription tier, their 6-year tenure, the specific charge they dispute.

  3. Track: A Goal grain is created with description: "Resolve billing discrepancy for invoice #789" and goal_state: "active".

  4. Act: Action grains record the CRM lookup, the credit application, and the ticket update.

  5. Snapshot: A Checkpoint captures the current state for handoff to the next session.

  6. Learn: A Workflow grain captures the resolution pattern — if a long-tenured enterprise customer disputes a charge, apply immediate credit and follow up within 24 hours.

  7. Resolve: The Goal transitions to "satisfied" with satisfaction_evidence pointing to the ToolCall that applied the credit and the Episode where the customer confirmed resolution.

Every grain in this chain is immutable, content-addressed, and linked through derived_from and provenance_chain. The entire resolution history is auditable. The customer's memory is portable. And the PII is protected by sensitivity classification, per-user encryption, and selective disclosure.

The customer service agent that handles the next call does not start from zero. It starts with structured knowledge about who this customer is, what they care about, what has been tried before, and what works. That is what persistent, structured, portable agent memory looks like in practice.