答えの前に手順を書かせる
手順を書かせるとは、答えの前に、考えた過程を書くようモデルに頼むことです。計算や計画では、手順があると、答えが正しくなりやすいことがあります。
手順は文字を増やし、文字は費用と時間を増やします。だから、頼む場面を見分け、頼んだら答えを確かめる力が要ります。
このレッスンのゴール
メモ
手順が役に立つときは頼み、役に立たないときは頼まずにいられます。
キーワード
| ことば | いみ |
|---|---|
| 手順 | 考えの小さな一歩 |
| 過程 | 答えに至る手順の流れ |
| 答えの行 | 答えだけを書く最後の行 |
| 確認 | 答えが正しいかを確かめる検査 |
| 費用 | 答えが使うトークンと時間 |
| 記録済み | 前に保存した、今作ったのではない答え |
学生が入力するセルは、このプレビューでは空白です。
手順がなぜ役に立つか
モデルは、文章を一つずつ書き、すでに書いた文章を次に使えます。過程が書かれていれば、次の手順がそれを使えます。2100 は、210、420、2100 と進むほうが、たどり着きやすいのです。
計算、計画、いくつもの部分がある決まりに効きます。国の首都を答えるような、ただの検索にはあまり効きません。
手順を頼む場面
| 問い | 手順 | 理由 |
|---|---|---|
| 15 回の電車代の合計 | 頼む | つながった二つの計算 |
| 次のうち破られた決まりはどれか | 頼む | 確かめる部分が複数 |
| この一文を丁寧に言い直す | 頼まない | 一手で済み、考えることがない |
| この感想は良いか悪いか | 頼まない | ラベルが答えのすべて |
学生が入力するセルは、このプレビューでは空白です。
モデル用の補助を書く(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"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
モデル用の補助を書く(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 d = await res.json();' ' return { text: d.choices[0].message.content, inTokens: count(d, \"prompt_tokens\"), outTokens: count(d, \"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 QUESTION = \"電車の運賃は一回 210 円です。毎日、行きと帰りに乗り、5 日間通います。運賃は合計でいくらですか。\";' 'const RECORDED = \"手順 1: 一回の運賃は 210 円。\n手順 2: 一日に二回乗ると 420 円。\n手順 3: 5 日で 2100 円。\n答え: 2100 円\";' > steps3.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
手順の分割を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function split(reply) {' ' const steps = [];' ' let answer = \"\";' ' for (const line of reply.split(\"\n\")) {' ' if (line.startsWith(\"手順 \")) steps.push(line.slice(line.indexOf(\":\") + 1).trim());' ' if (line.startsWith(\"答え:\")) answer = line.slice(3).trim();' ' }' ' return { steps: steps, answer: answer };' '}' 'function show(label, reply) {' ' const r = split(reply);' ' console.log(label + \": 手順 \" + r.steps.length + \" つ、答え \" + (r.answer || \"なし\"));' '}' >> steps3.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
手順の分割を書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' show(\"記録\", RECORDED);' ' const form = \"短い手順で考えてください。各行は 手順 1: のように始め、最後に 答え: で始まる行を一つ書いてください。\";' ' const reply = await ask([{ role: \"user\", content: QUESTION + form }], RECORDED, 200);' ' show(\"モデル\", reply.text);' '}' 'main();' >> steps3.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
答えを分けて実行する
docker exec -w /home/node/project agent node steps3.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
Run が表示するもの
最初の行は記録済みの答えを分けたもので、手順 3 つ、答え 2100 円です。次の行は、ラボのモデルの答えを同じように分けたものです。モデルがなければ「記録済みサンプル」と出て、二行目は一行目と同じになります。
モデルは、頼んだ形式を守らないことがあります。「答え なし」と出たら、「答え:」で始まる行がなかったのです。コードはそれを想定します。
よくある間違い
注意
「手順がもっともらしければ、答えも正しい」と考えがちです。きれいな手順を書いたあとで、ちがう答えを書く答えもあります。答えの行を、最後の手順や、知っている値と、必ず見比べてください。