<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://blog.huikang.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="http://blog.huikang.dev/" rel="alternate" type="text/html" /><updated>2026-06-29T07:17:36+00:00</updated><id>http://blog.huikang.dev/feed.xml</id><title type="html">Huikang’s blog</title><subtitle>Writing AGI fanfiction</subtitle><author><name>Tong Hui Kang</name></author><entry><title type="html">Design decisions of a generative recommender</title><link href="http://blog.huikang.dev/2026/06/28/generative-recommenders.html" rel="alternate" type="text/html" title="Design decisions of a generative recommender" /><published>2026-06-28T00:00:00+00:00</published><updated>2026-06-28T00:00:00+00:00</updated><id>http://blog.huikang.dev/2026/06/28/generative-recommenders</id><content type="html" xml:base="http://blog.huikang.dev/2026/06/28/generative-recommenders.html"><![CDATA[<p>Traditionally, recommendation systems retrieve and rank.
Generative models can help recommendation systems to retrieve and rank much better.</p>

<p>By now, there is plenty of literature of generative recommenders, and there are companies already productionizing generative recommenders <sup id="fnref:PRS" role="doc-noteref"><a href="#fn:PRS" class="footnote" rel="footnote">1</a></sup>.</p>

<p>To understand literature on generative recommenders, you should be familiar with the design decisions of generative recommenders <sup id="fnref:recsys-papers" role="doc-noteref"><a href="#fn:recsys-papers" class="footnote" rel="footnote">2</a></sup>. I write the key design decisions in this blog post.</p>

<h1 id="traditional-recommendation-systems">Traditional recommendation systems</h1>

<p>Traditionally, recommendation systems have two separate models for retrieval and ranking.</p>

<ul>
  <li>The ranking model predicts a list of P(action), given features.
    <ul>
      <li>Features generally come from three sources
        <ul>
          <li>user features (account age, last interacted content)</li>
          <li>item features (item popularity, item topics of the most recent interacted content)</li>
          <li>interaction features (whether the user “follows” the author)</li>
        </ul>
      </li>
      <li>Features generally fall into two types
        <ul>
          <li>dense features - each dense feature is a float number</li>
          <li>sparse features - each sparse feature is a list of integers</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>The retrieval model “two tower model” produces a pair of embeddings
    <ul>
      <li>a user two tower embedding for a user given user features</li>
      <li>an item two tower embedding given item features</li>
    </ul>
  </li>
  <li>Offline processes
    <ul>
      <li>Ranking model is trained</li>
      <li>Retrieval model is trained</li>
      <li>Item two tower embeddings are calculated with the retrieval model, indexed in a vector database</li>
    </ul>
  </li>
  <li>Online queries
    <ul>
      <li>The user two tower embedding is calculated with the retrieval model</li>
      <li>Items with the most similar embeddings “candidates” are retrieved from the vector database</li>
      <li>The list of P(action) is calculated for each candidate</li>
    </ul>
  </li>
</ul>

<h1 id="a-baseline-implementation-of-a-generative-recommender">A baseline implementation of a generative recommender</h1>

<p>I am describing an implementation of a generative recommender, not the implementation.
I think this implementation is the simplest end-to-end generative recommendation.
We will discuss design decisions of a generative recommender in future sections, with reference to this “baseline implementation”.</p>

<p>All items are represented with a semantic ID triple.</p>
<ul>
  <li>The number of possible semantic IDs is 1000.
  This means the number of possible semantic ID triples is 1 billion.</li>
  <li>There may be multiple items with the same semantic ID triple.</li>
  <li>There may be semantic ID triples that do not have items.</li>
  <li>The semantic ID design decision is out of scope for our discussion here.
  We assume we have an ideal set of semantic IDs.</li>
</ul>

<p>All users are represented by their history of items.</p>
<ul>
  <li>If you skipped item 1 (i1), clicked on item two (i2), the relevant part of the history would look like
  <code class="language-plaintext highlighter-rouge">(i1-s1) (i1-s2) (i1-s3) (skip) (i2-s1) (i2-s2) (i2-s3) (click)</code>
    <ul>
      <li>where <code class="language-plaintext highlighter-rouge">i2-s3</code> refers to the 3rd element of the semantic ID triplet of item 2.</li>
    </ul>
  </li>
  <li>Offline processes
    <ul>
      <li>The generative model is trained to predict every next token in the sequence</li>
      <li>During indexing
        <ul>
          <li>You calculate the semantic IDs of each item</li>
          <li>You store ordered lists of semantic ID triples to items
            <ul>
              <li>You also store ordered lists of semantic ID prefixes to items</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Online processes
    <ul>
      <li>The generative model generates multiple semantic ID triplets to retrieve
        <ul>
          <li>Content with the same semantic ID will be retrieved</li>
        </ul>
      </li>
      <li>The generative model predicts the action token for the item
        <ul>
          <li>The P(skip), P(click), P(downvote)</li>
          <li>There will be a value function that decides how to weigh the action probabilities
            <ul>
              <li>For example P(skip) + 10 * P(click) - 100 * P(downvote)</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h1 id="key-benefits-of-a-generative-recommender">Key benefits of a generative recommender</h1>

<p>One model for inputs</p>
<ul>
  <li>For a user with k interactions, the user is one sequence in the dataset</li>
  <li>The idea that training on a sequence trains on many objectives at the same time (cite Jason Wei blog)</li>
</ul>

<p>One model for outputs</p>
<ul>
  <li>New product surface can reuse the user history and recommender model</li>
  <li>DLRM cannot do this</li>
  <li>Easier maintainability (if done correctly)</li>
  <li>If you want to continuously train the model, you only train one model.</li>
</ul>

<p>One value to rule them all</p>
<ul>
  <li>No more constraint system</li>
</ul>

<p>Infrastructure and tooling for LLMs</p>
<ul>
  <li>There are tools that efficiently serve LLMs out of the box and form great benchmarks</li>
  <li>You can actually see how individual parts of the history affect the final prediction</li>
</ul>

<p>Interpretability</p>
<ul>
  <li>It is much easier to perturb sequence history than features.</li>
</ul>

<h1 id="design-decisions">Design decisions</h1>

<h3 id="item-representation">Item representation</h3>

<p>In the “baseline” implementation, each item is represented with a semantic ID triplet, and nothing else.</p>

<p>Design decisions in the item representation</p>
<ul>
  <li>Information to include
    <ul>
      <li>Pure ID
        <ul>
          <li>Each item has its own ID</li>
          <li>This is not sustainable in large scale recommendation systems</li>
        </ul>
      </li>
      <li>Semantic ID
        <ul>
          <li>There is a decision to be made</li>
          <li>One implementation of Google uses eight (citation needed).</li>
          <li>Eugene Yan’s implementation uses 3 (cite his blogpost).</li>
        </ul>
      </li>
      <li>Include realtime information
        <ul>
          <li>Examples - freshness, popularity</li>
          <li>We could have a token that indicates the popularity of the item</li>
          <li>(Number of likes and upvotes at the time of interaction)</li>
        </ul>
      </li>
      <li>Include author information
        <ul>
          <li>We could have something like an author semantic ID</li>
          <li>We could include the popularity of the author</li>
        </ul>
      </li>
      <li>Include interaction information
        <ul>
          <li>Whether you follow the author at the time of impression</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<p>Putting it together</p>

<p>In the baseline implementation the item</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(i1-s1) (i1-s2) (i1-s3)
</code></pre></div></div>

<p>With the augmentation I described</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(i1-s1) (i1-s2) (i1-s3) (age-s1) (popularity-s1) (author-s1) (followed-s1)
</code></pre></div></div>

<p>Note the order of the augmented information.
I place the augmented information after the semantic ID triplets.
The augmented information is only available after retrieving with the semantic IDs.</p>

<h3 id="user-representation">User representation</h3>
<ul>
  <li>In the “baseline” implementation, each user is represented with an item-action pair sequence, and nothing else</li>
</ul>

<p>Decisions</p>
<ul>
  <li>Information to include
    <ul>
      <li>User context information
        <ul>
          <li>In what context was the user exposed to the item?</li>
          <li>Examples: device type, entry point, time of day</li>
        </ul>
      </li>
      <li>Completeness of information
        <ul>
          <li>Not all actions by the user involve an item</li>
          <li>Example includes going to the setting page to turn off notifications</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Handling excessive user context
    <ul>
      <li>We are likely to have users that have hundreds of thousands of interactions with the platform.</li>
      <li>We need to decide what information to exclude</li>
    </ul>
  </li>
</ul>

<p>In the baseline implementation, the user is represented as a sequence like</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(i1-s1) (i1-s2) (i1-s3) (skip)
(i2-s1) (i2-s2) (i2-s3) (click)
</code></pre></div></div>

<p>With the augmented information, the user is represented with something like</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(visit homepage) (device-id-s1) (referral-id-1)
(i1-s1) (i1-s2) (i1-s3) (skip)
(i2-s1) (i2-s2) (i2-s3) (click)
(follows author-1)
(installs app)
</code></pre></div></div>

<h3 id="retrieval">Retrieval</h3>

<p>In the baseline representation, the model will predict a set of semantic ID triplets.
Content matching the semantic ID triplet is retrieved.
If there is insufficient content, content matching a prefix of the semantic ID triplet is retrieved.</p>

<p>Unlike two tower, there is no embedding retrieval.</p>

<p>Decisions on semantic ID retrieval</p>
<ul>
  <li>You want a set of semantic ID triplets for retrieval</li>
  <li>You can do something like greedy best-first search
    <ul>
      <li>You start with the greedy sequence (each token chosen in the triplet has the highest probability)</li>
      <li>Then for each prefix you greedily choose the highest probability next token that is not already chosen</li>
      <li>You end up with a set of semantic ID triplets with the highest probabilities</li>
    </ul>
  </li>
  <li>There are likely other strategies
    <ul>
      <li>You can validate this by figuring out which strategy maximizes the value function later</li>
    </ul>
  </li>
</ul>

<h3 id="ranking">Ranking</h3>

<p>In the baseline representation, the model will predict the action probabilities</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(i1-s1) (i1-s2) (i1-s3) (skip)
(i2-s1) (i2-s2) (i2-s3) (?)
</code></pre></div></div>

<p>In traditional recommendation systems, you do pointwise ranking.
You rank everything at once.
You apply some business rules such that you do not show the same creator twice.</p>

<p>In a generative recommendation system, you can autoregressively generate the sequence.
The second content to recommend depends on the first content to recommend.
If you really like a creator, the system should not prevent you from showing consecutive content from the same creator.</p>

<h3 id="pretraining">Pretraining</h3>

<p>In the baseline implementation, training is done from scratch.
There is no need for the model to be good at English and semantic ID.</p>

<p>Decisions</p>
<ul>
  <li>Model architecture</li>
  <li>Initialization weights
    <ul>
      <li>Maybe just start from random weights</li>
      <li>Do you really need your model to be good at math or human language?</li>
    </ul>
  </li>
  <li>What is masked
    <ul>
      <li>Do you really need to mask user device for example?</li>
    </ul>
  </li>
  <li>Metrics that you should look at
    <ul>
      <li>Should you just look at validation loss?</li>
    </ul>
  </li>
  <li>How often should you pretrain your model?
    <ul>
      <li>Pretraining is expensive.
        <ul>
          <li>For a model of X million activated parameters, each with an average sequence length of Y, for Z users, even without counting the quadratic attention term, you need at least XYZ floating point operations.
            <ul>
              <li>Note that each entry point of DLRM training is one interaction, whereas each entry point of generative recommender model pretraining is one sequence.</li>
              <li>In one backward pass for a sequence, you are training the model to predict the whole sequence, which usually contains many interactions.</li>
            </ul>
          </li>
        </ul>
      </li>
      <li>You might want to pretrain a model every month.</li>
      <li>You might want to pretrain a model as you add richer sequences
        <ul>
          <li>Maybe your first implementation of the generative recommender is the “baseline” recommender, which is shipped to everyone.</li>
          <li>Then you want to add richer sequence information</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h3 id="posttraining">Posttraining</h3>

<p>You might be doing this every day, or even continuously.</p>

<p>Decisions</p>
<ul>
  <li>Should you serve only one post-trained model?
    <ul>
      <li>Of course in A/B testing you should serve multiple post-trained models</li>
      <li>Should ranking and retrieval use the same post-trained model?</li>
      <li>Should different surfaces use the same post-trained model?</li>
    </ul>
  </li>
  <li>What offline metrics should you look at?</li>
</ul>

<h3 id="analysis">Analysis</h3>

<p>What do you want to analyze when you have your LLM</p>

<p>These are some analysis that you could do</p>

<ul>
  <li>Attention weights
    <ul>
      <li>What tokens were used to predict the action token?</li>
    </ul>
  </li>
  <li>What-if analysis
    <ul>
      <li>If the user had upvoted a content, would that change our prediction of whether the user likes the content</li>
      <li>(assuming that the user also experiences and reacts in exactly the same way after the perturbation)</li>
      <li>(You can also observe how all the logprobs would have independently changed)</li>
      <li>(You can do this analysis in traditional recommender system)</li>
    </ul>
  </li>
</ul>

<h3 id="migration">Migration</h3>

<p>If your product uses the traditional recommendation system, you do not just migrate to the generative recommender in one shot.</p>

<p>You will need to start with the end in mind.</p>

<p>What would the eventual state look like?
You will need to make most of the design decisions early.</p>

<p>You need to justify your headcount and the compute expenditure.
You need to propose milestones that you promise to hit.</p>

<p>Possible milestones</p>
<ul>
  <li>Semantic IDs
    <ul>
      <li>Reasonable clustering</li>
    </ul>
  </li>
  <li>For the ranking model
    <ul>
      <li>Match action AUC in a similar setting as DLRM</li>
      <li>As you increase more data, you can achieve better action AUC</li>
    </ul>
  </li>
  <li>For the retrieval model
    <ul>
      <li>Match recall@K</li>
    </ul>
  </li>
</ul>

<p>What would the end state look like?
You do not want to migrate halfway and you will need to maintain both systems.
Will your company still even be around when you finish your migration?</p>

<h3 id="iteration">Iteration</h3>

<p>Congratulations in advance on your migration to generative models.</p>

<p>This is still not the end, it is always likely there is room for improvement.</p>

<p>You want to design a system that can be easily iterated on.</p>

<p>These are components that could be iterated on</p>
<ul>
  <li>Semantic ID</li>
  <li>Model architecture</li>
  <li>Sequence modelling (item and user representation)</li>
  <li>Post-training</li>
  <li>Value function</li>
</ul>

<p>Let’s say your entire system is built on semantic ID.
Then you find out that half the level one IDs are effectively useless because they map to spammy content.
If you want to replace the semantic ID, you need to replace the entire system.
You need to pretrain a model in parallel, you need to serve models in parallel.
Your A/B test will involve all the surfaces.
If you do not design your system to easily change and test any component, you will be stuck with those components.</p>

<h1 id="conclusion">Conclusion</h1>

<p>I hope this is a comprehensive list of the design decisions when building a generative recommender.</p>

<h1 id="footnotes">Footnotes</h1>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:PRS" role="doc-endnote">
      <p>I attended the <a href="https://prs2026.splashthat.com/">2026 Netflix Workshop on Personalization, Recommendation and Search</a> (PRS), where LinkedIn, Pinterest and Netflix presented their generative recommendation system setup. <a href="#fnref:PRS" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:recsys-papers" role="doc-endnote">
      <p>Similarly, to understand papers in recommendation systems, you also need to understand the standard design of a recommendation system.
You need to understand whether a paper is trying to improve the retrieval part or the ranking part of the recommendation system, or neither.
Otherwise, you will just be very confused. <a href="#fnref:recsys-papers" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Tong Hui Kang</name></author><summary type="html"><![CDATA[Traditionally, recommendation systems retrieve and rank. Generative models can help recommendation systems to retrieve and rank much better.]]></summary></entry><entry><title type="html">I did “autoresearch” in a hackathon and won some GPU credits</title><link href="http://blog.huikang.dev/2026/05/31/autoresearch-hackathon.html" rel="alternate" type="text/html" title="I did “autoresearch” in a hackathon and won some GPU credits" /><published>2026-05-31T00:00:00+00:00</published><updated>2026-05-31T00:00:00+00:00</updated><id>http://blog.huikang.dev/2026/05/31/autoresearch-hackathon</id><content type="html" xml:base="http://blog.huikang.dev/2026/05/31/autoresearch-hackathon.html"><![CDATA[<p>There was an <a href="https://luma.com/fvz1h1dq?tk=3suiEk">Autoresearch</a> Systems Hackathon on May 30, 2026.</p>

<p>I won third place.
The prize was <span>$1,000</span> of Modal credits, $1,000 of ChatGPT credits, and 6 months of ChatGPT Pro.</p>

<p>I was admitted to the hackathon two days before the hackathon - I signed up three days before.</p>

<p>At that time I was already working on setting up my environment for <a href="https://arcprize.org/tasks/ar25">ARC-AGI-3</a>.</p>

<p>This is what I already had</p>
<ul>
  <li>Some neural network that was inspired by AlphaGo</li>
  <li>Ideal gameplay traces</li>
  <li>Submission pipeline</li>
</ul>

<p>I made some preparations before the hackathon</p>
<ul>
  <li>I made some “modified games”. “Modified games” have the same engine logic as the games published by the ARC Foundation, but with slight modifications. For example, for the “original game” <a href="https://arcprize.org/tasks/ar25">ar25</a>, the shapes of the objects were made different.</li>
  <li>I generated the ideal gameplay traces on the modified games.</li>
</ul>

<h4 id="autoresearch-objective">Autoresearch objective</h4>

<p>I only started the autoresearch system halfway through the hackathon.
The purpose of the autoresearch system is to figure out a good neural network model that is able to predict actions.</p>

<p>The model will need to</p>
<ul>
  <li>predict which of the six actions (up, down, left, right, space, click) will be taken</li>
  <li>predict which color (among the colors present in the frame) will be chosen</li>
  <li>predict which coordinate (among the coordinates of the chosen color) will be clicked</li>
</ul>

<p>The loss function is cross entropy.
Color and coordinate logits will be masked if irrelevant.
The loss for color and coordinate will not be computed if irrelevant.</p>

<p>Each action taken by the agent is transformed into a 384 x 64 x 64 matrix for input.
I defined the transformation.</p>

<p>The training data is the ideal gameplay actions on the modified games.
The validation data is the ideal gameplay actions on the original games.</p>

<p>I set up a system for the agent (Claude Code) to experiment with different model architectures that minimize the validation loss.</p>

<h4 id="performance">Performance</h4>

<p>Each training loop takes between 5 and 30 minutes.
The free tier of Modal allows you to use 10 GPUs at once.</p>

<p>This is the “autoresearch” <a href="https://tonghuikang.github.io/arc3/autoresearch/index.html?view=frontier">graph</a>.
The agent started with a model architecture with a validation loss of around 4.5, and eventually found a model architecture with a validation loss of 3.5.</p>

<p><img src="/assets/autoresearch-graph.png" alt="autoresearch-graph" /></p>

<p>For the model that achieved a validation loss of 3.5, the model initialized with a training loss of 12.0 and somewhat <a href="https://tonghuikang.github.io/arc3/autoresearch/index.html?view=model&amp;model=convnext_h128-b16-1780188860">converged</a> to a training loss of 2.0.</p>

<p>However, when playing even the games that it has trained on, I have no evidence that the trained model is performing better than a model that simply takes random actions.</p>

<h4 id="things-i-remembered-i-had-to-intentionally-design">Things I remembered I had to intentionally design</h4>

<p>The only job of the autoresearch agent is to figure out the model architecture.
I want the agent to assume that the data is fixed, and focus on optimizing the model architecture.
I decided that I would be the one to transform the game state into the model input (384 x 64 x 64).</p>

<p>Every model tested should be a standalone file that should not be modified after testing.
Initially I discovered that the agent modified the model architecture code after testing - this is bad because it makes it very difficult to understand exactly which model architecture produced a given result.</p>

<p>Validation loss should only be calculated at the end.
I want the training to be done quickly so that the agent can iterate on model architecture.
Maybe I could instead set up a system to export the current model and calculate the validation loss at every quarter of the data without blocking the training process.</p>

<h4 id="these-are-some-things-i-learnt">These are some things I learnt</h4>

<p>Data is important.
I spent more time on the data than on the autoresearch system, and I still think I need to spend more time on the data.
I still do not have the confidence that I am curve-fitting on a reasonable dataset.
I also suspect that I might be somewhat leaking the targets in the input.</p>

<p>Data shuffling is hard.
You want your data shuffled so that you can trust that the training loss has decreased.
You cannot load all the data in memory and generate the shuffle.
A simple filesystem where you have one file per datapoint would not scale.
This is something I must consider when I work on something similar.</p>

<p>Validation score is a distribution.
There was a model variant that hit 3.5 validation loss, and for a while, other model variants could not beat the score.
When the agent decided to rerun the training for that model variant, the validation loss was 3.9.
This means that scores on the frontier can be noisy.
One remedy is to encourage experiments to be repeated and frontier scores updated.</p>

<p>Validation loss does not really mean anything.
I used the trained model to play the games that it trained on, and its performance is bad.
It could not clear even the first level.
Even if I were to scale the amount of data, I do not think the model can learn to play a quarter of the games it has trained on. (Note that for the ARC-AGI leaderboard, the model will need to solve games it has not seen.)</p>

<p>Code quality is important.
The code I wrote in a short time has a lot of hacks.
For example, I need to decide whether I should treat each “modified game” the same as an “original game” in my system.
I also have to ensure that my system works with multiple external abstractions - ARC SDK, Kaggle notebooks.
I think I will be cleaning up most of the autoresearch code in the main repository.</p>

<p>Learning needs to be intentional.
The autoresearch system treats each training cycle as a black box.
The agent modifies the architecture, runs the training script, and reports the loss.
It does not attempt to understand why one model architecture is better than another.
Optimizing merely for the validation loss may be optimal if you want to produce immediate results, and this is the correct decision in a hackathon.
However, neither the agent nor I am learning anything, and we are very far from solving the main task of training a model to play games that it has seen in training.</p>]]></content><author><name>Tong Hui Kang</name></author><summary type="html"><![CDATA[There was an Autoresearch Systems Hackathon on May 30, 2026.]]></summary></entry><entry><title type="html">All coding models will be interaction models</title><link href="http://blog.huikang.dev/2026/05/15/coding-interaction.html" rel="alternate" type="text/html" title="All coding models will be interaction models" /><published>2026-05-15T00:00:00+00:00</published><updated>2026-05-15T00:00:00+00:00</updated><id>http://blog.huikang.dev/2026/05/15/coding-interaction</id><content type="html" xml:base="http://blog.huikang.dev/2026/05/15/coding-interaction.html"><![CDATA[<p>Thinking Machines released <a href="https://thinkingmachines.ai/blog/interaction-models/">interaction models</a> earlier this week (One year ago, I called them <a href="/2025/05/14/multichannel-prediction.html">multichannel models</a>).</p>

<p>I argue that all frontier coding harnesses will soon be using only interaction models.</p>

<h2 id="what-is-an-interaction-model">What is an interaction model</h2>

<p>Current frontier models (Opus 4.7, GPT-5.5) that we interact with are non-interaction models.
They have one output stream.
They are next-token predictors - they write one token at a time, each token conditioned on everything that came before, and have to finish one thought before starting another.
To do two things, they have to do one and then the other.</p>

<p>An interaction model runs many channels of input and output in parallel, instead of one stream that takes turns.
It can think on one channel while it writes to you on another, while it watches a tool’s output on a third, while it edits a file on a fourth.
The streams are concurrent, not sequential.</p>

<p>On the input side, an interaction model is not waiting for a turn either.
It reads your messages as you type them, terminal output as it streams, file changes as they happen, webhook events as they arrive.
None of these block the others.</p>

<p>The shape comes from how humans work.
A person on a coding task is reading the screen, hearing a coworker, typing, thinking about what to do next, and watching a test suite stream - all at the same time.
An interaction model is the same shape.</p>

<p>Thinking Machines’ <a href="https://thinkingmachines.ai/blog/interaction-models/">post</a> has video demos that make this concrete - <a href="https://www.youtube.com/watch?v=Ys6i_MGnjUA">seamless dialog management</a>, <a href="https://www.youtube.com/watch?v=n2GXGjy41HQ">visual interjection</a>, <a href="https://www.youtube.com/watch?v=2ky5MXBvZP8">simultaneous speech</a>, and <a href="https://www.youtube.com/watch?v=ly3GtaiRFyo">simultaneous tool calls and search</a>.
The demos are voice-and-video rather than coding, but the same multi-stream idea applies.</p>

<h2 id="think-of-how-you-interact-with-ai-coding-tools">Think of how you interact with AI coding tools</h2>

<p>You ask the coding agent to add an “Export to CSV” button to your analytics dashboard.
For each phase of the session, I describe what it is today, and what it should be.</p>

<h4 id="listening"><strong>Listening</strong></h4>

<p><em>What it is.</em>
You ask the model something like “add Export to CSV to the dashboard”.
You can send immediately, or refine your statement in full (which dashboard, what is the time limit<sup id="fnref:banks" role="doc-noteref"><a href="#fn:banks" class="footnote" rel="footnote">1</a></sup>) before sending.
You may be dictating through a speech-to-text tool like Wispr Flow, or macOS dictation.<sup id="fnref:voice" role="doc-noteref"><a href="#fn:voice" class="footnote" rel="footnote">2</a></sup>
Whatever you type, delete, or rephrase before sending is invisible to the model.
The model only sees the final message when you hit send, and starts working from there.</p>

<p><em>What it should be.</em>
You ask the model something like “add Export to CSV to the dashboard”.
When you complete the first phrase, the agent starts to research your code.
As you modify and elaborate more, it steers the research in real time.
By the time you complete your multi-sentence request, the model has already made significant progress in the research.
The agent can already ask useful follow-up questions for your request.</p>

<h4 id="researching"><strong>Researching</strong></h4>

<p><em>What it is.</em>
The agent starts with a general instruction on what to do “add Export to CSV to the dashboard”.
Claude Code could decide to search the codebase in parallel with <a href="https://code.claude.com/docs/en/sub-agents">Explore</a> subagents.
Claude Code writes the instruction up front for the subagent.
One subagent explores the dashboard component.
Another subagent explores the data query.
Another subagent explores the design components.
However, the main agent writes the instruction up front, waits for the subagent to return, and reads only its summary - it does not see what exactly the subagent saw.
Claude Code cannot steer subagents mid-flight.
Information learnt from one subagent cannot influence the research process of another subagent.
This slows the overall research process, and is likely incomplete because subagents do not talk to each other.</p>

<p><em>What it should be.</em>
The agent starts with a general instruction on what to do “add Export to CSV to the dashboard”.
The agent immediately starts multiple channels that investigate the different components.
One channel explores the dashboard component.
You do not waste tokens explaining the situation to the subagent.
Another channel, with the same prefix, explores the data query.
Another channel, with the same prefix, explores the design components.
Information learnt from one channel is immediately shared with another channel.
With each channel informed of how the other channels are doing, the research process is faster and more complete.
For example, if it is discovered that we have similar data export functions for chat history, this information helps to inform the design components to use and the data queries to make.
When the research is done, you also do not waste tokens writing the summary.</p>

<h4 id="aligning"><strong>Aligning</strong></h4>

<p><em>What it is.</em>
There are design decisions involved in a simple button to export a CSV.
Do we give a choice to the user on what to export?
Is exporting instantaneous, or is the user required to check back after an hour or so for their data?
These are questions you need to ask the user.
There is a tradeoff on whether you want to ask the questions early, or whether you want to do your research first before asking the questions.
There is a tradeoff on whether to even ask the question, because Claude Code currently does not work in the background when questions need to be answered.</p>

<p><em>What it should be.</em>
The agent should not need to make these tradeoffs.
The agent could ask questions as early as they can while working on the research in the background.
The agent could retract questions if they have found the answer in the resources (for example, there are data exports that are not instantaneous for data requests of smaller sizes).
All responses to the agent will immediately influence the research.</p>

<h4 id="steering"><strong>Steering</strong></h4>

<p><em>What it is.</em>
The agent is already working - it has drafted the button, wired up the export handler, and is running a first export to see the output.
Halfway through, you notice that the button text is not visible in dark mode.
You type a correction into the chat box.
Your message is queued until the next tool boundary - it feels like the agent is stonewalling you.
You can interrupt the agent to get your queries immediately answered, but this discards the agent’s current progress.</p>

<p><em>What it should be.</em>
You point out that the text is not visible in dark mode.
The model reads your message immediately and acknowledges your comment.
The planning channel updates to include the new constraint.
The implementation channel will pick it up at the next available opportunity.
You do not have to wait for a turn boundary, and you do not feel stonewalled.</p>

<h4 id="approving"><strong>Approving</strong></h4>

<p><em>What it is.</em>
The agent wants to run the export against the production database to validate it on real data, and it needs your approval to do so.
The agent halts and surfaces the approval prompt.
You approve or deny<sup id="fnref:auto-mode" role="doc-noteref"><a href="#fn:auto-mode" class="footnote" rel="footnote">3</a></sup>.
Everything else the agent was doing - drafting the button, type-checking the handler - stops too.
The model is single-stream, so a pending approval blocks all the work.</p>

<p><em>What it should be.</em>
The agent surfaces the approval to run the export against production on a dedicated approval channel.
Only the export channel pauses.
The other channels keep running - the agent continues drafting the button code and refining the export handler while you decide.
If you approve, the paused channel resumes and the export runs.</p>

<h4 id="testing"><strong>Testing</strong></h4>

<p><em>What it is.</em>
Testing follows a linear process.
Your dashboard has hundreds of existing tests that programmatically test each component.
You need to choose a testing setup - do you stop tests on the first failure, or do you continue to run all the tests?
If the agent stops tests on the first failure, the agent will not be aware of the other tests that will fail and the agent will need multiple round trips to fix.
If the agent lets all the tests run, the agent will not be able to fix the first failure as soon as it can.<sup id="fnref:monitor" role="doc-noteref"><a href="#fn:monitor" class="footnote" rel="footnote">4</a></sup></p>

<p><em>What it should be.</em>
The agent starts testing and there is one input channel dedicated to listening for errors.
There is an output channel that surfaces testing issues.
If there is indeed a failure, the channel working on the code will be informed and it will be expected to investigate and fix the failure.
Tests can continue to run so that if there are more test failures, they will be surfaced to the agent.
We get both early fixes to failures, and reduced round trips between fixing and testing.</p>

<h4 id="compacting"><strong>Compacting</strong></h4>

<p><em>What it is.</em>
The inference infrastructure is not wired to generate tokens after a certain context length.
Models are also not trained to generate tokens after a certain context length.
Agents need to compact their context before more tokens can be generated. <sup id="fnref:million-token-context" role="doc-noteref"><a href="#fn:million-token-context" class="footnote" rel="footnote">5</a></sup>
The agent cannot do anything when it is compacting.</p>

<p><em>What it should be.</em>
Compacting should be done in parallel.
As the agent works on the problem, there should be another channel that decides which information is worth storing and which information should be removed from context.
Information removed from context should still be searchable from any channel.</p>

<h4 id="improving"><strong>Improving</strong></h4>

<p><em>What it is.</em>
After you ship your feature, you want to improve your future experience working with the model.
You write and improve skills that help you do your work more efficiently.
For example, when testing the dashboard, the agent should remember to try both light and dark mode and confirm visibility of every text element.
The agent will need to search their history and correctly surface pain points that could have been informed with skills.</p>

<p><em>What it should be.</em>
Reflection happens continuously in a dedicated channel that the agent maintains throughout the session.
When the agent finds out that the button text is not visible in dark mode, the reflection channel should note down the issue in parallel.
When the feature is shipped, the agent will propose to make improvements to AI instructions.</p>

<h2 id="implications">Implications</h2>

<p>If interaction models are coming, here is what I think changes for users, builders, and the model market.</p>

<h4 id="humans-will-prefer-the-better-interface"><strong>Humans will prefer the better interface</strong></h4>

<p>I still prefer Claude Code as my main interface.</p>

<p>For most of the work that I do, it is not possible for me to give perfect instructions in the first turn.
I also operate with imperfect information, and I do not have all the answers.
I need to interact with the agent to understand the problem together.</p>

<p>For me, Claude Code still feels easier to interact with.</p>

<p>I do not really care whether one model is slightly more intelligent than the other.
I care about how easy it is for me to communicate with the agent and get things done.</p>

<p>The companies that ship interaction models first will set the floor for what users expect.
Going back to a single-stream model will feel like going from a chat app to email.</p>

<h4 id="current-interfaces-will-continue-to-be-supported"><strong>Current interfaces will continue to be supported</strong></h4>

<p>Coding agents using interaction models should not require you to turn on your webcam and microphone.</p>

<p>You should still be able to talk to your coding agent with chat, just that it is more responsive and effective.</p>

<p>For users, you should continue to be great at using the current text-interface AI coding tools.</p>

<h4 id="you-will-still-need-to-teach-your-coding-agent"><strong>You will still need to teach your coding agent</strong></h4>

<p>The coding agent does not know about your business.</p>

<p>You will still need to teach your coding agent the environment you are working with.
Even with interaction models, the model still starts every session afresh.</p>

<p>You will still need to manage instructions and resources for the agent to access.
Skills will continue to be written.
Resources will still need to be accessed.</p>

<h4 id="the-model-will-decide-everything"><strong>The model will decide everything</strong></h4>

<p>Currently the harnesses manage plenty of decisions - for example, whether to compact, whether to auto-approve a command, and the context reset after planning mode.</p>

<p>A lot of the harnesses are built with the assumption of a single-stream model - compaction, monitoring, chain-of-thought.
Prompts are written and evaluated.
With this, I think most of the harnesses that we use today will be thrown away.</p>

<p>If I am building yet another coding tool, I will make the harness work only with interaction models.</p>

<h4 id="there-will-be-one-coding-model-size"><strong>There will be one coding model size</strong></h4>

<p>My bet is that the coding model market will collapse to one model size served via API<sup id="fnref:local-models" role="doc-noteref"><a href="#fn:local-models" class="footnote" rel="footnote">6</a></sup>.</p>

<p>Currently Claude Code by default is served with two models - Opus as the main model and Haiku as the explore model.</p>

<p>With interaction models, everything will be one model, which means one model size.</p>

<p>There will be different knobs that the model can decide to turn for itself.<sup id="fnref:knobs" role="doc-noteref"><a href="#fn:knobs" class="footnote" rel="footnote">7</a></sup></p>

<h2 id="closing">Closing</h2>

<p>Coding is the first killer use case for LLMs.</p>

<p>I think coding will also be the first killer use case for interaction models.</p>

<p>All coding models will be interaction models<sup id="fnref:robotics" role="doc-noteref"><a href="#fn:robotics" class="footnote" rel="footnote">8</a></sup>.</p>

<h2 id="footnotes">Footnotes</h2>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:banks" role="doc-endnote">
      <p>For some reason all the banks I use make it difficult for me to export all my transaction history at once. <a href="#fnref:banks" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:voice" role="doc-endnote">
      <p>Coding tools should ship with dictation (the human’s voice in) and text-to-speech (the model’s voice out) built in. 
Today they do not.
I had to add a <a href="https://github.com/tonghuikang/claude-code-template/blob/main/.claude/hooks/notify_kokoro.py">TTS hook</a> to my Claude Code template so the agent can speak its notifications out loud.
I do not have voice dictation software. <a href="#fnref:voice" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:auto-mode" role="doc-endnote">
      <p>I am aware that Claude Code has an auto-mode where the agent has a process to automatically decide which commands are safe to run.
However, I think interaction models are useful here, there could be one channel where the model decides whether to approve running the command. <a href="#fnref:auto-mode" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:monitor" role="doc-endnote">
      <p>Claude Code has this <a href="https://code.claude.com/docs/en/tools-reference#monitor-tool">monitor tool</a> where the agent will monitor something in the background. <a href="#fnref:monitor" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:million-token-context" role="doc-endnote">
      <p>There are models with millions of tokens of context.
In my experience with Opus 4.7, I feel that the model simply forgets a lot of things after the 200,000th token.
I would rather the model automatically compact at the 200,000th token. <a href="#fnref:million-token-context" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:local-models" role="doc-endnote">
      <p>If there are models of different sizes being developed, I think they are local models that need to be run on device. <a href="#fnref:local-models" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:knobs" role="doc-endnote">
      <p>We are familiar with the thinking effort as a knob.
I think models should be able to tune their thinking effort by prompting themselves.
There are other knobs that could be turned if you train the model to do so.
Maybe the size of the prefix that you can attend to is tunable.
Maybe the number of experts that you can use is also tunable. <a href="#fnref:knobs" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:robotics" role="doc-endnote">
      <p>I think the first human-level robotics model will also be an interaction model. <a href="#fnref:robotics" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Tong Hui Kang</name></author><summary type="html"><![CDATA[Thinking Machines released interaction models earlier this week (One year ago, I called them multichannel models).]]></summary></entry><entry><title type="html">Drawing the flash attention animation</title><link href="http://blog.huikang.dev/2026/05/02/flash-attention-animation.html" rel="alternate" type="text/html" title="Drawing the flash attention animation" /><published>2026-05-02T00:00:00+00:00</published><updated>2026-05-02T00:00:00+00:00</updated><id>http://blog.huikang.dev/2026/05/02/flash-attention-animation</id><content type="html" xml:base="http://blog.huikang.dev/2026/05/02/flash-attention-animation.html"><![CDATA[<p>Some time ago, I was studying how Flash Attention works.</p>

<p>The main material available is the <a href="https://github.com/dao-ailab/flash-attention">pyramid visualization</a> from the Flash Attention paper.</p>

<p>I wanted the visualization to be animated. I did not manage to find a good resource out there <sup id="fnref:existing" role="doc-noteref"><a href="#fn:existing" class="footnote" rel="footnote">1</a></sup>. So I made mine.</p>

<p>The <a href="https://tonghuikang.github.io/flash-attention-animation/">animation</a> was made with MacOS Keynote, manually. <sup id="fnref:benchmark" role="doc-noteref"><a href="#fn:benchmark" class="footnote" rel="footnote">2</a></sup>. As of writing, it appears to rank second on <a href="https://www.google.com/search?q=flash+attention+animation">Google search</a>.</p>

<p>I also wrote <a href="https://www.quora.com/How-does-flash-attention-work/answer/Tong-Hui-Kang-1">a Quora answer</a> explaining how Flash Attention works.</p>

<p>I asked in the GPU Mode Discord for opinions on my work.</p>

<p><a href="https://x.com/gaunernst">gau.nernst</a> replied with the following comments, which I greatly appreciate.</p>

<blockquote>
  <p>i wanted to comment that the loop order was reversed. but upon checking, turns out FA1 used this loop ordering, but FA2 reversed it (and I only read the FA2 paper lmao)</p>

  <p>so in FA2, iterating along K/V is the inner loop, iterating along Q/O is the outer loop, which is implemented as 1 threadblock handling 1 Q/O tile</p>

  <p>yea I think FA3 and FA4 also follow the FA2’s general design, but optimized for Hopper and Blackwell respectively</p>
</blockquote>

<p>The explanation may not be complete, the details may not be fully correct, but I still hope this makes it slightly easier for you to understand Flash Attention.</p>

<h3 id="footnotes">Footnotes</h3>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:existing" role="doc-endnote">
      <p>There exists an <a href="https://github.com/Dao-AILab/flash-attention/pull/736">animation</a> made by LuisAVasquez, but I want to stay as close to the source material as possible. <a href="#fnref:existing" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:benchmark" role="doc-endnote">
      <p>I think a good benchmark for AI these days is to reproduce this work. <a href="#fnref:benchmark" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Tong Hui Kang</name></author><summary type="html"><![CDATA[Some time ago, I was studying how Flash Attention works.]]></summary></entry><entry><title type="html">Winning the Nemotron Progress Prize</title><link href="http://blog.huikang.dev/2026/05/02/nemotron-progress-prize.html" rel="alternate" type="text/html" title="Winning the Nemotron Progress Prize" /><published>2026-05-02T00:00:00+00:00</published><updated>2026-05-02T00:00:00+00:00</updated><id>http://blog.huikang.dev/2026/05/02/nemotron-progress-prize</id><content type="html" xml:base="http://blog.huikang.dev/2026/05/02/nemotron-progress-prize.html"><![CDATA[<p>I won the <a href="www.kaggle.com/competitions/nvidia-nemotron-model-reasoning-challenge/overview/prizes">Open Progress Prize</a> for <a href="https://www.kaggle.com/competitions/nvidia-nemotron-model-reasoning-challenge/">NVIDIA Nemotron Model Reasoning Challenge</a></p>

<p>The writeup with the links is available on <a href="https://www.kaggle.com/competitions/nvidia-nemotron-model-reasoning-challenge/discussion/689915">Kaggle</a>.</p>

<p>This is the first time I won prize money from Kaggle competitions.</p>]]></content><author><name>Tong Hui Kang</name></author><summary type="html"><![CDATA[I won the Open Progress Prize for NVIDIA Nemotron Model Reasoning Challenge]]></summary></entry><entry><title type="html">Writing skills for the company</title><link href="http://blog.huikang.dev/2026/02/21/skills.html" rel="alternate" type="text/html" title="Writing skills for the company" /><published>2026-02-21T00:00:00+00:00</published><updated>2026-02-21T00:00:00+00:00</updated><id>http://blog.huikang.dev/2026/02/21/skills</id><content type="html" xml:base="http://blog.huikang.dev/2026/02/21/skills.html"><![CDATA[<p>Agent <a href="https://agentskills.io/home">Skills</a> are instructions that agents can discover and use to do things more accurately and efficiently. The keywords are “accurately” and “efficiently”.</p>

<p>Think of the most capable person that you have ever worked with.</p>

<p>You hire them into a new company.</p>

<p>There are still things this person could have done more “accurately” and “efficiently”.</p>

<p>Think of what they will need to learn between the first day they join the company until the day where they become effective individual contributors. They will need to learn where to look for information. They will need to know who to approach to access privileged information. They will need to learn the processes necessary to ship things. They will need to learn the pitfalls that they should avoid.</p>

<p>Humans have memory and they could remember things.
For AI coding tools however, every time you start a chat, their memory is reset. They know absolutely nothing about your company at the beginning of each session. <sup id="fnref:nothing" role="doc-noteref"><a href="#fn:nothing" class="footnote" rel="footnote">1</a></sup> You need to teach them again.</p>

<p>This teaching process could have been accelerated with skill files.</p>

<p>I want to encourage my colleagues to write and maintain skills to accelerate their work.
Here I describe how I am doing it.</p>

<h2 id="what-exactly-are-skills">What exactly are skills?</h2>

<p>Skills consist of three elements at minimum:</p>
<ul>
  <li>Skill name</li>
  <li>Skill description</li>
  <li>Skill content</li>
</ul>

<p>Let’s use writing a commit message as an example of a skill.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
name: write-commit-message
description: Commit message guidelines. Use when writing git commit messages.
---

&lt;skill content&gt;
</code></pre></div></div>

<p>When the AI coding tool starts a session, it will load the skill name and skill description into the model context.</p>

<p>This is what you see in Claude Code when you run <code class="language-plaintext highlighter-rouge">/context</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Skills · /skills

Project
└ write-commit-message: 21 tokens
</code></pre></div></div>

<p>When the agent needs to write a commit message, it should decide to invoke a skill. When the skill is invoked, the agent will read the skill content.
In this case, the skill content contains the information on how to write commit messages.</p>

<h2 id="why-do-we-need-skills">Why do we need skills?</h2>

<p>Skills are needed to instruct the agent on how to do things more accurately and efficiently.</p>

<p>On writing commit messages, the company likely has some standards on how commit messages should be written.</p>

<p>For example, the commit message should contain this information:</p>
<ul>
  <li>How to write the commit title</li>
  <li>What to include in the commit description (context, design decisions, test plan)</li>
  <li>What not to include in the commit description</li>
  <li>Who the reviewers are</li>
  <li>What URLs need to be included (for example Slack, Asana)</li>
</ul>

<p>If agents want to write commit messages accurately without additional instructions, they will need to figure out these requirements by looking at similar commits, or running the unit tests related to the commit message.</p>

<p>However, if you require the agent to learn the commit message pattern on every session by reading many similar commits, this is not efficient.
If the agent skips the learning process, the agent is not being accurate.
This will still be the case even as models get better, because not every company has the same commit message standards.</p>

<p>Writing commit messages could have been done more accurately and efficiently<sup id="fnref:keywords" role="doc-noteref"><a href="#fn:keywords" class="footnote" rel="footnote">2</a></sup>.
Skill files allow this.
When the agent is going to write commit messages, it will “invoke the skill” and read the skill content.</p>

<p>Initially I placed the commit message standards in CLAUDE.md / AGENTS.md.
This was a reasonable place to put the instruction because it is globally relevant.
I have been advocating for CLAUDE.md to only include <a href="https://blog.huikang.dev/2025/05/31/writing-claude-md.html">globally</a> relevant information.
However, this means the commit message instructions are loaded even when the user is not writing a commit message, for example when asking questions about the codebase.
There is room for improvement here, regarding efficiency.</p>

<p>Then I moved the commit message standards to the git commit template.
Instead of placing the full instructions in CLAUDE.md, I added a pointer to the git commit template.
This is what I have been doing before we had skills.
This still follows the progressive disclosure principle, because I load the commit message standards only when I write the commit message.</p>

<p>Even though placing the commit message standards in the git commit template fulfills the principle of progressive disclosure, there is still benefit in making <code class="language-plaintext highlighter-rouge">write-commit-message</code> a skill.
We want to centralize our AI instructions instead of scattering them over the codebase.
When we implement telemetry and feedback loops for skills, <code class="language-plaintext highlighter-rouge">write-commit-message</code> can also benefit if it is a skill.</p>

<h2 id="when-you-should-write-a-skill">When you should write a skill</h2>

<p>If you want a process to be done more accurately and efficiently with AI coding tools, you should write a skill.
These are some examples where you should think about writing a skill.</p>

<p>You have a resource that you want your agent to access.
The resource could be Notion, Slack, Asana, or any internal pages.
Instead of playing telephone between the AI coding tool and the resource, you can write a skill that teaches the agent how to read the resource.
However, this assumes that your AI coding tool has access to the resources, which you will have to set up first.</p>

<p>You execute repetitive processes that you want automated.
For example, every day I am supposed to check the feed statistics for our recommendation system.
This involves looking at dashboards.
If there are significant movements in the metrics, I need to explain it by looking at commit logs.
This should have been a skill.</p>

<p>You want a process to be done more efficiently in the future.
One such process is on-call pages.
You might already be handling on-call pages with AI coding tools that have access to dashboards and error logs.
In the future, you want to handle this more efficiently.
You can write a skill that informs the agent of the resources that it should look at and the dead ends that it should be aware of.</p>

<p>There are cases where you should not write a skill.</p>

<ul>
  <li>Tasks that the agent could already solve accurately and efficiently.
For example, you should not add a skill on how to search the code, because the agent is likely already searching the code in the most efficient manner.<sup id="fnref:search" role="doc-noteref"><a href="#fn:search" class="footnote" rel="footnote">3</a></sup></li>
  <li>Features that the AI coding tool should already be good at.
There should not be a <code class="language-plaintext highlighter-rouge">plan-mode</code> or <code class="language-plaintext highlighter-rouge">clarify-user-questions</code> skill because AI coding tools should already include this in their system prompt.</li>
  <li>Workflows that should have been a deterministic script.
If you are writing a <code class="language-plaintext highlighter-rouge">check-commit-message</code> skill, you should not be asking the agent to run checks that could be unit tests.
The agent should not be an expensive linter.
If there is still value in writing <code class="language-plaintext highlighter-rouge">check-commit-message</code> to check the qualitative aspects of the commit message, the skill should ask the agent to run the relevant unit tests for the deterministic checks.</li>
</ul>

<h2 id="skill-writing-advice">Skill writing advice</h2>

<p>Start by writing the simplest possible skill that is worth using.</p>

<p>You could look at what you did in the past week and think of:</p>

<ul>
  <li>The documents that you need to repeatedly write or review</li>
  <li>Questions that you need to repeatedly answer</li>
  <li>Investigations that you need to repeat</li>
</ul>

<p>Then, think whether any of these processes could be done more accurately and efficiently with AI coding tools.</p>

<p>If so, you have found a good candidate for a skill.</p>

<p>Then write your skill. Start simple, with only a <code class="language-plaintext highlighter-rouge">SKILL.md</code> file.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
name: &lt;name&gt;
description: &lt;description&gt;
---

&lt;What exactly the skill does&gt;

Example queries
- &lt;example query&gt;

# Workflow

&lt;step by step process&gt;

Checklist
- [ ] Item 1
- [ ] Item 2

# Pitfalls to avoid

&lt;list them&gt;

</code></pre></div></div>

<p>After writing your skill, you should test it.
When you commit your skill, you should include evidence that it is tested.
Unlike unit tests, testing a skill is not deterministic, but you should still provide evidence of testing.</p>

<p>Here are some good forms of evidence:</p>

<ul>
  <li>If the skill output is a document, the resulting document could be evidence.</li>
  <li>If the skill provides instructions on how to read a resource, you could start a new session to see whether the agent could invoke the skill and read the resource without tripping over issues.</li>
  <li>If the skill helps with an investigation, the investigation thread could be evidence.</li>
</ul>

<p>Your colleagues will review your skill, just as code is reviewed in the codebase.</p>

<h2 id="managing-skills-for-the-company">Managing skills for the company</h2>

<p>As hundreds of colleagues commit skills into the codebase, you will soon have hundreds of skills.</p>

<p>This means that you will have hundreds of skill names, and hundreds of skill descriptions.
If each skill is 50 tokens, this will be 5000 tokens.
Also depending on the quality of your skill descriptions, the agent might invoke skills unnecessarily, or fail to invoke skills when it is needed.</p>

<p>If you look at the skills, there are skills that are company-wide and there are skills that are team-wide.
You should only load company-wide skills into context.</p>

<p>This can be done in Claude Code.
For team-wide skills, add <code class="language-plaintext highlighter-rouge">disable-model-invocation: true</code> to prevent the skill from being loaded in context.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
name: investigate-speed-feed
description: 
disable-model-invocation: true
---

&lt;skill content&gt;

</code></pre></div></div>

<p>This will mean that if you go to Claude Code and ask “please investigate feed speed”, the skill will not be invoked.
You need to write <code class="language-plaintext highlighter-rouge">/investigate-speed-feed</code>.
This is fine, because people who need to use the skill should know about the skill.</p>

<p>By separating team-wide skills and company-wide skills, and requiring all team-wide skills to have model invocation disabled, you reduce the risk of the agent not invoking necessary skills or invoking unnecessary skills<sup id="fnref:naming" role="doc-noteref"><a href="#fn:naming" class="footnote" rel="footnote">4</a></sup>.</p>

<p>Organize the team-wide skills and company-wide skills into two folders.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>skills/
     ├── company_wide/
     │   └── write-commit-message/
     │       └── SKILL.md
     └── team_wide/
         └── investigate-speed-feed/
             └── SKILL.md
</code></pre></div></div>

<p>However, the skill standard requires all skills to be at the same level.</p>

<p>Then symlink every skill folder into <code class="language-plaintext highlighter-rouge">skills/all</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>skills/
     ├── all/
     ├── company_wide/
     └── team_wide/
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">.claude/skills</code>, <code class="language-plaintext highlighter-rouge">.cursor/skills</code>, and <code class="language-plaintext highlighter-rouge">.codex/skills</code> are soft symlinks to <code class="language-plaintext highlighter-rouge">skills/all</code>.</p>

<p>To enforce that skills follow the intended format, you should write unit tests to test skills.</p>

<p>For example, you could have unit tests that check that</p>
<ul>
  <li>The skill name is short</li>
  <li>The skill description follows convention <sup id="fnref:description" role="doc-noteref"><a href="#fn:description" class="footnote" rel="footnote">5</a></sup></li>
  <li>Whether the SKILL.md file is under 500 lines</li>
  <li>Required components in the SKILL.md file (I require example queries to appear as its own section within the first 50 lines.)</li>
  <li>Whether the symlinks are added correctly</li>
</ul>

<p>As the skills maintainer, you need to define your responsibilities.
You should not be reviewing every line of every skill.
If there is an issue with a skill, you should direct the complaint to the owner of the skill. You can give advice, but should not be required to.</p>

<p>There are still responsibilities that belong to you as the skills maintainer.
For example, if a skill is not invoked when it is supposed to, or unnecessarily invoked, you need to investigate.
It might be the issue of how the user queries the model, how the skill name and description is written, or it might be the fault of the model or the AI coding tool.
It is still your duty to triage and provide advice here.
Periodically, you also need to review how skills are written and identify anti-patterns and legislate against these anti-patterns.</p>

<p>To help your colleagues write skills, you write a skill that helps them write skills.</p>

<p>Initially I used Anthropic’s <code class="language-plaintext highlighter-rouge">skill-creator</code> <a href="https://github.com/anthropics/skills/tree/main/skills/skill-creator">skill</a> to write skills.
However I found out there are many unnecessary parts.
For example, there is no need for the <code class="language-plaintext highlighter-rouge">init_skill.py</code> steps that create all the resource directories.
Skills should start simple with just a SKILL.md file.</p>

<p>To help your colleagues improve skills, you again write a skill that helps them improve skills.</p>

<p>There will be a <code class="language-plaintext highlighter-rouge">skill-feedback</code> skill where agents can provide feedback on skills.
Feedback should be provided when the agent finds an inaccuracy in the skill file, or pitfalls that the skill has not documented.
The feedback will be stored in some data lake, which should be queried when we iterate on skills.</p>

<p>I hope this helps you write and improve skills for your company, so that the agents can do things more accurately and efficiently.</p>

<h2 id="footnotes">Footnotes</h2>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:nothing" role="doc-endnote">
      <p>This sentence was paraphrased from the resource I recommend on how to write <a href="https://www.humanlayer.dev/blog/writing-a-good-claude-md">CLAUDE.md</a>. <a href="#fnref:nothing" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:keywords" role="doc-endnote">
      <p>If you have not noticed by now, the keywords are accurately and efficiently. <a href="#fnref:keywords" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:search" role="doc-endnote">
      <p>That said, it might still be reasonable to include a skill that helps to search code if the agent could not find the code.
 You might have some code that is hard to search, for example you might be searching for a string but the actual string is broken into two pieces with each string defined at different places.
 However, it should not be expected for the agent to trigger this skill for every search, but only when previous search attempts fail.
 Of course, the better way to solve this is to avoid writing code that requires doing this, or improving your codebase instead. <a href="#fnref:search" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:naming" role="doc-endnote">
      <p>The field name is <code class="language-plaintext highlighter-rouge">disable-model-invocation: true</code>, which unfortunately is a negative.
It could have been “invokable skills” or “non-invokable skills”, but it is confusing because users can invoke a skill with the slash command.
The more precise term is “model-invokable skills” and “non-model-invokable skills” but that is too long.
I am glad that I have arrived at the terms “team-wide skills” and “company-wide skills”. <a href="#fnref:naming" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:description" role="doc-endnote">
      <p>Anthropic recommends the <a href="https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices#naming-conventions">gerund form</a>.
 However Anthropic is not <a href="https://github.com/anthropics/skills/tree/main/skills">really</a> following the conventions they recommend.
 Currently I only require skill name to be in the format <code class="language-plaintext highlighter-rouge">{resource / workflow}-{team name}</code>.
 This is a problem I should worry when the monorepo actually has a hundred skills. <a href="#fnref:description" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Tong Hui Kang</name></author><summary type="html"><![CDATA[Agent Skills are instructions that agents can discover and use to do things more accurately and efficiently. The keywords are “accurately” and “efficiently”.]]></summary></entry><entry><title type="html">Fake tasks for LLMs</title><link href="http://blog.huikang.dev/2026/01/18/fake-tasks-for-LLMs.html" rel="alternate" type="text/html" title="Fake tasks for LLMs" /><published>2026-01-18T00:00:00+00:00</published><updated>2026-01-18T00:00:00+00:00</updated><id>http://blog.huikang.dev/2026/01/18/fake-tasks-for-LLMs</id><content type="html" xml:base="http://blog.huikang.dev/2026/01/18/fake-tasks-for-LLMs.html"><![CDATA[<p>When a frontier LLM fails at an evaluation test case, you should critically evaluate whether your test case is worth passing.</p>

<p>These are some evaluation test cases that I think LLMs should no longer be evaluated on.</p>

<h2 id="tasks-on-recalling-facts-not-critical-to-your-work">Tasks on recalling facts not critical to your work</h2>

<p>There are evals that test whether the LLM remembers certain things (MMLU).</p>

<p>This is the README example in <a href="https://huggingface.co/datasets/cais/mmlu">MMLU dataset</a>.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"question"</span><span class="p">:</span><span class="w"> </span><span class="s2">"What is the embryological origin of the hyoid bone?"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"choices"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"The first pharyngeal arch"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"The first and second pharyngeal arches"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"The second pharyngeal arch"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"The second and third pharyngeal arches"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"answer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"D"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>I argue that this is an unreasonable memorization task<sup id="fnref:bits" role="doc-noteref"><a href="#fn:bits" class="footnote" rel="footnote">1</a></sup>. LLMs do not need to memorize this to be helpful. Neither do doctors need to memorize this to execute their work.</p>

<p>Even as we do not expect LLMs to always provide the perfect answer to this, we still expect AI systems to solve this task perfectly. AI systems are more than just models. AI systems involve LLMs having access to tools such as web search.</p>

<h2 id="tasks-on-recalling-from-a-super-long-context">Tasks on recalling from a super-long context</h2>

<p>There are currently evals that test whether a model can retrieve a specific piece of information buried in a very long context. Model providers boast that their models could memorize the context well into the <a href="https://cloud.google.com/blog/products/ai-machine-learning/the-needle-in-the-haystack-test-and-how-gemini-pro-solves-it">millions</a>.</p>

<p>I argue that this is a non-goal. Humans can solve very long problems without the ability to memorize the entire conversation history. A math professor does not need to recall what they did exactly 300 days ago to solve an open math problem that requires a lot of research and trial and error.</p>

<p>The goal here is to solve complex tasks with the minimum compute. I expect the solution to involve an AI system where the LLM manages its own context and queries previous context with tools. The solution does not necessarily require a model that is able to retrieve something within a million tokens.</p>

<p>This means I still expect AI systems to be perfect at super-long context retrieval. LLMs should be able to use tools to query the history and perfectly retrieve any specific piece of information.</p>

<h2 id="tasks-that-require-a-playbook">Tasks that require a playbook</h2>

<p>Some evaluations test whether an LLM can answer domain-specific questions - legal advice, medical diagnoses, tax regulations.</p>

<p>You cannot expect a lawyer to do well at their job without access to the legal resources. Similarly, you cannot expect the LLM to perform as well as the lawyer without access to the same set of legal resources.</p>

<p>LLMs should have baseline domain knowledge, like a human professional would. We should avoid having evals that simply test the LLMs without providing them access to the necessary legal resources.</p>

<h2 id="tasks-that-are-better-served-with-tools">Tasks that are better served with tools</h2>

<p>Some evaluations test raw computation - multiplying large numbers, performing complex arithmetic, counting characters.</p>

<p>We should not be evaluating LLMs on whether they can do 100-digit multiplication without chain of thought.
LLMs should be able to figure out and follow recipes to do 100 by 100 digit multiplication in O(n²) tokens.</p>

<p>LLMs should be evaluated on whether they can discover and execute algorithms, not whether they can compute magically<sup id="fnref:architecture" role="doc-noteref"><a href="#fn:architecture" class="footnote" rel="footnote">2</a></sup>.</p>

<h2 id="tasks-based-on-noisy-data">Tasks based on noisy data</h2>

<p>We should not use the same mindset we used to participate in traditional Kaggle competitions to train LLMs.</p>

<p>In traditional Kaggle competitions, you do whatever it takes to maximize your score on the leaderboard, even if it means training on incorrect labels in the training set. We should not carry this mindset to evaluating LLMs.</p>

<p>I expect model providers to remove inconsistent or ambiguous test cases when evaluating their models internally.</p>

<h2 id="summary">Summary</h2>

<p>I hope we apply higher scrutiny to the test cases we use to evaluate LLMs.</p>

<p>I also hope that you understand the difference in standards that we apply to LLMs and to AI systems. There are tasks we expect AI systems to solve perfectly, but do not expect LLMs to get correct.</p>

<p>So when an LLM fails a benchmark task, ask yourself: is the model wrong, or is the task wrong?</p>

<h2 id="footnotes">Footnotes</h2>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:bits" role="doc-endnote">
      <p>Research shows that LLMs memorize approximately <a href="https://arxiv.org/abs/2505.24832">3.6 bits</a> of information per parameter. Based on this budget, model providers should probably at least have some internal standards on what LLMs are supposed to know and not supposed to know. <a href="#fnref:bits" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:architecture" role="doc-endnote">
      <p>It is still an interesting exercise to design weights to a given neural network architecture to multiply integers without a thinking process. <a href="#fnref:architecture" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Tong Hui Kang</name></author><summary type="html"><![CDATA[When a frontier LLM fails at an evaluation test case, you should critically evaluate whether your test case is worth passing.]]></summary></entry><entry><title type="html">My comments on the Evals FAQ</title><link href="http://blog.huikang.dev/2026/01/04/comments-on-evals-faq.html" rel="alternate" type="text/html" title="My comments on the Evals FAQ" /><published>2026-01-04T00:00:00+00:00</published><updated>2026-01-04T00:00:00+00:00</updated><id>http://blog.huikang.dev/2026/01/04/comments-on-evals-faq</id><content type="html" xml:base="http://blog.huikang.dev/2026/01/04/comments-on-evals-faq.html"><![CDATA[<p>Hamel’s evals FAQ is a great resource.</p>

<p><a href="https://hamel.dev/blog/posts/evals-faq/">hamel.dev/blog/posts/evals-faq/</a></p>

<p>Many of the ideas I have been promoting<sup id="fnref:prompting" role="doc-noteref"><a href="#fn:prompting" class="footnote" rel="footnote">1</a></sup> align with the Evals FAQ.</p>

<p>The content here does not represent the prevailing opinions or practices of any organization.</p>

<h2 id="sections-that-i-agree-with-and-promote">Sections that I agree with and promote</h2>

<p>These are ideas that I have been promoting.
I should have cited the Evals FAQ when promoting my ideas.</p>

<hr />
<p>On <a href="https://hamel.dev/blog/posts/evals-faq/#q-why-do-you-recommend-binary-passfail-evaluations-instead-of-1-5-ratings-likert-scales">binary</a> (pass/fail) evaluations instead of 1-5 ratings (Likert scales).</p>

<blockquote>
  <p>Having binary options forces people to make a decision rather than hiding uncertainty in middle values. Binary decisions are also faster to make during error analysis - you don’t waste time debating whether something is a 3 or 4.</p>
</blockquote>

<p>For example, you want to classify whether content is adult. You might want to ask for a scale of 1 to 4 instead of a binary classification. On the adult side, you want a label of 1 if the content is <a href="https://en.wikipedia.org/wiki/Child_pornography">CSAM</a> that you have to eliminate from your platform, and a label of 2 if it is merely adult. On the less adult side, you want a label of 3 if it is provocative that you do not want to show to new users, and 4 if the content is perfectly fine. Only users who opted in to adult content will see content with label 2.</p>

<p>I would rather we have three binary classifiers - on whether something is adult (1+2 vs 3+4), on whether something is CSAM (1 vs the rest), and on whether something should be shown to new users (4 vs the rest).</p>

<p>There are some arguments for having one classifier for all 4 labels</p>

<ul>
  <li>You intend to run the LLM for all your content. You think running the LLM only once saves you money, compared to running the LLM three times for three separate classifiers.</li>
  <li>You think that having one system is simpler, as opposed to multiple systems. You think that one system is easier to maintain.</li>
</ul>

<p>I argue that you should think harder about whether having a graduated 1-4 scale is really a better system than having three binary classifiers.</p>

<ul>
  <li>You need to think about what happens when you want to change one of the systems. Let’s say you want to have a different standard on what is allowed to be displayed to new users (label 3 versus label 4). You need to ensure that your single LLM classifier still works for label 2 and label 1. Experimentation is also more complicated now.</li>
  <li>There might be different levels of scrutiny for different classifications - you are okay if label 3 versus label 4 is mixed up, but you do not want to mix up label 1 versus label 2 because accounts posting label 1 content will get banned.</li>
  <li>Aligning a binary classifier is much easier than aligning a graduated classifier. It should be easier and faster to align three binary classifiers independently than to align one 4-class classifier. It is also easier to find a cheaper and equally performant classifier if the task is just binary classification.</li>
</ul>

<p>If your systems are uncoupled, you just need to align the parts that you need to align. Sure, it might cost more to run the LLMs three times, but engineering time usually costs even more money.</p>

<hr />
<p>On <a href="https://hamel.dev/blog/posts/evals-faq/#q-how-should-i-version-and-manage-prompts">versioning</a> and managing prompts</p>

<blockquote>
  <p>There is an unavoidable tension between keeping prompts close to the code vs. an environment that non-technical stakeholders can access.</p>

  <p><strong>My preferred approach is storing prompts in Git.</strong> This treats them as software artifacts that are versioned, reviewed, and deployed atomically with the application code.</p>

  <p>Prompt management tools are inherently limiting because they can’t easily execute your application’s code. Even when they can, there’s often significant indirection involved, making it difficult to test prompts with your system’s capabilities.</p>
</blockquote>

<p>There is indeed a requirement for non-technical stakeholders to read and write prompts. Prompts should already be easily readable if your organization has good AI tooling to point you to the exact prompt.</p>

<p>There is also the case where non-technical stakeholders want to write and test the prompts<sup id="fnref:system" role="doc-noteref"><a href="#fn:system" class="footnote" rel="footnote">2</a></sup> without opening a terminal or Jupyter notebook. It should be possible to keep the prompts in git while they experiment with the different prompts.</p>

<hr />

<p>On how many people should be <a href="https://hamel.dev/blog/posts/evals-faq/#q-how-many-people-should-annotate-my-llm-outputs">annotating</a> LLM outputs</p>

<blockquote>
  <p>A single expert eliminates annotation conflicts and prevents the paralysis that comes from “too many cooks in the kitchen”. The benevolent dictator can incorporate input and feedback from others, but they drive the process.</p>
</blockquote>

<p>I have written exactly on this <a href="https://blog.huikang.dev/2024/12/31/prompting-projects.html">before</a>.</p>

<blockquote>
  <p>I argue that democracy is a horrible way to build a dataset for LLM evaluation. Let’s say you want to build dataset to determine whether an advertisement is low quality. You get your team to label the content and treat the labels as immutable ground truth. But you notice the labels provided by your team often disagrees with each other.</p>

  <p>If you pass this dataset to your colleague, your colleague is simply guessing what people are voting. Your colleague will likely not perform well. This is what you will be tuning your prompt to.</p>
</blockquote>

<p>The FAQ suggests a benevolent dictator to own the prompts, which I agree with.</p>

<h2 id="sections-that-i-would-add-or-edit">Sections that I would add or edit</h2>

<p>Evals should only focus on cases you care about. In other words, you do not need a ground truth label for everything.</p>

<p>For example, in adult content classification, you only want to label what is <a href="https://en.wikipedia.org/wiki/I_know_it_when_I_see_it">obviously</a> adult and what is obviously not adult. If something is neither obviously adult nor obviously not adult, there is no need for a ground truth label. This means, in production, you are okay with the content being classified either way.</p>

<hr />

<p>I think some updates have to be made considering models are much more powerful now.</p>

<ul>
  <li>I do not think we should spend effort ensuring that every step the model takes is correct. For example, I do not think we need to care whether every search term made by the model is ideal. We know that frontier models serve as reliable agents that complete tasks. They know when they are making mistakes. As model users, we should not need to scrutinize every step the model is making; we should be more concerned with the outcome.</li>
  <li>I think models could play a bigger role in prompting. One new use case for LLMs in building the evaluation dataset is to brainstorm the edge cases that we care about. LLMs could even agentically query your data for mistakes the binary classifier is making in production, and put these labels up for human approval.</li>
</ul>

<h2 id="sections-that-i-disagree-with">Sections that I disagree with</h2>

<p>On passing 100% of your evals</p>

<blockquote>
  <p>Be <a href="https://ai-execs.com/2_intro.html#a-case-study-in-misleading-ai-advice">wary of optimizing for high eval pass rates</a>. If you’re passing 100% of your evals, you’re likely not challenging your system enough. A 70% pass rate might indicate a more meaningful evaluation that’s actually stress-testing your application. Focus on evals that help you catch real issues, not ones that make your metrics look good.</p>
</blockquote>

<p>I do not agree that passing 100% of your evals is wrong in itself. Of course, you still need to justify the value of evals that you already score 100% (or any passing rate as well).</p>

<p>When there is a classification failure, either one or multiple parts must be wrong - the classifier (model + prompt), or the label (or the LLM-as-a-judge evaluator). You should find out which one it is. It is possible that the label is wrong<sup id="fnref:borderline" role="doc-noteref"><a href="#fn:borderline" class="footnote" rel="footnote">3</a></sup>. If the prompt is wrong, you can probably tweak the prompt. If the model is wrong, you likely cannot do anything. My point here is, when there is a mistake, something must be wrong.</p>

<p>Mistakes are mistakes. It is possible to fix all these mistakes to get your eval to 100%. Models are better these days, and now you have AI to help you write and improve prompts. It is possible that you are not able to find any more mistakes with reasonable effort.</p>

<p>There could still be value for evals that pass 100%. For example, you have a classifier that classifies whether something is adult. The requirements are quite loose. You only need to classify correctly if something is obviously adult or obviously not adult. There are a lot of borderline cases where you are okay with the system tagging either way. Models today can perform 100% at this task. There is still value in this eval even though it passes 100%.</p>

<p>You can deploy this classifier and monitor mistakes in production. The classifier is likely to make mistakes (something obviously adult being classified as not adult), and you add the mistakes to the evaluation. Then you can tune the classifier to achieve performance on the mistakes and the initial dataset. The eval that passes 100% is still useful.</p>

<p>You can use the same eval when you migrate models. Models get deprecated, or you found a much cheaper model that is equally performant. The eval that scores 100% still serves as a unit test that is only run once every model migration.</p>

<p>What you should not do is try to add borderline examples to the dataset to make the eval perform at 70%. Similar to how “the goal of evaluations isn’t to pat yourself on the back for a perfect score”, it should also be the case that “the goal of evaluations isn’t to pat yourself on the back for an imperfect score”.</p>

<h2 id="footnotes">Footnotes</h2>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:prompting" role="doc-endnote">
      <p>I have previously written about prompting projects <a href="https://blog.huikang.dev/2024/12/31/prompting-projects.html">here</a>. <a href="#fnref:prompting" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:system" role="doc-endnote">
      <p>My view of my role as a prompt engineer is not to write the prompt, but to correctly build the system for other people to write the prompts. <a href="#fnref:system" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:borderline" role="doc-endnote">
      <p>Again, the case might be borderline and we should probably allow the system to make either classification and not compute loss. <a href="#fnref:borderline" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Tong Hui Kang</name></author><summary type="html"><![CDATA[Hamel’s evals FAQ is a great resource.]]></summary></entry><entry><title type="html">Predictions for 2026</title><link href="http://blog.huikang.dev/2026/01/01/predictions-for-2026.html" rel="alternate" type="text/html" title="Predictions for 2026" /><published>2026-01-01T00:00:00+00:00</published><updated>2026-01-01T00:00:00+00:00</updated><id>http://blog.huikang.dev/2026/01/01/predictions-for-2026</id><content type="html" xml:base="http://blog.huikang.dev/2026/01/01/predictions-for-2026.html"><![CDATA[<p>These are my predictions for 2026.<sup id="fnref:2025" role="doc-noteref"><a href="#fn:2025" class="footnote" rel="footnote">1</a></sup></p>

<p>There will continue to be more people working on AI models and products.
There will continue to be more compute.
Access to compute will be better.
You will also have competent AI that helps you improve AI.</p>

<h2 id="ai-will-be-human-level-at-manipulating-browsers">AI will be human-level at manipulating browsers</h2>

<p>The only AI-powered browsing experience that I have tried is the Claude extension on Chrome.</p>

<p>There are a few read-only tasks I want it to do:</p>
<ul>
  <li>Go to the date grid on Google Flights so I can see when the cheapest flights are</li>
  <li>Tabulate my rent options based on listings on Craigslist</li>
  <li>Estimate the value of my (mostly IKEA) furniture from an image of my room</li>
</ul>

<p>The experience is very slow and very inaccurate<sup id="fnref:puppeteer" role="doc-noteref"><a href="#fn:puppeteer" class="footnote" rel="footnote">2</a></sup>.</p>

<p>I see a few problems here:</p>
<ul>
  <li>Every action has to be preceded by a thought process</li>
  <li>Context is filled up quickly and compaction is slow, because the model is trying to keep every screenshot</li>
  <li>The model is bad at managing multiple tabs and parallelizing work</li>
  <li>The model is kind of blind and only looks once<sup id="fnref:blind" role="doc-noteref"><a href="#fn:blind" class="footnote" rel="footnote">3</a></sup></li>
</ul>

<p>I hope to be able to trust AI with all my read-only browser tasks.
After AI earns my trust with their performance on read-only tasks, I can slowly trust AI with tasks that make changes.</p>

<h2 id="context-constraints-will-be-invisible">Context constraints will be invisible</h2>

<p>For the user experience, I think context constraints should already be invisible to users of frontier AI products.
You should not be hit with an instruction on ChatGPT that your conversation is too long.
There are some processes in the background that compact the context.<sup id="fnref:decaching" role="doc-noteref"><a href="#fn:decaching" class="footnote" rel="footnote">4</a></sup></p>

<p>For the developer experience though, you need to be aware of the context limits.
In 2025, the phrase <a href="https://www.quora.com/What-do-you-think-of-context-engineering/answer/Tong-Hui-Kang-1">context engineering</a> was coined.</p>

<p>However, I think developers will no longer need to care about context length.
The model API will be shipped with context management,<sup id="fnref:responses" role="doc-noteref"><a href="#fn:responses" class="footnote" rel="footnote">5</a></sup> similar to how you talk to your friend - you do not need to ask your friend to delete old messages so that you can continue talking to them.</p>

<p>This developer experience should already be achievable with existing models and a suite of scaffolds.
There might also be improvements in the model architecture to help.<sup id="fnref:decaching:1" role="doc-noteref"><a href="#fn:decaching" class="footnote" rel="footnote">4</a></sup></p>

<h2 id="we-will-stop-taking-turns-with-ai">We will stop taking turns with AI</h2>

<p>We are familiar with the chat interface with AI.
You type something, hit enter, and AI replies with something.
You are taking turns with AI.</p>

<p>The experience talking to another human is different.
You take note of their facial expression and body language.
You look up certain information while they are talking.
You write notes to yourself.
There are no explicit turns to take.</p>

<p>There are some efforts to break this turn-based experience with AI:</p>
<ul>
  <li>You can interrupt while AI is replying.</li>
  <li>You may submit messages while AI is replying.
However, how early your replies steer the response differs between products.</li>
</ul>

<p>There are still some bottlenecks:</p>
<ul>
  <li>Ideally the response to my question should be ready by the time I hit send.<sup id="fnref:helpdesk" role="doc-noteref"><a href="#fn:helpdesk" class="footnote" rel="footnote">6</a></sup></li>
  <li>The AI may need to ask follow-up questions to clarify my search request.
But the AI should already start searching in parallel.</li>
</ul>

<p>It is possible to achieve all this with a suite of scaffolds.
There might also be improvements in the model architecture to help.<sup id="fnref:multichannel" role="doc-noteref"><a href="#fn:multichannel" class="footnote" rel="footnote">7</a></sup></p>

<h2 id="ai-will-write-their-own-instructions">AI will write their own instructions</h2>

<p>Currently we expect models, with their harness, to complete any task with the resources available to them.
They are expected to use existing instructions.<sup id="fnref:instructions" role="doc-noteref"><a href="#fn:instructions" class="footnote" rel="footnote">8</a></sup>
Models are already expected to check their own work before reporting success.</p>

<p>Soon we should expect AI to not just follow processes, but also to improve the processes.</p>

<p>Currently, the human needs to take the initiative to prompt the model to fix the instructions.
The instructions may include comments and docstrings in the code that inform future models.
The instructions may be <a href="https://agentskills.io/home">skills files</a> that provide guides on how to execute certain processes.</p>

<p>We will see AI products taking the initiative to suggest changes to the process.
We will see models that tastefully fix outdated and inaccurate comments in the codebase.
We will see skills written, and maintained as they are being used.</p>

<h2 id="footnotes">Footnotes</h2>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:2025" role="doc-endnote">
      <p>See my <a href="/2024/12/29/competitive-programming-and-superintelligence.html">predictions</a> <a href="/2024/12/30/prompting-in-2025.html">for</a> <a href="/2025/01/02/mathematical-superintelligence.html">2025</a> for reference on how inaccurate they were. <a href="#fnref:2025" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:puppeteer" role="doc-endnote">
      <p>I think I can get better results with Claude Code on puppeteer MCP with subagents.
Regarding puppeteer MCP - even though <a href="https://github.com/modelcontextprotocol/servers/tree/main/src/puppeteer"><code class="language-plaintext highlighter-rouge">@modelcontextprotocol/server-puppeteer</code></a> is deprecated, I am not recommending <a href="https://github.com/microsoft/playwright-mcp"><code class="language-plaintext highlighter-rouge">@playwright/mcp</code></a> because it takes up more tokens (4k vs 14k) and “No vision models needed, operates purely on structured data” is outdated. <a href="#fnref:puppeteer" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:blind" role="doc-endnote">
      <p>AI is kind of blind - you can see that the bottom set of 30 x 30 matrices are <a href="https://www.quora.com/Is-ARC-wrong-and-flawed-and-not-an-AGI-test-at-all/answer/Tong-Hui-Kang-1">misaligned</a>, which should not pass any design review.
I think o3 <a href="https://openai.com/index/thinking-with-images/">pioneered a method</a> where the model zooms in to a specific section.
I look forward to models drawing lines on an image to check whether they aligned the CSS correctly. <a href="#fnref:blind" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:decaching" role="doc-endnote">
      <p>One way to scaffold this is to have a parallel summarization process that kicks in at 50k tokens to summarize into 10k tokens, and at 80k tokens, replaces the 50k tokens with the 10k token summary so the conversation continues from 40k tokens.
I have an idea where the model discards the KV-cache of parts of the conversation that are no longer relevant, and uses tools to search the conversation history instead. This removes the need to generate 10k tokens every 50k tokens. One prime candidate for decaching is the screenshots when manipulating browsers. <a href="#fnref:decaching" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:decaching:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:responses" role="doc-endnote">
      <p>OpenAI <a href="https://developers.openai.com/blog/responses-api/">released</a> the <a href="https://platform.openai.com/docs/guides/conversation-state">Responses API</a> in 2025 which tracks conversation state server-side.
You do not need to pass in the entire conversation history with each request.
Instead, you pass around an id representing the state of the conversation, and OpenAI keeps it up-to-date for you.
You still need to manually call <code class="language-plaintext highlighter-rouge">/responses/compact</code> to compact the context. <a href="#fnref:responses" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:helpdesk" role="doc-endnote">
      <p>For some human-powered helpdesk chatbots, apparently the human operator can <a href="https://gizmodo.com/be-warned-customer-service-agents-can-see-what-youre-t-1830688119">see what you typed before you send</a>. <a href="#fnref:helpdesk" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:multichannel" role="doc-endnote">
      <p>I wrote about how <a href="/2025/05/14/multichannel-prediction.html">models should be multichannel</a> - humans have multiple input and output channels, and models should be able to converse like humans. <a href="#fnref:multichannel" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:instructions" role="doc-endnote">
      <p>I have written about how <a href="/2025/10/20/delivering-ai-instructions.html">instructions are more than just user and system prompts</a>. <a href="#fnref:instructions" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Tong Hui Kang</name></author><summary type="html"><![CDATA[These are my predictions for 2026.1 See my predictions for 2025 for reference on how inaccurate they were. &#8617;]]></summary></entry><entry><title type="html">Things that I should do on a plane</title><link href="http://blog.huikang.dev/2025/12/29/things-to-do-on-a-plane.html" rel="alternate" type="text/html" title="Things that I should do on a plane" /><published>2025-12-29T00:00:00+00:00</published><updated>2025-12-29T00:00:00+00:00</updated><id>http://blog.huikang.dev/2025/12/29/things-to-do-on-a-plane</id><content type="html" xml:base="http://blog.huikang.dev/2025/12/29/things-to-do-on-a-plane.html"><![CDATA[<p>There are some things that I wanted to do, but I do not think it is worth doing unless I am stuck on a plane.</p>

<p><strong>Crafting LLM weights to multiply integers without thinking.</strong></p>

<p>It is already expected that frontier models have perfect performance at multiplication without tool use. 
Frontier models should be able multiply step-by-step, only limited by time and space complexity.</p>

<p>However, I am interested in multiplication without a thought process, the model is expected to reply with the product directly.</p>

<p>I want to try crafting weights to perform multiplication perfectly. I will also need to study the multiplication algorithm itself.</p>

<p>There is this related work on <a href="https://arxiv.org/abs/2301.05217">training</a> a model to perform modular addition perfectly.</p>

<p><strong>Understanding <a href="https://cp-algorithms.com/data_structures/segment_tree.html">segment trees</a>.</strong></p>

<p>Segment trees are an important concept if you want to get a higher rating in competitive programming.
I have a <a href="https://github.com/tonghuikang/codecomp/blob/3f93809d7577a544e05eb56754370c9a68f6fc4c/template/template_segment_trees.py">template</a> that I do not know how to use.
I think I also need to understand the algorithm and the intuition behind segment trees.</p>

<p><strong>Understanding <a href="https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm">Aho–Corasick algorithm</a>.</strong></p>

<p>LeetCode contests tested this two times.</p>
<ul>
  <li><a href="https://leetcode.com/problems/construct-string-with-minimum-cost/">Construct String with Minimum Cost</a> in <a href="https://leetcode.com/contest/weekly-contest-405/">Weekly Contest 405</a></li>
  <li><a href="https://leetcode.com/problems/minimum-number-of-valid-strings-to-form-target-ii/">Minimum Number of Valid Strings to Form Target II</a> in <a href="https://leetcode.com/contest/weekly-contest-415/">Weekly Contest 415</a></li>
</ul>

<p>I solved both problems with other methods.</p>

<p><strong>Tree matching problem with wildcards</strong></p>

<p>You have two rooted trees.
Some nodes are wildcards.</p>

<p>The task is to determine whether you can assign each wildcard a subtree so that you can match the two trees.</p>

<p>Please do not ask me where I got this problem.</p>

<p><strong>Memorize the entire architecture of <a href="https://huggingface.co/openai/gpt-oss-120b">gpt-oss</a>.</strong></p>

<p>gpt-oss-120b is the go-to model for <a href="https://www.kaggle.com/competitions/ai-mathematical-olympiad-progress-prize-3">AIMO 3</a>. I want to understand the model architecture very well.
I need to be able to derive the parameter count, and also its GPU memory usage.
I will also need to compare and explain the differences between 20b and 120b versions.</p>

<p><strong>Watch some films</strong></p>

<p>These are the films I have yet to watch</p>
<ul>
  <li>Zootopia 2</li>
  <li>Demon Slayer: Kimetsu no Yaiba Infinity Castle</li>
  <li>Chainsaw Man - The Movie: Reze Arc (only watched the first half)</li>
  <li>Ne Zha 2</li>
  <li>KPop Demon Hunters</li>
  <li>Neon Genesis Evangelion</li>
</ul>]]></content><author><name>Tong Hui Kang</name></author><summary type="html"><![CDATA[There are some things that I wanted to do, but I do not think it is worth doing unless I am stuck on a plane.]]></summary></entry></feed>