平均では良いのに、安全ではない
エージェントの評価スイートは、最終状態、道のり、繰り返した実行を、変更が通らなければならない1つのゲートにまとめます。必要な理由は、どのチェックも単独では、別の悪い変更を通してしまうからです。
店の新しいエージェントは、61%に対して77%を出しました。それでも、決して失敗してはならないケースが、ときどき失敗するようになりました。
このレッスンのゴール
メモ
軌跡、状態、繰り返した実行を、1つのゲートにまとめられる。
キーワード
| ことば | いみ |
|---|---|
| ゲート | 実行の前に決めておく合格の基準 |
| 必須ケース | どの実行でも失敗してはならないケース |
| ステップの増加 | 新バージョンが余計に踏んでよいステップの量 |
| 終了コード | スクリプトが返す数。0 は成功 |
| スイート | 変更のたびにまとめて動かすチェック |
学生が入力するセルは、このプレビューでは空白です。
ゲートごとに、捕まえる変更が違う
結果を見る前に、各ゲートを決めます。1つ目は、最終状態が十分な頻度で正しいこと。2つ目は、ステップが増えすぎていないこと。3つ目は、必須ケースを名指しします。4つ目は、変更が明らかに悪くはないことです。
ステップのゲートは、ステップの増加を制限します。エージェントにとってはコストのゲートです。ステップごとに費用がかかるからです。
例: 4つのゲート
| ゲート | 規則 | 捕まえるもの |
|---|---|---|
| 状態の率 | 実行の70%以上が、正しく終わる | 失敗が多すぎるバージョン |
| ステップ | ステップの増加が20%以内 | 正しいが遅いバージョン |
| 必須ケース | ケース4が、どの実行でも合格する | 最も大切な部分の破損 |
| 悪化なし | 符号検定が悪化と言わない | 少し直して、多く壊した変更 |
学生が入力するセルは、このプレビューでは空白です。
ゲートは終了コードで終わる
各ゲートは、失敗したときに自分の名前を、失敗のリストに加えます。スイートはリストを表示し、終了コードを返します。リストが空なら 0、そうでなければ 1 です。
ラボにはCIがありません。CIとは継続的インテグレーションの略で、変更のたびにチェックを動かす仕組みです。そこでは本物のスイートが、このスクリプトを動かし、1が返ったら公開を止めます。
記録済みの実行を書く
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 各ケースの記録済みの3回の実行: [状態が正しい(1 か 0), 踏んだステップ数]。' 'const oldVersion = [' ' [[1, 4], [1, 4], [1, 5]], [[1, 5], [0, 6], [1, 5]], [[0, 6], [0, 7], [1, 6]],' ' [[1, 4], [1, 4], [1, 4]], [[1, 5], [1, 5], [0, 5]], [[0, 7], [0, 7], [0, 6]],' '];' 'const newVersion = [' ' [[1, 4], [1, 4], [1, 4]], [[1, 5], [1, 5], [1, 6]], [[1, 6], [0, 7], [1, 6]],' ' [[1, 5], [0, 5], [1, 5]], [[1, 5], [1, 5], [1, 5]], [[0, 7], [1, 7], [0, 7]],' '];' 'const settings = { minState: 70, stepGrowth: 20, must: [3] };' 'module.exports = { oldVersion, newVersion, settings };' > runs.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
スイートを書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { oldVersion, newVersion, settings } = require(\"./runs\");' 'function states(version) {' ' return version.map(function (runs) {' ' return runs.filter(function (run) { return run[0] === 1; }).length;' ' });' '}' 'function stateRate(version) {' ' const total = states(version).reduce(function (a, b) { return a + b; }, 0);' ' return Math.floor(total * 100 / (version.length * version[0].length));' '}' 'function steps(version) {' ' let sum = 0;' ' version.forEach(function (runs) { runs.forEach(function (run) { sum = sum + run[1]; }); });' ' return sum;' '}' > suite.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
スイートを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function signP(better, worse) {' ' const n = better + worse;' ' if (n === 0) return 1;' ' let total = 0;' ' let ways = 1;' ' for (let i = 0; i <= Math.min(better, worse); i = i + 1) {' ' total = total + ways;' ' ways = ways * (n - i) / (i + 1);' ' }' ' return Math.min(1, 2 * total / Math.pow(2, n));' '}' 'const before = states(oldVersion);' 'const after = states(newVersion);' 'let better = 0;' 'let worse = 0;' 'before.forEach(function (n, at) {' ' if (after[at] > n) better = better + 1;' ' if (after[at] < n) worse = worse + 1;' '});' >> suite.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
スイートを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const failures = [];' 'const rate = stateRate(newVersion);' 'console.log(\"状態の率 \" + rate + \"%、必要なのは \" + settings.minState + \"%\");' 'if (rate < settings.minState) failures.push(\"状態の率\");' 'console.log(\"ステップ \" + steps(newVersion) + \"、旧は \" + steps(oldVersion) + \"、上限は \" + settings.stepGrowth + \"% 増\");' 'if (steps(newVersion) * 100 > steps(oldVersion) * (100 + settings.stepGrowth)) failures.push(\"ステップ\");' 'const broken = settings.must.filter(function (at) { return after[at] < newVersion[at].length; });' 'console.log(\"実行で失敗した必須ケース: \" + broken.map(function (at) { return at + 1; }).join(\"、\"));' 'if (broken.length > 0) failures.push(\"必須ケース\");' >> suite.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
スイートを書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const p = signP(better, worse);' 'console.log(\"良い \" + better + \"、悪い \" + worse + \"、p \" + p.toFixed(3));' 'if (p < 0.05 && worse > better) failures.push(\"悪化\");' 'console.log(failures.length === 0 ? \"すべてのゲートを通過\" : \"失敗したゲート: \" + failures.join(\"、\"));' 'process.exit(failures.length === 0 ? 0 : 1);' >> suite.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
スイートを動かし終了コードを読む
docker exec -w /home/node/project agent node suite.js; echo 終了コード $?ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
スイートが表示するもの
| 行 | 示すこと |
|---|---|
| 状態の率 | 77%、必要なのは70%。通過 |
| ステップ | 旧95に対して98、上限は20%増。通過 |
| 必須ケース | ケース4が実行で失敗 |
| 良い悪い | 良い4、悪い1、p 0.375。通過 |
| 結果 | 失敗したゲート: 必須ケース |
| 終了コード | 1 |
学生が入力するセルは、このプレビューでは空白です。
試してみましょう
ヒント
runs.js で、新バージョンの4つ目のケースの2回目の実行を [0, 5] から [1, 5] に変えて、もう一度動かします。必須ケースのゲートは通り、終了コードは 0 になります。
よくある間違い
注意
良い変更がゲートに引っかかると、ゲートをゆるめる人がいます。失敗のあとに動かしたゲートは、何も守りません。ゲートが誤っていたなら、理由を書いて別の見直しで直します。そのゲートに失敗した変更の中では、決して直しません。