Bitdoze Logo

Getting Started with Agno Agents: uv, RAG & Memory

Learn how to build Agno agents in Python with this step-by-step guide. Set up uv, add web search, memory, and RAG with LanceDB, then build multi-agent teams.

DragosDragos22 min read
Getting Started with Agno Agents: uv, RAG & Memory

You want an AI agent that searches the web, remembers your preferences, and digs through PDFs. Agno is an open-source Python framework for building Agno agents that do exactly that. Pair it with uv, the Rust-based package manager from Astral (10-100x faster than pip), and you can have working agents running tonight.

This guide is a full refresh for Agno 2.x. As of August 2026 the latest stable release is 2.8.6 (v3.0.0a1 is in alpha if you want to live on the edge). If you’ve seen older Agno tutorials (including the original March 2025 version of this article), the API got a major overhaul in v2.0. Old favorites like SqliteAgentStorage, PDFUrlKnowledgeBase, and Agent(team=[...]) are gone, replaced by a cleaner toolkit. Everything below is verified against the real thing.

We’ll set up uv first, then build an Agno agent that starts as a simple chatbot and grows into a memory-aware, Retrieval-Augmented Generation (RAG) system powered by LanceDB and DuckDuckGo. We’ll finish with a two-agent team that coordinates on complex questions. Code snippets, cost breakdowns, and troubleshooting included.

Updated for Agno 2.x

This article was originally published in March 2025 against Agno 1.x. It has been rewritten and verified against Agno 2.8.6 (August 2026). The embedded video below shows the older API in action. The code in this guide is current.

Getting Started with Agno Agents

Step 1: Set up your environment with uv

Before you install Agno, you need a fast Python package manager. uv comes from the Astral crew (the same folks behind ruff) and runs circles around pip.

You can check more on how you can get started with uv.

Installing uv

For macOS/Linux:

curl -LsSf https://astral.sh/uv/install.sh | sh

For Windows (PowerShell):

irm https://astral.sh/uv/install.ps1 | iex

Check it’s alive:

uv --version  # Expect "uv 0.12" or newer

Initializing a project

Initialize the project:

uv 0.12+ changed the default layout

Starting with uv 0.12.0, uv init creates a packaged project by default (src/ layout with build system). For this tutorial we use a flat main.py layout, so pass --no-package. If you forget, your code goes in src/agno_adventure/main.py instead.

uv init --no-package agno-adventure
cd agno-adventure

This whips up a tidy project structure: pyproject.toml for dependencies, .python-version, main.py to code in, a README.md, and a fresh git repo. Lock in Python 3.12 for consistency (need to install Python on Mac first?):

uv python pin 3.12

Setting up the environment

Now, create a virtual environment:

uv venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

Load up the essentials. Agno splits its integrations into opt-in extras, so you only install what you need:

uv add "agno[openai,lancedb,pdf,ddg,sqlite]" typer rich

That single line pulls in:

  • agno: the framework itself, plus:
    • openai: the OpenAI model provider (GPT models are Agno’s default)
    • lancedb: our vector database for RAG later
    • pdf: the PDF reader (pypdf under the hood)
    • ddg: DuckDuckGo web search (via the modern ddgs package)
    • sqlite: SQLite session storage (sqlalchemy + friends)
  • typer and rich: for the interactive CLI we’ll build in Step 5

Agno needs an OpenAI API key, so set it up:

export OPENAI_API_KEY="sk-your-key-here"

Pro tip: Keep your keys in a .env file. uv run auto-loads .env from your project directory, so no extra tooling needed. Just add OPENAI_API_KEY=your-key-here to .env and run scripts with uv run python main.py.

Step 2: Your first Agno agent

Let’s build your first agent. Agno is an open-source Python framework (~40k GitHub stars, Apache-2.0 license) that makes building AI agents straightforward. Our first agent is a simple chatbot.

The code

Edit main.py:

from agno.agent import Agent

agent = Agent(
    model="openai:gpt-5.5",
    description="You're a cheerful AI pal who loves a good chat!",
    markdown=True
)

agent.print_response("Hey! What's cooking today?", stream=True)

Run it:

uv run python main.py

Tip: add debug_mode=True

Add debug_mode=True to any Agent constructor to see raw tool calls, model prompts, and timing in the terminal output. Great for learning what’s happening under the hood.

How it works

  • Agent: the core class. You define personality (via description), model, and tools.
  • model="openai:gpt-5.5": Agno 2.x uses model string references like "provider:model-id". The string resolves to the right model class under the hood (for OpenAI it maps to OpenAIResponses). You can swap in any provider: "anthropic:claude-sonnet-4-5", "google:gemini-3.5-flash", "ollama:llama3.1:8b", etc. The older class-based style (from agno.models.openai import OpenAIChat) still imports for backwards compatibility, but string refs are cleaner.
  • description: sets the agent’s personality. Here it’s a friendly chatbot.
  • markdown=True: formats responses with Markdown.
  • print_response: streams the reply in real-time.

You’ll get a response like, “Hey there! Just here to spice up your day. What’s on the menu?” It’s basic, but it works.

More about Agno

Agno is lightweight and fast. It handles simple chatbots and complex multi-tool agents without heavy abstractions. You can add tools, memory, and knowledge bases incrementally. If TypeScript is more your thing, build an AI agent with Mastra instead. But Agno’s Python-first approach works well for rapid prototyping.

Since v2.0, Agno also ships with AgentOS, a runtime that serves your agents as REST APIs with tracing, session isolation, and RBAC. We’ll cover that later.

Troubleshooting

  • “ModuleNotFoundError”: Forgot a package? Run uv add "agno[openai]" and try again.
  • Silent Agent: Check your OPENAI_API_KEY. No key, no chat.

Step 3: Adding DuckDuckGo web search tools

The agent can chat but can’t look anything up. Let’s add DuckDuckGo web search.

The code

Update main.py:

from agno.agent import Agent
from agno.tools.duckduckgo import DuckDuckGoTools

agent = Agent(
    model="openai:gpt-5.5",
    description="You're a web-savvy AI explorer!",
    tools=[DuckDuckGoTools()],
    markdown=True
)

agent.print_response("What's the buzz in New York right now?", stream=True)

Run it:

uv run python main.py

How it works

  • DuckDuckGoTools: gives your agent web search. It decides when to use it based on the question.
  • Tool calls in the terminal: the old show_tool_calls=True parameter is gone in Agno 2.x. When you stream a response, the CLI printer shows tool calls as they happen. For full trace-level detail, add debug_mode=True to the Agent or run the agent through AgentOS and inspect the trace UI.
  • Output: Expect something like, “Calling DuckDuckGo… Here’s the latest from NYC!” It’s now a worldly conversationalist.

Agno’s tool power

Agno’s tool system is modular. DuckDuckGoTools is just one option — Agno supports a growing toolbox you can mix and match, from APIs to custom Python functions (just pass any function in tools=[...]). You can also integrate tools via Model Context Protocol (MCP), the emerging standard for tool interoperability — see Agno with Context7 MCP for a hands-on example.

If you need a web search API with more control (structured results, pagination, geo-targeting), TinyFish Web Search API is worth a look for production agents that outgrow DuckDuckGo’s rate limits.

Troubleshooting

  • No Web Results: Ensure the ddg extra is installed — run uv add "agno[ddg]".
  • Stuck?: Rate limits might be the culprit. Add debug_mode=True to the Agent for a deeper look at what’s tripping it up.

Step 4: Agno memory that sticks

Our agent’s got charisma but forgets everything the moment you blink. Let’s give it a memory upgrade with Agno’s SQLite-backed storage, turning it into a loyal companion.

The code

Create memory_agent.py:

from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from rich.pretty import pprint

agent = Agent(
    model="openai:gpt-5.5",
    description="You're an AI with a memory like an elephant!",
    user_id="dragos",
    db=SqliteDb(db_file="tmp/agent_storage.db"),
    add_history_to_context=True,
    num_history_runs=3,
    update_memory_on_run=True,
    session_id="my_chat_session",
    markdown=True
)

agent.print_response("I love spicy Thai food. What's your favorite cuisine?")
agent.print_response("What did I just say I love?")

# Inspect what the agent remembers
pprint(agent.get_session_messages(session_id="my_chat_session"))
pprint(agent.get_user_memories(user_id="dragos"))

Run it:

uv run python memory_agent.py

How it works

The memory API got a big cleanup in Agno 2.x — the old SqliteAgentStorage class and the whole agno.storage package are gone. Here’s the new model:

Memory vs history vs state

Agno separates three concepts cleanly. Memory = extracted facts about a user, scoped by user_id, shared across sessions. Chat history = messages and tool calls from previous runs, scoped by session_id, for conversational continuity. Session state = application data (carts, task lists, counters) managed by your code or tools.

  • db=SqliteDb(db_file=...): Stores sessions, chat history, and extracted memories in a SQLite database. No extra service needed.
  • user_id="dragos": Scopes memory extraction to a specific user. Without it, memories won’t be reliably recalled across sessions.
  • add_history_to_context=True + num_history_runs=3: Feeds the last few runs (each with all its messages) into the prompt, giving conversational context. These replaced the old add_history_to_messages / num_history_responses.
  • update_memory_on_run=True: Enables automatic memory — after each run, Agno extracts durable facts about the user (preferences, goals) and stores them keyed by user_id. The alternative is agentic memory (enable_agentic_memory=True), where the model itself decides when to inspect, create, or update memories during a run. Pick one mode per agent.
  • session_id: Links interactions under one session — use the same ID, and it’s like picking up where you left off.
  • get_session_messages() / get_user_memories(): The v2 way to peek inside — chat history and extracted facts, respectively. The old agent.memory.messages attribute is no more.
  • search_knowledge=True: This is actually the default when a knowledge base is set on the agent, so you don’t need to specify it explicitly — but keeping it there is harmless and makes the intent clear.

Ask about Thai food, then test its recall. It’ll proudly declare, “You love spicy Thai food!” Memory unlocked!

This pattern is the foundation for real-world agents like a Discord AI bot with Agno that remembers users across conversations.

Troubleshooting

  • Amnesia: Same session_id? Same user_id? Check both — memory is scoped by user_id, history by session_id.
  • No Storage: Run uv add "agno[sqlite]" — it’s the backbone of SQLite storage.
  • Missing directory: Run mkdir tmp if the tmp/ directory doesn’t exist yet.

Pro tip: For bigger projects, swap SqliteDb for PostgresDb from agno.db.postgres via uv add "agno[postgres]". You can deploy a Postgres vector database with pgvector for combined vector search and session storage in one database.

Step 5: RAG with LanceDB — Knowledge is your superpower

Time to make your agent a Thai cuisine expert with RAG (Retrieval-Augmented Generation). Using LanceDB, it’ll pull recipes from PDFs and back them up with web smarts — interactive style!

The code

Create rag_agent.py:

import typer
from rich.prompt import Prompt

from agno.agent import Agent
from agno.knowledge.knowledge import Knowledge
from agno.knowledge.embedder.openai import OpenAIEmbedder
from agno.knowledge.reader.pdf_reader import PDFReader
from agno.vectordb.lancedb import LanceDb, SearchType
from agno.tools.duckduckgo import DuckDuckGoTools

# LanceDB Vector DB
vector_db = LanceDb(
    table_name="recipes",
    uri="tmp/lancedb",
    search_type=SearchType.hybrid,
    embedder=OpenAIEmbedder(id="text-embedding-3-small"),
)

# Knowledge Base
knowledge = Knowledge(
    vector_db=vector_db,
    readers=[PDFReader()],
)

def lancedb_agent(user: str = "user"):
    agent = Agent(
        model="openai:gpt-5.5",
        description="You're a Thai cuisine expert with web backup!",
        user_id=user,
        knowledge=knowledge,
        search_knowledge=True,
        tools=[DuckDuckGoTools()],
        instructions=[
            "Search the knowledge base for Thai recipes first.",
            "Use DuckDuckGo if more info is needed."
        ],
        markdown=True
    )

    print(f"Session ID: {agent.session_id}\n")

    while True:
        message = Prompt.ask(f"[bold] :sunglasses: {user} [/bold]")
        if message in ("exit", "bye"):
            break
        agent.print_response(message, stream=True)

if __name__ == "__main__":
    # Load the PDF into the knowledge base (idempotent - safe to run every time)
    knowledge.insert(url="https://agno-public.s3.amazonaws.com/recipes/ThaiRecipes.pdf")
    typer.run(lancedb_agent)

Run it:

uv run python rag_agent.py

Built-in CLI alternative

If you don’t want to pull in typer and rich, you can use agent.cli_app() instead — it gives you a basic interactive chat loop with zero extra dependencies.

How it works

The knowledge API was completely restructured in Agno 2.x — PDFUrlKnowledgeBase is gone. Here’s the new shape:

  • Knowledge: The single knowledge-base class (from agno.knowledge.knowledge). It combines a vector DB, optional readers, and content ingestion.
  • readers=[PDFReader()]: Tells the knowledge base how to parse PDFs (the pdf extra installs pypdf under the hood). Readers now live under agno.knowledge.reader — there are ready-made ones for PDF, DOCX, CSV, Markdown, Excel, YouTube, Wikipedia, and more.
  • knowledge.insert(url=...): The v2 replacement for knowledge_base.load(recreate=True) — downloads the file, parses it with the matching reader, chunks it, and embeds it into the vector DB. It’s idempotent by default (upsert=True), so re-running the script won’t duplicate content. You can also insert by local path=, raw text_content=, or even topics= for query-based loading.
  • LanceDb + SearchType.hybrid: Hybrid blends keyword and semantic searches for max accuracy — and since LanceDB moved to native full-text search, you no longer need the tantivy package.
  • OpenAIEmbedder: Moved in v2 — it now lives at agno.knowledge.embedder.openai. Same idea: converts text to embeddings using text-embedding-3-small.
  • search_knowledge=True: The v2 flag that lets the agent search its knowledge base during a run (agentic RAG — the agent decides when to retrieve). This is actually the default when knowledge is set.
  • typer/Prompt: Keeps the chat going until you say “bye” — perfect for recipe hunting!
  • Output: Ask, “How do I make chicken and galangal coconut soup?” It’ll dig into the PDF, then surf the web if needed.

Agno’s RAG edge

RAG combines retrieval (from LanceDB) with generation (via GPT), making your agent a knowledge ninja. Agno supports 20+ vector databases — from local options like LanceDB and ChromaDB to managed services like Pinecone and Weaviate — and lets you swap them by changing a few lines.

Troubleshooting

  • PDF Won’t Load: Verify the URL and run uv add "agno[pdf,lancedb]".
  • Embedding Errors: OpenAIEmbedder needs the same OPENAI_API_KEY as the chat model — check your .env.
  • No Chat Prompt: Run uv add typer rich for the interactive goodies (or use agent.cli_app()).
  • Duplicate Content: Don’t worry — insert() upserts by default, so re-running is safe.

Pro tip: Add more sources to knowledge.insert() — cookbooks, travel guides, markdown docs — to create a custom knowledge empire.

Step 6: Multi-agent teams — Chef and researcher duo

Why stop at one agent when you can have a dynamic duo? Let’s pair a Thai chef with a web researcher for a collab that’s pure magic.

The code

Create team_agent.py:

from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.knowledge.knowledge import Knowledge
from agno.knowledge.embedder.openai import OpenAIEmbedder
from agno.knowledge.reader.pdf_reader import PDFReader
from agno.vectordb.lancedb import LanceDb, SearchType
from agno.tools.duckduckgo import DuckDuckGoTools
from agno.team import Team, TeamMode

# Shared knowledge base
vector_db = LanceDb(
    table_name="recipes",
    uri="tmp/lancedb",
    search_type=SearchType.hybrid,
    embedder=OpenAIEmbedder(id="text-embedding-3-small"),
)
knowledge = Knowledge(vector_db=vector_db, readers=[PDFReader()])
knowledge.insert(url="https://agno-public.s3.amazonaws.com/recipes/ThaiRecipes.pdf")

# Chef Agent
chef = Agent(
    name="ThaiChef",
    role="Thai cuisine expert",
    model="openai:gpt-5.5",
    knowledge=knowledge,
    search_knowledge=True,
    instructions=["Provide detailed Thai recipes from the knowledge base."],
    markdown=True
)

# Researcher Agent
researcher = Agent(
    name="WebResearcher",
    role="Web info gatherer",
    model="openai:gpt-5.5",
    tools=[DuckDuckGoTools()],
    instructions=["Search the web for supplementary info when asked."],
    markdown=True
)

# Team Leader
team = Team(
    name="Thai Team",
    members=[chef, researcher],
    mode=TeamMode.coordinate,
    db=SqliteDb(db_file="tmp/team_storage.db"),
    instructions=[
        "Ask ThaiChef for recipes first.",
        "If more context is needed, consult WebResearcher.",
        "Blend their inputs into a cohesive answer."
    ],
    markdown=True
)

team.print_response("Tell me about Thai chicken soup and its cultural significance.", stream=True)

Run it:

uv run python team_agent.py

How it works

Multi-agent collaboration got a dedicated class in Agno 2.x — Agent(team=[...]) is gone. Say hello to agno.team.Team:

  • Team(members=[...]): The team leader coordinates its member agents, delegating tasks based on their roles and synthesizing results. Members can even be nested teams.
  • TeamMode: Makes collaboration styles explicit — coordinate (default; decompose work, delegate, synthesize), route (send to a single specialist), broadcast (same task to all members), or tasks (task-list loop until done).
  • ThaiChef: Recipe guru, pulling from the PDF via LanceDB — OpenAIEmbedder produces the embeddings, search_knowledge=True lets it retrieve during runs.
  • WebResearcher: Web sleuth, digging up cultural context with DuckDuckGo.
  • db=SqliteDb(...): Keeps the team’s sessions and history sharp across runs.
  • Output: You’ll get a recipe and a story — like, “This soup’s a Thai staple, tied to ancient herbal traditions!”

For a more advanced multi-agent setup, check out how to build an AI research squad with Agno using Streamlit and multiple specialized agents.

Troubleshooting

  • Team Mute: Add debug_mode=True to the Team to spy on the delegation chatter.
  • Storage Snag: Ensure the SqliteDb import is there and tmp/ exists.
  • Slow Start: knowledge.insert() upserts by default, so you can move the insert out of the hot path (or guard it with a file check) once the PDF is loaded.

What this costs — API pricing at a glance

Running through this tutorial doesn’t have to break the bank. Here’s what you’re looking at as of August 2026:

Model Input (per 1M tokens) Output (per 1M tokens) Context window
gpt-5.4-mini $0.75 $3.00 1,000,000
gpt-5.6-luna $1.00 $6.00 1,000,000
gpt-5.5 $5.00 $30.00 1,050,000
gpt-5.6-sol $5.00 $30.00 1,050,000

My recommendation for learning: swap all the runnable examples to "openai:gpt-5.4-mini". It’s Agno’s documented default, and the whole tutorial will cost you under $0.10. Save gpt-5.5 or gpt-5.6-sol for when you’re building something for real.

Embeddings (text-embedding-3-small) are billed separately from chat tokens — they’re dirt cheap for the volumes in this tutorial.

Watch your spend

Set OpenAI usage limits at platform.openai.com/usage, especially before running the multi-agent team example — it makes several agent calls per request.

Production notes: Telemetry, security & deployment

Disable telemetry (optional)

Agno sends one telemetry event per agent run (model provider choice only — no prompts, messages, or outputs are sent). If you’d rather opt out:

export AGNO_TELEMETRY=false

Add it to your .env file to make it permanent for the project.

AgentOS — Ship your agent as a REST API

Once your agent works locally, you can serve it as a production REST API with AgentOS — Agno’s built-in runtime that adds streaming, tracing, session isolation, and JWT-based RBAC:

uv add "agno[os]"
from agno.os import AgentOS

# Assuming you have an `agent` and `db` already defined
agent_os = AgentOS(agents=[agent], db=db)
agent_os.serve(port=7777)

This gives you a REST API at http://localhost:7777 and a trace UI. You can also manage agents from the cloud control plane at os.agno.com.

Security warning

AgentOS’s local quickstart runs without authentication. Do not expose port 7777 to the public internet without configuring Security and Auth first. Treat it like any other unauthenticated dev server — localhost only until hardened.

Deployment: Once you’re ready to run this on a VPS, you can deploy a Python uv project with Dokploy and Docker. For cheap, reliable hosting, I’d look at a Hetzner VPS (starts around €4.50/month) or a DigitalOcean droplet — both handle a lightweight AgentOS deployment without breaking a sweat.

Conclusion: Your Agno journey takes flight

You’ve just gone from zero to building real AI agents. With uv’s blazing speed, you set up a pro environment in seconds. Then, with Agno 2.x, you built an agent that chats, surfs the web, remembers with SQLite, masters RAG with LanceDB, and teams up for complex tasks.

Key wins:

  • uv: One-stop setup replacing pip, venv, and more — with .env auto-loading baked in.
  • Agno agents: Lightweight, modular, and fast, with memory, tools, and RAG that make your agents useful out of the box.
  • Multi-agent teams: Coordinate specialized agents that tackle big questions together.

What’s next? A few directions worth chasing:

  1. Dive into Agno’s extras — multimodal inputs (images, audio, video), workflows (deterministic agent pipelines), and 100+ pre-built toolkits. Try building Agno workflows for structured pipelines.
  2. Explore agent.cli_app() — a built-in interactive chat loop that replaces the typer/rich pattern with zero extra dependencies.
  3. Ship it with AgentOS — turn your agent into a production REST API with tracing and RBAC (see the production notes above).

Agno 3.0 is in alpha too (v3.0.0a1), so the framework is moving fast. Worth keeping an eye on the changelog.

Now go whip up that Thai chicken soup your agent’s been raving about. You’ve got the code, the skills, and the tools — go build something.

Explore Agno Workflows Build a Research Squad

FAQ

What Python version does Agno support?

Agno requires Python >=3.9, <4. This tutorial pins Python 3.12 for consistency, but anything 3.9+ will work. If you need help setting up Python, here’s how to install Python on Mac.

Can I use a local model instead of OpenAI?

Yes. Use "ollama:llama3.1:8b" (or any model you have pulled in Ollama) as the model string. You need Ollama running locally — no API key needed. The tradeoff: local models are slower and the quality depends on your hardware, but you pay zero per token.

How much does it cost to run this tutorial?

With gpt-5.4-mini (Agno’s default), under $0.10 for the whole walkthrough including the RAG and multi-agent steps. With gpt-5.5, expect a few dollars depending on output length. Embeddings (text-embedding-3-small) add fractions of a cent for this volume.

Is Agno telemetry opt-out?

Yes. Set AGNO_TELEMETRY=false in your environment or .env file. Agno only sends the model provider choice — never prompts, outputs, or personal data.

What's the difference between Agno and other frameworks like Mastra or CrewAI?

Agno is Python-first, lightweight (~no heavy abstractions), and ships AgentOS for deployment out of the box. Mastra is TypeScript-first and good if you’re already in the Node ecosystem — here’s how to build an AI agent with Mastra. CrewAI and LangGraph are heavier orchestration frameworks with more built-in abstractions but more boilerplate. Agno’s sweet spot is “add what you need, skip what you don’t.”