Quick Start
This chapter follows the official Starter Tutorial (OpenAI): FunctionAgent + tools, then VectorStoreIndex, then persist and multi-turn Context.
Examples use gpt-4o-mini. The framework historically defaulted to gpt-3.5-turbo—follow current docs. Set OPENAI_API_KEY and keep a data/ folder (any .txt / .md).
Minimal FunctionAgent
Plain Python functions become tools. Type hints and the docstring become the schema the model uses to decide on a call.
import asyncio
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai import OpenAI
def multiply(a: float, b: float) -> float:
"""Useful for multiplying two numbers."""
return a * b
agent = FunctionAgent(
tools=[multiply],
llm=OpenAI(model="gpt-4o-mini"),
system_prompt="You are a helpful assistant that can multiply two numbers.",
)
async def main():
response = await agent.run("What is 1234 * 4567?")
print(str(response))
if __name__ == "__main__":
asyncio.run(main())
What happened: the question plus tool schemas go to the LLM → it picks multiply and fills arguments → the framework runs the function → the model writes a natural-language answer. Official samples prefer async.
Multi-turn chat: pass Context back
Session state for FunctionAgent lives in Context. Reuse the same ctx across run calls so the model remembers the previous turn.
from llama_index.core.workflow import Context
ctx = Context(agent)
response = await agent.run("My name is Logan", ctx=ctx)
response = await agent.run("What is my name?", ctx=ctx)
print(str(response))
A new Context every time is amnesia. In production, persist Context per user / session (see official workflow checkpoint examples).
Five-minute RAG: folder → index → query engine
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("What are these documents about?")
print(response)
By default OpenAI embeddings (historically text-embedding-ada-002) write chunks into an in-memory vector store. Wrap the query as a tool so the agent can mix retrieval and math:
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai import OpenAI
import asyncio
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
def multiply(a: float, b: float) -> float:
"""Useful for multiplying two numbers."""
return a * b
async def search_documents(query: str) -> str:
"""Answer questions about files in the data folder."""
response = await query_engine.aquery(query)
return str(response)
agent = FunctionAgent(
tools=[multiply, search_documents],
llm=OpenAI(model="gpt-4o-mini"),
system_prompt="You can calculate and search local documents.",
)
async def main():
print(await agent.run("What did the author do in college? Also, what's 7 * 8?"))
if __name__ == "__main__":
asyncio.run(main())
Persist so you do not re-embed every launch
index.storage_context.persist(persist_dir="storage")
from llama_index.core import StorageContext, load_index_from_storage
storage_context = StorageContext.from_defaults(persist_dir="storage")
index = load_index_from_storage(storage_context)
query_engine = index.as_query_engine()
If the index already lives in Chroma / pgvector / Qdrant, vectors are in that database—usually VectorStoreIndex.from_vector_store(vector_store) instead of load_index_from_storage. See Vector Stores.
If you customized transformations / embed_model, reload with the same Settings or retrieval will miss.
First checklist
pip install llama-index and set OPENAI_API_KEY
- Run the tiny
FunctionAgent multiply script
- Drop files into
data/, run VectorStoreIndex + as_query_engine
- Add
search_documents and ask a fact that exists only in those files
persist, restart, query again; reuse one Context for two name turns
Next steps