Real assistants must remember the conversation, survive across web requests and show progress while working.
By default each agent.run(...) call starts fresh. To keep a conversation, pass a `Context`:
ctx = Context(agent)
await agent.run("My name is Ana.", ctx=ctx)
await agent.run("What's my name?", ctx=ctx) # remembers: AnaThe context holds the chat memory and a store of arbitrary state. Read and write it with await ctx.store.get("key", default=None) and await ctx.store.set("key", value). Tools can receive the context too: add a parameter typed ctx: Context and LlamaIndex passes it in, so a tool can update state such as a shopping cart.
Between web requests, turn the context into a plain dictionary with ctx.to_dict(), save it (database, cache), and rebuild it later with Context.from_dict(agent, data). Values in the store must be serialisable (convertible to JSON-like data).
Long conversations are trimmed by the agent's memory to fit a token limit. For control, pass a Memory object (from llama_index.core.memory import Memory), which supports a short-term token limit plus long-term memory blocks such as extracted facts.
Streaming. agent.run(...) returns a handler. Iterate its events before awaiting the result:
handler = agent.run("...", ctx=ctx)
async for ev in handler.stream_events():
...
response = await handlerUseful event types: AgentStream (ev.delta is new text), ToolCall and ToolCallResult (ev.tool_name, ev.tool_kwargs, ev.tool_output). Show tool activity in your interface; users trust agents more when they can see what was looked up.
Example
import asyncio
from llama_index.core.agent.workflow import AgentStream, FunctionAgent, ToolCallResult
from llama_index.core.workflow import Context
from llama_index.llms.openai import OpenAI
async def add_to_cart(ctx: Context, item: str) -> str:
"""Add an item to the user's shopping cart."""
cart = await ctx.store.get("cart", default=[])
cart.append(item)
await ctx.store.set("cart", cart)
return f"Cart now has: {', '.join(cart)}"
agent = FunctionAgent(tools=[add_to_cart], llm=OpenAI(model="gpt-4o-mini"))
async def main():
ctx = Context(agent)
handler = agent.run("Add apples and bread to my cart.", ctx=ctx)
async for ev in handler.stream_events():
if isinstance(ev, ToolCallResult):
print(f"[tool] {ev.tool_name}({ev.tool_kwargs}) -> {ev.tool_output}")
elif isinstance(ev, AgentStream): print(ev.delta, end="", flush=True)
await handler
ctx2 = Context.from_dict(agent, ctx.to_dict()) # e.g. saved between requests
print("\n", await ctx2.store.get("cart", default=[])) # ['apples', 'bread']
asyncio.run(main())Remember
- Pass the same
Contextto keep conversation and state ctx.store.get/set(awaited) for state; tools can takectx: Contextctx.to_dict()/Context.from_dict()across requests; stream withstream_events()
Watch out for
- Creating a new Context on every web request and wondering why the agent forgets the conversation.