クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

プロンプトに個人情報を入れない

このレッスンのあとで: モデルを呼ぶ前に個人情報を置き換え文字に変え、あとで元に戻すことができる。

65分のレッスンコマンド8件AI コーディングエージェントのラボ:opencode でコードを説明・修正し、その結果を確かめる

電話番号の入った問い合わせ

プロンプトに個人情報を入れないとは、モデルに、仕事に必要なものだけを見せることです。プロンプトは別のサービスへ送られ、しばしばログに残るので、大切になります。

サポートツールが、問い合わせをモデルに送って返信案を作らせます。問い合わせには、名前、メールアドレス、電話番号があります。返信にはどれも要りません。ツールは、それを置き換え文字に変え、あとで元に戻せます。

このレッスンのゴール

メモ

モデルを呼ぶ前に個人情報を置き換え文字に変え、あとで元に戻すことができる。

重要語句

ことばいみれい
個人情報生きている個人を特定できる、あらゆる情報
データの最小化仕事に必要なものだけを送ること
マスクする値を、代わりのものに置き換えること
置き換え文字取り除いた値の代わりに置くラベル[メール_1]
対応表各置き換え文字と元の値を結ぶ表

学生が入力するセルは、このプレビューでは空白です。

仕事に必要なものだけを送る

まず、その値がモデルに必要かどうかを問います。苦情への返信に要るのは苦情の中身で、電話番号ではありません。要らないものは取り除きます。

返信に人の名前が必要なら、置き換え文字を使います。モデルが「[氏名_1]様」と書き、あとで自分のコードが本当の名前を戻します。

例: 前と後

値モデルに送るもの対応表に残すもの
さくら[氏名_1][氏名_1] はさくら
[email protected][メール_1][メール_1] は [email protected]
09012345678[電話_1][電話_1] は 09012345678
さくら、もう一度[氏名_1]同じ値は、同じ置き換え文字のまま

学生が入力するセルは、このプレビューでは空白です。

マスクには限界がある

規則が見つけるのは、書いておいた形だけです。アットマークのある語、長い数字の並び、リストにある名前などです。通りの名前、あだ名、空白の入った番号は見逃します。

だからマスクはリスクを下げますが、なくしはしません。乱雑な例で試し、残りを探す検査も持っておきます。

モデル用の補助を書く(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 names = [\"さくら\", \"健太\"];' '// 単純にするため、空白で区切った語を一つずつ調べる。' 'const ticket = \"こんにちは、 さくら です。 [email protected] に メール、 09012345678 に 電話 してください。 健太 が 昨日 手伝ってくれました。\";' 'function allDigits(text) {' '  return text.length >= 8 && text.split(\"\").every(function (c) { return c >= \"0\" && c <= \"9\"; });' '}' > redact.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

マスクの処理を書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function redact(text, names) {' '  const map = {};' '  const seen = {};' '  const words = text.split(\" \");' '  for (let i = 0; i < words.length; i++) {' '    let bare = words[i];' '    while (bare.length > 0 && \".,。、\".includes(bare.slice(-1))) bare = bare.slice(0, -1);' '    const tail = words[i].slice(bare.length);' '    let kind = \"\";' '    if (bare.includes(\"@\")) kind = \"メール\";' '    if (allDigits(bare)) kind = \"電話\";' '    if (names.includes(bare)) kind = \"氏名\";' >> redact.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

マスクの処理を書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '    if (kind === \"\") continue;' '    if (seen[bare] === undefined) {' '      const n = Object.keys(map).filter(function (k) { return k.startsWith(\"[\" + kind); }).length + 1;' '      seen[bare] = \"[\" + kind + \"_\" + n + \"]\";' '      map[seen[bare]] = bare;' '    }' '    words[i] = seen[bare] + tail;' '  }' '  return { text: words.join(\" \"), map: map };' '}' >> redact.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

マスクの処理を書く(4)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function restore(text, map) {' '  let out = text;' '  for (const key of Object.keys(map)) out = out.split(key).join(map[key]);' '  return out;' '}' 'async function main() {' '  const safe = redact(ticket, names);' '  console.log(\"モデルに送る文章: \" + safe.text);' '  console.log(\"@が残っている: \" + safe.text.includes(\"@\"));' '  const rule = \"お客様への二文の返信を書いてください。角かっこの置き換え文字は、書かれているとおりに残してください。\";' '  const messages = [{ role: \"system\", content: rule }, { role: \"user\", content: safe.text }];' '  const sample = \"[氏名_1]様、お問い合わせありがとうございます。本日中に[メール_1]へご連絡します。\";' >> redact.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

マスクの処理を書く(5)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  const reply = await ask(messages, sample, 100);' '  console.log(\"置き換え文字つきの返信: \" + reply);' '  console.log(\"お客様向けに元へ戻した返信: \" + restore(reply, safe.map));' '  console.log(\"ログの行: \" + Object.keys(safe.map).length + \" 件を置き換え、返信は \" + reply.length + \" 文字\");' '}' 'main();' >> redact.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

マスクの処理を実行

コマンド
docker exec -w /home/node/project agent node redact.js

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

出力の一例

行意味
モデルに送る文章値の代わりに [氏名_1]、[メール_1]、[電話_1]、[氏名_2] が入った問い合わせ
@が残っているfalse
記録済みサンプルモデルが答えなかったときだけ、三行目に出る
置き換え文字つきの返信置き換え文字を残した二文。言葉は実行のたびに変わる
お客様向けに元へ戻した返信本当の値を戻した返信
ログの行置き換えた件数と返信の長さだけで、個人の文章はない。長さは返信によって変わる

学生が入力するセルは、このプレビューでは空白です。

ログに入れてよいもの

ログは、アプリより多くの人に読まれ、より長く残ります。不具合を見つけるのに必要なものを記録します。リクエストID、利用者ID、件数、時刻、判断です。

プロンプトと返信を、全文は記録しません。デバッグのために残すなら、マスクしたものを、短い期間、アクセス制限をつけて残します。

よくある間違い

注意

プロンプトをマスクすれば、ログは安全だと考える人が多くいます。返信、エラーメッセージ、元のリクエストには、まだ本当の値が残っていることがあります。文章が通るすべての場所を確かめます。プロンプト、返信、ログ、そしてキャッシュです。

このラボとソースについて: AI コーディングエージェントのラボ:opencode でコードを説明・修正し、その結果を確かめる

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。