PSA: the attachment links in an Airtable CSV export stop working after 2 hours

Was setting up a proper backup of one of my bases and went to check what the export actually contains. Posting because I did not know this.

Attachment URLs expire two hours after the API returns them. It's in the docs (https://airtable.com/developers/web/api/field-model), along with the recommendation to download the files rather than store the links. So the attachment column in a CSV export is a list of links that are dead by tomorrow: the records survive, the files do not.

While I was in there, two more things I had assumed were covered and are not:

- Linked records export as bare rec... IDs, with nothing alongside them to rebuild the relations.

- Field types, select options and formula definitions are not in the export at all.

Not bugs, just what the export is. But if your backup plan is "I can export a CSV whenever I want", it's worth testing that assumption on a base you care about - specifically, open one of the exported attachment links tomorrow and see what happens.

If anyone has found a way to keep attachments through the built-in export I'd genuinely like to hear it.

reddit.com
u/No_Advertising2536 — 2 days ago
▲ 1 r/mcp

MCP server for logs that don't fit in context — 208MB → 12KB in 9s, no model in the server

Every agent I use has the same blind spot: hand it a log file bigger than its context and it reads the first few hundred lines, greps around, then reasons confidently about whatever it happened to see. A bigger model doesn't fix it — the file is just larger than the window.

So I built an MCP server that reads the whole file and hands back a summary of what actually happened in it.

{ "mcpServers": { "logsleuth": { "command": "logsleuth-mcp" } } }

brew install alibaizhanov/tap/logsleuth or pipx install logsleuth. Zero dependencies — pure Python standard library, nothing to pull in.

Measured on a 208MB log: 1,576,412 lines read in 9.1s using 58MB of RAM, returned as 12,646 characters. Memory doesn't grow with file size, so a 2GB log costs the same as a 2MB one.

No model runs in the server. Your agent is the model, and a much better one than anything I'd run locally. The server's only job is to make the file legible: it's all deterministic, so nothing leaves the machine and nothing is nondeterministic between calls.

Three tools:

- read_log_evidence — the whole file, or a window (last: "30m", or since/until)

- inspect_log_file — cheap check before you spend a turn on something that turns out to be a core dump

- log_parse_diagnostics — format diagnostics containing zero log content, safe to show a user

What "12KB" actually contains, because truncation would be useless: deduplicated line patterns with how often each occurs and where it first appears; near-unique lines ranked as candidate state changes; numeric trends across the file; how errors distribute across service/pod/host; and raw context around where new errors start.

The ranking is by rarity and position, not volume — a config line that appears once, thirty seconds before the first new error, outranks ten thousand timeouts. That's not a style choice. On an annotated benchmark of 30 microservice failures, "blame the service with the most error lines" gets it right 0 times out of 30, worse than chance, because the loudest service is the caller that timed out waiting rather than the one that broke. Write-up with the numbers: https://alibaizhanov.github.io/logsleuth/loudest-service/

Limits, so you don't find them at 3am: logs only — a failure that's invisible in logs is invisible to this. Timestamps parse on 87% of the 132 public corpora I tested against, so an exotic format will get you a thinner summary. And it never writes to the file you point it at — the only thing it ever creates is a temp file when you ask for a time window, which it deletes afterwards.

MIT, source and every benchmark script: https://github.com/alibaizhanov/logsleuth

Happy to answer anything, and if it produces a bad summary on a log of yours I'd genuinely like to see it — I have no telemetry, so a report is the only signal I get.

reddit.com
u/No_Advertising2536 — 14 days ago
▲ 0 r/cursor

.cursorrules can't remember decisions you made yesterday — the MCP memory pattern I settled on after months of re-explaining my project

Every new Cursor session, same ritual: re-explain the architecture, re-state the constraints, watch it suggest the approach I rejected two weeks ago. Rules files help but they're static — they hold what I remembered to write down, not what actually happened in sessions. And they drift: my .cursorrules said "3-step deploy" for a month after the process became 4 steps.

What ended the ritual for me is a pattern, not a product (though I'll disclose mine at the end): **memory as an MCP server + one paragraph in your rules telling the agent when to use it.**

The mechanics:

**1. An MCP memory server** exposes `remember` / `recall` / `search` tools to Cursor (Settings → MCP, or `~/.cursor/mcp.json`). Any memory backend works — a self-hosted store, a hosted one, even a homegrown SQLite wrapper. The point is the memory lives *outside* the context window and *outside* static files.

**2. One rules paragraph** makes it automatic. Mine says roughly:

> Before starting significant work, call `recall` with the task topic. After completing significant work or when the user states a decision/preference/constraint, call `remember` with a one-line summary.

Without this paragraph, MCP memory is a tool the agent forgets to use (ironic). With it, capture and recall become part of every session's rhythm — no manual bookkeeping.

**3. The unexpected payoff is cross-tool.** Because it's MCP, the same memory server plugs into Claude Code, Windsurf, or a Codex setup. Explore a codebase in one tool, and the understanding is there when you open another. The "re-explain everything per tool" tax disappears — for me that was worth more than the per-session persistence.

**Honest limitations, because this isn't magic:**

- Capture quality depends on the agent actually calling `remember` — rules-driven invocation is ~reliable in Agent mode, less so in quick edits. (Claude Code solves this with hooks that fire deterministically; Cursor doesn't have an equivalent yet — if Cursor ever ships lifecycle hooks, this whole pattern gets strictly better.)

- Retrieval adds a tool call of latency when the agent decides to recall.

- A memory full of stale facts is worse than no memory — whatever backend you pick needs decay/versioning, or you're rebuilding .cursorrules drift with extra steps.

If you're hand-rolling: SQLite + a tiny FastMCP server gets you a working remember/recall in an evening, and honestly that's the right way to feel out whether the pattern fits you.

---

Disclosure (rule 6): I build Mengram (mengram.io) — a hosted/self-hostable memory backend that speaks MCP and does the extraction/decay/versioning part (free tier exists). The pattern above works with any backend; the rules-paragraph trick is the actual takeaway.

reddit.com
u/No_Advertising2536 — 29 days ago
▲ 5 r/Rag

Our monitoring said 62% of retrievals were failing. The real bug: RRF scores stored in the same column as cosine similarities

Yesterday I nearly declared a production retrieval emergency that didn't exist, and the mechanism is general enough that anyone running hybrid search should check for it.

**Setup:** hybrid retrieval over personal memory — vector similarity + BM25, fused with Reciprocal Rank Fusion, optional cross-encoder rerank on top for some tiers. Every search logs `top_score` for quality monitoring.

**The scare:** analyzing 10,706 logged searches, I applied the obvious threshold — top_score < 0.3 = weak retrieval. Result: 62% "failures," a dozen users at "100% failure with avg score 0.017," and a terrifying month-over-month "degradation" trend. One of the "100% failed" users was a paying customer with a thousand searches. I was halfway into incident mode.

**The tell:** a search for an exact entity name — a guaranteed hit — logged top_score 0.0426. And those "failing" users all averaged 0.016–0.021. Then it clicked: RRF scores are 1/(k + rank) with the standard k=60. Top rank = 1/60 ≈ 0.0167. My "catastrophic" users weren't failing — **their top result was rank-1 almost every time.** avg 0.017 is what perfect RRF retrieval looks like.

What actually happened: requests that go through the reranker log cosine-style scores (0–1 scale, 0.3+ = good). Requests on the raw RRF path log fusion scores (0.016–0.05 scale, where 0.017 = excellent). Both landed in the same `top_score` column with no scale tag. Every aggregate over that column — means, z-scores, my failure thresholds, even the health monitoring cron — was averaging apples with orbital velocities. The "month-over-month degradation" was just the RRF-path share growing as more traffic moved to hybrid.

**What survived scale-correction:** true failure (zero results) was 9–13%, driven mostly by two accounts whose agents were querying literally empty stores — a real integration problem, but a completely different one than "retrieval is broken."

**Lessons, generalized:**

  1. **A fused ranking score is not a similarity.** RRF outputs rank information, not confidence. The moment you fuse, your score's absolute value stops meaning what your dashboards think it means.
  2. **Never store scores from different scoring regimes in one unlabeled column.** Log a `score_kind` (or a scale-aware quality label computed at write time, which is what we shipped: strong/weak/no_match with per-scale bands). Analysis-time guessing is how you get 3am false incidents.
  3. **The only scale-free failure signal is emptiness.** Zero results means the same thing on every path. When in doubt, count zeros, not thresholds.
  4. **Validate your alarm against a known-good query before believing it.** One exact-match search that "scored 0.04" saved me from paging myself.

Sources for the RRF math: Cormack, Clarke & Buettcher (2009), "Reciprocal Rank Fusion outperforms Condorcet and individual rank learning methods" — the k=60 default everyone inherits comes from there.

Disclosure per rule 3: the production system is Mengram (mengram.io), a memory layer for AI agents — but the trap applies to any RAG stack mixing rerankers with fusion scoring. Nothing here requires my product to check: grep your score column and look for a bimodal cluster around 1/60.

reddit.com
u/No_Advertising2536 — 29 days ago

Your agent's memory remembers everything except how to do its job

Most "agent memory" today stores facts and events: what the user said, what the project is, what happened last session. Useful — but watch where an agent actually burns tokens and retries: it re-derives the process every single run. Wrong step order, forgets the migration, retries the exact thing that failed on Tuesday. The context was in memory. The know-how wasn't.

Psychology has a name for this split: episodic memory ("I remember going to the dentist") vs procedural memory ("I remember how to ride a bike"). Nearly every memory framework ships the first kind and skips the second — because the second is structurally harder. A workflow isn't a fact you extract once. It's a procedure that must CHANGE when it fails.

There's fresh academic backing. A recent paper from Zhejiang University + Alibaba (Memp — arXiv 2508.06433) built procedural memory from agents' own past trajectories, tested on GPT-4o, Claude Sonnet and Qwen. Their strongest mechanism wasn't storing successes — it was reflecting on failures to revise the stored procedure. The failure is the signal.

From running this in production, three arguments:

  1. Session recall and workflow learning are different problems. Perfect episodic memory still pays the full process tax every run.

  2. Procedures need version history, not overwrites. v1 from a session → v2 adds the missing migration step after a failed run → v3 reorders after an env-var race, 11 successes since. An agent loading v3 doesn't repeat the two mistakes that produced it.

  3. A procedure that never failed is a procedure you can't trust yet. Success count alone is survivorship bias — you want fail_count, what changed after each failure, recency. That's also your pruning signal.

The uncomfortable implication: hand-maintained instruction files (CLAUDE.md, AGENTS.md, rules) are static snapshots of procedural knowledge — they rot because updating them requires a human to notice the failure, remember to edit, and phrase it as an instruction. Nobody does that reliably.

Curious what others do for workflow-level memory: hand-rolled? Fine-tuning? Just eating the re-derivation cost every run?

reddit.com
u/No_Advertising2536 — 1 month ago
▲ 2 r/mcp

Lessons from running a multi-tenant streamable-HTTP MCP server in production (multiple workers, per-request auth)

I run a remote MCP server (memory for AI agents — disclosure at the bottom) that serves streamable HTTP at a single /mcp endpoint behind gunicorn with multiple workers. Getting it stable taught me three things that aren't obvious from the SDK docs, and I haven't seen them written up anywhere, so here they are.

1. Session-based transport breaks the moment you have >1 worker

The Python SDK's default flow assumes the session that handled initialize sees the follow-ups. With 2+ gunicorn workers there's no such guarantee — the follow-up lands on a worker that's never heard of your mcp-session-id, and clients get intermittent failures that are miserable to reproduce (works locally with one worker, flakes in prod).

  • Fix: Run the transport stateless (mcp_session_id=None, stateless=True) and build a fresh server per request. Any worker serves any request.

2. Starlette silently wraps route functions — mount an ASGI class instead

A plain async function route gets wrapped by request_response(), which expects a returned Response. But transport.handle_request() drives the ASGI cycle itself and returns None → intermittent TypeErrors and dropped connections that look like client bugs.

  • Fix: Mount a real ASGI callable (class MCPStreamableHandler: async def __call__(self, scope, receive, send)) — Starlette leaves the cycle alone.

3. StreamableHTTPSessionManager can't do per-request (multi-tenant) auth

It takes a single Server instance up front — nowhere to inject the tenant when every request carries a different Bearer token.

  • Fix: You need a per-request factory: parse auth header → construct a Server bound to that tenant → fresh stateless transport. Rock-solid for months since.

Happy to answer questions or share more of the setup — took embarrassingly long to figure out from symptoms like "MCP works in Claude Desktop on Mondays."

Disclosure / showcase: The server is Mengram (mengram.io) — long-term memory for agents (semantic facts + episodic events + procedural workflows, ~29 MCP tools). I'm the founder. Free tier 40 adds + 200 searches/mo, paid from $5/mo, core Apache 2.0 / self-hostable. This week it also landed in the official MCP registry as io.github.alibaizhanov/mengram.

reddit.com
u/No_Advertising2536 — 1 month ago
▲ 4 r/VoiceAutomationAI+1 crossposts

5 gotchas I hit building cross-call memory for Vapi voice agents (so you don't have to)

Vapi assistants are stateless across calls. The standard solutions are mem0+n8n tutorials, Synthflow's bundled memory, or rolling your own with a database and webhook. I spent the last week building a webhook adapter for this and ran into 5 things that weren't obvious from the docs. Sharing in case it saves someone else the trial-and-error.

1. Vapi sends BOTH toolCalls AND toolCallList — different shapes

When your custom tool fires, Vapi posts BOTH arrays on the same message:

  • toolCalls: OpenAI-spec nested — [{id, type, function: {name, arguments}}]
  • toolCallList: flattened — [{id, name, arguments}]

"arguments" in toolCalls arrives as a JSON STRING (per OpenAI spec); in toolCallList it's already parsed. If your webhook only reads one shape, you'll silently get undefined names or empty args from the other. Handle both — and JSON.parse the string variant.

Source: github.com/VapiAI/docs/blob/main/fern/tools/custom-tools.mdx

2. The server URL receives EVERY event, not just end-of-call

If you wire one Server URL for the assistant, you'll get: status-update, conversation-update, partial transcript chunks (streamed mid-call, transcriptType: "partial"), end-of-call-report, hang, speech-update, transfer-destination-request, etc.

If your memory-save handler doesn't filter on message.type == "end-of-call-report", it will fire on every partial transcript — running your extraction pipeline dozens of times per call, duplicating data, and burning quota. The fix is a 2-line type guard.

3. Final transcript lives at TWO paths

End-of-call-report has the transcript at BOTH message.transcript AND message.artifact.transcript. They're identical for completed calls, but on transfer/hangup edge cases I've seen one populated and not the other. Read whichever is present, don't assume.

4. For "known caller" recall, semantic search is the wrong primitive

I started with vector similarity for the recall webhook ("find facts relevant to this caller"). The query "important facts about caller +1555" matched basically nothing against real fact embeddings like "prefers morning slots."

For a phone-keyed caller you want EVERYTHING you know about them, not "most relevant to a query." Direct fetch by sub_user_id=voice:phone_number returns deterministic results. Then sort: persons by fact count desc so the actual caller surfaces ahead of mentioned people (their daughter, their doctor, the clinic agent — all get extracted as type=person).

5. Web "Talk to Assistant" calls have NO customer.number

If you're testing via Vapi's web button, call.customer doesn't exist. You can't phone-key web calls — fall back to call.id as the namespace, or surface a "no phone number, can you tell me your name?" path in the assistant prompt. For real end-to-end testing, just buy a $1/mo Vapi number and call yourself.

What I'd actually benchmark next

Latency. End-of-call extraction is async (fine), but recall is in the greeting hot path. Single recall on my setup is ~800ms p50; under 20 concurrent calls it climbs to ~1200ms p95. For sub-1s SLAs you need either a smaller summary (skip rerank, return fewer facts) or precompute caller summaries via cron.

Anyone running voice memory at production scale (>1000 calls/day) — what were your retrieval-latency tricks?

Disclosure: I built an open-source MCP memory server (Apache 2.0) that does the above; if anyone wants reference code, the Vapi adapter is at github.com/alibaizhanov/mengram. But the gotchas above apply to any implementation.

reddit.com
u/No_Advertising2536 — 3 months ago

Temporal decay + episode importance weighting for LLM agent memory — implementation notes

I've been building an MIT-licensed memory layer for LLM agents (disclosure: I'm the author, repo at the bottom). Sharing two implementation choices that moved retrieval quality the most, in case useful for anyone working on similar.

Problem

Vector similarity alone ranks "I bought milk in 2019" the same as "I bought milk yesterday" if embeddings are close. Agent memory needs recency AND salience biasing retrieval, not just semantic match.

Approach 1 — Ebbinghaus decay for facts

For semantic facts (e.g. "User lives in Berlin"), exponential decay:

decay = e^(-k * days_since_last_access)

Here, k = 0.03, tuned so facts halve in salience in about 23 days.

>

Final score:

final = rrf_score * decay

Approach 2 — Importance weighting for episodes

Inspired by Stanford's Generative Agents (Park et al. 2023,https://arxiv.org/abs/2304.03442). At extraction time, the LLM scores each episode 0–1 on emotional/factual salience. At retrieval, importance modulates score with bounded range:

boost = 0.8 + 0.4 * importance (range: [0.8, 1.2])

final = rrf_score * decay * boost

Bounding to [0.8, 1.2] is critical — wider range (e.g. 0.5–2.0) drowns out vector similarity. Tight band lets importance break ties between similar-quality results without overriding semantic match.

What didn't work

  • Linear decay (too aggressive past day 7).
  • Importance multiplier >2x (overrides semantic match badly).
  • Decay on episodes without importance signal (loses old but important memories).

Hybrid retrieval base

Decay/importance sits on top of Reciprocal Rank Fusion (RRF) over [vector, BM25]. Pure vector misses keyword queries ("what was the API key?").

>

Stack

  • Python (FastAPI)
  • Postgres + pgvector
  • OpenAI text-embedding-3-large (1536-dim)
  • MCP server frontend

Full implementation (MIT):

https://github.com/alibaizhanov/mengram

Relevant files: cloud/store.pysearch_episodes_vector, search_procedures_vector

The choices around k = 0.03 and importance bounding [0.8, 1.2] took the most iteration. Would love to hear what others tuned for similar memory systems — especially how you handle procedural memory (workflows/skills) vs declarative.

reddit.com
u/No_Advertising2536 — 3 months ago
▲ 2 r/MCPservers+2 crossposts

Mengram — open-source MCP memory server with hybrid retrieval and temporal decay

Hey r/mcp — solo founder here, built this because I got tired of my Claude Desktop forgetting everything between sessions.

What it is:

An MCP (Model Context Protocol) server that gives any agent (Claude Desktop, Cursor, Codex, Cline, Continue) persistent memory across sessions. It features 30 tools to add, recall, search, reflect, dedup, and manage memories.

How it works under the hood:

  • Hybrid retrieval: Vector (text-embedding-3-large) + BM25 + Reciprocal Rank Fusion.
  • Temporal decay: Implements the Ebbinghaus forgetting curve for facts using the formula: e^(-0.03 * days)
  • Episode importance weighting: Provides a 0.8–1.2x boost based on emotional or factual salience.
  • Procedure weighting: Surfaces successful and recent workflows first.
  • Bi-temporal facts support: Uses event_time vs valid_from/valid_to (partial support, currently working on full time-travel).

Install (literally one line):

Bash

pip install mengram &amp;&amp; mengram signup --email you@example.com

Then it prints the MCP config for Claude Desktop / Cursor / etc.

Self-host:

Bash

git clone https://github.com/alibaizhanov/mengram
cd mengram
docker compose up
# Bring your own Postgres + OpenAI key

What makes it different from mem0/Letta/Zep (genuinely, not marketing):

  • MCP-native: Built from the ground up for MCP, not a Python SDK-first tool like mem0.
  • Procedures: Supports runnable workflows, not just static facts.
  • No lock-in: Self-hostable in just one command without managed-only restrictions.
  • Usable free tier: Includes 40 adds and 200 searches per month.
  • Repo:https://github.com/alibaizhanov/mengram
  • Hosted:https://mengram.io

Happy to answer anything about the retrieval approach, the self-hosting setup, or why I picked MCP over a custom API. Roast welcome.

u/No_Advertising2536 — 3 months ago

Three bugs that only surfaced when a real coding agent ran my install instructions

Shipped something today: an "install via one prompt" flow for my open-source AI memory layer. The idea is the same one Karpathy hinted at recently — docs written for the agent, not the human. User pastes one prompt into Claude Desktop / Cursor / Codex, the agent fetches a plain-text guide and does the rest (pip install, signup, MCP config edit, round-trip verification).

I tested it in synthetic harnesses for a couple hours. Doctor passed, all CI green. Felt safe to release.

Then I had a real agent in real Claude Desktop run the guide against my own machine. Three releases in six hours. Here's what only surfaced once a real LLM was driving:

  1. Wrote the guide assuming pip install &lt;pkg&gt; would give the user a working install. It doesn't on python.org Python — Python's default urllib refuses to verify TLS without a CA bundle. pip install only pulls hard deps, not optional ones. Had to make certifi a hard dep. Took a release.
  2. My MCP server only worked because I happened to have the mcp package installed from earlier dev work. It was listed as an optional extra: mengram-ai[mcp]. A plain pip install left the server unable to start — Claude Desktop tried to attach, got "process exited immediately." Made mcp a hard dep too. Another release.
  3. Third try: tools appeared in Claude Desktop, the agent discovered all 30 of them. Then every tool call failed with SSL: CERTIFICATE_VERIFY_FAILED. My CLI's HTTP helpers were using certifi correctly. My SDK's HTTP helpers (which the MCP server actually calls) weren't. Two separate code paths, only one was patched. Third release.

The synthetic tests passed every time. The "verify" step in my own install guide passed every time. The only thing that found these was: a real agent, in a real host, on a real machine without my dev environment leaking through.

The bigger takeaway, for anyone writing install instructions for agents to follow: your dep graph is a contract with the agent. Optional extras (pkg[xyz]) and "oh just run this manually once" steps don't survive agent execution. The agent will not run Install Certificates.command for you. It will not remember to also install the optional extras unless your guide says exactly so, in plain language, before the step that needs them.

Also: write your "doctor" to fail loud on the same things the host would fail loud on. My doctor only tested the API round-trip; it didn't test import mcp. Once I added a pre-check there, the next install caught the issue at verification, not later when the user opened Claude Desktop.

Anyone else building agent-native install paradigms? What caught you out?

u/No_Advertising2536 — 3 months ago

I've been operating an AI memory layer for the past year, watching what shapes agent memory actually takes in production. Most tutorials stop at "add fact, retrieve fact." Real production agents combine these primitives into wildly different products. Here are 5 patterns I keep seeing, with the architecture for each.

1. The Daily Brief

Shape: Agent runs on cron, pulls fresh sources, diffs against memory, emits digest only if something changed.

Common variants: morning news brief, KPI report, dependency update digest, security alert summary.

Why memory matters: without persistence, every run starts blind. The agent re-summarizes the same article you saw yesterday.

Architecture: cronfetch sourcessearch memory ("what did I report yesterday?") → diff vs memoryif delta &gt; threshold: emit briefsave to memory.

>

2. Multi-Tenant SaaS Memory

Shape: Each end-user has their own memory scope, but the application uses a single backend.

Why memory matters: without per-user isolation, Alice's history bleeds into Bob's. Search returns wrong context. Trust collapses.

Architecture: Every memory operation takes a user_id derived from your auth layer (NEVER from LLM output — that's a data leak waiting to happen).

The deep design rationale: a multi-tenant agent needs two-tier identity — your API credential authenticates the application, while user_id inside each call scopes the end-user. MCP spec doesn't define this out of the box, you have to build it on top.

3. Non-Developer Knowledge Work

Shape: Workflow has nothing to do with code: drafting briefs, reviewing documents for sensitive language, cross-referencing meeting notes, organizing coalition working groups.

Who builds it: researchers, organizers, lawyers, journalists. Not engineers. They use Claude Desktop / Cursor with memory as MCP server, no custom code.

Why memory matters: knowledge work is fundamentally about connecting current input to remembered prior context. Without persistence, AI is souped-up Ctrl+F.

Interesting wrinkle: these users structure memory differently. A developer's entity is "AWS Lambda" with config facts. A knowledge worker's entity is "Partner Working Group" with attendees, decisions, linked documents. Same primitives, totally different shape.

4. Cloud Infrastructure Automation

Shape: Agent manages a sprawl of cloud resources — AWS roles, DNS records, certificates, billing alerts, deployment pipelines.

Why memory matters: cloud accounts accumulate state at a rate humans can't track. By month two there are 80+ IAM roles, 200+ DNS records. Without memory, every change is fresh archaeology.

Architecture: entities = cloud resources, facts updated on every describe-* API call. Procedural memory captures repeatable workflows ("monthly billing report upload," "rotate IAM keys").

>

5. Personal Life Dashboard

Shape: Assistant that knows your routines, relationships, projects, preferences. Surfaces what matters. Smart triggers when something contradicts memory.

Why memory matters: the original "personal AI" promise. Without long-term memory it's a chatbot that forgets your spouse's name between sessions.

Trap: over-collection. Memory grows fast — a few weeks in, search results dilute with stale facts. Need decay (Ebbinghaus-style weighting) plus periodic curator passes.

How patterns combine

Real production agents are usually two or three patterns stacked:

  • Daily Brief + Personal Life Dashboard — your morning agent that already knows what you care about.
  • Multi-Tenant SaaS + Cloud Infra Automation — internal tool where each engineer has their own scoped AWS memory.
  • Non-Developer Knowledge Work + Multi-Tenant SaaS — coalition platform where each working group has isolated memory.

Most common architectural mistake I see: starting with "I'll add memory to my chatbot" (chatbot pattern), but actually needing the Daily Brief pattern — where memory is the diff against past output, not conversation history.

Pick the pattern that matches your workflow shape, not your interface shape.

What patterns are you seeing? Curious if there are shapes I'm missing — especially anything outside the dev/knowledge-work axis.

reddit.com
u/No_Advertising2536 — 4 months ago

Solo dev here. I build an open-source memory layer for AI agents. Just hit a small milestone (paying customers, modest MRR — not Twitter-flexable yet), but I felt it was worth sharing something that almost killed the product before I even noticed.

== The hidden bug ==

A customer in Russia pinged me: "The AI agent keeps asking me my name every conversation, can you fix it?"

Memory writes were succeeding and logs were clean. However, search reads were returning empty. From my dashboard, everything looked fine. I almost told him: "User error, save your name first."

But I decided to test with his actual queries and saw retrieval scores like 0.03 (basically random) for non-English queries. The bug was in the embedding model, not the agent code.

== The Industry Default Bias ==

OpenAI's text-embedding-3-large is the industry default. It's also English-first by design. On non-English queries, the cosine similarity drops off a cliff:

  • English: 0.70 cosine — Works
  • Spanish: 0.30 cosine — Weak
  • Russian: 0.25 cosine — Weak
  • Chinese: 0.03 cosine — Basically random

If your SaaS serves international users and uses OpenAI embeddings, you likely have this problem. You probably don't know it because:

  • Memory writes succeed silently.
  • There are no errors in your logs.
  • Native English testing passes perfectly.
  • International customers just churn quietly.

== What I changed ==

Switched to Cohere's multilingual-v3 model. The same query/data went from 0.03 to 0.77 on Chinese.

The migration took a weekend. Total cost: under $1 in API fees for backfilling 80k+ embeddings.

== Lesson for solo founders ==

The metrics that look fine to you are filtered through the language YOU use. If your customers churn quietly and you don't know why, look at the parts of your stack that have known biases toward your own context.

Embedding models, content moderation APIs, OCR — all are biased toward common-locale English-speaking users. If your product crosses that boundary, all of them silently underperform without throwing a single error.

I should have caught this 6 weeks earlier than I did.

reddit.com
u/No_Advertising2536 — 4 months ago
▲ 4 r/LangChain+1 crossposts

I built a memory layer for AI agents. Recently, one of our paying customers came back with a frustrating bug: "The agent keeps asking me my name every single session."

The memory was being saved correctly in the database. Search just wasn't finding it.

The Bug

Their queries weren't in English. The agent was using OpenAI's text-embedding-3-large (the industry default), which is English-first by design. On non-English queries, the embedding quality drops off a cliff.

Look at the cosine similarity for the same data, same model, just changing the query language:

  • English query → 0.70 cosine (finds the right fact)
  • Spanish query → 0.30 cosine (weak match)
  • Chinese query → 0.03 cosine (basically random)

The customer's agent was retrieving zero relevant memory on every query. From the agent's perspective, the user had no history, so it just started over. Every time.

Why this matters for anyone building agents

If your agent serves non-English users (or users who code-switch), you likely have this problem and don't know it. Memory writes work. Memory reads silently fail. Your agent looks "dumb," but you’ll see zero errors in your logs.

The Fix

The fix is the embedding model, not the agent code. Switching to Cohere's multilingual-v3 closed the gap immediately (Chinese cosine went from 0.03 → 0.77 on identical data).

Don't just look at dimensions. Pick a model trained for multilingual parity, not one fine-tuned mostly on the English internet.

Practical Takeaways

  1. Test in native languages: The bug isn't visible in English-only evals.
  2. Measure Cosine Similarity: If you use OpenAI for non-English data, measure real queries against real data before assuming RAG works.
  3. Zero-Downtime Migration: Add a new column to your DB, route queries by vector dimensionality, and backfill asynchronously.

The migration cost under $1 in API fees and took one weekend. The agent now finally remembers its users.

Happy to share the technical migration details (dual-column schema, backfill script, and two production gotchas) in the comments if useful!

reddit.com
u/No_Advertising2536 — 4 months ago