Agents with FunctionAgent

LlamaIndex · Practical · 14 min

Agents can pick between several data sources and tools per question, which a fixed query engine can't.

An agent is an LLM in a loop: it reads the request, decides which tool to call, looks at the result and repeats until it can answer. In LlamaIndex the standard agent is `FunctionAgent`, which uses the model's tool (function) calling:

agent = FunctionAgent(tools=[multiply, search_docs], llm=OpenAI(model="gpt-4o-mini"),
                      system_prompt="You are a helpful assistant.")
response = await agent.run("What is 7 * 8, and what does the handbook say about leave?")

Tools. A plain Python function becomes a tool automatically: its name, type hints and docstring tell the model what it does, so write the docstring for the model. FunctionTool.from_defaults(fn, name=..., description=...) gives more control. `QueryEngineTool.from_defaults(query_engine, name=..., description=...)` turns a whole RAG pipeline into one tool. That is how an agent can choose between, say, an HR index and a product index.

Async. agent.run(...) is asynchronous. async/await is Python's way of running code that waits for slow things (like model calls) without blocking everything else. In practice: put your calls inside an async def main():, write await before agent.run(...), and start it with asyncio.run(main()). In Jupyter notebooks you can use await directly.

Other agent types. ReActAgent works with models that lack native tool calling, by reasoning in text. CodeActAgent writes and runs code as its actions, which needs a sandbox.

Older APIs such as OpenAIAgent, AgentRunner and ReActAgent.from_tools are legacy; use the workflow-based agents.

Example

import asyncio
from llama_index.core import Document, VectorStoreIndex
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.core.tools import QueryEngineTool
from llama_index.llms.openai import OpenAI

def multiply(a: float, b: float) -> float:
    """Multiply two numbers."""
    return a * b

hr_index = VectorStoreIndex.from_documents([Document(text="Staff get 25 leave days a year.")])
hr_tool = QueryEngineTool.from_defaults(
    hr_index.as_query_engine(), name="hr_handbook",
    description="Answers questions about HR policies such as leave and benefits.")

agent = FunctionAgent(tools=[multiply, hr_tool], llm=OpenAI(model="gpt-4o-mini"),
                      system_prompt="Use tools for facts and arithmetic.")

async def main():
    response = await agent.run("How many leave days do 3 employees get in total?")
    print(str(response))        # 3 employees get 75 leave days in total.

asyncio.run(main())

Remember

  • FunctionAgent(tools, llm, system_prompt) runs a tool-calling loop
  • Functions become tools via name, type hints and docstring; QueryEngineTool wraps RAG
  • await agent.run(...) inside async def, started with asyncio.run

Watch out for

  • Calling agent.run(...) without await in normal code and printing a handler object instead of the answer.

Check yourself

  1. You have separate indexes for HR policies and product manuals. How can one agent use both?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developers.llamaindex.ai/python/framework/understanding/agent/