Building Production AI Agents at Scale with Temporal and KGoose

block · Finance · 2026 · source Agentic: yes · promotional bias: low · source-backed · extracted with prompt 0.5.5 (schema v2)

Generated from extraction.json — do not edit by hand. Every claim cites verbatim source evidence; stated means the quote directly asserts the claim, implied means it supports it indirectly.

Problem

Domain: Production financial-services agent platform

Block needed to turn Goose-style local agents into KGoose, a shared service that could power Cash App, Square, and internal products while handling long-running sessions, tool calls, non-deterministic LLM outputs, financial-data compliance, and frequent AI workflow iteration.

Constraints:

Architecture

KGoose evolved from a stateless query API to a DynamoDB-backed synchronous chat API and then to an asynchronous Temporal-backed agent workflow. Clients push messages and poll for results. The API persists session/task/message state in DynamoDB, starts or updates a Temporal chat workflow for the session, and uses Temporal state variables to coordinate one task at a time. The workflow runs an await-driven agent loop, fetches messages from DynamoDB, calls the LLM through activities, stores responses, executes tool calls through MCP, and clears the current task when complete. Activities are split between chat/state/tool work and inference/model-resolution work. The platform adds lifecycle termination, interruption cleanup, human-in-the-loop patterns, context compaction, continue-as-new, model adapters, guardrails, error categorization, Get Version/replay testing, and monitoring workflows.

Diagram confidence: explicit (topology described by the source).

flowchart LR
  product-agents["Product agents"] --> kgoose-api["KGoose API"]
  kgoose-api --> dynamodb-session-store["DynamoDB session store"]
  kgoose-api --> temporal-chat-workflow["Temporal chat workflow"]
  temporal-chat-workflow --> dynamodb-session-store
  temporal-chat-workflow --> chat-activities["Chat activities"]
  temporal-chat-workflow --> llm-activities["LLM activities"]
  temporal-chat-workflow --> human-approval-surface["Human approval surface"]
  temporal-chat-workflow --> continue-as-new-lifecycle["Continue-as-new lifecycle"]
  temporal-chat-workflow --> monitoring-workflow["Monitoring workflow"]
  chat-activities --> dynamodb-session-store
  chat-activities --> mcp-tools["MCP tools"]
  chat-activities --> guardrail-services["Guardrail services"]
  llm-activities --> model-adapter-layer["Model adapter layer"]
  llm-activities --> guardrail-services
  message-compaction["Message compaction"] --> temporal-chat-workflow
  message-compaction --> dynamodb-session-store
  replay-and-versioning-tests["Replay and versioning tests"] --> temporal-chat-workflow
  monitoring-workflow --> temporal-chat-workflow
  monitoring-workflow --> dynamodb-session-store
  human-approval-surface --> product-agents
Drag to pan · scroll or use +/− to zoom · reconstructed from the source's described component topology

Components

Component Role Interacts with
KGoose API Client-facing service that accepts messages, validates configuration, starts workflows, and returns session IDs for polling. DynamoDB session store, Temporal chat workflow
DynamoDB session store Stores sessions, tasks, message history, tool payloads, structured UI outputs, token usage, and client-facing data. KGoose API, Temporal chat workflow, Product agents
Temporal chat workflow Long-running orchestration loop that coordinates tasks, interruptions, tool requests, LLM calls, and lifecycle policy. DynamoDB session store, Chat activities, LLM activities, Human approval surface, Continue-as-new lifecycle, Monitoring workflow
Chat activities Handle DynamoDB operations, reading and writing messages, and tool-call execution. DynamoDB session store, MCP tools, Guardrail services
LLM activities Perform inference-related work such as LLM calls, prompt construction, model resolution, failure categorization, and rate-limited traffic handling. Model adapter layer, Guardrail services, Temporal chat workflow
MCP tools Provide server-side tool integrations that the agent can call during tasks. Chat activities
Human approval surface Lets clients approve sensitive actions or execute structured recommendations outside the agent. Temporal chat workflow, Product agents
Guardrail services Detect complaints, escalations, sensitive PII, out-of-band statements, and agent-specific nonsense outputs. LLM activities, Chat activities
Message compaction Summarizes subsets of messages to reduce context-window pressure. Temporal chat workflow, DynamoDB session store
Continue-as-new lifecycle Rolls Temporal executions into fresh histories when event-history limits approach. Temporal chat workflow
Model adapter layer Keeps the agent loop model-agnostic and adapts model limitations such as missing image support. LLM activities
Replay and versioning tests Use Get Version and recorded histories to verify deterministic replay during prompt/tool/control-flow changes. Temporal chat workflow
Monitoring workflow Detects stuck non-deterministic workflows and recovers sessions by starting replacement chat workflows. Temporal chat workflow, DynamoDB session store
Product agents Money Bot, Manager Bot, G2, automations, subagents, and memory extraction workloads powered by the shared workflow. KGoose API, DynamoDB session store, Human approval surface

Data flow

A client sends a V3 push message with a message, model choice, and enabled tools. The API validates it, stores the session and task in DynamoDB, starts or finds the Temporal chat workflow, checks whether the session is busy, and calls the start-task update method. The workflow waits until a task is available, tool requests are resolved, and the session is not interrupted. It fetches current messages from DynamoDB, invokes the LLM through an LLM activity, saves the response, processes MCP tool calls through chat activities, and exposes updates to clients through DynamoDB-backed polling. Guardrails, context compaction, continue-as-new, interruption cleanup, model fallback, and monitoring workflows handle production failure cases.

4 supporting quotes
“The client calls V3 push messages with a message and some configuration like what model to use, what tools enable, we make some validations.”transcript, stated
“The API server persists the session and task are done with DB. Checks if a Temporal workflow is already running for that session ID”transcript, stated
“workflow await that blocks until three conditions are met. There is a task to process, if there are any tool requests that they're resolved”transcript, stated
“We fetched the latest set of messages from DynamoDB, call the LLM via an activity, save the response back to Dynamo”transcript, stated

Why this architecture

Block moved to an asynchronous Temporal architecture because synchronous agent loops timed out under real production tool use. Temporal was already trusted inside Block for durable tier-zero workflows, while DynamoDB was better suited for low-latency client-facing data. The final design intentionally separates orchestration from data and keeps workflows lean while activities hold business logic, retries, and mutable AI behavior, allowing a small team to share one workflow across customer-facing and internal products.

4 supporting quotes
“with one input, the LLM would result in dozens of tool calls that took a long time to execute on the server side.”transcript, stated
“We trust Temporal for its durability and reliability, which is key when you wanna have long running agent loops.”transcript, stated
“Temporal, for durability, retries and coordination and DynamoDB for fast and flexible data. The second principle has to do with keeping workflows lean and activities complex. And you've heard about this so you want your workflow to focus on orchestration”transcript, stated
“you want your workflow to focus on orchestration while offloading your heavy logic, retries and state management to activities.”transcript, stated

Models used: LLMs accessed through KGoose · Model X for fast quality responses · Separate vision-capable model adapter · External guardrail models · Anthropic models · OpenAI models

Scale

Alternatives considered (roads not taken)

Keep the V1 one-message query API.

Rejected because: It had no agent capability and forced clients to track message history themselves.

“send one message and get back a response from the LM. There is no agent, but with this client could build a very easy simple demo chat app. It was just a little bit painful because clients would need to keep track of the message history themselves. So in V2, two we introduced DynamoDB to keep track”transcript, stated
“client could build a very easy simple demo chat app. It was just a little bit painful because clients would need to keep track of the message history themselves. So in V2, two we introduced DynamoDB to keep track of chats and messages. V2 push messages was a synchronous stateful chat”transcript, stated

Keep the V2 synchronous stateful chat API.

Rejected because: Long tool-call chains frequently timed out and degraded production use.

“history themselves. So in V2, two we introduced DynamoDB to keep track of chats and messages. V2 push messages was a synchronous stateful chat API. So the user sends a message and waits for the full agent loop to complete. The burden is no longer for the caller to persist”transcript, stated
“with a synchronous setup of V2, that means that we were frequently seeing a bunch of timeouts. So for V3, we wanted to introduce an async pattern powered by Temporal. The client submits inputs through our API and then pulls for the outputs. For async,”transcript, stated

Store primary application data inside Temporal workflow state.

Rejected because: Temporal event histories and queries were poor fits for low-latency APIs, streaming, schema evolution, and data access outside the agent lifecycle.

“If we store the primary application data with Temporal, we would need to make additional considerations making sure that the application state would fit in the Temporal event history.”transcript, stated
“We would need to back our API calls with Temporal queries which are slower than DynamoDB queries. Streaming messages at least at the time was not something Temporal had native support for but that has changed if we all attended the keynote this morning with”transcript, stated

Terminate and rehydrate every workflow on every user message.

Rejected because: Rehydrating on every request would add latency, so Block chose an inactivity-based lifecycle compromise.

3 supporting quotes
“deprecate old logic. At the other extreme, because our session state is persistent in DynamoDB, we can safely terminate workflow executions and resume them on demand. However, doing this on every push messages request will introduce additional agencies due to workflow rehydration and state”transcript, stated
“doing this on every push messages request will introduce additional agencies due to workflow rehydration and state initialization.”transcript, stated
“request will introduce additional agencies due to workflow rehydration and state initialization. So as a compromise, we use an inactivity based lifecycle policy. Workflows can remain active while the session is hot and are terminated after one hour of an activity. And this threshold”transcript, stated

Let agents directly execute every sensitive action after approval.

Rejected because: For high-trust operations, Block sometimes returns structured recommendations to the client instead, simplifying auth by avoiding direct agent capabilities.

“the agent returns a structured recommendation with a suggested payload to the client. The client and the user are responsible for deciding whether to and how to carry the action. So here the responsibility of the agent changes. It's not asking for an”transcript, stated
“You wanna simplify the auth by avoiding granting the agent direct execution of capabilities. So for challenge two, I will talk about all sorts of limits. We're dealing with three systems in our agent loop. So we have Temporal history, LLMs and DynamoDB. Each”transcript, stated

Use Temporal's default continue-as-new suggestion alone.

Rejected because: The SDK suggestion was too conservative for KGoose's large agent payloads, so custom thresholds and future reference-passing were needed.

“it turns out that is continue-as-new, it's just a little bit too conservative for our use case. And so we use the custom threshold for triggering continue-as-new. The real problem here is that the activities that you see here are passing in full messages.”transcript, stated
“The real fix for this is to rewrite our activities in a way where we're not passing the full message contents but the references by ID.”transcript, stated

Patterns

durable-execution (reliability)

KGoose runs each long-lived chat session as a Temporal workflow, using async polling, update/signal/query methods, continue-as-new, lifecycle termination, and recovery workflows to keep agent loops durable.

3 supporting quotes
“So for V3, we wanted to introduce an async pattern powered by Temporal.”transcript, stated
“We trust Temporal for its durability and reliability, which is key when you wanna have long running agent loops.”transcript, stated
“We use Temporal's continue-as-new feature to carry over into a new execution with a fresh event history.”transcript, stated

tool-use-orchestration (agent_tools)

The workflow treats a task as a user message plus all subsequent tool calls, uses MCP for tools, and coordinates tool request resolution before the loop advances.

3 supporting quotes
“a task is a message and all the subsequent tool calls that's required to fulfill the intent of the message.”transcript, stated
“see if the LLM requested any tool calls and then process them through MCP.”transcript, stated
“executing tool calls, while LLM activity, handle all things inference related.”transcript, stated

human-in-the-loop-gate (safety)

KGoose supports both approval-gated executions and recommendation-only actions when clients should decide or execute sensitive operations.

3 supporting quotes
“So in KGoose, we offer two patterns of dealing with human in the loop.”transcript, stated
“we yield to the client to approve it before it is executed. And once approved, the agent resumes control”transcript, stated
“the agent returns a structured recommendation with a suggested payload to the client.”transcript, stated

guardrails-io-filtering (safety)

The platform checks user inputs for complaints/escalations, LLM outputs for PII and out-of-band statements, and agent-specific nonsense through regex and external guardrail services.

3 supporting quotes
“we're looking for things like complaints or user requested escalations and we have external models that detect these cases.”transcript, stated
“we're worried about things like sensitive PII being exposed, the LLM saying things out of band.”transcript, stated
“we deploy these regex based checks as a best effort to combat these issues.”transcript, stated

model-routing (model_operations)

KGoose keeps the agent loop model-agnostic, uses adapters for model capability gaps, and falls back to another model during provider outages or rate limits.

3 supporting quotes
“enabling model selection and keeping the agent loop model agnostic.”transcript, stated
“we built adapters to overcome limitations that a given model might have.”transcript, stated
“we'll attempt another LLM call on a different model but with the same request.”transcript, stated

observability-tracing (operations)

Block uses production conversation monitoring, error traces, per-session inspection, and stuck-workflow monitoring to discover and recover from agent failures.

3 supporting quotes
“a combination of human testing and like evaluations and just very active monitoring of production conversations.”transcript, stated
“looking through error traces and seeing what problems can come up.”transcript, stated
“we need to be able to provide more in depth observability into the agent behavior to our product teams”transcript, stated

eval-driven-development (evaluation)

Harness changes to prompts, tools, MCP definitions, and guardrails are paired with product-specific eval suites and human testing.

3 supporting quotes
“the prompts, the tools, the MCP definitions and all of those changes are paired with sort of an eval suite that is run for each product.”transcript, stated
“the eval suite allows us to catch these issues to see how much of it is fixed.”transcript, stated
“having mature and successful agents that actually help users accomplish their tasks require us to be able to evaluate agent performance”transcript, stated

agent-memory (context_management)

The chat workflow is used as a unit of LLM inference for memory extraction, while session state and message history persist outside the workflow in DynamoDB.

“The chat workflow also acts as a unit of LLM inference that we use for subagents, memory extraction.”transcript, stated
“Everything else is application data that we store in DynamoDB. So this is like our full message history”transcript, stated

Tradeoffs

Move KGoose V3 to asynchronous Temporal orchestration.

Chose Client submit-and-poll with a Temporal chat workflow over Synchronous stateful chat requests.

Rationale: Async orchestration avoids request timeouts from long tool chains and gives the workflow durable control over long-running agent loops.

3 supporting quotes
“So for V3, we wanted to introduce an async pattern powered by Temporal.”transcript, stated
“The client submits inputs through our API and then pulls for the outputs.”transcript, stated
“We trust Temporal for its durability and reliability, which is key when you wanna have long running agent loops.”transcript, stated

Separate orchestration state from application data.

Chose Temporal for lightweight orchestration, DynamoDB for messages and client-facing data over Putting full application data in Temporal event history.

Rationale: This gives Temporal the durability/retry role while DynamoDB handles low-latency APIs, streaming workarounds, schema evolution, and data access outside agent lifecycles.

3 supporting quotes
“The first one is separating orchestration from data. So if you take a look at the workflow state that we keep around session ID, current task ID, all that stuff, most of these are just references to the main entities that we talked about and or some Boolean state values.”transcript, stated
“They're lightweight and their main purpose is to orchestrate the workflow execution.”transcript, stated
“Temporal, for durability, retries and coordination and DynamoDB for fast and flexible data.”transcript, stated

Keep workflows lean and activities complex.

Chose Business logic, state management, retries, and model work in activities over Heavy workflow logic.

Rationale: Activity-level changes are easier to test, retry, time out, and evolve without constantly versioning deterministic workflow code.

3 supporting quotes
“The second principle has to do with keeping workflows lean and activities complex. And you've heard about this so you want your workflow to focus on orchestration while offloading your heavy logic, retries and state management to activities.”transcript, stated
“Activities are record Temporal primitive and we wanna leverage its core retrial policies in timeouts instead of rebuilding our own.”transcript, stated
“if you keep all your changes at the activity level, you don't have to worry about adding a workflow versioning as much.”transcript, stated

Use hot-session inactivity termination.

Chose Keep active sessions warm and terminate after one hour idle over Never-ending workflows or rehydrating on every request.

Rationale: The policy balances responsiveness for active sessions with operational flexibility for versioning and deprecating old logic.

“Workflows can remain active while the session is hot and are terminated after one hour of an activity.”transcript, stated
“this allows us to balance responsiveness and operational flexibility.”transcript, stated

Offer both direct approval and recommendation-only human-in-the-loop modes.

Chose Approval-gated execution for some tools and structured recommendations for others over One universal agent-executes-after-approval model.

Rationale: Recommendation-only mode supports many suggested actions and high-trust operations where granting direct agent execution would complicate authentication.

3 supporting quotes
“rather than blocking on the tool call. Instead, the agent returns a structured recommendation with a suggested payload to the client.”transcript, stated
“particularly helpful for product experiences where you want to present many of these actions and not have them block”transcript, stated
“You wanna simplify the auth by avoiding granting the agent direct execution of capabilities.”transcript, stated

Recover context-limit failures by changing the tool response rather than appending user-visible messages.

Chose Replace oversized tool output with a narrower-query error and retry over Expose immutable-context failure directly to users.

Rationale: The agent can adapt its next request while the original oversized message remains hidden for debugging.

3 supporting quotes
“the message history is immutable from the user's perspective and you can't add more messages.”transcript, stated
“we hide the original message but keep it for debugging, then we retry the call LLM call.”transcript, stated
“maybe I'll try to readjust by giving us fewer rows or filtering the data differently”transcript, stated

Accept that some non-deterministic workflow executions must be discarded.

Chose Recover user sessions by replacing stuck workflow executions over Assuming Get Version and replay tests prevent all determinism bugs.

Rationale: Block uses versioning and tests first, but production mistakes can still affect many workflows at once; session state in DynamoDB enables recovery with a new workflow.

3 supporting quotes
“This helps us with preventing bad changes from going out in prod, but in practice, it's really hard to test every single scenario in a unit test.”transcript, stated
“So non-determinism will still occur in production occasionally due to mistakes like the one I made.”transcript, stated
“accept the fact that sometimes you just need to throw away workflow executions”transcript, stated

Gotchas (what bit them)

Synchronous chat APIs time out once agents call many tools.

Root cause: One user input could produce dozens of server-side tool calls that took too long to complete before the response returned.

Resolution: Move to an asynchronous Temporal workflow where clients submit inputs and poll for outputs.

3 supporting quotes
“with one input, the LLM would result in dozens of tool calls that took a long time to execute on the server side.”transcript, stated
“with a synchronous setup of V2, that means that we were frequently seeing a bunch of timeouts.”transcript, stated
“The client submits inputs through our API and then pulls for the outputs.”transcript, stated

Long-lived workflows create lifecycle and versioning pressure.

Root cause: Keeping every chat workflow alive forever makes it harder to deprecate old logic, while rehydrating on every request adds latency.

Resolution: Use an inactivity-based lifecycle policy that keeps hot sessions active but terminates after one hour of inactivity.

3 supporting quotes
“long lived workflows would make it harder to deprecate old logic.”transcript, stated
“doing this on every push messages request will introduce additional agencies due to workflow rehydration and state initialization.”transcript, stated
“So as a compromise, we use an inactivity based lifecycle policy.”transcript, stated

Interrupting a tool-using agent can corrupt message history for the next LLM call.

Root cause: Different providers impose message-history rules, and open tool requests must be followed by corresponding tool responses.

Resolution: Check interruption flags at multiple loop points and stub or add cleanup messages so history remains valid.

3 supporting quotes
“we check the interruptive flag in multiple points in the loop before the LLM call, during the LLM call, before tool processing”transcript, stated
“tool requests must be followed by their corresponding tool response.”transcript, stated
“The goal is to always leave a message history in a valid state so that the flow can accept more input should the user comes back and resume the session.”transcript, stated

Temporal history grows too quickly if activities pass full messages.

Root cause: Agent turns can add megabytes of payload to event history, pushing close to continue-as-new thresholds.

Resolution: Use custom continue-as-new thresholds and plan to pass message references by ID or use large-payload storage.

3 supporting quotes
“each turn was adding about 3.4 megabytes of data into event history”transcript, stated
“every message basically meant that you were remaking a new execution.”transcript, stated
“The real fix for this is to rewrite our activities in a way where we're not passing the full message contents but the references by ID.”transcript, stated

Incorrect continue-as-new state initialization can cause duplicate work or loops.

Root cause: The original message was re-added to history during continuation, making the agent treat it as new work.

Resolution: Fix continuation state handling so only current work resumes and monitor continue-as-new counts.

3 supporting quotes
“we were adding the original message back into the message history”transcript, stated
“it was getting a new message and redoing all the work and sometimes this meant that it was infinitely continue-as-new-ing.”transcript, stated
“after fixing them, we were only seeing a very small number of continue-as-new executions in production.”transcript, stated

Large tool responses blow past LLM context limits after successful tool calls.

Root cause: Merchant catalogs can contain thousands of items, so the next LLM call receives too much tool-response content.

Resolution: Replace the oversized tool response with an error asking for a narrower query, hide the original for debugging, and retry the LLM call.

3 supporting quotes
“sessions were dying very quickly due to hitting LLM context limits.”transcript, stated
“some merchants just have a catalog with thousands of catalog items.”transcript, stated
“we replace the oversight tool response with an error message that says the tool was too large, please try again with a more specific query”transcript, stated

LLMs can emit tool doom loops or nonsensical outputs even when calls are valid.

Root cause: The model may repeatedly request the same valid tool or produce malformed/wrong-language responses despite advanced capabilities.

Resolution: Track cumulative tool calls, substitute dummy responses to steer the model, add regex checks, and eventually terminate tasks when loops persist.

3 supporting quotes
“it just keeps on asking for the same tool with the same arguments over again and again.”transcript, stated
“the doom loop will still happen over a hundred consecutive tool calls”transcript, stated
“even these super advanced models can respond to you in gibberish for no apparent reason”transcript, stated

Workflow non-determinism can make sessions inoperable.

Root cause: AI teams frequently change prompts, tools, and control flow while old workflows are still active and must replay with compatible code paths.

Resolution: Use Get Version, replay tests, and a monitoring workflow that recovers stuck sessions by starting a replacement chat workflow.

3 supporting quotes
“When a workflow fails for non-determinism, as we can see on this screenshot, it can no longer respond to queries and updates.”transcript, stated
“With AI systems we're constantly updating prompts, tools and control flow and increases the likelihood of introducing incompatible changes.”transcript, stated
“we added a monitoring Temporal workflow that detects stuck workflows by inspecting the event history.”transcript, stated

Evaluation

Block evaluates agent behavior through human testing, product-specific eval suites, active monitoring of production conversations, error traces, aggregate session inspection, per-session inspection, guardrail/generation-error bucketing, replay tests for Temporal determinism, and future deeper observability for product teams.

Metrics: Human testing results · Product-specific eval-suite outcomes · Production conversation monitoring · Error traces and error categories · Aggregate and per-session inspection · Recorded Temporal event-history replay tests · Stuck workflow detection · Agent performance observability still planned for deeper product-team use

5 supporting quotes
“a combination of human testing and like evaluations and just very active monitoring of production conversations.”transcript, stated
“observe the sessions at an aggregate level and also be able to like inspect per session what's happening.”transcript, stated
“the prompts, the tools, the MCP definitions and all of those changes are paired with sort of an eval suite that is run for each product.”transcript, stated
“the replay test then re-execute the recorded event histories using the latest code to make sure that the new code is determinism safe”transcript, stated
“we need to be able to provide more in depth observability into the agent behavior to our product teams”transcript, stated

Outcomes

The talk reports over 100 Temporal-backed services at Block, over 100 million weekly internal/Cash App activities, 300-400 actions per second, over 10,000 concurrent workflows in each namespace, one-hour idle workflow termination, 10-minute lifecycle checks, 50 MB/50,000-event Temporal limits, and some turns adding 3.4 MB to history. It does not disclose user counts, accuracy, cost, or revenue impact.

5 supporting quotes
“products that you see on this page are just some of the ongoing initiatives at the company right now, but you might be surprised to hear that they're all powered by a single platform”transcript, stated
“The chat workflow also acts as a unit of LLM inference that we use for subagents, memory extraction.”transcript, stated
“Our internal and Cash App use cases are at about over a hundred million weekly activities or the 300 to 400 actions per second range.”transcript, stated
“we're about over 10,000 workflows that's running concurrently in each namespace at any given time.”transcript, stated
“there's a large gap between the most basic form of an agent to scaling it to serve a production user facing product.”transcript, stated