How do LlamaIndex Workflow-based agents add structure over a bare ReAct loop?
AgentWorker decided one step. AgentRunner owned the Task and the loop, enabling pause, inspect and review. Both were removed in 0.13.0. Workflow-based agents keep the same split.
Imagine a chess engine and a chess clock. The engine knows how to pick the next move given the current board. The clock knows whose turn it is, when to pause, and how to resume a game from a saved position. If you smash both into one program, you have a chess bot that always plays start to finish in one go. You cannot stop it mid-game, you cannot review the moves before the next one, and you cannot swap in a different engine without rewriting the clock. LlamaIndex first split its agents this way: AgentWorker was the engine, and AgentRunner was the clock plus the saved-game folder. Its current agents keep the same idea. The agent picks each move, and a separate workflow runtime runs the game and can save it.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Early agent frameworks shipped one abstraction: an agent class with a run(query) method that internally looped until done. That abstraction worked for demos and failed for production. You could not pause it, inspect it mid-run, or resume after a crash.
LlamaIndex's first response was the runner/worker split: AgentRunner orchestrated Tasks, and AgentWorker decided one step. LlamaIndex has since removed both classes (in llama-index-core 0.13.0) and built its agents on Workflows, which keep the same split. The same response shows up across mature agent runtimes: LangGraph splits executor and node, OpenAI Agents SDK separates the loop and handoffs, Mastra typed workflows split runtime and step handlers.
AgentWorker. The per-step decision
An AgentWorker had one job. Given current state and message history, return the next action: think, call tool, or finalise. Built-in implementations included ReActAgentWorker (reasoning then action loop) and FunctionCallingAgentWorker (native tool calls on supported models).
The key surface was run_step(step, task): advance the task by exactly one decision. The worker did not loop. The runner decided when to call it again.
This narrowness was the point. A worker mapped state in to a decision out, with no role in the rest of the loop. That made workers swappable and testable in isolation. In current LlamaIndex, the agent class (FunctionAgent, ReActAgent) plays this role inside a Workflow.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
import asyncio
from llama_index.core.agent.workflow import FunctionAgent, ToolCallResult
from llama_index.core.workflow import Context, JsonSerializer
from llama_index.llms.openai import OpenAI
def search(query: str) -> str:
"""Search internal notes for a query."""
return f"Notes about {query}"
agent = FunctionAgent(tools=[search], llm=OpenAI(model="gpt-5.5"))
async def main():
# The Workflow runtime drives the steps; the agent decides each one
ctx = Context(agent)
handler = agent.run("Research X, draft a one-page brief.", ctx=ctx)
# Inspect each tool step as it happens
async for ev in handler.stream_events():
if isinstance(ev, ToolCallResult):
print("tool:", ev.tool_name, "->", ev.tool_output)
response = await handler
print(response)
# Save the run state; restore it later, even in another process
ctx_dict = ctx.to_dict(serializer=JsonSerializer())
restored = Context.from_dict(agent, ctx_dict, serializer=JsonSerializer())
followup = await agent.run("Now shorten it to 200 words.", ctx=restored)
print(followup)
asyncio.run(main())
Real products, models, and research that use this idea.
- LlamaIndex's agent docs build FunctionAgent and ReActAgent on Workflows, with the runtime driving steps and the agent deciding each one
- Document-QA agents built on FunctionAgent add human-approval gates with InputRequiredEvent and HumanResponseEvent
What an interviewer would ask next. Try answering before peeking at the approach.
QHow does LlamaIndex Workflows generalise the runner/worker pattern?
Walk through the event-driven Step decorator model, typed StartEvent/StopEvent flow, and how Steps replace AgentWorker as the per-decision unit while the Workflow runtime replaces AgentRunner.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Writing new code against AgentRunner or AgentWorker. Both were removed. Workflow-based agents such as FunctionAgent now provide the stepped loop with inspectable, saveable state.
60 second bullets to scan on the way to the call.
Role of AgentWorker. Per-step decision
Role of AgentRunner. Task orchestration and state between steps
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.