別のモデルを使わずに採点する
ルールで返事を採点するとは、ふつうのコードで確かめることです。JSON として読めるか、ラベルは合っているか、数は範囲内か、といった確認です。ルールは速く、モデルの費用がかからず、いつも同じ答えを返すので役に立ちます。
ショップの振り分け機能は、ラベル、緊急度、客への1文を JSON で返すようになりました。どの部分も、ルールで確かめられます。
このレッスンのゴール
メモ
私は、モデルを使わないルールで、返事を採点できます。
キーワード
| ことば | いみ | れい |
|---|---|---|
| ルール | 返事が通るか落ちるかを決める、コードで書いた確認 | |
| 完全一致 | 値が期待する値と等しいこと | 請求 |
| JSON として読める | 返事が求めた形式として解釈できること | |
| 範囲内 | 数が2つの限度のあいだにあること | |
| 点数 | 返事が通ったルールの数 | |
| 禁止フレーズ | 返事に決して入れてはいけない言葉 |
学生が入力するセルは、このプレビューでは空白です。
4種類のルールで、ほとんど足りる
確認の多くは、4種類に収まります。JSON として読める: 返事が求めた JSON である。完全一致: 項目が期待する値である。範囲内: 数が限度のあいだにある。含む: あるフレーズが入っている、または入っていてはいけない。
どのルールも数行です。モデルは要らないので、実行に費用はかかりません。
例: 部分ごとに1つのルール
| 返事の部分 | ルール | 落ちる例 |
|---|---|---|
| 返事全体 | 有効な JSON のオブジェクト | もちろんです。これは配送のメッセージです |
| label | ケースとの完全一致 | 配送が期待なのに請求 |
| urgency | 1から3の整数 | 7 |
| reply | 120文字まで、禁止フレーズなし | 返金を承認しました |
学生が入力するセルは、このプレビューでは空白です。
ケースごとの点数で、壊れた場所が分かる
返事が通ったルール1つにつき、1点を与えます。最初の失敗で止めてはいけません。点数を見れば、どのルールが壊れたかが分かります。ケースが合格になるのは、すべてのルールを満たしたときだけです。
返事が JSON として読めなければ、後のルールは動かせないので、点数は 0 です。
モデルの補助を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '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 || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
モデルの補助を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (error) {' ' console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' ' const list = [].concat(sample);' ' const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return list[Math.min(turn, list.length - 1)];' ' }' '}' 'module.exports = { ask };' >> model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ルールを書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const forbidden = [\"返金を承認しました\", \"保証します\"];' 'function score(text, want) {' ' const out = { parses: false, label: false, range: false, reply: false };' ' let data = null;' ' try { data = JSON.parse(text); } catch (error) { return out; }' ' if (data === null || Array.isArray(data) || typeof data === \"string\" || typeof data === \"number\" || typeof data === \"boolean\") return out;' ' out.parses = true;' ' out.label = data.label === want;' > score.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ルールを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' out.range = Number.isInteger(data.urgency) && data.urgency >= 1 && data.urgency <= 3;' ' const said = typeof data.reply === \"string\" ? data.reply : \"\";' ' const low = said.toLowerCase();' ' const banned = forbidden.some(function (phrase) { return low.includes(phrase); });' ' out.reply = said.length > 0 && said.length <= 120 && banned === false;' ' return out;' '}' 'function points(out) {' ' return (out.parses ? 1 : 0) + (out.label ? 1 : 0) + (out.range ? 1 : 0) + (out.reply ? 1 : 0);' '}' 'module.exports = { score, points };' >> score.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
確認のスクリプトを書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const { score, points } = require(\"./score\");' 'function reply(label, urgency, text) {' ' return JSON.stringify({ label: label, urgency: urgency, reply: text });' '}' 'const recorded = [' ' { id: 1, want: \"請求\", text: reply(\"請求\", 2, \"二重のご請求について確認いたします。\") },' ' { id: 2, want: \"配送\", text: reply(\"請求\", 2, \"確認いたします。\") },' ' { id: 3, want: \"その他\", text: \"もちろんです。これは配送のメッセージです。\" },' > check2.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
確認のスクリプトを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' { id: 8, want: \"配送\", text: reply(\"配送\", 7, \"申し訳ありません。\") },' ' { id: 10, want: \"不明\", text: reply(\"不明\", 1, \"返金を承認しました。\") },' '];' 'function yesNo(value) { return value ? \"はい\" : \"いいえ\"; }' 'function show(name, out) {' ' console.log(name + \": \" + points(out) + \" / 4 (JSON \" + yesNo(out.parses) + \", ラベル \" + yesNo(out.label) + \", 範囲 \" + yesNo(out.range) + \", 返事 \" + yesNo(out.reply) + \")\");' '}' 'for (const one of recorded) show(\"記録したケース \" + one.id, score(one.text, one.want));' >> check2.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
確認のスクリプトを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const sample = reply(\"請求\", 2, \"ご注文の内容を確認いたします。\");' ' const prompt = \"このお店のメッセージを仕分けてください。3つのキーを持つ JSON だけで答えてください。label (請求、配送、その他、不明のどれか)、urgency (1から3の整数)、reply (100文字より短い丁寧な1文) です。\n\nメッセージ: 同じ注文が二重に請求されました\";' ' const text = await ask([{ role: \"user\", content: prompt }], sample, 120);' ' console.log(\"実際の返事: \" + text);' ' show(\"実際のケース 1\", score(text, \"請求\"));' '}' 'main();' >> check2.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
記録5つと実際の1つを採点する
docker exec -w /home/node/project agent node check2.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
出力の一例
| 行 | 示す内容 |
|---|---|
| 記録したケース 1 | 4 / 4、すべてのルールを満たす |
| 記録したケース 2 | 3 / 4、ラベルが違う |
| 記録したケース 3 | 0 / 4、JSON ではなく文章 |
| 記録したケース 8 | 3 / 4、緊急度 7 が範囲の外 |
| 記録したケース 10 | 3 / 4、禁止フレーズ |
| 実際のケース 1 | たいてい 3 か 4 / 4。実行のたびに変わることがある |
学生が入力するセルは、このプレビューでは空白です。
試してみましょう
ヒント
score.js に3つ目の禁止フレーズ「二重のご請求について」を足して、もう一度実行します。記録したケース 1 は、最後の1点を失うはずです。
よくある間違い
注意
ルールで満点なら良い返事だと考える人が多くいます。ルールが確かめるのは形と事実だけです。JSON として読め、ラベルも合っていて、それでも失礼で役に立たない返事はありえます。ルールはコードで決められることに使いましょう。