Section 33.8: Safe LLM-agent interfaces

"The danger is not that the model says something harmful. The danger is that it says something plausible, and the interface asks no further questions before sending it to the motors."

An Actuation Boundary Inspector
Illustration for Section 33.8: Safe LLM-agent interfaces
Figure 33.8A: A safe LLM-agent interface treats the model as a proposer, never the final authority on actuation: typed schemas, state guards, monitors, and human escalation sit between language and motors so a plausible-sounding command cannot reach the robot unchecked.

A single fluent sentence, "clear the aisle fast," can put a human in a hospital if nothing between the language model and the motors ever pauses to ask whether the words are safe to obey; the figure above is that missing pause drawn as an engineering envelope. The LLM should never be the final authority on actuation; typed interfaces, guards, monitors, and human escalation define what commands can reach the robot.

This section assumes familiarity with the state-estimation pipeline introduced in section 2.2 and the layered control architecture from section 7.1, because the safety filter \(\sigma\) operates over estimated state and hands rejected proposals back to the controller. The typed-interface patterns developed here recur in section 54.4 (action shielding under conformal risk bounds, where conformal risk control is a statistical method that turns a target error rate into a provable bound on how often the shield may pass an unsafe action) and the full deployment safety case in section 54.6, where every layer discussed here must be evidenced under realistic operating conditions.

Two hardware names appear below for concreteness: the Franka Panda is a widely used 7-degree-of-freedom research robot arm, and the Robotiq 2F-85 is a two-finger parallel gripper commonly mounted on it.

The closed loop below (Figure 33.8) names the four stations a proposal passes through: the instruction enters, the planner turns it into an action object, the tool API exposes that object for verification, and the verifier's evidence feeds back into the next decision.

Figure 33.8

A closed loop of four stages, instruction, planner, tool API, and verifier, with the verifier's evidence looping back to the instruction stage. The diagram forces the reader to name the input, model boundary, action interface, and evidence record before trusting the system. This is the same diagram introduced as Figure 33.1.

Review and Consolidation

Depth and self-containment. This section must turn 'safety' into concrete interface rules: typed permissions, state guards, action filters, and human escalation. Readers should leave with a real control surface, not a slogan.

Production and evaluation contract. The artifact must log the proposed action, the active safety checks, the blocked or modified result, and the escalation path. Without those fields, safety claims are impossible to reproduce.

Checklist Memory Anchor

Name the language interface, grounded world state, executable action contract, and evidence artifact before trusting any claimed improvement.

Mini Audit Exercise

Write one evidence row recording instruction, world-state estimate, chosen action, verifier result, and failure label. Then identify which field would change first under command misunderstanding.

Big Picture

A warehouse robot receives the instruction "clear the aisle fast." The LLM proposes a perfectly coherent action sequence, the interface forwards it without question, and a worker is struck before any safety check fires. The model was not wrong in any linguistic sense; the interface simply had no boundary between language and motors. As LLMs move from chat assistants into embodied agents controlling real actuators, the interface layer becomes the last reliable line of defense. Here you will learn to design typed permission boundaries, state-conditioned action guards, and escalation paths that ensure a plausible-sounding proposal never becomes a hazardous command just because no one asked a second question.

Embodied systems interpose safety logic between LLM proposals and robot action so that language never becomes direct authority over hazardous motion.

The practical question is which safety properties can be checked automatically at interface time and which require escalation or hard-coded limits in the controller.

Action Is The Test

The safest place to catch a bad plan is before it becomes an actuator command. Interface safety is cheaper than recovery.

Theory

Let the LLM propose action object \(u_t\), and let a safety filter \(\sigma\) map that proposal and state estimate to an allowed action: $$a_t = \sigma(u_t, \hat s_t), \qquad \sigma : \mathcal U \times \mathcal S \to \mathcal A \cup \{\text{block}, \text{escalate}\}.$$ The filter may pass, modify, block, or escalate the action depending on geometric, task, or policy constraints.

This formulation matters because it places safety at the interface boundary, where the planner is still symbolic and the controller still has time to refuse. Once an unsafe instruction becomes continuous motion, the system has fewer and more expensive options. Ablation studies in agent-safety benchmarks (as of 2024) show the gap directly. Enforcing safety purely through prompt instructions cuts unsafe command pass-through by roughly 40%. Adding a two-field typed schema guard, such as requiring human_clear: bool and zone_id: str, cuts it by over 95%, because the schema check fires unconditionally regardless of what the model was told.

Mechanism

A practical shield checks permissions, geometry, resource bounds, and human-approval rules. The LLM proposes. The shield decides whether that proposal is admissible now, admissible only after modification, or inadmissible without escalation.

Step-Through: the \(\sigma\)-Shield on one proposal

Trace the filter with a tiny example. Set the risk threshold \(\alpha = 0.7\), the geometric collision cutoff \(\theta_{\text{geo}} = 0.05\), and a hard rule human_clear_required_above_0.5. The LLM proposes u = {action: "move_to", shelf: 7, speed: 1.2} and the state estimate is s = {human_clear: false, zone_id: "cell-3", collision_prob: 0.02}.

  1. Schema validation: all required fields (action, speed, human_clear, zone_id) are present and correctly typed. Pass.
  2. Permission check: the agent holds the navigation permission for cell-3. Pass.
  3. Risk estimation: the risk model returns \(r = 0.81\). Log \(r = 0.81\).
  4. Geometric feasibility: collision_prob = 0.02, which is below \(\theta_{\text{geo}} = 0.05\). Pass.
  5. Policy constraint scan: the rule fires because speed = 1.2 > 0.5 while human_clear = false. Return \(d_t = \text{block}\), rule fired = human_clear_required_above_0.5.

Risk routing never runs: even though \(r = 0.81 > \alpha\) would have produced escalate, the hard policy rule short-circuits to block first. The evidence row is \((u_t, s_t, r{=}0.81, d_t{=}\text{block}, \text{rule}{=}\texttt{human\_clear\_required\_above\_0.5})\). Notice the low collision score (0.02) would have fooled a geometry-only shield: the block came from policy, not physics.

Algorithm: LLM Proposal Safety Filtering (\(\sigma\)-Shield)

Input: LLM action proposal \(u_t \in \mathcal{U}\), typed proposal schema \(\Pi\), state estimate \(\hat{s}_t\), policy constraints \(\mathcal{C} = \{c_1, \ldots, c_k\}\), risk threshold \(\alpha \in [0,1]\)

Output: Admissible action \(a_t \in \mathcal{A}\), or decision \(d_t \in \{\text{block}, \text{escalate}\}\) with a logged evidence record

  1. Schema validation: Parse \(u_t\) against typed schema \(\Pi\); if any required field is missing or the type is incorrect, return \(d_t = \text{block}\) immediately without reading downstream fields.
  2. Permission check: For each permission class \(p \in \Pi.\text{permissions}\), verify that the requesting agent holds \(p\) under current context \(\hat{s}_t\); block if any class is unauthorized.
  3. Risk estimation: Compute proposal risk \(r(u_t, \hat{s}_t) \in [0,1]\) using the active risk model (geometric, semantic, or learned); record \(r\) in the evidence log.
  4. Geometric feasibility: Query the collision and kinematic checker (e.g., MoveIt 2) with state \(\hat{s}_t\); if the proposed motion is infeasible or yields a collision probability above threshold \(\theta_{\text{geo}}\), return \(d_t = \text{block}\).
  5. Policy constraint scan: Evaluate each constraint \(c_i \in \mathcal{C}\) against \((u_t, \hat{s}_t)\); if any hard constraint fires, return \(d_t = \text{block}\) and record which rule \(c_i\) triggered.
  6. Risk routing: If \(r(u_t, \hat{s}_t) \leq \alpha\), proceed to step 7 (pass). If \(\alpha < r(u_t, \hat{s}_t) \leq 1\), route to human review and return \(d_t = \text{escalate}\).
  7. Optional rewrite: If a rewrite rule \(\rho \in \mathcal{C}\) applies (e.g., clamp speed, require confirmed field), apply it to produce modified proposal \(u_t' = \rho(u_t)\) and re-evaluate steps 3 and 4 on \(u_t'\).
  8. Pass through: Set \(a_t = \sigma(u_t, \hat{s}_t)\) and dispatch \(a_t\) to the low-level controller, which applies its own independent geometric and dynamic limits.
  9. Evidence logging: Append to the safety log the tuple \((u_t, \hat{s}_t, r, d_t, \text{rule fired})\); retain blocked proposals alongside passed ones so planner improvement does not require weakening the shield.
  10. Feedback to planner: If \(d_t \in \{\text{block}, \text{escalate}\}\), return the blocking reason to the planning loop \(\pi_\theta\) so that replanning or human input can produce a new proposal \(u_{t+1}\).

What "escalate" means operationally: an escalated proposal is queued to a human operator's approval console together with the proposal, the risk score, and the rule context, while the controller holds the robot at its current pose (or a pre-declared safe pose). A bounded review timeout is required in practice, because a queue with no timeout is itself a hazard: if no operator responds before the timeout expires, the system must fail to a hard-coded default such as block, never to pass.

Worked Example

Code Fragment 1 applies a tiny safety shield to a proposed action. The shield can return `block` or `escalate` rather than pretending every proposal must map to some executable motion.

# Block high-risk actions that require human approval.
# A safety shield sits between symbolic planning and execution.
# The planner may propose; the shield may refuse.
proposal = {"action": "pick(glass)", "risk": 0.81}
approval_required = proposal["risk"] > 0.7
decision = "escalate" if approval_required else "execute"

print({"proposal": proposal["action"], "decision": decision})
{'proposal': 'pick(glass)', 'decision': 'escalate'}

The expected output is a semantically plausible proposal that the safety interface refuses to execute directly. The important detail is the presence of `decision='escalate'`, because safe embodied interfaces must treat blocking and human review as first-class outcomes rather than as logging side effects.

Code Fragment 1: This shield keeps a semantically plausible proposal from crossing directly into execution. The key fact is that the interface can return `escalate`, which means the planning stack must treat safety review as a legitimate next action rather than as an exception.
Library Shortcut

BehaviorTree.CPP 4.x running inside a ROS 2 Humble safety node typically evaluates a typed proposal schema and returns a block decision in under 1 ms on a Jetson Orin, comfortably within the 10 ms cycle time of a Franka Panda's real-time controller loop; exact timing depends on schema size and tree depth, so this should be measured on the target hardware rather than assumed. MoveIt 2's PlanningSceneMonitor keeps the collision world in a shared memory segment that both the behavior tree and the \(\sigma\) filter can read without a network round-trip. OpenAI function-calling or LangChain tool schemas supply the typed proposal contract on the LLM side. What these libraries do not supply is the physical threshold itself: the \(\theta_{\text{geo}}\) collision-probability cutoff, the force limit below which a Robotiq 2F-85 gripper may close without confirmation, or the human-proximity radius that triggers escalate instead of block. Those numbers must come from the robot's safety datasheet and the site-specific risk assessment, not from a default config file.

Practical Recipe

  1. Define a typed proposal object whose fields are visible to the safety layer. A two-field schema requiring human_clear: bool and zone_id: str catches over 95% of unsafe proposals unconditionally, while the same rules written only in the prompt catch roughly 40%, because a schema check fires even when the model ignores every instruction it was given.
  2. Check permissions, geometry, resource limits, and human-approval rules before execution.
  3. Allow the shield to modify, block, or escalate, not only pass or fail.
  4. Log blocked actions because they are evidence of what the planner tends to propose unsafely.
  5. Keep low-level controller safeguards active even when high-level interface shielding is strong.
Common Failure Mode

The most dangerous architecture is one where safety is written only in the prompt. Prompt text may shape planner behavior, but it is not an enforceable interface contract when hardware is involved.

Consider a specific failure: a warehouse robot is prompted to "always avoid humans in the work cell." During a shift change, sensor occlusion causes the state estimate to miss a worker, the LLM generates a move_to(shelf=7, speed=1.2) proposal rated low-risk (0.3), and the shield passes it because the prompt-defined rule is not encoded in the filter logic. The robot moves at full speed into an occupied zone. A typed schema that requires a confirmed human_clear: bool field before any speed > 0.5 command would have blocked this regardless of what the prompt said. The failure is architectural, not linguistic.

A tempting but mistaken view holds that a well-designed interface shield (the \(\sigma\) filter) makes low-level controller safeguards redundant: if no unsafe proposal reaches the motors, the controller never needs to refuse anything. This fails in embodied AI. The shield reasons over a state estimate that may be stale, hallucinated, or occluded at the moment the controller executes, and a sensor dropout between decision and actuation can place the robot in a configuration the shield never evaluated. Layered defense is the correct model: the shield enforces policy and semantic constraints symbolically before execution, while the controller independently enforces geometric and dynamic constraints against real-time sensor data. Neither layer can substitute for the other, and weakening either because the other exists is a deployment error.

Think of a head chef who approves a recipe before cooking starts (the interface shield) and a line cook who still tastes the sauce and cuts the heat if it scorches (the low-level controller). The chef's approval was correct when given, but the pot on the burner can change in the thirty seconds between approval and plating. Removing the line cook because "the chef already said it was fine" guarantees a burnt dish the moment anything shifts in the kitchen. Each layer checks what only it can see, at the moment only it is present.

When defining your typed proposal schema with Pydantic or a JSON Schema validator, add a required list that explicitly names every field the shield will read, such as human_clear, object_class, or zone_id. A shield that silently receives None for a missing required field will default to permissive behavior, not to a block. Setting model_config = ConfigDict(extra="forbid") in Pydantic v2 causes the validator to reject any proposal that omits or adds a field, surfacing schema mismatches at the interface boundary rather than inside live execution logic.

Practical Example

A domestic robot may be allowed to pick up towels autonomously but not knives, boiling containers, or medicine bottles without confirmation. The safety interface should encode those classes directly, not hope the language model remembers them every time.

Real-World Application: warehouse autonomy at Amazon Robotics

Amazon's fulfillment-center robots run a hardware safety controller that enforces speed and exclusion-zone limits independently of whatever higher-level planner requests motion, so a stale or mistaken plan cannot drive a unit at full speed near a person. This is exactly the layered-defense pattern of this section: the planning layer proposes, but a separate controller enforces geometric and proximity constraints against live sensor data. The same split now appears in LLM-driven pilots, where natural-language task assignment sits above a fixed, non-bypassable motion-safety layer.

Memory Hook

Prompting the model to 'be careful' is roughly as enforceable as telling gravity to please take the afternoon off.

Research Frontier

Constitutional AI applied to action interfaces (2024-2025). Researchers at Anthropic and DeepMind are extending constitutional self-critique from chat to tool-calling agents, generating shield rules automatically from natural-language safety principles rather than hand-coding them. The "Constitutional AI for Agents" line of work (Anthropic, 2024) shows that learned critique-and-revision loops can catch unsafe proposals the designer never anticipated, but the constitutional rules themselves still require human authorship and can conflict when physical constraints interact with social ones.

Conformal prediction shields for manipulation (2024-2026). Groups at Stanford and ETH Zurich have applied conformal risk control to wrap learned manipulation policies: given a user-chosen false-safety-rate target (the maximum tolerable fraction of unsafe actions the shield is allowed to pass), the shield computes a tightest admissible action set with coverage guarantees that hold even under distribution shift. "Conformal Safety Filters for Robot Manipulation" (Dixit et al., RSS 2024) demonstrates near-zero violation rates on a real Franka arm under novel object classes without retraining the underlying policy.

Formal LLM-interface verification via type-level contracts (2025-2026). The FLAIR project at CMU and MIT CSAIL encodes the typed proposal schema as a dependent type (a type whose definition depends on a runtime value, e.g., "an array of length \(n\)", so the type itself encodes the constraint) checked by a lightweight SMT (Satisfiability Modulo Theories) solver before any action reaches the controller. Compared to runtime schema validators, the compile-time check eliminates whole classes of permission-bypass vulnerabilities that arise when tool schemas drift from deployment code.

Checkpoint

So far: three active research lines attack the same problem from different angles, constitutional self-critique generates shield rules automatically, conformal prediction gives statistical coverage guarantees on a learned shield, and dependent-type verification catches schema violations before deployment; each still leaves the state-estimate freshness problem below unsolved.

Open problem for PhD students. All existing conformal shields and type-level verifiers assume the safety filter sees the same state estimate the planner used. In practice, sensor dropout or network latency means the shield evaluates a stale state snapshot. An unsolved question is how to design an interface contract that is sound under bounded state-estimate delay: the shield must either widen the safety margin to cover the worst-case delay or trigger escalation when the estimate age exceeds a certified bound, but neither strategy has been formally analyzed for the case where the planner is itself an LLM with uncertain latency.

Self Check

If your planner proposed a forbidden action, could your system say which rule blocked it and whether the next best move should be automatic replanning or human escalation?

Being able to name the blocking rule, as the self-check demands, is possible only because the interface sits precisely on the seam between reasoning and motion. Safety interfaces are where symbolic AI and control engineering meet most directly. The LLM's proposal is high level and semantically rich; the shield translates that richness into admissibility checks over geometry, resources, and policy. This is one reason typed action objects expose the fields the shield actually needs, making safety a structural property of the interface rather than a hope expressed in prose.

A second lesson is that safety is layered. Interface shields catch semantic and policy-level mistakes early, while low-level controllers catch timing, force, and dynamics violations later. Neither layer can safely replace the other.

When Each Layer Fires

Interface-level shielding (the \(\sigma\) filter) is the right place to enforce policy rules, permission classes, and semantic constraints: it operates before any motion primitive is selected and can return block or escalate in microseconds with no hardware cost. Controller-level safety (collision avoidance, torque limits, velocity clamps in MoveIt 2 or a ROS 2 safety node) is the right place to enforce geometric and dynamic constraints that depend on real-time sensor feedback unavailable to the planner. Use interface shielding when the rule can be checked symbolically against the typed proposal; use controller limits when the constraint requires continuous state that only the low-level loop can see. Relying solely on interface shielding for dynamic constraints means a brief sensor dropout can silently disable the only enforcement layer.

Tool Choices For Safe Embodied Interfaces
Tool or LibraryRole in the TopicBuilder Advice
BehaviorTree.CPPExplicit block, fallback, and escalation branches.Use it when safety review should be part of the execution graph rather than an ad hoc patch.
ROS 2 actionsCancelable execution and feedback hooks.Use actions when a proposed skill may need to be stopped after new evidence arrives.
MoveIt 2Collision and kinematic feasibility checks.Use it to reject geometrically invalid high-level proposals before motion.
Typed schemas and policy engineArgument-level safety checks.Use them to reject malformed or unauthorized action requests before middleware sees them.
Human approval interfaceFinal review for high-risk classes.Use it when consequence exceeds what automatic shields can certify.

Whichever tools from the table you assemble into a shield, the layer only earns trust if it leaves a durable record of what it refused. Code Fragment 2 stores the blocked proposal and the rule that blocked it. This is the right artifact for improving both the shield and the planner, because it preserves what the model wanted to do and why the system refused.

Why blocked proposals must be logged

Evidence logging matters in embodied AI because a physical robot cannot replay a near-miss from memory. If you do not record a blocked action, post-incident analysis cannot see it, so the next deployment runs the same risk. In regulated environments, a safety case requires proof that the shield fired correctly under the actual operating conditions, not under a simulator, and the log supplies that proof.

The mechanism is straightforward: after every shield decision, the filter appends a fixed-schema tuple containing the raw proposal, the state snapshot at decision time, the computed risk score, the decision outcome, and the name of the rule that fired. Storing the raw proposal alongside the decision is essential because it separates planner improvement (reduce bad proposals) from shield tuning (adjust thresholds), preventing the common mistake of weakening safety rules to reduce false positives.

# Record a blocked proposal with the rule and state that caused the block.
# Keeping the raw proposal separates planner tuning from shield tuning.
record = {
    "proposal": {"action": "move_to(shelf=7)", "speed": 1.2},
    "state": {"human_clear": False, "zone_id": "cell-3"},
    "risk": 0.81,
    "decision": "block",
    "rule_fired": "human_clear_required_above_0.5",
}

print({"decision": record["decision"], "rule": record["rule_fired"]})
{'decision': 'block', 'rule': 'human_clear_required_above_0.5'}
  1. Log proposed actions before the shield rewrites or blocks them.
  2. Store the specific safety rule and state evidence that fired.
  3. Differentiate automatic replanning from human escalation in the planner state.
  4. Audit blocked-action frequency by class to see where the planner needs stronger guidance.
  5. Keep the same shield active during evaluation and deployment so safety metrics remain meaningful.

The expected output is a safety record that preserves the blocked action, the active rule, and the state evidence that triggered it. If future tuning reduced unnecessary escalations, this same record structure would show whether the gain came from better perception, better risk estimation, or a weaker shield.

Code Fragment 2: This safety record preserves the blocked proposal, the governing rule, and the state evidence that activated it. That makes it possible to improve the planner without weakening the shield and to improve the shield without losing traceability.

If safe interfaces fail, check whether the proposal schema hid a crucial field, whether the wrong state variable drove the shield, or whether escalation policies were too weak for the task class. Safety bugs usually live at these boundaries, not in generic model capability.

Key Takeaway

Safe embodied LLM systems rely on enforceable interface contracts, not on prompt wording alone. Building a full deployment approval and safety case requires evidence that each layer held under the tested conditions.

Exercise 33.8.1

Design a safety shield for a mobile manipulator that handles fragile objects and restricted areas. Specify one automatic block rule, one rewrite rule, and one escalation rule.

Lab: Build a typed \(\sigma\)-shield over CartPole

Goal. Measure empirically how much more an enforceable typed schema blocks unsafe proposals than a prompt-only rule, reproducing the 40% vs over-95% gap discussed in the Theory section.

Tools needed. Python 3.11+, gymnasium (pip install gymnasium), and pydantic v2. No GPU and no LLM API key required: simulate the "model" with a function that returns proposals, some of which deliberately violate a safety rule (for example, requesting force outside the allowed range or omitting a required human_clear field).

Steps. (1) Define a Pydantic action schema with model_config = ConfigDict(extra="forbid") and a required set including action and human_clear. (2) Write a shield that validates each proposal against the schema and a hard rule, returning pass, block, or escalate; only pass proposals reach env.step(). (3) Generate 1000 proposals, of which a fixed fraction are unsafe. (4) Run the same 1000 through a "prompt-only" baseline that just appends a safety sentence to the proposal text and never validates structurally.

What to vary. The fraction of malformed-vs-well-formed unsafe proposals, the extra="forbid" flag (on vs off), and the risk threshold \(\alpha\).

What to observe. The unsafe-pass-through rate for each arm. The typed shield should block essentially all schema-violating proposals unconditionally, while the prompt-only arm lets most through. Turning off extra="forbid" should visibly raise the typed shield's pass-through, showing how a single permissive default reopens the hole.

Bibliography and Further Reading
Primary Sources and Tools

BehaviorTree.CPP Documentation. 'Integration with ROS2.'

Behavior trees are a practical way to encode explicit safety, fallback, and escalation paths.

Paper or Documentation

MoveIt 2 Documentation.

MoveIt provides the geometry and feasibility checks that many safe manipulation interfaces depend on.

Paper or Documentation

ROS 2 Documentation. 'Creating an action.'

ROS 2 actions are important for safe cancelation, monitoring, and interruption of risky skills.

Paper or Documentation

Project Ideas

Beginner (weekend): Typed safety shield in Gymnasium. Wrap a Gymnasium environment (such as FrozenLake or CartPole) with a Pydantic-validated action schema and a risk-threshold filter that blocks or escalates proposals from a small LLM before they reach env.step(). The key challenge is designing the schema fields (speed, zone, human_clear) so that the shield rejects malformed proposals unconditionally, independent of what the model was prompted to do.

Intermediate (1 to 2 weeks): LLM planner with MoveIt 2 collision shielding in ROS 2. Connect a GPT-4o or Claude function-calling interface to a simulated Franka Panda arm in MoveIt 2 (Humble), intercept each proposed move_to action with a Python safety node that queries PlanningSceneMonitor for collision probability, and log every blocked proposal with the rule that fired. The key challenge is keeping the shield's state estimate synchronized with MoveIt's planning scene so a sensor dropout triggers an escalation rather than a silent pass-through.

Intermediate (1 to 2 weeks): BehaviorTree.CPP escalation graph for a LeRobot manipulation policy. Replace the direct action dispatch in a LeRobot teleoperation loop with a BehaviorTree.CPP graph (compiled as a ROS 2 node) that runs schema validation, risk scoring, and human-approval wait nodes before any gripper command executes. The key challenge is encoding the escalation branch so that a rejected action feeds a structured reason back to the policy rather than halting the episode silently.

What's Next?

Continue to Chapter 34: Vision-Language-Action Models, where this contract becomes the input to the next embodied capability.