Company OAsAll ProblemsOA CalendarInterview ExperiencesPremium
OAHelper

Built by students, for students - practice company-specific OAs, DSA sheets, and real interview experiences to land your dream role.

© 2026 OAHelper.in·Terms·Privacy·Refunds·Trust & Safety·Contact·
Ready to crack your next OA?

Practice company-specific questions trusted by thousands of students across India.

Start PracticingGo Premium
OA Practice·DSA·Placements

Disclaimer: OAHelper is an independent educational platform. We (oahelper.in) do not own the images or questions shown. Content is uploaded by users.

Module 03

Agno and Mastra

  • LangGraph Stateful Graphs
  • AutoGen Actor Model
  • CrewAI Role-Based Crews
  • OpenAI Agents SDK
  • Claude Agent SDK
  • Agno and Mastra
On this page

This week

  • LangGraph Stateful Graphs
  • AutoGen Actor Model
  • CrewAI Role-Based Crews
  • OpenAI Agents SDK
  • Claude Agent SDK
  • Agno and Mastra

In plain words

Big agent frameworks are like a wedding dinner: lots of structure, slow and planned. A runtime is like the canteen counter at 1 PM: thin, fast, gets out of your way. Agno does this for Python teams on FastAPI, Mastra does it for TypeScript teams. Both let you drop an agent loop into the backend you already have.

How it flows

  1. 1Request arrives→
  2. 2Build fresh agent→
  3. 3Load session from DB→
  4. 4Run the loop→
  5. 5Save session back

A tiny example

Python
def handle_request(user_id, question):
    agent = Agent(tools=[check_pnr])   # cheap to build, every time
    history = db.load(user_id)         # memory lives outside
    reply = agent.run(question, history)
    db.save(user_id, history + [reply])
    return reply

Notice the agent is created fresh on every request and holds no memory itself; the database does.


What you will learn

  • Why a "runtime" is different from a big agent framework.
  • What Agno gives a Python team and what Mastra gives a TypeScript team.
  • Why fresh-agent-per-request with state in a database is a safe production shape.
  • How to pick between them without falling for speed numbers you do not need.

The problem, simply

Think about your college canteen at 1 PM. Two hundred students walk in. Nobody wants a seven-course menu. One plate, fast, and out.

A wedding dinner is the opposite: many courses, servers who remember who wanted extra sweet. Slow and personal. But run the canteen like a wedding and the queue never clears.

Agent software has the same split. In Module 3 you saw heavy frameworks like LangGraph, CrewAI and AutoGen. Those are the wedding: they own graphs, state and roles for you. Useful when one agent runs for ten minutes on a hard task.

But plenty of teams say: "See, I already have a backend, FastAPI or Next.js. I do not want a whole framework on top. Just give me the agent loop and get out of my way." That is what a runtime is. Agno is that answer for Python, Mastra for TypeScript.

The idea

A runtime is thin on purpose

A framework asks you to build inside it. A runtime sits inside what you already built. So it cares about three boring things: how cheap it is to start an agent, how clean the tool shapes are, and how easily it drops into a normal web server.

  1. 1Request comes in→
  2. 2Make fresh agent→
  3. 3Load session from DB→
  4. 4Run the loop→
  5. 5Save session→
  6. 6Reply

Agno: fast, plain Python

Agno is a Python agent runtime. Their own line is basically "no graphs, no chains, just pure Python."

What they advertise is instantiation cost: their docs cite roughly 2 microseconds to create an agent and about 3.75 KiB of memory per agent. They support around 23 model providers, plus multimodal input (text, image, audio, video, files) and agentic RAG, which just means the agent itself decides when to search your documents.

The recommended production shape is a stateless, session-scoped FastAPI backend. Plain words: every HTTP request builds a brand new agent object, pulls that user's session history out of a database, does the work, writes the session back, and dies.

IMP

Note: "Stateless" does not mean the agent forgets. It means the server process holds nothing. The memory lives in the database, so any server box can serve any user.

Suppose Priya builds a doubt-solving bot for her college. At 9 PM before an exam, 4,000 students open it at once. A heavy agent object means that setup cost gets multiplied by 4,000. At microseconds, it disappears. That is exactly where Agno's numbers matter.

Now flip it. Rahul's agent reads a 40-page PDF and writes a report. One request takes 90 seconds. Saving 2 microseconds changes nothing. The model call is the bottleneck.

IMPRemember: speed numbers only matter when the thing being measured is actually your bottleneck.

Mastra: typed pieces for TypeScript

Mastra is the TypeScript side. It is built on the Vercel AI SDK, which is a common TypeScript library for talking to models.

It gives you three building blocks, and you should be able to name all three in an interview:

  • Agents — a model plus instructions plus tools.
  • Tools — functions the agent can call, with typed inputs. Mastra uses Zod, a TypeScript library that checks a value's shape at runtime. If a tool needs { pnr: string } and the model sends a number, it is rejected before your code runs.
  • Workflows — fixed multi-step pipelines, for when you want a defined order instead of letting the model decide every step.

Two more things worth knowing. The Unified Model Router is one client surface reaching thousands of models across many providers, so switching provider is a config change, not a rewrite. And composite storage means memory, workflows and observability can each go to a different backend.

It plugs into normal TypeScript servers (Express, Hono, Fastify, Koa) with first-class Next.js and Astro support, and ships Mastra Studio, a local debugging UI.

  1. 1Typed tool input→
  2. 2Zod checks shape→
  3. 3Agent runs step→
  4. 4Workflow next step→
  5. 5Store result

Choosing, without drama

  • Agno — your backend is Python and FastAPI, and you expect many short-lived agents.
  • Mastra — your backend is TypeScript, you deploy on Next.js, you want typed tools and easy provider switching.
  • LangGraph — see the LangGraph lesson in Module 3. Pick it when durable state and an explicit graph matter more than raw speed.
  • A provider's own SDK — the OpenAI and Claude Agent SDK lessons in Module 3. Pick when you are happy with the shape that provider ships.
Warning

Warning: Mastra is Apache 2.0 except for the ee/ directories, which are source-available. Source-available means you can read the code but commercial use is restricted. If you plan to fork it for a product, read those terms first.

Build it

Here is a toy that shows the two shapes side by side. No real model, no network.

Python
# Two runtime shapes, same job: answer a student with session memory.
SESSION_DB = {}  # stands in for a real database

def fake_model(prompt):
    if "pnr" in prompt.lower():
        return "CALL check_pnr"
    return "Your seat is confirmed."

def check_pnr(pnr):
    return f"PNR {pnr}: CNF, coach S4"

# ---------- Shape 1: Agno-style. Fresh agent per request, state in DB ----------
class Agent:
    def __init__(self, name, tools):
        self.name = name          # building this is meant to be very cheap
        self.tools = tools

    def run(self, user_id, question):
        history = SESSION_DB.get(user_id, [])      # load session
        reply = fake_model(question)
        if reply.startswith("CALL "):
            tool = self.tools["check_pnr"]
            reply = tool("4207891234")
        history.append((question, reply))
        SESSION_DB[user_id] = history              # save session, then die
        return reply

def handle_request(user_id, question):
    agent = Agent("helper", {"check_pnr": check_pnr})  # built new every time
    return agent.run(user_id, question)

# ---------- Shape 2: Mastra-style. Typed tools inside a fixed workflow ----------
def typed_tool(schema, fn):
    """Reject bad input before the real function ever sees it."""
    def wrapped(args):
        for key, kind in schema.items():
            if key not in args or not isinstance(args[key], kind):
                return f"REJECTED: '{key}' must be {kind.__name__}"
        return fn(**args)
    return wrapped

pnr_tool = typed_tool({"pnr": str}, check_pnr)

WORKFLOW = ["understand", "call_tool", "reply"]

def run_workflow(question, args):
    out = []
    for step in WORKFLOW:
        if step == "understand":
            out.append(f"model says: {fake_model(question)}")
        elif step == "call_tool":
            out.append(f"tool says: {pnr_tool(args)}")
        else:
            out.append("workflow finished")
    return out

if __name__ == "__main__":
    print("Agno-style:")
    print(" ", handle_request("priya", "check my pnr"))
    print(" ", handle_request("priya", "is it confirmed?"))
    print("  session rows for priya:", len(SESSION_DB["priya"]))

    print("\nMastra-style (good input):")
    for line in run_workflow("check my pnr", {"pnr": "4207891234"}):
        print(" ", line)

    print("\nMastra-style (bad input):")
    for line in run_workflow("check my pnr", {"pnr": 4207891234}):
        print(" ", line)

Look at three things. First, handle_request builds a new Agent every call, yet Priya's history still grows to 2 rows, because memory lives in SESSION_DB. Second, the workflow always runs the same three steps in order. Third, when the PNR is sent as a number, the typed tool refuses it and check_pnr is never reached. That is what Zod-style typing buys you.

Where you will see this

  • Chat support bots on Indian e-commerce and travel sites, where thousands of short conversations arrive at once.
  • Evaluation pipelines that spawn one agent per test case, where startup cost actually adds up.
  • Next.js products that want an agent endpoint beside their existing API routes, with no separate Python service.
  • Internal tools at Python-and-FastAPI shops that do not want another framework in the dependency list.
  • Teams that switch model providers often and cannot afford a rewrite each time.

Common mistakes

  • Picking a runtime for its benchmark numbers alone. If your request already waits several seconds on a model, microseconds of startup are invisible. You optimised the wrong line.
  • Keeping session state in a module-level variable in "stateless" mode. It works on your laptop with one process. In production with four server instances, users randomly lose their history.
  • Ignoring the license before forking. Mastra's ee/ parts are source-available, not open source. Finding that out after you ship is an expensive surprise.
  • Assuming a runtime replaces a graph framework. These are deliberately thin. If your task needs durable checkpoints and branching, you will end up rebuilding LangGraph badly.
  • Choosing against your team's language. A Python team running Mastra spends its time on plumbing instead of the product.

If they ask in an interview

Q: What is the difference between an agent framework and an agent runtime?

A: A framework owns the structure and you build inside it, like LangGraph with its graphs and checkpoints. A runtime is thin and sits inside your existing backend, giving you just the agent loop and tool calling. You pick a runtime when you already have a server and do not want another layer.

Q: Why does Agno recommend a stateless session-scoped backend?

A: A fresh agent per request means no state is stuck inside one server process, so you can run many identical instances and load-balance freely. Session history goes to a database, so any instance can serve any user. It works only because creating an agent is extremely cheap in Agno.

Q: What do you get from Mastra's typed tools?

A: Tool inputs are validated against a Zod schema before your function runs, so a wrongly shaped argument from the model is rejected at the boundary instead of crashing deep in your code. It also gives the model a clear contract for each tool, which reduces bad calls in the first place.

Try these

  1. Add a second user, Karthik, to the toy. Confirm his history stays separate from Priya's even though a new agent is built each time.
  2. Break the stateless rule: move history into the Agent object instead of SESSION_DB. Watch the memory vanish, and write one line on why this bites you in production.
  3. Add a second typed tool, book_seat, that needs {"train": str, "count": int}. Send it a string count and check that it is rejected.
  4. Use time.perf_counter to build 100000 Agent objects and print the total. Decide honestly whether that number matters when each model call takes two seconds.

Words, simply

WordMeaning in simple words
RuntimeA thin layer that runs the agent loop inside your existing backend
AgnoA Python agent runtime built for very cheap agent creation and FastAPI
MastraA TypeScript agent runtime with agents, tools and workflows
Stateless backendThe server keeps nothing between requests; memory lives in a database
Instantiation costHow much time and memory it takes to create one agent object
Typed toolA tool whose inputs are checked for correct shape before it runs
Model routerOne client that can reach many models from many providers
Source-availableYou may read the code, but commercial use is restricted

Quick recap

  • A runtime is not a framework: it fits into your backend instead of asking you to build inside it.
  • Agno is the Python answer (cheap agents, stateless FastAPI, session in a DB); Mastra is the TypeScript answer (agents, tools, workflows, typed inputs, one model router).
  • Pick by your team's language and your actual bottleneck, not by whose benchmark number sounds most impressive.

Check what you learned

1 / 7. Which language does each runtime belong to?
1/7
PreviousClaude Agent SDKNextSWE-bench and GAIA

On this page