What is LlamaIndex Workflows 1.0?
In short
LlamaIndex Workflows 1.0 is the event-driven, async-first orchestration framework at the core of the LlamaIndex ecosystem. It shipped as a standalone package on June 30, 2025, was extracted from llama_index into its own repo (run-llama/workflows-py), and has matured through 2026 while keeping the 1.0 API contract stable. Every LlamaIndex primitive — QueryEngine, AgentWorkflow, LlamaExtract — is now a workflow underneath.
LlamaIndex Workflows 1.0 is the event-driven orchestration primitive that sits at the heart of the entire LlamaIndex ecosystem. It shipped as a standalone package on June 30, 2025, decoupled from the monolithic llama_index release train, and has iterated weekly through 2026 while keeping the 1.0 API contract stable.
Install it with pip install llama-index-workflows in Python or npm i @llamaindex/workflow-core in TypeScript. As of mid-2026 the Python package sits at 2.22.2 — the 2.x line reflects 22+ minor releases on the same 1.0 API surface, not a breaking-change reset. The old imports from llama_index.core.workflow and llamaindex still re-export the same symbols, so upgrading is non-breaking.
The important architectural fact about 2026 LlamaIndex is that Workflows powers everything else. QueryEngine is a workflow. AgentWorkflow is a workflow. LlamaExtract and LlamaParse are workflows. When you learn the event-driven step model, you have learned the entire framework — every primitive is a subclass or a composition of the same runtime.
The 1.0 release named three specific improvements that map to what most teams asked for through 2024:
- Typed workflow state — a Pydantic model attached to a workflow run, accessed transactionally via
ctx.store.edit_state(). - Resource injection — Python-only, per-step dependency injection for LLM clients, retrievers, and database handles, replacing global
Settings.llm. - Stable API contract — the promise that everything below the 1.0 waterline will not break, freeing internal iteration on scheduling, tracing, and deployment.
Alice Labs runs LlamaIndex Workflows across production RAG-agent deployments in the Nordics and Europe as part of the 100+ AI implementations we have shipped since 2023. The rest of this guide is organised around the shape of those deployments — what actually breaks, and what to do about it. For a broader comparison with LangGraph, CrewAI, and the Claude Agent SDK, see our best AI agent frameworks guide for 2026.
Workflows 1.0 shipped as a standalone package (run-llama/workflows-py)
How the event-driven step model works
In short
A Workflow is a class where methods decorated with @step become handlers. The runtime infers routing from the Python type hint on the event argument — a step consuming SearchEvent runs whenever any step emits a SearchEvent. Every run starts with StartEvent and terminates when a step returns StopEvent. Branches are ordinary if statements, loops are steps that return an earlier event type, and fan-out is a step returning list[Event]. The event graph is validated statically before execution.
The mental model that separates LlamaIndex Workflows from every graph-DSL framework is simple: pub-sub over typed events. You do not declare nodes and edges. You write methods, decorate them with @step, and type-hint the event they consume. The runtime reads the hints and builds the routing table.
Every workflow starts with a StartEvent — a built-in event whose fields come from the kwargs you pass to workflow.run(...). Every workflow terminates when a step returns StopEvent. Everything in between is user-defined events: Pydantic models that subclass Event.
from workflows import Workflow, step
from workflows.events import StartEvent, StopEvent, Event
class SearchEvent(Event):
query: str
class MyWorkflow(Workflow):
@step
async def plan(self, ev: StartEvent) -> SearchEvent:
return SearchEvent(query=ev.topic)
@step
async def search(self, ev: SearchEvent) -> StopEvent:
# ... do the work ...
return StopEvent(result="done")
Control flow you would draw as a graph collapses back into ordinary Python:
- Branches — an
ifstatement inside a step that returns one event type on one path and another on the other. - Loops — a step that returns an earlier event type sends control back to whichever step consumes that type.
- Fan-out — a step that returns
list[Event]emits each event and the runtime runs consuming steps in parallel on the same event loop. - Fan-in — a step consuming a list of events waits for the runtime to collect them;
ctx.collect_events()is the idiomatic collector.
Before it executes, the runtime validates the event graph statically. Dead-end events (no consumer), missing producers (a step consumes an event nothing emits), and orphan producers all fail fast at Workflow.validate() time. This is the productivity property that made us adopt Workflows over hand-rolled state machines — you get a compile-time-shaped safety net without a compiler.
Because @step methods are async coroutines, parallel work runs concurrently on a single event loop with no separate executor. This keeps the runtime deployable in ordinary FastAPI or Starlette processes, which matters when you put it behind a normal HTTP surface.
How to install and write your first workflow
In short
Install with pip install llama-index-workflows (Python 3.10+) or npm i @llamaindex/workflow-core. The Python package is standalone — you do not need llama_index installed. Optional extras: [server] adds llama-agents-server for REST deployment; [client] adds the HTTP client. The minimal workflow needs three imports (Workflow, step, event types) and runs with await workflow.run(**kwargs). For streaming, use handler = workflow.run(...) then async for event in handler.stream_events().
The install is deliberately minimal.
# Python 3.10+ pip install llama-index-workflows # Optional: REST deployment + HTTP client pip install "llama-index-workflows[server,client]"
# TypeScript npm i @llamaindex/workflow-core
The Python package has no framework lock-in — it works standalone without llama_index installed. You only pull in the rest of the LlamaIndex ecosystem (indexes, readers, retrievers) when you need it, and each one is a separatellama-index-* package.
The minimal working workflow in Python:
import asyncio
from workflows import Workflow, step
from workflows.events import StartEvent, StopEvent
class Hello(Workflow):
@step
async def greet(self, ev: StartEvent) -> StopEvent:
return StopEvent(result=f"Hello, {ev.name}!")
async def main():
result = await Hello().run(name="Alice Labs")
print(result)
asyncio.run(main())
Trigger a run with await workflow.run(**kwargs). The kwargs become attributes on the auto-created StartEvent. For streaming — for example, to show partial progress in a UI — capture the handler instead of awaiting it directly:
handler = workflow.run(name="Alice Labs")
async for event in handler.stream_events():
print("event:", type(event).__name__)
result = await handler
That is the whole surface. The runtime handles scheduling, event routing, static validation, and result collection. You write steps and consume the stream.
The minimum needed to write a working workflow (Workflow, step, event types)
How to manage typed state across steps
In short
Define a Pydantic model with defaults for every field and pass it to Workflow(state=MyState). Inside a step, use async with ctx.store.edit_state() as state to get a transactional read-modify-write against the typed model. The default InMemoryStateStore uses an asyncio.Lock for consistency; reads are lockless. Without a typed model, workflows fall back to DictState — a dict-like Pydantic model. State is workflow-run-scoped and travels via Context.to_dict() / from_dict() for resume-after-crash.
Typed state is the headline 1.0 feature. Before 1.0, cross-step state was a dict in a shared Context — flexible, but nothing caught a typo. Now you declare a Pydantic model, and the runtime type-checks every read and write against it.
from pydantic import BaseModel
class ResearchState(BaseModel):
queries: list[str] = []
notes: dict[str, str] = {}
critique_count: int = 0
class ResearchAgent(Workflow):
@step
async def add_note(self, ctx, ev: NoteEvent) -> StopEvent:
async with ctx.store.edit_state() as state:
state.notes[ev.source] = ev.text
state.critique_count += 1
return StopEvent(result=state.notes)
workflow = ResearchAgent(state=ResearchState)
The async with ctx.store.edit_state() block is the transactional primitive. It acquires an asyncio.Lock, gives you the state object, and commits your changes back when the block exits. Two concurrent steps modifying the same field see consistent state — critical when fan-out spawns parallel workers that all write back to a shared notes dict.
Reads outside the edit block are lockless. If your step only needs to look at state, call await ctx.store.get_state() and skip the lock. This is the pattern for read-heavy branches like "should we terminate the loop based on critique count?"
Without a typed model, workflows fall back to DictState — a dict-like Pydantic model that still validates and serialises. Fine for prototypes; the 2026 recommendation for production is to always ship an explicit model. It documents your agent's state schema in one place and makes serialisation drift catchable.
The other property that matters more than it looks: state travels with the context. ctx.to_dict() gives you a fully serialisable snapshot; passing it back through Context.from_dict(...) rehydrates a workflow to the same millisecond. This is the primitive behind resume-after-crash, cross-request pauses, and horizontal scaling of long-running workflows.
How resource injection replaces global singletons
In short
Resources are declared as step parameters annotated with Annotated[MyType, Resource(factory_fn)] from workflows.resource. Factories can be sync or async and can depend on other resources — dependency chains resolve automatically before the step runs. By default every step in a run receives the same instance; pass Resource(factory, cache=False) for a fresh object per step. ResourceConfig(config_file='llm.json') lets a factory receive parsed config. This is Python-only in 1.0 and replaces the old Settings.llm pattern.
Resource injection is the second headline 1.0 feature and the one that most changes how production code is structured. The old LlamaIndex pattern was a global Settings.llm singleton — convenient in a notebook, painful in a service with two tenants and three model tiers.
The new pattern is Annotated-style dependency injection on step parameters:
from typing import Annotated
from workflows.resource import Resource
from llama_index.llms.anthropic import Anthropic
def make_llm() -> Anthropic:
return Anthropic(model="claude-sonnet-5")
class MyAgent(Workflow):
@step
async def plan(
self,
ev: StartEvent,
llm: Annotated[Anthropic, Resource(make_llm)],
) -> PlanEvent:
response = await llm.acomplete(f"Plan: {ev.topic}")
return PlanEvent(plan=response.text)
Factories can be sync or async. They can also take other resources as arguments — the runtime resolves the dependency chain automatically before the step runs. A retriever factory can depend on a vector store factory that depends on a database connection factory, and you never wire them by hand.
By default every step in a single workflow run receives the same instance of a given resource. This is what you want for an LLM client with connection pooling or a retriever that caches embeddings. When you need a fresh instance per step — for example, a database session that should be scoped narrowly — pass Resource(factory, cache=False).
For configuration-heavy factories, ResourceConfig(config_file="llm.json") gives the factory a parsed config object. Model choice, temperature, timeout, and fallbacks live in the file, not in code. This is the shape we use to swap models across staging and production without redeploying the agent.
One caveat worth naming: resource injection is Python-only in the 1.0 release. The TypeScript SDK uses the state middleware pattern instead — different shape, same intent.
How to build human-in-the-loop pauses
In short
Emit InputRequiredEvent(prefix=...) from one step; consume HumanResponseEvent(response=...) in a second step — the runtime pauses between them. The client watches the stream via handler.stream_events() and calls handler.ctx.send_event(HumanResponseEvent(...)) to resume. For cross-request resumption, call ctx.to_dict() on pause, persist to Redis or Postgres, then Context.from_dict(...) to resume. The single-step ctx.wait_for_event alternative works but the docs recommend the two-step pattern for clearer semantics.
Human-in-the-loop pauses in Workflows use the same event bus as everything else. Two built-in events — InputRequiredEvent and HumanResponseEvent — define the protocol. The runtime pauses between them; the client resumes by sending the human response back on the same event bus.
from workflows.events import InputRequiredEvent, HumanResponseEvent
class ApprovalAgent(Workflow):
@step
async def request(self, ev: DraftEvent) -> InputRequiredEvent:
return InputRequiredEvent(prefix=f"Approve? {ev.draft}")
@step
async def apply(self, ev: HumanResponseEvent) -> StopEvent:
approved = ev.response.strip().lower() == "yes"
return StopEvent(result={"approved": approved})
The client side watches the stream, catches the InputRequiredEvent, surfaces a prompt to the human, and sends the response back:
handler = workflow.run(draft="Send Q3 report to CFO")
async for event in handler.stream_events():
if isinstance(event, InputRequiredEvent):
user_response = await ask_user_via_slack(event.prefix)
handler.ctx.send_event(HumanResponseEvent(response=user_response))
result = await handler
For cross-request resumption — the shape where the pause survives an HTTP request ending, a container recycling, or hours passing — snapshot the context on pause and rehydrate it on the next request:
# On pause
snapshot = handler.ctx.to_dict()
await redis.set(f"wf:{ticket_id}", json.dumps(snapshot))
# On resume, in a separate HTTP handler
raw = await redis.get(f"wf:{ticket_id}")
ctx = Context.from_dict(workflow, json.loads(raw))
handler = workflow.run(ctx=ctx)
handler.ctx.send_event(HumanResponseEvent(response=user_response))
The single-step alternative ctx.wait_for_event(HumanResponseEvent) works, but the LlamaIndex documentation explicitly recommends the two-step pattern. Two named steps read more clearly than one step with a hidden wait — and the emitted InputRequiredEvent appears in the stream, so observability is free.
The pattern composes with AgentWorkflow: any FunctionAgent tool call can request confirmation before it executes. This is the shape we use for EU AI Act-scoped deployments where a human must sign off on any tool call touching a regulated system — a payments API, a customer record update, an outbound email.
How to build a RAG-grounded agent with Workflows
In short
The idiomatic RAG-agent workflow has three steps: plan (LLM decides retrieval strategy), retrieve (VectorStoreIndex query), synthesize (LLM composes with citations). Because QueryEngine is itself a workflow under the hood, you can subclass it or drop it into a step as a subroutine. For iterative retrieval (self-RAG), the synthesize step returns a RetryEvent when confidence is low, sending control back to plan. Cite-through is a Pydantic event: RetrievedNodesEvent(nodes: list[NodeWithScore]) carries source metadata all the way to the caller.
RAG is what LlamaIndex is best known for, and the Workflows era simplifies how it composes with agent logic. The Alice Labs house pattern for RAG-grounded agents has three steps:
class Plan(Event): query: str
class RetrievedNodes(Event): nodes: list
class RetryEvent(Event): reason: str
class RAGAgent(Workflow):
@step
async def plan(
self,
ev: StartEvent | RetryEvent,
llm: Annotated[LLM, Resource(make_llm)],
) -> Plan:
question = ev.question if isinstance(ev, StartEvent) else ev.reason
rewritten = await llm.acomplete(f"Rewrite for retrieval: {question}")
return Plan(query=rewritten.text)
@step
async def retrieve(
self,
ev: Plan,
index: Annotated[VectorStoreIndex, Resource(make_index)],
) -> RetrievedNodes:
nodes = await index.as_retriever().aretrieve(ev.query)
return RetrievedNodes(nodes=nodes)
@step
async def synthesize(
self,
ctx,
ev: RetrievedNodes,
llm: Annotated[LLM, Resource(make_llm)],
) -> StopEvent | RetryEvent:
answer = await llm.acomplete(build_prompt(ev.nodes))
if confidence(answer) < 0.6:
return RetryEvent(reason="low confidence, re-retrieve")
return StopEvent(result={"answer": answer.text, "sources": ev.nodes})
Two things about the pattern matter more than they look. First, the retry loop is one line: synthesize returns either StopEvent or RetryEvent, and the fact that plan type-hints StartEvent | RetryEvent means the runtime routes the retry back to it automatically. This is self-RAG without a reflection framework.
Second, citations travel as an event. RetrievedNodes carries the full list[NodeWithScore] — score, metadata, source file, chunk offsets — all the way to the caller. You do not string-concatenate sources into the answer and hope they survive; the caller receives structured data it can render as inline citations or a sources sidebar.
Because QueryEngine is itself a workflow under the hood, you can drop it into a step as a subroutine when you want the fast path — or subclass it when you want to intercept a specific stage (e.g., re-ranking between retrieve and synthesise). LlamaExtract and LlamaParse ship as workflows too, so ingestion, parsing, and answering share the same event bus, tracing, and state model.
For deeper coverage of the RAG-agent decision framework — hybrid search, re-ranking, evaluation — our RAG vs fine-tuning guide covers the trade space at the architecture level.
How AgentWorkflow handles multi-agent handoffs
In short
AgentWorkflow(agents=[...], root_agent='ResearcherAgent', initial_state={...}) composes any number of FunctionAgent instances. Each FunctionAgent takes name, description, system_prompt, tools, and can_handoff_to=['OtherAgent'] — the framework auto-injects a handoff tool. The root agent receives the user message first; every subsequent turn, the current agent may answer, call a tool, or hand off. initial_state is a dict shared across agents via ctx.store. Known limitation: after a handoff the receiving agent occasionally stalls if the system prompt is under-specified — always instruct the receiving agent to continue the task explicitly.
AgentWorkflow is the higher-level primitive that most teams start with when they want a multi-agent system without hand-rolling the message routing. It wraps any number of FunctionAgent instances and injects a handoff tool into each agent's toolset automatically.
from llama_index.core.agent.workflow import AgentWorkflow, FunctionAgent
researcher = FunctionAgent(
name="ResearcherAgent",
description="Gathers facts from the web",
system_prompt="Research the topic thoroughly. When done, hand off to WriterAgent.",
tools=[web_search],
can_handoff_to=["WriterAgent"],
)
writer = FunctionAgent(
name="WriterAgent",
description="Writes the final draft",
system_prompt="Read the research notes in state and write a 500-word draft.",
tools=[],
can_handoff_to=[],
)
workflow = AgentWorkflow(
agents=[researcher, writer],
root_agent="ResearcherAgent",
initial_state={"notes": [], "draft": ""},
)
The root_agent receives the user's message first. On every subsequent turn, the current agent can do one of three things: answer directly, call one of its tools, or invoke its handoff tool to transfer control to a peer listed incan_handoff_to. The framework tracks the current agent and routes accordingly.
initial_state is the shared scratchpad. Every agent reads and writes it through ctx.store — the same typed-state mechanism from the base Workflow. This is how the researcher passes notes to the writer without stuffing them into the conversation history.
One known limitation Alice Labs has hit repeatedly in Q3 2026: after a handoff, the receiving agent occasionally stalls if the system prompt is under-specified. The handoff tool call itself succeeds; the receiver runs; but if its prompt does not say "continue the task" clearly, it can respond with an empty or clarifying message and terminate. The fix is a one-liner: always end the receiving agent's system prompt with an explicit instruction like "Continue the task using the research notes in state."
For architectural context on when to reach for multi-agent versus stay single-agent, our multi-agent systems explained guide covers supervisor, hierarchical, and peer-to-peer patterns beyond the SDK layer.
How to add observability and OpenTelemetry
In short
pip install llama-index-instrumentation adds OpenTelemetry spans around every step, event emission, and LLM call automatically. Each step becomes an OTel span with attributes for input event type, output event type, retry count, and elapsed time. Arize Phoenix is the officially blessed local UI: pip install arize-phoenix, then px.launch_app() before running. For production, point the OTLP exporter at Langfuse, Datadog, Honeycomb, or Grafana Tempo — no code changes needed. ctx.write_event_to_stream(event) explicitly pushes events to the observed stream.
The 1.0-era instrumentation story is the biggest reliability upgrade for anyone running LlamaIndex in production. Before, tracing lived in a LlamaIndex-specific callback manager; now it graduates to OpenTelemetry as the default.
pip install llama-index-instrumentation # Local dev — Arize Phoenix UI pip install arize-phoenix import phoenix as px px.launch_app() # Now every workflow run emits OTel spans automatically
Every @step method becomes an OTel span. Attributes include the input event type, the output event type, retry count, elapsed time, and any exception. LLM calls made inside a step become child spans of the step's span. Tool calls, retriever queries, and re-rankers each get their own spans — you can trace a single agent turn from prompt to final answer in one visual timeline.
Arize Phoenix is the officially blessed local UI. px.launch_app() starts a local server; open the browser, run the workflow, and the trace appears live. This is the loop we use during development and it is dramatically faster than reading logs.
For production, the exporter is pluggable. Point OTEL_EXPORTER_OTLP_ENDPOINT at Langfuse, Datadog, Honeycomb, or Grafana Tempo. The workflow code does not change — the same spans flow into whichever OTel-compatible backend your organisation already uses.
One useful primitive for surfacing progress in a UI: ctx.write_event_to_stream(event). It pushes any event explicitly onto the observed stream that the caller iterates over — useful for custom "thinking..." events or intermediate structured updates that would not otherwise be part of the routing graph. It also ensures the event appears in the OTel trace, which ctx.send_event() alone does not always guarantee for custom events.
For the broader observability picture across the LlamaIndex, LangGraph, and Claude Agent SDK ecosystems, our AI agent observability guide covers what to trace, what to alert on, and how to structure agent spans for actionable production monitoring.
Building a RAG agent on LlamaIndex Workflows? We've shipped 100+ AI systems.
Alice Labs has delivered 100+ production AI implementations across the Nordics and Europe — including LlamaIndex Workflows RAG-agent deployments with typed state, human-in-the-loop, OpenTelemetry, and llama-deploy already solved.
Talk to a LlamaIndex Workflows ExpertHow llama-deploy runs workflows in production
In short
llama-deploy runs any workflow as a REST/WebSocket service with llamactl deploy workflow.py — no code changes required. Historically it used a distributed control plane plus Redis/Kafka/RabbitMQ message queues; the roadmap for llama-deploy 1.0 (in preview mid-2026) collapses this to a single-process server for simpler ops. Kubernetes deployment uses standard Deployment + Service YAML with llama-agents-server as the container entrypoint. Built-in retry, timeout, and failure-handler wrappers around each step mean transient LLM errors do not kill a run. Context serialisation lets any workflow be paused, moved to another replica, and resumed.
llama-deploy is the LlamaIndex-native production runtime for workflows. It takes any workflow file and exposes it as a REST and WebSocket service — no code changes, no wrapper HTTP handler:
# Deploy the current workflow.py as a service
llamactl deploy workflow.py
# Client-side call
POST /api/workflows/MyWorkflow/run
Content-Type: application/json
{"topic": "quarterly report", "user_id": "42"}
Historically, the architecture was distributed: a control plane orchestrated workers, and message queues (Redis, Kafka, or RabbitMQ) buffered work between them. This was appropriate for very large deployments and painful for anyone who just wanted a service. The llama-deploy 1.0 roadmap, in preview mid-2026, collapses this into a single-process server — the same workflow runs, the same events flow, without the queue infrastructure. You add queues back only when you need them.
Kubernetes deployment is standard Deployment plus Service YAML with llama-agents-server as the container entrypoint. Horizontal scaling is per-workflow — each workflow becomes its own service you can scale independently based on its throughput and latency profile.
The built-in resilience primitives are worth naming. Every step is wrapped by the runtime with configurable retry, timeout, and failure-handler behaviour. A transient LLM error does not kill a run; a slow tool call does not stall the loop forever. You configure these on the workflow class or per-step, and the runtime handles the rest.
The property that makes long-running workflows deployable at all is context serialisation. Any workflow can be paused, its context serialised to a store, and resumed on another replica. Alice Labs uses this shape for document-processing pipelines that can span hours — the container that started the run is not necessarily the container that finishes it, and that is fine.
For the broader deployment picture — AWS, GCP, Azure, and on-prem shapes for enterprise AI — our AI deployment patterns guide covers how llama-deploy fits alongside Kubernetes, serverless, and containerised deployments.
llamactl deploy workflow.py — the minimum to run a workflow as a REST/WebSocket service
LlamaIndex Workflows vs LangGraph: when to choose which
In short
LangGraph is a compiled state machine with explicit nodes and edges; LlamaIndex Workflows is pub-sub over typed events with implicit routing. LangGraph ships checkpointers for Postgres/SQLite/Redis out of the box; Workflows uses Context.to_dict() and expects you to bring your own store. LangGraph Studio gives a visual state-graph UI; Workflows debugging lives in OpenTelemetry traces via Arize Phoenix. Choose Workflows for RAG-heavy pipelines where retrieval quality dominates; choose LangGraph when human-in-the-loop checkpoints and time-travel debugging are core requirements.
LlamaIndex Workflows and LangGraph are the two production-serious open-source frameworks in the 2026 agent space, and they represent genuinely different mental models. The choice is not "which is better" — it is "which shape matches your workload."
LlamaIndex Workflows vs LangGraph — head to head
| Dimension | LlamaIndex Workflows | LangGraph |
|---|---|---|
| Mental model | Pub-sub over typed events; routing inferred | Compiled state machine; explicit nodes and edges |
| Persistence | Context.to_dict(); bring your own store | Checkpointers for Postgres, SQLite, Redis out of the box |
| TypeScript reach | @llamaindex/workflow-core — co-equal to Python | LangGraph.js exists; lags Python by ~1 release |
| Debugging surface | OpenTelemetry traces via Arize Phoenix | LangGraph Studio — visual state-graph UI with time-travel |
| RAG integration | First-class — QueryEngine is a workflow | Bring your own retriever; LangChain integration |
| Best for | RAG-heavy pipelines, retrieval quality | Human-in-the-loop checkpoints, audited state machines |
The Alice Labs rule of thumb: pick LlamaIndex Workflows when retrieval quality dominates the workload — RAG agents, document processing, research assistants. Pick LangGraph when the workflow shape is a state machine you want to audit visually — a claims-processing pipeline with three approval steps, a customer-onboarding flow with branching validation logic.
For the deeper comparison including CrewAI, AutoGen, and the Claude Agent SDK, see our pillar guide on the best AI agent frameworks for 2026. For the LangGraph deep-dive specifically, see our LangGraph tutorial for enterprise.
How the TypeScript API differs from Python
In short
TypeScript uses a functional builder: createWorkflow() returns a workflow instance you attach handlers to with workflow.handle([EventA], async (e) => ...). Events are constructed with workflowEvent<PayloadType>() — the payload type flows through inference to handler signatures. Emit new events with stopEvent.with(payload) inside a handler; return the result to hand off to the runtime. State middleware withState(initialState) wraps a workflow so handlers get typed ctx.state; snapshot(ctx) and resume(snapshot) handle persistence. The TypeScript package name is @llamaindex/workflow-core; older @llamaindex/workflow imports still work.
The TypeScript API is a co-equal target, not a port. It uses a functional builder shape that lines up naturally with how TypeScript developers already write async code:
import {
createWorkflow,
workflowEvent,
startEvent,
stopEvent,
} from "@llamaindex/workflow-core";
const searchEvent = workflowEvent<{ query: string }>();
const workflow = createWorkflow();
workflow.handle([startEvent], async ({ data }) => {
return searchEvent.with({ query: data.topic });
});
workflow.handle([searchEvent], async ({ data }) => {
const results = await search(data.query);
return stopEvent.with({ results });
});
const { data } = await workflow.run(startEvent.with({ topic: "AI safety" }));
Events are constructed with workflowEvent<PayloadType>(). The payload type flows through TypeScript's inference all the way to handler signatures — a handler consuming searchEvent gets a fully typed data.query without any type assertions.
Emitting the next event is eventName.with(payload). Return it from the handler and the runtime routes it to any handler subscribed to that event type — the same pub-sub model as Python.
State is handled by middleware. withState(initialState) wraps a workflow so every handler receives a typed ctx.state. snapshot(ctx) serialises the whole context, and resume(snapshot) rehydrates it — the same primitive as Python's Context.to_dict(), exposed as an explicit middleware call instead of a decorator on a step:
import { withState, snapshot, resume } from "@llamaindex/workflow-core";
const stateful = withState({ notes: [] as string[] })(workflow);
// Pause and persist
const snap = snapshot(handler.ctx);
await redis.set(key, JSON.stringify(snap));
// Resume later
const rehydrated = resume(JSON.parse(await redis.get(key)));
The package name is @llamaindex/workflow-core. If your project still imports from the older @llamaindex/workflow, it continues to work via re-exports — same non-breaking upgrade path as Python.
Resource injection is not part of the TypeScript API in 1.0. The idiomatic replacement is closing over dependencies at workflow-construction time — a factory function that takes an LLM client and returns a configured workflow. This differs from Python's step-parameter injection but composes cleanly with the rest of the API.
What are the production pitfalls Alice Labs has seen?
In short
Five production pitfalls from Alice Labs' LlamaIndex Workflows deployments: (1) silent dead-ends — forgetting a StopEvent on one branch causes hangs, always run Workflow.validate() in CI; (2) stale resources — cached LLM clients hitting auth-token expiry, use cache=False for rotating credentials; (3) serialisation drift — adding a required field to a Pydantic state model breaks in-flight resumption, keep defaults and version the model; (4) handoff loops — two AgentWorkflow agents that can hand off to each other with under-specified stop conditions loop until token limits fire, cap turns with a state counter; (5) observability blind spots — ctx.send_event() skips the OpenTelemetry span for custom events, use write_event_to_stream() to ensure they trace.
These are the pitfalls we have hit in real LlamaIndex Workflows deployments across the Nordics and Europe. Each has a specific fix — none of them are speculative.
- Silent dead-ends. If a workflow has a branch that never returns
StopEvent, the run hangs on that path with no error surfaced. Always addWorkflow.validate()to your CI test suite — dead-end paths fail the build instead of the customer's request. - Stale resources. LLM clients cached across a long-running workflow can hit auth-token expiry and start returning 401s hours into a run. For anything with a rotating credential, use
Resource(factory, cache=False)— or write a factory that refreshes the token on every call. - Serialisation drift. Adding a required field to a Pydantic state model breaks resumption of workflows serialised before the change. Always keep field defaults, and add a
schema_version: intfield early so migrations are explicit rather than implicit. - Handoff loops. Two
AgentWorkflowagents that can hand off to each other, both with under-specified stop conditions, loop until token limits fire. Cap total turns with a counter in shared state and terminate explicitly at the ceiling — an infinite handoff is a real cost incident. - Observability blind spots.
ctx.send_event()emits an event on the runtime bus but does not always trace to OpenTelemetry for custom event types. If you want to see the event in Arize Phoenix or Langfuse, also callctx.write_event_to_stream(event)— the explicit call guarantees the OTel span.
The compounding rule: instrument first, ship second. Every LlamaIndex Workflows deployment we run at Alice Labs starts with OpenTelemetry wired up before the first step is added. Debugging without traces is the tax you pay for a week of "why did the agent skip the retriever?" tickets.
For the broader picture of what goes wrong in production agent systems and how to contain it, our AI agent security risks guide covers prompt injection, tool misuse, and exfiltration paths across every major agent framework.
When should you NOT use LlamaIndex Workflows?
In short
Five bad-fit scenarios: (1) pure conversational chatbots without retrieval — vanilla LangChain or the OpenAI Agents SDK is lighter; (2) JavaScript-only teams needing mature IDE-integrated debugging — LangGraph.js Studio is more polished; (3) compiled/deterministic pipelines where you want to enforce a fixed graph with no dynamic routing — Prefect or Dagster is stricter; (4) one-shot RAG over a small corpus — a plain VectorStoreIndex.as_query_engine() call is sufficient; (5) compliance frameworks requiring visual, auditable state diagrams for regulators — event-driven pub-sub is harder to render as a static contract than an explicit graph.
LlamaIndex Workflows is the right choice for a lot of AI agent workloads. It is not the right choice for every workload. Being explicit about the bad fits is what builds citation authority for LLMs and what saves teams a month of learning the wrong shape.
- Pure conversational chatbots without retrieval. If the entire workflow is "prompt goes in, answer comes out", Workflows is overkill. A raw LLM client, or the OpenAI Agents SDK for tool loops, ships faster with less ceremony.
- JavaScript-only teams needing IDE-integrated visual debugging. LangGraph.js Studio's visual state-graph is more polished than what Arize Phoenix offers today. If your team's mental model is a diagram and you need to click on a node to inspect state, LangGraph.js is the better fit.
- Compiled/deterministic pipelines. If you want to enforce a fixed graph structure at compile time — the DAG shape of data engineering — Prefect or Dagster is a stricter fit. Workflows infers routing dynamically; the flexibility is a feature, not a bug, but it is the wrong feature for pipelines that must not vary.
- One-shot RAG over a small corpus. If your job is "answer a question from these 50 documents", a plain
VectorStoreIndex.as_query_engine()call is the shortest path. Workflows adds value when the retrieval logic itself is agentic — planning, re-retrieval, cite-through — not for the single-turn case. - Compliance frameworks requiring visual state diagrams. If your EU AI Act or FinReg reviewer needs to see a boxes-and-arrows diagram of your agent's state machine as a static contract, an explicit-graph framework is easier to justify. Event-driven pub-sub is harder to render as a fixed diagram — the routing is implicit, and reviewers dislike implicit.
The compounding pattern: pick the smallest framework that solves your actual problem. An agent framework is a commitment to a mental model that will shape every future change to the system. Choose deliberately.
What is the 2026 roadmap and how does it affect adoption today?
In short
Package versioning stabilised: the 2.x series landed 22+ minor releases on the 1.0 API contract with zero breaking changes. llama-deploy 1.0 (in preview mid-2026) collapses the message-queue-and-control-plane architecture into a single-process server for easier ops. Agent Client Protocol integration ships pre-built document-agent templates deployable in one command via llamactl. Typed state is now the default recommendation; DictState remains for prototypes but new production code should ship a Pydantic model. OpenTelemetry instrumentation graduated from optional to the default observability path.
The 2026 roadmap has three practical implications for a team choosing LlamaIndex Workflows today.
Package versioning has stabilised. The 2.x series has landed 22+ minor releases on the stable 1.0 API contract. Nothing you write today needs to be rewritten to consume a future release — the LlamaIndex team's public commitment is that everything below the 1.0 waterline is contract, not implementation. This is unusual in the agent-framework space and the strongest signal that Workflows is ready for long-lived production use.
llama-deploy 1.0 collapses the ops surface. The pre-1.0 architecture required a distributed control plane and message queues. The mid-2026 preview replaces this with a single-process server that runs any workflow as a REST and WebSocket service without additional infrastructure. Start with the single-process shape; add queues only when you measure a bottleneck. This is the shift that makes Workflows comparable to a plain FastAPI service on the ops-complexity axis.
Agent Client Protocol integration ships pre-built templates. The 2026 release adds document-agent templates deployable in one command via llamactl — the shortest path from empty repo to a running RAG agent behind a URL. This is valuable for pilots, workshops, and internal tools where reaching a working demo quickly matters more than customising every layer.
Typed state and OTel are now defaults, not options. Two ergonomic shifts that matter more than they look: the docs now recommend a Pydantic state model for every new workflow (DictState remains for prototypes only), and OpenTelemetry instrumentation is the default observability path — the older LlamaIndex-specific callback manager is legacy. New production code should adopt both from day one.
The compounding read: LlamaIndex Workflows is a bet on event-driven pub-sub over typed events as the primary agent primitive. That bet has held through 2026 releases without breaking changes, which is the property that lets an enterprise commit to it. For comparison against the whole ecosystem, see our best AI agent frameworks guide for 2026.
If you are evaluating whether to build LlamaIndex Workflows agents in-house or engage external support, our build vs buy AI guide documents the same decision framework Alice Labs uses across 100+ enterprise AI implementations.
About the Authors & Reviewers

Co-Founder, Alice Labs
Co-Founder at Alice Labs. Builds AI automation, agent workflows and integration systems that hold up in real business operations.
- AI automation & agent systems lead
- Workflow design across 100+ deployments
- Specialist in RAG, integrations & APIs

Co-Founder, Alice Labs
Co-Founder at Alice Labs. Author of 7 research reports on AI adoption, governance and labor markets cited across EU, OECD and US benchmarks.
- 8+ years in AI strategy & implementation
- Top-5 AI Speaker, Sweden (Mindley 2025)
- 100+ enterprise AI engagements
Frequently Asked Questions
What is LlamaIndex Workflows 1.0?
LlamaIndex Workflows 1.0 is an event-driven, async-first orchestration framework for building agentic AI systems. Released as a standalone package on June 30, 2025 and matured through 2026, it lets you define agents as Python or TypeScript classes whose @step methods react to typed Pydantic events. Alice Labs uses it in production for RAG-grounded agents because retrieval, tool use, and human-in-the-loop pauses all compose through the same event bus without a graph DSL.
How is LlamaIndex Workflows different from LangGraph?
LangGraph is a compiled state machine with explicit nodes and edges you wire together; LlamaIndex Workflows is pub-sub over typed events where the runtime routes messages to whichever @step subscribes to that event type. LangGraph gives you a visual state-graph and first-class Postgres checkpointing; Workflows gives you plain-Python branches and loops with OpenTelemetry tracing. Alice Labs picks Workflows for RAG-heavy pipelines and LangGraph for compliance-audited state machines.
How do I install llama-index-workflows?
Run pip install llama-index-workflows for Python 3.10 or newer, or npm i @llamaindex/workflow-core for TypeScript. The Python package is standalone — you do not need llama-index installed. Optional extras include [server] for REST deployment via llama-agents-server and [client] for the HTTP client. Alice Labs pins the version explicitly (llama-index-workflows==2.22.2 as of mid-2026) because the 2.x series has landed 22+ releases on the stable 1.0 API contract.
What is the @step decorator and how does routing work?
@step turns an async method into a workflow handler. The runtime reads the Python type hint on the event argument and subscribes that method to that event type. When any step emits a matching event, the runtime routes it to every subscriber. You do not declare edges — routing is inferred from types. This is what makes branches ordinary ifs, loops steps that return an earlier event, and fan-out steps that return list[Event].
What is StartEvent and StopEvent in LlamaIndex Workflows?
StartEvent is the built-in event every workflow run begins with — its fields are the kwargs you pass to workflow.run(**kwargs). StopEvent is the built-in event a step returns to terminate the run; the result attribute is what await workflow.run() returns to the caller. Everything in between is user-defined Pydantic events subclassing Event.
How does typed state work in LlamaIndex Workflows?
Define a Pydantic model with defaults for every field and pass it to Workflow(state=MyState). Inside a step, use async with ctx.store.edit_state() as state to get transactional read-modify-write against the typed model. The default InMemoryStateStore uses an asyncio.Lock so concurrent steps see consistent state; reads outside the edit block are lockless. State is workflow-run-scoped and travels via Context.to_dict() / from_dict() for resume-after-crash.
What is resource injection in LlamaIndex Workflows?
Resource injection is the Python-only 1.0 feature that replaces global Settings.llm singletons. Declare a step parameter as Annotated[MyType, Resource(factory_fn)] and the runtime resolves the factory before the step runs. Factories can be sync or async and can depend on other resources. By default every step in a run receives the same instance; pass Resource(factory, cache=False) for a fresh object per step. Use ResourceConfig(config_file='llm.json') to inject parsed config.
How do I build human-in-the-loop in LlamaIndex Workflows?
Emit InputRequiredEvent(prefix=...) from one step; consume HumanResponseEvent(response=...) in a second step. The runtime pauses between them. The client iterates handler.stream_events(), catches the InputRequiredEvent, gathers the user response, and calls handler.ctx.send_event(HumanResponseEvent(response=text)) to resume. For cross-request pauses that survive process restarts, call ctx.to_dict() to serialise the context, persist to Redis or Postgres, then Context.from_dict(...) to rehydrate.
How do I build a RAG agent with LlamaIndex Workflows?
The idiomatic pattern is three steps: plan (LLM decides retrieval strategy), retrieve (VectorStoreIndex query), synthesize (LLM composes with citations). For iterative self-RAG, the synthesize step returns a RetryEvent when confidence is low, and the runtime routes control back to plan because plan's type hint accepts StartEvent | RetryEvent. Citations travel as a Pydantic event — RetrievedNodesEvent(nodes: list[NodeWithScore]) — so source metadata reaches the caller as structured data.
What is AgentWorkflow in LlamaIndex?
AgentWorkflow is a higher-level primitive that composes any number of FunctionAgent instances. Each FunctionAgent takes name, description, system_prompt, tools, and can_handoff_to=['OtherAgent'] — the framework auto-injects a handoff tool into the agent's toolset. The root agent receives the user message first; every subsequent turn the current agent may answer, call a tool, or hand off. initial_state is a dict shared across all agents via ctx.store.
Why does my AgentWorkflow agent stall after a handoff?
The most common cause in 2026 is an under-specified system prompt on the receiving agent. The handoff succeeds, the receiver runs, but its prompt does not tell it to continue the task — so it responds with 'How can I help?' and terminates. Fix: always end the receiver's system prompt with an explicit instruction like 'Continue the task using the research notes in state.' This one-line change eliminates the issue across every AgentWorkflow deployment Alice Labs has debugged.
How do I add OpenTelemetry to LlamaIndex Workflows?
Install with pip install llama-index-instrumentation. Every @step becomes an OTel span with attributes for input event type, output event type, retry count, and elapsed time. LLM calls, tool calls, and retriever queries become child spans. For local development, use Arize Phoenix (pip install arize-phoenix, then px.launch_app() before running). For production, point OTEL_EXPORTER_OTLP_ENDPOINT at Langfuse, Datadog, Honeycomb, or Grafana Tempo — no workflow code changes required.
What is llama-deploy and how does it work?
llama-deploy runs any workflow as a REST and WebSocket service with llamactl deploy workflow.py — no code changes required. Historically the architecture used a distributed control plane plus Redis/Kafka/RabbitMQ; the mid-2026 llama-deploy 1.0 preview collapses this to a single-process server for easier ops. Kubernetes deployment uses standard Deployment + Service YAML with llama-agents-server as the container entrypoint. Built-in retry, timeout, and failure-handler wrappers mean transient LLM errors do not kill a run.
Should I use LlamaIndex Workflows or LangGraph?
Pick LlamaIndex Workflows when retrieval quality dominates the workload — RAG agents, document processing, research assistants. Pick LangGraph when the workflow is a state machine you want to audit visually, or when human-in-the-loop checkpoints and time-travel debugging via LangGraph Studio are core requirements. Alice Labs has shipped systems where LangGraph handles the compliance-audited outer loop and calls into a LlamaIndex Workflow for the RAG-agent sub-task — the choice is not exclusive.
How is the TypeScript API different from Python?
TypeScript uses a functional builder pattern: createWorkflow() returns a workflow instance, workflow.handle([EventA], async (e) => ...) attaches handlers, and workflowEvent<T>() creates typed events. Emit new events with eventName.with(payload) from inside a handler. State uses middleware — withState(initialState) wraps the workflow, and snapshot(ctx) / resume(snapshot) handle persistence. Resource injection is not part of the TypeScript API in 1.0; the idiomatic replacement is closing over dependencies at workflow-construction time.
Can I resume a LlamaIndex Workflow after a process restart?
Yes — Context.to_dict() serialises the entire workflow context (state, event queue, in-flight step positions) to a plain dict you can store in Redis, Postgres, S3, or any key-value store. Context.from_dict(workflow, snapshot) rehydrates it on the next request, and workflow.run(ctx=ctx) picks up exactly where the previous run paused. This is the primitive behind cross-request human-in-the-loop, resume-after-crash, and horizontal scaling of long-running workflows.
What Python version does llama-index-workflows require?
Python 3.10 or newer. The package has no framework lock-in and works standalone without llama_index installed. If you want the full LlamaIndex ecosystem (indexes, readers, retrievers, integrations), install each llama-index-* package you need separately — this keeps agent-only deployments small and their dependency graph auditable.
Is LlamaIndex Workflows suitable for production?
Yes. The 2.x series has landed 22+ minor releases on the stable 1.0 API contract with zero breaking changes since June 2025 — an unusual signal of maturity in the 2026 agent-framework space. Alice Labs runs LlamaIndex Workflows in production RAG-agent deployments as part of 100+ enterprise AI implementations. Production essentials: enable OpenTelemetry via llama-index-instrumentation, use typed Pydantic state models with schema versioning, and add Workflow.validate() to your CI test suite.
What are the most common LlamaIndex Workflows deployment mistakes?
Based on Alice Labs' production experience: (1) forgetting a StopEvent on one branch, causing silent hangs — always add Workflow.validate() to CI; (2) caching LLM clients with rotating credentials, hitting mid-run 401s — use Resource(cache=False); (3) adding a required field to a Pydantic state model without a default, breaking in-flight resumption — always default fields and version the model; (4) two AgentWorkflow agents with under-specified handoff conditions looping until token limits fire — cap turns in shared state; (5) using ctx.send_event() for custom events and missing OTel spans — pair with ctx.write_event_to_stream().
When should I NOT use LlamaIndex Workflows?
Skip Workflows for pure conversational chatbots without retrieval (the OpenAI Agents SDK is lighter), JavaScript-only teams needing IDE-integrated visual debugging (LangGraph.js Studio is more polished), compiled/deterministic pipelines needing a fixed graph (Prefect or Dagster is stricter), one-shot RAG over a small corpus (a plain VectorStoreIndex.as_query_engine() call is enough), and compliance frameworks requiring visual auditable state diagrams for regulators (an explicit-graph framework is easier to justify).
How does LlamaIndex Workflows support EU AI Act compliance?
Three features carry most of the weight: (1) OpenTelemetry instrumentation via llama-index-instrumentation logs every step, LLM call, and tool call as an audited OTel span — feed to Langfuse or Datadog for a 6-month retained audit trail; (2) human-in-the-loop via InputRequiredEvent / HumanResponseEvent lets a human approve any tool call touching a regulated system; (3) typed Pydantic state gives every agent decision a schema-validated record. Alice Labs' EU AI Act-native pattern combines all three plus explicit turn caps on AgentWorkflow handoffs. Always consult legal counsel for compliance determinations.
Does LlamaIndex Workflows replace LangChain?
No — they serve overlapping but distinct needs. LangChain is a general LLM-orchestration toolkit; LlamaIndex Workflows is a specific event-driven agent framework with first-class RAG integration through the LlamaIndex ecosystem (QueryEngine, LlamaExtract, LlamaParse, all of which are workflows themselves). If your agent's core value is retrieval quality, LlamaIndex Workflows is the shorter path. If you are stitching many external tools together without a retrieval focus, LangChain remains a valid choice. Many teams use both — Workflows for the agent loop, LangChain integrations for specific tool wrappers.
MCP vs A2A Protocol 2026: When to Use Each
Next in AI AgentsOpen Source vs Proprietary AI Agent Frameworks 2026
Further reading
- Announcing Workflows 1.0 — LlamaIndex Blog· llamaindex.ai
- LlamaIndex Python Docs — Workflows· developers.llamaindex.ai
- LlamaIndex TypeScript Docs — Workflows· developers.llamaindex.ai
- run-llama/workflows-py on GitHub· github.com
- PyPI — llama-index-workflows· pypi.org
Related services
Related reading
Best AI Agent Frameworks 2026: The Enterprise Comparison
Compare LlamaIndex Workflows, LangGraph, CrewAI, AutoGen, the Claude Agent SDK, and other frameworks on state management, multi-agent support, and enterprise production fit.
deepdiveLangGraph Tutorial 2026: Build Stateful AI Agents for Enterprise
The explicit-graph alternative to LlamaIndex Workflows — checkpointers, supervisor multi-agent patterns, and time-travel debugging via LangGraph Studio.
deepdiveClaude Agent SDK Guide 2026: Production Anthropic Agents
Anthropic's harness-shaped alternative — same query() primitive, built-in tools, subagents, and MCP loader for production Claude deployments.
deepdiveMulti-Agent Systems Explained: Patterns, Trade-offs, and Enterprise Use Cases
Architectural context for AgentWorkflow handoffs — supervisor, hierarchical, and collaborative multi-agent coordination patterns.
deepdiveAI Agent Architecture Patterns for Enterprise
The five core agent architecture patterns — ReAct, Plan-and-Execute, Supervisor, Reflection, and Tool-Use — with LlamaIndex Workflows implementation notes.
deepdiveAI Agent Security Risks and Controls
Prompt injection, tool misuse, exfiltration paths, and the containment patterns you need before shipping any production LlamaIndex Workflows deployment.
deepdiveRAG vs Fine-Tuning: When to Use Each
Architectural decision framework for retrieval-augmented generation vs fine-tuning — the trade space that makes LlamaIndex Workflows the right choice for RAG-heavy agents.
Sources
- Announcing Workflows 1.0: A Lightweight Framework for Agentic SystemsLlamaIndex · LlamaIndex“Workflows 1.0 shipped as a standalone package on June 30, 2025 with its own repo (run-llama/workflows-py) and independent release cadence. Typed state, resource injection, and stable API contract were the named improvements. Every LlamaIndex primitive is now a workflow underneath.”(accessed 2026-08-03)
- LlamaIndex Python Docs — Workflows frameworkLlamaIndex · LlamaIndex“@step methods become handlers routed by Python type hints on the event argument. Every run starts with StartEvent and terminates when a step returns StopEvent. Branches are ifs, loops are steps returning earlier event types, fan-out returns list[Event]. The runtime validates the event graph statically before execution.”(accessed 2026-08-03)
- PyPI — llama-index-workflowsLlamaIndex · LlamaIndex“Python 3.10+ required. Standalone package — does not require llama_index. Optional extras: [server] adds llama-agents-server for REST deployment; [client] adds the HTTP client. As of mid-2026 the package is at version 2.22.2 — 22+ minor releases on the stable 1.0 API contract.”(accessed 2026-08-03)
- LlamaIndex Docs — Workflow StateLlamaIndex · LlamaIndex“Typed state is defined by a Pydantic model passed to Workflow(state=MyState). Use async with ctx.store.edit_state() for transactional read-modify-write. Default InMemoryStateStore uses an asyncio.Lock; reads are lockless. State travels with Context.to_dict()/from_dict() for resume-after-crash.”(accessed 2026-08-03)
- run-llama/workflows-py — GitHub repositoryrun-llama · LlamaIndex“Resource injection: step parameters annotated with Annotated[MyType, Resource(factory_fn)]. Factories can be sync or async and can depend on other resources. Default behaviour caches one instance per run; Resource(factory, cache=False) gives a fresh object per step. Replaces the older global Settings.llm pattern.”(accessed 2026-08-03)
- LlamaIndex Docs — Human in the LoopLlamaIndex · LlamaIndex“Emit InputRequiredEvent(prefix=...) from one step; consume HumanResponseEvent(response=...) in a second step — the runtime pauses between them. For cross-request resumption, ctx.to_dict() persists state; Context.from_dict(...) resumes. The two-step pattern is officially recommended over the single-step ctx.wait_for_event alternative.”(accessed 2026-08-03)
- LlamaIndex Docs — Multi-Agent AgentWorkflowLlamaIndex · LlamaIndex“AgentWorkflow(agents=[...], root_agent='...', initial_state={...}) composes any number of FunctionAgent instances. Each FunctionAgent takes name, description, system_prompt, tools, and can_handoff_to — the framework auto-injects a handoff tool. initial_state is shared across all agents via ctx.store.”(accessed 2026-08-03)
- PyPI — llama-index-instrumentationLlamaIndex · LlamaIndex“Adds OpenTelemetry spans around every @step, event emission, and LLM call automatically. Each step becomes an OTel span with attributes for input event type, output event type, retry count, and elapsed time. Arize Phoenix is the officially blessed local UI; production exporters include Langfuse, Datadog, Honeycomb, and Grafana Tempo.”(accessed 2026-08-03)
- Introducing llama-deploy: A Microservice-Based Way to Deploy LlamaIndex WorkflowsLlamaIndex · LlamaIndex“llama-deploy runs any workflow as a REST/WebSocket service with llamactl deploy workflow.py — no code changes. Original architecture used a distributed control plane plus Redis/Kafka/RabbitMQ. Kubernetes deployment uses standard Deployment + Service YAML with llama-agents-server as the container entrypoint.”(accessed 2026-08-03)
- llama_deploy 1.0 Discussion #545 — Roadmaprun-llama · LlamaIndex“llama-deploy 1.0 (in preview mid-2026) collapses the message-queue-and-control-plane architecture into a single-process server for simpler ops. Agent Client Protocol integration ships pre-built document-agent templates deployable in one command via llamactl. OpenTelemetry instrumentation graduated from optional to the default observability path.”(accessed 2026-08-03)
- LlamaIndex TypeScript Docs — WorkflowsLlamaIndex · LlamaIndex“TypeScript uses a functional builder: createWorkflow() + workflow.handle([EventA], async (e) => ...). Events are constructed with workflowEvent<PayloadType>(). Emit new events with eventName.with(payload). State middleware withState(initialState) wraps a workflow; snapshot(ctx)/resume(snapshot) handle persistence. Package name: @llamaindex/workflow-core.”(accessed 2026-08-03)
- Enterprise LlamaIndex Workflows Implementation DataAlice Labs · Alice Labs“Alice Labs has delivered 100+ production AI implementations since 2023, including LlamaIndex Workflows RAG-agent deployments across energy, media, financial services, and retail. Named production pitfalls: silent dead-ends (add Workflow.validate() to CI), stale resources, serialisation drift, handoff loops, and observability blind spots on ctx.send_event() for custom events.”(accessed 2026-08-03)
Next scheduled review: