SageMaker backend setup
This doc describes how to train an AgentCore Runtime-deployed agent with Amazon SageMaker Training Sessions. Unlike the slime / verl backends, there is no GPU cluster required for the training: SageMaker hosts the policy weights, the sampler, and the optimizer behind an SDK, and the RL loop itself runs as a plain Python process on a small EC2 box.
The host does need to be network-reachable from your ACR runtime, so a laptop does not work with this setup — see Networking before you deploy.
The loop is implemented in-repo at
src/agentcore_rl_toolkit/backends/experimental/sagemaker/
and is driven by a single YAML config:
| File | Role |
|---|---|
train_grpo.py |
The GRPO training loop: rollout → advantages → forward_backward → optim_step → rebind sampler. |
config.py / config.yaml.example |
Typed config dataclass and a template to copy. |
rollout.py |
One ACR rollout: create gateway session, invoke the agent over HTTP with AgentCoreHttpClient, await its result, drain trajectories. |
datum.py |
Converts a gateway TraceRecord (+ advantage) into a SageMaker training datum. |
prepare_datasets/ |
Dataset preprocessing scripts (GSM8K). |
Token capture uses the same in-repo
rollout gateway
as the verl backend. The SageMaker-specific seam is
SageMakerSdkBackend
(rollout_gateway/sampling_backends/sagemaker_sdk.py),
a token_ids -> token_ids + logprobs sampling backend over the SageMaker
SamplingClient.
Prerequisites
Section titled “Prerequisites”- Python 3.12 and
uv. No local GPU required. - AWS credentials with permission to invoke an AgentCore Runtime and create SageMaker training sessions.
- An AgentCore Runtime deployment of an
AgentCoreRuntimeAppagent, such asexamples/strands_math_agent/rl_app.py. Save the resulting runtime ARN (agent_runtime_arnbelow). The agent readsbase_url,model_id, andapi_keyfrom the payload’s_configfield (config.get("api_key") or "EMPTY") — the gateway keys token capture off the api-key / Bearer slot — and returns{"reward": <float>}. - SageMaker resources: an execution role ARN, an S3 output path for checkpoints, a model package group ARN for saved states, and the hub content ARN of the base model you want to fine-tune.
- An EC2 instance in a VPC to run the loop on, with the ACR runtime deployed into the same VPC. See Networking.
Networking
Section titled “Networking”The training loop hosts the rollout gateway itself. train_grpo.py starts a
ThreadedGatewayServer
bound to the host’s primary private IP, then passes the resulting
http://<private-ip>:<port>/v1 to the agent as base_url in the _config
payload field. Every model call the agent makes is therefore an inbound connection
from the ACR runtime to your host — the reverse of the direction people usually
expect. Rollout results come back out-of-band through S3, but sampling does not.
That gives three requirements:
- The ACR runtime must be in VPC mode, in a VPC that routes to the loop host. ACR deploys to the public internet by default, which cannot reach a private IP. For the Math agent, use the VPC deployment commands in its README.
- The host’s security group must allow inbound TCP on the gateway port from the runtime’s subnets or security group.
- Pin the port.
gateway_portdefaults to0, which binds an OS-assigned ephemeral port — fine for a wide-open security group, but it makes a narrow inbound rule impossible, since the port changes every run. Setgateway_portto a fixed value and open exactly that port.
A laptop will not work. It has no address the ACR runtime can dial, so every
sampling call fails and each rollout ends up scored 0.0 (run_one_rollout()
logs the failure and returns a zero reward rather than raising, so this shows up
as a run that trains on nothing rather than as a crash). Run the loop on an EC2
instance in the VPC instead. The “no GPUs” claim is about the instance’s size, not
its location — a small CPU box is plenty.
The gateway serves plain HTTP with no TLS, and the api-key slot carries the per-rollout session id rather than a secret, so keep it on private subnets and scope the security group tightly.
Installation
Section titled “Installation”cd /path/to/agentcore-rl-toolkituv venv --python=3.12source .venv/bin/activate
uv pip install sagemaker-train # SageMaker Training Sessions SDKuv pip install -e ".[rollout,gateway]" # rollout client and token captureuv pip install transformers==5.12 # version the SDK's tokenizer expectsuv pip install wandb # optional, for training curvesPrepare data
Section titled “Prepare data”The dataset is a Parquet or JSONL file with a single payload column /
field. Each row’s payload is forwarded verbatim (plus the _config
field) as the dict your @app.entrypoint function receives — no trainer-side tokenization, no
prompt column. Put every per-rollout field your agent needs (prompt,
ground-truth answer, task IDs, resource URIs) inside payload.
Preprocessing scripts live in
prepare_datasets/.
Configuration
Section titled “Configuration”Copy the template and fill in the required ARNs:
cd src/agentcore_rl_toolkit/backends/experimental/sagemakercp config.yaml.example config.yamlRequired — training fails fast without these:
| Key | Purpose |
|---|---|
role_arn |
SageMaker execution role. |
base_model_arn |
Base model to fine-tune, e.g. arn:aws:sagemaker:<region>:aws:hub-content/SageMakerPublicHub/Model/<model-name>. Also passed to the agent as model_id. |
s3_output_path |
S3 prefix for model checkpoints. |
model_package_group_arn |
Model package group that saved states / weights land in. |
agent_runtime_arn |
Your deployed AgentCore Runtime agent. |
dataset_path |
Training Parquet/JSONL file. |
The rest, grouped as in config.yaml.example:
region: "us-east-1"
# --- LoRA ---lora_rank: 32lora_alpha: 64resume_from_model_package: "" # model package ARN to resume optimizer+adapter from
# --- Training ---batch_size: 32 # prompts per global stepresponses_per_prompt: 4 # GRPO group size (K)learning_rate: 1.0e-5max_context_tokens: 32768 # gateway-enforced context budget per sessionmax_new_tokens: 8192temperature: 1.0epochs: 1loss: ppo # ppo | importance_sampling | cispo
# --- Dataset ---max_prompts: 0 # 0 = use all
# --- Evaluation ---eval_dataset_path: "/path/to/test.parquet"eval_every: 10 # 0 = only at the endeval_max_prompts: 0eval_temperature: 0.0
# --- Gateway ---gateway_port: 0 # 0 = OS-assigned; pin it to open a narrow inbound # security-group rule (see Networking)history_mode: tree # tree | linear; linear keeps an append-only rollout in one recordchat_template_kwargs: {} # passed to apply_chat_template, e.g. {enable_thinking: false}
# --- Checkpointing ---save_every: -1 # -1 = only at the endsave_weights_at_end: false # also publish an inference-ready model packagetrain_op_timeout: 600.0 # seconds to wait on each forward_backward / optim_step / save
# --- Rollout ---max_rollout_time: 1800.0 # per-rollout deadline, secondsacr_tps_limit: 5 # AgentCore invoke TPS ceiling (per-account limit is 25)
# --- Logging ---wandb_project: "" # empty = console onlywandb_run_name: "" # defaults to the SageMaker training session namerollout_log_path: "" # JSONL of per-rollout diagnostics; auto-named if emptyLaunch training
Section titled “Launch training”cd src/agentcore_rl_toolkit/backends/experimental/sagemakerpython train_grpo.py --config config.yamlWhat the loop does
Section titled “What the loop does”Per global step, train_grpo.py:
- Rollout — pops
batch_sizepayloads; for each, runsresponses_per_promptACR rollouts concurrently. Each rollout gets a fresh UUID used as the ACRruntimeSessionId, the gateway capture session id, and theapi_key— so one id ties the container, the trajectory, and the result together. - Capture — the agent’s OpenAI/Anthropic calls go to the gateway
(
base_url), which renders messages to tokens, samples through the SageMakerSamplingClient, and records token ids, logprobs, and a loss mask per turn. - Reward — the agent returns
{"reward": ...}, which the loop fetches over HTTP (max_rollout_time) and stamps onto the trajectory. A timeout, an ACR error, an agent exception, or a missing/non-numeric reward all score 0.0 and are logged rather than raised — one bad rollout never kills a run. - Advantages — rewards are centered and normalized within each GRPO group; every trajectory-tree leaf becomes one datum (multi-turn agents and sub-agent forks yield several datums per rollout). Datums with an empty or all-zero loss mask are dropped; a step with no valid datums is skipped.
- Update —
forward_backward(datums, loss_fn=<loss>)thenoptim_step(AdamParams(learning_rate=...)). - Rebind —
save_weights_and_get_sampling_client()returns a new sampling client, which is swapped intoSageMakerSdkBackendso the next step samples on-policy. In-flight generations finish against the old weights.
Evaluation runs every eval_every steps (and once at the end) over
eval_dataset_path at eval_temperature, logging eval/accuracy. Checkpoints:
save_state() every save_every steps and once at the end, returning a model
package ARN you can feed back as resume_from_model_package; set
save_weights_at_end: true to also publish an inference-ready model package.
Metrics go to the console and, if wandb_project is set, to Weights & Biases:
sampler/mean_reward, sampler/num_datums, time/rollout, time/train,
eval/accuracy, plus the SDK’s own train/* metrics.
Notes and current limits
Section titled “Notes and current limits”- Rewards are agent-side. The agent returns a scalar
{"reward": ...}from its@app.entrypoint; there is no trainer-side reward model. - LoRA only. Sessions are created via
create_lora_training_client(lora_rank/lora_alpha); full fine-tuning is not exposed here. - GRPO only.
train_grpo.pyis the only algorithm implemented;lossselects the surrogate (ppo,importance_sampling,cispo). For a different algorithm, write your own loop using SageMaker training APIs.trace_record_to_datum(), and the gateway assembly are all reusable as-is. - Single process, one gateway. The loop is a single asyncio process serving one gateway on a background thread — it scales by ACR concurrency, not by local hardware.