Introduction to Reward Hacking in inference optimization

@zakariaornot
Zakaria@zakariaornot
19 views Nov 04, 2026 ~6 min read
Advertisement

Lately, there’s been a lot of interest in using agents for inference optimization. Agents can run longer experiments, GPU capacity is expensive, and inference providers have a strong incentive to improve their margins.

Media image

We’re seeing this through initiatives like the Muse Spark Open Kernel Challenge and systems like Decagon’s AutoResearch, where agents repeatedly profile a deployment, make changes, and measure the results.

With all the discussion around recursive self-improvement, I wanted to cover something that deserves more attention: reward hacking.

In inference optimization, reward hacking happens when an agent improves its score by exploiting the evaluation without delivering the improvement we actually wanted. This can happen at the kernel level or across the whole serving stack.

At first glance, inference optimization seems fairly easy to verify:

  • Check that the output is correct.
  • Measure how long it takes.
  • The problem is that there are quite a few ways to pass both checks without doing the task properly. This article will cover a few examples we've seen at Overshoot and from the literature (see sources below).

    Fooling the timer

    We'll start with the most common one in kernel generation: using a side stream.

    To understand it, it helps to know how kernels get timed. When the CPU launches a kernel, it doesn't wait for it to run. It adds the kernel to a queue of GPU work, called a CUDA stream, and moves on. Generally, all instructions go into the same default stream.

    When timing a kernel, the evaluator adds a start marker to the default stream, launches the kernel, and adds an end marker. The GPU works through the queue in order and records the time at each marker, so the gap between the two markers is how long the kernel took.

    In a common hack, a kernel runs its computation on a separate CUDA stream. As a result, the default stream now contains only the two markers, so the GPU reaches the end marker right away and the measured time is close to zero, no matter how long the kernel takes. Smart agents will put some work on the default stream and the most expensive work on the side stream to not make it too obvious.

    The astute reader will note that this is the evaluator's fault. Indeed, the main fix for this hack is to wait for every stream to finish before ending the timer.

    This can happen also when optimizing the serving stack. InferenceBench, a benchmark for inference optimization agents, found the same behavior on full servers. When tasked to reduce the time to first token (TTFT), two proposed solutions sent an empty token as the first streamed token before processing the prompt at all. Measured latency fell below 3 ms, and because the real answer followed, the accuracy check still passed.

    Doing less work than requested when the passing criteria are loose

    When tasked to write a fast kernel in BF16, an agent wrote a kernel that computes the result at a lower precision and converts the final result back to BF16. If the accuracy tests are loose enough, the evaluator will not be able to tell. In fact, this was the most common hack in NVIDIA’s SOL-ExecBench.

    If such a hack happens during post-training, the model is rewarded for ignoring its instructions, and that behavior gets reinforced.

    The agent can tell how it’s being evaluated and acts accordingly

    Most evaluation harnesses in performance engineering run two separate evaluations:

  • Performance evaluation (e.g. throughput)
  • Accuracy evaluation (e.g. score on a benchmark)
  • If the agent can tell when they’re being evaluated for one vs the other, they can return fast and false answers for the performance evaluation and slow but correct answers during the accuracy evaluation. One such example is well-documented here.

    Caching responses and reusing them

    Benchmarks usually run the same call many times on the same inputs and average the timings to reduce noise. An agent can take advantage of this by saving the output the first time it computes it, along with a key that identifies the input, such as its memory address. Every later call finds the key and returns the saved output without running anything on the GPU, so the measured time is close to zero. The outputs are correct because they were computed for real the first time. If the evaluator checks correctness on fresh inputs, those have a new address, so the kernel computes them for real and passes that check too.

    In production, every request carries new inputs, so this cache would never hit and the speedup disappears.

    How to prevent your agent from cheating?

    Besides the obvious “check your agent’s work” which , let's be honest, most folks bother to do that anymore. Here are a few suggestions we found to be useful:

  • Babysit your agent as much as you can. Be involved in the process of idea generation / iteration as much as you can
  • Keep the evals out of the agent’s reach. Even if the agent cannot edit the evaluation benchmark, it can still find a way to infer what happens inside. Run the real evaluation in a sandbox, so the agent can't read the evaluation code or its inputs and only gets a score back. For kernels this isn't enough, because the timer has to run in the same process as the kernel, where the kernel can tamper with it. pygpubench handles this by keeping the timing logic in compiled C++, which Python code can't easily patch, and by writing results to a file the kernel can't reopen.
  • Have another independent agent verify that the instructions have been followed
  • Be suspicious of results that look too good. Have a roofline metric and generally assume that getting anywhere near 75% is very difficult.
  • Confirm gains on production traffic. Replay real requests and measure requests per second per GPU within your latency SLA. A trick that only exploits the benchmark won't show up there. Decagon's setup is a good reference: it measures baseline noise first, tests on production-shaped replays, and has a human review changes before they ship. We will share ours in a future article.
  • Sources:

  • InferenceBench: A Benchmark for Open-Ended LLM Inference Optimization by AI Agents, Jehyeok Yeon, Ben Rank and Maksym Andriushchenko (arXiv, July 2026): https://arxiv.org/abs/2607.20468
  • When AI Starts Writing Systems Code, Mark Saroufim (Core Automation, May 2026): https://www.coreauto.com/blog/when-ai-starts-writing-systems-code
  • A Field Guide to Reward Hacking in AI Kernel Generation, Emilio Andere (Wafer, March 2026):https://www.wafer.ai/blog/reward-hacks-field-guide
  • Anatomy of a Reward Hack: A Real Story from the Latest GPU Mode NVFP4 Competition, Natalia Kokoromyti (GPU MODE, March 2026): https://github.com/gpu-mode/kernelboard/blob/main/kernelboard/static/news/2026-03-09-reward-hacking.md
  • Preference Model Blog: https://www.preferencemodel.com/blog
  • Building an inference stack that improves itself, Pavi Dhiman and Cyrus Asgari (Decagon, October 2026): https://x.com/DecagonAI/status/2115511110120120437
  • Actions
    What You Can Do
    • Export as PDF or Markdown
    • Batch Export to Notion
    • Bookmark & Highlight
    • Screenshot Tweet
    Create Free Account

    Includes 7-day Premium trial

    Advertisement