Skip to content
AgentEnv Framework

Deploying your agent

Deploy your agent on any sandbox and connect with any env

Package it

The framework deploys container images, so the first step is a Dockerfile next to agent.py:

claudius/Dockerfile
FROM python:3.12-slim
WORKDIR /app
RUN pip install --no-cache-dir "agentenv-framework-protocol[agent]" "mcp>=1.25,<2" anthropic
COPY agent.py .
CMD ["python", "agent.py"]

Register it

Registering builds the image and stores it as an artifact, a stored object that the framework versions. It then stores the agent, a document that points at that artifact, under an id you choose. These pages use the id claudius:

Terminal
agent-env a2a-agent put --id claudius --dockerfile claudius/Dockerfile --skip-validation
Output
Building Docker image...
Creating DockerImageArtifact...
Created artifact: id=a2a-agent-claudius version=1
Registering A2A agent...
Created A2A agent: id=claudius version=1 image=a2a-agent-claudius:1

Each put of the same id stores a new version, and older versions never change. Without --skip-validation, the put also deploys the agent to run conformance checks, which need an object store that signs URLs, such as S3; on the local defaults they stop with RuntimeError: A2A validation requires a signable object store for the presigned-URI probe.

Point it at a model

A deploy passes the agent a model endpoint and key, as LITELLM_BASE_URL and LITELLM_API_KEY. Neither has a default, so set them under [model] in .agentenv/config.toml, with the key as a secret: reference:

.agentenv/config.toml
[model]
base_url = "https://llm.example.com/v1"
api_key = "secret:litellm_api_key"

secret:litellm_api_key names a key in your secret store, so the file never holds the key itself. The default store reads it from a litellm_api_key environment variable or a YAML file, and Secret store covers the others.

The LITELLM_BASE_URL and LITELLM_API_KEY environment variables win over the file. When the secret store has no value for the key, a deploy stops before it starts a container, with Error: Unresolved secret:litellm_api_key reference (no value and no default).

ClaudiusRLMAgent sends Anthropic Messages API requests to that endpoint, for claude-opus-5-5 by default. A LiteLLM proxy with that model serves them at /v1/messages, next to its OpenAI-compatible routes.

Deploy your agent

With the agent registered and pointed at a model, one command deploys it:

Terminal
agent-env a2a-agent deploy --id claudius
Output
Sandbox backend: config default
Fetching A2A agent: id=claudius version=latest...
Found: id=claudius version=1 image=a2a-agent-claudius
Deploying (ttl=7200s)...
Deployed!
Instance ID: claudius-4msj7n4d
A2A URL: http://localhost:42031
Sandbox ID: local-531d8cae
Agent Card: claudius
Expires At (UTC): 2026-09-29 21:44 UTC

A2A URL is how the framework, or you, reach your agent: it serves its card there and takes tasks at /a2a. The instance id uniquely identifies this deployment of the agent.

Where the instance runs depends on the sandbox. The default is local, where the agent is a container on your machine's Docker. --sandbox picks another built-in, such as modal, or a provider you register under [sandbox.providers.<name>] in config.toml.

The framework records every instance. agent-env a2a-agent get-instance --id claudius-4msj7n4d reads the record back: the agent id and the exact version it deployed, the instance's A2A URL, the sandbox it runs in and when it expires. The record also stores the card the instance served.

Use it

The framework uses your agent through what the instance's card declares. The card's url, /a2a, takes tasks as A2A messages, and its extensions list what else the agent supports: mcp-config takes environments, agent-config takes settings such as the model, and trajectory hands back what the agent did. In a task, a deploy_agent step deploys the agent and posts it each env's MCP URL, and a prompt_agent step sets its config, sends the prompt and reads the trajectory.

Other extensions are declared the same way, and the framework uses one only when the card lists it: an agent that implements peer-agents/v1 can message the other agents in a task. Useful A2A extensions covers every one.

Tear it down

Deploying again starts another instance with another id, and the agent itself never changes. An instance runs until its TTL runs out, which you set with --ttl-seconds, or until you close it from Python by its id:

teardown_agent.py
import asyncio

from agent_env.a2a_agent.store import get_a2a_agent_instance_store
from agent_env.providers.sandbox_providers.sandbox_provider import build_sandbox_provider


async def main() -> None:
    deployed = get_a2a_agent_instance_store().get("claudius-4msj7n4d")
    sandbox = await build_sandbox_provider(deployed.sandbox_type).get_sandbox(deployed.sandbox_id)
    await sandbox.terminate()


asyncio.run(main())

In a task, a teardown_sandboxes step closes the instances of the agents it names.

Last updated on

Ask a question · Report an issue

On this page