- Runtime init and config
- run vs start vs stream vs deploy vs serve vs plan
- The control-plane AgentClient
- Structured output
- Credentials and secrets
- Plans and PLAN_EXECUTE
- Schedules
- Skills
AgentRuntime is the entry point. Use it as a context manager so workers shut down
cleanly. Config comes from AgentConfig.from_env() by default, or pass overrides.
from conductor.ai.agents import AgentRuntime, AgentConfig
# From env (AGENTSPAN_SERVER_URL etc.)
with AgentRuntime() as runtime:
runtime.run(agent, "hi")
# Explicit kwargs
with AgentRuntime(server_url="https://prod:8080/api",
api_key="...") as runtime:
...
# Or an AgentConfig
config = AgentConfig.from_env()
config.auto_start_server = False
with AgentRuntime(config=config) as runtime:
...AgentConfig is a dataclass; from_env() reads the AGENTSPAN_* environment
variables (full list in Getting started).
The Conductor Configuration object underneath is built from server_url and the
auth fields (api_key, or auth_key/auth_secret).
For one-off scripts, top-level functions use a shared singleton runtime:
import conductor.ai.agents as ag
ag.configure(server_url="https://prod:8080/api", auto_start_server=False) # before first run
result = ag.run(agent, "Hello!")
ag.shutdown() # explicit cleanup; not required for simple scriptsconfigure(...) must be called before the first run/start/stream. Available:
run, run_async, start, start_async, stream, stream_async, resume,
resume_async, deploy, deploy_async, serve, plan, configure, shutdown.
| Call | Blocks? | Returns | When |
|---|---|---|---|
runtime.run(agent, prompt) |
yes | AgentResult |
Simplest case — run and get the answer |
runtime.start(agent, prompt) |
no | AgentHandle |
Fire-and-forget; poll/control later |
runtime.stream(agent, prompt) |
iterates | AgentStream |
Watch events live; drive HITL |
runtime.deploy(*agents) |
yes | list[DeploymentInfo] |
CI/CD: compile + register, no execution |
runtime.serve(*agents) |
yes (blocks) | — | Long-lived worker process; polls until interrupted |
runtime.plan(agent) |
yes | dict |
Compile to a workflow def without running anything |
run/start/stream accept media=, session_id=, idempotency_key=,
credentials=, and extra **kwargs as workflow input. run/run_async also accept
on_event= to stream while running synchronously, timeout=, and context=.
plan(agent) returns {"workflowDef": ..., "requiredWorkers": ...} — useful to
inspect the compiled Conductor workflow:
result = runtime.plan(agent)
print(result["workflowDef"]["name"])
print(result["workflowDef"]["tasks"])# CI/CD step:
runtime.deploy(agent)
# CLI alternative:
# agentspan deploy --package my_pkg.my_module
# agentspan deploy --path ./agents --agents greeter,support
# Long-lived worker process:
runtime.serve(agent) # blocks, polling for tool tasksresume(execution_id, agent) re-attaches to a previously started execution and
re-registers its tool workers (e.g. after a process restart):
handle = runtime.start(agent, "Long job")
eid = handle.execution_id
# later, even after a restart:
handle = runtime.resume(eid, agent)
result = handle.join(timeout=120)runtime.client is the control-plane AgentClient (formerly AgentHttpClient —
the old name is kept as an alias). It talks to the /agent/* HTTP endpoints directly:
compile, deploy, start, run, schedule, status, respond, stop, signal, SSE. It is
control-plane only — its run/start do not register or poll local @tool
workers, so use it for agents whose tools are all server-side (HTTP/MCP/built-in) or
already deployed.
with AgentRuntime() as runtime:
client = runtime.client
result = client.run(agent, "Hello") # compile + start + poll
handle = client.start(agent, "Long job")
infos = client.deploy(agent) # compile + register
# Cron lifecycle (same surface as runtime.schedules_client()):
client.schedule(agent, [nightly]) # reconcile schedules
client.schedules.pause("agent-nightly")Key methods: run/run_async, start/start_async, deploy/deploy_async,
schedule(agent, schedules), get_status, respond, stop, signal,
stream_sse, and .schedules (the schedule lifecycle — pause/resume/delete/
run_now/preview_next/reconcile, now carried by SchedulerClient itself). Both
sync and async forms exist. Most users call runtime.run/start/deploy instead,
which add local-worker management on top of this client.
The raw /agent/* HTTP transport behind this client is
conductor.client.ai.AgentApiClient — also reachable without the agents layer via
OrkesClients.get_agent_client() (and get_scheduler_client() for the cron
lifecycle). AgentClient composes that transport and adds the agent-level surface.
Pass output_type= a Pydantic model (or dataclass) to get a typed, validated result.
Pydantic is only needed when you use this feature.
from pydantic import BaseModel
from conductor.ai.agents import Agent, AgentRuntime, tool
class WeatherReport(BaseModel):
city: str
temperature: float
condition: str
recommendation: str
@tool
def get_weather(city: str) -> dict:
"""Get weather data."""
return {"city": city, "temp_f": 72, "condition": "Sunny"}
agent = Agent(name="reporter", model="openai/gpt-4o",
tools=[get_weather], output_type=WeatherReport,
instructions="Report the weather with a recommendation.")
with AgentRuntime() as runtime:
result = runtime.run(agent, "What's the weather in NYC?")
print(result.output) # conforms to WeatherReport's schemaStore secrets in the server's credential store (never in code), then declare them per
tool with credentials=[...]. Inside the tool, read the injected value with
get_secret(name).
from conductor.ai.agents import tool, get_secret
@tool(credentials=["OPENAI_API_KEY"])
def call_openai(prompt: str) -> str:
"""Call OpenAI directly using a stored credential."""
key = get_secret("OPENAI_API_KEY") # only works inside a credentials-aware tool
...You can also declare credentials at the agent level (Agent(..., credentials=[...])),
and HTTP/built-in tools resolve ${CRED_NAME} placeholders in headers from the same
store at execution time. Pass credentials=[...] to runtime.run(...) to supply
credential names for a specific execution.
get_secret raises CredentialNotFoundError when the credential is absent. Other
credential errors: CredentialAuthError, CredentialRateLimitError,
CredentialServiceError. Store a credential via the CLI:
agentspan credentials set OPENAI_API_KEY sk-...Strategy.PLAN_EXECUTE runs a planner agent that emits a JSON plan, which is then
executed deterministically against a fixed tool set. Build the harness with the
plan_execute helper, or the Agent named-slot API.
from conductor.ai.agents import plan_execute
harness = plan_execute(
"report_builder",
tools=[create_directory, write_file, check_word_count],
planner_instructions="Plan a multi-section report, then write each section.",
model="openai/gpt-4o",
)
result = runtime.run(harness, "Write a report on Rust adoption.")Or directly:
from conductor.ai.agents import Agent, Strategy
planner = Agent(name="rb_planner", model="openai/gpt-4o", instructions="Plan it.")
harness = Agent(name="report_builder", strategy=Strategy.PLAN_EXECUTE,
planner=planner, tools=[write_file, check_word_count])PLAN_EXECUTE requires planner= (the agent that emits the plan) and tools= on the
parent (the canonical executable tools); fallback= is optional.
Build a deterministic plan in Python with the typed builders and pass it to run:
from conductor.ai.agents.plans import Plan, Step, Op, Generate, Validation, Ref
plan = Plan(
steps=[
Step("setup", operations=[Op("create_directory", args={"path": "out"})]),
Step("write", depends_on=["setup"], parallel=True, operations=[
Op("write_file", generate=Generate(
instructions="Write the introduction.",
output_schema='{"path": "out/intro.md", "content": "..."}')),
]),
Step("summarize", depends_on=["write"], operations=[
Op("summarize", args={"document": Ref("write")}), # wire a prior step's output
]),
],
validation=[Validation("check_word_count", args={"path": "out/intro.md", "min_words": 200})],
)
runtime.run(harness, "build it", plan=plan)Op takes either args= (literal) or generate= (LLM-generated args). Ref("step")
injects an upstream step's output (the step must be in depends_on). Step.parallel
runs a step's operations concurrently; depends_on expresses cross-step concurrency.
Ground the planner with reference documents via planner_context= — inline text or a
URL fetched at planner-run time:
from conductor.ai.agents.plans import Context
harness = plan_execute(
"kyc", tools=[...],
planner_instructions="Follow the KYC process.",
planner_context=[
"Tier-1 customers skip manual review.", # inline string
Context(url="https://wiki/kyc-rules", headers={"Authorization": "Bearer ${KYC_TOKEN}"}),
],
)Attach cron schedules at deploy time, or manage them through the schedule client.
from conductor.ai.agents import Schedule
nightly = Schedule(name="nightly", cron="0 0 * * *", timezone="UTC",
input={"prompt": "Daily summary."})
runtime.deploy(agent, schedules=[nightly]) # upsert; [] purges; omit leaves as-is
sc = runtime.schedules_client() # or runtime.client.schedules
sc.get_all_schedules(workflow_name=agent.name) # list — source-of-truth read
sc.pause("greeter-nightly", reason="ship freeze")
print(sc.preview_next("0 0 * * *", n=5)) # next 5 fire times (epoch ms)
from conductor.ai.agents.schedule import schedules
schedules.run_now("greeter-nightly", runtime=runtime) # fire once -> execution idLoad an agentskills.io skill directory (with a SKILL.md) as an Agent:
from conductor.ai.agents import skill, load_skills
researcher = skill("./skills/deep-research", model="openai/gpt-4o",
params={"rounds": 3})
all_skills = load_skills("./skills", model="openai/gpt-4o") # dict: name -> Agent
runtime.run(researcher, "Research durable execution engines.")skill(path, model="", agent_models=None, search_path=None, params=None) returns an
ordinary Agent you can run, compose (e.g. via agent_tool), deploy, and serve.
Sub-agent files (*-agent.md), scripts/, and resource files are discovered
automatically; cross-skill references resolve from sibling and ~/.agents/skills
directories plus any search_path.