Case Study
Agent 17 · Transaction Narrative Enrichment
How a six-field output schema and a stateless single-turn architecture transforms raw acquirer strings into clean, categorised transaction narratives — with a confidence signal built in for downstream triage.
The Problem
Bank transaction strings are generated for payment processing infrastructure — not for humans. What lands in a customer's account history is often the acquirer reference, a truncated legal entity name, or a POS terminal code: SQ *PRET A MANGER LONDON GB or AMZN MKTP UK*2K4L9.
For consumer-facing fintech products — PFM apps, open banking platforms, spend analytics dashboards — this creates a costly enrichment problem. Raw strings must be resolved to real merchant names, categorised into spend buckets, and described in language customers recognise. Traditionally this requires merchant lookup databases, category taxonomies, and rule-based classifiers that need continuous maintenance. The long tail of novel, ambiguous, or regional strings breaks every rule-based system eventually.
Enrichment Example
Design Approach
Agent 17 is stateless by design — each batch of transaction strings is a single API call returning a complete set of enrichment records. There is no conversation history, no follow-up turns, no state to manage. This mirrors how transaction enrichment would operate in a real pipeline: a batch in, structured records out.
The core schema enforces six fields per transaction: RAW (the original string reproduced for traceability), MERCHANT, DESCRIPTION, CATEGORY, CHANNEL, and CONFIDENCE. Each field maps to a specific downstream use — merchant name for display, category for spend analytics, channel for fraud signals, confidence for routing to manual review queues.
The input uses a monospace textarea — a deliberate choice. Transaction strings are technical data, not natural language, and the font signals to the user that raw, unformatted strings are expected. The send button is labelled "Enrich" rather than "Send" or "Analyse" to name the operation precisely.
System Prompt Architecture
The prompt defines a single repeating delimiter block — ===TXN=== to ===END=== — with six named fields. The frontend parses each block and renders it as a transaction card with the raw string, merchant name, description, and two metadata tags (category and channel), plus a colour-coded confidence badge.
Merchant Resolution
The prompt explicitly distinguishes merchant name from acquirer code — the model must return the real trading name, not the payment processor reference. This prevents outputs like "SQ *" appearing as the resolved merchant.
Category Taxonomy
A fixed 11-category list is specified in the prompt — Groceries, Dining, Travel, Transport, Retail, Subscriptions, Utilities, Healthcare, Entertainment, Finance, Other. Constraining the taxonomy prevents free-form category invention and makes outputs machine-readable downstream.
Channel Classification
Six channel options — In-Store, Online, Contactless, Direct Debit, ATM, Transfer, Unknown — are defined as a closed list. Channel inference from string patterns (e.g. a URL suffix → Online, DD REF → Direct Debit) is a signal the model picks up reliably.
No-Skip Rule
An explicit prompt constraint — "process every transaction string, never skip one" — prevents the model from silently dropping ambiguous strings. Every input line must produce a TXN block, even if the enrichment is low-confidence.
What I Learned
The monospace input is a UX signal, not just aesthetic. Using a monospace font in the textarea communicates that raw, technical strings are expected — not sentences or paragraphs. It reduces the cognitive friction of pasting data directly from a terminal or CSV export, and aligns the interface with how practitioners actually work with transaction data.
Closed taxonomies are load-bearing. Without a fixed category list, the model produces categories that are semantically correct but machine-unreadable — "Fast Food," "Quick Service Restaurant," and "Takeaway" all mean the same thing but would create three separate spend buckets in a PFM dashboard. A constrained list of 11 categories eliminates this entirely.
Confidence is triage, not hedging. The original design had CONFIDENCE as a quality signal. Testing revealed it works better as a routing signal — HIGH goes straight to display, LOW goes to a review queue. Reframing the field in the prompt produced more calibrated outputs than asking the model to "rate its certainty."
The no-skip rule matters more than it seems. Without it, the model silently drops strings that look like internal reference codes or genuinely ambiguous entries — exactly the strings a fraud analyst most needs to review. Making skipping impossible, and requiring a LOW-confidence enrichment instead, keeps the pipeline complete.
Stateless is correct for batch enrichment. Unlike a conversational agent, transaction enrichment has no memory dependency. Each batch is independent. Stateless architecture reduces latency, eliminates context window bloat, and makes the output deterministic — every run on the same input produces the same structure.
Typical Token Profile
A batch of 6–8 transaction strings enriched in a single call typically produces: