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

無料レッスン

スキーマで JSON を検証する方法

このレッスンのあとで: 返事を、スキーマで確かめられる。

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

正しい JSON、間違ったデータ

スキーマで JSON を検証するとは、レコードの各項目を、規則のリストと照らし合わせることです。規則は、型、必須かどうか、許される値を決めます。読み取れた JSON でも、ページ数が「たくさん」という文字列のことがあるからです。

図書館のアプリが、本のレコードをモデルに頼みます。返事は問題なく読めます。ところが、ページ数が文字列の「180」で届き、合計のページが意味のない数字を出します。

このレッスンのゴール

メモ

返事を、スキーマで確かめられる。

キーワード

ことばいみれい
スキーマレコードの項目を確かめる、チェックリスト
型項目が持つ値の種類integer
必須必ずなければならない項目
許される値項目が取れる、唯一の値小説、歴史、科学
違反規則を破る、1つの形
レコード項目の集まりを持つ、1つのオブジェクト

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

スキーマはチェックリスト

読み取りで分かるのは、文字列が JSON だということだけです。データが正しいかどうかは分かりません。そのためにスキーマを使います。項目ごとに1つの規則を持つ決まりです。

規則は、項目の型、必須かどうか、場合によっては許される値を決めます。JSON Schema という標準は、この規則を決まった書式で書きます。ここでは、自分のチェックリストをコードで書きます。

例: 本のスキーマ

項目型必須許される値
titlestringはい任意の文字列
pagesintegerはい任意の整数
genrestringはい小説、歴史、科学
ratingnumberいいえ任意の数値

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

最初の1つだけでなく、すべての違反を集める

最初の違反で止まる確認は、教えてくれることが少ないです。すべての違反を、短い文字列として、リストに集めます。たとえば pages: 型が違う です。

このリストには、2つの使い道があります。レコードを受け入れるかを決めます。次のレッスンでは、これをモデルへの知らせとして送り返します。

モデル用の補助を書く(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\");' '' '// スキーマは、項目ごとに1つの規則を持つチェックリストです。' 'const schema = [' '  { name: \"title\", type: \"string\", required: true },' '  { name: \"pages\", type: \"integer\", required: true },' '  { name: \"genre\", type: \"string\", required: true, allowed: [\"小説\", \"歴史\", \"科学\"] },' '  { name: \"rating\", type: \"number\", required: false },' '];' > check2.js"

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

スキーマの確認を書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function isRecord(x) {' '  if (x === null) return false;' '  if (Array.isArray(x)) return false;' '  return typeof x === \"object\";' '}' '' 'function typeOk(value, type) {' '  if (type === \"string\") return typeof value === \"string\";' '  if (type === \"integer\") return Number.isInteger(value);' '  if (type === \"number\") return typeof value === \"number\";' '  if (type === \"boolean\") return typeof value === \"boolean\";' '  return false;' '}' >> check2.js"

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

スキーマの確認を書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 最初の1つだけでなく、すべての違反を集めます。' 'function faults(record, schema) {' '  if (isRecord(record) === false) return [\"オブジェクトではない\"];' '  const found = [];' '  for (const rule of schema) {' '    if (Object.prototype.hasOwnProperty.call(record, rule.name) === false) {' '      if (rule.required) found.push(rule.name + \": 欠けている\");' '      continue;' '    }' '    const value = record[rule.name];' '    if (typeOk(value, rule.type) === false) found.push(rule.name + \": 型が違う\");' '    else if (rule.allowed && rule.allowed.includes(value) === false) found.push(rule.name + \": 許可されていない値\");' '  }' '  return found;' '}' >> check2.js"

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

スキーマの確認を書く(4)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function pullJson(text) {' '  const start = text.indexOf(\"{\");' '  const end = text.lastIndexOf(\"}\");' '  if (start === -1 || end < start) return null;' '  try { return JSON.parse(text.slice(start, end + 1)); } catch (error) { return null; }' '}' '' 'const records = [' '  { title: \"川の地図帳\", pages: 240, genre: \"科学\" },' '  { title: \"潮見表\", pages: \"180\", genre: \"歴史\" },' '  { title: \"月の手紙\", pages: 96, genre: \"詩\", rating: 4.5 },' '  { pages: 310, genre: \"小説\", rating: \"良い\" },' '];' 'records.forEach(function (record, at) {' '  const found = faults(record, schema);' '  console.log(\"レコード \" + (at + 1) + \":\", found.length === 0 ? \"ok\" : found.join(\"、\"));' '});' >> check2.js"

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

スキーマの確認を書く(5)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const messages = [{ role: \"user\", content: \"大学図書館の本を1冊考えてください。キー title (文字列)、pages (整数)、genre (小説、歴史、科学のどれか) を持つ JSON オブジェクトだけで答えてください。\" }];' '  // 記録済みサンプル。モデルが答えないときだけ使う。わざと2つの規則を破ってある。' '  const sample = JSON.stringify({ title: \"静かな港\", pages: \"210\", genre: \"推理\" });' '  const reply = await ask(messages, sample, 100);' '  const record = pullJson(reply);' '  const found = faults(record, schema);' '  console.log(\"モデルのレコード:\", JSON.stringify(record));' '  console.log(\"違反:\", found.length === 0 ? \"なし\" : found.join(\"、\"));' '}' 'main();' >> check2.js"

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

確認を実行する

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

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

出力の一例

行表示されるもの
レコード 1ok
レコード 2pages: 型が違う
レコード 3genre: 許可されていない値
レコード 4title: 欠けている、rating: 型が違う
記録済みサンプル: ...モデルが答えなかったときだけ、5行目に出る
モデルのレコード、違反実行ごとに変わる。モデルのレコードと、その違反またはなし

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

よくある間違い

注意

「180」と 180 は同じものだと考える人が多くいます。見た目は似ていますが、プログラムは、文字列を数値のように足したり比べたりできません。値の見た目ではなく、型を確かめてください。

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

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