Ask any team what it takes to ship one production agent, and the answer is usually a shopping list: a model client for your provider, a vector database for document retrieval, a memory store for user facts, somewhere durable to persist sessions, and a hand-rolled loop that stitches tool calls together. That is five repositories, five sets of dependency versions, and five distinct places for things to break at 2 a.m. Composable stacks like LangChain give you excellent parts, but the seams between the parts remain yours to hold together.

Agno takes the opposite bet: the runtime ships the whole stack. A single Agent object carries tools, a knowledge base, memory, and storage hooks; Team coordinates several agents; Workflow chains steps deterministically. Everything lives in one package under libs/agno/agno/ in the monorepo, distributed as the agno package (pyproject.toml, L2, currently version 3.1.12). As the finale of our agent-framework source tours, it is the batteries-included runtime that stops asking you to glue libraries together.

This tour pins the code at main and asks one question: what actually happens between agent.run("...") and the returned text? We follow Agent.run, L1523 through system-prompt assembly, knowledge and memory retrieval, the provider tool loop, and the final structured response β€” then map where Team, L83 orchestration and session persistence plug in. The two diagrams below render the same tour at different zoom levels.

Agno architecture overview: Agent.run fanning out into context assembly, knowledge and memory retrieval, the model tool loop, session persistence, plus Team and Workflow orchestration

Agno overview: one Agent.run fans out into context assembly, the model/tool loop, and persistence; Team and Workflow wrap the very same runtime rather than replacing it.

Reading the overview from left to right:

Agno detail diagram: numbered stages inside Agent._arun, the provider tool loop in Model.response, team delegation via delegate_task_to_member, and session upserts into BaseDb

Agno detail: the numbered stages of Agent._arun, the provider tool loop inside Model.response, team delegation, and the single session-upsert path.

One Runtime, Five Primitives

The monorepo layout tells the design story. agent/ holds the Agent class (agent.py, L82) plus nine private modules β€” _run.py, _messages.py, _response.py, _tools.py, _session.py β€” that split the runtime into testable units. Open _arun, L1513 and the stages are literally numbered in comments: tools resolve first via determine_tools_for_model, L2260, messages assemble at stage 6, memory extraction starts at stage 7, reasoning runs at stage 8, and the model answers at stage 9. There is also a resume path: continue_run, L1685 and its async twin acontinue_run, L1769 replay a stored checkpoint, which is how a paused run β€” say, one waiting on a tool confirmation β€” picks up without losing state. team/ and workflow/ reuse the same session and message machinery rather than reimplementing it, which is why a Workflow step can embed a Team that embeds Agents without any adapter code. Tools are a registry concern: add_tool, L853 accepts a Toolkit, a callable, or a raw dict and normalizes everything into Function objects. Knowledge (Knowledge, L86) and memory are injected as components, so swapping PgVector, L65 for Qdrant or Milvus changes one constructor argument, not the run loop. Workflows compose the other primitives declaratively: Workflow, L643 takes steps built from Step, Loop, Parallel, Router, and Condition operators, each with its own module under workflow/, so deterministic stages and agent decisions mix in one graph. And when a provider call dies mid-run, _try_fallback_models, L387 retries the prompt on the next entry in fallback_models, emitting a fallback_model_activated event so clients know the answer came from plan B.

From Install to Your First Model

The README has pivoted to a coding-agent-first flow: it suggests handing your coding agent a prompt that clones the agentos-railway template and follows a setup skill (README.md, L33), with prebuilt templates for Docker, Railway, AWS, GCP, and more (README.md, L56). If you just want the library, the distribution is named agno (pyproject.toml, L2), so pip install agno or uv add agno is enough; contributors instead clone the repo and run pip install uv plus ./scripts/dev_setup.sh (CONTRIBUTING.md, L39). The first agent from cookbook/02_agents/01_quickstart/basic_agent.py is deliberately minimal:

from agno.agent import Agent
from agno.models.openai import OpenAIResponses

agent = Agent(
    name="Quickstart Agent",
    model=OpenAIResponses(id="gpt-5.2"),
)

agent.print_response("Say hello and introduce yourself in one sentence.", stream=True)

Every model provider lives behind the same Model, L134 interface β€” invoke, L553 is the single provider call β€” so switching from OpenAI to Anthropic is an import change.

Inside the Tool Loop

The async run reaches the model in stage 9: _arun, L2338 hands control to ahandle_model_response_stream, L1193, which wraps the provider call in acall_model_stream_with_fallback, L328 β€” a wrapper that detects context-overflow errors, compacts history, and can activate fallback models. Tool calls stream back as events, get dispatched by handle_tool_call_updates, L1086, and execute through run_tool, L869 after determine_tools_for_model, L662 has resolved which toolkits the chosen provider supports. Stage 8, just before the model call, belongs to reasoning: ahandle_reasoning_stream, L125 gives the model a scratchpad pass and emits step-by-step events. Structured output can then take one of two extra passes β€” a cheap parser model rewriting the first answer (parse_response_with_parser_model, L349) or a dedicated output model finishing the run (generate_response_with_output_model, L582). Everything surfaces as typed events on the result object β€” ToolCallStartedEvent, L427, SessionSummaryStartedEvent, L388 β€” so a UI can render exactly what the runtime is doing. Where LangGraph internals make the loop an explicit state machine you compile yourself, Agno hides the loop inside Model.response and exposes controls β€” limits, confirmation gates, streaming events β€” at the edges.

Teams: Coordination as a Tool Call

Team.run, L917 delegates to its own dispatch module (_run.py, L1902) and branches on TeamMode, L6: coordinate has a leader model pick members and craft tasks; route sends work to one specialist and returns its answer untouched; broadcast gives the same task to every member, concurrently under arun. The modes compile into two flags at construction: route sets respond_directly, broadcast sets delegate_to_all_members, and anything else falls back to coordinate (_init.py, L216). A fourth mode, tasks, decomposes a goal into a shared task list and loops until the work is done. The clever part is that coordination is implemented as ordinary tool calls β€” the leader receives delegate_task_to_member, L713 like any other function, so no separate planner process exists. Members can be Agents or whole Teams (team.py, L88), which is how hierarchies form. If you liked CrewAI’s role-play framing, Teams feel similar, but the member-selection logic here is just the same tool loop you read above, running one level up.

Where State Lives

Three stores cover state. Sessions: get_session, L574 and upsert_session, L683 on BaseDb, L302, with sixteen adapters from Postgres and SQLite to Mongo, Dynamo, and Firestore. Memory: MemoryManager, L46 extracts per-user facts with a model call and reads them back via search_user_memories, emitting MemoryUpdateStartedEvent, L377 when the background task fires. Knowledge: documents flow into one of twenty-one vector-store adapters, each implementing the same search contract, and tenancy is handled inside the query β€” Knowledge.search, L1187 forwards a user_id scope to the store (knowledge.py, L1246) and injects an instance-scope filter (knowledge.py, L1230). That memory layer is the main overlap with Mem0 β€” which does exactly this one thing, with deeper graph variants β€” while Agno trades that depth for keeping memory, RAG, and sessions on one config object.

Honest Limits

  • Telemetry is on by default. Agno sends one event per agent; disable it with AGNO_TELEMETRY=false (README.md, L96). No prompts or outputs are transmitted, but it is worth knowing on day one.
  • The README no longer contains a quickstart. Setup flows through docs.agno.com links and cloud templates, so the repo front door assumes you want the AgentOS platform, not just the runtime library (README.md, L17).
  • Version churn is real. The package sits at 3.1.12 with a platform pivot behind it; code written against older releases needs migration review before upgrading.
  • Config sharp edges exist. Setting a reranker on both Knowledge and the vector db raises a warning because only one component may own rescoring (knowledge.py, L172).
  • Breadth over depth. Twenty-one vector stores and sixteen session stores is impressive surface, but each adapter inherits the store’s quirks; budget time to validate the one you actually deploy. Python support is >=3.9,<4 (pyproject.toml, L5).
Watch PyShine on YouTube

Contents