Python asynchrone pour les développeurs d’agents
Bases d’asyncio, async def, await, boucle d’événements — le modèle mental de l’asynchronisme.
Python asynchrone pour les développeurs d’agents est une leçon AI Agents gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Agents, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Agents comprend 4 leçons au total.
Pourquoi utiliser l’asynchrone pour les agents ?
Les agents effectuent de nombreux appels liés aux entrées-sorties : API de LLM, requêtes web et requêtes de base de données. Le code synchrone reste inactif pendant que ces appels s’exécutent. Le code asynchrone effectue d’autres tâches pendant ces attentes, ce qui améliore considérablement le débit.
Bases de l’asynchrone : async def et await
async def déclare une fonction coroutine. await suspend l’exécution jusqu’à la fin de l’opération attendue, ce qui permet à la boucle d’événements d’exécuter d’autres coroutines entre-temps.
import asyncio
async def fetch_data(source: str) -> str:
print(f'Starting fetch from {source}')
await asyncio.sleep(1) # Simulates a network call
print(f'Finished fetch from {source}')
return f'Data from {source}'
async def main():
# Sequential: takes 2 seconds total
result1 = await fetch_data('source-A')
result2 = await fetch_data('source-B')
print('Sequential results:', result1, result2)
asyncio.run(main())
# asyncio.run() starts the event loop and runs main()
# It is the entry point for async programsLa boucle d’événements
La boucle d’événements est le cœur d’asyncio. Elle gère une file d’attente de coroutines et de rappels d’entrées-sorties, et les exécute lorsqu’ils sont prêts. Tout le code asynchrone s’exécute dans la boucle d’événements sur un seul thread.
import asyncio
async def task_a():
print('Task A: start')
await asyncio.sleep(2)
print('Task A: done')
async def task_b():
print('Task B: start')
await asyncio.sleep(1)
print('Task B: done')
async def main():
# asyncio.gather runs both tasks concurrently
# Total time: ~2 seconds (not 3)
await asyncio.gather(task_a(), task_b())
print('Both tasks complete')
# Expected output order:
# Task A: start
# Task B: start
# Task B: done <- after 1s
# Task A: done <- after 2s
# Both tasks complete
asyncio.run(main())Coroutines et threads
Les coroutines sont coopératives : elles cèdent explicitement le contrôle avec await. Les threads sont préemptifs : l’OS peut basculer de l’un à l’autre à tout moment. Les coroutines sont plus légères, ne rencontrent pas de problèmes liés au GIL pour les entrées-sorties et sont plus faciles à raisonner.
import asyncio
import threading
import time
# Thread approach: multiple OS threads
def thread_worker(name):
print(f'Thread {name}: start')
time.sleep(1) # Blocks the thread
print(f'Thread {name}: done')
threads = [threading.Thread(target=thread_worker, args=(i,)) for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()
print('---')
# Coroutine approach: single-threaded event loop
async def coro_worker(name):
print(f'Coro {name}: start')
await asyncio.sleep(1) # Suspends, does NOT block other coroutines
print(f'Coro {name}: done')
async def main():
await asyncio.gather(*[coro_worker(i) for i in range(3)])
asyncio.run(main())
# Both approaches run 3 tasks in ~1 second total, but coroutines use one threadPoint d’entrée asyncio.run()
asyncio.run() crée une nouvelle boucle d’événements, exécute la coroutine donnée jusqu’à son terme, puis ferme la boucle. C’est le point d’entrée standard des programmes asynchrones en Python 3.7 et versions ultérieures.
import asyncio
async def agent_main():
print('Agent starting')
# All agent async work goes here
results = await asyncio.gather(
asyncio.sleep(0.1), # Simulated LLM call
asyncio.sleep(0.1), # Simulated DB query
)
print('Agent done')
return 'complete'
# Run the agent
result = asyncio.run(agent_main())
print('Result:', result)
# WRONG: calling asyncio.run() inside an already-running event loop
# In Jupyter notebooks, use: await agent_main() directly
# Or: nest_asyncio.apply() then asyncio.run()Erreur courante : bloquer dans un contexte asynchrone
N’appelez jamais de fonctions bloquantes (time.sleep, requests.get, entrées-sorties synchrones sur des fichiers) dans du code asynchrone. Cela bloque toute la boucle d’événements et annule toute concurrence.
import asyncio
import time
import httpx
# WRONG: blocks the event loop
async def bad_agent_step():
time.sleep(2) # Blocks all other coroutines
# requests.get(url) # Also blocks - do NOT use requests in async code
return 'done'
# RIGHT: use async equivalents
async def good_agent_step():
await asyncio.sleep(2) # Suspends, other coroutines can run
async with httpx.AsyncClient() as client:
response = await client.get('https://api.example.com/data')
return response.text
# For CPU-intensive work: use run_in_executor
import concurrent.futures
async def cpu_intensive_step(data: str):
loop = asyncio.get_event_loop()
with concurrent.futures.ProcessPoolExecutor() as pool:
result = await loop.run_in_executor(pool, expensive_cpu_fn, data)
return result
def expensive_cpu_fn(data):
# CPU-bound work runs in separate process
return data.upper()
print('Blocking vs non-blocking patterns demonstrated')await oublié : erreur silencieuse
Oublier await ne lève pas d’erreur : cela renvoie un objet coroutine au lieu du résultat. Il s’agit d’une erreur silencieuse qui provoque ensuite des erreurs ou des résultats vides.
import asyncio
async def get_answer() -> str:
await asyncio.sleep(0.1)
return 'The answer is 42'
async def bad_call():
result = get_answer() # WRONG: forgot await
print(type(result)) # <class 'coroutine'> - not a string!
# Using result as a string here causes AttributeError or wrong behavior
return result
async def good_call():
result = await get_answer() # CORRECT
print(type(result)) # <class 'str'>
return result
async def main():
bad = await bad_call()
print('Bad result:', bad) # coroutine object, not the string
good = await good_call()
print('Good result:', good) # 'The answer is 42'
# Clean up the uncollected coroutine
if asyncio.iscoroutine(bad):
bad.close()
asyncio.run(main())Problème des boucles d’événements imbriquées
Appeler asyncio.run() à l’intérieur d’une boucle d’événements déjà active (par exemple dans Jupyter ou FastAPI) lève une RuntimeError. Solutions : utilisez directement await ou utilisez nest_asyncio dans les notebooks.
import asyncio
async def my_agent_coroutine():
await asyncio.sleep(0.1)
return 'done'
# In FastAPI or other async frameworks, the event loop is already running
# Use await directly in async endpoints:
async def fastapi_endpoint():
# WRONG inside async context:
# result = asyncio.run(my_agent_coroutine()) # RuntimeError!
# CORRECT: just await
result = await my_agent_coroutine()
return result
# In Jupyter notebooks: install nest_asyncio
# import nest_asyncio
# nest_asyncio.apply()
# Then asyncio.run() works
# Detect if running in event loop:
def run_agent(coro):
try:
loop = asyncio.get_running_loop()
# Already in async context
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor() as pool:
future = pool.submit(asyncio.run, coro)
return future.result()
except RuntimeError:
# No running loop
return asyncio.run(coro)
print('Nested event loop solution defined')asyncio.create_task
Utilisez asyncio.create_task() pour planifier l’exécution d’une coroutine sans attendre immédiatement qu’elle se termine. Vous pouvez ainsi démarrer plusieurs tâches et attendre leur achèvement plus tard.
import asyncio
async def background_job(job_id: int) -> str:
await asyncio.sleep(0.5)
return f'Job {job_id} completed'
async def main():
# Start all tasks without waiting
task1 = asyncio.create_task(background_job(1))
task2 = asyncio.create_task(background_job(2))
task3 = asyncio.create_task(background_job(3))
# Do other work while tasks run
print('Tasks started, doing other work...')
await asyncio.sleep(0.1)
print('Other work done')
# Now wait for all tasks
results = await asyncio.gather(task1, task2, task3)
print('All results:', results)
# Or wait for the first to complete
task_a = asyncio.create_task(background_job(4))
task_b = asyncio.create_task(background_job(5))
done, pending = await asyncio.wait([task_a, task_b], return_when=asyncio.FIRST_COMPLETED)
for t in pending:
t.cancel() # Cancel remaining tasks
print('First result:', done.pop().result())
asyncio.run(main())Gestionnaires de contexte asynchrones
De nombreuses bibliothèques asynchrones utilisent des gestionnaires de contexte asynchrones avec async with. Cela garantit l’initialisation et la libération correctes des connexions et des ressources dans le code asynchrone.
import asyncio
import httpx
async def fetch_multiple_urls(urls: list) -> list:
# async with ensures the client is properly closed
async with httpx.AsyncClient(timeout=10.0) as client:
# Fetch all URLs concurrently
tasks = [client.get(url) for url in urls]
responses = await asyncio.gather(*tasks, return_exceptions=True)
results = []
for url, response in zip(urls, responses):
if isinstance(response, Exception):
results.append({'url': url, 'error': str(response)})
else:
results.append({'url': url, 'status': response.status_code})
return results
# Async generators for streaming
async def stream_agent_events():
events = ['thinking', 'searching', 'generating', 'done']
for event in events:
await asyncio.sleep(0.2) # Simulate event arrival
yield event
async def consume_stream():
async for event in stream_agent_events():
print(f'Event: {event}')
asyncio.run(consume_stream())Gestion des erreurs dans le code asynchrone
Utilisez asyncio.gather(..., return_exceptions=True) pour intercepter les échecs individuels des tâches sans interrompre tout le lot. Vérifiez le type d’exception de chaque résultat.
import asyncio
async def might_fail(task_id: int) -> str:
await asyncio.sleep(0.1)
if task_id == 2:
raise ValueError(f'Task {task_id} failed')
return f'Task {task_id} succeeded'
async def robust_gather():
tasks = [might_fail(i) for i in range(1, 5)]
# return_exceptions=True: exceptions are returned as values, not raised
results = await asyncio.gather(*tasks, return_exceptions=True)
successes = []
failures = []
for i, result in enumerate(results):
if isinstance(result, Exception):
failures.append({'task': i + 1, 'error': str(result)})
else:
successes.append(result)
print(f'Succeeded: {len(successes)}, Failed: {len(failures)}')
print('Failures:', failures)
return successes, failures
asyncio.run(robust_gather())Vérification des connaissances : Python asynchrone
Vérifiez votre compréhension de Python asynchrone pour le développement d’agents.
Résumé de Python asynchrone
Règles essentielles de Python asynchrone pour les développeurs d’agents : utilisez async def/await pour toutes les opérations liées aux entrées-sorties ; n’appelez jamais de fonctions bloquantes dans un contexte asynchrone ; utilisez asyncio.gather() pour l’exécution parallèle ; utilisez return_exceptions=True pour les appels parallèles tolérants aux pannes ; utilisez async with pour gérer les ressources ; exécutez les tâches gourmandes en CPU dans un exécuteur de processus.
Questions Fréquemment Posées
La leçon « Python asynchrone pour les développeurs d’agents » est-elle gratuite ?
Oui — le texte complet de « Python asynchrone pour les développeurs d’agents » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Agents, passe à CoddyKit PRO. Le cours AI Agents comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Python asynchrone pour les développeurs d’agents » ?
Bases d’asyncio, async def, await, boucle d’événements — le modèle mental de l’asynchronisme. Tu pratiques AI Agents avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer AI Agents ?
Aucune expérience préalable n'est requise. AI Agents sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.
Combien de temps prend la leçon « Python asynchrone pour les développeurs d’agents » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon AI Agents ?
Oui. Chaque leçon AI Agents inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Python asynchrone pour les développeurs d’agents
- Files d’événements et courtiers de messages
- Exécution parallèle non bloquante des outils
- Frameworks d’agents asynchrones : LangChain et au-delà