実際の失敗が、最良のテストになる
本番からの評価とは、実際の失敗をテストケースにすることです。必要な理由は、お客様がテストの作者も思いつかない不具合を見つける一方、そのメッセージには、残してはならない名前、住所、番号が含まれるからです。
店のエージェントが、今週4回失敗しました。そのチャットのどの部分を、テストのセットに入れてよいでしょうか。
このレッスンのゴール
メモ
使われている中での失敗を、残してはならないものを残さずに、ケースにできる。
キーワード
| ことば | いみ |
|---|---|
| トレース | 実際の1回の実行の記録 |
| マスク | 個人情報を目印に置き換えること |
| サンプリング | どのトレースを読むかを選ぶこと |
| 対照ケース | 壊れてはならないものを示すために残す、合格したトレース |
| ドリフト | セットが変わり続け、同じものを測らなくなること |
学生が入力するセルは、このプレビューでは空白です。
どのトレースを読むかを選ぶ
すべてのトレースは読めません。サンプリングで選びます。ユーザーが報告した失敗や、チェックが示した失敗を、すべて取ります。修正で、うまく動いていたものが壊れないように、合格したトレースを数件、対照ケースとして加えます。
失敗がケースになるのは、起きるべきだったことを、人が書いてからです。
例: 6件のトレースが4つのケースになる
| トレース | 結果 | どうなるか |
|---|---|---|
| t1 | 失敗 | ケースになる。メールを置き換える |
| t2 | ok | 対照ケースになる |
| t3 | 失敗 | ケースになる。電話番号を置き換える |
| t4 | 失敗 | ケースになる |
| t5 | 失敗 | 除く。マスク後の入力が t4 と同じ |
| t6 | ok | 選ばない |
学生が入力するセルは、このプレビューでは空白です。
保存する前に、個人情報を取り除く
マスクは、名前、住所、番号を、[メール] のような目印に置き換えます。読んで試せる規則で行い、モデルに頼んではいけません。期待するテキストもマスクします。
そのあと確かめます。できあがったケースの中で、消えているはずのものを探します。住所が残るケースは、テストのセットの中の情報漏えいです。
セットがドリフトしないようにする
ドリフトとは、セットが気づかないうちに変わり、先月の点数が同じ意味を持たなくなることです。セットにバージョンを付けます。ケースは、人が見直してからだけ加えます。毎晩動くスクリプトでは加えません。
大きさに上限を設け、重複を除きます。1つの不具合が大量に流れ込んでも、ほかを圧倒しないようにするためです。
記録済みのトレースを書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 店のエージェントの、作り話のトレース。人物も番号も架空です。' 'const traces = [' ' { id: \"t1\", outcome: \"failed\", user: \"注文A17を返金してください 連絡先 [email protected] です。\", expected: \"どの商品かを尋ね、1回だけ返金する\" },' ' { id: \"t2\", outcome: \"ok\", user: \"注文B02は どこですか?\", expected: \"発送済みと伝える\" },' ' { id: \"t3\", outcome: \"failed\", user: \"注文C11の件で +81-90-1234-5678 に 電話してください。\", expected: \"ヘルプのページを案内し、電話は約束しない\" },' > traces.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
記録済みのトレースを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' { id: \"t4\", outcome: \"failed\", user: \"荷物は どこですか? [email protected]\", expected: \"注文を調べ、番号を尋ねる\" },' ' { id: \"t5\", outcome: \"failed\", user: \"荷物は どこですか? [email protected]\", expected: \"注文を調べ、番号を尋ねる\" },' ' { id: \"t6\", outcome: \"ok\", user: \"こんにちは\", expected: \"お客様に挨拶する\" },' '];' 'module.exports = { traces };' >> traces.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ケースの作成を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { traces } = require(\"./traces\");' 'function redact(text) {' ' return text.split(\" \").map(function (word) {' ' let core = word;' ' let tail = \"\";' ' while (core.length > 0 && \"。、?.,?\".includes(core[core.length - 1])) {' ' tail = core[core.length - 1] + tail;' ' core = core.slice(0, -1);' ' }' ' if (core.includes(\"@\")) return \"[メール]\" + tail;' ' const digits = core.split(\"\").filter(function (c) { return c >= \"0\" && c <= \"9\"; }).length;' ' const plain = core.split(\"\").every(function (c) { return (c >= \"0\" && c <= \"9\") || c === \"+\" || c === \"-\"; });' ' if (plain && digits >= 7) return \"[電話番号]\" + tail;' ' return word;' ' }).join(\" \");' '}' > cases.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ケースの作成を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const seen = [];' 'const cases = [];' 'const dropped = [];' 'let okSeen = 0;' 'traces.forEach(function (trace) {' ' const made = { input: redact(trace.user), expected: redact(trace.expected), source: trace.id };' ' if (trace.outcome === \"ok\") {' ' if (okSeen % 2 === 0) { made.kind = \"対照\"; cases.push(made); }' ' okSeen = okSeen + 1;' ' return;' ' }' ' if (seen.includes(made.input)) { dropped.push(trace.id); return; }' ' seen.push(made.input);' ' made.kind = \"失敗\";' ' cases.push(made);' '});' >> cases.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ケースの作成を書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'cases.forEach(function (one, at) {' ' console.log(\"ケース \" + (at + 1) + \" (\" + one.kind + \"、\" + one.source + \" から): \" + one.input);' '});' 'console.log(\"重複として除いた: \" + dropped.join(\"、\"));' 'const left = cases.filter(function (one) { return one.input.includes(\"@\") || one.input.includes(\"1234\"); });' 'console.log(\"個人情報が残るケース: \" + left.length);' 'console.log(\"セット バージョン1、ケース数 \" + cases.length);' >> cases.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
トレースからケースを作る
docker exec -w /home/node/project agent node cases.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
作成が表示するもの
| 行 | 示すこと |
|---|---|
| ケース1から4 | 失敗3件と対照1件。個人情報は置き換え済み |
| 除いたもの | t5 |
| 残った個人情報 | 0 |
| セット | バージョン1、ケース4件 |
学生が入力するセルは、このプレビューでは空白です。
試してみましょう
ヒント
traces.js に、[email protected] のようなアドレスを含む、失敗したトレースを足して、もう一度実行します。ケースには [メール] と出るはずです。次に cases.js の redact の呼び出しを外して、何が漏れるかを見ます。
よくある間違い
注意
ユーザーのメッセージだけをマスクする人がいます。期待するテキスト、トレースの id、ツールの引数にも、同じアドレスが入ることがあります。残す項目はすべてマスクするか、必要な項目だけを残します。そして、できあがったセットに、消えているはずのものが残っていないかを試します。