HomeAboutOur TeamContact
HomeArtificial Intelligence
Async and Await in Python: A Primer for AI Agent Code (2026)

Async and Await in Python: A Primer for AI Agent Code (2026)

Artificial Intelligence
June 22, 2026
4 min read
Beginner
Table of Contents
01
What async and await actually mean: waiting without freezing
02
async def and await: the only two keywords
03
Running async code: asyncio.run
04
Doing things at once: gather and TaskGroup
05
The two mistakes that bite beginners
06
When you don't actually need async
07
Quick recap
08
Frequently Asked Questions
09
Conclusion

Open almost any AI agent example and the first line of real code is async def. Then every interesting call has an await in front of it, and the whole thing ends with asyncio.run(main()). If that syntax makes a simple tutorial feel advanced, you are not missing anything deep — you are missing two keywords that nobody stopped to explain. This async and await in Python primer explains them, so agent code reads like plain English.

You do not need to master async to build agents in 2026. You need to read it, because the LLM SDKs and frameworks you will use ship async-first examples. By the end of this short read you will know what async def and await mean, when they help, and the two mistakes that catch every beginner.

🟢 Beginner⏱️ 12 min readStack: Python 3.10+ (reading level — no agent code needed)
Before you start
  • You can read a basic Python function and run a .py file — new to that? The Python for AI agents primer covers the basics
  • That’s the whole list — this primer assumes no async experience at all
🎯 Key takeaways
  • async def makes a coroutine: calling it doesn’t run the body, it hands you a coroutine you have to await.
  • await means “pause here until this finishes” and lets the event loop run other work while you wait — that’s concurrency, not parallelism.
  • asyncio.run(main()) is the on-ramp: it starts the event loop and runs your top-level async function.
  • The two beginner traps are forgetting await (nothing runs) and calling slow blocking code inside async (everything freezes).

What async and await actually mean: waiting without freezing

Picture a barista taking an order, starting the espresso machine, and — instead of standing there watching it drip — taking the next customer’s order while the machine works. Same person, one counter, but no idle waiting. That is async: one thread that switches to other work whenever it would otherwise be stuck waiting.

The thing it waits on is almost always I/O (input/output) — a network request, a database query, an LLM API call. These are slow not because your computer is busy, but because it’s waiting on someone else’s server. Async lets a single program fire off one API call and start another while the first is still in flight. Async is about not wasting time waiting, not about doing two calculations at once. That second thing is parallelism, and it’s a different tool (threads and processes).

This is exactly why agent code is async-first: an agent’s whole life is waiting on model and tool calls over the network. Async is the natural fit.

async def and await: the only two keywords

Here are the two pieces of syntax you’ll read constantly. A function defined with async def is a coroutine. The surprise for newcomers: calling it does not run the body.

python
import asyncio
async def get_weather(city: str) -> str:
await asyncio.sleep(1) # pretend this is a slow API call
return f"It's sunny in {city}."
result = get_weather("Pune") # ⚠️ does NOT run — result is a coroutine object

To actually run a coroutine, you await it from inside another async function. await means “run this, and pause me until it returns — and while I’m paused, let other work happen.”

python
async def main():
report = await get_weather("Pune") # runs it, waits for the result
print(report)
🔑 The one-sentence version`async def` defines work that can be paused; `await` is where it pauses and resumes. Every other piece of asyncio is built on those two ideas.

Running async code: asyncio.run

You can only await inside an async function — so how does the first one start? That’s what asyncio.run() is for. It starts the event loop (the scheduler that runs coroutines and switches between them) and runs your top-level coroutine to completion.

python
import asyncio
async def main():
print(await get_weather("Delhi"))
asyncio.run(main()) # the single entry point that boots the loop

The rule of thumb: asyncio.run(main()) appears exactly once, at the very bottom of the script. Everything async flows out of that one call.

Doing things at once: gather and TaskGroup

The real payoff comes when you await several slow things together instead of one after another. The modern, safe way is asyncio.TaskGroup (Python 3.11+), which starts tasks and waits for all of them — and cancels the rest if one fails.

python
async def main():
async with asyncio.TaskGroup() as tg:
t1 = tg.create_task(get_weather("Pune"))
t2 = tg.create_task(get_weather("Delhi"))
print(t1.result(), t2.result()) # both ran concurrently

Run two one-second calls sequentially and it takes two seconds; run them in a TaskGroup and it takes about one. For an agent calling several tools at once, that difference is the whole point.

💡 TaskGroup over gather in 2026Older tutorials use `asyncio.gather(...)`. It still works, but `TaskGroup` has cleaner error handling — if one task fails, it cancels the others instead of leaving them running. Prefer it for new code.

The two mistakes that bite beginners

Almost every async bug a beginner hits is one of these two, so learn to spot them now.

Forgetting await. Call a coroutine without await and the body never runs — you get a coroutine object and a “coroutine was never awaited” warning, with no error and no result. If an async call seems to do nothing, check for a missing await before anything else.

Blocking the loop. The event loop only switches at an await. Drop a slow synchronous call into an async function — time.sleep(5), requests.get(...), a heavy CPU loop — and the whole program freezes, because nothing yields control back to the loop.

⚠️ Use the async version of slow callsInside async code, use `await asyncio.sleep()` not `time.sleep()`, and an async HTTP client like `httpx.AsyncClient` not `requests`. A blocking call in a coroutine defeats the entire purpose of async — one frozen call stalls every other task.

When you don’t actually need async

Async is not free — it adds keywords, an event loop, and a class of bugs. If your script makes one API call and exits, plain synchronous code is simpler and just as fast. Reach for async when you’re doing many I/O operations and waiting is the bottleneck: several tool calls at once, a web server handling many requests, a batch of documents to fetch. That’s why agent frameworks default to it — but a small one-shot script doesn’t need it.

Quick recap

Everything above, in five lines:

  • async def defines a coroutine; calling it returns an object you must await.
  • await runs a coroutine and pauses until it’s done, freeing the loop for other work.
  • asyncio.run(main()) boots the event loop — once, at the bottom of the script.
  • TaskGroup runs several coroutines concurrently and fails safely.
  • Watch for a missing await (nothing runs) and blocking calls (everything freezes).

Frequently Asked Questions

What do async and await actually do? async def makes a coroutine that doesn’t run until awaited; await runs it and pauses the caller until it returns, letting the event loop do other work meanwhile.

Do I need async to build AI agents? You need to read it, because frameworks ship async examples. Writing your own comes later, when you actually need concurrency.

Async or threads? Async (single thread, switches at await) suits I/O-bound work like API calls; threads suit CPU-bound work. Agents are I/O-bound, so async fits.

Why doesn’t my coroutine run? You called it without await. Add await inside an async function, or run the top one with asyncio.run().

Conclusion

Async stops being intimidating the moment you read async and await as one idea: async def marks work that can pause, await is where it pauses and resumes, and asyncio.run starts the whole thing. That’s enough to follow any agent quickstart line by line — which is the real goal, since you’ll read far more async code than you write at first.

What’s the first piece of async agent code you want to actually understand — an LLM call, a tool, a whole agent loop? Tell me in the comments.

🧭 Where to go from here

References

  1. Coroutines and Tasks — Python documentation (asyncio)
  2. asyncio — Asynchronous I/O (Python docs)
  3. PEP 492 — Coroutines with async and await syntax
  4. asyncio.TaskGroup — Python documentation

Tags

#PythonForAI#AsyncAwait#Asyncio#PythonBasics#AIAgents#AIForDevelopers

Share

Previous Article
Which AI Agent Framework Should You Use in 2026?

Read next

AI agent versus workflow comparison banner showing a fixed Step 1 to Step 2 workflow path beside an agent where the model decides the path, on a dark background
AI Agent vs Workflow: What's the Actual Difference? (2026)
June 22, 2026
5 min
Beginner
See all by Sukhveer Kaur

Get new guides in your inbox

Practical AI, software engineering, and cloud articles — straight to your inbox. No spam, unsubscribe anytime.
Async and Await in Python: A Primer for AI Agent Code (2026)
4 min left
Sukhveer Kaur

Sukhveer Kaur

Software Developer & AI Engineer

Table Of Contents

1
What async and await actually mean: waiting without freezing
2
async def and await: the only two keywords
3
Running async code: asyncio.run
4
Doing things at once: gather and TaskGroup
5
The two mistakes that bite beginners
6
When you don't actually need async
7
Quick recap
8
Frequently Asked Questions
9
Conclusion
© 2026, All Rights Reserved.

Quick Links

Advertise with usOur TeamAbout UsEditorial StandardsContact Us

Social Media