Figure 2. Four building blocks, one customer AWS account. The browser SPA talks to two endpoints: the AgentCore runtime for streaming chat, and the chat history service for persistence.
Amazon Bedrock AgentCore Runtime. AgentCore is AWS's managed home for the part of a chatbot stack nobody wants to operate themselves: a streaming, JWT-authenticated agent endpoint with session storage, observability, and auto-scaling. You ship a container, you point it at an entrypoint, and you get back an /invocations URL that any client can call directly with a bearer token. The surface area leaves room for your agent code to do interesting things without making you re-implement transport, auth handoff, or scaling.
Strands Agents. Strands is the open-source Python agent framework AWS released alongside AgentCore, and that runs naturally inside it. It gives you an async-generator agent loop with first-class tool use, reasoning, streaming, and structured content blocks. If you have written against any modern agent SDK, you will recognize the shape. The reason it matters here is that it speaks Bedrock Converse natively, which means the model side of the stack stops being three layers of translation and becomes one.
Assistant-UI. An open-source React library for chat surfaces, with real community adoption and a clean external-store contract. It is the piece that let us ship a polished, accessible UI in days rather than weeks; Section 3 is where we make that argument in full.
A custom chat history service. This is the part AWS does not hand you, and it turned out to be where most of the interesting design decisions lived. We built a small FastAPI service running on Lambda (via the Lambda Web Adapter), backed by a single-table DynamoDB design that holds threads, messages, and a per-user conversation index. It is the system of record for conversations, and it is the seam where branching, editing, attachment metadata, and ownership boundaries all have to be modeled coherently.
Individually, each piece is interesting. The rest of the article is about what happens when you make them talk to each other. (That talking-to-each-other layer is what we packaged into our Rocketry framework, so the next deployment starts from working code rather than a blank repo.)
Standing on a Framework Instead of Reinventing the Chat UI
If there is one decision in this project that we would defend the hardest, it is not a clever architectural pattern. It is the decision about where to spend engineering effort, and where to refuse to spend it.
Every team that sets out to build a chatbot starts the same way: "we'll throw together a quick chat UI." Two weeks in, that team has discovered that a serious chat interface is roughly thirty features deep, none of it intellectually interesting, and all of it necessary. We chose to inherit that work rather than redo it.
Assistant-UI ships those primitives behind a clean external-store contract, which means we can pair its UI with whatever transport, persistence, and routing layers we choose without forking the library. We get a polished, accessible chat surface on day one, and we keep getting better as the community ships improvements.