Reward Shaping for Language Agents: From Format Checks to Trajectory Rewards
~13 min read
Designing a good reward function is the hardest part of RFT/ART-style training. The book's own GRPO reward functions show a practical pattern — layer cheap, deterministic checks — that generalizes to trajectory-level agent rewards.
The previous three subtopics established WHAT reward functions do (score correctness, drive learning via GRPO) but not HOW to actually design a good one — arguably the single hardest practical skill in both RFT (sft-vs-rft topic) and ART-based agent training (previous subtopic). This subtopic covers that design problem specifically for TEXT-based tasks.
This course's own GRPO walkthrough (in this same fine-tuning chapter, immediately preceding the RL-environments sections) demonstrates a genuinely useful pattern by using FOUR separate reward functions rather than one: match format exactly, match format approximately, check the answer, and check numbers. Layering multiple checks like this, rather than relying on one all-or-nothing reward, gives the model partial credit and a smoother learning signal — an output that's almost right (correct answer, slightly wrong format) scores better than a completely wrong one, which is far more informative for learning than a single binary 'correct or not' signal that can't distinguish 'close' from 'nowhere close.'
This same layering principle extends naturally to TRAJECTORY-level rewards for language agents (the ART framework's domain). A few practical categories worth knowing: outcome rewards check whether the FINAL result was correct (did the agent's answer match, did the task actually get completed) — the most important signal, but the sparsest, since it only fires once at the very end of a potentially long trajectory. Process rewards score intermediate steps along the way (did the agent call an appropriate tool for this situation, did it avoid an obviously wasteful repeated action) — denser and more frequent than outcome rewards, which helps address the classic RL problem of SPARSE rewards (if the only signal is 'did the whole 20-step trajectory succeed,' the model gets very little information about which of those 20 steps actually helped). Efficiency penalties discourage unnecessarily long trajectories or redundant tool calls — directly mirroring this course's own reward-function pattern of rewarding not just correctness but ADHERENCE to a desired shape (its 'match format' checks reward the RIGHT SHAPE of answer, not just any correct-content answer).
The overarching design principle, generalizing from this course's own deterministic, hard-to-game reward functions: prefer reward signals that are automatically checkable and resistant to gaming over ones that require subjective judgment. A reward like 'did the final numeric answer match' is nearly impossible for the model to satisfy without actually solving the problem. A vaguer reward like 'did the response seem helpful' (if scored by a weaker automated check rather than a careful LLM-as-judge, from the LLM-evaluation unit) is far easier for a model to learn to superficially satisfy without genuinely improving — exactly the reward-hacking failure mode the sft-vs-rft topic's tradeoffs subtopic warned about.
💻 Code example
# Implementing a layered, multi-check reward function in the style
# of the book's 4 GRPO reward functions, extended to a trajectory-
# level agent task (outcome + process + efficiency rewards combined).
def format_exact_match(output: str, expected_format: str) -> float:
return 1.0 if output.strip() == expected_format else 0.0
def format_approx_match(output: str, required_tags: list[str]) -> float:
"""Partial credit: how many of the required structural tags are present."""
present = sum(1 for tag in required_tags if tag in output)
return present / len(required_tags) if required_tags else 0.0
def outcome_reward(trajectory: list[dict], expected_answer: str) -> float:
"""Sparse: fires only on the final trajectory outcome."""
final = next((s for s in trajectory if s["type"] == "final_answer"), None)
return 1.0 if final and expected_answer in final["content"] else 0.0
def process_reward(trajectory: list[dict]) -> float:
"""Denser: rewards appropriate tool use along the way, not just the end."""
good_tool_calls = sum(1 for s in trajectory if s["type"] == "tool_call")
return min(good_tool_calls * 0.2, 0.6) # capped partial credit
def efficiency_penalty(trajectory: list[dict], ideal_length: int = 4) -> float:
"""Discourage unnecessarily long/redundant trajectories."""
excess_steps = max(0, len(trajectory) - ideal_length)
return -0.1 * excess_steps
def combined_trajectory_reward(trajectory: list[dict], expected_answer: str) -> float:
"""Layering multiple checks (the book's own pattern) instead of
one all-or-nothing signal -- smoother, more informative learning signal."""
return (
outcome_reward(trajectory, expected_answer)
+ process_reward(trajectory)
+ efficiency_penalty(trajectory)
)
example_trajectory = [
{"type": "reasoning", "content": "thinking..."},
{"type": "tool_call", "tool": "search"},
{"type": "final_answer", "content": "The capital is Paris"},
]
reward = combined_trajectory_reward(example_trajectory, expected_answer="Paris")
print(f"combined trajectory reward: {reward:.2f}")
💬 Deep Dive with AI
Key points
- •The book's own GRPO reward functions layer 4 separate checks (format-exact, format-approximate, answer, numbers) rather than one binary signal, giving smoother partial credit
- •The same layering principle extends to trajectory-level agent rewards: outcome rewards (sparse, fires at the end), process rewards (denser, scores intermediate steps)
- •Sparse outcome-only rewards give the model little information about WHICH steps in a long trajectory actually helped — process rewards help address this
- •Efficiency penalties discourage unnecessarily long or redundant trajectories, mirroring the book's 'match format' checks rewarding the right SHAPE, not just correct content
- •Prefer automatically-checkable, hard-to-game reward signals over subjective ones — the same reward-hacking risk from the sft-vs-rft tradeoffs subtopic applies directly here