<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shriya Naorem</title>
    <description>The latest articles on DEV Community by Shriya Naorem (@shriya_naorem_2449bbe4bbc).</description>
    <link>https://dev.to/shriya_naorem_2449bbe4bbc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4147764%2Fe85ef607-16ff-4f90-8981-681935e43fed.png</url>
      <title>DEV Community: Shriya Naorem</title>
      <link>https://dev.to/shriya_naorem_2449bbe4bbc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shriya_naorem_2449bbe4bbc"/>
    <language>en</language>
    <item>
      <title>Giving My AI Agents Long-Term Memory Without Wrecking My Database Schemas</title>
      <dc:creator>Shriya Naorem</dc:creator>
      <pubDate>Mon, 28 Sep 2026 18:28:11 +0000</pubDate>
      <link>https://dev.to/shriya_naorem_2449bbe4bbc/giving-my-ai-agents-long-term-memory-without-wrecking-my-database-schemas-52fi</link>
      <guid>https://dev.to/shriya_naorem_2449bbe4bbc/giving-my-ai-agents-long-term-memory-without-wrecking-my-database-schemas-52fi</guid>
      <description>&lt;p&gt;We’ve all been there:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzbysig3pbcl0eri6cud6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzbysig3pbcl0eri6cud6.jpeg" alt=" " width="707" height="320"&gt;&lt;/a&gt; you build an LLM-powered application, and for the first ten minutes, it feels magical. Then a user updates their preferences, changes their target audience, or tweaks a campaign goal, and your agent completely forgets everything it learned two minutes ago. Traditional applications solve this by running relational database migrations and stuffing massive JSON objects into system prompts. But when building BrandPulse—a platform coordinating automated digital out-of-home (DOOH) advertising campaigns—watching our database bloat with brittle state logic became an engineering bottleneck we needed to eliminate.&lt;/p&gt;

&lt;p&gt;Instead of writing custom CRUD wrappers around user preferences, I ripped out our session management tables and integrated Hindsight. Below is how we decoupled agent state from rigid database schemas using isolated streaming memory banks.&lt;/p&gt;

&lt;p&gt;The Problem: Stateless LLMs and Rigid Databases&lt;br&gt;
When an agent needs to remember operational context—like a brand's specific tone, target regions, or strict safety boundaries—traditional databases force you into painful trade-offs:&lt;/p&gt;

&lt;p&gt;The Migration Trap: Every time your agent logic evolves to track a new constraint (e.g., automated safety filtering tags), you have to rewrite SQL schemas and update backend serialization models.&lt;/p&gt;

&lt;p&gt;Prompt Inflation: Pulling raw rows into every chat turn or API call eats up token budgets and introduces high-latency context stuffing.&lt;/p&gt;

&lt;p&gt;Cross-Talk Hazards: Shared table scopes lead to bleeding user data if tenant isolation isn't enforced cleanly at the query level.&lt;/p&gt;

&lt;p&gt;By looking at how agent memory works via Vectorize, I realized that agent state shouldn't be treated like static rows in a ledger. It needs to act as a living stream of retained entities and reflections.&lt;/p&gt;

&lt;p&gt;Architectural Blueprint&lt;br&gt;
In our FastAPI service, every brand gets an isolated workspace or "bank" mapped to a deterministic identifier (e.g., brand-dior). Instead of handling manual lookups, incoming profile updates flow directly into the Hindsight engine.&lt;/p&gt;

&lt;p&gt;The integration relies on three fundamental primitives:&lt;/p&gt;

&lt;p&gt;Retain: Ingests unstructured brand specifications and constraints into the memory store.&lt;/p&gt;

&lt;p&gt;Recall: Pulls specific factual history when requested by incoming queries.&lt;/p&gt;

&lt;p&gt;Reflect: Synthesizes high-level strategic reasoning and programmatic placement suggestions tailored to the brand's exact profile.&lt;/p&gt;

&lt;p&gt;According to the official Hindsight documentation, these memory streams automatically index semantic relationships over time, letting your agent reason over historical preferences rather than forcing keyword matches.&lt;/p&gt;

&lt;p&gt;Code-In-Action: The FastAPI Integration&lt;br&gt;
Here is a look at how clean our backend profile ingestion route became once we dropped the old database tables in favor of direct Hindsight calls:&lt;/p&gt;

&lt;p&gt;Python&lt;br&gt;
import os&lt;br&gt;
from fastapi import FastAPI, HTTPException&lt;br&gt;
from fastapi.middleware.cors import CORSMiddleware&lt;br&gt;
from pydantic import BaseModel&lt;br&gt;
from hindsight_client import Hindsight&lt;/p&gt;

&lt;p&gt;app = FastAPI(title="BrandPulse AI API")&lt;/p&gt;

&lt;p&gt;app.add_middleware(&lt;br&gt;
    CORSMiddleware,&lt;br&gt;
    allow_origins=["&lt;em&gt;"],&lt;br&gt;
    allow_credentials=True,&lt;br&gt;
    allow_methods=["&lt;/em&gt;"],&lt;br&gt;
    allow_headers=["*"],&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;HINDSIGHT_BASE_URL = os.environ.get("HINDSIGHT_BASE_URL", "&lt;a href="https://api.hindsight.vectorize.io%22" rel="noopener noreferrer"&gt;https://api.hindsight.vectorize.io"&lt;/a&gt;)&lt;br&gt;
HINDSIGHT_API_KEY = os.environ.get("HINDSIGHT_API_KEY", "")&lt;/p&gt;

&lt;p&gt;client = Hindsight(base_url=HINDSIGHT_BASE_URL, api_key=HINDSIGHT_API_KEY)&lt;/p&gt;

&lt;p&gt;class BrandProfile(BaseModel):&lt;br&gt;
    brand_name: str&lt;br&gt;
    industry: str&lt;br&gt;
    target_audience: str&lt;br&gt;
    campaign_goals: str&lt;br&gt;
    brand_tone: str&lt;br&gt;
    keywords: str&lt;br&gt;
    blocked_topics: str&lt;br&gt;
    target_locales: str&lt;/p&gt;

&lt;p&gt;@app.post("/api/brand/login")&lt;br&gt;
def save_brand_profile(profile: BrandProfile):&lt;br&gt;
    bank_id = f"brand-{profile.brand_name.strip().lower().replace(' ', '-')}"&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;profile_content = (
    f"Brand Name: {profile.brand_name}. Industry: {profile.industry}. "
    f"Target Audience: {profile.target_audience}. Campaign Goals: {profile.campaign_goals}. "
    f"Brand Tone: {profile.brand_tone}. Keywords: {profile.keywords}. "
    f"Blocked Safety Topics: {profile.blocked_topics}. Target Locales: {profile.target_locales}."
)

# Push the profile state into Hindsight memory
client.retain(bank_id=bank_id, content=profile_content, context="brand-profile-login")

# Trigger reflection to produce custom programmatic strategies
reflect_res = client.reflect(
    bank_id=bank_id, 
    query=f"Synthesize strategic marketing suggestions and programmatic digital screen placements in {profile.target_locales} matching the tone '{profile.brand_tone}' and goals '{profile.campaign_goals}'."
)

return {
    "status": "success",
    "bank_id": bank_id,
    "strategic_recommendation": getattr(reflect_res, 'text', str(reflect_res))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Before &amp;amp; After Comparison&lt;br&gt;
Before (Relational CRUD Tables):&lt;/p&gt;

&lt;p&gt;Adding a new preference filter meant writing migration scripts, altering ORM models, and manually injecting variables into giant string templates.&lt;/p&gt;

&lt;p&gt;Context queries returned rigid, static strings that lacked semantic flexibility when user inputs varied slightly.&lt;/p&gt;

&lt;p&gt;After (Streaming Memory Banks):&lt;/p&gt;

&lt;p&gt;Profile updates stream straight into Hindsight via client.retain(), instantly indexing new constraints without touching database tables.&lt;/p&gt;

&lt;p&gt;Reflection calls dynamically synthesize contextual business strategies, keeping system prompts lean and relevant.&lt;/p&gt;

&lt;p&gt;Dead Ends and Hard Lessons&lt;br&gt;
Watch Out for Silent Network Failures: Early in development, unhandled API timeouts during memory calls caused silent application fallbacks. Wrapping calls in explicit error handlers ensured our service surfaces connectivity issues immediately.&lt;/p&gt;

&lt;p&gt;Keep Bank IDs Deterministic: Relying on random UUIDs for memory scopes made debugging messy. Switching to predictable strings like brand-{name} kept our multi-tenant logs clean and isolated.&lt;/p&gt;

&lt;p&gt;Reflect, Don't Just Search: Relying purely on keyword search gave us flat historical snippets. Using the reflection endpoint allowed the agent to synthesize actual strategic output from raw memory streams.&lt;br&gt;
&lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;https://github.com/vectorize-io/hindsight&lt;/a&gt;&lt;br&gt;
&lt;a href="https://hindsight.vectorize.io/" rel="noopener noreferrer"&gt;https://hindsight.vectorize.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F11g4t6wqkkcijxi4f45p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F11g4t6wqkkcijxi4f45p.jpg" alt=" " width="800" height="437"&gt;&lt;/a&gt; &lt;a href="https://vectorize.io/what-is-agent-memory" rel="noopener noreferrer"&gt;https://vectorize.io/what-is-agent-memory&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>fastapi</category>
      <category>architecture</category>
      <category>database</category>
    </item>
  </channel>
</rss>
