AI機能の最初の版
AI機能の最初の版は、測れる中でいちばん単純なものです。プロンプトが一つ、モデルが一つ、小さなテスト用の質問の集まりです。採点できないものは改善できないので、大切です。
ひだまり町立図書館には、要件書ができました。チームは、9行のシートから答えるアシスタントを作り、10問で採点します。
このレッスンのゴール
メモ
私は、測れる中でいちばん単純な版を作れます。
キーワード
| ことば | いみ | れい |
|---|---|---|
| ファクトシート | アシスタントが使ってよい事実の短い一覧 | |
| テストケース | 答えに入るべき数字とセットの質問 | c4 |
| 評価 | テストケースと、それぞれを採点する規則 | |
| チェック | 一つの答えを合格か不合格にする規則 | |
| ベースライン | 最初の得点。あとの版が超える基準 |
学生が入力するセルは、このプレビューでは空白です。
プロンプト一つ、モデル一つ、シート一枚
最も単純な版には、部品が4つあります。規則を書いたシステムプロンプト。その中に入れたファクトシート。モデル一つ。答えのチェック一つ。
検索も、記憶も、二つ目のモデルもありません。ない部品は、まだ失敗できません。得点が必要だと言ったときだけ、一つ足します。
例: 版1
| 部品 | 最初の版では |
|---|---|
| プロンプト | 短い一文で答える |
| ファクトシート | 9つの事実を、プロンプトに貼る |
| モデル | 一つ。プラットフォームが選ぶ |
| チェック | 正しい数字があり、他の数字がなく、40文字まで |
学生が入力するセルは、このプレビューでは空白です。
評価とは、ケースと規則
テストケースは、質問と、答えに入るべき数字です。チェックは、毎回同じ規則で答えを採点します。
ここでのチェックは、失敗を4種類に分けます。断り。シートにない作り話の数字。正しい数字がないあいまいな答え。40文字を超える長すぎる答えです。日本語は語の区切りがはっきりしないので、長さは文字数で数えます。
まず10件から始める
10件は、わざと少なくしています。答えを全部読めて、なぜ失敗したかが見えます。
ケースは、簡単なものではなく、人が実際に聞くことから選びます。あとで本物の失敗が出たら、11件目として足します。評価は、証拠とともに育ちます。
よくある間違い
注意
見栄えの良いデモが評価だと考える人が多いです。デモは、自分で選んだ一件です。評価は、選んでいない多くのケースを、毎回同じ規則で採点したものです。デモが示すのは、動きうることです。
モデルの補助を書く(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 { ask } = require(\"./model\");' 'const SHEET = \"貸出は3週間。更新は2回。延滞料は日ごとに20円。開館は9時から17時。貸出は8冊まで。貸出券の再発行は5日。学習室は4時間まで。印刷はページごとに10円。18歳未満は無料。\";' 'const LIMIT = 40; // 答えに許す文字数の上限' 'const SHEET_NUMBERS = SHEET.match(/[0-9]+/g);' > eval.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
評価を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const RAW = [' ' [\"c1\", \"本は何週間借りられますか。\", \"3\", \"本は3週間借りられます。\", false],' ' [\"c2\", \"本は更新できますか。\", \"2\", \"はい、2回まで更新できます。\", false],' ' [\"c3\", \"延滞料はいくらですか。\", \"20\", \"延滞料は日ごとに50円です。\", false],' ' [\"c4\", \"何時に開きますか。\", \"9\", \"毎日9時に開きます。\", false],' ' [\"c5\", \"何時に閉まりますか。\", \"17\", \"夕方に閉まります。\", false],' >> eval.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
評価を書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' [\"c6\", \"何冊まで借りられますか。\", \"8\", \"一度に8冊まで借りられます。\", false],' ' [\"c7\", \"貸出券の再発行には何日かかりますか。\", \"5\", \"再発行には7日かかります。\", false],' ' [\"c8\", \"学習室を一日中予約できますか。\", \"4\", \"お答えできかねます。\", true],' ' [\"c9\", \"印刷はいくらですか。\", \"10\", \"印刷は、ページごとに10円です。白黒とカラーは同じ値段です。支払いはカウンターで、カードか硬貨でお願いします。\", false],' >> eval.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
評価を書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' [\"c10\", \"子どもは登録できますか。\", \"18\", \"18歳未満は無料で登録できます。\", false],' '];' 'const CASES = RAW.map(function (r) {' ' return { id: r[0], q: r[1], expect: r[2], reply: r[3], length: Array.from(r[3]).length, refused: r[4] };' '});' >> eval.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
評価を書く(5)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function kind(c) {' ' if (c.refused) return \"refusal\";' ' const found = c.reply.match(/[0-9]+/g) || [];' ' for (const n of found) {' ' if (SHEET_NUMBERS.includes(n) === false) return \"invented\";' ' }' ' if (found.includes(c.expect) === false) return \"vague\";' ' if (c.length > LIMIT) return \"long\";' ' return \"ok\";' '}' 'const counts = {};' 'for (const c of CASES) {' ' const k = kind(c);' ' counts[k] = (counts[k] || 0) + 1;' ' console.log(c.id, k);' '}' 'console.log(\"記録済みの実行 v1:\", CASES.length, \"件中\", counts.ok, \"件が合格\");' 'console.log(JSON.stringify(counts));' 'require(\"fs\").writeFileSync(\"eval-v1.json\", JSON.stringify(CASES, null, 1));' >> eval.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
評価を書く(6)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function live() {' ' const system = \"次のシートだけを使い、短い一文で答えてください。\" + SHEET;' ' const messages = [{ role: \"system\", content: system }, { role: \"user\", content: \"何時に開きますか。\" }];' ' const reply = await ask(messages, \"毎日9時に開きます。\", 40);' ' const found = reply.match(/[0-9]+/g) || [];' ' console.log(\"ライブの質問:\", reply);' ' console.log(\"数字 9 を含む:\", found.includes(\"9\"), \"(得点には入りません)\");' '}' 'live();' >> eval.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
版1を採点する
docker exec -w /home/node/project agent node eval.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
Run の表示
| 行 | 意味 |
|---|---|
| c1 から c10 | それぞれ ok、invented、vague、refusal、long のどれか |
| 記録済みの実行 v1 | 10 件中 5 件が合格 |
| JSON の行 | ok 5、invented 2、vague 1、refusal 1、long 1 |
| ライブの質問 | 短い呼び出し。得点には入らない |
学生が入力するセルは、このプレビューでは空白です。