Zenmem
Login
๐Ÿงพ

Ledger & recovery agent

RetailKhata / receivablesPython 3.14 ยท Libraryzenmem-open/retail-agents

View source

About this agent

The third of three unbundled retail agents, also called the "Khata Agent" after the running-balance ledger tradition it automates. It takes the packing slip the Warehouse & Dispatch Agent produced and invoices the buyer only for what was actually dispatched โ€” a partially fulfilled order is billed accordingly, never for the full original ask. The invoice is posted as a debit to a pluggable LedgerStore keyed by merchant and buyer, and payments post back as credits against the same running balance. Invoicing and balance arithmetic are deterministic, for the same reason the Warehouse agent avoids an LLM on stock numbers. Where the model does earn its place is tone: the reminders have to be polite and relationship-preserving rather than just cash-recovering, so reminder text is composed by an LLM call with a fixed prompt that explicitly forbids threatening or legal language, and a daily sweep can message every buyer overdue past a configurable number of days. It never changes what was billed or waives a balance on its own initiative โ€” record_payment is the only way a balance moves down, and it always requires an explicit amount from the caller.

RUNTIMEPython 3.14 ยท Library
MEMORY TYPESession + project, per merchant
SDKzenmem 0.4.4
INTERFACEPython library

What changed with Zenmem?

The same agent, built twice against the same contract โ€” once on Zenmem, once on MongoDB + LangChain/LangGraph.

Before โ†’ after

Code for invoice loggingโˆ’28%

Code for reminder compositionโˆ’50%

New infrastructure to stand upnone

New dependencies to install0

Schema, collection and index worknone

Reminder history per merchantone scope

Buyer data pulled into the promptnone

Before With Zenmem

What the team gained

  • Every reminder sent โ€” buyer, amount, days overdue โ€” is logged into the merchant's scope, so a merchant reviewing their own collections activity has the history without a report to build.
  • Invoices are logged into the same session audit trail the earlier two agents write, so an order reads end to end across all three.
  • Reminder composition passes the buyer's name, balance and days overdue explicitly and retrieves nothing, so no other buyer's balance can reach the prompt.
  • The model is used only for tone, which is the one part of a reminder worth generating.
  • A new reminder stage is a logged document, not a state column on an invoice table.

How memory is scoped

Session scope, keyed by the order thread, logs each invoice as it is posted โ€” the same audit trail the earlier two agents write into. Project scope, keyed by merchant id, logs every reminder sent (buyer, amount, days overdue), which is useful signal for a merchant reviewing their own collections activity over time. Unlike the Order Intake Agent, this agent does not read merchant memory back into its LLM prompt โ€” reminder composition only needs the buyer's name, balance and days overdue, all passed explicitly, not retrieved.

How it works

Invoicing, ledger posting, and the recovery sweep.

Invoice what shipped

Invoice lines are built only from the packing-slip lines that actually dispatched, so a partial fulfilment is billed for exactly that.

Post to the khata ledger

The invoice total is posted as a debit; a later payment is posted as a credit against the same merchant/buyer balance.

Compute overdue

The ledger tracks how long a balance has stood above zero, to decide who is actually due a reminder.

Compose a polite reminder

An LLM call drafts a short, warm WhatsApp reminder from a fixed prompt that explicitly rules out threatening or legal language.

Run the daily sweep

Every buyer overdue past a configurable number of days gets a reminder in one pass, each one logged back to merchant memory.

What it does

The agent's public surface, as a Python class.

Capabilities

  • invoice_and_post(slip, ...) invoices only the dispatched lines, computes line and total amounts, and sends the invoice document to the buyer.
  • Posts the invoice total as a debit to LedgerStore, keyed by merchant and buyer.
  • record_payment(merchant_id, buyer_id, amount) posts a payment as a credit against the same running balance.
  • Tracks outstanding balance and days overdue per buyer via LedgerStore.balance() and days_since_first_unpaid_debit().
  • send_balance_reminder(...) drafts a polite, non-confrontational reminder via callLLM and sends it through the channel adapter.
  • run_daily_recovery_sweep(...) sends reminders to every buyer overdue by at least a configurable number of days.
  • Logs every invoice and reminder into session or merchant project memory as it happens.