--- title: "Why Your AWS Strands Agent Keeps Starting From Scratch (And How to Stop It)" authors: [benfrank241] date: 2026-03-25T12:00 tags: [strands, aws, agents, python, memory, tutorial] image: /img/blog/strands-persistent-memory.png hide_table_of_contents: true --- ![Why Your AWS Strands Agent Keeps Starting From Scratch (And How to Stop It)](/img/blog/strands-persistent-memory.png) AWS Strands agents are stateless by default — every session starts cold. Here's how to add persistent long-term memory using `hindsight-strands`, so your agent remembers what matters across runs. ## TL;DR - AWS Strands agents are stateless by default — every session starts cold - `hindsight-strands` adds three native memory tools to any Strands agent: retain, recall, and reflect - `memory_instructions()` pre-loads relevant memories directly into the system prompt before the agent starts - Works with any Strands-compatible LLM backend: Bedrock, Anthropic, OpenAI - You control what gets remembered, how much is recalled, and how memories are scoped with tags --- ## The Problem AWS Strands gives you a clean, decorator-based way to build agents. Define your tools, wire up a model, and you're running. The problem: every conversation starts from scratch. Ask your agent to remember your AWS region preference. It will. Ask it again next session — it won't have any idea what you're talking about. The `@tool` pattern handles the shape of your agent's capabilities, but it has nothing to say about persistence. For most demo use cases this doesn't matter. For real applications — customer-facing agents, coding assistants, support bots — it's a fundamental limitation. --- ## The Approach [Hindsight](https://github.com/vectorize-io/hindsight) is a memory layer for AI agents. It stores facts, extracts semantically relevant ones at query time, and can synthesize reasoned answers from accumulated context. The `hindsight-strands` package wraps this into native Strands tools, so your agent can manage its own memory the same way it uses any other capability. Three tools get added to your agent: - **`hindsight_retain`** — stores a piece of information to the memory bank - **`hindsight_recall`** — searches memory semantically and returns relevant facts - **`hindsight_reflect`** — synthesizes a reasoned answer from everything in memory There's also a `memory_instructions()` helper that runs a recall query before the agent starts and injects the results into the system prompt — useful when you want the agent to be context-aware from the first message, not just when it explicitly calls a tool. ``` User session ┌─────────────────────────────────────────┐ │ System prompt │ │ + memory_instructions() results │ ├─────────────────────────────────────────┤ │ Agent (Strands) │ │ ├── hindsight_retain │ │ ├── hindsight_recall │ │ └── hindsight_reflect │ └─────────────────────────────────────────┘ │ ▲ retain recall │ │ ▼ │ ┌───────────────────────────────┐ │ Hindsight bank │ └───────────────────────────────┘ ``` --- ## Implementation ### Install ```bash pip install hindsight-strands ``` You'll also need a running Hindsight instance. Two options: **Option 1 — Hindsight Cloud (no setup required)** Sign up at [ui.hindsight.vectorize.io](https://ui.hindsight.vectorize.io/signup) and grab your API URL and key from the dashboard. Pass them directly to `create_hindsight_tools()`. > **Note:** Use Hindsight Cloud if you want to skip self-hosting entirely — free to get started. **Option 2 — Self-hosted with Docker** ```bash export OPENAI_API_KEY=sk-... docker run --rm -it --pull always \ -p 8888:8888 -p 9999:9999 \ -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \ -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \ ghcr.io/vectorize-io/hindsight:latest ``` The API listens on port `8888`. The UI is available at `http://localhost:9999`. Hindsight also supports Anthropic, Gemini, Groq, and Ollama — swap `HINDSIGHT_API_LLM_PROVIDER` and the key accordingly. ### Basic setup ```python from strands import Agent from hindsight_strands import create_hindsight_tools tools = create_hindsight_tools( bank_id="user-123", hindsight_api_url="http://localhost:8888", ) agent = Agent(tools=tools) ``` `bank_id` is how Hindsight partitions memory. Use something meaningful: a user ID, a session namespace, a project slug. The bank is created automatically on first write. Now the agent has three new tools it can invoke. Run a session: ```python agent("Remember that I'm deploying to us-east-1 and I prefer Bedrock Nova Pro for inference.") ``` The agent will call `hindsight_retain` with the relevant facts. Next session: ```python agent("What model and region should I target for this deployment?") ``` It will call `hindsight_recall`, retrieve the stored preferences, and answer correctly — even in a fresh Python process. ### Pre-loading memory into the system prompt Tool-based recall works well when the agent knows it needs to look something up. But sometimes you want the agent to already have context before the first message. That's what `memory_instructions()` is for: ```python from strands import Agent from hindsight_strands import create_hindsight_tools, memory_instructions bank_id = "user-123" api_url = "http://localhost:8888" # Recall relevant context before the agent starts memories = memory_instructions( bank_id=bank_id, hindsight_api_url=api_url, query="user preferences and environment setup", ) agent = Agent( tools=create_hindsight_tools(bank_id=bank_id, hindsight_api_url=api_url), system_prompt=f"You are a helpful AWS infrastructure assistant.\n\n{memories}", ) ``` `memory_instructions()` runs a recall query synchronously and returns a formatted string you can embed anywhere in your prompt. The output looks like: ``` ## Relevant memories - User prefers Bedrock Nova Pro for inference tasks - Default deployment region is us-east-1 - Project uses CDK for infrastructure ``` Combine both approaches: use `memory_instructions()` for ambient context, leave the tools in place for the agent to actively learn new things during the session. ### Global configuration If you're instantiating agents across multiple modules, threading the `hindsight_api_url` and other params everywhere gets tedious. Use `configure()` once at startup: ```python from hindsight_strands import configure, create_hindsight_tools configure( hindsight_api_url="http://localhost:8888", api_key="YOUR_API_KEY", # if auth is enabled budget="mid", # low / mid / high tags=["env:prod"], # tag everything written this session ) # Elsewhere, no need to pass connection details tools = create_hindsight_tools(bank_id="user-123") ``` `budget` controls how many tokens are spent on recall — `low` is fast and cheap, `high` pulls more context. Default is `mid`. ### Tag-based scoping Tags let you organize and filter memory across banks or environments: ```python tools = create_hindsight_tools( bank_id="user-123", hindsight_api_url="http://localhost:8888", tags=["env:prod", "team:platform"], # applied to everything retained recall_tags=["env:prod"], # only recall memories with this tag recall_tags_match="all", # must match all tags (vs "any") ) ``` This is useful if you're sharing a bank across environments or teams and want to keep recall scoped. ### Selective tools You don't have to enable all three tools. A read-only agent is useful for serving end users who should be able to query memory but not modify it — for example, a customer-facing assistant that reads from a shared knowledge bank maintained by a separate process: ```python tools = create_hindsight_tools( bank_id="user-123", hindsight_api_url="http://localhost:8888", enable_retain=False, enable_recall=True, enable_reflect=True, ) ``` A write-only agent is useful for ingestion pipelines — a background agent that processes documents, support tickets, or logs and stores structured facts without exposing recall: ```python tools = create_hindsight_tools( bank_id="user-123", hindsight_api_url="http://localhost:8888", enable_retain=True, enable_recall=False, enable_reflect=False, ) ``` --- ## Pitfalls & Edge Cases **The agent won't retain unless it decides to.** Memory tools are just tools — the agent calls them based on context. If you ask it to "remember X," it will. If you have a casual exchange and expect it to file something away, it probably won't. Use `memory_instructions()` to inject what matters rather than hoping the agent retains it autonomously. **`bank_id` is your consistency primitive.** If two sessions use different `bank_id` values, they have separate memory — full stop. Make sure user identity maps reliably to `bank_id` across your application. A common bug is using a session ID instead of a user ID, which silently gives every session a blank slate. **Asyncio conflicts with Strands.** Strands runs its own event loop. The `hindsight-strands` package handles this by executing Hindsight client calls in a `ThreadPoolExecutor` rather than `asyncio.run()`. If you're building on top of this or writing custom tooling, don't try to `await` Hindsight calls directly inside a Strands tool — you'll get "event loop already running" errors. **`memory_instructions()` is synchronous and blocking.** It runs a network call at agent initialization time. Don't call it inside a hot loop or on every request if the agent is being reconstructed frequently. Cache the result if the query is static and you're spinning up many agents. **Reflect is expensive.** `hindsight_reflect` synthesizes an answer from memory rather than just retrieving facts. It's more useful for open-ended queries but will use more tokens. If cost is a concern, leave it disabled and use recall instead. --- ## Tradeoffs & Alternatives **When not to use this:** If your sessions are fully self-contained and users expect no continuity, adding memory tooling introduces unnecessary complexity and latency. Not every agent needs persistent state. **Conversation history vs. long-term memory:** Strands manages within-session context automatically via its message history. `hindsight-strands` is for cross-session memory — facts that need to survive beyond a single conversation. Don't use it as a substitute for prompt history. **Alternatives:** - **DynamoDB / RDS**: You can roll your own persistence by writing session data to a database and loading it back. More control, more code, no semantic search. - **Amazon Bedrock AgentCore Memory**: AWS's fully managed memory service for Bedrock agents. Handles both short-term (within-session) and long-term (cross-session) memory automatically. Tighter AWS integration but locks you into the Bedrock agent runtime — you can't use it with a Strands agent backed by a non-Bedrock provider. - **LangChain memory primitives**: If you're already on LangChain, its memory abstractions are more mature. `hindsight-strands` exists because Strands has its own tool protocol that LangChain memory doesn't plug into. --- ## Recap Strands agents are stateless by default. `hindsight-strands` adds three native memory tools — retain, recall, reflect — that let your agent manage its own long-term memory using the same `@tool` protocol it uses for everything else. `memory_instructions()` handles the case where you want context pre-loaded rather than retrieved on demand. The mental model: memory banks are scoped by `bank_id`, tags let you filter across environments, and `budget` controls the recall depth. Everything else is just a Strands agent with extra tools. --- ## Next Steps - [hindsight-strands on PyPI](https://pypi.org/project/hindsight-strands/) - [Hindsight docs: Strands integration guide](https://hindsight.vectorize.io/sdks/integrations/strands) - Run Hindsight locally with Docker and try the quick start above - Explore `memory_instructions()` for pre-warming agents in customer-facing applications - Check out the [CrewAI](https://hindsight.vectorize.io/blog/2026-03-02-crewai) and [LangGraph](https://hindsight.vectorize.io/blog/2026-03-24-langgraph-longterm-memory) integration posts for comparison on similar patterns