質問はそのまま検索語にならない
クエリの書き換えとは、検索に使う前に質問を変えて、より多くを見つけさせることです。人は短い質問、あいまいな質問、二つの質問を一度に書き、索引は自分が持つ語しか知らないので、役に立ちます。
「お金 戻したい」には、「返金」と書いたページと共通の語がありません。「何日 まで か」には、何も当てはまりません。
このレッスンのゴール
メモ
質問を書き換えてより多くを見つけさせ、既知の出典で、書き換えが役に立ったと確かめられる。
キーワード
| ことば | いみ |
|---|---|
| クエリ | 検索が実際に受け取るテキスト |
| 拡張 | 関連する語をクエリに足すこと |
| 分解 | 質問をより小さな質問に分けること |
| 続きの質問 | それまでの会話があって初めて意味が通る質問 |
| 同義語 | 同じ考えを表す別の語 |
| 既知の出典 | テスト用の質問が見つけるべきページ |
学生が入力するセルは、このプレビューでは空白です。
拡張はページが使う語を足す
返金のページには「返金」と書いてあります。人は「お金 戻したい」と言います。拡張は、関連する語をクエリに足します。「お金 戻したい」は「お金 戻したい 返金 返品」になります。同義語の表でできますし、モデルに提案させることもできます。
元の語は残して足します。人の語を置き換えると、肝心だった一語を失うことがあります。
二つの質問は二回の検索
「日曜は休みか と 荷物は返品できるか」を考えましょう。一回の検索では、片方によく合うページしか返りません。もう片方は失われます。
分解は、「と」の所で質問を分け、それぞれを検索します。そのあと、レッスン 1 の統合で、リストを一つにします。
続きの質問には会話が要る
「何日 まで か」だけでは意味が通りません。前の質問「荷物は返品できるか」につなぐか、モデルにそれだけで通じる質問を書かせます。
会話の全部を送ってはいけません。長いクエリは的を外します。足すのは直前の質問だけで、新しい質問が短いときに限ります。
よくある間違い
注意
書き換えはいつも役に立つと考える人がいます。同義語が別のページに導くことも、長い履歴がクエリを的から外すこともあります。出典が分かっているテスト用の質問を用意し、変更のたびに前後のヒット数を数えます。
モデルの補助を書く(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 docs = {' ' hours: \"店 土曜 午前9時 開店 午後1時 閉店\",' ' sunday: \"店 日曜 休み\",' ' returns: \"荷物 30日 以内 返品 可能 返金\",' ' delivery: \"配送 3日 かかる 送料 400円\",' '};' 'const synonyms = { お金: [\"返金\", \"返品\"] };' > rewrite1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
書き換えを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function words(t) { return t.toLowerCase().split(\" \"); }' 'function best(q) {' ' const w = words(q);' ' let id = \"なし\", top = 0;' ' Object.keys(docs).forEach(function (k) {' ' const n = words(docs[k]).filter(function (x) { return w.includes(x); }).length;' ' if (n > top) { top = n; id = k; }' ' });' ' return id;' '}' 'function expand(q) {' ' const extra = [];' ' q.split(\" \").forEach(function (w) {' ' (synonyms[w] || []).forEach(function (s) { extra.push(s); });' ' });' ' return q + \" \" + extra.join(\" \");' '}' >> rewrite1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
書き換えを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const cases = [' ' { q: \"土曜 店 何時 開店\", want: \"hours\" },' ' { q: \"お金 戻したい 方法\", want: \"returns\" },' ' { q: \"配送 何日 かかる\", want: \"delivery\" },' '];' 'let plain = 0, wider = 0;' 'cases.forEach(function (c) {' ' const a = best(c.q), b = best(expand(c.q));' ' if (a === c.want) plain++;' ' if (b === c.want) wider++;' ' console.log(c.q + \" | そのまま: \" + a + \" | 広げた: \" + b);' '});' 'console.log(\"ヒット そのまま:\", plain, \"広げた:\", wider, \"/\", cases.length);' >> rewrite1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
書き換えを書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const two = \"日曜 休み と 荷物 返品 できる\";' 'console.log(\"質問全体で見つかる:\", best(two));' 'console.log(\"分けると見つかる:\", two.split(\" と \").map(best).join(\" + \"));' 'async function main() {' ' const text = \"前の質問: 荷物 は 返品 できる か。 今の質問: 何日 まで か。 最後の質問を、それだけで通じる質問に書き直してください。語はスペースで区切り、助詞は書かないでください。質問だけを答えてください。\";' ' const reply = await ask([{ role: \"user\", content: text }], \"荷物 返品 期限 何日\", 40);' ' console.log(\"書き直し:\", reply.text.trim());' ' console.log(\"見つかる:\", best(reply.text));' '}' 'main();' >> rewrite1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
実行してヒットを数える
docker exec -w /home/node/project agent node rewrite1.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
これは練習用の代用品です
探す部分は、四つのページとの共通の語を数えるだけです。語はスペースで区切り、助詞は除いてあります。本物は本物の索引を探します。モデルの呼び出しは続きの質問の書き直しに一回だけで、答えがなければ記録済みの書き直しを使います。
方法は規模が変わっても同じです。クエリを変え、既知の出典でヒットを数えます。
Run の出力の最初の3行
| 質問 | そのまま | 広げた |
|---|---|---|
| 土曜 店 何時 開店 | hours | hours |
| お金 戻したい 方法 | なし | returns |
| 配送 何日 かかる | delivery | delivery |
学生が入力するセルは、このプレビューでは空白です。