By Sagar Shankaran, Founder of CallSphere
Learn how to build AI agent pipelines with Prefect. Covers flow definition, task decorators, deployments, scheduling, and real-time monitoring for agent workloads.
Key takeaways
Prefect takes a Python-native approach to workflow orchestration. Unlike systems that require you to learn a new DSL or configuration language, Prefect lets you turn any Python function into a tracked, retryable, observable workflow step by adding a single decorator. For AI engineers already writing agent logic in Python, this means near-zero friction to go from a working script to a production pipeline.
Prefect 3.x introduced native async support, improved caching, and a completely redesigned task runner — all features that align well with the async, IO-heavy nature of AI agent workloads.
pip install prefect
prefect server start # Local server with UI at http://localhost:4200
A Prefect flow is the top-level orchestration function. Tasks are individual units of work within a flow that get their own retry logic, caching, and observability.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
flowchart LR
INPUT(["User intent"])
PARSE["Parse plus<br/>classify"]
PLAN["Plan and tool<br/>selection"]
AGENT["Agent loop<br/>LLM plus tools"]
GUARD{"Guardrails<br/>and policy"}
EXEC["Execute and<br/>verify result"]
OBS[("Trace and metrics")]
OUT(["Outcome plus<br/>next action"])
INPUT --> PARSE --> PLAN --> AGENT --> GUARD
GUARD -->|Pass| EXEC --> OUT
GUARD -->|Fail| AGENT
AGENT --> OBS
style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style OUT fill:#059669,stroke:#047857,color:#fff
from prefect import flow, task
from prefect.tasks import task_input_hash
from datetime import timedelta
import httpx
@task(
retries=3,
retry_delay_seconds=[10, 30, 60],
cache_key_fn=task_input_hash,
cache_expiration=timedelta(hours=1),
log_prints=True,
)
async def call_llm(prompt: str, model: str = "gpt-4") -> str:
"""Call an LLM with automatic retries and response caching."""
async with httpx.AsyncClient(timeout=90) as client:
response = await client.post(
"https://api.openai.com/v1/chat/completions",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": model,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.0,
},
)
response.raise_for_status()
result = response.json()["choices"][0]["message"]["content"]
print(f"LLM returned {len(result)} chars")
return result
@task(retries=2, retry_delay_seconds=5)
async def fetch_context(query: str) -> list[dict]:
"""Retrieve relevant documents from a vector store."""
async with httpx.AsyncClient(timeout=30) as client:
response = await client.post(
"http://localhost:8000/search",
json={"query": query, "top_k": 5},
)
response.raise_for_status()
return response.json()["results"]
@task
async def format_report(answer: str, sources: list[dict]) -> str:
"""Format the agent output as a structured report."""
source_list = "\n".join(
f"- {s['title']}: {s['snippet']}" for s in sources
)
return f"## Answer\n\n{answer}\n\n## Sources\n\n{source_list}"
@flow(
name="research-agent",
description="Multi-step research agent with RAG",
log_prints=True,
timeout_seconds=600,
)
async def research_agent_flow(query: str) -> str:
# Step 1: Retrieve context
context = await fetch_context(query)
print(f"Retrieved {len(context)} context documents")
# Step 2: Build prompt with context
context_text = "\n".join(
f"[{c['title']}]: {c['snippet']}" for c in context
)
prompt = (
f"Answer this question using the provided context.\n\n"
f"Question: {query}\n\nContext:\n{context_text}"
)
# Step 3: Generate answer
answer = await call_llm(prompt)
# Step 4: Format and return
report = await format_report(answer, context)
return report
# Run locally
if __name__ == "__main__":
import asyncio
result = asyncio.run(
research_agent_flow("What is retrieval-augmented generation?")
)
print(result)
Prefect deployments let you trigger flows on schedules, via API, or from events.
from prefect import flow
from prefect.runner import serve
async def deploy():
research_deployment = await research_agent_flow.to_deployment(
name="scheduled-research",
cron="0 */6 * * *", # Every 6 hours
parameters={"query": "latest AI agent frameworks"},
tags=["research", "production"],
)
await serve(research_deployment)
Prefect supports concurrent task execution for agent steps that are independent.
from prefect import flow, task
import asyncio
@flow
async def multi_query_agent(queries: list[str]) -> list[str]:
"""Run multiple research queries in parallel."""
tasks = [call_llm(q) for q in queries]
results = await asyncio.gather(*tasks)
return list(results)
Prefect provides a built-in dashboard at http://localhost:4200 showing flow runs, task states, logs, and timing. Each task run displays its status (Completed, Failed, Retrying, Cached), duration, and any logged output. You can filter by flow name, deployment, or tags.
For programmatic monitoring, query the Prefect API:
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
from prefect.client.orchestration import get_client
async def check_recent_runs():
async with get_client() as client:
runs = await client.read_flow_runs(
limit=10,
sort="EXPECTED_START_TIME_DESC",
)
for run in runs:
print(f"{run.name}: {run.state_name} ({run.total_run_time})")
Prefect retries tasks within the same process by default, while Temporal dispatches activities to separate workers. Prefect is simpler to set up but does not provide the same cross-process durability. If your worker process dies, Prefect loses in-progress task state unless you configure external result storage.
Yes. Use the cache_key_fn=task_input_hash parameter on your task decorator. Prefect hashes the task inputs and returns the cached result if the same inputs appear within the cache_expiration window. This is particularly useful for deterministic LLM calls with temperature=0.
No. Prefect runs entirely self-hosted with prefect server start. Prefect Cloud adds managed infrastructure, RBAC, automations, and push work pools, but the open-source server covers all core orchestration features.
#Prefect #WorkflowOrchestration #Python #AIPipelines #MLOps #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
Run offline evals as a CI gate. GitHub Actions wiring, threshold gates, LangSmith Experiments, and how to block merges on agent regression — with real YAML.
A principal engineer's playbook for curating, versioning, and growing a golden dataset for an agent — from production trace mining to annotation queues in LangSmith.
Step-by-step build of a working agent with the OpenAI Agents SDK — Agent class, tools, handoffs, tracing — plus an eval pipeline that catches regressions before merge.
Smolagents lets agents write Python instead of JSON. Why code-as-action reduces tool errors and where the security trade-offs are for production deployments.
Three RAG evaluation frameworks compared on real production RAG pipelines: RAGAS, TruLens, and DeepEval. Strengths, weaknesses, when to use each.
LLM tokens are the visible cost. The hidden 60-70% — evals, observability, guardrails, human review — is where TCO actually lives.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.