Is Your AI Agent Temporalizable? Durable Workflows with Quarkus and LangChain4j
with Les JacksonLes Jackson from Temporal joined a live build of a Quarkus vacation-approval agent. We killed the LLM and the app itself mid-workflow to see whether Temporal's durable execution could bring it back without hand-written recovery code. It did, and coined a word along the way.

YouTube
Is Your AI Agent Temporalizable? Durable Workflows with Quarkus and LangChain4j
Load YouTube video
This video is embedded from YouTube and will only be loaded after your consent. When loading it, personal data may be transmitted to YouTube or Google and cookies may be set.
More details are available in the privacy policy.
Project Source
Working Repository
Explore prompts, instructions, and examples used in the live modernization workflow.
Open repositorySession Timeline
- 00:00Introduction
- 01:11Welcoming Les Jackson
- 04:02What durable execution means
- 06:50When Temporal is overkill
- 08:46Temporal vs. cron jobs and job queues
- 11:02The determinism misconception
- 13:22The most common mistake with Temporal
- 14:51Moving to the live demo
- 17:00The architecture: Quarkus, Temporal server, Postgres, Ollama
- 20:22Submitting a vacation request, no Temporal yet
- 23:03Inside the vacation-request code
- 24:12What actually counts as non-deterministic
- 29:22The LangChain4j LLM call
- 36:17The human decision step
- 39:23Crashing Ollama with no safety net
- 44:58Asking GitHub Copilot to temporalize the app
- 48:19Reviewing the generated workflow and activities
- 01:01:53First run with Temporal, and a crash
- 01:03:51Diagnosing the ActivateRequestContext bug
- 01:09:57Trying again after the fix
- 01:11:24Waiting on a human-in-the-loop signal
- 01:12:27Killing and restarting the worker mid-workflow
- 01:17:28Testing retries by killing Ollama again
- 01:19:41Retry policies and what the Temporal UI shows
- 01:25:20Don't double up on retries
Les Jackson, staff developer advocate at Temporal, joined me straight off a business trip to build a vacation-approval system in Quarkus and LangChain4j, then retrofit it onto Temporal's durable execution platform. The question we kept coming back to: if an AI agent's LLM call fails, or the whole app crashes mid-request, what actually survives?
What is Temporal?
Temporal calls itself a durable execution platform. Les summed up the pitch and immediately punctured it:
Code will always fail. What Temporal does is help your code recover really well and really elegantly, without you as the developer having to do too much.
Under the hood, Temporal is an event-sourcing system. It stores every event a workflow emits. When a service crashes, those events replay to reconstruct exactly where things left off. Your code never has to know how to resume itself.
Two building blocks make that possible. A workflow is your business logic: the loop that decides what happens next, and it has to be deterministic so Temporal can safely replay it. An activity is a call to something else, an LLM, a REST API, a database, and it can be as non-deterministic as it likes.
A common misconception creeps in right here. Hearing "the workflow must be deterministic" and assuming that rules out AI agent loops entirely. It doesn't. The loop only orchestrates. The unpredictable parts, the ones that time out or answer differently every time, live in the activities it calls.
Temporal is overkill for a small app with no external calls that returns fast. It earns its keep once you're calling other services, running anything long-lived, or protecting something that genuinely cannot fail, Les's example was banking workloads. It doesn't replace cron or job queues one for one, but it sits above them: anything you'd schedule with cron, Temporal can also orchestrate, plus the crash recovery cron never gave you for free.
How the session went
The baseline had no safety net. The demo: a vacation-request app in Quarkus, LangChain4j, and a local Llama 3.2 model in Ollama. An employee submits a request, the app checks it against everyone else's vacation dates, LangChain4j asks the LLM to review it, and a manager's decision triggers a LangChain4j-drafted notification email. All state lived in a ConcurrentHashMap, in memory only, shown running at 20:22.
Killing Ollama mid-call proved the point. At 39:23, the app returned a plain internal server error. The request was simply gone: no retry, no memory it had ever existed.
Copilot did the retrofit in a single prompt. After installing the official skill-temporal-developer skill, so Copilot worked from Temporal's real conventions instead of guessing, one prompt at 44:58 generated a workflow, wrapped both LangChain4j calls as activities, and wired application.properties with the Temporal target, namespace, and task queue.
It failed on the first try, and that failure is the useful part. At 1:03:51, the Temporal UI's event history named the exact problem: a ContextNotActiveException, because the LangChain4j calls now ran on an activity thread outside any HTTP request, where Quarkus's CDI request scope doesn't exist. The fix was one annotation, @ActivateRequestContext. Les's bigger point: don't ask an agent to "temporalize" an app in a single big-bang prompt. Ask for a migration plan first, then apply it in steps.
The real test was survival, not success. With the fix in, the Temporal UI showed a workflow parked in a running state at 1:11:24, waiting on a signal for the manager's decision. Killing the whole worker process at 1:12:27 and restarting it changed nothing: approving through the UI afterward picked the workflow up exactly where it left off, calling LangChain4j only for the notification, not the review already finished.
Retries have limits, and the demo hit them honestly. Killing Ollama again while a notification activity retried, at 1:17:28, showed the retries running live in the Temporal UI. Once the default policy's attempts ran out, the workflow failed and the request vanished, because the app itself never handled that failure state. Temporal retries a call for you. It doesn't fix your UI's assumptions about what always succeeds.
Three things that made this click
1. Retries remember where they were
Temporal's retries are durable, not in-process. Les's comparison: an in-process .NET retry library that crashes mid-retry restarts at attempt one once the service comes back. Temporal doesn't. A call on retry five of ten when the process died resumes at retry five.
That detail has a real cost: restarting a retry from scratch wastes an LLM call, not just time.
One live viewer added a detail worth keeping: leave LangChain4j's own chat-model retries on alongside Temporal's, and the two stack. Set the chat model's retries to zero. Let Temporal own retries alone.
2. Waiting is free
A workflow parked on a signal, waiting for a human, costs nothing while it waits and needs nobody watching it. That's what made the vacation-approval scenario more than a toy example: real approvals wait on real people, sometimes for days, and the workflow has to survive that gap the same way it survives a crash.
3. A real skill got Copilot further than expected
Loading the official Temporal skill before touching the code mattered. It found the right annotations, the retry-option shape, and the workflow and activity interfaces, without either of us pointing it at documentation.
It also introduced one real bug, the CDI context issue above. A skill makes the agent competent at a framework's conventions. It doesn't make the agent's output correct by default.
Final thought
Temporal didn't make the LLM smarter or the approval logic more sophisticated. It isn't free either: you still run a server and a database, and think in workflows and activities instead of plain functions.
What it bought us, for a genuinely small amount of code, was an agent that came back from a crash without either of us writing a line of recovery logic. If a piece of your own code calls something unreliable, and losing its state matters, ask the question we kept asking that night: is it temporalizable?
Useful links
- Temporal CLI setup
temporalio/skill-temporal-developer, the Copilot skill used live- Introducing the Temporal Developer Skill
- LangChain4j
- Quarkus
- Quarkus CDI reference, covering
@ActivateRequestContext - Ollama
- Temporal Community on GitHub, including the
temporal-ai-agentsample
Comments
Load comments from GitHub optionally
The comment section is provided via Giscus and GitHub Discussions. It will only be loaded after your explicit consent. When loading it, personal data such as your IP address and technical metadata may be transmitted to GitHub, and cookies or similar technologies may be set.
Please confirm first before loading the comment section.