Agent as Policy#

In agent as policy, a hosted foundation model is the controller, with no robot training. At each request, the policy shows the model the task, the robot state, and the camera images, and asks it for a structured command. Arena checks the command and runs it. To try a different model, you change the endpoint instead of retraining.

Arena includes two such policies in the isaaclab_arena_vlm_agent package. Both call any OpenAI-compatible endpoint. VLMGoalPolicy (vlm_goal) asks for one goal pose and gripper state at a time. A motion generator drives the robot to that goal, and the next request tells the model whether the goal was reached, blocked, or timed out. VLMChunkPolicy (vlm_chunk) asks for a short sequence of commands and runs one per step. VLM Agent runs both on DROID pick-and-place.

How a request works#

Each request passes through a small pipeline. Every stage is its own class:

observation ─► observation adapter ─► images + JSON state ─┐
                                                           ├─► inference backend ─► JSON
embodiment preset ─► system prompt + response schema ──────┘                          │
                                                                                      ▼
robot action ◄─ motion generator (goal only) ◄─ action adapter: validate + decode ◄──┘

The embodiment preset (DroidVLMAdapter) ties the pipeline to one robot. It writes the system prompt, which explains the command layout, the coordinate frames, and the camera calibration, and it builds the action adapter and motion generator. The observation adapter (VLMObservationAdapter) chooses the images and state to send. observation_modality selects image, state, or image_state. The inference backend (InferenceBackend) calls the endpoint with a strict response schema and retries failed requests.

The action adapter (ChunkActionAdapter or GoalActionAdapter) checks the model’s JSON and converts it into robot commands. If the response is invalid, the adapter sends the error back to the model and asks for a correction, up to max_attempts times. For the goal policy, the motion generator (MotionGenerator) tracks the goal pose and reports when it is reached or blocked.

When several environments need a response on the same step, their requests run concurrently, up to max_concurrent_requests at a time. Each environment keeps its own conversation with the model.

Extend the pipeline#

Because each stage is a separate class, you can replace one without touching the others.

To support a new robot, write an embodiment preset with make_chunk_action_adapter, make_goal_action_adapter, and get_system_prompt. The included absolute action adapters work unchanged when every action term is absolute pose IK, joint position, or a binary gripper (see command_indices.py). Add the preset name to the vlm_embodiment_adapter check in VLMPolicyCfg and to _get_vlm_embodiment_adapter, and then select it in YAML:

policy:
  type: isaaclab_arena_vlm_agent.vlm_agent_policy.VLMGoalPolicy
  endpoint: internal  # internal, public, or openai
  model: openai/openai/gpt-6-astra  # Must be served by the endpoint
  vlm_embodiment_adapter: my_robot
  max_steps_per_goal: 30

To send different inputs, subclass VLMObservationAdapter and implement extract_vlm_input_from_observation. To change the command format, write an action adapter with a Pydantic response_model and a decode_command method. Raise InvalidAgentResponseError there to ask the model for a correction. To use a different controller, subclass MotionGenerator.

To change how commands are executed over time, subclass VLMPolicy and implement _needs_new_prediction, _set_prediction, _get_next_command, and reset. The subclass inherits request assembly, concurrent requests, validation, and the correction loop. The same structure extends beyond VLMs: an agent that plans, calls tools, or keeps memory is a new execution strategy over the same adapters.

Next steps#

Continue to Code as Policy for policies that run programs instead of commands.