エージェントの変更の見直し方
変更のまとまりは、エージェントが一つのタスクのために足し、変え、消したファイルのすべてです。エージェントは一分で二十のファイルを変えられます。全部を同じ注意で読むやり方は、追いつきません。
危険から先に見る見直しでは、危ないファイルを見つけて、それを先に読みます。前に学んだのは、小さな差分を一つ読むことでした。このレッスンでは、まとまり全体を並べます。
このレッスンのゴール
メモ
読む前に、エージェントの変更のまとまりを危険の順に並べることができる。
キーワード
| ことば | いみ |
|---|---|
| 変更のまとまり | 一つのタスクで変わったファイルのすべて |
| 範囲の広がり | タスクが頼んだ以上の変更 |
| 依存パッケージ | コードが動くのに要るパッケージ |
| 危険の印 | 変更に注意が要るというしるし |
| 重み | 印が何点になるか |
学生が入力するセルは、このプレビューでは空白です。
注意が要る変更のしるし
タスクが指定していないファイルは、範囲の広がりです。消えたテストや、行が減ったテストは、壊れた機能を隠すことがあります。設定ファイルが変わると、ビルドのしかた全体が変わることがあります。
とても大きな新しいファイルは、読むのが大変です。ここまでが、点をつける危険の印です。
新しい依存パッケージは、チームの誰も書いていないコードを持ち込みます。これは点にせず、別の行で知らせて見ます。
例: 印とその重み
| 印 | つくとき | 重み |
|---|---|---|
| テスト削除 | テストファイルが消された | 5 |
| テスト縮小 | テストファイルで、足した行より消した行が多い | 3 |
| 範囲外 | タスクがそのファイルを指定していない | 3 |
| 設定 | package.json、package-lock.json、ci.yml | 2 |
| 大きい | 足した行と消した行が合わせて 200 より多い | 1 |
学生が入力するセルは、このプレビューでは空白です。
点をつけ、並べ、それから読む
ファイルごとに、印の重みを足して点にします。点の高い順に並べ、同じ点なら来た順を保ちます。
それから、上のファイルを一行ずつ全部読みます。点の低いファイルも読みますが、もう危ないファイルを隠しません。テストが消えていれば、まとまり全体を差し戻します。
よくある間違い
注意
見直しとは、どの行も同じ注意で読むことだと考える人が多いです。400 行あると、危ない十行が安全な行の間に隠れます。先に危険の順に並べ、危ないファイルはすべて全部読みましょう。
助っ人 model.js を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// 応答のトークン数。なければ null。' 'function count(data, name) {' ' return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
助っ人 model.js を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' const data = await res.json();' ' const text = data.choices[0].message.content;' ' return { text, inTokens: count(data, \"prompt_tokens\"), outTokens: count(data, \"completion_tokens\"), recorded: false };' ' } catch (error) {' ' console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' ' return { text: sample, inTokens: null, outTokens: null, recorded: true };' ' }' '}' 'module.exports = { ask };' >> model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
危険の見直しを書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '// タスクが指定したファイルは二つ。エージェントは七つを返した。' 'const allowed = [\"shop.js\", \"shop.test.js\"];' 'const CONFIG = [\"package.json\", \"package-lock.json\", \"ci.yml\"];' 'const WEIGHT = { \"テスト削除\": 5, \"テスト縮小\": 3, \"範囲外\": 3, \"設定\": 2, \"大きい\": 1 };' > risk.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
危険の見直しを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const changes = [' ' { path: \"shop.js\", added: 12, removed: 2, kind: \"modified\" },' ' { path: \"shop.test.js\", added: 1, removed: 9, kind: \"modified\" },' ' { path: \"prices.json\", added: 1, removed: 1, kind: \"modified\" },' ' { path: \"old.test.js\", added: 0, removed: 40, kind: \"deleted\" },' ' { path: \"package.json\", added: 1, removed: 0, kind: \"modified\" },' ' { path: \"README.md\", added: 3, removed: 0, kind: \"modified\" },' ' { path: \"utils/format.js\", added: 230, removed: 0, kind: \"added\" },' '];' 'const before = [\"date-tools\"];' 'const after = [\"date-tools\", \"money-format\"];' >> risk.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
危険の見直しを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function flags(c) {' ' const out = [];' ' const isTest = c.path.indexOf(\".test.\") >= 0;' ' if (isTest && c.kind === \"deleted\") out.push(\"テスト削除\");' ' if (isTest && c.kind === \"modified\" && c.removed > c.added) out.push(\"テスト縮小\");' ' if (allowed.indexOf(c.path) < 0) out.push(\"範囲外\");' ' if (CONFIG.indexOf(c.path) >= 0) out.push(\"設定\");' ' if (c.added + c.removed > 200) out.push(\"大きい\");' ' return out;' '}' 'function score(c) {' ' return flags(c).reduce(function (sum, f) { return sum + WEIGHT[f]; }, 0);' '}' >> risk.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
危険の見直しを書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const order = changes.slice().sort(function (a, b) { return score(b) - score(a); });' ' order.forEach(function (c) {' ' console.log(score(c) + \" \" + c.path + (flags(c).length ? \": \" + flags(c).join(\"、\") : \"\"));' ' });' ' after.filter(function (d) { return before.indexOf(d) < 0; })' ' .forEach(function (d) { console.log(\"新しい依存: \" + d); });' ' const gone = changes.some(function (c) { return flags(c).indexOf(\"テスト削除\") >= 0; });' ' console.log(gone ? \"差し戻す: テストが消された\" : \"この順で見直す\");' ' const q = \"短く一文で答えてください。見直す人が、消えたテストファイルを最初に見るのは、なぜですか。\";' ' const sample = \"テストが消えると、もう何も確かめないので、壊れた機能が隠れるからです。\";' ' const reply = await ask([{ role: \"user\", content: q }], sample, 80);' ' console.log(\"モデル: \" + reply.text);' '}' 'main();' >> risk.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
危険の見直しを動かす
docker exec -w /home/node/project agent node risk.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
Run の出力
| 行 | 意味 |
|---|---|
| 8 old.test.js: テスト削除、範囲外 | 一番危ないファイルで、最初に読む |
| 5 package.json: 範囲外、設定 | タスクが指定していない設定ファイル |
| 4 utils/format.js: 範囲外、大きい | 誰も頼んでいない大きな新しいファイル |
| 3 shop.test.js: テスト縮小 | 指定したテストの行が減った |
| 3 prices.json: 範囲外 | タスクが指定していない値段のファイル |
| 3 README.md: 範囲外 | タスクが指定していない説明の文書 |
| 0 shop.js | タスクが本来扱うファイル |
| 新しい依存: money-format | チームの外から来た新しいコード |
| 差し戻す: テストが消された | まとまり全体についての判断 |
学生が入力するセルは、このプレビューでは空白です。