"The language model knows what sounds like a good idea. The value function knows what the room will actually permit. SayCan is the negotiation between them."
Section 33.2
This section assumes familiarity with value functions and reinforcement learning basics from section 14.2 and with skill libraries and hierarchical decomposition from sections 26.3 and 26.5. The affordance-grounding idea introduced here is extended in section 33.3, which replaces discrete skill selection with LLM-generated executable code, and recurs in Part X alongside open-world skill discovery in section 51.3.
Read the figure as the SayCan product rule in system form: language likelihood proposes what is useful, affordance likelihood estimates what is possible, and the robot acts only where both scores support an executable skill.
The SayCan closed loop. An instruction reaches the planner, where the language-times-affordance product selects one executable skill; the tool API commits that skill to hardware, and the verifier feeds success or failure evidence back so the next decision is grounded in what the robot just actually did. This is the same diagram introduced as Figure 33.1.
Review and Consolidation
Depth and self-containment. Readers should leave with the exact factorization used by SayCan and a clear view of why language plausibility alone is insufficient for robot planning. The section must also clarify where the affordance score comes from and what it assumes.
Production and evaluation contract. The artifact must record candidate skills, language-model probabilities, affordance values, the combined score, and the selected action. Only then can one audit whether the planner failed semantically or physically.
Name the language interface, grounded world state, executable action contract, and evidence artifact before trusting any claimed improvement.
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.
A robot in a kitchen hears "bring me something to drink." The language model scores a dozen candidate actions: "pick up the soda can" ranks high. But the can is on a high shelf the robot cannot reach from its current position, while an open water bottle sits at arm's level. Without grounding, the planner confidently attempts the impossible. SayCan fixes this by multiplying language plausibility with a learned affordance value that encodes what the robot can actually execute right now. The result is a planning rule precise enough to deploy on real hardware today. Work through this section to understand the product-rule factorization, where the affordance score comes from, and why neither source of evidence is sufficient alone.
Ask a fluent language model what a thirsty person wants and it will confidently say "pick up the soda can," even when that can sits on a shelf the robot's arm cannot reach and an open water bottle waits at its fingertips: the words are right and the physics is wrong. An affordance names what a skill can actually accomplish in the current physical state: whether the object is reachable, graspable, and the action executable right now, as opposed to merely sounding correct. SayCan is the natural antidote to this gap, because it makes the combination of semantic relevance and executability stronger than either source of evidence alone. Figure 33.2 traces the closed loop this grounding sits inside, where an instruction reaches a planner, the planner commits to a skill through the tool API, and a verifier feeds failure evidence back into the next decision.
A planner that speaks fluently but cannot reach the shelf is not a robot controller; it is a suggestion box.
The practical question is how to combine semantic relevance and executability without letting one wash out the other.
SayCan works because semantic plausibility and physical feasibility answer different questions. One says what the human probably wants next; the other says what the robot can actually do now.
Theory
SayCan scores each candidate skill \(k\) with a language prior and an affordance value: $$k^* = \arg\max_k \; p_\text{LLM}(k \mid x, h_t) \cdot V_k(s_t).$$ The language term prefers semantically appropriate next steps, while the value function estimates whether the robot can execute that step successfully in the current state.
The multiplication matters. A skill with high semantic probability but near-zero affordance should be rejected, and a highly executable skill with no semantic relevance should not dominate just because it is easy. This is called the language-times-affordance product rule, and in the original SayCan experiments on real kitchen hardware it typically lifted task completion from roughly 56% (language score alone) to 74% (joint score), a gap reported across 101 real-robot trials in that study. The method therefore depends on score calibration and on the quality of the candidate skill library.
Why the affordance value carries physical constraints
The value function \(V_k(s_t)\) matters for embodied AI because robots face hard physical constraints that a text-trained language model cannot observe. Joint limits, reach envelopes, friction coefficients, and object occlusion all determine whether a skill is physically executable in a given configuration. A wrong choice carries real consequences: dropped objects, collision, or wasted time that the physical world will not roll back. A scalar learned on real interaction data keeps those constraints implicit yet grounds them in actual robot experience rather than language statistics.
Mechanically, each \(V_k\) is a per-skill success-value function trained with reinforcement learning (RL) or behavioral cloning on robot episodes. Given the current sensor observation \(s_t\), it estimates the probability of completing skill \(k\) from that state. Training on real or simulated executions lets it capture preconditions such as object reachability, grasp clearance, and surface stability, and it does so without any explicit symbolic encoding of those constraints.
Checkpoint
So far: the affordance value \(V_k\) is a per-skill success predictor trained on robot experience (via RL or behavioral cloning) that captures physical preconditions like reachability and grasp clearance implicitly, without any symbolic encoding of those constraints.
The data asymmetry sharpens the point. The original SayCan system needed roughly 68,000 robot episodes across 551 skills to build the affordance library, while the language model required zero additional robot interaction and contributed its entire semantic prior from pre-training on text alone.
A good mental model is product-of-experts planning, where each expert model scores candidates on a different criterion and the final decision multiplies their scores together so an option only wins when every expert finds it acceptable. The LLM narrows the skill search to task-consistent options, and the affordance model removes options that are impossible or low value in the current world state.
Algorithm: SayCan Affordance-Grounded Skill Selection
Input: natural-language instruction \(x\), interaction history \(h_t\), robot state \(s_t\), skill library \(\mathcal{K} = \{k_1, \ldots, k_n\}\), language model with parameters \(\theta\), per-skill value functions \(\{V_{k}\}\) trained via RL with learning rate \(\alpha\)
Output: selected skill \(k^*\) and an audit tuple \((k, p_\text{LLM}, V_k, \text{score})\) for every candidate
- Encode the instruction and history: form the prompt \(c_t = (x, h_t)\) and pass it through the frozen LLM \(\pi_\theta\) to obtain a distribution over skill tokens.
- Generate the candidate set: sample or enumerate the top-\(m\) skills \(\mathcal{K}' \subseteq \mathcal{K}\) ranked by \(p_\theta(k \mid c_t)\) to avoid scoring the entire library.
- Score language plausibility: for each \(k \in \mathcal{K}'\) compute \(p_\text{LLM}(k) = p_\theta(k \mid c_t)\) and apply temperature scaling, where the exponent \(1/\tau\) sharpens the distribution toward the top candidate when \(\tau<1\) and flattens it toward uniform when \(\tau>1\): \(\hat{p}(k) = p_\text{LLM}(k)^{1/\tau}\), then renormalize over \(\mathcal{K}'\).
- Estimate affordance: query each value function \(V_k(s_t) \in [0, 1]\) using the current scene observation; apply min-max normalization (rescaling values to a common 0-to-1 range using the candidate set's own minimum and maximum, so the two scores stay comparable): \(\hat{V}_k = (V_k(s_t) - V_{\min}) / (V_{\max} - V_{\min})\) over \(\mathcal{K}'\).
- Compute the joint score: \(\text{score}(k) = \hat{p}(k) \cdot \hat{V}_k\) for all \(k \in \mathcal{K}'\).
- Select the action: \(k^* = \arg\max_{k \in \mathcal{K}'} \text{score}(k)\).
- Write audit record: append the tuple \((c_t,\, s_t,\, k^*,\, \hat{p}(k^*),\, \hat{V}_{k^*},\, \text{score}(k^*),\, k_2,\, \text{score}(k_2))\) where \(k_2\) is the runner-up, to the episode log.
- Execute \(k^*\) on the robot and observe the resulting state \(s_{t+1}\) and verifier signal \(r_t \in \{0, 1\}\).
- Update history: \(h_{t+1} = h_t \cup \{(k^*, r_t)\}\) and increment \(t\).
- Repeat from step 1 until the task is complete or a budget of \(T\) steps is exceeded; on failure, inspect the audit log to determine whether the semantic prior (\(\hat{p}\)) or the affordance estimate (\(\hat{V}\)) was the proximate cause.
Worked Example
The ten-step algorithm above is easier to trust once you watch its scoring core run on concrete numbers, so the next fragment strips the loop down to the single product computation at its heart.
Code Fragment 1 implements the core SayCan score on three skills. The example is tiny, but it makes the product structure visible and shows how the selected action can differ from the highest language score alone.
# Combine semantic plausibility with grounded affordance values.
# The best skill is not the one with the largest language score alone.
# Product scoring removes semantically attractive but infeasible actions.
skills = {
"pick_sponge": {"p_llm": 0.55, "affordance": 0.92},
"turn_on_sink": {"p_llm": 0.30, "affordance": 0.95},
"wipe_spill": {"p_llm": 0.80, "affordance": 0.18},
}
combined = {name: round(v["p_llm"] * v["affordance"], 3) for name, v in skills.items()}
print(combined)
print(max(combined, key=combined.get))
The expected output is a ranking where the chosen skill is not merely the most semantically plausible sentence completion, but the action with the highest joint semantic and affordance score. Here `pick_sponge` wins because it is both relevant and executable in the present scene, while `wipe_spill` is semantically tempting but prematurely chosen.
Step-Through: the language-times-affordance product rule
Trace the selector for the instruction "clean up the spill" with three candidates. Start from raw scores, then normalize and multiply, watching the winner change at each stage.
Step 1, raw scores. The frozen LLM returns semantic probabilities and the value functions return affordances: wipe_spill has \(p_\text{LLM}=0.80\), \(V=0.18\); pick_sponge has \(p_\text{LLM}=0.55\), \(V=0.92\); turn_on_sink has \(p_\text{LLM}=0.30\), \(V=0.95\). By language alone the winner is wipe_spill (0.80), which is physically premature.
Step 2, normalize the language term. With temperature \(\tau=1\) the probabilities renormalize over the three candidates: \(0.80/1.65=0.485\), \(0.55/1.65=0.333\), \(0.30/1.65=0.182\).
Step 3, normalize the affordance term. Min-max over \(\{0.18, 0.92, 0.95\}\) gives \(V_{\min}=0.18\), \(V_{\max}=0.95\), so wipe_spill becomes \((0.18-0.18)/0.77=0.00\), pick_sponge becomes \((0.92-0.18)/0.77=0.961\), turn_on_sink becomes \((0.95-0.18)/0.77=1.00\).
Step 4, multiply. The joint scores are wipe_spill \(=0.485\times0.00=0.000\), pick_sponge \(=0.333\times0.961=0.320\), turn_on_sink \(=0.182\times1.00=0.182\).
Step 5, select. \(\arg\max\) picks pick_sponge at 0.320. The language favorite (wipe_spill) collapsed to zero because the floor of the affordance range zeroed it out, and the most-affordable skill (turn_on_sink) lost on relevance. The product rule chose the action that is both wanted and possible, exactly the negotiation the section describes.
The same idea can be implemented with a few lines using an LLM API plus an affordance model wrapped behind a typed tool interface. Those libraries remove prompt and schema boilerplate, but they do not remove the need to calibrate the affordance score and the candidate skill set.
Practical Recipe
- Define a compact skill library whose actions expose clear preconditions and effects.
- Generate only semantically plausible skill candidates rather than scoring the entire API surface.
- Estimate affordance or value in the current state before execution, not from a stale scene snapshot.
- Normalize or calibrate the two scores so one term does not dominate by scale alone.
- Inspect failure cases where the right long-horizon plan starts with a low-probability semantic step.
Before multiplying the two scores, apply temperature scaling to the LLM log-probabilities and normalize the affordance values to the same range (for example, min-max normalization over the current candidate set). Without this step, a value model that returns raw success probabilities near 1.0 for all feasible skills will compress the semantic signal to near-zero influence, making the planner behave like a greedy affordance selector. A fast diagnostic: log the ratio p_llm / affordance across a held-out episode; if it is consistently below 0.05, the affordance term is dominating and calibration is the first thing to fix, not the prompt.
SayCan can fail if the candidate skill set is too narrow, the value functions are poorly calibrated, or the semantic model overprefers narratively obvious steps that are not optimal for the current embodiment.
In practice the most common failure is affordance miscalibration. A value model trained on a narrow set of scenes can return high scores for skills that are physically possible in training but inapplicable to the current configuration. For example, a value function trained on an uncluttered countertop may assign \(V_\text{pick\_sponge}(s_t) = 0.90\) even when a bowl blocks the sponge. The model never learned that obstacle. The LLM sees a high combined score, selects the skill, and the robot fails. The symptom looks like a planning error, but the root cause is a distributional gap in the affordance model. A second, subtler failure appears at long horizons. The product rule is myopic: it scores each skill only against the current state. In a multi-step cleanup task, the optimal first action may be to move a bowl. That step has a low immediate semantic score, but it unlocks the sponge for step two. SayCan has no lookahead and can get stuck in a locally attractive but globally suboptimal sequence.
In kitchen cleanup, 'wipe the spill' sounds like the right next step, but the robot may first need to pick the sponge or move a blocking bowl. Affordance grounding keeps the planner from issuing impossible or premature skills.
Real-World Application: kitchen mobile manipulation at Google
SayCan was deployed on Everyday Robots' mobile manipulators in a real office kitchen, where a single robot interpreted free-form requests like "I spilled my drink, can you help?" and chained skills such as finding a sponge, picking it up, and bringing it over. The language model proposed the helpful next step while the learned affordance values, trained on roughly 68,000 real robot episodes across 551 skills, pruned anything the arm could not actually execute from its current pose.
SayCan is the polite adult in the room. It lets the language model dream big, then asks whether the robot can actually reach the sponge before promising heroics.
Vision-language-action models replacing the two-stage product rule. Rather than maintaining separate language-prior and affordance-value modules, recent work collapses them into a single generalist policy trained end-to-end on robot trajectories paired with language. Google DeepMind's RT-2 (Brohan et al., 2023) and its successor pi0 (Black et al., 2024) demonstrated that a vision-language backbone fine-tuned on action tokens can implicitly encode both semantic relevance and physical feasibility, typically reporting strong generalization on the BridgeData V2 benchmark (as of 2024) without a separate value function. The open research question is whether implicit grounding scales to novel object configurations as reliably as an explicit learned affordance model.
If the product rule cannot see past the current step, what happens when the only path to success requires three seemingly wrong-looking moves first?
World-model-grounded lookahead. SayCan's product rule is myopic: it scores only the immediate next skill. A 2024-2025 line of work from Berkeley and CMU (UniSim, Dreamer-v3 applied to manipulation) trains compact video prediction models that simulate the outcome of each candidate skill one or two steps forward, replacing the scalar value function with a rollout-based affordance estimate. This directly addresses the "narrow door" failure mode and has been demonstrated on real Franka hardware in multi-step rearrangement tasks, though inference latency remains a practical bottleneck.
Open-vocabulary affordance without per-skill RL. Training a separate value function for each skill in a large library is expensive. Work from the Princeton Robot Learning Lab (GR-2, 2024) and from the Open X-Embodiment consortium uses a single vision-language model queried in zero-shot to estimate affordance by asking "can the robot successfully do X given this image?" and extracting the yes/no log-probability as the affordance score. This removes the per-skill RL training requirement but introduces calibration sensitivity to prompt phrasing.
Open problem for PhD students. All three directions above assume the skill library is fixed before deployment. A tractable open problem is online skill discovery under affordance grounding: when the product rule consistently returns a near-zero joint score for every candidate skill at a given state, the system has implicitly identified a coverage gap in the library. Designing an algorithm that uses these repeated failures as a signal to propose, name, and learn a new primitive skill, then integrate it into the affordance model, remains unsolved at the level needed for real-world deployment on heterogeneous hardware.
If a skill has the highest language score but the lowest affordance, do you know where that skill should still appear in the diagnostic trace and why it should not win execution?
Knowing where a losing skill belongs in the trace only helps if its two scores are comparable, which exposes the deeper issue: calibration. The product formula is meaningful only when the two terms share an interpretation. A miscalibrated value model swamps the semantic term and reduces the planner to greedily grabbing whichever skill is easiest right now.
The myopic product rule is like choosing your next step in a maze by asking only "which door looks most promising right now?" without a map. A navigator who always picks the widest, best-lit corridor will sail confidently forward until the path dead-ends, while the narrow, dim side passage that felt wrong at step one was actually the only route to the exit. SayCan scores each skill against the current scene and moves on; it has no memory of which future doors a choice today will open or close.
The method also inherits the classic option-discovery problem from hierarchical RL. It can only select among skills it already knows. If the correct subtask is missing from the library, no amount of language fluency will recover it, which is why skill design and language as a high-level controller remain central.
| Tool or Library | Role in the Topic | Builder Advice |
|---|---|---|
| LLM API with structured outputs | Candidate skill proposal. | Use it when the skill library is large enough that language can prune it meaningfully. |
| RL or success-value model | Affordance estimate for each skill. | Use it when executability depends on the current scene and embodiment. |
| BehaviorTree.CPP | Execution shell for chosen skills. | Use it when each skill needs explicit retry and failure handling. |
| ROS 2 actions | Typed skill invocation. | Use actions when each selected skill is long running and needs feedback. |
| EmbodiedBench or task-specific simulator | Construct-matched evaluation. | Use a matched benchmark when comparing SayCan-style planners against simpler baselines. |
Whichever combination of these tools you assemble, the planner is only debuggable if it records why it chose what it chose, which is what the next fragment captures. Code Fragment 2 stores the separate scores as an audit artifact rather than only the winning skill. This is the minimum needed to understand whether the semantic prior or the affordance estimator caused a bad decision. The steps below map directly onto the code that follows:
- Log the candidate skills and both scores for each decision point.
- Keep the value-estimation state snapshot or seed so scores can be reproduced.
- Store the chosen skill and the first rejected alternative for debugging.
- Measure how often the affordance term changes the top language choice.
- Benchmark with the same skill library and same execution stack when comparing alternatives.
# Build one audit record per decision point instead of logging only the winner.
# Keeping both raw scores lets a failure be traced to language or affordance.
def audit_record(candidates, p_llm, affordance):
scored = {
name: round(p_llm[name] * affordance[name], 3) for name in candidates
}
ranked = sorted(scored, key=scored.get, reverse=True)
chosen, runner_up = ranked[0], ranked[1]
return {
"chosen": chosen,
"chosen_score": scored[chosen],
"runner_up": runner_up,
"runner_up_score": scored[runner_up],
"p_llm": p_llm[chosen],
"affordance": affordance[chosen],
}
candidates = ["pick_sponge", "turn_on_sink", "wipe_spill"]
p_llm = {"pick_sponge": 0.55, "turn_on_sink": 0.30, "wipe_spill": 0.80}
affordance = {"pick_sponge": 0.92, "turn_on_sink": 0.95, "wipe_spill": 0.18}
print(audit_record(candidates, p_llm, affordance))
The expected output is an audit tuple that preserves both factors of the SayCan product. If a future run selected the wrong skill with a high semantic score but a weak affordance score, the repair target would be calibration or candidate generation rather than the planner prompt alone.
If the chosen skill is poor, first check candidate generation, then affordance calibration, then library coverage. SayCan errors often come from what is missing from the candidate set, not only from how the final score is computed.
A common assumption is that a more capable LLM could simply replace the affordance value function, reasoning that a smarter language model would "know" whether a skill is physically executable. This is wrong in the embodied AI context because the LLM is trained on text and has no runtime access to the robot's current sensor state: it cannot observe joint positions, object occlusion, or surface friction at the moment of planning. Physical executability is a function of the present world configuration, not of linguistic knowledge, and it changes every time the robot or its environment moves. The correct mental model is that the language model answers "what should come next given the goal?" while the affordance model answers "what can the robot actually do right now?"; these are structurally different questions that require different evidence sources, regardless of how powerful the language model becomes.
SayCan succeeds by treating language and affordance as complementary experts rather than competing controllers.
Construct a three-skill example where the top semantic choice is not executable and the top affordance choice is semantically irrelevant. Show how the product rule resolves the conflict and when it might still fail.
Project Ideas
Tabletop SayCan scorer in Gymnasium (beginner, one weekend): Build a discrete grid-world environment in Gymnasium where objects are scattered on a table and a small skill library (pick, push, move) has hand-coded affordance values that depend on agent proximity; wire an LLM API to score each skill by language probability and multiply the two scores to select actions, then log which term changed the winner. The key challenge is calibrating the affordance values so neither term dominates by scale.
SayCan planner on a PyBullet robot arm (intermediate, one to two weeks): Implement the full SayCan loop on a simulated UR5 or Franka in PyBullet: train per-skill success-value functions with behavioral cloning on ten demonstrations each, call an LLM API to score a library of five to eight parameterized skills given a natural-language instruction, and run the product-rule selector in a closed loop until task completion or a step budget is exhausted. The key challenge is keeping the per-skill value functions calibrated across object poses and scene configurations that differ from the training distribution.
ROS2 skill dispatcher with affordance gating (intermediate, one to two weeks): Using ROS2 actions and a LeRobot-pretrained manipulation policy as the execution backend, build a skill dispatcher node that receives a natural-language goal, queries an LLM for a ranked candidate list, queries a small MLP affordance model trained on wrist-camera observations for each candidate, and publishes only the top-scoring skill as a ROS2 action goal. The key challenge is reducing end-to-end latency so affordance inference does not stall the planning loop.
Lab: watch the affordance term flip the language winner
Goal. Build a minimal SayCan scorer and measure how often the affordance term overrides the top language choice, so you feel the product rule empirically rather than reading it.
Tools needed. Python with NumPy only (no GPU). Optionally an LLM API key if you want real language scores; otherwise use hand-set probabilities. Budget 15 to 30 minutes.
Setup. Define a library of six tabletop skills (pick_sponge, wipe_spill, turn_on_sink, move_bowl, open_drawer, place_cup). For each skill, draw a language probability from a fixed prompt (or assign by hand) and an affordance value \(V_k(s_t)\in[0,1]\) from a synthetic scene generator that places a random blocking object on the table. Apply temperature scaling to the language term and min-max normalization to the affordance term, then select \(\arg\max\) of the product.
What to vary. (1) The temperature \(\tau\) from 0.5 to 4.0; (2) whether a blocking object zeros out the relevant skill's affordance; (3) the spread of the affordance values (all near 1.0 versus widely separated).
What to observe. Log the language-only winner and the product winner for 500 random scenes and report the override rate (fraction where they differ). You should see the override rate climb as affordance spread grows, and collapse toward zero when all affordances cluster near 1.0, the exact calibration failure the Tip callout warns about. Plot override rate against \(\tau\) to see how an over-sharp language term suppresses the affordance signal.
SayCanPay is a useful follow-on showing how the original idea can be extended with heuristic planning and payoff estimates.
Ahn et al. (2022). "Do As I Can, Not As I Say: Grounding Language in Robotic Affordances." arXiv.
This is the primary SayCan source and the definitive reference for the language-times-affordance factorization.
MoveIt is relevant because many SayCan-style systems still hand off chosen subgoals to classical geometric planning stacks.