Prerequisites
- Complete the Quickstart so the CLI, SDK, and
xpander loginare already set up. - Python 3.12+ for the local handler.
- An LLM provider key in your shell like
OPENAI_API_KEY,ANTHROPIC_API_KEY. (These keys will only be used locally.)
1. Install
2. Set up scaffolding
xpander_config.json reference
xpander_config.json
3. Create task handler
The full pattern, wrapped in@on_task so the platform routes tasks to it:
xpander_handler.py
Backend(configuration=task.configuration)picks up the API key, organization ID, and base URL from the active task. No need to read.envdirectly.await backend.aget_args(task=task)calls the control plane and returns a dict with the full agent configuration (instructions, tools, model, knowledge bases, session storage, memory, guardrails). Always passtask=taskinside an@on_taskhandler so task-level overrides (instructions_override,expected_output,output_schema) are merged in.Agent(**agno_args, debug_mode=True)splats that dict into Agno’s ownAgentclass.debug_modeprints tool calls and token usage; remove it for production.task.to_message()flattens the prompt text, file URLs, and any inline-readable file content into a single string ready for Agno.task.get_files()andtask.get_images()return Agno-typedagno.media.Fileandagno.media.Imageobjects.- Reporting
task.tokensandtask.used_toolsis optional. Skipping them just means the metrics view in Agent Studio shows “no usage data” for that run.
backend.aget_args reference
Input parameters
backend.aget_args accepts these arguments:
What can
override change?
override accepts any key Agno’s Agent.__init__ takes. Two ways to use it:
- Replace any value the SDK already resolves: any key from the output-params table below (
model,instructions,tools,db,knowledge_retriever,pre_hooks,output_schema, and so on). - Add Agno-native kwargs the SDK doesn’t set itself. Common ones:
See the Agno Agent reference for the full surface.
Setting
override["model"] skips the SDK’s own model resolution entirely, so use it whenever you want a different model client without re-implementing credential handling.
Example using override to A/B-test two models against the same agent definition:
override to tune Agno-native sampling parameters:
tools to inject an ephemeral test tool:
Agent(...). The dict is yours; the SDK won’t reach back in.
Output parameters
Callingbackend.aget_args returns these fields:
4. Edit the agent’s system prompt
agent_instructions.json contains the agent’s system prompt and has exactly three fields:
agent_instructions.json
xpander agent dev or xpander agent deploy syncs it to the control plane.
5. Set up streaming (optional)
For token-by-token output, decorate anasync def that yields TaskUpdateEvent objects instead of returning a Task. The decorator detects the difference automatically.
streaming_handler.py
stream=True, stream_events=True, yield_run_output=Truetell Agno to emit events instead of buffering. The handler receives chunks, tool-call events, and a finalRunOutput.- The
Chunkevent forwards each token to the platform’s SSE stream so clients render output as it arrives. - The
TaskFinishedevent signals the end of the stream and carries the final task back to the platform.
POST /invoke, returning Server-Sent Events. The platform’s SSE listener for cloud-deployed agents expects a regular handler that returns a Task. So if you need both an interactive streaming experience and platform-routed tasks, run two handlers, or have your streaming endpoint proxy through a regular handler.
6. Test local development
Run the handler with the dev server. Tasks created from any channel (REST, Slack, Agent Studio) route to your laptop:--output_format and --output_schema are useful for testing structured output without changing the agent’s settings in the control plane.
7. Deploy to xpander cloud
When the local handler works, push it as a managed container:- The CLI bundles
xpander_handler.py,requirements.txt, theDockerfile, and the rest of the project. - xpander builds a Docker image, pushes it, and rolls out a new immutable version. The previous version stays available for instant rollback.
- Once the rollout finishes, the platform routes inbound tasks to the new container. The first deploy takes a couple of minutes; subsequent deploys are faster thanks to layer caching.
Secrets and environment variables
.env ships with the deploy by default. For values you don’t want bundled into the image (production keys, rotating secrets), upload them to xpander’s secret store instead:
xpander secrets-sync whenever you rotate a secret. Don’t commit .env to source control either way.
Lifecycle hooks
Containers support@on_boot and @on_shutdown for one-time resource setup and teardown. Use them for caches you want to warm before the first task lands, or open connections you want to close cleanly when the container is replaced:
When to redeploy
Anything that changes Python code, dependencies, or the Dockerfile needs a redeploy. The control-plane bits stay live without one:- Live (no redeploy): instructions, model selection, memory settings, attached agents, attached knowledge bases, tool selection from the catalog.
- Needs
xpander agent deploy: any change toxpander_handler.py,requirements.txt,Dockerfile, or other files in the container.
Inspect the deployment settings
After everyxpander agent dev or xpander agent deploy, the live Agno settings for the cloud version are saved on the agent. Read them back from the Agents() class to confirm what’s actually running:
You change
agno_settings in Agent Studio, not in code. There’s no SDK call to flip them by design: changing memory settings affects billing and persistence semantics, so they live in the control plane.
Troubleshooting
`AttributeError: 'NoneType' object has no attribute 'instructions_override'`
`AttributeError: 'NoneType' object has no attribute 'instructions_override'`
Backend.aget_args() reads task.instructions_override while building the args. Inside an @on_task handler, always pass the active task: await backend.aget_args(task=task). The agent_id-only form is supported outside a handler (in scripts and notebooks), but inside one the active task is the source of truth for instruction overrides.Postgres connection errors when running the handler
Postgres connection errors when running the handler
Session storage is on by default, so the args dict includes a
db wired to xpander’s Postgres. For cloud-hosted agents this is automatic. For self-hosted or air-gapped deployments, the database needs to be reachable from your container. Check the connection string with await agent.aget_connection_string() and confirm the host is reachable. To turn session storage off, flip agno_settings.session_storage to False in Agent Studio.Wrong model or wrong key used at runtime
Wrong model or wrong key used at runtime
Custom LLM keys configured on the agent take precedence on cloud deployments. Locally, your shell’s
OPENAI_API_KEY (or the equivalent for your provider) wins. If you want the cloud-side custom key locally too, mirror it into your .env.`pip install xpander-sdk[agno]` says 'no matches found' on macOS
`pip install xpander-sdk[agno]` says 'no matches found' on macOS
zsh expands the brackets. Quote the package name:
pip install "xpander-sdk[agno]".Next steps
Quickstart
The 10-minute scaffold-to-deploy walkthrough that produced the handler shown above.
Custom Tools
Wrap private APIs as tools with
@register_tool and pass them through the args dict.Memory & State
The deep dive on
session_storage, user memories, and agent memories.Containers
Ship the handler as a container managed by xpander.
Core Concepts
The SDK class names mapped onto agents, tasks, threads, and memory.
Frameworks overview
What’s auto-wired vs. manual for Agno, OpenAI Agents SDK, LangChain, and AWS Strands.

