跳到正文
DEV Community· Sven Welack·· 2 小時前AI 評分56

消費級硬件上的大語言模型(第 8 部分):QA 問題——「能執行」不等於「正常運作」

LLMs on Consumer Hardware — Part 8: The QA Problem — Why 'It Ran' Isn't 'It Works'

AI 導讀

作者以團隊的 QA 經驗指出,確認程式載入或指令執行成功,並不足以證明交付物真正可用;測試必須涵蓋關鍵互動、可用性和實際內容。遊戲雖能載入,卻在炮塔首次開火時因錯誤呼叫而崩潰;只在載入時截圖檢查,未能觸發這個錯誤。後來加入 CANNOT VERIFY 判定,並要求檢查具體操作;Hans 亦因此指出故事網站缺少正文和圖片標籤。

正文

Part 8 of a series. Previously: Part 7 — The Strategist.

By this point the team could build things and, when blocked, adapt. What it could not reliably do was tell whether what it had built actually worked. That job belonged to Hans, the QA agent — the member of the team whose entire role is to run the others' deliverables and verify them — and for a long stretch Hans was the weakest link in the whole system. The reason is worth dwelling on, because it generalises far beyond this project: the gap between "it ran" and "it works."

The game that broke on the first shot

The clearest case came from a game. Asked to build a tower-defense game, the team produced something genuinely creditable: a self-contained HTML canvas game with its graphics generated in code, a sensible class architecture — Enemy, Tower, Projectile — and a working game loop. It looked finished. It was not. Buried in it was a single fatal mistake: the towers fired by pushing plain objects into the projectile list, while the Projectile class that was supposed to represent them was defined and then never used.

// a Tower fires by pushing a plain object:
projectiles.push({ x, y, target, ttl });

// but the game loop expects Projectile instances:
class Projectile { update() { /* move, check for a hit */ } }
for (const p of projectiles) p.update();   // TypeError: p.update is not a function

The game loaded perfectly. It drew the map, the towers, the enemies. Then, the instant the first tower took its first shot, p.update() was called on a plain object that had no such method, and the game threw. The bug was invisible until the first interaction — and unmissable immediately after.

Three passes at QA

Hans was asked to verify it, and the first verdict was PASS. He had not run it at all.

Strengthening his instructions was the obvious fix, and it was tried: an iron rule that nothing may be marked PASS that has not actually been executed; a third verdict, CANNOT VERIFY, so that "I couldn't test this" stopped quietly collapsing into "PASS"; an explicit instruction to run JavaScript through node -e; and a direction to trace the first user action specifically.

Hans genuinely tried harder on the next pass. He launched headless Chromium, loaded the page, took a screenshot, and checked the console for errors. There were none — and he returned PASS again. The screenshot had been taken at load. Nothing in it had placed a tower, so nothing had fired a shot, so the first-shot bug had never had the chance to appear.

The lesson: load is not play

This is the heart of it: verifying that something loads is not verifying that it works. A page that renders without console errors has proven only that it renders. The bug lived one interaction deeper, and every cheap form of testing — a screenshot, a load check — stops short of that interaction. Left to find its own way to "test," the agent reliably found the easiest thing that superficially counted as testing, and stopped there.

The instinct, familiar by now, was to wonder whether a more capable model would do better. It would not. Handed the same game, the 120B coordinator reached the same verdict from the same load-time screenshot. Testing the right thing is a question of method, not of model size — the same shape of lesson as the orchestration ceiling in Part 6. The fix lives in the instructions: say place a tower and fire a shot, not check that it loads.

The same failure in disguise: mechanics versus usability

The mistake wore a second disguise on a different deliverable. The team built a small command-line task tool, and it worked. Hans verified it by running a command with the task's full identifier, which succeeded. But the identifiers were raw UUIDs, and no human would ever type one:

$ python3 taskflow.py done 1
Task ID 1 not found.        # the real ID is da258ee6-66ca-4f1a-a27e-9169b15739bf

The tool functioned; it was simply unusable. Hans had verified the mechanics — does the command work when handed a valid ID? — and not the usability — can a person actually use this? It is the same error as the screenshot: testing the easy, literal thing instead of the thing that matters.

The method takes

It did, eventually, take. On a later project — a generated website meant to present a long illustrated story — Hans caught a real and subtle failure and reported it correctly:

Verification Result – CANNOT VERIFY
- story.md – present and complete.
- All 50 scene images (scene1.png … scene50.png) – present and each > 5 KB.
- index.html – generated but contains only a title and CSS; it does not include
  the story text or any <img> tags referencing the scene images.

Every file was present; every image was on disk and non-empty. A file-existence check, or a load check, would have passed it. But the page that was supposed to tie them together was hollow — a title and some styling, and none of the actual content. Hans noticed, declined to call it PASS, and returned CANNOT VERIFY with the specific reason. The third verdict had done exactly what it was added to do.

The environment lies too

Verification was not the only place a cheap signal misled; the same autonomous runs turned up failures of the environment itself. Files written to relative or ~-based paths "vanished," because in a multi-process setup each agent's working directory was its own profile folder — so a path meant for /home/user/hermes-runs/ landed somewhere like /home/user/.hermes/profiles/gatsby-manager/home/hermes-runs/ instead. The fix was to insist on absolute output paths everywhere. On another run a launcher failed instantly because the script its configuration named had never been copied to that path. In each case the lesson rhymes with the QA one: a green light — a command that returned, a file that was "written" — is not the same as the thing being where and what it should be.

The throughline

The lesson of QA is almost banal, and almost universally ignored: test the thing that matters, not the thing that is easy to test. Play, not load. Usability, not mechanics. The content, not the file list. None of it required a better model; all of it required telling the checker, precisely, what "working" means.

What follows

The hollow story-site that Hans flagged belonged to the team's most ambitious undertaking: an attempt to write and illustrate its own animated story — which first meant teaching a team that only knew text and code how to make pictures at all. That, and the AMD-specific fight to get image generation running locally, is the final entry.

來源:DEV Community · dev.to