<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: WonderLab</title>
    <description>The latest articles on DEV Community by WonderLab (@wonderlab).</description>
    <link>https://dev.to/wonderlab</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3797373%2F25beba30-d8d4-4d2e-9ec6-170356089350.jpg</url>
      <title>DEV Community: WonderLab</title>
      <link>https://dev.to/wonderlab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wonderlab"/>
    <language>en</language>
    <item>
      <title>Is Loop Engineering a New Technology, or Just Reheated Leftovers? Explained Through Training a Dog Classifier</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Wed, 11 Nov 2026 07:35:09 +0000</pubDate>
      <link>https://dev.to/wonderlab/is-loop-engineering-a-new-technology-or-just-reheated-leftovers-explained-through-training-a-dog-1gal</link>
      <guid>https://dev.to/wonderlab/is-loop-engineering-a-new-technology-or-just-reheated-leftovers-explained-through-training-a-dog-1gal</guid>
      <description>&lt;h2&gt;
  
  
  The Short Answer
&lt;/h2&gt;

&lt;p&gt;The term "loop engineering" is everywhere right now, and it sounds like a new craft. My view: &lt;strong&gt;the idea is leftovers, but this is the first time the leftovers are actually edible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The leftover part: let a system iterate on its own, use a set of criteria to judge how each round went, adjust from the feedback, and repeat until the goal is met. That is the most basic machine learning training loop, and also the feedback loop from control theory.&lt;/p&gt;

&lt;p&gt;The new part: in the past, what got adjusted inside the loop had to be numbers (model parameters). Now it can be a piece of text, a file, or a knowledge base, and the feedback can be a plain sentence. That is why the pattern can, for the first time, be applied to open-ended tasks.&lt;/p&gt;

&lt;p&gt;Below I start from the most basic machine learning training, then use three real agent tasks to take loop engineering apart.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Start Somewhere Familiar: Training a Dog Classifier
&lt;/h2&gt;

&lt;p&gt;Say you want to train a model that tells whether a picture contains a dog. The whole process has just five parts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;In the dog model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Goal&lt;/td&gt;
&lt;td&gt;95% recognition accuracy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What can be adjusted&lt;/td&gt;
&lt;td&gt;The model's parameters (millions of numbers)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evaluation&lt;/td&gt;
&lt;td&gt;The loss function: compares predictions with the correct answers and computes "how wrong"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feedback&lt;/td&gt;
&lt;td&gt;The gradient: tells each parameter which direction to move and by how much&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stop condition&lt;/td&gt;
&lt;td&gt;Accuracy reached, N epochs done, or several rounds with no progress&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Then there is a loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while not done:
    prediction = model(image)                  # do a round
    loss = loss_fn(prediction, correct_answer) # evaluate the round
    gradient = backprop(loss)                  # what is wrong, how to adjust
    params = params - lr * gradient            # adjust
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the division of labor: &lt;strong&gt;the model does not know by itself whether it is right.&lt;/strong&gt; The loss function outside it does the scoring. The model only does the work, and the direction of adjustment comes from the evaluation system.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Put the Same Skeleton on an Agent
&lt;/h2&gt;

&lt;p&gt;Now let an agent do the work. Not a single part is missing; only the carrier changes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Training the dog model&lt;/th&gt;
&lt;th&gt;Loop engineering&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Goal&lt;/td&gt;
&lt;td&gt;95% accuracy&lt;/td&gt;
&lt;td&gt;Type check and tests all pass / 90% accuracy on an eval set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A round&lt;/td&gt;
&lt;td&gt;Model makes predictions&lt;/td&gt;
&lt;td&gt;Agent completes part of the task, producing code, answers, or a report&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evaluation&lt;/td&gt;
&lt;td&gt;Loss function&lt;/td&gt;
&lt;td&gt;Compiler, tests, eval set, check scripts, a rubric, another model as judge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feedback&lt;/td&gt;
&lt;td&gt;Gradient (numbers)&lt;/td&gt;
&lt;td&gt;Error messages, failing cases, explanations of lost points (text)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What can be adjusted&lt;/td&gt;
&lt;td&gt;Model parameters&lt;/td&gt;
&lt;td&gt;Code in the workspace, prompts, an attached knowledge base, a task list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stop condition&lt;/td&gt;
&lt;td&gt;Goal reached / epoch cap / early stopping&lt;/td&gt;
&lt;td&gt;Passed / iteration cap / budget cap / several rounds without progress&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As a diagram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        ┌──────────── Goal ────────────┐
        ▼                              │
   Agent does a round ──▶ Evaluation ──▶ Done? ──yes──▶ End
        ▲                                │no
        │                                ▼
        └── Adjust from feedback ◀── What is good, what is not
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick look at where things stand: today's agents are quite capable in a single conversation, and simple tasks come out right the first time. What loop engineering actually solves is a different class of problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The task is too big.&lt;/strong&gt; One conversation's context cannot hold it, and things get messier toward the end&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The task is too open-ended.&lt;/strong&gt; A single pass gives unstable quality: three out of ten attempts go wrong&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nobody is watching.&lt;/strong&gt; When it goes wrong, no one notices, and the error is passed along&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The three cases below correspond to these three problems.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Case One: Migrating Hundreds of Files from JavaScript to TypeScript
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task and Why It Is Hard
&lt;/h3&gt;

&lt;p&gt;A team has a front-end project with 300-plus &lt;code&gt;.js&lt;/code&gt; files. All of them must move to TypeScript, and afterward the project has to compile and every test has to pass.&lt;/p&gt;

&lt;p&gt;Hand the whole thing to an agent in one go, and typical results are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first forty or fifty files look good, then it starts cutting corners with &lt;code&gt;any&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The context grows, it forgets types it defined earlier, and defines them again&lt;/li&gt;
&lt;li&gt;Halfway through it says "done" while dozens of files are untouched&lt;/li&gt;
&lt;li&gt;At the end hundreds of compile errors appear at once, and nobody knows where to start&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not the agent being dumb. The size of the task exceeds what a single conversation can carry.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Build the Loop
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Break the big task into a list, and keep the list in a file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TODO.md
[x] src/utils/date.js
[x] src/utils/format.js
[ ] src/api/user.js
[ ] src/api/order.js
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list is the loop's "memory." It survives every round and does not depend on conversation context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Each round does one small step, from a fresh context&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The instruction for each round is simple: read the list, pick one unfinished file, migrate it, run the checks, tick it off and commit if they pass, then end. The next round is a brand-new conversation with none of the accumulated clutter, only the list and the codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Evaluation is a compiler and tests the agent cannot change&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evaluation commands:
  tsc --noEmit            # type check; no new suppressions beyond what exists
  npm test                # all tests
  check script: count occurrences of @ts-ignore and `as any`; must not increase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Feedback: pass the compiler's errors to the next round as they are&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Compiler output such as &lt;code&gt;error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'&lt;/code&gt; is a precise diagnosis, more useful than any summary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Stop condition&lt;/strong&gt;: every item ticked and checks passing; or at most N rounds; or two rounds in a row where the failure count does not drop (it is stuck and needs a human).&lt;/p&gt;

&lt;p&gt;Here is the smallest skeleton of that loop. It does three things: call the agent, call the evaluation command, and decide whether to stop. I ran its control flow with stub commands (both the "goal reached" exit and the "stuck" exit), but I did not connect a real agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# Skeleton: do a round -&amp;gt; external evaluation -&amp;gt; leave feedback for next round -&amp;gt; stop check&lt;/span&gt;
&lt;span class="c"&gt;# AGENT_CMD: have the agent do one round (fresh context each time)&lt;/span&gt;
&lt;span class="c"&gt;# EVAL_CMD : evaluation command; exit code 0 means the goal is met; the agent cannot modify it&lt;/span&gt;
&lt;span class="nv"&gt;MAX_ITERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;${MAX_ITERS:-10}&lt;/span&gt;
&lt;span class="nv"&gt;PATIENCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;${PATIENCE:-2}&lt;/span&gt;
&lt;span class="nv"&gt;best_fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;999999&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;stale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;span class="k"&gt;for&lt;/span&gt; i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$(&lt;/span&gt;seq 1 &lt;span class="s2"&gt;"$MAX_ITERS"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"== Round $i =="&lt;/span&gt;
  bash -c &lt;span class="s2"&gt;"$AGENT_CMD"&lt;/span&gt;                      &lt;span class="c"&gt;# 1. The agent does a round&lt;/span&gt;
  bash -c &lt;span class="s2"&gt;"$EVAL_CMD"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; eval.log 2&lt;span class="o"&gt;&amp;gt;&amp;amp;&lt;/span&gt;1       &lt;span class="c"&gt;# 2. External evaluation&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; -eq 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Goal met, stopping"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; 0&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="nv"&gt;fails&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;grep -c &lt;span class="s2"&gt;"FAIL"&lt;/span&gt; eval.log&lt;span class="o"&gt;)&lt;/span&gt;          &lt;span class="c"&gt;# 3. Track progress by failure count&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"$fails"&lt;/span&gt; -lt &lt;span class="s2"&gt;"$best_fail"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="nv"&gt;best_fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$fails&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;stale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nv"&gt;stale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;stale+1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"$stale"&lt;/span&gt; -ge &lt;span class="s2"&gt;"$PATIENCE"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No progress for $PATIENCE rounds, stopping"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; 2&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;

  cp eval.log feedback.txt                  &lt;span class="c"&gt;# 4. Leave feedback for the next round to read&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Iteration cap reached, stopping"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Ralph loop that circulates in the community is, in essence, the plainest version of this skeleton: a &lt;code&gt;while&lt;/code&gt; loop that feeds the same task description to the agent every time and lets it pick up from progress stored in files. The difference is that production use needs a budget cap and stop conditions added, otherwise it is just burning money without limit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the Success Rate Goes Up
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Hand it all to the agent at once&lt;/th&gt;
&lt;th&gt;With a loop&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Context&lt;/td&gt;
&lt;td&gt;Keeps growing; quality drops later on&lt;/td&gt;
&lt;td&gt;Clean each round, carrying only the list and feedback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completion&lt;/td&gt;
&lt;td&gt;The agent says "done" by its own feeling&lt;/td&gt;
&lt;td&gt;The compiler and tests decide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error handling&lt;/td&gt;
&lt;td&gt;Hundreds of errors surface at the end&lt;/td&gt;
&lt;td&gt;Each file is checked and fixed on the spot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When something breaks&lt;/td&gt;
&lt;td&gt;No idea where to roll back to&lt;/td&gt;
&lt;td&gt;One commit per round; exact rollback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I have not measured this scenario, so I give no specific success rate. The key is not "running it more times," but that &lt;strong&gt;the question "is it done" moves from the agent's own impression to the compiler's judgment.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Case Two: Making a Support Agent Handle Tickets More Accurately
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task and Why It Is Hard
&lt;/h3&gt;

&lt;p&gt;A company has a customer support agent that sorts incoming tickets (refund / shipping / account / other) and drafts a suggested reply. After launch, some tickets are misclassified and sent to the wrong team.&lt;/p&gt;

&lt;p&gt;What gets adjusted this time is not a piece of code but &lt;strong&gt;the agent's own configuration&lt;/strong&gt;: the prompt and the knowledge base it can search. This is the closest to training the dog model, because it really is parameter tuning, except the parameters are text.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Build the Loop
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Prepare an eval set first: a batch of historical tickets with the correct answers labeled by people&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Say 200 of them. It plays the role of "images with correct labels" in the dog model. &lt;strong&gt;Then set 40 of them aside and never look at them during the whole tuning process&lt;/strong&gt;, using them only once at the end. This is the old machine learning rule of keeping the training and test sets apart. Without it, the high score you see at the end only means the prompt memorized those questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Run a round, look at the score, and look harder at where it went wrong&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The numbers below are made up to illustrate the idea, not real experiment results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Round 1  config v1   accuracy 72%
  Error analysis (grouped by type):
    - "Package shows delivered but I never got it" classified as "other" (should be shipping)   x 17
    - "Want to change my linked phone number" classified as "other" (should be account)         x 12
    - "Refund is slow to arrive" classified as "shipping" (should be refund)                    x 8
    - Other scattered errors                                                                    x 7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Have another agent read the error analysis and propose configuration changes&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Changes in v2:
  - Add a prompt rule: package problems (delivered but not received, lost, damaged) all go to shipping
  - Add a prompt rule: changing a phone number, email, or password goes to account
  - Add 12 example sentences to the knowledge base that separate "refund" from "shipping"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Re-run the eval set, confirm it improved, and confirm nothing that was right got broken&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Round 2  config v2   accuracy 86% (tuning portion)
Round 3  config v3   accuracy 91%
Round 4  config v4   accuracy 91% (no progress) -&amp;gt; stop
Final: check once on the 40 held-out tickets -&amp;gt; 88%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the last line: &lt;strong&gt;88% on the held-out set is a little lower than the 91% during tuning.&lt;/strong&gt; That is normal. The gap is the most honest number in the whole process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the Success Rate Goes Up
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Editing the prompt by gut feeling&lt;/th&gt;
&lt;th&gt;With a loop&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;How do you know it improved&lt;/td&gt;
&lt;td&gt;"It feels a bit better"&lt;/td&gt;
&lt;td&gt;Compare accuracy on the same questions before and after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How do you know what to change&lt;/td&gt;
&lt;td&gt;Guess from impressions&lt;/td&gt;
&lt;td&gt;Error analysis points to specific error types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Could it break something else&lt;/td&gt;
&lt;td&gt;No idea&lt;/td&gt;
&lt;td&gt;Re-run the whole eval set each round; regressions show up immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When to stop&lt;/td&gt;
&lt;td&gt;When you are tired of editing&lt;/td&gt;
&lt;td&gt;When several rounds in a row make no progress&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this case, &lt;strong&gt;the eval set is itself an asset&lt;/strong&gt;: the next time you switch models or change the workflow, it tells you right away whether things got better or worse.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Case Three: Having an Agent Write a Competitor Research Report (Where Evaluation Gets "Soft")
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Task and Why It Is Hard
&lt;/h3&gt;

&lt;p&gt;Ask an agent to research three competitors and write a comparison report for a product manager. This kind of task has no test cases, and "is it good" seems to come down to feel.&lt;/p&gt;

&lt;p&gt;Typical problems from a single generation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Numbers have no source, or the source page does not contain that number at all&lt;/li&gt;
&lt;li&gt;The three products get different amounts of space and depth&lt;/li&gt;
&lt;li&gt;The conclusion is vague: "each has its strengths"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to Build the Loop
&lt;/h3&gt;

&lt;p&gt;The key for soft tasks: &lt;strong&gt;break "good" into small questions that can be checked one by one&lt;/strong&gt;, and make some of those checks deterministic so they do not depend on a model's subjective judgment.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;th&gt;Hard / soft&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Every number in the report has a source link&lt;/td&gt;
&lt;td&gt;A script extracts numbers and links and confirms each link is reachable&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The number actually appears on the source page&lt;/td&gt;
&lt;td&gt;A script fetches the page and searches for it&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All three competitors cover the same dimensions&lt;/td&gt;
&lt;td&gt;Tick through a dimension checklist&lt;/td&gt;
&lt;td&gt;Semi-hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The conclusion gives a clear recommendation&lt;/td&gt;
&lt;td&gt;Another model scores it against a rubric and writes the reason for each deduction&lt;/td&gt;
&lt;td&gt;Soft&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The agent writes a first draft&lt;/li&gt;
&lt;li&gt;The check scripts run the hard checks and the judge model runs the soft ones, merged into a &lt;strong&gt;list of problems with exact locations&lt;/strong&gt; (for example, "the '23% market share' in paragraph 3 cannot be found on the cited page")&lt;/li&gt;
&lt;li&gt;The agent edits only for the items on the list and does not rewrite the whole piece, so parts already fixed are not broken again&lt;/li&gt;
&lt;li&gt;Check again, for at most three or four rounds&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why the Success Rate Goes Up
&lt;/h3&gt;

&lt;p&gt;The biggest gain comes from the hard checks: &lt;strong&gt;numbers without sources and made-up citations get caught by a script on the spot.&lt;/strong&gt; These are exactly the errors a human reviewer misses most easily and that damage credibility most.&lt;/p&gt;

&lt;p&gt;But to be honest: &lt;strong&gt;the ceiling for this kind of loop is lower than in cases one and two.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The judge is a model with its own biases, and it can score the same report differently on two runs&lt;/li&gt;
&lt;li&gt;Model judges often favor longer, fancier writing, so the agent gets wordier with every edit&lt;/li&gt;
&lt;li&gt;"Good enough" is itself a fuzzy call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So a soft-evaluation loop is better for &lt;strong&gt;raising the floor and filtering out obvious problems&lt;/strong&gt; than for producing a final draft automatically. An important report still needs a person to sign off.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. What the Three Cases Have in Common
&lt;/h2&gt;

&lt;p&gt;Side by side:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Case 1: code migration&lt;/th&gt;
&lt;th&gt;Case 2: support tickets&lt;/th&gt;
&lt;th&gt;Case 3: research report&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Why one pass falls short&lt;/td&gt;
&lt;td&gt;Task too big for the context&lt;/td&gt;
&lt;td&gt;Config is off, tuned by feel&lt;/td&gt;
&lt;td&gt;Open-ended task, unstable quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who evaluates&lt;/td&gt;
&lt;td&gt;Compiler + tests&lt;/td&gt;
&lt;td&gt;Eval set with correct answers&lt;/td&gt;
&lt;td&gt;Scripts + rubric + judge model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What feedback looks like&lt;/td&gt;
&lt;td&gt;Compiler errors&lt;/td&gt;
&lt;td&gt;Error analysis&lt;/td&gt;
&lt;td&gt;Problem list with locations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What gets adjusted&lt;/td&gt;
&lt;td&gt;Code, task list&lt;/td&gt;
&lt;td&gt;Prompt, knowledge base&lt;/td&gt;
&lt;td&gt;The draft report&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How "hard" the evaluation is&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;td&gt;Fairly hard&lt;/td&gt;
&lt;td&gt;Mix of hard and soft&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is only one shared rule: &lt;strong&gt;a loop's ceiling is set by the quality of its evaluation.&lt;/strong&gt; The harder and more specific the evaluation, the more reliable the loop; the softer it is, the more it needs a person.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. A Few Easily Misunderstood Points
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. There Is No Real "Gradient" in a Loop
&lt;/h3&gt;

&lt;p&gt;In the dog model the gradient is computed mathematically, and the direction is exact. In a loop, the "gradient" is a piece of text feedback that a large model reads and then decides how to act on. So adjustments can oscillate: fixing one item may break another, and no number of rounds guarantees convergence. That is why an iteration cap, early stopping, and regression checks are necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Model Itself Does Not Change; What Surrounds It Does
&lt;/h3&gt;

&lt;p&gt;Training modifies a model's weights. A loop does not. The large model's parameters stay frozen throughout, and what gets adjusted is the workspace, the context, the prompt, and the knowledge base. Case one adjusts a work product, case two adjusts the agent's own configuration, and they solve different problems: the former is "get this one thing done," the latter is "make the agent more accurate at this kind of work from now on."&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Agent Must Not Control the Evaluator
&lt;/h3&gt;

&lt;p&gt;This is the most important point, and the biggest lesson it shares with machine learning. If the party being evaluated decides how it is evaluated, it learns to "get a high score" rather than to "get it right." Common ways agents cheat include deleting or editing failing tests, hard-coding answers for the test cases, and writing their own tests, passing them, and declaring victory.&lt;/p&gt;

&lt;p&gt;The countermeasures: keep tests and evaluation scripts where the agent has no write access; have different roles write the code and write the tests; keep a validation set the agent has never seen. One line from the community puts it well: only loop on what you can verify.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Evaluation Needs a "Diagnosis," Not Just a "Score"
&lt;/h3&gt;

&lt;p&gt;With only a 72, the agent knows it is not good enough but not where to change. The score is the loss value; the specific failing items and reasons are the gradient. When designing evaluation, try to have it output which item, which location, and why it is wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. So, New Technology or Leftovers?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The leftover part, which is most of it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feedback loops: the thermostat in control theory has had this structure for a long time&lt;/li&gt;
&lt;li&gt;Iterating until the goal is met: the machine learning training loop, and "change it, run the tests, change it again" in software&lt;/li&gt;
&lt;li&gt;Continuous improvement: PDCA (plan, do, check, act)&lt;/li&gt;
&lt;li&gt;Eval sets, early stopping, guarding against overfitting: the basics of machine learning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you already know these, you have been using about eighty percent of loop engineering all along. There is no need to mythologize it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The new part does exist:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What can be adjusted has changed.&lt;/strong&gt; It used to have to be differentiable numbers; now it can be a piece of text or a file, so open-ended tasks can enter the loop for the first time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback can be plain language.&lt;/strong&gt; Evaluation used to be numbers only; now an error message or a comment can drive the next round of edits directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "adjuster" is itself a model that can reason.&lt;/strong&gt; It can read the feedback and draw lessons, rather than following a gradient mechanically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New risks.&lt;/strong&gt; An evaluator that can be gamed, runaway cost, and biased judges are less prominent in traditional training.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One more caveat: &lt;strong&gt;the boundaries of the term are still fuzzy.&lt;/strong&gt; It overlaps heavily with harness engineering (building the runtime around an agent) and context engineering, and the terminology is still shifting; different articles mean slightly different things by loop engineering. Do not get hung up on the word. Hold on to the five parts.&lt;/p&gt;

&lt;p&gt;A more accurate way to say it: &lt;strong&gt;old wine in a new bottle, but with one new ingredient added: the "adjust" step can now be done by a model that understands text feedback.&lt;/strong&gt; That ingredient lets the old wine be used in places it could not reach before, which is why it is worth discussing again.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. A Checklist Before You Start
&lt;/h2&gt;

&lt;p&gt;To add a loop to your own agent, answer these six questions first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What is the goal?&lt;/strong&gt; Can you state it in one sentence, ideally with a number?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who evaluates?&lt;/strong&gt; Is the evaluator out of the agent's reach? Is there a hard metric you can use?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does the feedback contain a diagnosis?&lt;/strong&gt; When something fails, can the agent see exactly what is wrong?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What can the agent adjust?&lt;/strong&gt; Work products, or its own prompt and knowledge base? Is the scope drawn?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When does it stop?&lt;/strong&gt; Is there an iteration cap, a budget cap, and a stop after several rounds without progress?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How do you prevent regressions?&lt;/strong&gt; When one thing is fixed, is there a regression check that nothing else was broken?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A simple test: &lt;strong&gt;if you cannot answer question 2, do not start the loop yet.&lt;/strong&gt; Without reliable evaluation, a loop only amplifies errors faster and with more confidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The essence&lt;/strong&gt;: loop engineering is the most basic machine learning engineering mindset: goal, do a round, evaluate, feedback, adjust, go again, until the goal is met or it stops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What it solves&lt;/strong&gt;: tasks too big for the context, tasks too open-ended to be stable, and errors that propagate when nobody is watching. The three cases map to these in turn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ceiling is set by evaluation&lt;/strong&gt;: compilers and tests are hardest, eval sets next, model judges softest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New versus old&lt;/strong&gt;: the idea is leftovers; what is fresh is that the adjustable thing can be text and the feedback can be plain language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In practice&lt;/strong&gt;: practice on tasks with hard evaluation first, then try soft evaluation; design the evaluation first, then let the loop run.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Notes: the control flow of the skeleton was verified with stub commands (the goal-reached and stuck exits) and not connected to a real agent. The numbers in case two are invented for illustration. The three cases are summaries of common practice; I have not run full experiments on cases one and three, so no specific success rates are given. The description of how the community uses "loop engineering" comes from public articles, and the definition of the term is still not settled.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>harness</category>
      <category>agents</category>
    </item>
    <item>
      <title>Open Source Project #229: e2e — Write E2E Tests in Plain English, Then Replay Verified Agent Actions With Zero Model Calls</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Wed, 07 Oct 2026 12:11:22 +0000</pubDate>
      <link>https://dev.to/wonderlab/open-source-project-229-e2e-write-e2e-tests-in-plain-english-then-replay-verified-agent-11h7</link>
      <guid>https://dev.to/wonderlab/open-source-project-229-e2e-write-e2e-tests-in-plain-english-then-replay-verified-agent-11h7</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Describe a goal in natural language and an agent drives the app to reach it. Check the result with locators and assertions in the same test."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is &lt;strong&gt;article #229&lt;/strong&gt; in the "One Open Source Project a Day" series. Today's project is &lt;strong&gt;e2e&lt;/strong&gt; — an end-to-end testing framework for web and mobile apps by TesterArmy, 6,361 Stars, Apache-2.0 license.&lt;/p&gt;

&lt;p&gt;End-to-end testing has long been stuck in a tension: &lt;strong&gt;hardcoded selectors break the moment the UI is redesigned, but if test logic is too loose, it can't run reliably in CI.&lt;/strong&gt; e2e's answer is to split every test into three fundamentally different kinds of steps — natural-language agent actions (Goals), model-judged assertions (Assertions), and precise selector-based checks (Locators) — then layer a "replay cache" on top, so verified agent actions can &lt;strong&gt;skip model calls entirely&lt;/strong&gt; on later runs, waking the agent back up only when the app actually changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You'll Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;How the three test step types divide responsibility: Goal / Assertion / Locator&lt;/li&gt;
&lt;li&gt;How the replay cache lets AI-driven tests escape the "call the model every single run" cost problem&lt;/li&gt;
&lt;li&gt;The Decision Model executor: answering multiple-choice questions with a small model instead of letting a large one free-wheel&lt;/li&gt;
&lt;li&gt;Quick start: from &lt;code&gt;npx e2e init&lt;/code&gt; to your first test&lt;/li&gt;
&lt;li&gt;Migration paths from Playwright, Cypress, Detox, and Maestro&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Experience writing Playwright, Cypress, or similar end-to-end tests&lt;/li&gt;
&lt;li&gt;Basic understanding of how LLM agents work (instruction → observation → action loop)&lt;/li&gt;
&lt;li&gt;TypeScript fundamentals&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;e2e is built by TesterArmy — the same company behind an agentic testing platform that runs natural-language tests on web and mobile apps on every pull request or on a schedule. e2e is the open-source core engine behind that capability.&lt;/p&gt;

&lt;p&gt;It's not another "let AI generate your test code" tool. It embeds &lt;strong&gt;AI-driven action itself&lt;/strong&gt; into the test runtime: within a single test, which steps should be left to the agent's own judgment, and which must be pinned down with exact selectors, is something the developer explicitly decides.&lt;/p&gt;

&lt;h3&gt;
  
  
  Author / Team
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organization&lt;/strong&gt;: TesterArmy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary language&lt;/strong&gt;: TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: Apache-2.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Created&lt;/strong&gt;: 2026-07-22&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Active development toward 1.0; APIs and config can still change between minor releases&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;6,361+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: 285+&lt;/li&gt;
&lt;li&gt;📄 License: Apache-2.0&lt;/li&gt;
&lt;li&gt;📅 Created: 2026-07-22&lt;/li&gt;
&lt;li&gt;🏷️ Topics: e2e, e2e-testing, end-to-end-testing, mobile, mobile-testing, playwright, web&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx e2e init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;init&lt;/code&gt; asks for an engine (web or mobile) and a model provider, then writes a config and an example test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write a Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tests/checkout.e2e.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;e2e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a member upgrades to Pro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/settings/billing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upgrade the workspace to the Pro plan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;the invoice preview shows a prorated amount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toContainText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three step types show up in this one test: &lt;code&gt;agent.act&lt;/code&gt; lets the agent figure out how to complete "upgrade to Pro" on its own, &lt;code&gt;agent.assert&lt;/code&gt; has the model judge whether the invoice preview is semantically correct, and &lt;code&gt;expect&lt;/code&gt; verifies the final state with a precise role-based selector.&lt;/p&gt;

&lt;h3&gt;
  
  
  Supported Engines
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;e2e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The SDK, runner, and CLI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@e2e-dev/web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Browser engine: Chromium, Firefox, WebKit through Playwright&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@e2e-dev/mobile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;iOS/Android engine: simulators and emulators through agent-device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@e2e-dev/github&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reporter that posts results as a pull request comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@e2e-dev/kernel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel-hosted cloud browsers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@e2e-dev/eas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;EAS Simulators-hosted iOS simulators and Android emulators&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@e2e-dev/decision&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Decision-model executors for bounded semantic actions and assertions, instead of a full LLM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The project ships ready-made examples for Vite, Next.js, Expo, and SwiftUI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core: How the Three Step Types Divide Responsibility
&lt;/h2&gt;

&lt;p&gt;e2e splits every test step into three categories, each with a different model-call policy:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;Model call&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;agent.act&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes, unless replayed from cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Assertion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;agent.assert&lt;/code&gt; / &lt;code&gt;agent.waitFor&lt;/code&gt; / &lt;code&gt;agent.extract&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Locator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;screen&lt;/code&gt; and &lt;code&gt;expect&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Goal: Let the Agent Drive the Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete checkout with the test card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invite {email} as an editor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ada@example.test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;agent.act&lt;/code&gt; takes exactly one goal. The agent decides what to click and what to type. The developer controls the &lt;strong&gt;order&lt;/strong&gt; of goals, not the execution details inside each one. The project's own guidance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write one goal per call, using the words the screen actually shows&lt;/li&gt;
&lt;li&gt;Pass test data through &lt;code&gt;params&lt;/code&gt;; for passwords, use a &lt;code&gt;Secret&lt;/code&gt; (the model never sees the plaintext)&lt;/li&gt;
&lt;li&gt;Wrap values that change on every run — like emails or timestamps — in &lt;code&gt;unique()&lt;/code&gt;, so the replay cache can match them correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Assertion: Let the Model Judge Semantics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;the dashboard shows a trial badge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a download link appears&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;every todo title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The judging model only sees a &lt;strong&gt;text snapshot of the current screen&lt;/strong&gt; plus the agent's context — not the history or summaries of earlier steps. This isolation is deliberate: it keeps assertions from being swayed by the "narrative" of prior steps. For visual checks (charts, layouts), pass &lt;code&gt;vision: true&lt;/code&gt; to let the model see a screenshot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Locator: No Model Involved
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sign in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;listitem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Design review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Archive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2 remaining&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you know exactly which control to click and exactly what text to check, use a Locator — zero model calls, zero uncertainty, same writing style as a traditional Playwright test.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Most Interesting Design: The Replay Cache
&lt;/h2&gt;

&lt;p&gt;This is e2e's core differentiator against "just let an LLM drive the whole test," and the key mechanism for solving the "AI testing is too expensive and too slow" problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Once an &lt;code&gt;agent.act()&lt;/code&gt; step is followed by a verification step that &lt;strong&gt;confirms the result&lt;/strong&gt;, the runner records exactly which actions it took (which control, what value) into a JSON entry&lt;/li&gt;
&lt;li&gt;On the next run of the same test, the runner &lt;strong&gt;replays those actions directly&lt;/strong&gt;, with no model call at all&lt;/li&gt;
&lt;li&gt;If replay finds a control missing or the page structure changed, the agent &lt;strong&gt;takes over from the current screen&lt;/strong&gt; and finishes the rest using the model&lt;/li&gt;
&lt;li&gt;The run report shows the breakdown: &lt;code&gt;4 replayed · 1 handed off · 1 missed&lt;/code&gt; — which steps were pure replay, which fell back to the agent mid-replay, and which had no cache hit at all
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;           AI  4.1k tokens · 2 model calls · anthropic/claude-sonnet-4.5
        Cache  4 replayed · 1 handed off · 1 missed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Counts as "Verified"
&lt;/h3&gt;

&lt;p&gt;The cache doesn't record unconditionally. Only when &lt;code&gt;agent.act&lt;/code&gt; is immediately followed by a step that &lt;strong&gt;actually verifies the outcome&lt;/strong&gt; — like &lt;code&gt;expect(locator).toHaveText(...)&lt;/code&gt; or &lt;code&gt;agent.assert(...)&lt;/code&gt; — does a recording get written. Plain-value assertions like &lt;code&gt;expect(value).toBe(...)&lt;/code&gt; or read-only calls like &lt;code&gt;agent.extract(...)&lt;/code&gt; don't count as verification and won't trigger recording.&lt;/p&gt;

&lt;p&gt;This design forces a disciplined "Act, then immediately check" test structure — which happens to be exactly the rigor end-to-end tests should have anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why a Replay Fails
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;no-entry&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No recorded entry matches this step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wrong-context&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The app is on a different screen than when recorded (different origin/path/query)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;target-not-found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A recorded control didn't appear within 15 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;end-mismatch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every action ran, but the expected final state never showed up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An action simply can't be replayed (e.g. one that depended on reading pixels)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In production, &lt;code&gt;--strict-cache&lt;/code&gt; turns "cache exists but fails to replay" into a hard failure instead of a silent hand-off — closing off the hidden cost problem of "every CI run is quietly spending model calls and nobody notices."&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Design Matters
&lt;/h3&gt;

&lt;p&gt;The biggest pain point of most "AI-driven testing" tools is &lt;strong&gt;uncontrolled cost&lt;/strong&gt;: every CI run makes a real LLM call, and multiply that by dozens of tests times multiple CI triggers per day, and the bill spirals fast. e2e's replay cache essentially separates "the agent discovering a reliable path" from "repeatedly executing that path" — you pay for model calls during discovery, and execution afterward is nearly free, until the app actually changes enough to warrant paying for discovery again. That's a lot smarter than "ask the AI again from scratch every time."&lt;/p&gt;




&lt;h2&gt;
  
  
  The Decision Model Executor: Multiple-Choice Instead of Free-Form
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@e2e-dev/decision&lt;/code&gt; offers another way to execute &lt;code&gt;agent.act&lt;/code&gt; / &lt;code&gt;agent.assert&lt;/code&gt;: instead of a full LLM making free-form decisions, a dedicated &lt;strong&gt;Decision Model&lt;/strong&gt; answers one multiple-choice question per action — "which operation, and which element" — with a probability distribution rather than free text. Only when the chosen operation is "type" does a small text model generate the actual input value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;decisionExecutor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@e2e-dev/decision&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decisionExecutor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;typeSafeAi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decisionModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jev-latest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;textModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;openrouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inception/mercury-2.5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;minProbability&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;minConfidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point of this design: model output never becomes a selector, a URL, or a key — it can only pick an index from a finite set of options the executor built. &lt;code&gt;minProbability&lt;/code&gt; and &lt;code&gt;minConfidence&lt;/code&gt; let you reject low-confidence actions outright instead of gambling on an uncertain guess. The trade-off is reduced capability — no vision, no multi-select lists, no drag-and-drop — which makes it a good fit for apps with regular, predictable UI structure where you want to run a large test suite more cheaply.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/tester-army/e2e" rel="noopener noreferrer"&gt;tester-army/e2e&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/e2e" rel="noopener noreferrer"&gt;e2e&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href="https://e2e.tester.army/docs" rel="noopener noreferrer"&gt;e2e.tester.army/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🏢 &lt;strong&gt;Made by&lt;/strong&gt;: &lt;a href="https://tester.army" rel="noopener noreferrer"&gt;TesterArmy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Discord&lt;/strong&gt;: &lt;a href="https://tester.army/discord" rel="noopener noreferrer"&gt;tester.army/discord&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔄 &lt;strong&gt;Migration guides&lt;/strong&gt;: from Cypress, Detox, Maestro, Playwright, and Selenium&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;e2e represents a clear-eyed judgment about AI testing tools: &lt;strong&gt;getting an agent to operate an app isn't the hard part — the hard part is doing it in a way that adapts to UI changes without turning every CI run into a model-call bill.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three things worth noting:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The explicit split into three step types corrects an overgeneralization in "AI testing."&lt;/strong&gt; Many AI testing tools try to have one agent handle everything — acting, asserting, verifying, all via model judgment. e2e splits tests into Goal / Assertion / Locator layers, letting developers choose precisely where they need flexibility and where they need determinism, instead of being forced to hand everything to a model or write every selector by hand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The replay cache separates "discovery cost" from "execution cost," which is the key to running AI-driven tests sustainably in real CI.&lt;/strong&gt; Without this mechanism, AI-driven end-to-end testing is nearly impossible to scale in production — model-call latency and cost would grow linearly with the number of tests. e2e's "record after verification passes, replay afterward, rediscover only when things change" strategy keeps most runs almost free of model cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Decision Model executor shows that not every AI decision needs a full LLM.&lt;/strong&gt; Turning action selection into a multiple-choice question for a dedicated Decision Model is cheaper, more controllable, and more auditable than letting a general-purpose LLM free-wheel — worth watching for teams that need to run large test suites at scale.&lt;/p&gt;

&lt;p&gt;If your team is looking for an AI-driven end-to-end testing approach that can actually run reliably in real CI — not just impress in a demo — e2e is worth a serious look.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Explore &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills, each validated against real enterprise workflows. No hype, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Visit my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;personal site&lt;/a&gt; for more insights and interesting products.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>testing</category>
      <category>e2e</category>
      <category>playwright</category>
    </item>
    <item>
      <title>One Open Source Project a Day (No. 192): AX — Google's Open-Source 'Kubernetes for Agents,' Orchestrating Billions of Agent Tasks</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Sat, 26 Sep 2026 08:49:32 +0000</pubDate>
      <link>https://dev.to/wonderlab/one-open-source-project-a-day-no-192-ax-googles-open-source-kubernetes-for-agents-5558</link>
      <guid>https://dev.to/wonderlab/one-open-source-project-a-day-no-192-ax-googles-open-source-kubernetes-for-agents-5558</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Agents are a new kind of workload. They are neither stateless microservices nor run-to-completion batch jobs."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;192nd&lt;/strong&gt; article in the "One Open Source Project a Day" series. Today's project is &lt;strong&gt;AX&lt;/strong&gt; (repository: &lt;code&gt;google/ax&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Once a team starts running AI agents at scale, they quickly hit an awkward fact: none of the existing scheduling infrastructure fits well. Kubernetes' Pod model assumes workloads are either stateless services or run-to-completion batch jobs, but agents accumulate state, need strict sandbox isolation, call out to model APIs and tool servers constantly, and can burn through a token budget in a loop the moment nobody's watching. Traditional CI/CD or batch platforms weren't designed for needs like "pause an agent and resume it exactly where it left off" or "SSH into the sandbox to see what it's actually doing."&lt;/p&gt;

&lt;p&gt;AX is Google's open-source "high-throughput, declarative agent orchestration runtime," aimed at running billions of autonomous agent workloads in a single cluster. If you've used Kubernetes, the &lt;code&gt;ax&lt;/code&gt; experience will feel very familiar — &lt;code&gt;apply&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;watch&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;, a kubectl-shaped command set, just with agent tasks as the scheduling object. It runs on top of &lt;a href="https://github.com/agent-substrate/substrate" rel="noopener noreferrer"&gt;Agent Substrate&lt;/a&gt; for sandboxed execution, and focuses purely on the declarative orchestration layer itself.&lt;/p&gt;

&lt;p&gt;11,600+ Stars, 560 Forks, Apache-2.0 License, developed and led by Google. It's currently in Alpha — core concepts and protocols are still rapidly evolving, and the project explicitly warns of likely major breaking changes before a stable release.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Will Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AX's three core primitives — &lt;code&gt;Task&lt;/code&gt;, &lt;code&gt;Workspace&lt;/code&gt;, &lt;code&gt;Model&lt;/code&gt; — and what each solves&lt;/li&gt;
&lt;li&gt;Why agent workloads need a new scheduling model, neither a stateless service nor a batch job&lt;/li&gt;
&lt;li&gt;The layered relationship between AX and Agent Substrate: orchestration layer vs. sandbox execution layer&lt;/li&gt;
&lt;li&gt;The control-plane architecture: why AX stores state in Redis instead of directly as Kubernetes CRDs&lt;/li&gt;
&lt;li&gt;The sandbox lifecycle design behind &lt;code&gt;ax suspend&lt;/code&gt; / &lt;code&gt;ax resume&lt;/code&gt; / &lt;code&gt;ax ssh&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Familiarity with basic Kubernetes concepts (Pods, CRDs, &lt;code&gt;kubectl&lt;/code&gt; habits)&lt;/li&gt;
&lt;li&gt;A basic understanding of containerized deployment and sandbox isolation&lt;/li&gt;
&lt;li&gt;Optional: a basic understanding of MCP (Model Context Protocol)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;AX describes itself as "Google's open agentic orchestration runtime." The README opens with a tight operational description: "\"Declare an agentic task with workspaces and model specifications. AX sandboxes it, wires up its workspace, and helps running it at scale.\" That single sentence captures the project's division of labor — AX itself doesn't handle sandbox isolation or execution; that's delegated to the underlying Agent Substrate. AX focuses on declaratively defining tasks, scheduling at scale, and lifecycle management."&lt;/p&gt;

&lt;p&gt;The project documentation directly explains why existing tools fall short: "Agents are a new kind of workload. They are neither stateless microservices nor run-to-completion batch jobs. They accumulate state, need strict isolation, call out to model APIs and tool servers, and can burn money in a loop if nobody is watching." AX's answer is three small primitives that can be declared, rather than one all-encompassing framework trying to model everything an agent does.&lt;/p&gt;

&lt;h3&gt;
  
  
  Team and Background
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organization&lt;/strong&gt;: google&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: Apache License 2.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary language&lt;/strong&gt;: Go&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Underlying substrate&lt;/strong&gt;: &lt;a href="https://github.com/agent-substrate/substrate" rel="noopener noreferrer"&gt;Agent Substrate&lt;/a&gt; (sandboxed actor execution environment)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://agentexecutor.io" rel="noopener noreferrer"&gt;agentexecutor.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Alpha — core concepts and protocols are still evolving, with explicit warnings of likely major breaking changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;11,600+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: &lt;strong&gt;560&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📄 License: Apache-2.0&lt;/li&gt;
&lt;li&gt;🐛 Open Issues: &lt;strong&gt;48&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📅 Created: 2026-03&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem It Solves
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running agent workloads on general-purpose scheduling platforms:
  Kubernetes Pod model → assumes workloads are stateless services or batch jobs
  ↑ agents accumulate state, need strict isolation, call external model APIs frequently
  ↑ no native support for "pause and resume in place"
  ↑ no debugging channel for "SSH in and see what the agent is doing"
  ↑ every run re-clones repos, re-wires tools, re-configures MCP servers — high cold-start cost

AX's approach:
  Three declarative primitives: Task (sandboxed execution unit)
                                + Workspace (a pre-warmed environment)
                                + Model (a cluster-level model configuration)
  ↓
  Declare the whole thing at once with ax apply -f task.yaml
  ↓
  ax watch / ax ssh / ax suspend / ax resume give K8s-style
  observability and lifecycle management
  ↑ sandbox isolation is delegated to Agent Substrate; AX focuses on the orchestration layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Running autonomous coding/ops agents at scale&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each agent task runs as its own sandbox, declaring the Git repos, MCP servers, and tools it needs — fitting scenarios like "dispatch a separate agent to fix bugs across a fleet of repos"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Long-running, pausable agent workflows&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Via &lt;code&gt;ax suspend&lt;/code&gt; / &lt;code&gt;ax resume&lt;/code&gt;, an agent's state can be checkpointed and resumed later in place, without needing to design custom logic for "how do I save resources on a long-running task"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Development scenarios needing to debug and observe actual agent behavior&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ax ssh&lt;/code&gt; lets you go directly into a running sandbox to inspect the filesystem or run commands, instead of only being able to stare at log output&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multi-team scenarios needing centralized model credential and config management&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Model&lt;/code&gt; is declared as a cluster-level resource, so rotating a key or switching model versions is a single &lt;code&gt;ax apply&lt;/code&gt; instead of hunting through each agent's environment variables individually&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Requires a Kubernetes cluster with Agent Substrate already installed&lt;/span&gt;
kubectl get svc api &lt;span class="nt"&gt;-n&lt;/span&gt; ate-system   &lt;span class="c"&gt;# verify Substrate's Control API is ready&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Install the CLI:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/google/ax/cmd/ax@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deploy the control plane:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make deploy &lt;span class="nv"&gt;AX_IMAGE_REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-registry&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Declare a Workspace and Task in one YAML file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# task.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ax.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Workspace&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;golang&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;git&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/golang/go.git&lt;/span&gt;
      &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-fix"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ax.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Task&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workspaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;golang&lt;/span&gt;
      &lt;span class="na"&gt;goal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ensure&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;that&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Go&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;chain&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;available&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;built&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;source"&lt;/span&gt;
  &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;   &lt;span class="c1"&gt;# lets you `ax ssh` into the sandbox&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Apply and observe:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ax apply &lt;span class="nt"&gt;-f&lt;/span&gt; task.yaml
ax watch task &lt;span class="nb"&gt;test
&lt;/span&gt;ax ssh &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-al&lt;/span&gt; /workspace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Three orthogonal declarative primitives&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Solves&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Task&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs untrusted agent code in an isolated sandbox with CPU/memory limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Workspace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pre-wires Git repos, MCP servers, and skill packages so every agent "starts warm"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Declares which LLM the platform itself uses, with credentials from a Kubernetes secret&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2. A kubectl-shaped CLI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ax apply&lt;/code&gt;, &lt;code&gt;ax get&lt;/code&gt;, &lt;code&gt;ax describe&lt;/code&gt;, &lt;code&gt;ax watch&lt;/code&gt;, &lt;code&gt;ax delete&lt;/code&gt;, plus agent-specific verbs like &lt;code&gt;ax ssh&lt;/code&gt;, &lt;code&gt;ax suspend&lt;/code&gt;, &lt;code&gt;ax resume&lt;/code&gt;. It follows your active &lt;code&gt;kubectx&lt;/code&gt; context — switch clusters and &lt;code&gt;ax&lt;/code&gt; automatically resolves and tunnels to that cluster's control plane in the background.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Task's deliberately minimal design philosophy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over its lifetime, an agent plans, delegates, retries, and fans work out. AX makes no attempt to model that shape. It gives you one primitive that's cheap to create, isolate, suspend, and throw away, and lets the agent compose as many of them as its work demands. A single Task may be the entire job, or the root of a large tree of tasks spawned as the agent breaks the problem down — either way, every node gets the same sandbox, the same lifecycle, the same tooling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Goal-driven Workspace bootstrapping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Workspace binding can carry a &lt;code&gt;goal&lt;/code&gt; — a plain-language description of the environment the task needs. On first boot, the runner hands that goal to an agent that finishes the setup (say, installing a toolchain or dependencies), so the task's own command starts in an already-ready environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Cluster-level model resources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Declaring model configuration as a cluster resource, rather than scattered across each agent's environment variables, means rotating a key, pinning a new model version, or tuning generation parameters is a single &lt;code&gt;ax apply&lt;/code&gt;. AX's own components — for example, the process that plans a Workspace from a goal — read &lt;code&gt;Model&lt;/code&gt; resources too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Suspend/resume and SSH debugging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ax suspend&lt;/code&gt; checkpoints actor state and pauses the task; &lt;code&gt;ax resume&lt;/code&gt; picks it back up. &lt;code&gt;ax ssh&lt;/code&gt; requires a task to explicitly set &lt;code&gt;spec.debug: true&lt;/code&gt; before it will connect — because the underlying guest services grant arbitrary process execution and file access, keeping this off by default is a deliberate security decision.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Advantages
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Running agents directly on Kubernetes&lt;/th&gt;
&lt;th&gt;Building your own agent orchestration&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;AX&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fit with the state model&lt;/td&gt;
&lt;td&gt;Poor — Pods assume stateless or batch&lt;/td&gt;
&lt;td&gt;Depends on how much you build&lt;/td&gt;
&lt;td&gt;Designed for agents' "accumulates state + needs suspend/resume" nature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduling at scale&lt;/td&gt;
&lt;td&gt;Storing millions of tasks as CRDs hits etcd's limits&lt;/td&gt;
&lt;td&gt;Needs to be solved yourself&lt;/td&gt;
&lt;td&gt;Redis + Streams built for a billions-of-tasks target&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment pre-warming&lt;/td&gt;
&lt;td&gt;Needs custom initContainer logic&lt;/td&gt;
&lt;td&gt;Needs custom implementation&lt;/td&gt;
&lt;td&gt;The Workspace primitive handles it declaratively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging channel&lt;/td&gt;
&lt;td&gt;Logs/exec only&lt;/td&gt;
&lt;td&gt;Needs custom implementation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ax ssh&lt;/code&gt; built in, off by default for security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model config management&lt;/td&gt;
&lt;td&gt;Scattered everywhere&lt;/td&gt;
&lt;td&gt;Needs your own wrapper&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Model&lt;/code&gt; managed centrally as a cluster resource&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why choose this project?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Led by Google, already thinking through problems at the scale of "billions of agent tasks" — architectural decisions like choosing Redis over etcd are made specifically for that scale&lt;/li&gt;
&lt;li&gt;The operational mental model directly reuses Kubernetes experience, so &lt;code&gt;kubectl&lt;/code&gt; users face near-zero onboarding cost&lt;/li&gt;
&lt;li&gt;A clear layered design — AX focuses purely on orchestration, while sandbox execution is delegated to Agent Substrate, keeping responsibilities cleanly separated&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A Deeper Look
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Task State Isn't Stored as a Kubernetes CRD
&lt;/h3&gt;

&lt;p&gt;This is a particularly interesting architectural decision in AX. The project documentation states the reasoning directly: "Storing millions of short-lived tasks as Kubernetes CRDs pushes etcd past its comfort zone (single-digit GB storage limits, write-rate bottlenecks, control plane degradation)."&lt;/p&gt;

&lt;p&gt;AX's solution is to store state in Redis, using Redis Streams as the work queue between the API server and a horizontally scaled pool of controllers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ax apply -f task.yaml
        │
        ▼
    ax-server (stateless gRPC API)
        │
   store &amp;amp; publish event
        │
        ▼
      Redis (Task Hashes + Event Streams + PubSub)
        │
   XREADGROUP (consume the stream)
        │
        ▼
  ax-controller (horizontally scaled worker pool)
        │
      gRPC call
        │
        ▼
   Agent Substrate (sandboxed execution)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This choice shows the AX team never intended to box themselves into the "everything is a CRD" Kubernetes mental model — they only borrowed its &lt;strong&gt;operational experience&lt;/strong&gt; (&lt;code&gt;apply&lt;/code&gt;/&lt;code&gt;get&lt;/code&gt;/&lt;code&gt;watch&lt;/code&gt;), while swapping the underlying storage engine for something better suited to high-frequency, short-lived objects. It's a pragmatic tradeoff: the interaction pattern users perceive stays the same, but the backend is redesigned for the real scale requirement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task's "Small and Composable" Design
&lt;/h3&gt;

&lt;p&gt;AX's philosophy around Task is worth unpacking further. Many orchestration systems try to model "a complete agent workflow" — defining DAGs, defining step dependencies. AX does the opposite: it refuses to model an agent's internal behavior (planning, delegating, retrying, decomposing), offering instead only an atomic, cheap execution unit and letting the agent decide how to compose it.&lt;/p&gt;

&lt;p&gt;The upside of this design is flexibility — a single Task can be the entire job, or the root of an entire tree of tasks, and AX never needs to understand the shape of that tree; every node gets the same sandbox and lifecycle primitives. The tradeoff: dependencies and data flow between tasks have to be managed by the agent or a higher-level tool — AX doesn't provide built-in workflow-orchestration semantics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workspace's Goal-Driven Bootstrapping Mechanism
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Workspace.spec.goal&lt;/code&gt; is a somewhat unusual design: rather than directly specifying "install these dependencies," it's a plain-language description of an environment goal (e.g., "ensure the Go toolchain is available and built from source"), handed to an agent at sandbox startup to interpret and execute.&lt;/p&gt;

&lt;p&gt;This turns what would normally be mechanical work — writing setup scripts, writing Dockerfiles — into a dynamic process that can be described in natural language and completed by an agent. The project's roadmap shows this direction continuing to deepen: "Dynamic Agentic Environment Curation" plans to let this bootstrapping process automatically inspect repository contents, resolve toolchains, and discover relevant MCP servers and skills from registries, rather than just executing one pre-written goal description.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sandbox Lifecycle: The Runner as a Persistent PID 1
&lt;/h3&gt;

&lt;p&gt;Inside every task container, &lt;code&gt;ax-task-runner&lt;/code&gt; starts as PID 1, taking on responsibilities well beyond simply "starting a command": it loads the Task and Workspace specs, starts a metadata and guest-management daemon on port 80, prepares each workspace in binding order on first run (cloning repos, setting up the skills path, and handing off to an agent if a goal is set), and only then starts &lt;code&gt;spec.command&lt;/code&gt; as a supervised child process.&lt;/p&gt;

&lt;p&gt;The key detail: the runner stays alive as PID 1 even after the command exits, meaning the metadata server keeps responding and &lt;code&gt;ax ssh&lt;/code&gt; still works after the main command has finished. This makes it possible to debug an already-completed task, rather than having the whole container vanish the moment the task ends.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Layered Relationship with Agent Substrate
&lt;/h3&gt;

&lt;p&gt;AX doesn't handle sandbox isolation itself — that's fully delegated to Agent Substrate, a lower-level system responsible for atespace provisioning, actor creation and activation, and worker assignment. AX's controller drives tasks toward their desired state by calling Substrate's Control API over gRPC. This layering lets AX focus purely on "declarative orchestration semantics" and "scheduling at scale," leaving the harder, lower-level problem of "how do you safely isolate an untrusted process" to a dedicated project — which is also why AX's roadmap devotes so much space to how the Substrate actor architecture will evolve (migrating to the new actor API, splitting workspace initialization into a dedicated actor, minimally privileged policies, idleness detection with automatic suspension, and so on).&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Links and Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Official Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/google/ax" rel="noopener noreferrer"&gt;https://github.com/google/ax&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://agentexecutor.io" rel="noopener noreferrer"&gt;https://agentexecutor.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;License&lt;/strong&gt;: Apache License 2.0&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Issue Tracker&lt;/strong&gt;: &lt;a href="https://github.com/google/ax/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/agent-substrate/substrate" rel="noopener noreferrer"&gt;Agent Substrate&lt;/a&gt; — The sandboxed execution environment AX depends on&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/agent-substrate/env" rel="noopener noreferrer"&gt;Agent Substrate Guest Services&lt;/a&gt; — The process/filesystem services that power &lt;code&gt;ax ssh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; — The tool-integration standard Workspaces can wire in&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Three orthogonal primitives cover the core needs of agent orchestration&lt;/strong&gt;: Task handles isolated execution, Workspace handles environment pre-warming, Model handles cluster-level configuration management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reuses Kubernetes' operational mental model, but redesigns the storage backend&lt;/strong&gt;: replacing etcd with Redis + Streams is a tradeoff tailored to a billions-of-short-lived-tasks target&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Doesn't model agent behavior, only provides a cheap composable execution unit&lt;/strong&gt;: Task is deliberately small, leaving decomposition, delegation, and retry logic to the agent itself&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A clear layered architecture&lt;/strong&gt;: AX focuses on declarative orchestration, delegating sandbox isolation to Agent Substrate, keeping responsibilities cleanly separated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Still in Alpha&lt;/strong&gt;: core concepts and protocols are still rapidly evolving — watch for breaking changes before adopting it in production&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Who This Is For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Teams running autonomous agent workloads at scale&lt;/strong&gt;: especially where task volume has already outgrown hand-managed scripts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams already using Kubernetes and comfortable with its operational mental model&lt;/strong&gt;: the kubectl-style experience means near-zero migration cost&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scenarios needing to pause/resume long-running agents, or needing a debugging channel to observe actual agent behavior&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developers willing to accept the risk of an Alpha-stage project in exchange for getting in early on the design and contributing&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  One-Line Verdict
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;AX isn't trying to reinvent how agents should "think" — it's first solving the infrastructure problem of how to run this new, state-accumulating workload safely and at scale in a cluster.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>google</category>
      <category>kubernetes</category>
      <category>agents</category>
    </item>
    <item>
      <title>One Open Source Project a Day (No. 191): Strands Agents Harness SDK — From Hand-Rolled Agent Loops to a Production-Ready Agent in One Call</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Fri, 25 Sep 2026 09:42:33 +0000</pubDate>
      <link>https://dev.to/wonderlab/one-open-source-project-a-day-no-191-strands-agents-harness-sdk-from-hand-rolled-agent-loops-egm</link>
      <guid>https://dev.to/wonderlab/one-open-source-project-a-day-no-191-strands-agents-harness-sdk-from-hand-rolled-agent-loops-egm</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Choose Strands when you would otherwise write your own agent loop: it runs in your process with no hosted control plane, and it covers the jobs a hand-rolled loop grows into."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;191st&lt;/strong&gt; article in the "One Open Source Project a Day" series. Today's project is &lt;strong&gt;Strands Agents&lt;/strong&gt; (repository name &lt;code&gt;harness-sdk&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Writing an AI agent that actually works in production is rarely as simple as "call the model, run the tool." In real usage you quickly run into a set of small but unavoidable problems: what happens when the conversation runs too long? What happens when tool results flood the context window? How do you resume after a session gets interrupted? Do you have to rewrite everything when switching model providers? Most teams' answer is to cobble together their own agent loop, then keep patching it as new problems surface — until that hand-rolled loop has grown session management, context compression, multi-model adapters, guardrails, and a pile of other things that should really be infrastructure, not bespoke code.&lt;/p&gt;

&lt;p&gt;Strands Agents is an open-source agent SDK from the AWS team, shipping Python and TypeScript implementations, built to solve exactly this. It's organized in two layers: at the bottom is the &lt;strong&gt;Strands SDK&lt;/strong&gt;, a model-driven, fully customizable agent loop, tool system, and multi-model support; on top of it is &lt;strong&gt;Strands Harness&lt;/strong&gt;, which gives you a fully assembled, production-grade agent with a single &lt;code&gt;create_harness()&lt;/code&gt; (or &lt;code&gt;createHarness()&lt;/code&gt; in TypeScript) call — already configured with context management, session persistence, long-term memory, and a skill system. And the object you get back is still a plain &lt;code&gt;strands.Agent&lt;/code&gt;, meaning everything Harness configures for you can be inspected, overridden, or replaced at will.&lt;/p&gt;

&lt;p&gt;8,300+ Stars, 1,200+ Forks, Apache-2.0 License, developed and maintained primarily by the Strands team at AWS.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Will Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The layered relationship between the Strands SDK and Strands Harness: "build your own agent loop" vs. "get a pre-configured production-grade agent"&lt;/li&gt;
&lt;li&gt;What &lt;code&gt;create_harness()&lt;/code&gt; gives you by default: context management, session persistence, long-term memory, skill loading&lt;/li&gt;
&lt;li&gt;The built-in tool set: shell, file operations, &lt;code&gt;programmatic_tool_caller&lt;/code&gt;, and &lt;code&gt;subagent&lt;/code&gt; delegation&lt;/li&gt;
&lt;li&gt;Model portability: switching between Bedrock, Anthropic, OpenAI, and Gemini using the same code&lt;/li&gt;
&lt;li&gt;MCP integration, interventions (tool-call gating), and subagent delegation patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Basic development experience in Python or TypeScript&lt;/li&gt;
&lt;li&gt;A basic understanding of LLM agent concepts (tool calling, multi-turn conversations)&lt;/li&gt;
&lt;li&gt;Optional: a basic understanding of MCP (Model Context Protocol)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;Strands Agents describes itself as "a model-driven approach to building AI agents in just a few lines of code." The README states the value proposition directly: "Choose Strands when you would otherwise write your own agent loop: it runs in your process with no hosted control plane, and it covers the jobs a hand-rolled loop grows into." This sentence captures the project's core pitch — it isn't just another thin skin over an "agent framework," it's the infrastructure that hand-rolled agent loops keep reinventing (lifecycle controls, tools, structured output, MCP, multi-agent patterns, memory, sessions, model portability, streaming, guardrails, tracing, evals), already built and ready to use.&lt;/p&gt;

&lt;p&gt;This repository (&lt;code&gt;harness-sdk&lt;/code&gt;) is a monorepo containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;harness-py/&lt;/code&gt;, &lt;code&gt;harness-ts/&lt;/code&gt;: the Python/TypeScript Strands Harness, assembled via &lt;code&gt;create_harness()&lt;/code&gt; / &lt;code&gt;createHarness()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;strands-cli/&lt;/code&gt;: a CLI to prototype and chat with a harness agent from the terminal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;strands-py/&lt;/code&gt;, &lt;code&gt;strands-ts/&lt;/code&gt;: the underlying SDKs — the agent loop, model providers, tool system&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;site/&lt;/code&gt;: source for the official documentation site (built with Astro/Starlight)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Team and Background
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organization&lt;/strong&gt;: strands-agents (led by the Strands team at AWS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: Apache License 2.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language support&lt;/strong&gt;: Python 3.10+, TypeScript (Node.js 22+)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distribution&lt;/strong&gt;: PyPI (&lt;code&gt;strands-agents&lt;/code&gt;, &lt;code&gt;strands-harness&lt;/code&gt;), npm (&lt;code&gt;@strands-agents/sdk&lt;/code&gt;, &lt;code&gt;@strands-agents/harness&lt;/code&gt;, &lt;code&gt;@strands-agents/cli&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;8,300+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: &lt;strong&gt;1,246&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📄 License: Apache-2.0&lt;/li&gt;
&lt;li&gt;🐛 Open Issues: &lt;strong&gt;837&lt;/strong&gt; (high activity level, frequent community feedback)&lt;/li&gt;
&lt;li&gt;📅 Created: 2025-05&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem It Solves
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The typical evolution of a hand-rolled agent loop:
  v1: a simple loop of model.generate() + tool calls
  ↓ conversations get longer → manually truncate history, risking dropped context
  ↓ tool results get too large → write custom summarization logic to fit the prompt
  ↓ need to switch model providers → rewrite the calling layer and parameter mapping
  ↓ need resumable sessions → implement your own session storage and recovery
  ↓ need to remember user preferences → implement your own "memory" storage and retrieval
  ↑ every step reinvents infrastructure, and each team's version is incompatible with everyone else's

Strands' approach:
  Bottom layer SDK: a model-driven agent loop + tool system + multi-model support, ready to use
  ↓
  Top layer Harness: one create_harness() call
  ↓
  Get back an agent pre-configured with context management/sessions/memory/skills
  ↑ the return value is still a plain Agent — everything Harness configures
    can be overridden, extended, or replaced
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Quickly building a coding/ops agent that handles long-running tasks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The kind of task shown in the official example — &lt;code&gt;agent("Find the slowest test in this repo and explain why it's slow")&lt;/code&gt; — requiring multi-step file reading, running shell commands, and analyzing results&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Production systems that need model portability&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Teams may run Bedrock, Anthropic, and OpenAI simultaneously, or need to switch providers later; Strands' &lt;code&gt;provider/model&lt;/code&gt; string syntax makes that a one-parameter change&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Assistant-style products that need long-term memory and session recovery&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Harness persists every conversation to disk by default and distills cross-conversation long-term memory via a background task, fitting scenarios like customer support or personal assistants that need to "remember" user preferences&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complex tasks that need to delegate subtasks and keep the main context from flooding with search results or multi-step exploration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The built-in &lt;code&gt;subagent&lt;/code&gt; tool lets the main agent hand off tasks like "search across many files," "a multi-step change," or "open-ended exploration" to a clean child agent, returning only its final answer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Minimal SDK usage (Python):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;strands-agents strands-agents-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is the square root of 1764&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Minimal SDK usage (TypeScript):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @strands-agents/sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Agent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@strands-agents/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;What is the square root of 1764?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Getting a production-grade agent directly with Harness (Python):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;strands-harness
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands_harness&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_harness&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_harness&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Find the slowest test in this repo and explain why it&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s slow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Getting a production-grade agent directly with Harness (TypeScript):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @strands-agents/harness
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createHarness&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@strands-agents/harness&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createHarness&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Find the slowest test in this repo and explain why it's slow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefer not to write code at all? Install the CLI with &lt;code&gt;npm install -g @strands-agents/cli&lt;/code&gt; and you get a &lt;code&gt;strands&lt;/code&gt; terminal command that chats directly with a Harness agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Layered design: SDK provides the foundation, Harness provides the assembly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The underlying &lt;code&gt;strands.Agent&lt;/code&gt; is a set of primitives you can fully assemble yourself; &lt;code&gt;create_harness()&lt;/code&gt; composes those primitives into a ready-to-use agent using benchmarked defaults. There's no gap between the two layers — the agent Harness returns is a plain &lt;code&gt;Agent&lt;/code&gt;, so every piece it configures can still be overridden after the fact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Production-grade capabilities out of the box&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Default behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Defaults to Claude Opus on Amazon Bedrock; supports Bedrock/Anthropic/OpenAI/Google and more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tools&lt;/td&gt;
&lt;td&gt;Comes with shell, file read/write/edit, &lt;code&gt;web_fetch&lt;/code&gt;, and &lt;code&gt;programmatic_tool_caller&lt;/code&gt; (a sandbox for tool orchestration)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context management&lt;/td&gt;
&lt;td&gt;Automatically summarizes older conversation turns and relocates bulky tool results to storage, leaving a short reference behind&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sessions&lt;/td&gt;
&lt;td&gt;Every conversation is persisted to &lt;code&gt;./.agent/sessions&lt;/code&gt; by default, resumable by session id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-term memory&lt;/td&gt;
&lt;td&gt;Distills user preferences and project facts across conversations, saved as markdown and folded back into context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;System prompt, tool definitions, and history are cached automatically wherever the provider supports it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3. A flexible model-selection syntax&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A single &lt;code&gt;provider/model&lt;/code&gt; string switches between providers using consistent syntax; reasoning effort is also unified into one set of levels (&lt;code&gt;off&lt;/code&gt;/&lt;code&gt;minimal&lt;/code&gt;/&lt;code&gt;low&lt;/code&gt;/&lt;code&gt;medium&lt;/code&gt;/&lt;code&gt;high&lt;/code&gt;/&lt;code&gt;xhigh&lt;/code&gt;/&lt;code&gt;max&lt;/code&gt;), with the SDK mapping them to each provider's own parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;create_harness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic/claude-opus-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;create_harness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai/gpt-5.6-sol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;create_harness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bedrock/global.anthropic.claude-opus-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# the default
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Fine-grained control over built-in tools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;builtin_tools&lt;/code&gt; supports two shapes: a list &lt;strong&gt;pins&lt;/strong&gt; exactly the tools named, while a mapping &lt;strong&gt;edits&lt;/strong&gt; the default set — &lt;code&gt;False&lt;/code&gt; removes a tool, &lt;code&gt;True&lt;/code&gt; adds one, and a config dict both enables and configures it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;create_harness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builtin_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;                     &lt;span class="c1"&gt;# keep only read
&lt;/span&gt;&lt;span class="nf"&gt;create_harness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builtin_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subagent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;          &lt;span class="c1"&gt;# defaults minus subagent delegation
&lt;/span&gt;&lt;span class="nf"&gt;create_harness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builtin_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web_fetch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai/gpt-5-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;  &lt;span class="c1"&gt;# configure web_fetch's own model
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Two subagent delegation patterns&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The built-in &lt;code&gt;subagent&lt;/code&gt; tool lets the main agent hand a subtask off to a full Harness child that inherits the parent's model, tools, skills, and intervention policies; alternatively, &lt;code&gt;Agent.as_tool()&lt;/code&gt; wraps any dedicated agent into a tool — useful when you need a fixed role (like a "reviewer" agent that only reviews code). Both can coexist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Native MCP and web access support&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Point &lt;code&gt;mcp_servers&lt;/code&gt; at a standard &lt;code&gt;mcpServers&lt;/code&gt; config (a JSON file or an inline dict) and Harness connects each server, discovers its tools, and adds them to the tool list automatically; &lt;code&gt;web_fetch&lt;/code&gt; is on by default, &lt;code&gt;web_search&lt;/code&gt; turns on or off automatically depending on whether the model provider natively supports it, and can also be explicitly routed through Exa as a third-party search backend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Agent Skills and interventions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Drop &lt;a href="https://agentskills.io" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt; into &lt;code&gt;./.agent/skills&lt;/code&gt; and Harness loads them automatically; the &lt;code&gt;interventions&lt;/code&gt; parameter supports &lt;code&gt;"ask"&lt;/code&gt;, &lt;code&gt;"smart"&lt;/code&gt;, a custom policy string, or even a &lt;code&gt;.cedar&lt;/code&gt; policy file — a gate you can put in front of tool calls for approval or validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Advantages
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Hand-rolling an agent loop from scratch&lt;/th&gt;
&lt;th&gt;Using only the underlying SDK (strands-py/ts)&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Strands Harness&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time to first result&lt;/td&gt;
&lt;td&gt;Slow — reinventing infrastructure&lt;/td&gt;
&lt;td&gt;Moderate — loop and tool primitives already in place&lt;/td&gt;
&lt;td&gt;Fast — one call gets you a full default configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customizability&lt;/td&gt;
&lt;td&gt;Fully open&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High (the return value is still a native Agent — override anything)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context/session/memory management&lt;/td&gt;
&lt;td&gt;Build it yourself&lt;/td&gt;
&lt;td&gt;Wire it in yourself&lt;/td&gt;
&lt;td&gt;Built in by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model portability&lt;/td&gt;
&lt;td&gt;Build your own adapter layer&lt;/td&gt;
&lt;td&gt;Multi-provider support already exists at the SDK layer&lt;/td&gt;
&lt;td&gt;One string parameter switch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production readiness&lt;/td&gt;
&lt;td&gt;Depends on the team&lt;/td&gt;
&lt;td&gt;Depends on how much you fill in yourself&lt;/td&gt;
&lt;td&gt;Defaults are "benchmarked and tested" out of the box&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why choose this project?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Led by the AWS team, with active updates and issue response (837 open issues signals a real, actively-used user base)&lt;/li&gt;
&lt;li&gt;Consistent API design across Python and TypeScript — mixed-stack teams don't need two mental models&lt;/li&gt;
&lt;li&gt;A "layered but not locked in" philosophy — Harness gives you defaults, not a closed framework you're boxed into&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A Deeper Look
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Harness Actually Assembles
&lt;/h3&gt;

&lt;p&gt;The parameter list of &lt;code&gt;create_harness()&lt;/code&gt; reads like a checklist of "what a production-grade agent needs":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;create_harness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bedrock/global.anthropic.claude-opus-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# model provider and version
&lt;/span&gt;    &lt;span class="n"&gt;effort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                          &lt;span class="c1"&gt;# reasoning effort
&lt;/span&gt;    &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                      &lt;span class="c1"&gt;# domain instructions appended to the system prompt
&lt;/span&gt;    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                             &lt;span class="c1"&gt;# custom tools
&lt;/span&gt;    &lt;span class="n"&gt;mcp_servers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                       &lt;span class="c1"&gt;# MCP server configuration
&lt;/span&gt;    &lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                           &lt;span class="c1"&gt;# custom plugins
&lt;/span&gt;    &lt;span class="n"&gt;builtin_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                     &lt;span class="c1"&gt;# add/remove/edit the built-in tool set
&lt;/span&gt;    &lt;span class="n"&gt;background_tasks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                  &lt;span class="c1"&gt;# background task policy
&lt;/span&gt;    &lt;span class="n"&gt;builtin_plugins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;todos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# built-in feature plugins
&lt;/span&gt;    &lt;span class="n"&gt;caching&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                         &lt;span class="c1"&gt;# context caching
&lt;/span&gt;    &lt;span class="n"&gt;context_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;# context management policy
&lt;/span&gt;    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                           &lt;span class="c1"&gt;# session persistence
&lt;/span&gt;    &lt;span class="n"&gt;skills&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                            &lt;span class="c1"&gt;# skill loading
&lt;/span&gt;    &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                            &lt;span class="c1"&gt;# long-term memory
&lt;/span&gt;    &lt;span class="n"&gt;interventions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                     &lt;span class="c1"&gt;# tool-call intervention policy
&lt;/span&gt;    &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;agent_kwargs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logic behind this list is clear: every capability that a hand-rolled agent loop eventually grows into — lifecycle controls, context compression, session recovery, cross-conversation memory, approval gates — is already implemented with a benchmarked default, rather than being left for each user to rediscover on their own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context Management: Summarization Plus Externalized Storage
&lt;/h3&gt;

&lt;p&gt;The easiest thing to lose control of in a long-running task is the context window. Harness's default strategy is two-pronged: automatically summarize older conversation turns, while relocating bulky tool results (say, the large volume of file content returned by a search) to external storage, leaving only a short reference in the context. This means the context doesn't grow linearly no matter how many turns a task runs for — when an old result is actually needed, the agent can pull it back by reference instead of every turn carrying the full history.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subagent Delegation: Two Modes for Two Different Needs
&lt;/h3&gt;

&lt;p&gt;The project distinguishes between two delegation patterns, reflecting two different intents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The built-in &lt;code&gt;subagent&lt;/code&gt; tool&lt;/strong&gt;: the model autonomously decides to delegate a task to a full Harness child that inherits the parent's entire configuration (model, built-in tools, plugins, skills, intervention policies). The child can narrow the tools granted by the parent, but never widen them — a deliberate safety boundary. Delegation depth is bounded by default to prevent unbounded recursive delegation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A dedicated agent wrapped via &lt;code&gt;Agent.as_tool()&lt;/code&gt;&lt;/strong&gt;: suited for a fixed role with a single responsibility (like a &lt;code&gt;reviewer&lt;/code&gt; that only does code review). Each call runs from a fresh conversation, making it a clean, focused delegate rather than a shared session.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two can coexist: the built-in &lt;code&gt;subagent&lt;/code&gt; handles general-purpose "exploratory" delegation, while &lt;code&gt;as_tool()&lt;/code&gt; handles expert delegation with a fixed role and prompt. This distinction reflects two different understandings of "delegation" — one where the model judges when to hand off work itself, and one where the engineer pre-defines the role boundaries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-in Tools and Sandboxed Orchestration: &lt;code&gt;programmatic_tool_caller&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;One notable design choice in Harness's default tool set is &lt;code&gt;programmatic_tool_caller&lt;/code&gt; — a sandbox where the agent writes code to chain, loop over, and parallelize its other tools, instead of going through a full "model generates call parameters → executes → model reads the result → decides next step" reasoning cycle for every single tool call. For batch, mechanical operations — like "read 50 files and count how often a pattern appears" — this kind of orchestration is far more efficient than calling tools one at a time, and it saves on unnecessary model reasoning overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Model Portability Is Implemented
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;provider/model&lt;/code&gt; string is a thin alias mapping (&lt;code&gt;bedrock&lt;/code&gt;, &lt;code&gt;bedrock-mantle&lt;/code&gt;, &lt;code&gt;anthropic&lt;/code&gt;, &lt;code&gt;openai&lt;/code&gt;, &lt;code&gt;google&lt;/code&gt;, &lt;code&gt;ollama&lt;/code&gt;, &lt;code&gt;litellm&lt;/code&gt;); for any provider outside that list, you simply pass a &lt;code&gt;Model&lt;/code&gt; instance and the SDK uses it as-is. The &lt;code&gt;effort&lt;/code&gt; parameter follows the same philosophy: unified into one set of enumerated levels, with the SDK mapping them to each provider's own native parameter names — but if a requested level isn't offered by a given provider, the SDK raises rather than silently degrading, avoiding the hidden failure mode of "thinking you turned on high-effort reasoning when it silently didn't take effect."&lt;/p&gt;

&lt;h3&gt;
  
  
  Compared to Building From Scratch: Layered but Not Locked In
&lt;/h3&gt;

&lt;p&gt;Strands' design philosophy can be summarized as "layered but not locked in." Most agent frameworks give you either a set of fully free primitives that you have to assemble entirely yourself, or a tightly integrated black box that's hard to escape when you need custom behavior. Strands tries to have both: the Harness layer gives you a benchmarked, deploy-ready default configuration, but what it returns is a completely ordinary &lt;code&gt;Agent&lt;/code&gt; — meaning you can bypass Harness's defaults at any time and reach directly into the underlying SDK's hooks, intervention mechanisms, and custom session backends, without paying the cost of "escaping the framework" through an expensive rewrite.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Links and Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Official Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/strands-agents/harness-sdk" rel="noopener noreferrer"&gt;https://github.com/strands-agents/harness-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href="https://strandsagents.com" rel="noopener noreferrer"&gt;https://strandsagents.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;License&lt;/strong&gt;: Apache License 2.0&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Issue Tracker&lt;/strong&gt;: &lt;a href="https://github.com/strands-agents/harness-sdk/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Community&lt;/strong&gt;: &lt;a href="https://discord.gg/strands" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; — The tool-integration standard Strands natively supports&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://agentskills.io" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt; — The specification Harness's skill system follows&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/strands-agents/samples" rel="noopener noreferrer"&gt;Strands Samples&lt;/a&gt; — The official repository of practical examples&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A layered architecture&lt;/strong&gt;: the underlying SDK provides a fully customizable agent loop and tool primitives, while Harness on top provides a one-call, production-grade default configuration, with no gap between the two layers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defaults cover the common pain points of hand-rolled loops&lt;/strong&gt;: context management, session persistence, long-term memory, and caching optimizations all work out of the box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model portability is a first-class concern&lt;/strong&gt;: a unified &lt;code&gt;provider/model&lt;/code&gt; syntax and reasoning-effort levels let you switch model providers without rewriting business logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two subagent delegation patterns serve different intents&lt;/strong&gt;: the built-in &lt;code&gt;subagent&lt;/code&gt; supports model-driven autonomous delegation, while &lt;code&gt;Agent.as_tool()&lt;/code&gt; supports engineer-defined expert roles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Led by the AWS team, with strong activity&lt;/strong&gt;: 8,300+ Stars and 837 open issues indicate a project that's genuinely in use and actively iterated on&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Who This Is For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Teams about to write their own agent loop, but haven't yet gotten to sessions, memory, or context compression&lt;/strong&gt;: can skip straight past the "reinvent infrastructure" phase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams that need to stay flexible across multiple model providers&lt;/strong&gt;: the unified model-switching syntax reduces provider lock-in risk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developers wanting to quickly build a coding/ops agent that handles long-running, multi-step operations&lt;/strong&gt;: Harness's default tool set (shell, file operations, orchestration sandbox) covers most scenarios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams mixing Python and TypeScript stacks&lt;/strong&gt;: both SDKs keep a consistent API design, avoiding two separate mental models&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  One-Line Verdict
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Strands Agents doesn't wrap "building an agent" into a closed black box — it first honestly builds out the infrastructure that a hand-rolled loop would eventually need to add anyway, then lets you decide whether to start from it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>agents</category>
      <category>harness</category>
      <category>aws</category>
    </item>
    <item>
      <title>LLM-Driven Automated Testing Series (04): Web UI Automation — Midscene's Vision-Driven Scripting</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Fri, 25 Sep 2026 08:43:33 +0000</pubDate>
      <link>https://dev.to/wonderlab/llm-driven-automated-testing-series-04-web-ui-automation-midscenes-vision-driven-scripting-42em</link>
      <guid>https://dev.to/wonderlab/llm-driven-automated-testing-series-04-web-ui-automation-midscenes-vision-driven-scripting-42em</guid>
      <description>&lt;h2&gt;
  
  
  Two ways to write the same test step
&lt;/h2&gt;

&lt;p&gt;A traditional Playwright script needs a stable selector before it can click a button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-testid="submit-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line assumes a stable &lt;code&gt;data-testid&lt;/code&gt; actually exists on the page. If that button is a bare icon, drawn on a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;, or buried inside a cross-origin iframe, this approach breaks down entirely — with no semantic annotation, a selector has nothing to grab onto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Midscene&lt;/strong&gt;, open-sourced by ByteDance's web-infra team, answers this differently: skip the selector, hand the full screenshot to a vision model, and describe what to do in natural language:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aiTap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This maps directly onto &lt;strong&gt;Path 1: visual grounding&lt;/strong&gt; from the vocabulary built in the previous post (02) — no dependency on any structured interface the app exposes, purely "looking." This post digs into what that path actually looks like in a real, production-shaped testing framework.&lt;/p&gt;




&lt;h2&gt;
  
  
  Four core APIs, four levels of granularity
&lt;/h2&gt;

&lt;p&gt;Midscene isn't a single "control the page with natural language" black box — it splits different levels of need into four APIs with clearly separated responsibilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aiTap / aiInput   —— Atomic actions: precise, single-step "click" or "type"
      ↓
aiAct             —— Autonomous flow: hand a multi-step natural-language task
                     to the agent and let it plan its own execution
                     (e.g. "search and sort by price ascending")
      ↓
aiQuery           —— Structured extraction: "read" structured data off the page
                     (e.g. extract all item names and prices in the current cart)
      ↓
aiAssert          —— Visual assertion: verify whether "what a user would actually see"
                     matches expectations (e.g. is this button highlighted, is the layout broken)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The design intent behind this split: &lt;strong&gt;not everything in a test script should be handed off to autonomous agent decision-making.&lt;/strong&gt; Atomic actions (&lt;code&gt;aiTap&lt;/code&gt;/&lt;code&gt;aiInput&lt;/code&gt;) preserve the determinism and readability of traditional scripts, where every single step is explicitly specified by a human. The autonomous flow (&lt;code&gt;aiAct&lt;/code&gt;) is reserved for exploratory tasks where the number of steps isn't fixed in advance. This is fundamentally different from a pure Computer Use approach, where a single model decides every action end to end — Midscene lets the test engineer choose exactly where uncertainty gets introduced, rather than betting the entire test flow on the model's planning ability.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;aiAssert&lt;/code&gt; deserves a callout of its own: it directly addresses the &lt;strong&gt;oracle problem&lt;/strong&gt; from post 01. A lot of visual correctness can't be judged by pixel diffing or checking a DOM attribute — e.g., "does this button look disabled?" &lt;code&gt;aiAssert&lt;/code&gt; hands that judgment to the vision model as a semantic decision, instead of hardcoding a CSS class check. This idea gets expanded further in post 12 (visual and semantic regression testing).&lt;/p&gt;




&lt;h2&gt;
  
  
  Core implementation one: three model roles, not one model doing everything
&lt;/h2&gt;

&lt;p&gt;The previous section said &lt;code&gt;aiAct&lt;/code&gt; "hands a multi-step task to the agent to plan and execute on its own," but glossed over how that "planning" actually maps onto model calls. Midscene's answer is to split a single automation task across three roles (its Model Strategy), each of which can be bound to a different underlying model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Default model    —— Handles element locating, plus whatever workload
                     Planning/Insight don't take over — the base model
                     that carries most automation needs by default
Planning model    —— Enhances planning for complex goals, multi-step
                     tasks, and branching scenarios
Insight model     —— Enhances data extraction, assertion judgment,
                     and page understanding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key design choice is that these three roles &lt;strong&gt;can each be bound to a different underlying model&lt;/strong&gt; — the official docs put it as "users can layer Planning and Insight models on top of the Default model," letting each model play to whichever role it's actually good at: pair "planning" with a model that reasons better, pair "locating" with a model that's more precise visually, instead of forcing one general-purpose model to carry both entirely different capability demands at once.&lt;/p&gt;

&lt;p&gt;That flexibility isn't free, though. The official docs explicitly warn: "this collaboration extends Midscene's ability to handle complex tasks, but also increases task latency and token consumption." The recommended usage is therefore incremental: get things working with the Default model first, and only bring in a Planning or Insight model once you hit a clear capability bottleneck — not turn all three on by default.&lt;/p&gt;

&lt;p&gt;This role split is itself an engineering-level response to post 02's conclusion that "locating precision isn't the only variable": explicitly separating "planning" and "locating" as independent capability needs, each with its own model choice, makes it easier to optimize precisely against a bottleneck than a single model trying to cover both — at the cost of extra latency and call overhead for that flexibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core implementation two: aiAct's execution loop — when to replan, when to split out locating
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;aiAct&lt;/code&gt; doesn't just call the model once, get back a full step list, and execute it end to end. The official docs describe it as continuously using AI to plan the next action from the latest UI state, until the goal is complete — meaning &lt;strong&gt;it re-observes the current screen after every single step before deciding the next one&lt;/strong&gt;, rather than sticking rigidly to whatever plan got generated when the task started. The benefit: if a permission dialog pops up mid-flow or a loading delay shifts the layout, planning naturally adjusts from the latest state, without needing extra branch logic hand-written for "what if an unexpected dialog appears."&lt;/p&gt;

&lt;p&gt;This loop isn't infinite replanning, either — a &lt;code&gt;replanningCycleLimit&lt;/code&gt; (max replanning cycles) parameter backstops it, with a default that varies by model type (20 for standard models, 40 for UI-TARS, 100 for AutoGLM). Once that limit is exceeded, or an assertion embedded in the prompt fails, &lt;code&gt;aiAct&lt;/code&gt; throws immediately rather than continuing to retry indefinitely. This is the same design philosophy as post 03's "fail conservatively" idea: better to surface an error quickly for a human to judge than let a task that can't produce a result spin silently.&lt;/p&gt;

&lt;p&gt;By default, &lt;code&gt;aiAct&lt;/code&gt; handles "plan the next step" and "locate the target element" &lt;strong&gt;in a single model call&lt;/strong&gt;. But complex tasks sometimes need those two split into separate calls — that's what the &lt;code&gt;deepThink&lt;/code&gt; parameter does: turning it on splits planning and locating into two independent calls, trading more calls and latency for better stability on complex tasks.&lt;/p&gt;

&lt;p&gt;There's a historical wrinkle here worth clarifying, since it's an easy source of confusion: the parameter name &lt;code&gt;deepThink&lt;/code&gt; has meant two different things across different APIs. In &lt;code&gt;aiAct()&lt;/code&gt;, it has always been the "planning mode" switch; but in single-step methods like &lt;code&gt;aiTap&lt;/code&gt;, the older &lt;code&gt;deepThink&lt;/code&gt; meant "enhanced element locating." To resolve that ambiguity, Midscene renamed the "enhanced locating" concept to its own &lt;code&gt;deepLocate&lt;/code&gt; parameter, leaving &lt;code&gt;deepThink&lt;/code&gt; consistently meaning "planning mode" — and inside &lt;code&gt;aiAct()&lt;/code&gt;, &lt;code&gt;deepThink&lt;/code&gt; (whether to split out planning) and &lt;code&gt;deepLocate&lt;/code&gt; (whether to boost locating precision) &lt;strong&gt;coexist as two independent parameters&lt;/strong&gt;, not mutually exclusive ones.&lt;/p&gt;




&lt;h2&gt;
  
  
  Relationship to Playwright: augmentation, not replacement
&lt;/h2&gt;

&lt;p&gt;Midscene didn't reinvent a browser-control protocol — it wraps the existing Playwright &lt;code&gt;page&lt;/code&gt; object via &lt;code&gt;PlaywrightAgent&lt;/code&gt;. Everything Playwright already does (navigation, waits, traditional selector-based assertions) stays fully intact; Midscene's four AI APIs are simply layered on top. The same integration approach also supports Puppeteer.&lt;/p&gt;

&lt;p&gt;The practical implication: &lt;strong&gt;migration cost is incremental, not a rewrite.&lt;/strong&gt; An existing Playwright test suite can start by swapping &lt;code&gt;aiAssert&lt;/code&gt; into the handful of assertion points most likely to break on a UI redesign, leaving everything else unchanged — you don't need to rip out the whole framework just to introduce visual locating.&lt;/p&gt;

&lt;p&gt;Going further, Midscene turns this Agent API into a unified cross-platform interface: "the same API covers Web, Android, iOS, HarmonyOS, and desktop applications." That means a test engineer who's already internalized &lt;code&gt;aiTap&lt;/code&gt;/&lt;code&gt;aiQuery&lt;/code&gt;/&lt;code&gt;aiAssert&lt;/code&gt; doesn't need to learn a new API when switching to mobile or desktop automation — only the underlying screenshot-capture and event-injection implementation changes, not the call pattern. (Specific mobile projects, each with their own mobile-specific optimizations, get their own detailed treatment in posts 05-09.)&lt;/p&gt;




&lt;h2&gt;
  
  
  The caching mechanism: how the biggest pain point of visual approaches gets mitigated
&lt;/h2&gt;

&lt;p&gt;The previous post already covered the cost of Path 1 (visual grounding): &lt;strong&gt;every locating step requires a model inference pass — slower and more expensive than reading structured data.&lt;/strong&gt; If a test suite has hundreds of test cases, each with dozens of steps, and every single step makes a real vision-model call, that cost is untenable to run in CI.&lt;/p&gt;

&lt;p&gt;Midscene's answer is to &lt;strong&gt;cache two distinct kinds of output separately&lt;/strong&gt;, rather than lumping everything under a generic "cache the locate result":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Planning cache (for ai / aiAct)
  key   = the raw natural-language instruction the user wrote
  value = the execution plan the model returned (a sequence of concrete steps)

Locate cache (for aiLocate / aiTap and other single-step locating calls)
  key   = the locate prompt
  value = the matched element's XPath
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query-type operations (&lt;code&gt;aiQuery&lt;/code&gt;/&lt;code&gt;aiBoolean&lt;/code&gt;/&lt;code&gt;aiAssert&lt;/code&gt;) are explicitly excluded from caching altogether — because the whole point of those calls is to "read the page's actual current state," and caching a past judgment would be semantically wrong.&lt;/p&gt;

&lt;p&gt;The locate cache isn't "cache the XPath and trust it blindly." &lt;strong&gt;Every run first re-validates whether that XPath is still valid on the current page&lt;/strong&gt;, and invalidates it if either "the text content of the element at that same XPath has changed since it was cached" or "the page's DOM structure has changed relative to when it was cached." This is a deliberately strict matching policy — better to invalidate a bit too often and trigger a fresh locate than to click, on a page that's already changed, an element that merely looks visually similar but is no longer semantically the right one. The planning-cache side has a similarly conservative design: if a cached &lt;code&gt;aiAct&lt;/code&gt; plan fails during execution, Midscene &lt;strong&gt;falls back to the normal AI planning flow and clears the stale cache entry&lt;/strong&gt;; but the freshly-regenerated plan from that fallback &lt;strong&gt;doesn't get written back into the cache&lt;/strong&gt; under the original prompt, since it may not represent a complete, correct plan starting "from the task's original state."&lt;/p&gt;

&lt;p&gt;There's one more boundary condition worth calling out on its own: &lt;strong&gt;the locate cache is structurally unusable in rendering scenarios with unstable structure&lt;/strong&gt; — shapes drawn inside a Canvas have no corresponding DOM nodes, so there's naturally no XPath to cache; cross-origin iframes are inaccessible internally due to browser security policy; Closed Shadow DOM is entirely inaccessible from the outside; WebGL/dynamic SVG have inherently unstable DOM structure, so even a cached result would likely invalidate on the very next run. This lines up exactly with the point made at the top of this post: &lt;strong&gt;canvas/iframe/custom rendering are precisely the scenarios where a pure-visual approach has its core advantage — and also precisely the scenarios where the caching mechanism is structurally unavailable and every run genuinely needs a model call.&lt;/strong&gt; The cost savings caching offers and the pure-visual approach's core advantage scenarios sit in structural tension with each other — that isn't a design flaw in Midscene, it's an inevitable consequence of those interfaces lacking structured information in the first place.&lt;/p&gt;

&lt;p&gt;This design effectively downgrades "visual locating" from "must be used every single time" to "a fallback mechanism." Under normal conditions, tests run a cached, deterministic sequence of operations — with speed and cost essentially indistinguishable from a traditional selector-based script. Only when the UI genuinely changes and invalidates the cache does it fall back to the vision model for fresh locating — though in canvas/iframe and other structurally-missing scenarios, that "fallback mechanism" is in fact the only mechanism available. In a sense, this already carries a flavor of the "self-healing locators" topic covered in post 10 — the difference is that post 10's self-healing logic focuses on "how to recover after a locate failure," whereas this caching mechanism solves a different problem: "most of the time, locating shouldn't need to be triggered at all."&lt;/p&gt;




&lt;h2&gt;
  
  
  Real benchmark numbers
&lt;/h2&gt;

&lt;p&gt;Midscene's official site publishes three benchmark results, worth comparing against the ScreenSpot/OSWorld numbers from post 02:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AndroidWorld Benchmark      Pass@1 93.1%    Pass@3 97.4%
MobileWorld Benchmark       Pass@1 78.6% (92/117 passed)
AppControlBench Benchmark   Pass@1 96.7% (58 items passed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are all mobile benchmarks (AndroidWorld gets a full treatment in post 05), but there's a comparison worth flagging here: post 02 mentioned OSWorld's early baseline models achieving only a 12.24% task success rate, while Midscene achieves a 93.1% Pass@1 on AndroidWorld. This isn't saying Midscene's underlying model is dramatically stronger than what OSWorld tested — the task types and evaluation paradigms differ. Benchmarks like AndroidWorld/AppControlBench, purpose-built for mobile GUI agents, have more clearly-scoped tasks, and combined with dedicated planning + retry mechanisms (Pass@3 being 4 points higher than Pass@1 shows that allowing retries genuinely recovers some single-attempt locating failures), can hit success rates far above OSWorld's open-ended desktop tasks. This reinforces post 02's conclusion: &lt;strong&gt;end-to-end task success depends on more than grounding precision alone — planning, retries, and task-scope design matter just as much.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparing paths against Stagehand
&lt;/h2&gt;

&lt;p&gt;Midscene isn't the only open-source project in this space. &lt;strong&gt;Stagehand&lt;/strong&gt; (open-sourced by Browserbase) is a worthwhile comparison — it also layers a natural-language API (&lt;code&gt;act&lt;/code&gt;/&lt;code&gt;extract&lt;/code&gt;/&lt;code&gt;observe&lt;/code&gt;) on top of Playwright, but its underlying technical path leans more toward &lt;strong&gt;Path 2 from post 02: DOM/accessibility-tree semantic understanding&lt;/strong&gt; — it prioritizes structured information exposed by the browser for locating and action, with visual capability as a supplement rather than the core.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Primary locating method       Best fit                            Cost
──────────────────────────────────────────────────────────────────────────────────────────
Midscene            Pure vision (screenshot+model)  canvas/iframe/icon-only scenarios    Requires a model
                                                     lacking semantic annotation           inference call per
                                                                                            locate (mitigated by cache)
Stagehand           DOM/accessibility tree first,   Conventional web apps with            Weaker fallback than a
                    vision as backup                reliable structured info              pure-visual approach when
                                                                                            structure is missing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This comparison echoes post 02's core conclusion: &lt;strong&gt;the two paths aren't a matter of one being technically superior — they're trade-offs suited to different scenarios.&lt;/strong&gt; If the test target is a conventional web app with complete structural info and proper semantic annotation, a DOM-first approach (like Stagehand) is usually faster and cheaper. If the test target itself involves heavy canvas rendering, custom-rendered widgets, or needs one unified test logic maintained across Web/mobile/desktop, a pure-visual approach (like Midscene) has a clearer edge in generality.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Midscene takes the &lt;strong&gt;pure visual grounding path&lt;/strong&gt; — no dependency on selectors or semantic annotations, feeding screenshots directly to a vision model — which lets it cover canvas rendering, icon-only elements, and cross-origin iframes that traditional approaches can't locate at all&lt;/li&gt;
&lt;li&gt;The four core APIs (&lt;code&gt;aiTap&lt;/code&gt;/&lt;code&gt;aiInput&lt;/code&gt; for atomic actions, &lt;code&gt;aiAct&lt;/code&gt; for autonomous flows, &lt;code&gt;aiQuery&lt;/code&gt; for structured extraction, &lt;code&gt;aiAssert&lt;/code&gt; for visual assertions) let the test engineer choose exactly which layer to introduce uncertainty into, rather than betting the entire flow on the model's planning ability&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Default/Planning/Insight model-role split&lt;/strong&gt; lets "planning" and "locating" be bound to different models and tuned independently, at the cost of extra latency and token spend — the official recommendation is to adopt Planning/Insight incrementally, not enable all three by default&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aiAct&lt;/code&gt;'s execution loop &lt;strong&gt;replans from the latest UI state after every step&lt;/strong&gt;, rather than sticking to whatever plan was generated at the start; the &lt;code&gt;deepThink&lt;/code&gt; parameter controls whether planning and locating happen in one model call or two separate ones, trading extra calls for stability on complex tasks&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;caching mechanism&lt;/strong&gt; caches planning output (keyed on the raw instruction) and locate results (keyed on the locate prompt, valued as XPath) separately, with the locate cache enforcing strict invalidation on text or DOM-structure changes; it's structurally unusable in canvas/iframe and similar structure-missing scenarios — precisely where the pure-visual approach's core advantage and the cache mechanism's blind spot overlap&lt;/li&gt;
&lt;li&gt;Compared against Stagehand (DOM/accessibility-tree first), the two paths aren't a matter of one being better — they map onto two different scenarios: unreliable structural information versus complete, reliable structural information&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>autotest</category>
      <category>opensource</category>
      <category>bytedance</category>
      <category>llm</category>
    </item>
    <item>
      <title>One Open Source Project a Day (No. 190): AIO Sandbox — Browser, Shell, File, MCP, and VSCode Server in One Docker Container</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Wed, 23 Sep 2026 11:11:53 +0000</pubDate>
      <link>https://dev.to/wonderlab/one-open-source-project-a-day-no-190-aio-sandbox-browser-shell-file-mcp-and-vscode-server-1acb</link>
      <guid>https://dev.to/wonderlab/one-open-source-project-a-day-no-190-aio-sandbox-browser-shell-file-mcp-and-vscode-server-1acb</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"What an agent needs isn't five tools each doing their own thing — it's one environment where every tool can see the same file."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;190th&lt;/strong&gt; article in the "One Open Source Project a Day" series. Today's project is &lt;strong&gt;AIO Sandbox&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Setting up an execution environment for AI agents runs into a small but persistent annoyance: browser automation lives in one container, code execution in another sandbox, file storage in a third service. Passing files and state between them means hand-writing glue code — a file downloaded in the browser can't be read by a shell script without an extra hop; results generated in Jupyter need to be manually relocated before a browser tool can use them. Every new tool you add means another round of custom integration work.&lt;/p&gt;

&lt;p&gt;AIO Sandbox (full project name agent-infra/sandbox) is built to solve exactly this "tool island" problem. It packs browser automation, a shell terminal, file operations, MCP servers, and VSCode Server into &lt;strong&gt;a single Docker container&lt;/strong&gt;. Its core selling point is a unified filesystem shared across all tools — a file downloaded in the browser is immediately usable via shell commands or file APIs, with zero relocation step in between.&lt;/p&gt;

&lt;p&gt;6.0k Stars, Apache-2.0 License, with working integrations verified against Browser Use, LangChain, OpenAI Assistants, and MiniMax.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Will Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AIO Sandbox's layered architecture: browser/VNC layer, dev tools layer, MCP integration layer, infrastructure layer&lt;/li&gt;
&lt;li&gt;How the "unified filesystem" eliminates data-relocation costs between tools&lt;/li&gt;
&lt;li&gt;The four built-in MCP servers: browser, file, shell, and markitdown&lt;/li&gt;
&lt;li&gt;How to deploy it three ways: Docker, Docker Compose, and Kubernetes&lt;/li&gt;
&lt;li&gt;How it actually integrates with frameworks like Browser Use, LangChain, and OpenAI Assistants&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Familiarity with basic Docker operations (run, compose)&lt;/li&gt;
&lt;li&gt;Basic understanding of MCP (Model Context Protocol)&lt;/li&gt;
&lt;li&gt;Optional: basic understanding of Chrome DevTools Protocol (CDP) and browser automation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;AIO Sandbox's official positioning: "an all-in-one agent sandbox environment that unifies Browser, Shell, File, MCP operations, and VSCode Server in a single Docker container," built on cloud-native lightweight sandbox technology. The project documentation directly names the pain point of traditional sandboxes: "traditional sandboxes are typically single-purpose (browser, code, or shell), making file sharing and functional coordination extremely challenging" — AIO Sandbox is the unified answer to that exact pain point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Team and Background
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organization&lt;/strong&gt;: agent-infra&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: Apache License 2.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: Docker container (Kubernetes also supported)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Official SDKs&lt;/strong&gt;: Python (&lt;code&gt;agent-sandbox&lt;/code&gt;), TypeScript/JavaScript (&lt;code&gt;@agent-infra/sandbox&lt;/code&gt;), Go (&lt;code&gt;agent-sandbox-sdk-go&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;6,000+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: &lt;strong&gt;534&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;👀 Watchers: &lt;strong&gt;32&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📄 License: Apache-2.0&lt;/li&gt;
&lt;li&gt;📊 Commits: 130 (main branch)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem It Solves
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Building an agent environment without AIO Sandbox:
  Browser automation → container A (Playwright/Puppeteer)
  Code execution     → container B (Jupyter/Sandbox Fusion)
  File storage       → a separate file service C
  ↑ the three don't talk to each other — files downloaded by the browser
    need manual relocation before container B can use them
  ↑ every new tool integrated means rewriting cross-container data-passing
    logic from scratch

AIO Sandbox's approach:
  Browser + Shell + File + MCP + VSCode all packed into one container
  ↓ a unified filesystem as the shared underlying layer
  Browser downloads a file → shell reads it directly → Jupyter processes
  it directly → file API writes it back directly
  ↑ every tool sees the same filesystem, no relocation needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AI agent tool execution and browser automation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An agent needs to open web pages, click through interactions, scrape content, and also execute code to process what it scraped&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Web-page-to-Markdown processing pipelines&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The official example: use Playwright over CDP to scrape a page → convert HTML via Jupyter's &lt;code&gt;markdownify&lt;/code&gt; → read the result back through the file API, all inside the same container&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Agent development that needs visual debugging&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watch browser automation execute live through a VNC remote desktop, or debug sandbox code directly via VSCode Server&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A unified execution backend for multiple framework integrations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serve as the shared execution backend for Browser Use, LangChain, OpenAI Assistants, and other frameworks, instead of building a separate execution environment for each&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;One-command Docker launch:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--security-opt&lt;/span&gt; &lt;span class="nv"&gt;seccomp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unconfined &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;SANDBOX_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-secret-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:8080:8080 ghcr.io/agent-infra/sandbox:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once running, access:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/v1/docs&lt;/code&gt; — API documentation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/vnc/index.html&lt;/code&gt; — VNC remote desktop&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/code-server/&lt;/code&gt; — VSCode Server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/mcp&lt;/code&gt; — MCP service entry point&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users in mainland China can substitute an alternate mirror registry for &lt;code&gt;ghcr.io&lt;/code&gt;. For production, use a pinned version tag (e.g., &lt;code&gt;1.11.0&lt;/code&gt;) instead of &lt;code&gt;latest&lt;/code&gt; to keep deployments reproducible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install the SDK:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Python&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;agent-sandbox

&lt;span class="c"&gt;# TypeScript/JavaScript&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @agent-infra/sandbox

&lt;span class="c"&gt;# Go&lt;/span&gt;
go get github.com/agent-infra/sandbox-sdk-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Unified Filesystem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Shared across the browser, shell, file operations, and Jupyter — the core design of the entire project, eliminating the glue code needed to pass files between tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Four Built-in MCP Servers&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MCP Server&lt;/th&gt;
&lt;th&gt;Core Capabilities&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;browser&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;navigate, screenshot, click, type, scroll&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;file&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;read, write, list, search, replace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;shell&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;exec, create_session, kill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;markitdown&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;convert, extract_text, extract_images&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3. Multiple Access Interfaces&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;VNC (remote desktop), VSCode Server, Jupyter Notebook, WebSocket terminal — the same sandbox can be accessed in whatever way best fits the current task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Core REST API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Endpoints like &lt;code&gt;/v1/sandbox&lt;/code&gt;, &lt;code&gt;/v1/shell/exec&lt;/code&gt;, &lt;code&gt;/v1/file/read&lt;/code&gt;, &lt;code&gt;/v1/file/write&lt;/code&gt;, &lt;code&gt;/v1/browser/screenshot&lt;/code&gt;, and &lt;code&gt;/v1/jupyter/execute&lt;/code&gt; cover the common execution needs of an agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Three Deployment Options&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Docker (single command)&lt;/td&gt;
&lt;td&gt;Local development, quick trials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Compose&lt;/td&gt;
&lt;td&gt;Scenarios needing volume persistence (with &lt;code&gt;shm_size: "2gb"&lt;/code&gt; and similar config)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;Production cluster deployment (with resource limits like &lt;code&gt;memory: "2Gi"&lt;/code&gt;, &lt;code&gt;cpu: "1000m"&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;6. Authentication Mechanism&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enabled via the &lt;code&gt;SANDBOX_API_KEY&lt;/code&gt; environment variable, supporting header, Bearer token, or query-parameter auth uniformly across API, JupyterLab, and VNC access; leaving it unset keeps services open (for backward compatibility).&lt;/p&gt;




&lt;h2&gt;
  
  
  A Deeper Look
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Layered Architecture: From Visualization Down to Infrastructure
&lt;/h3&gt;

&lt;p&gt;AIO Sandbox's architecture can be understood as a four-layer stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser + VNC layer (visualization)
  ↓
VSCode Server + Shell terminal + File operations layer (dev layer)
  ↓
MCP Hub + Sandbox Fusion layer (integration layer)
  ↓
Preview Proxy + Service monitoring layer (infrastructure layer)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All layers share the same container and the same filesystem. The key insight in this design: &lt;strong&gt;it doesn't stitch multiple independent services together over a network interface — it runs them all within the same process environment and filesystem namespace&lt;/strong&gt;. This eliminates the serialization overhead and consistency problems that come with cross-container/cross-service communication — there's no need to solve the classic distributed-systems question of "when will the shell container see the file the browser container just wrote."&lt;/p&gt;

&lt;h3&gt;
  
  
  The Real Pain Point the Unified Filesystem Solves
&lt;/h3&gt;

&lt;p&gt;Look at the typical workflow the official docs describe, and the design choice becomes concrete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Use Playwright over CDP, connected to the sandbox's browser, to scrape a page
2. The page content → written directly to the sandbox filesystem
3. Call Jupyter's markdownify inside the sandbox to convert HTML to Markdown
4. The converted result → read back directly through the file API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a traditional multi-container setup, steps 2 and 3 require a cross-container file transfer — maybe a volume mount, an object-storage relay, or an API call carrying the file content as payload. In AIO Sandbox, this step is just an ordinary file write and read within the same filesystem — no extra transport layer, no extra failure point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why MCP as the Unified Access Protocol Matters
&lt;/h3&gt;

&lt;p&gt;AIO Sandbox chose to wrap its browser, file, shell, and markitdown tools in the MCP protocol rather than defining its own bespoke REST API spec for each. The benefit: any agent framework that already supports an MCP client (Claude Code, or any other MCP-compatible tool) can plug into the sandbox's full capability set with "zero configuration" — no adapter layer needs to be written specifically for AIO Sandbox. Compared to the traditional pattern where "every sandbox defines its own private API, and integrating a new tool means writing a new adapter layer," this eliminates a large amount of repeated integration work.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Framework Integrations Actually Work
&lt;/h3&gt;

&lt;p&gt;The README shows several typical framework integrations, and the pattern reveals the sandbox's design intent: to be &lt;strong&gt;called as an execution backend&lt;/strong&gt;, not to become its own agent orchestration framework:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Integration Method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;browser-use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Connect a &lt;code&gt;BrowserSession&lt;/code&gt; to the sandbox's CDP endpoint, reusing the browser instance already running inside the sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LangChain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wrap &lt;code&gt;shell.exec_command&lt;/code&gt; in a custom &lt;code&gt;BaseTool&lt;/code&gt; subclass and plug it into the tool chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenAI Assistants / Function Calling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Expose a &lt;code&gt;run_code&lt;/code&gt; tool that routes to the sandbox's internal Jupyter or Node.js execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MiniMax&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Call via the OpenAI-compatible API (&lt;code&gt;base_url="https://api.minimax.io/v1"&lt;/code&gt;), noting that MiniMax requires &lt;code&gt;temperature &amp;gt; 0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This "content to be an execution layer, not competing for the orchestration seat" positioning means it can be grafted broadly onto existing agent ecosystems without requiring users to give up an orchestration framework they're already using.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Compares to Traditional Single-Purpose Sandboxes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Traditional Single-Purpose Sandbox (pure browser/pure code execution)&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;AIO Sandbox&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tool coverage&lt;/td&gt;
&lt;td&gt;Usually covers one category of capability&lt;/td&gt;
&lt;td&gt;Browser+Shell+File+MCP+VSCode all-in-one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-tool file sharing&lt;/td&gt;
&lt;td&gt;❌ Requires manual relocation/extra relay layer&lt;/td&gt;
&lt;td&gt;✅ Unified filesystem, natively shared&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access protocol&lt;/td&gt;
&lt;td&gt;Each defines its own API&lt;/td&gt;
&lt;td&gt;✅ Standardized MCP protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visual debugging&lt;/td&gt;
&lt;td&gt;Depends on the sandbox type, often missing&lt;/td&gt;
&lt;td&gt;✅ Dual VNC + VSCode Server channels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment options&lt;/td&gt;
&lt;td&gt;Depends on the project&lt;/td&gt;
&lt;td&gt;✅ Three tiers: Docker / Compose / K8s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Positioning&lt;/td&gt;
&lt;td&gt;A standalone tool&lt;/td&gt;
&lt;td&gt;Content to be an execution backend, compatible with mainstream agent frameworks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Project Links and Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Official Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/agent-infra/sandbox" rel="noopener noreferrer"&gt;https://github.com/agent-infra/sandbox&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;License&lt;/strong&gt;: Apache License 2.0&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Issues&lt;/strong&gt;: &lt;a href="https://github.com/agent-infra/sandbox/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Community&lt;/strong&gt;: GitHub Discussions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; — The standard protocol AIO Sandbox uses to wrap its tool capabilities&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chromedevtools.github.io/devtools-protocol/" rel="noopener noreferrer"&gt;Chrome DevTools Protocol&lt;/a&gt; — The underlying protocol behind the sandbox's browser automation capability&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/browser-use/browser-use" rel="noopener noreferrer"&gt;Browser Use&lt;/a&gt; — A browser automation framework with a verified integration into AIO Sandbox&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A unified filesystem is the core design&lt;/strong&gt;: eliminates the file-relocation cost between the browser, shell, and code execution, with all tools sharing the same filesystem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same-container architecture instead of cross-container orchestration&lt;/strong&gt;: browser, shell, file, MCP, and VSCode all run inside the same process environment, avoiding distributed consistency problems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP as the unified access protocol&lt;/strong&gt;: all four built-in tools (browser/file/shell/markitdown) are exposed via MCP, compatible with the existing MCP client ecosystem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content to be an execution backend&lt;/strong&gt;: its integrations with Browser Use, LangChain, OpenAI Assistants, and MiniMax are all about "being called," not "replacing" — it doesn't compete with existing orchestration frameworks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three deployment tiers cover different scenarios&lt;/strong&gt;: a complete deployment path from a single local command to a Kubernetes production cluster&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Who This Is For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI agent developers&lt;/strong&gt;: who need one unified execution environment instead of building separate infrastructure for browser automation, code execution, and file storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams using multiple frameworks&lt;/strong&gt;: already using LangChain, Browser Use, and similar tools, and want a shared execution backend instead of reinventing it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scenarios needing visual debugging&lt;/strong&gt;: watch an agent's execution live via VNC/VSCode Server instead of only reading logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Users of the MCP ecosystem&lt;/strong&gt;: who want an out-of-the-box set of MCP servers covering browser/file/shell without maintaining their own&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  One-Line Verdict
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;AIO Sandbox isn't trying to build a smarter agent framework — it's solving a more foundational problem first: making sure the tools an agent uses can at least see the same filesystem.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>sandbox</category>
      <category>langchain</category>
      <category>mcp</category>
    </item>
    <item>
      <title>LLM-Driven Automated Testing Series (03): Unit Test Generation — TestGen-LLM and Qodo Cover</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Wed, 23 Sep 2026 11:10:45 +0000</pubDate>
      <link>https://dev.to/wonderlab/llm-driven-automated-testing-series-03-unit-test-generation-testgen-llm-and-qodo-cover-4mp7</link>
      <guid>https://dev.to/wonderlab/llm-driven-automated-testing-series-03-unit-test-generation-testgen-llm-and-qodo-cover-4mp7</guid>
      <description>&lt;h2&gt;
  
  
  Where "just write some tests" goes wrong
&lt;/h2&gt;

&lt;p&gt;Hand a function to an LLM and ask it to "generate unit tests to improve coverage," and the most common outcome isn't tests that are merely mediocre — it's tests that &lt;strong&gt;look fine but test nothing at all&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_calculate_discount&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_discount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# this assertion will never fail
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_process_order&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# doesn't even compile: the mock's return type doesn't match the real interface
&lt;/span&gt;    &lt;span class="n"&gt;mock_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These tests have coverage numbers attached to them — sometimes they even make the coverage report look "better" — but they provide zero protection against real regressions. &lt;code&gt;assert result is not None&lt;/code&gt; will never fail, which means it asserts nothing. Worse still is a second failure mode: LLM-generated tests sometimes don't even compile or run, and dropping them straight into CI just manufactures new red X's.&lt;/p&gt;

&lt;p&gt;Meta's 2024 paper "Automated Unit Test Improvement using Large Language Models at Meta" tackles exactly this problem, with a tool called &lt;strong&gt;TestGen-LLM&lt;/strong&gt;. Its core idea isn't "train a smarter model to write tests" — it's the opposite: &lt;strong&gt;assume the LLM's output is probably garbage, build a mechanical filter pipeline that discards anything substandard, and keep only the small fraction that can prove its own value.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  TestGen-LLM's four-layer filter
&lt;/h2&gt;

&lt;p&gt;The paper calls this approach &lt;strong&gt;Assured Offline LLM-Based Software Engineering (Assured LLMSE)&lt;/strong&gt;: the LLM is just one generation step in a pipeline, and its output is never trusted directly — it has to pass a series of verifiable checks, and only what clears every check gets recommended to a human. That word "assured" is the key distinction from "just use the model's output as the result."&lt;/p&gt;

&lt;p&gt;Concretely, this is a four-stage cascade of elimination:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM generates a candidate test
      ↓
① Build Filter
   The candidate code must fully compile within the app's existing build infrastructure
   Fails to build → discarded immediately
      ↓
② Pass Filter (first execution)
   A test that compiles must pass on its first run
   — this step doesn't try to judge "did the failure reveal a real bug,"
     because there's currently no automated way to distinguish
     a real bug from a bad assertion, so the conservative default is:
     fails on first run → discard
      ↓
③ Flakiness Filter (stability)
   Run the test 5 times in a row — it must pass every single time
   Even one failure out of five → deemed flaky, discarded
      ↓
④ Coverage Filter
   Must prove it actually increases code coverage
   Fails to improve coverage → discarded, even if it passed the first three filters
      ↓
Only tests that survive all four layers get recommended to an engineer for review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The design philosophy is &lt;strong&gt;narrow the funnel at every stage — it's better to lose a valuable test than let a garbage test through.&lt;/strong&gt; Layer three is particularly worth noting — running a test five times to filter flakiness is a technique Qodo Cover's roadmap later borrowed directly (more on that below).&lt;/p&gt;

&lt;p&gt;One point worth clarifying, since it's easy to get wrong: &lt;strong&gt;mutation testing is not part of TestGen-LLM's filter pipeline.&lt;/strong&gt; The paper does mention that mutation coverage would theoretically be the best-performing criterion, but it explicitly lists this as future work, noting that "it is challenging to deploy such computationally demanding techniques at the scale we would require." In other words, the mutation-testing idea is directionally right, but too expensive to run in practice — so this version relies on the much lighter "did coverage improve" signal instead of the more precise but far costlier "were the mutants of each line actually killed."&lt;/p&gt;




&lt;h2&gt;
  
  
  Real numbers: how this performed inside Meta
&lt;/h2&gt;

&lt;p&gt;The paper reports two sets of numbers — one from a lab-style evaluation, one from real engineer usage — and the gap between them is itself informative.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lab evaluation: Instagram's Reels/Stories components
&lt;/h3&gt;

&lt;p&gt;Tested on 86 Kotlin components (31 Stories, 55 Reels):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;75%  of test classes: got at least one new test case that compiles correctly
57%  of test classes: compiles AND passes reliably (cleared the first three filters)
25%  of test classes: compiles, passes reliably, AND increases line coverage (cleared all four filters)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells a very practical story: &lt;strong&gt;there's a big drop-off between "can an LLM generate a test" and "can an LLM generate a test that's actually worth something."&lt;/strong&gt; Three-quarters of components got a test that "looks like it compiles," but only a quarter actually improved coverage — the middle three filters are precisely what's cutting away the "looks useful but isn't" output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-world usage: three test-a-thons
&lt;/h3&gt;

&lt;p&gt;More convincing than the lab numbers is data from three internal test-a-thons — sprint events where engineers actually reviewed the generated diffs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                        Diffs generated   Accepted by engineers   Acceptance rate
──────────────────────────────────────────────────────────────────────────────
Instagram (1st round)        17                   16                  94%
Instagram (2nd round)        42                   36                  86%
Facebook                    280                   144                 51% (~70% among reviewed diffs)
──────────────────────────────────────────────────────────────────────────────
Total: 196 out of 1,979 test classes were successfully improved (~10%)
       73% of TestGen-LLM's improvement suggestions were accepted for production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comparing the two datasets side by side: in the lab, roughly 25% of test classes "clear all four filters." In the real-world test-a-thons, 73% of improvements were accepted by engineers. These numbers don't contradict each other — the filters eliminate output that's mechanically substandard (doesn't build, unstable, doesn't improve coverage), while "engineer acceptance" measures whether a test that's &lt;strong&gt;already cleared the mechanical bar&lt;/strong&gt; is actually worth merging. In other words, the four filters solve "don't let garbage through" — they don't guarantee "everything generated is valuable." That judgment still needs a human.&lt;/p&gt;




&lt;h2&gt;
  
  
  The open-source implementation: how Qodo Cover turns this into a CLI tool
&lt;/h2&gt;

&lt;p&gt;TestGen-LLM itself is an internal Meta tool and was never open-sourced. &lt;strong&gt;Qodo Cover&lt;/strong&gt; (formerly Codium's Cover-Agent) is the first public open-source implementation of this same approach, runnable directly against your own project.&lt;/p&gt;

&lt;p&gt;Its architecture maps the paper's "filter" concept onto four concrete components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt Builder
   Gathers necessary context from the codebase (source file, existing tests,
   coverage report) and assembles it into a prompt for the LLM
        ↓
AI Caller
   Calls the LLM with the assembled prompt to generate a candidate test
   (via LiteLLM, so the underlying model provider is swappable)
        ↓
Test Runner
   Actually runs the project's test command against the newly generated test
        ↓
Coverage Parser
   Parses the coverage report (Cobertura/JaCoCo, etc.) and checks
   whether coverage genuinely increased
        ↓
   Passes → keep the test, move to next iteration
   Fails  → discard, generate a new candidate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is effectively a concrete implementation of TestGen-LLM's Pass Filter + Coverage Filter (the Build Filter is implicit in a failed Test Runner execution; a Flakiness Filter is on the roadmap, explicitly described as planned to "check test flakiness, e.g. by running 5 times as suggested by TestGen-LLM").&lt;/p&gt;

&lt;p&gt;Usage is a CLI-driven loop: specify the source file, test file, coverage-report path, a target coverage percentage (&lt;code&gt;--desired-coverage&lt;/code&gt;), and a max iteration count (&lt;code&gt;--max-iterations&lt;/code&gt;); the tool repeatedly generates candidate tests, runs them, and checks coverage until it hits the target or runs out of iterations. This loop can be wired straight into a CI pipeline, or run locally as a one-shot "bump this module's coverage" command.&lt;/p&gt;

&lt;p&gt;Worth flagging: Qodo Cover, the open-source project, is no longer actively maintained as of mid-2025 (the README explicitly marks it "no longer maintained" and suggests forking if you want to keep developing it). That doesn't diminish its value as a reference implementation of the TestGen-LLM approach, but it does raise a maintainability risk if you'd depend on it in production — a recurring theme in this series: open-source proof-of-concept projects and production-ready tools are often not the same thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this approach actually holds up
&lt;/h2&gt;

&lt;p&gt;Putting Meta's lab data and test-a-thon data together, you can map out where this method genuinely works and where it doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Works well&lt;/strong&gt;: modules with relatively self-contained logic and clear dependencies — the LLM can reasonably infer input/output relationships from function signatures and existing test style, and the four-layer filter effectively strips out most garbage output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Doesn't work well&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code that depends heavily on external state or complex mocks — LLM-generated mocks frequently don't match the real interface's actual behavior, which tends to get caught by the Build Filter or Pass Filter, resulting in a low survival rate&lt;/li&gt;
&lt;li&gt;Code that requires deep business context to write a meaningful assertion — a test might "run successfully" but assert something meaningless (like &lt;code&gt;assert result is not None&lt;/code&gt; in the opening example). The coverage filter only checks whether the code path was executed, not whether the assertion itself means anything — this is a blind spot the four-layer filter never addresses&lt;/li&gt;
&lt;li&gt;Scenarios demanding extreme determinism and reproducibility — the flakiness filter only runs five times, so statistically it can still miss a test that's fundamentally unstable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One point worth emphasizing: &lt;strong&gt;the four-layer filter guarantees that a test is mechanically valid — it does not guarantee that the test's assertions are semantically correct for the business logic.&lt;/strong&gt; Coverage improvement is an automatable proxy metric, but "did this test actually check the thing I care about" still needs human review. That's exactly why Meta's own data treats "cleared all filters" and "accepted by engineers" as two distinct numbers that each need to be examined separately.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;LLM-generated tests are probably garbage by default — they may not compile, assert something trivial, or contribute nothing to coverage. TestGen-LLM's fix isn't a smarter model, it's a &lt;strong&gt;four-layer mechanical filter&lt;/strong&gt; (build, first-run, stability, coverage) that eliminates substandard output stage by stage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutation testing is not currently part of TestGen-LLM's methodology&lt;/strong&gt; — the paper explicitly lists it as future work constrained by computational cost, and uses the much lighter "did coverage improve" signal instead&lt;/li&gt;
&lt;li&gt;The lab number (25% clear all filters) and the real-world number (73% accepted by engineers) measure two different things — the former is mechanical validity, the latter is human judgment of value, and the four-layer filter only solves the former&lt;/li&gt;
&lt;li&gt;The open-source implementation Qodo Cover turns this idea into a runnable CLI/CI iteration loop, but the project itself is no longer maintained — maintainability risk needs to be weighed before relying on it in production&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>llm</category>
      <category>autotest</category>
      <category>unittest</category>
    </item>
    <item>
      <title>LLM-Driven Automated Testing Series (02): The Unified Technical Foundation — Visual Grounding / DOM Semantics / Computer Use</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Tue, 22 Sep 2026 01:21:00 +0000</pubDate>
      <link>https://dev.to/wonderlab/llm-driven-automated-testing-series-02-the-unified-technical-foundation-visual-grounding-dom-5dcb</link>
      <guid>https://dev.to/wonderlab/llm-driven-automated-testing-series-02-the-unified-technical-foundation-visual-grounding-dom-5dcb</guid>
      <description>&lt;h2&gt;
  
  
  Same problem, three answers
&lt;/h2&gt;

&lt;p&gt;"Find the submit button on this page and click it."&lt;/p&gt;

&lt;p&gt;Hand that instruction to three different open-source GUI agent projects and you'll get three entirely different implementations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One feeds the full screenshot to a specially-trained visual grounding model, which directly outputs a coordinate like &lt;code&gt;(842, 613)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Another parses the page's DOM or accessibility tree, finds the node with &lt;code&gt;role=button, name="Submit"&lt;/code&gt;, and calls its click handler directly&lt;/li&gt;
&lt;li&gt;A third skips any dedicated locating step entirely — a general-purpose multimodal model just looks at the screenshot and decides "click here" on its own&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These three paths aren't interchangeable implementation details — they're distinct architectural decisions, each with clear trade-offs and boundaries. This post builds the vocabulary the next 12 case-study posts will reuse. Without it, every post would have to re-explain "which locating approach this project uses" from scratch.&lt;/p&gt;




&lt;h2&gt;
  
  
  Path 1: Pixel-Based Visual Grounding Models
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;This path treats the GUI as an image. Given a natural-language reference ("the submit button") and a screenshot, the model directly outputs the element's coordinates or bounding box on screen.&lt;/p&gt;

&lt;p&gt;This is a dedicated sub-task in the literature called &lt;strong&gt;GUI grounding&lt;/strong&gt;: it doesn't decide "what to do next" — it only maps a natural-language reference precisely onto screen coordinates. Representative open-source models include UGround, Aria-UI, and Alibaba's self-trained GUI-Owl (used in Mobile-Agent-v3, covered in a later post).&lt;/p&gt;

&lt;p&gt;The typical architecture is two-stage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User task: "Submit this form"
        ↓
Planning model (can be a general LLM): "Next step is to click the submit button"
        ↓
Visual grounding model (dedicated, e.g. UGround): "submit button" + screenshot → (842, 613)
        ↓
Execution layer: click at (842, 613)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Planning and grounding are two separate models, each specialized for one job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;: no dependency on any application-internal structured data — pure "looking," which makes it naturally cross-platform (web, mobile, desktop, even game UIs — anywhere you can take a screenshot). A key result from the UGround paper: this path beats accessibility-tree-dependent approaches (Path 2 below) by up to 20 absolute percentage points on multiple GUI grounding tasks, because real-world accessibility trees are often incomplete or unreliable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Costs&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every locating step requires a visual model inference pass — slower and more expensive than reading structured data directly&lt;/li&gt;
&lt;li&gt;Accuracy drops sharply for small elements and dense layouts (the ScreenSpot-Pro numbers below quantify how much)&lt;/li&gt;
&lt;li&gt;The grounding model itself needs dedicated training and maintenance — you can't get high accuracy "for free" from a general-purpose model&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Path 2: DOM / Accessibility-Tree Structured Understanding
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;This path doesn't look at pixels — it looks at structure. Web pages have a DOM tree; desktop and mobile apps have an accessibility tree (the semantic view the OS exposes for screen readers and other assistive tools). Serialize that tree into text and hand it to an LLM directly: "Given this structure, which node is the submit button?"&lt;/p&gt;

&lt;p&gt;Once the target node is found, subsequent actions don't need coordinates at all — you call the node's exposed &lt;code&gt;click()&lt;/code&gt;, &lt;code&gt;setValue()&lt;/code&gt;, etc. directly, the same mechanism traditional Selenium/Playwright scripts already use. The only thing that changes is that "finding the node" moves from a hardcoded selector to LLM-based semantic matching.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Structured data is text, so token cost is far lower than images, and it's faster&lt;/li&gt;
&lt;li&gt;Locating accuracy doesn't degrade with screen resolution or element size — if the node is in the tree, semantic matching is precise regardless&lt;/li&gt;
&lt;li&gt;Can reuse an existing automation framework's execution layer (Playwright, Appium) — the change is isolated to the "locating" step&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Costs&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Strongly dependent on the app exposing complete, reliable structured information.&lt;/strong&gt; Canvas-rendered content, embedded WebViews, and heavily custom-rendered mobile widgets often show up as an opaque black-box node in the accessibility tree with no semantic information at all — at that point this path simply fails and must fall back to a visual approach&lt;/li&gt;
&lt;li&gt;Real-world DOM/accessibility trees can be enormous (modern SPAs routinely have thousands of nodes); stuffing the whole thing into an LLM's context is expensive, requiring pruning or downsampling — this is exactly the problem work like "DOM Downsampling for LLM-Based Web Agents" sets out to solve&lt;/li&gt;
&lt;li&gt;Tree structures differ significantly across platforms (Web DOM, Android View tree, iOS UIKit tree), so one semantic-understanding pipeline doesn't transfer cleanly across platforms&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Path 3: Computer Use — Raw Coordinate Clicking
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;This is the most "brute-force," yet also the most commercially deployed path of the past two years: Anthropic's Computer Use and OpenAI's Operator/CUA both fall into this category.&lt;/p&gt;

&lt;p&gt;The key difference from Path 1: &lt;strong&gt;there's no separate "grounding model" step.&lt;/strong&gt; A general-purpose multimodal model takes the screenshot directly as input and directly outputs the next action — "click at (842, 613)," "type text," "press Tab" — perception and decision-making happen in a single pass by the same model. There's no "planning model + dedicated grounding model" division of labor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Screenshot → general multimodal model (perception + decision combined) → directly outputs: click(842, 613)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Simplest architecture — no separate grounding model to maintain, no platform-specific structured data to parse&lt;/li&gt;
&lt;li&gt;Because it has zero dependency on any interface the app exposes, it has, in principle, the highest ceiling on generality — "if you can screenshot it, you can operate it" — and it's the technical basis for turning an agent from "a web automation tool" into "a general assistant that operates a computer"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Costs&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Locating accuracy is the weakest of the three paths on public benchmarks — the numbers in the next section quantify this&lt;/li&gt;
&lt;li&gt;Highest latency and cost: every single action requires a full multimodal inference pass, and tasks often need many consecutive steps to complete — this is the most expensive path of the three to run&lt;/li&gt;
&lt;li&gt;No structured-data fallback: Path 2 can approach near-100% precision when the structure is reliable; Path 3 has no such ceiling guarantee — it's purely bounded by the model's visual grounding ability&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Quantifying the gap with benchmarks
&lt;/h2&gt;

&lt;p&gt;Saying "one path is better" without numbers is meaningless. A few public benchmarks let us compare the three paths on the same scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  The ScreenSpot family: testing "locating" in isolation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ScreenSpot&lt;/strong&gt; was the earliest GUI grounding benchmark: given a natural-language reference and a screenshot, the model must point to the target element's coordinates. Leading models already score above 90% accuracy on this benchmark.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ScreenSpot-Pro&lt;/strong&gt; is the hardened version: all screenshots come from real professional desktop software (CAD tools, IDEs, data-analysis apps), at higher resolution with smaller, denser interface elements. The same leading models drop to 37.1% (one of the highest scores reported by a 2025-generation model — most models score far lower).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Benchmark          What it measures              Leading model accuracy
─────────────────────────────────────────────────────────────────────
ScreenSpot          Locating in simple UIs         90%+
ScreenSpot-Pro       Locating in professional UIs   ~37% (best case)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That cliff-edge drop reveals a key fact: &lt;strong&gt;"locating accuracy" isn't a fixed property of a model — it collapses sharply as interface density and element size push in the wrong direction.&lt;/strong&gt; This is exactly why mobile automation (covered in five dedicated posts, 05-09) leans so heavily on dedicated grounding models — icons on a phone screen tend to be smaller and denser than buttons on desktop software.&lt;/p&gt;

&lt;h3&gt;
  
  
  OSWorld: testing end-to-end task success
&lt;/h3&gt;

&lt;p&gt;ScreenSpot only tests "can you find the element." &lt;strong&gt;OSWorld&lt;/strong&gt; tests whether a full task actually gets completed — executing a multi-step task in a real OS environment (e.g., "clean up this data in LibreOffice Calc and export it as a PDF"), which requires planning, locating, execution, and error recovery across the whole chain.&lt;/p&gt;

&lt;p&gt;The OSWorld paper reports a human baseline of &lt;strong&gt;72.36%&lt;/strong&gt; task completion, while early baseline models (e.g. GPT-4V) achieved only &lt;strong&gt;12.24%&lt;/strong&gt; — a gap of nearly 60 percentage points. The paper explicitly attributes the bottleneck to GUI grounding and operational knowledge, not task comprehension itself. That number has since closed rapidly as dedicated agent architectures (e.g. the Agent S series) improved — by 2025-2026, leading approaches reach roughly 65%-70%+, closing in on the human baseline, but that gain came from agent-architecture-level engineering — planning, reflection, retry loops — not purely from a better underlying grounding model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                     Human baseline    Early baseline model    2026 leading approaches
──────────────────────────────────────────────────────────────────────────────────
OSWorld task success   72.36%           12.24%                  ~65%-70%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The takeaway: &lt;strong&gt;grounding accuracy and task success rate are two different things.&lt;/strong&gt; A perfectly accurate grounding model still won't get you a high end-to-end success rate if the agent can't re-plan after a failure or verify whether its action achieved the intended effect. That's why "planning + reflection mechanisms" will be just as important a topic as "which grounding model was chosen" when we get to mobile automation projects like ARTEMIS and Mobile-Agent-v3.&lt;/p&gt;




&lt;h2&gt;
  
  
  The three paths aren't mutually exclusive
&lt;/h2&gt;

&lt;p&gt;In practice, mature open-source projects rarely bet purely on one path. The common pattern is &lt;strong&gt;layered fallback&lt;/strong&gt;: try Path 2 first (DOM/accessibility tree — fast and precise), and fall back to Path 1 or Path 3 (visual grounding) whenever reliable structured data isn't available. AppAgent, covered in a later post (08), is a textbook example of this "parser tree + visual features, dual input" design — the two paths aren't a mutually exclusive technology choice, but two tiers of fallback ranked by reliability within a single system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Concept vocabulary (reused across the series)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Referenced in&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GUI Grounding&lt;/td&gt;
&lt;td&gt;Mapping a natural-language reference to on-screen coordinates/elements&lt;/td&gt;
&lt;td&gt;04-09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visual grounding model&lt;/td&gt;
&lt;td&gt;A model specifically trained for GUI grounding (UGround, GUI-Owl)&lt;/td&gt;
&lt;td&gt;05, 07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accessibility tree / DOM semantic understanding&lt;/td&gt;
&lt;td&gt;Locating elements via structured views instead of pixels&lt;/td&gt;
&lt;td&gt;04, 08, 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Computer Use&lt;/td&gt;
&lt;td&gt;A general multimodal model directly outputting coordinate actions, no separate grounding stage&lt;/td&gt;
&lt;td&gt;09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ScreenSpot / ScreenSpot-Pro&lt;/td&gt;
&lt;td&gt;GUI grounding benchmarks that measure single-step locating accuracy only&lt;/td&gt;
&lt;td&gt;05-09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OSWorld / AndroidWorld&lt;/td&gt;
&lt;td&gt;End-to-end benchmarks measuring full multi-step task success rate&lt;/td&gt;
&lt;td&gt;05-09&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;There are three technical paths for GUI element locating: &lt;strong&gt;dedicated visual grounding models&lt;/strong&gt; (broadly cross-platform, but slow and expensive), &lt;strong&gt;DOM/accessibility-tree structured understanding&lt;/strong&gt; (fast and precise, but dependent on the platform exposing reliable structured data), and &lt;strong&gt;Computer Use raw coordinate clicking&lt;/strong&gt; (simplest architecture, highest generality ceiling, but the lowest locating-precision ceiling)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ScreenSpot → ScreenSpot-Pro cliff&lt;/strong&gt; (90%+ dropping to ~37%) shows that locating accuracy collapses sharply with interface density — it isn't a fixed model capability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OSWorld's human-vs-model gap&lt;/strong&gt; (72.36% vs. an early baseline of 12.24%) shows that end-to-end task success depends on more than grounding precision alone — planning and error-recovery mechanisms matter just as much&lt;/li&gt;
&lt;li&gt;Mature systems typically use &lt;strong&gt;layered fallback&lt;/strong&gt; rather than betting on a single path — this vocabulary will recur throughout every case study that follows&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>llm</category>
      <category>autotest</category>
      <category>guiagent</category>
      <category>ai</category>
    </item>
    <item>
      <title>One Open Source Project a Day (No. 189): NanoJev — A 0.6B Parallel Decision Model That Outputs Probability Distributions Instead of Generating Tokens</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Tue, 22 Sep 2026 01:19:41 +0000</pubDate>
      <link>https://dev.to/wonderlab/one-open-source-project-a-day-no-189-nanojev-a-06b-parallel-decision-model-that-outputs-22ao</link>
      <guid>https://dev.to/wonderlab/one-open-source-project-a-day-no-189-nanojev-a-06b-parallel-decision-model-that-outputs-22ao</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"A decision doesn't need to write a sentence to explain itself — it just needs a probability distribution."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;189th&lt;/strong&gt; article in the "One Open Source Project a Day" series. Today's project is &lt;strong&gt;NanoJev&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Most LLM-driven decision-making approaches can't escape an awkward efficiency problem: the model autoregressively generates a chunk of text (something like "I think we should choose option B, because..."), and the final decision has to be parsed out of that text afterward. This requires token-by-token decoding — slow, costly, and the "confidence" the model expresses is usually just an adjective it happened to use, not a genuinely calibrated probability.&lt;/p&gt;

&lt;p&gt;If a task's essence is really "given a state and a set of candidates, tell me the probability of each candidate," why make the model generate a wall of text first and parse it after? NanoJev's answer: skip the generation, output the distribution directly. It's a lightweight recreation of a parallel decision model system called "Jev." Built on a Qwen3-0.6B backbone with dedicated decision heads, it takes a state and a question as input and, in a single forward pass, outputs a complete probability distribution — zero output-token decoding.&lt;/p&gt;

&lt;p&gt;954 Stars, MIT License, validated with maze navigation and Snake game benchmarks.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Will Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;How NanoJev structures a decision as a "state + question + candidate set" triple&lt;/li&gt;
&lt;li&gt;Three decision head types: dynamic Choice, Boolean judgment, and Ordered Score&lt;/li&gt;
&lt;li&gt;Why "zero output-token decoding" fits decision-making better than autoregressive generation&lt;/li&gt;
&lt;li&gt;The full training pipeline: build queries → organize data → train → evaluate → serve&lt;/li&gt;
&lt;li&gt;How NanoJev compares against original Jev and an untuned Qwen3-0.6B on game benchmarks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of how LLMs run inference (autoregressive generation vs. a single forward pass)&lt;/li&gt;
&lt;li&gt;Familiarity with basic probability and classification/regression concepts&lt;/li&gt;
&lt;li&gt;Optional: familiarity with reward modeling concepts in reinforcement learning&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;NanoJev's official positioning: "A 0.6B parallel decision model. States and questions in, complete probability distributions out — with zero output-token decoding."&lt;/p&gt;

&lt;p&gt;Worth clarifying up front: this is not an image generation or editing tool. It's a &lt;strong&gt;structured decision model/reasoning framework&lt;/strong&gt; — essentially it keeps an LLM's language understanding ability while re-engineering the output side from "generate text" to "output a calibrated probability distribution," aimed at use cases like game agent decision-making and probability calibration research.&lt;/p&gt;

&lt;h3&gt;
  
  
  Team and Background
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Author&lt;/strong&gt;: TianyuCodings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: MIT License&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base model&lt;/strong&gt;: Qwen3-0.6B&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Related resources&lt;/strong&gt;: model repo &lt;a href="https://huggingface.co/C-Tianyu/NanoJev" rel="noopener noreferrer"&gt;C-Tianyu/NanoJev&lt;/a&gt;, dataset &lt;a href="https://huggingface.co/datasets/C-Tianyu/NanoJev-Data" rel="noopener noreferrer"&gt;C-Tianyu/NanoJev-Data&lt;/a&gt; (both on HuggingFace)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;954&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: &lt;strong&gt;127&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;👀 Watchers: &lt;strong&gt;8&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📄 License: MIT&lt;/li&gt;
&lt;li&gt;📊 Commits: 13&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem It Solves
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traditional LLM decision-making approach:
  State + question → autoregressively generate text ("I think we should
  pick B, because...")
  ↓ parse the text to extract the final decision
  ↑ token-by-token decoding is slow and costly
  ↑ "confidence" is just an adjective in the text, never truly calibrated

NanoJev's approach:
  State + question + candidate set → a single forward pass →
  directly output a complete probability distribution
  ↑ zero output-token decoding, fast
  ↑ the distribution is trained and calibrated, usable directly for
    ranking / greedy selection / probability sampling
  ↑ a single forward pass can batch multiple independent states and questions
     (official example: "6 states · 18 questions · 44 candidate paths ·
     1 backbone forward")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Game agent decision-making&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maze navigation (8×8 up to 50×50 in various sizes), real-time decisions for a Snake game agent&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Local safety judgment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast boolean judgments for scenarios like collision avoidance, combined with code-based planning&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Probability calibration research&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calibration experiments using CE (cross-entropy)/Brier loss and paired proper-reward learning&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scenarios needing batched decisions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Evaluate multiple candidate paths in a single forward pass, suited to applications needing high-throughput decision-making&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Simplest path: run the interactive demo locally (no model weights needed)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/TianyuCodings/NanoJev.git
&lt;span class="nb"&gt;cd &lt;/span&gt;NanoJev
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 8080 &lt;span class="nt"&gt;--bind&lt;/span&gt; 127.0.0.1 &lt;span class="nt"&gt;--directory&lt;/span&gt; web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;code&gt;http://127.0.0.1:8080/side-by-side.html&lt;/code&gt; for a three-panel comparison demo, or &lt;code&gt;arcade.html&lt;/code&gt; (dark arena demo) and &lt;code&gt;comparison.html&lt;/code&gt; (early benchmark viewer).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full run: download model weights and start the service&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements-toy.txt

&lt;span class="c"&gt;# Download model checkpoint and dataset&lt;/span&gt;
python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
from huggingface_hub import snapshot_download
snapshot_download(
    repo_id='C-Tianyu/NanoJev', local_dir='checkpoints/NanoJev',
    allow_patterns=['best.safetensors', 'config.json', 'tokenizer/*', 'backbone_config/*'],
)
snapshot_download(
    repo_id='C-Tianyu/NanoJev-Data', repo_type='dataset', local_dir='data/NanoJev',
)
"&lt;/span&gt;

&lt;span class="c"&gt;# Start the persistent service&lt;/span&gt;
python scripts/serve_decisions.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--checkpoint-dir&lt;/span&gt; checkpoints/NanoJev &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--web-root&lt;/span&gt; web &lt;span class="nt"&gt;--port&lt;/span&gt; 8765
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once started, the service loads the model once and accepts repeated batches of decision requests via &lt;code&gt;POST /api/evaluate&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Three Decision Head Types&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Range&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynamic Choice&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2–255 candidates&lt;/td&gt;
&lt;td&gt;A shared scalar head + set attention, outputs a probability for each candidate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Boolean&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A single proposition&lt;/td&gt;
&lt;td&gt;Single-path sigmoid, outputs the probability the proposition is true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ordered Score&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2–10 levels&lt;/td&gt;
&lt;td&gt;Computes a probability-weighted expectation over the ordered level distribution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2. Triple-Structured Decisions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every decision is defined by a &lt;strong&gt;state&lt;/strong&gt;, a &lt;strong&gt;question&lt;/strong&gt;, and a &lt;strong&gt;candidate set&lt;/strong&gt;. A shared decision head processes the input and returns the corresponding distribution output for the candidate set based on the decision type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Five-Step Training Pipeline&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build queries
  ↓ generate states, questions, candidate descriptions, and target distributions
Organize data
  ↓ keep related maps, rules, and variants in the same data split
Train
  ↓ Qwen3-0.6B init + decision-head warmup + complete-question distribution losses
Evaluate
  ↓ measure probability quality + game controller records actual actions
Serve and visualize
  ↓ reuse a persistent model endpoint, replay full trajectories in the browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Multiple Pretrained Checkpoint Variants&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variant Name&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;variants/local_atomic_seed17&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;50×50 maze demo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;variants/games_gold_seed17&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Snake demo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;variants/games_api_seed17&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full map comparison benchmark&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;variants/events_ce_seed17&lt;/code&gt; / &lt;code&gt;events_brier_seed17&lt;/code&gt; / &lt;code&gt;events_paired_seed17&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Calibrated decision experiments (different loss functions)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  A Deeper Look
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Performance Implications of "Zero Output-Token Decoding"
&lt;/h3&gt;

&lt;p&gt;NanoJev's core architectural decision is to abandon autoregressive generation entirely, re-engineering the LLM's output side into structured decision heads. The official documentation gives a fairly intuitive number for the performance difference this brings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"6 states · 18 questions · 44 candidate paths · 1 backbone forward"

Meaning:
  6 independent states + 18 questions + 44 candidate paths
  ↓ all folded into a single backbone forward pass
  ↓ instead of 44 separate autoregressive generations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cost of autoregressive generation accumulates token by token — generating a chunk of explanatory text might require tens to hundreds of forward passes (one per token). NanoJev compresses the evaluation of 44 candidate paths into "1 backbone forward pass" — essentially swapping "expressing confidence in text" for "expressing probability directly through the model's internal numerical output," bypassing the entire autoregressive decoding cost chain.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Design Trade-offs Behind Choice / Boolean / Score Heads
&lt;/h3&gt;

&lt;p&gt;Why not use one generic head for every decision type? The three head types actually correspond to three fundamentally different statistical problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Choice (dynamic selection):
  Variable candidate count (2-255) → needs "set attention"
  ↑ the model must understand "these candidates form a mutually
    exclusive set," not independent judgments

Boolean:
  A single proposition, binary yes/no → a single-path sigmoid is enough
  ↑ no need to be aware of other candidates

Score (ordered evaluation):
  Levels have an order relationship (1 star to 5 stars) → can't be
  treated as a plain classification problem
  ↑ using a probability-weighted expectation lets a non-integer
    expectation like "3.5 stars" emerge naturally
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design reflects a simple but easily overlooked principle: &lt;strong&gt;when decisions have different statistical structures, the loss function and output head should differ too&lt;/strong&gt;. Forcing "which option," "true or false," and "what rating" into the same softmax loses the structural information specific to each question type.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-World Training Results: Small Model vs. Fine-Tuning
&lt;/h3&gt;

&lt;p&gt;NanoJev was benchmarked against original Jev and an untuned Qwen3-0.6B across three game benchmarks, and the results are fairly compelling:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;th&gt;NanoJev&lt;/th&gt;
&lt;th&gt;Jev (original)&lt;/th&gt;
&lt;th&gt;Untuned Qwen3-0.6B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;50×50 maze (attempts/collisions)&lt;/td&gt;
&lt;td&gt;244 attempts / 36 collisions, goal reached&lt;/td&gt;
&lt;td&gt;2,738 attempts / 1,044 collisions, goal reached&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12×12 Snake (food eaten/alive)&lt;/td&gt;
&lt;td&gt;27 food, alive at horizon&lt;/td&gt;
&lt;td&gt;30 food, alive at horizon&lt;/td&gt;
&lt;td&gt;25 food, trapped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;40-map navigation benchmark (test/OOD)&lt;/td&gt;
&lt;td&gt;95% / 90%&lt;/td&gt;
&lt;td&gt;100% / 95%&lt;/td&gt;
&lt;td&gt;35% / 15%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few points worth noting:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The untuned Qwen3-0.6B lags significantly behind&lt;/strong&gt; (35%/15% vs. NanoJev's 95%/90%), showing that the purpose-trained decision heads deliver a real improvement — not just a rebranded model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NanoJev requires far fewer attempts than original Jev on the 50×50 maze&lt;/strong&gt; (244 vs. 2,738), suggesting the lightweight recreation is actually more efficient on this specific task, likely due to more focused training data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NanoJev's overall accuracy is slightly lower than original Jev&lt;/strong&gt; (e.g., 95% vs. 100% on the navigation benchmark), which is expected — recreating a system with fewer resources involves a reasonable trade-off in performance&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Positioning: A Research Recreation, Not a Commercial Product
&lt;/h3&gt;

&lt;p&gt;NanoJev explicitly positions itself as a "lightweight, reproducible" research alternative. That positioning is clear: it doesn't aim to outperform original Jev, but rather to let more researchers validate and extend the "parallel decision model" idea at a smaller training cost with a simpler deployment path. Roadmap items like expanding data scale and RLCD (contrastive distillation reinforcement learning) expansion all point toward "continuing to explore the boundaries of this architecture" rather than "polishing it into a product."&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Links and Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Official Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/TianyuCodings/NanoJev" rel="noopener noreferrer"&gt;https://github.com/TianyuCodings/NanoJev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🤗 &lt;strong&gt;Model&lt;/strong&gt;: &lt;a href="https://huggingface.co/C-Tianyu/NanoJev" rel="noopener noreferrer"&gt;C-Tianyu/NanoJev&lt;/a&gt; (HuggingFace)&lt;/li&gt;
&lt;li&gt;🤗 &lt;strong&gt;Dataset&lt;/strong&gt;: &lt;a href="https://huggingface.co/datasets/C-Tianyu/NanoJev-Data" rel="noopener noreferrer"&gt;C-Tianyu/NanoJev-Data&lt;/a&gt; (HuggingFace)&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;License&lt;/strong&gt;: MIT License&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Issues&lt;/strong&gt;: &lt;a href="https://github.com/TianyuCodings/NanoJev/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/QwenLM/Qwen3" rel="noopener noreferrer"&gt;Qwen3&lt;/a&gt; — The 0.6B backbone network NanoJev is built on&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Brier_score" rel="noopener noreferrer"&gt;Brier Score&lt;/a&gt; — One of the calibration scoring methods used in NanoJev's training&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero output-token decoding&lt;/strong&gt;: abandons autoregressive generation, using decision heads to output probability distributions directly, sidestepping the performance overhead of token-by-token decoding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triple-structured decisions&lt;/strong&gt;: state + question + candidate set clearly bounds every decision query&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three decision heads, each fit for purpose&lt;/strong&gt;: Choice/Boolean/Score correspond to distinct statistical problem structures rather than being forced into a single softmax&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real benchmark validation&lt;/strong&gt;: significant improvement over untuned Qwen3-0.6B on maze navigation and Snake game benchmarks, with a reasonable performance trade-off versus original Jev&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research-oriented positioning&lt;/strong&gt;: explicitly a lightweight recreation rather than a commercial product, serving further exploration of the "parallel decision model" direction&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Who This Is For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Game AI / agent decision-making researchers&lt;/strong&gt;: need a lightweight, reproducible baseline for parallel decision models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Researchers focused on LLM efficiency&lt;/strong&gt;: interested in architectural changes that skip autoregressive generation in favor of directly outputting structured distributions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Probability calibration researchers&lt;/strong&gt;: want to study the practical effect of CE/Brier loss and paired reward learning in decision-making scenarios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teaching/reproduction-oriented learning contexts&lt;/strong&gt;: want to understand a "decision head + backbone" architecture without training a large-scale system from scratch&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  One-Line Verdict
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;NanoJev raises a question worth sitting with: if a task's essence is producing a probability distribution, why detour through generating a paragraph of text at all?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>nanojev</category>
      <category>llm</category>
      <category>jev</category>
    </item>
    <item>
      <title>LLM-Driven Automated Testing Series (01): Why Testing Is LLM's New Battlefield</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Mon, 21 Sep 2026 03:06:25 +0000</pubDate>
      <link>https://dev.to/wonderlab/llm-driven-automated-testing-series-01-why-testing-is-llms-new-battlefield-509h</link>
      <guid>https://dev.to/wonderlab/llm-driven-automated-testing-series-01-why-testing-is-llms-new-battlefield-509h</guid>
      <description>&lt;h2&gt;
  
  
  A scenario every test engineer has lived through
&lt;/h2&gt;

&lt;p&gt;A product manager moves a button by a few pixels — barely noticeable to the eye. The next day, 40 tests fail red in CI simultaneously.&lt;/p&gt;

&lt;p&gt;After half an hour of digging, the culprit isn't broken business logic. It's a hardcoded CSS selector, &lt;code&gt;#submit-btn-v2&lt;/code&gt;, that can no longer find the element — the DOM structure changed and the id changed with it. The business code is entirely correct; the test script died first.&lt;/p&gt;

&lt;p&gt;This is one of the most common, most labor-draining, and least technically interesting failure modes in automated testing. It's also the starting point of this series: &lt;strong&gt;can LLMs turn this problem — and a few other long-standing ones in the testing world — from "brute-force human maintenance" into something that's actually engineered?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Three long-standing problems in traditional automated testing
&lt;/h2&gt;

&lt;p&gt;Before discussing what LLMs can do, let's name the problems clearly. None of these are new — the industry has debated them for over a decade without a cheap, general solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 1: Fragile Locators
&lt;/h3&gt;

&lt;p&gt;Automated test scripts have to "find" an element on the page before they can act on it — click a button, fill a form, read some text. The common ways to locate an element are CSS selectors, XPath, element IDs, or raw coordinates.&lt;/p&gt;

&lt;p&gt;All of them share the same fatal assumption: &lt;strong&gt;the UI structure won't change after the test script is written.&lt;/strong&gt; In reality, UI structure changes constantly — redesigns, A/B tests, a front-end framework rewrite that restructures the component tree. The more specific a script's dependency on UI structure, the more fragile it becomes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Selector specificity     Robustness     Maintenance cost
────────────────────────────────────────────────────────
#submit-btn-v2           Very low       Breaks on any UI tweak
.btn.primary              Low            Breaks on class refactor
//div[3]/button[1]        Very low       Breaks on DOM hierarchy change
Coordinates (320, 480)    Very low       Breaks on resolution/layout change
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The industry has a name for this: &lt;strong&gt;"selector hell."&lt;/strong&gt; The larger a test team grows, the higher the proportion of engineering time spent maintaining locators — not writing new tests, but fixing the locating logic of old ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 2: The Oracle Problem (how do you know if the result is right?)
&lt;/h3&gt;

&lt;p&gt;"Oracle" in testing refers to the standard used to judge whether an output is correct. A traditional unit test's oracle is explicit: &lt;code&gt;assert result == 42&lt;/code&gt;. But in many testing scenarios, the oracle itself is fuzzy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A screenshot differs from the last version by a few pixels — is that a bug,
  or just font-rendering jitter?
The returned JSON has an extra field — is that an API change,
  or dirty test-environment data?
The user flow completed, but the page "just doesn't look right" —
  hard to say exactly what's wrong
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The traditional fix is to make the oracle explicit: pixel-level diffing (what threshold counts as a real change), schema validation (which fields must exist), assertion chains (explicitly checking state at each step). The shared problem with these approaches: &lt;strong&gt;the stricter the oracle, the more false positives (flagging meaningless changes as failures); the looser the oracle, the more false negatives (real bugs slip through).&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 3: Maintenance Cost (test scripts age faster than the business code)
&lt;/h3&gt;

&lt;p&gt;This is a consequence of the first two problems, but it deserves its own callout because it's the direct reason teams eventually abandon automated testing.&lt;/p&gt;

&lt;p&gt;A real pattern: teams that iterate faster on features tend to end up with lower actual test coverage over time — not because nobody wants to write tests, but because tests go stale the moment they're written, nobody has time to fix them, and eventually they get &lt;code&gt;skip&lt;/code&gt;ped or ignored outright. The entropy of the test codebase grows faster than the team's capacity to maintain it.&lt;/p&gt;

&lt;p&gt;These three problems reinforce each other: fragile locators → tests fail red constantly → maintenance cost rises → the team has no time to maintain them → coverage gets cut → the oracle problem becomes harder to catch (because coverage itself is shrinking).&lt;/p&gt;




&lt;h2&gt;
  
  
  Why now: testing happens to be a "clear right-or-wrong" scenario
&lt;/h2&gt;

&lt;p&gt;For the past two years, LLM Agent adoption has stalled in the same place across many domains: &lt;strong&gt;how do you know the Agent got it right?&lt;/strong&gt; Writing code, making decisions, generating content — the "correctness" of these outputs is usually continuous and subjective, requiring a whole separate evaluation layer to judge (which is exactly what the &lt;code&gt;eval-series&lt;/code&gt; on this blog is about).&lt;/p&gt;

&lt;p&gt;Testing is the exception. A testing task carries its own explicit success signal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unit test passes / fails             —— boolean, no middle ground
UI element found / not found         —— boolean
Assertion holds / doesn't hold       —— boolean
Coverage increased by X percentage points —— a quantifiable number
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the quality of an LLM's output in a testing context can be verified using the test's own execution result, without needing an extra meta-layer of "is this LLM output reasonable." This is a structural advantage testing has over other LLM use cases — and it's why the past two years have produced a wave of open-source projects that actually work in production, not just proof-of-concept demos.&lt;/p&gt;

&lt;p&gt;That said, "clear right-or-wrong" only applies to the test's final execution result. Whether a test script should be generated, whether a locator should self-heal, whether a failure is a real bug or an environment flake — these judgments remain fuzzy. That's exactly where LLMs have room to intervene, not in the final "did the test pass" verdict itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three places where LLMs can plug in
&lt;/h2&gt;

&lt;p&gt;Coming back to the three problems from the opening: here's where LLMs can plug in for each.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fragile locators   → Semantic locating ("find the submit button" instead of "find #submit-btn-v2")
                    → Self-healing locators (when the UI changes, re-find the element
                      semantically instead of the script just dying)

Oracle problem      → Semantic judgment ("is this visual change an actual bug to the user")
                    → instead of pure pixel/structural comparison

Maintenance cost    → Coverage-driven automatic test generation
                    → Automatic failure classification (real bug / environment issue / test issue)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These three threads map to what the rest of the series will cover: unit test generation (03), Web/mobile UI automation (04-09), self-healing locators (10), API testing (11), visual regression (12), and failure classification (13).&lt;/p&gt;

&lt;p&gt;The next post (02) will first lay out the shared technical foundation behind all of these approaches — visual grounding, DOM-based semantic understanding, and pure coordinate-clicking Computer Use. Each has its own principles and trade-offs, and this vocabulary will be reused in every case study that follows.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this series won't do
&lt;/h2&gt;

&lt;p&gt;Setting expectations upfront:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;This is not a trend-report on "AI testing."&lt;/strong&gt; Each post picks one technical point paired with a verifiable open-source project — no "reportedly," no "may become possible in the future" claims that can't be checked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;This isn't about evaluating LLM output quality.&lt;/strong&gt; That's the &lt;code&gt;eval-series&lt;/code&gt;'s territory. This series is about "using LLMs to do testing," not "evaluating LLM outputs"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitations won't be glossed over.&lt;/strong&gt; Every technical approach comes with a discussion of where it breaks down and what it costs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fragile locators, the oracle problem, and maintenance cost&lt;/strong&gt; are the three long-standing problems in automated testing, and they reinforce each other&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing tasks carry an explicit success signal&lt;/strong&gt; (pass/fail) — this is testing's structural advantage over other LLM use cases&lt;/li&gt;
&lt;li&gt;LLMs plug in at semantic locating/self-healing, semantic oracle judgment, and automatic generation/classification — not at replacing the final "did the test pass" verdict itself&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>llm</category>
      <category>autotest</category>
      <category>agents</category>
      <category>ai</category>
    </item>
    <item>
      <title>One Open Source Project a Day (No. 188): ARTEMIS — Google's Open-Source Mobile AI Automation Framework, Let AI Assistants Operate Phones Like a Human</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Mon, 21 Sep 2026 02:01:12 +0000</pubDate>
      <link>https://dev.to/wonderlab/one-open-source-project-a-day-no-188-artemis-googles-open-source-mobile-ai-automation-3mkg</link>
      <guid>https://dev.to/wonderlab/one-open-source-project-a-day-no-188-artemis-googles-open-source-mobile-ai-automation-3mkg</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"A test script breaks the moment a button moves — an AI that actually understands the interface doesn't."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;188th&lt;/strong&gt; article in the "One Open Source Project a Day" series. Today's project is &lt;strong&gt;ARTEMIS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Mobile UI automation testing has long lived with a fundamental tension: traditional scripts based on element IDs or coordinates are fast, but extremely brittle — a small layout tweak breaks test cases en masse, and maintenance cost grows linearly with how fast the app iterates. Vision-model-driven approaches are more robust, but slow and expensive, making them a poor fit for large-scale regression testing.&lt;/p&gt;

&lt;p&gt;ARTEMIS is Google's attempt to find a balance between these two extremes. It turns natural-language instructions — like "open Settings, find the Battery option, tell me the current level" — into reliable Android automation, while offering two execution modes: one optimized for speed on routine deterministic tasks, another optimized for accuracy and verifiability on complex, long-running ones. More notably, it plugs directly into Claude Code, Antigravity, Codex, and other AI IDEs via MCP, giving AI coding assistants the ability to operate real phones.&lt;/p&gt;

&lt;p&gt;8.1k Stars, Apache-2.0, built in Python, achieving 99%+ task completion on the AndroidWorld benchmark.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Will Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;How ARTEMIS turns natural-language instructions into Android automation&lt;/li&gt;
&lt;li&gt;The architectural differences between the Flash and Pro execution modes, and when to use each&lt;/li&gt;
&lt;li&gt;Multi-modal target localization: combining the accessibility hierarchy, OCR, and vision models&lt;/li&gt;
&lt;li&gt;How it integrates with Claude Code and other AI IDEs via MCP&lt;/li&gt;
&lt;li&gt;How the Python SDK plugs into existing automated testing frameworks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of Android automation testing concepts (ADB, Accessibility services)&lt;/li&gt;
&lt;li&gt;Familiarity with Python async programming (asyncio)&lt;/li&gt;
&lt;li&gt;Optional: basic understanding of MCP (Model Context Protocol)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;ARTEMIS's official positioning: "ARTEMIS turns natural-language instructions into reliable Android automation" — letting AI assistants and test suites operate real phones like a human would.&lt;/p&gt;

&lt;p&gt;One clarification worth making up front: this is not a security testing or fuzzing tool. It's an intelligent agent system focused on &lt;strong&gt;mobile UI automation testing and everyday task execution&lt;/strong&gt;, squarely in the AI-driven mobile testing/automation framework space.&lt;/p&gt;

&lt;h3&gt;
  
  
  Team and Background
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organization&lt;/strong&gt;: Google&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: Apache License 2.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary language&lt;/strong&gt;: Python (requires Python 3.12+)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code provenance&lt;/strong&gt;: the project notes it includes source code developed by Minitap, Inc., building on the open-source mobile-use project&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;8,100+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: &lt;strong&gt;770+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;👀 Watchers: &lt;strong&gt;71&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📄 License: Apache-2.0&lt;/li&gt;
&lt;li&gt;🏆 Benchmark: 99%+ task completion on AndroidWorld (covering 20+ apps, 100+ multi-step tasks)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem It Solves
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traditional element-ID/coordinate-based automation scripts:
  UI layout shifts slightly → element lookup fails → tests break en masse
  ↑ Fast, but extremely brittle, high maintenance cost

Pure vision-model-driven approaches:
  Every step relies on a vision model to read the screen → accurate but slow
  ↑ Good robustness, but slow and expensive, hard to scale

ARTEMIS's approach:
  Natural-language instruction → multi-modal localization (element index first,
  vision/coordinates as fallback)
  ↓ Flash mode: routine deterministic tasks, 3-5 sec/step, fast reactive loop
  ↓ Pro mode: complex long-running tasks, 15-40 sec/step, plan-execute-verify workflow
  ↑ Choose the right mode for task complexity, balancing speed and reliability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mobile app end-to-end test automation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Describe test steps in natural language instead of maintaining brittle element-locator scripts&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bug reproduction and diagnosis&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Combined with Logcat log and screenshot capture, quickly reproduce and pinpoint issues&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Exploratory stability testing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pro mode supports long-running, continuously monitored exploratory testing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CI/CD-integrated automated testing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrate via the Python SDK into pytest and similar frameworks, folded into continuous integration pipelines&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AI IDE-driven real device operation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let Claude Code, Antigravity, and other AI coding assistants directly operate a phone to verify functionality&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Clone and one-command start:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/google/artemis.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;artemis

&lt;span class="c"&gt;# macOS/Linux one-command start (auto-detects and installs ADB, scrcpy, FFmpeg, etc.)&lt;/span&gt;
./start.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run a task directly via CLI:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run artemis run &lt;span class="s2"&gt;"Open Settings, find Battery and tell me current level"&lt;/span&gt; &lt;span class="nt"&gt;--profile&lt;/span&gt; flash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Install MCP integration for an IDE:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install for Antigravity&lt;/span&gt;
uv run artemis mcp &lt;span class="nt"&gt;--install&lt;/span&gt; antigravity

&lt;span class="c"&gt;# Install for all supported IDEs (including Codex)&lt;/span&gt;
uv run artemis mcp &lt;span class="nt"&gt;--install&lt;/span&gt; all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On first task execution, ARTEMIS installs an "Artemis Accessibility Helper" service on the device — it only reads the screen layout locally and uploads no data, and can be pre-installed, checked, or uninstalled via command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Four Ways to Use It&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Web visual test console (&lt;code&gt;artemis ui&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Interactive debugging and results review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP server&lt;/td&gt;
&lt;td&gt;Connecting an AI IDE to drive a real device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer CLI (&lt;code&gt;artemis run&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Automated testing or benchmark scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python SDK&lt;/td&gt;
&lt;td&gt;Integrating into existing test frameworks like pytest / CI pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2. Dual Execution Modes: Flash and Pro&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Flash Mode&lt;/th&gt;
&lt;th&gt;Pro Mode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Response speed&lt;/td&gt;
&lt;td&gt;~3-5 sec/step&lt;/td&gt;
&lt;td&gt;~15-40 sec/step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;Single-model observe-think-act reactive loop&lt;/td&gt;
&lt;td&gt;Multi-agent graph (Planner+Operator+Checker)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task planning&lt;/td&gt;
&lt;td&gt;No task plan&lt;/td&gt;
&lt;td&gt;Planner maintains a Markdown task plan with milestones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safety mechanism&lt;/td&gt;
&lt;td&gt;No pre-execution safety net&lt;/td&gt;
&lt;td&gt;Every action XML-verified first, pixel fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verification&lt;/td&gt;
&lt;td&gt;No checkpoint verification or final report&lt;/td&gt;
&lt;td&gt;Checker verifies plan checkpoints, supports a final review before exit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toolset&lt;/td&gt;
&lt;td&gt;No ADB shell&lt;/td&gt;
&lt;td&gt;Full toolset: Explorer, notes, history lookback, video analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Routine deterministic UI tasks&lt;/td&gt;
&lt;td&gt;100+ step long-running workflows, continuous monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3. Python SDK Integration Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv add &lt;span class="s2"&gt;"artemis-client @ git+https://github.com/google/artemis.git#subdirectory=packages/artemis-client"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;artemis_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArtemisClient&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ArtemisClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://artemis-host:8000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;device_serial&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;emulator-5554&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# optional: target specific device serial
&lt;/span&gt;        &lt;span class="n"&gt;default_profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# "flash" (fast reactive) or "pro" (deep reasoning)
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Open System Settings, go to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Battery&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, verify battery percentage is displayed, and check for any crash dialogs.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;succeeded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Test failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ Test Passed! Device: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device_serial&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | Trace ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK claims "zero runtime dependencies" — ADB, the agent, models, and image processing all stay on the device host. It supports strongly typed Pydantic structured outputs and assertions, and plugs directly into frameworks like pytest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. MCP Integration Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using Claude Desktop as an example (&lt;code&gt;claude_desktop_config.json&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"artemis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/path/to/artemis/.venv/bin/python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-m"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp_server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cwd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/path/to/artemis"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The docs also recommend mounting a behavior rules file (&lt;code&gt;mcp_server/rules.md&lt;/code&gt;) for AI agents, covering proactive exploration before coding, Flash/Pro routing strategy, and latency compensation logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Deeper Look
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Flash vs. Pro: A Pragmatic Speed/Reliability Trade-off
&lt;/h3&gt;

&lt;p&gt;ARTEMIS doesn't use a single mode for every task — it explicitly splits into two architecturally distinct execution paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flash mode (reactive):
  Observe screen → think about next step → execute action → loop
  ↑ Single-model loop, no planning, no safety net, no final report
  ↑ Loop count is unbounded by default (history is compressed, not truncated)
  ↑ Good for short, deterministic tasks like "open Settings and flip a switch"

Pro mode (plan-and-verify):
  Planner drafts a Markdown task plan (with milestones and verification items)
      ↓
  Operator runs a "Safety Net" check before each action
      ↓ (checks against the live UI tree first, then pixel fallback)
  Hits a blocker → opens an "execution incident," Operator handles recovery autonomously
      ↓
  Checker (read-only) verifies plan checkpoints, supports a final review before exit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core insight behind this design: &lt;strong&gt;mobile automation task complexity is bimodal&lt;/strong&gt;. A large share of tasks are deterministic short tasks — "open a page and confirm a state" — where a simple reactive loop is fast and sufficient. A minority are complex workflows that need multi-step planning, fault tolerance, and long-running monitoring, where only a multi-agent plan-execute-verify structure can guarantee reliability. Using one architecture for both extremes wastes something either way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fault-Tolerant Design in Multi-Modal Target Localization
&lt;/h3&gt;

&lt;p&gt;ARTEMIS's element localization mechanism reflects a "prefer precision, degrade gracefully" fault-tolerance philosophy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Localization priority:
  1. Accessibility Hierarchy — most precise, the go-to for standard Android UI
  2. OCR text recognition — handles cases where the hierarchy tree lacks information
  3. Vision model recognition — fallback, handles custom Canvas/Compose/Flutter UIs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Standard Android widgets are usually exposed accurately by accessibility services, making that the fastest and most reliable localization method. But more and more apps render their UI with Compose, Flutter, or custom Canvas drawing, which is unfriendly to accessibility services — that's when OCR and vision models step in. This layered fallback strategy avoids the waste of "use the most expensive method for everything" while also avoiding the fragility of "just fail outright when standard widgets aren't recognized."&lt;/p&gt;

&lt;h3&gt;
  
  
  Shared History Compression: Memory Management for Long-Running Tasks
&lt;/h3&gt;

&lt;p&gt;Flash and Pro share the same history compression mechanism, and this detail is worth noting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Problem: long-running tasks (especially Pro's 100+ step workflows) accumulate
a large volume of screenshots and action logs
  ↑ Stuffing them directly into context blows past the window limit
  ↑ Simple truncation loses important early information

ARTEMIS's approach:
  Old screenshots → replaced with visual summaries (keep semantics, drop pixel detail)
  Completed steps → compressed into retrievable history chunks
  ↑ The agent can retrieve past context when it needs to look back,
    instead of always carrying the full history
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design lets Flash mode run stably long-term even with no loop cap, and is also the memory-management foundation that makes Pro's 100+ step workflows possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Compares to Similar Mobile Automation Approaches
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Appium (traditional scripts)&lt;/th&gt;
&lt;th&gt;Pure vision-model-driven&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;ARTEMIS&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Localization method&lt;/td&gt;
&lt;td&gt;Fixed element ID/XPath&lt;/td&gt;
&lt;td&gt;Pure visual recognition&lt;/td&gt;
&lt;td&gt;Element index first + OCR + vision fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI-change tolerance&lt;/td&gt;
&lt;td&gt;❌ Brittle&lt;/td&gt;
&lt;td&gt;✅ Fairly robust&lt;/td&gt;
&lt;td&gt;✅ Fairly robust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution speed&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;Flash is fast / Pro is moderate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Natural-language driven&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Partial support&lt;/td&gt;
&lt;td&gt;✅ Native support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI IDE integration (MCP)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Uncommon&lt;/td&gt;
&lt;td&gt;✅ Native support across multiple IDEs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-running task plan/verify&lt;/td&gt;
&lt;td&gt;Must build yourself&lt;/td&gt;
&lt;td&gt;Uncommon&lt;/td&gt;
&lt;td&gt;✅ Native in Pro mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open source&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Varies by project&lt;/td&gt;
&lt;td&gt;✅ Apache-2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ARTEMIS's differentiation is combining "natural-language driven + multi-modal fault-tolerant localization + native AI IDE integration" into one tool, using its dual-mode design to avoid the dilemma of "either too slow with pure vision models, or too brittle with fixed scripts."&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Links and Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Official Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/google/artemis" rel="noopener noreferrer"&gt;https://github.com/google/artemis&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;License&lt;/strong&gt;: Apache License 2.0&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Issues&lt;/strong&gt;: &lt;a href="https://github.com/google/artemis/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/google-research/android_world" rel="noopener noreferrer"&gt;AndroidWorld&lt;/a&gt; — Google Research's mobile agent benchmark project, and ARTEMIS's evaluation benchmark&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; — The standard protocol underlying ARTEMIS's integration with Claude Code and other AI IDEs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/minitap-ai/mobile-use" rel="noopener noreferrer"&gt;mobile-use&lt;/a&gt; — The open-source project (from Minitap, Inc.) that ARTEMIS is built on&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Natural-language-driven mobile automation&lt;/strong&gt;: replaces brittle element-locator scripts with instructions, lowering test maintenance cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dual Flash/Pro mode design&lt;/strong&gt;: choose a fast reactive loop or a plan-execute-verify workflow based on task complexity, balancing speed and reliability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-modal fault-tolerant localization&lt;/strong&gt;: accessibility hierarchy first, OCR and vision models as graceful fallbacks, adapting from standard widgets to custom Canvas UIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native MCP integration&lt;/strong&gt;: gives Claude Code, Antigravity, Codex, and other AI IDEs the ability to directly operate real Android devices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;99%+ on AndroidWorld&lt;/strong&gt;: reliability validated on a standard mobile agent benchmark&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Who This Is For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mobile app testing teams&lt;/strong&gt;: want to escape brittle element-locator scripts and reduce maintenance cost from UI changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI application developers&lt;/strong&gt;: want AI coding assistants to be able to operate real Android devices for feature verification or bug reproduction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD pipeline maintainers&lt;/strong&gt;: need to seamlessly fold mobile automation testing into existing test frameworks and continuous integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exploratory/stability testing teams&lt;/strong&gt;: need long-running, continuously monitored mobile testing capability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  One-Line Verdict
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;ARTEMIS doesn't pick a side between "fast but brittle" and "robust but slow" — it splits the tension apart directly with its Flash/Pro dual-mode design, which may be a pragmatic middle answer as mobile automation moves toward AI-driven approaches.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>google</category>
      <category>android</category>
      <category>autotest</category>
    </item>
    <item>
      <title>One Open Source Project a Day (No. 187): TeamAI-CLI — Tencent's Team-Level AI Agent Middleware, Turning Individual AI Skills Into Shared Team Assets</title>
      <dc:creator>WonderLab</dc:creator>
      <pubDate>Sun, 20 Sep 2026 02:28:57 +0000</pubDate>
      <link>https://dev.to/wonderlab/one-open-source-project-a-day-no-187-teamai-cli-tencents-team-level-ai-agent-middleware-bgk</link>
      <guid>https://dev.to/wonderlab/one-open-source-project-a-day-no-187-teamai-cli-tencents-team-level-ai-agent-middleware-bgk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"An AI agent's capabilities shouldn't be trapped in one person's chat history — they should become a shared asset for the whole team."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;187th&lt;/strong&gt; article in the "One Open Source Project a Day" series. Today's project is &lt;strong&gt;TeamAI-CLI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When everyone on a team is using Claude Code, Cursor, or Codex to write code, a hidden problem starts building up: &lt;strong&gt;everyone is independently "training" their own AI&lt;/strong&gt;. One engineer spends half a day figuring out the right prompting approach to get Claude Code to correctly understand the project's architecture; another hits the exact same wall with no idea it's already been solved, and has to work through it from scratch. Skills, Rules, and CLAUDE.md configs scattered across the team never converge into anything shared or synchronized.&lt;/p&gt;

&lt;p&gt;That's exactly the problem TeamAI-CLI addresses. It's not another AI coding assistant — the project's own documentation lists Claude Code, Codex, and Cursor as tools it's &lt;em&gt;compatible with&lt;/em&gt;, not competitors. It's a &lt;strong&gt;meta-management layer&lt;/strong&gt; that sits on top of the AI tools your team already uses, responsible for syncing skills, rules, and knowledge bases across members, and for turning individual experience gained while using AI into a shared team asset.&lt;/p&gt;

&lt;p&gt;4.8k Stars, MIT License, built in TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Will Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;TeamAI-CLI's three-layer product architecture: Team Execution / Team Context / Team Improvement&lt;/li&gt;
&lt;li&gt;The push → MR → review-and-merge → SessionStart-hook-sync distribution flow&lt;/li&gt;
&lt;li&gt;The friction-signal-driven mechanism for automatically capturing experience&lt;/li&gt;
&lt;li&gt;How the codebase knowledge graph is built (tree-sitter AST + regex heuristics)&lt;/li&gt;
&lt;li&gt;Its relationship to Claude Code, Codex, and similar tools: not a replacement, but a "team layer"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Some experience using AI coding tools like Claude Code, Cursor, or Codex&lt;/li&gt;
&lt;li&gt;Familiarity with Git workflows (branches, Merge Requests)&lt;/li&gt;
&lt;li&gt;Optional: basic understanding of MCP (Model Context Protocol)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;TeamAI's official positioning is "the shared foundation for how your team works, learns, and improves with AI." The core idea, in one sentence: &lt;strong&gt;turn individual AI capabilities into shared team capabilities — across agents, machines, and team members.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Team and Background
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organization&lt;/strong&gt;: Tencent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: MIT License&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary language&lt;/strong&gt;: TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distribution&lt;/strong&gt;: global npm install (&lt;code&gt;teamai-cli&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Stats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⭐ GitHub Stars: &lt;strong&gt;4,800+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🍴 Forks: &lt;strong&gt;340+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;👀 Watchers: &lt;strong&gt;17&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;📄 License: MIT&lt;/li&gt;
&lt;li&gt;🔧 Open Issues: 33, Open PRs: 14 (actively maintained)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem It Solves
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A team without TeamAI:
  One engineer built a perfect set of project rules in Claude Code  ← lives only on their machine
  Another spent three days figuring out how to get the AI to understand the architecture  ← that lesson never spreads
  Someone's CLAUDE.md and team conventions went stale long ago  ← nobody keeps them in sync
  ↑ Everyone's AI capability stays siloed — the team's collective ability never compounds

TeamAI's approach:
  A unified team resource repository (Skills/Rules/Docs/Agents/Hooks/MCP)
  ↓ teamai push (share) → MR review → merge
  ↓ SessionStart hook auto-triggers teamai pull (sync)
  Every team member's local AI tools automatically get the latest team standards
  ↑ Individual experience → team asset — AI capability can be version-controlled,
    reviewed, and distributed just like code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A tech lead standardizing AI tool conventions across the team&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write project-specific coding standards and architecture constraints as Skills/Rules once, sync them to everyone&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;New members onboarding quickly into an existing AI collaboration setup&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After joining the team repo, local AI tools automatically pull the latest skills and context — no starting from scratch&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Team members sharing personal experience&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;teamai contribute&lt;/code&gt; to share a hard-won debugging insight with the whole team in one command&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Codebase knowledge accumulation for AI retrieval&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The knowledge graph feature lets AI automatically retrieve relevant code context and historical decisions before tackling a coding task&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Visualizing team-wide AI usage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;dashboard&lt;/code&gt; and &lt;code&gt;digest&lt;/code&gt; commands to understand team-wide AI usage trends and activity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Conversational install (let the AI set it up)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Paste this directly to Claude Code or any agent that supports Skills:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install the teamai skill: https://github.com/Tencent/teamai-cli/tree/main/skills/teamai ,
load the teamai skill, then set up TeamAI for my team from scratch.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: Manual CLI install&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Admin: create the team's shared repository&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; teamai-cli
teamai init https://github.com/yourorg/yourrepo

&lt;span class="c"&gt;# Team member: join an existing team&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/my-project
teamai init https://github.com/yourorg/yourrepo

&lt;span class="c"&gt;# Or install at the user scope (applies across all projects)&lt;/span&gt;
teamai init https://github.com/yourorg/yourrepo &lt;span class="nt"&gt;--scope&lt;/span&gt; user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. A Three-Layer Product Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Positioning&lt;/th&gt;
&lt;th&gt;Core Capabilities&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Team Execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"Make every agent work the team's way"&lt;/td&gt;
&lt;td&gt;init/pull/push sync for Skills/Rules/Agents/Hooks/MCP/Env&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Team Context&lt;/strong&gt; (beta)&lt;/td&gt;
&lt;td&gt;"Make every agent understand the team"&lt;/td&gt;
&lt;td&gt;Knowledge recall, learnings, codebase knowledge graph, team wiki&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Team Improvement&lt;/strong&gt; (beta)&lt;/td&gt;
&lt;td&gt;"Make every execution improve the team"&lt;/td&gt;
&lt;td&gt;Friction-signal-driven experience sharing, session logs, weekly digest, usage dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2. A Git-Native Distribution Flow&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;teamai push → creates a branch + MR → reviewer approves and merges
                                              ↓
        SessionStart hook auto-triggers teamai pull → synced to local AI tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supports GitHub, GitLab, GitCode, CNB, TGit, and private Git servers — essentially folding AI collaboration standards into the normal code review process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Key Commands&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;teamai init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Initialize: OAuth login, link repo, register member, inject hooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;teamai pull&lt;/code&gt; / &lt;code&gt;push&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Pull/push team resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;teamai status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the diff between local state and the team repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;teamai contribute&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Share session experience to the team repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;teamai recall &amp;lt;query&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search the team knowledge base (BM25 + graph-augmented)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;teamai codebase --extract/--deep-enrich/--reconcile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Codebase knowledge graph operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;teamai digest&lt;/code&gt; / &lt;code&gt;dashboard&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Team usage stats and visualization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;teamai doctor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Diagnose configuration issues&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;4. A Broad Agent Compatibility Matrix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TeamAI-CLI explicitly claims compatibility with over a dozen AI coding tools, including Claude Code, Codex, Cursor, GitHub Copilot CLI, CodeBuddy, OpenCode, and Kiro. The documentation includes a detailed compatibility table showing which capabilities (skills/rules/docs/env/hooks/mcp) each tool supports.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Deeper Look
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Friction-Signal-Driven Experience Capture
&lt;/h3&gt;

&lt;p&gt;The most interesting design in TeamAI-CLI's Team Improvement layer is its trigger logic: instead of requiring users to proactively log their experience, it &lt;strong&gt;monitors friction signals in a session&lt;/strong&gt; to detect "something worth recording just happened."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Examples of friction signals:
  "The user interrupted the AI twice"
  "The AI retried the same failing tool call 8 times in a row"
  ↓
  The system identifies unusual friction
  ↓
  It prompts the user: "This debugging session looks like it has something
  worth sharing — want to contribute it to the team?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is smarter than "asking employees to write weekly summaries of lessons learned" — most valuable debugging insights happen in the moment while solving a problem, and details fade fast in hindsight. Using interaction friction as the trigger captures experience while it's still fresh.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Codebase Knowledge Graph: A Dual-Track Extraction Strategy
&lt;/h3&gt;

&lt;p&gt;The codebase knowledge graph in the Team Context layer uses a dual-track strategy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AST Track (precise, but costly)
  Uses a tree-sitter WASM parser to parse code
  ↓ precisely extracts functions, classes, and dependency relationships
  ↓ suited to deep parsing of core modules

Heuristic Track (fast, but coarse-grained)
  Scans code with regex heuristics
  ↓ quickly covers a broad range of files
  ↓ suited to fast indexing of peripheral code

Both tracks' results → written to the teamwiki/ directory → power the recall
command's BM25-based retrieval
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dual-track design reflects a practical trade-off: running tree-sitter-level precise AST parsing across an entire large codebase is expensive, while pure regex matching loses semantic relationships. Combining both — precise parsing for core modules, fast coverage for the periphery — is a pragmatic middle ground.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Relates to Claude Code and Codex
&lt;/h3&gt;

&lt;p&gt;This is the point most likely to cause confusion about TeamAI-CLI, so it's worth clarifying directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Wrong framing: TeamAI-CLI competes with Claude Code, as another AI coding CLI
Correct framing: TeamAI-CLI sits on top of Claude Code/Codex/Cursor,
                  acting as a team-level distribution and knowledge management layer

Analogy:
  Claude Code / Codex / Cursor  ≈ the IDE on each person's machine
  TeamAI-CLI                    ≈ the team's Git repo + code review process
                                   (but for Skills/Rules/knowledge, not code)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works by injecting hooks (like a SessionStart hook) and shared config files (CLAUDE.md, Skills directories, etc.) into already-installed AI tools. It doesn't provide reasoning capability itself — it orchestrates "who should use which rules, and when they get synced."&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Compares to Similar Team Collaboration Tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Manually Maintained CLAUDE.md&lt;/th&gt;
&lt;th&gt;Cursor Rules (local)&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;TeamAI-CLI&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cross-agent compatibility&lt;/td&gt;
&lt;td&gt;Depends on each tool's own implementation&lt;/td&gt;
&lt;td&gt;Cursor only&lt;/td&gt;
&lt;td&gt;✅ A dozen+ tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team sync mechanism&lt;/td&gt;
&lt;td&gt;Manual copy-paste / word of mouth&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Git push/pull + MR review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic experience capture&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ Friction-signal-driven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codebase knowledge graph&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ AST + heuristic dual-track&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usage visualization&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ dashboard/digest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Version control and review&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Local files, no review&lt;/td&gt;
&lt;td&gt;✅ Standard Git flow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;TeamAI-CLI's differentiation is treating "AI collaboration conventions" as a first-class citizen of standard software engineering process (version control, code review) — instead of leaving everyone to maintain their own local config files.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Links and Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Official Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🌟 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Tencent/teamai-cli" rel="noopener noreferrer"&gt;https://github.com/Tencent/teamai-cli&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;npm package&lt;/strong&gt;: &lt;code&gt;teamai-cli&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;License&lt;/strong&gt;: MIT License&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Issues&lt;/strong&gt;: &lt;a href="https://github.com/Tencent/teamai-cli/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://tree-sitter.github.io/tree-sitter/" rel="noopener noreferrer"&gt;tree-sitter&lt;/a&gt; — The parser framework behind TeamAI-CLI's codebase knowledge graph AST Track&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; — The resource distribution standard involved in TeamAI-CLI's compatibility matrix&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://claude.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; — One of the main AI coding agents TeamAI-CLI integrates with&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Not an AI coding assistant, but a team-level middleware layer&lt;/strong&gt;: sits on top of Claude Code/Codex/Cursor, focused on syncing standards rather than generating code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A three-layer architecture that builds progressively&lt;/strong&gt;: Team Execution handles distribution, Team Context handles understanding, Team Improvement handles evolution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Git-native distribution flow&lt;/strong&gt;: push → MR review → merge → hook-triggered sync — AI collaboration conventions follow standard software engineering process&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Friction-signal-driven experience capture&lt;/strong&gt;: doesn't rely on manual summarization; interaction anomalies automatically trigger experience capture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatible with a dozen+ agents&lt;/strong&gt;: not locked to a single AI tool ecosystem, covering mainstream choices like Claude Code, Codex, and Cursor&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Who This Is For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Engineering team leads&lt;/strong&gt;: want to standardize AI collaboration conventions across the team instead of letting everyone figure it out independently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mid-to-large engineering teams&lt;/strong&gt;: members use different AI tools and need a neutral layer to keep standards in sync&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams that value knowledge management&lt;/strong&gt;: want to systematically capture hard-won lessons and codebase knowledge instead of losing them to staff turnover&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams already using Claude Code/Cursor and similar tools&lt;/strong&gt;: don't want to switch tools, just want the ones they already use to be smarter and more consistent&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  One-Line Verdict
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;TeamAI-CLI isn't trying to solve "how AI writes code" — it's trying to solve "how one person's AI experience becomes everyone's experience," which may be a more interesting direction than simply stacking on stronger models.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://primeskills.store" rel="noopener noreferrer"&gt;PrimeSkills&lt;/a&gt; — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find more useful knowledge and interesting products on my &lt;a href="https://home.wonlab.top/en" rel="noopener noreferrer"&gt;Homepage&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>tencent</category>
      <category>agents</category>
      <category>agentskills</category>
    </item>
  </channel>
</rss>
