Publish AI, ML & data-science insights to a global community of data professionals.

Build LLM Agents Faster with Datapizza AI

The new GenAI framework "Made in Italy"

Photo by Adrian Infernus on Unsplash

Intro

Organizations are increasingly investing in AI as these new tools are adopted in everyday operations more and more. This continuous wave of innovation is fueling the demand for more efficient and reliable frameworks. Following this trend, Datapizza (the startup behind Italy’s tech community) just released an open-source framework for GenAI with Python, called Datapizza AI.

When creating LLM-powered Agents, you need to pick an AI stack:

  • Language Model – the brain of the Agent. The first big choice is open-source (i.e. Llama, DeepSeek, Phi) vs paid (i.e. ChatGPT, Claude, Gemini). Then, based on the usecase, one needs to consider the LLM knowledge: generic (knows a little bit of everything like Wikipedia) vs topic-specific (i.e. fine-tuned for coding or finance).
  • LLM Engine – it’s what runs the language model, responding to prompts, inferring meaning, and creating text. Basically, it generates intelligence. The most used are OpenAI (ChatGPT), Anthropic (Claude), Google (Gemini), and Ollama (runs open-source models locally).
  • AI Framework – it’s the orchestration layer to build and manage workflows. To put it in another way, the framework must structure the intelligence created by LLMs. At the moment, the landscape is dominated by LangChain, LLamaIndex, and CrewAI. The new library Datapizza AI falls in this category and wants to be an alternative to the other main frameworks.

In this article, I’m going to show how to use the new Datapizza framework for building LLM-powered AI Agents. I will present some useful Python code that can be easily applied in other similar cases (just copy, paste, run) and walk through every line of code with comments so that you can replicate this example.

Setup

I will use Ollama as the LLM engine, because I like to host models locally on my computer. That is the standard practice for all companies with sensitive data. Keeping everything local gives full control over data privacy, model behavior, and cost.

First of all, you need to download Ollama from the website. Then, pick a model and run the command indicated on the page to pull the LLM. I’m going with Alibaba’s Qwen, as it’s both smart and lite (ollama run qwen3).

Datapizza AI supports all the main LLM engines. We can complete the setup by running the following commands:

pip install datapizza-ai
pip install datapizza-ai-clients-openai-like

As indicated in the official documentation, we can quickly test our AI stack by calling the model with a simple prompt and asking a question. The object OpenAILikeClient() is how you connect to the Ollama API, which is usually hosted on the same localhost URL.

from datapizza.clients.openai_like import OpenAILikeClient

llm = "qwen3"

prompt = '''
You are an intelligent assistant, provide the best possible answer to user's request. 
''' 

ollama = OpenAILikeClient(api_key="", model=llm, system_prompt=prompt, base_url="http://localhost:11434/v1")

q = '''
what time is it?
'''

llm_res = ollama.invoke(q)
print(llm_res.text)

Chatbot

Another way to test the capability of the LLM is to build a simple Chatbot and do some conversation. To do so, at every interaction, we need to store the chat history and feed it back to the model, specifying what was said by whom. The Datapizza framework already has a built-in memory system.

from datapizza.memory import Memory
from datapizza.type import TextBlock, ROLE

memory = Memory()
memory.add_turn(TextBlock(content=prompt), role=ROLE.SYSTEM)

while True:
    ## User
    q = input('🙂 >')
    if q == "quit":
        break
    
    ## LLM
    llm_res = ollama.invoke(q, memory=memory)
    res = llm_res.text
    print("🍕 >", f"\x1b[1;30m{res}\x1b[0m")

    ## Update Memory
    memory.add_turn(TextBlock(content=q), role=ROLE.USER)
    memory.add_turn(TextBlock(content=res), role=ROLE.ASSISTANT)

If you want to retrieve the chat history, you can just access the memory. Usually, AI frameworks use three roles in the interaction with an LLM: “system” (core instructions), “user” (what was said by the human), “assistant” (what the chatbot replied).

memory.to_dict()

Obviously, the LLM alone is very limited and it can’t do much besides chatting. Therefore, we need to give it the possibility to take action, or in other words, to activate Tools.

Tools

Tools are the main difference between a simple LLM and an AI Agent. When the user requests something that goes beyond the LLM knowledge base (i.e. “what time is it now?“), the Agent should understand that it doesn’t know the answer, activate a Tool to get additional information (i.e. checking the clock), elaborate the result through the LLM, and generate an answer.

The Datapizza framework allows you to create Tools from scratch very easily. You just need to import the decorator @tool and any function can become actionable for the Agent.

from datapizza.tools import tool

@tool
def get_time() -> str:
    '''Get the current time.'''
    from datetime import datetime
    return datetime.now().strftime("%H:%M")

get_time()

Then, assign the designated Tool to the Agent, and you’ll have an AI that combines language understanding + autonomy decision making + tool use.

from datapizza.agents import Agent
import os

os.environ["DATAPIZZA_AGENT_LOG_LEVEL"] = "DEBUG"  #max logging

agent = Agent(name="single-agent", client=ollama, system_prompt=prompt, 
              tools=[get_time], max_steps=2)

q = '''
what time is it?
'''

agent_res = agent.run(q)

An LLM-powered AI Agent is an intelligent system built around a language model that doesn’t just respond, but it reasons, decides, and acts. Besides conversation (which means chatting with a general-purpose knowledge base), the most common actions that Agents can do are RAG (chatting with your documents), Querying (chatting with a database), Web Search (chatting with the whole Internet).

For instance, let’s try a web searching Tool. In Python, the easiest way to do it is with the famous private browser DuckDuckGo. You can directly use the original library or the Datapizza framework wrapper (pip install datapizza-ai-tools-duckduckgo).

from datapizza.tools.duckduckgo import DuckDuckGoSearchTool

DuckDuckGoSearchTool().search(query="powell")

Let’s create an Agent that can search the web for us. If you want to make it more interactive, you can structure the AI like I did for the Chatbot.

os.environ["DATAPIZZA_AGENT_LOG_LEVEL"] = "ERROR" #turn off logging

prompt = '''
You are a journalist. You must make assumptions, use your tool to research, make a guess, and formulate a final answer.
The final answer must contain facts, dates, evidences to support your guess.
'''

memory = Memory()

agent = Agent(name="single-agent", client=ollama, system_prompt=prompt, 
              tools=[DuckDuckGoSearchTool()], 
              memory=memory, max_steps=2)

while True:
    ## User
    q = input('🙂 >')
    if q == "quit":
        break
    
    ## Agent
    agent_res = agent.run(q)
    res = agent_res.text
    print("🍕 >", f"\x1b[1;30m{res}\x1b[0m")

    ## Update Memory
    memory.add_turn(TextBlock(content=q), role=ROLE.USER)
    memory.add_turn(TextBlock(content=res), role=ROLE.ASSISTANT)

Multi-Agent System

The real strength of Agents is the ability to collaborate with each other, just like humans do. These teams are called Multi-Agent Systems (MAS), a group of AI Agents that work together in a shared environment to solve complex problems that are too difficult for a single one to handle alone.

This time, let’s create a more advanced Tool: code execution. Please note that LLMs know how to code by being exposed to a large corpus of both code and natural language text, where they learn patterns, syntax, and semantics of programming languages. But since they can not complete any real action, the code they create is just text. In short, LLMs can generate Python code but can’t execute it, Agents can.

import io
import contextlib

@tool
def code_exec(code:str) -> str:
    '''Execute python code. Use always the function print() to get the output'''
    output = io.StringIO()
    with contextlib.redirect_stdout(output):
        try:
            exec(code)
        except Exception as e:
            print(f"Error: {e}")
    return output.getvalue()

code_exec("from datetime import datetime; print(datetime.now().strftime('%H:%M'))")

There are two types of MAS: the sequential process ensures tasks are executed one after the other, following a linear progression. On the other hand, the hierarchical structure simulates traditional organizational hierarchies for efficient task delegation and execution. Personally, I tend to prefer the latter as there is more parallelism and flexibility.

With the Datapizza framework, you can link two or more Agents with the function can_call(). In this way, one Agent can pass the current task to another Agent.

prompt_senior = '''
You are a senior Python coder. You check the code generated by the Junior, 
and use your tool to execute the code only if it's correct and safe.
'''
agent_senior = Agent(name="agent-senior", client=ollama, system_prompt=prompt_senior, 
                     tools=[code_exec])

prompt_junior = '''
You are a junior Python coder. You can generate code but you can't execute it. 
You receive a request from the Manager, and your final output must be Python code to pass on.
If you don't know some specific commands, you can use your tool and search the web for "how to ... with python?".
'''
agent_junior = Agent(name="agent-junior", client=ollama, system_prompt=prompt_junior, 
                     tools=[DuckDuckGoSearchTool()])
agent_junior.can_call([agent_senior])

prompt_manager = '''
You know nothing, you're just a manager. After you get a request from the user, 
first you ask the Junior to generate the code, then you ask the Senior to execute it.
'''
agent_manager = Agent(name="agent-manager", client=ollama, system_prompt=prompt_manager, 
                      tools=[])
agent_manager.can_call([agent_junior, agent_senior])

q = '''
Plot the Titanic dataframe. You can find the data here: 
https://raw.githubusercontent.com/mdipietro09/DataScience_ArtificialIntelligence_Utils/master/machine_learning/data_titanic.csv
'''

agent_res = agent_manager.run(q)
#print(agent_res.text)

Conclusion

This article has been a tutorial to introduce Datapizza AI, a brand new framework to build LLM-powered Chatbots and AI Agents. The library is very flexible and user-friendly, and can cover different GenAI usecases. I used it with Ollama, but it can be linked with all the famous engines, like OpenAI.

Full code for this article: GitHub

I hope you enjoyed it! Feel free to contact me for questions and feedback or just to share your interesting projects.

👉 Let’s Connect 👈

(All images are by the author unless otherwise noted)


Towards Data Science is a community publication. Submit your insights to reach our global audience and earn through the TDS Author Payment Program.

Write for TDS

Related Articles