This article discusses a common architectural flaw in agent systems: storing UI presentation details directly within message transcripts. It highlights the issues arising from this tightly coupled design, such as lack of flexibility for future UI changes and inaccurate data for analytics. The author proposes an event-driven approach to separate structured data about agent actions (like tool calls and memory retrievals) from the raw message content, improving data purity and architectural flexibility.
Read original on Dev.to #architectureMany agent systems inadvertently store UI-specific information, such as HTML tags for step banners or collapsed details blocks, directly within the message table alongside the actual transcript. This practice couples the data layer tightly with the presentation layer, creating significant problems for system evolution and data integrity. Once presentation details are persisted as data, they become immutable, making future UI restyling, data exports, and share links inherently rigid and difficult to manage without data migration.
Architectural Anti-Pattern
Storing UI-specific markup or formatting within core data entities (like a `messages` table) is an architectural anti-pattern. It violates the principle of separation of concerns, making the system harder to maintain, scale, and evolve. Data purity is compromised when raw message content is intertwined with display logic.
The author's investigation revealed that a substantial amount of stored data was dedicated to UI chrome rather than meaningful transcript content. This not only bloated the database but also obscured the actual actions taken by the agent. For example, critical information like the number of memories retrieved or tools called, which represents the core "product claim" of an intelligent agent, was either embedded as debug HTML or not captured as structured data at all.
To address this, the article advocates for an event-driven architecture where structured data about agent turns (e.g., tool calls, memory usage, skills executed) is emitted as separate `turn_receipt` events. These events carry the semantic information in a structured format, decoupled from the message content. This allows the UI to render dynamic, computed displays (like chip strips showing counts) based on this structured data, rather than parsing static, pre-rendered strings from the message body.
Implementing this separation requires careful consideration of existing data and code paths. The author details challenges encountered, such as a `clean` flag not being consistently threaded through all call sites, leading to cached or secondary entry points still persisting UI elements. Furthermore, cleaning historical data requires robust scripting to identify and remove only the extraneous UI elements without corrupting legitimate user-generated content or model prose. The article emphasizes the importance of verifying data patterns with samples before executing bulk updates.
Key Invariant for System Design
No field a surface could render from structured data appears inside a persisted message body. If it is derivable, it is a projection, and projections belong at read time. Additionally, a presentation flag must be a parameter at every call site, not a default at one to ensure consistent behavior across all data paths.