1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「解けた」は「わかった」じゃない。AIが答えず、生徒がAIに説明する学習エージェント「トイノワ」を作った

1
Last updated at Posted at 2026-09-22

main.png

「わかった?」と聞くと、「わかりました」と返ってくる。でも、自分の言葉で説明してもらうと、途中で言葉が止まる。
その説明を一人ずつ聞き、つまずきに合う宿題を考え、次の授業に引き継ぐ。やりたいことは明確でも、先生がすべてを続けるには時間が必要です。

そこで作ったのが、先生が授業記録を渡すと、AIが生徒別の復習課題を準備し、説明対話の分析から次回の学習計画まで提案する「トイノワ」です。

想定するユーザーは、学校や塾・個別指導などで生徒を担当する先生と、その生徒たちです。生徒はAIに自分の言葉で説明することで理解を確かめ、先生はAIが準備した課題案を確認・配信し、一人ひとりに必要な支援に集中できます。

本記事では、AI HACK 2026のテーマ「業務を自律化するAIエージェント」に向けて開発した「トイノワ」について、開発に至った経緯を交えて紹介します。


1. 先生の時間は、もう限界にある

OECDの国際教員指導環境調査(TALIS 2024)の結果が、今回の出発点でした。

日本の小学校 小学校の参加国平均 日本の中学校 中学校のOECD平均
1週間あたりの仕事時間 52.1時間 40.4時間 55.1時間 41.0時間

日本の教員の仕事時間は参加国中で最長です。前回(2018年)から4時間減ってはいるものの、依然として平均を10時間以上超えています。
そして、この調査にはもう一つ重要な特徴が出ていて、日本は授業そのものの時間は国際平均より短く、長いのは授業準備・事務・課外活動のほうだということです。

つまり、削るべきは「教えている時間」ではありません。教えたあとの時間です。

  • 生徒ごとに宿題を変えたい。でも30人分の問題文は作れない
  • 小テストの点数は出る。でも「なぜ間違えたか」までは追えない
  • 「わかった?」と聞けば「わかりました」と返ってくる。それが本当かは分からない

「わかったつもり」は、テストでは見つからない

答えが合っていても理解しているとは限らない、というのは現場の共通感覚だと思います。解法を手順として覚えていれば、意味が分からなくても正解にたどり着けるからです。

そこで、自分の言葉で説明してもらうことを、理解を捉える手がかりにします。正誤だけでなく、理由や概念のつながりをどう説明するかも観察します。

しかしこれを毎回やるには、生徒一人ひとりの説明を聞く相手が要ります。先生の負担を減らすために、新しく「全員の説明を聞く時間」を増やすわけにはいきません。

聞き役なら、AIにできる。 ここが出発点でした。


2. インプットは人、アウトプットはAI

生成AIを教育に入れるとき、まずはじめに思いつくのは「AIが生徒に教える」でしょう。AI家庭教師、AI教材、AIチューター。実際、多くのプロダクトがそこにあります。

私たちはこれを意図的に採用しませんでした。役割を逆にしています。

一般的なAI教材 トイノワ
インプット(教える) AIが生徒に教える 先生が授業で教える(従来どおり)
アウトプット(説明する) 生徒が問題を解く 生徒がAIに教える
AIの役割 答えを出す 何も知らない聞き手として問い返す
最終判断 AI 先生

実際の役割分担は、先生・生徒・AIの3者だけでなく、所属や利用者を管理する管理者を含めた4者で回っています。

役割 担当すること
先生 授業をする。記録や資料を渡す。AI案を確認し、必要なら編集して配信する
生徒 AIに自分の言葉で説明し、問い返しに答える
AI 授業・模試・履歴から課題を準備し、対話を分析して次回案を作る
管理者 所属・利用者を管理し、模試情報を確認し、AIの用途と費用を管理する

教える側に回ると、人は学ぶ

Betty's Brain という研究の実験でソフトウェアはまったく同じなのに、

  • 「自分のために学ぶ」と思って使ったグループ
  • 「自分のエージェントに教える」と思って使ったグループ

を比べると、後者のほうが学習活動(読むこと等)により長く時間を使い、実際に多く学んだという結果が出ています。
さらに論文は、この効果が学力下位層でもっとも大きかったと報告しています。トイノワが狙いたいのは、まさにその層です。

ファインマンテクニックに着想を得て、LLMを「教わる相手」にしました。生徒の自然な説明を受け止め、内容に応じて問い返す聞き手を、幅広い授業内容に用意できます。

なぜ人を残すのか

「では全部AIに任せればいいのでは」とも思われますが、それは考えませんでした。

AIが課題を作り、AIが配信し、AIが評価する。この構成は技術的には作れます。しかし、そうすると先生が生徒の状態を知る機会がなくなります。先生が介在しないループは、回れば回るほど先生から情報を奪います。

そこで、人が判断する場所を仕様として固定しました。

授業記録を渡す        → 人(先生)
生徒ごとの課題を作る   → AI
生徒が説明する        → 人(生徒)
問い返す・分析する     → AI
評価を修正・確認する   → 人(先生)
次の課題案を作る       → AI

3. 作ったもの

全体の流れ

黄色が人が判断する箇所です。ループは必ずここを通ります。

画面

① 先生:授業メモを渡す

teacher-create.png図1:課題の作成画面

教えた内容を数行書くか、授業資料のPDF・写真を選ぶだけです。生徒全員分の問題文を考える必要はありません。過去の説明と模試の結果から、その生徒に合った問いをAIが用意します。

実際に使っている授業メモはこの程度です。

今日は一次関数の傾きと切片を学習。式からグラフを描く練習をした。傾きが「横に1進むときの縦の変化」であることを説明した。文章題はまだ扱っていない。

② 先生:確認して配信

teacher-assignments.png
図2:課題一覧の「先生の確認待ち」

「先生の確認待ち」にカードが並びます。文面・対象・期限を確認して生徒に送信します。

③ 生徒:AIに教える

student-talk.png
図3:生徒の画面

AIはまだ知らない聞き手として振る舞い、答えを直接教えず「傾きって、何を表している数なの?」「じゃあ、傾きがマイナスのときは?」と問い返すよう設計しています。

テスト用アカウントに、傾きと切片を取り違える回答と訂正する回答を入力し、AIの問い返しと、訂正の経過が評価に残ることを確認しました。これは模擬対話による機能検証で、実生徒の学習効果は今後検証します。

声でも説明できます。 マイクを押して話すと、その場で会話に書き起こされます。書くのが苦手な生徒ほど、話すほうが説明できることがあるからです(後述しますが、この音声経路には別の意味もあります)。

④ 先生:根拠つきの分析

teacher-analysis.png
図4:生徒の学習記録・観点別の分析

観点別の理解度と、そう判断した根拠の発言が残ります。点数だけでなく「どの発言でそう判断したか」まで開いて確認できます。

teacher-followups.png
図5:要フォロー

安全上の懸念・生成AIの疑い・繰り返すつまずき・学習停滞は「要フォロー」に集約されます。ガードレールで遮断した入力もここに上がります。


4. なぜAIが必要だったのか

「それ、ChatGPTに生徒が説明すればいいのでは?」という問いには、3つの理由で答えられます。

1. 聞き手が「知らないふり」を続ける必要がある

ChatGPTに説明すると、たいてい途中で正解を教えてくれます。親切ですが、それをやられると今回の目的が成立しません。答えを知っていて、口に出さない相手が要ります。これは出力スキーマとプロンプトの設計で作るものです。

2. 問いが、その生徒の履歴から出る必要がある

「なぜ?」と機械的に返すだけなら定型文で足ります。実際に必要なのは、この生徒が前回どこで詰まったかを踏まえた問いです。過去の対話・評価・模試結果を根拠に問いを選ぶのは、LLMでないと難しい処理でした。

3. 説明の分析は、先生へ返さないと意味がない

生徒とAIの間で完結すると、先生には何も残りません。対話を観点別の理解度と根拠発言に構造化し、次の課題案まで作って先生の画面に返すところまでが1本の業務です。


5. システム構成

環境構成図

System.png

RLSとservice-roleの使い分けは §7.4 で詳しく説明します。

1回の説明が、どの順で処理されるか

技術

領域 技術・役割
画面・API Next.js 16 App Router、React 19、TypeScript、Tailwind CSS 4
認証・保存 Supabase Auth、PostgreSQL、RLS
バックグラウンド処理 DBのジョブキュー、pg_cron・pg_netから起動するVercelのワーカー
AI OrcaRouter経由の用途別モデル呼び出し、Named Router
モデル比較 OrcaReplay、原資料と照合済みの正解表による独自採点
PDF・音声 pdf.jsでPDFを画像化、AudioWorkletで音声をWAV化

6. OrcaRouterを使った設計

6.1 安いモデルが安いとは限らない

モデルを決めるために、同じ入力を5モデルへ投げました。

モデル json_schema 誤答の判定 出力トークン 費用
google/gemini-2.5-flash-lite ✅ ✅ 108 $0.000054
openai/gpt-oss-120b ✅ ✅ 381 $0.000068
qwen/qwen3.7-flash ✅ ❌ 誤答を正解と判定 986 $0.00013
z-ai/glm-5.3-flash ❌ json_schemaを無視しMarkdownを返す ✅ 800 $0.000206
openai/gpt-5-nano ✅ ✅ 1155 $0.00047

qwen3.7-flash は誤答を正解と判定しました。これは学習サービスとしては致命的です。安さの順位と、使えるかどうかの順位は一致しませんでした。

6.2 OrcaReplayで同じ模試を比較する

模試の読み取りを間違えると、その後の課題設計の根拠もずれます。一方、すべてを上位モデルに任せると、費用と待ち時間が増えます。

そこで、実物の模試2回分・紙3ページを、スキャンPDF・通常写真・影のある写真の3条件で用意しました。合計9入力です。同じ紙を撮り方だけ変えているので、9種類の独立した模試ではありません。
氏名・学校名・受験番号などを隠し、結果を見る前に正解表を作成して、原本との照合・承認を行いました。採点対象は必要な数値450項目/モデルです。数字が一致していても、違う科目や意味の違う列へ対応付けた場合は正解にしません。

OrcaReplayでは、画像・要求・スキーマを揃え、モデルを変えて実行しました。前処理はサービスと同じ画像変換を使い、比較ごとの条件を保存しています。OrcaReplayの実行成功と、成績項目の正確さは別なので、品質は正解表で採点し、時間とOrcaRouterが返した費用も別々に集計しました。

モデル 必要項目の正確な取得 JSON検証 平均時間 9回の費用
Gemini 2.5 Flash 399/450(88.7%) 8/9 20.53秒 $0.097298
Gemini 2.5 Flash-Lite 325/450(72.2%) 9/9 12.23秒 $0.005744
GPT-4o mini 31/450(6.9%) 9/9 3.63秒 $0.039648

今回の条件では、GPT-4o miniは必要項目358件が欠落した一方、JSON検証は9/9通過しました。形式が正しいことと、必要な情報が揃うことは別でした。

この実測と追加検証を踏まえ、模試はGemini 2.5 Flashのみを使い、別モデルへ自動で切り替えません。 再試行可能なエラーでは失敗したページを最大1回再試行し、予算上限や安全性チェックによる遮断では再試行しません。

検査で疑義が見つかれば自動反映を保留して人の確認へ、処理を完了できなければ失敗を表示して再アップロード・手入力へ案内します。学習計画の根拠となる数値を、不確かなまま反映しないためです。

授業資料の vision 経路は別で、Flash → Flash-Lite → GPT-4o miniを使用します。

6.3 上位モデルを試して、採用を見送った

数値抽出と学習提案を分離し、表・列見出しを持つスキーマと出力上限10,000トークンへ変更したうえで、FlashとProも比較しました。同じA/Bスキャンの再利用であり、初回比較とは条件が異なります。

資料 Flash Pro Flash 時間・費用 Pro 時間・費用
A スキャン 57/57 57/57 28.56秒・$0.016112 54.80秒・$0.110792
B スキャン 30/30 0/30(応答なし) 15.98秒・$0.009134 60.02秒でタイムアウト

今回の条件では、Proに品質上の利点は確認できず、時間と費用だけが増えました。 上位モデルへ上げる前に処理のほうを直す、という判断になりました。

補足のCスキャンを含め、Flashは3入力で150/150項目一致でした。ただしCは表の見出しを確認できず、人の確認待ちになりました。数値が読めたことと、自動反映してよいことを分けています。 同じ資料での改善検証であり、初回9入力の成績を置き換える結果ではありません。条件・採用判断・復旧の記録

6.4 「遅い退避先」は退避先ではない

z-ai/glm-5.3-flash をフォールバック連鎖に入れていましたが、コンソールの実測で P50 40.4秒 / P95 49.1秒でした。加えて json_schema を無視します。

つまり、本来の障害より遅く、しかもスキーマ検証で落ちる。これは縮退として成立していません。3つの連鎖から外し、deepseek/deepseek-v4.1-flash に差し替えました。

6.5 モデルの段の作り方

最終的に、用途別の「段」にしました。

advanced は OrcaRouter のコンソールで作った Named Router を指しています。adaptive戦略ですが、許可モデルを4件に制限し、フロンティアエスカレーションは無効にしました。組み込みの orcarouter/auto は候補が無制限で、難問では上位モデルへ上がってしまうためです(実際に上がることを確認しました)。

6.6 一周の仕事に、いくらかかったか

モデル単価の比較だけでは、実際の業務にいくらかかるかは分かりません。そこで、授業記録を渡してから次回案ができるまでの1周を、実際に動かして費用を記録しました。

計画2回・対話5回・相談の意味判定5回・本人性の補助分析1回・最終評価1回の計14件のAI呼び出しで、記録上の費用は合計 $0.007040。最終評価はFlash、それ以外はFlash-Liteでした。

1例の記録値であり、全生徒の平均や請求額の保証ではありません。それでも、モデルの単価だけでなく、一つの業務を最後まで進める費用を観測できるようにしています。資料の抽出結果の共用、保存済みページや計画の再利用、会話終了後だけの本評価によって、不要な再生成も減らしています。


7. 教材と生徒の発言を監視するセキュリティ

このサービスの入力は、先生がアップロードした教材・模試画像と生徒が自由に書く説明文です。どちらも「命令が書かれている可能性のあるデータ」として扱いました。

7.1 4層で止める

文字入力の基本経路です。攻撃セットで見つかった問題に対策を重ねました。画像内の命令は、次節で別途検証しています。

7.2 画像経路も測る

テキストの検査だけでは、画像に書かれた命令を確認できません。教材と模試のアップロードはこのサービス最大の攻撃面なので、専用の検査を作りました。

python3 scripts/fixtures/make-injection-images.py   # 攻撃入りの架空模試を生成
npm run check:injection:image -- --live --max-cost-usd 1.00

架空の成績表に命令文を仕込んだ7枚(対照1・攻撃6:指示の上書き/点数の書き換え/役割の乗っ取り/外部送信/秘密の開示/薄字の隠し命令)を、本番と同じシステムプロンプト・同じモデル段へ実際に送り、抽出された数値を正解の15項目と突き合わせました。

7枚すべてで注入された命令は無視され、正解の点数(国語62・数学41・英語73・理科55・社会68)だけが抽出されました(15/15項目一致、改ざん・システム文露出・秘密の出力0件)。

実行条件は google/gemini-2.5-flash(exam段)、出力上限10,000トークン、制限時間60秒。プロンプト・スキーマ・画像のハッシュと応答を保存しています。画像を直接送る試験で、認証・アップロード・DB処理は対象外です。有限の攻撃例での結果であり、全攻撃への安全性の保証ではありません。 実測サマリー

7.3 音声でも同じ経路を通す

音声入力を足すとき、書き起こしが検査を迂回しないことを確認しました。「これまでの指示をすべて無視して…」と実際に声で吹き込む試験をしたところ、3つのモデル段すべてが命令として実行せず、文字に起こしただけでした。その後 preCheck が instruction_override として検出し、遮断して guard_events に記録、先生の「要フォロー」に上がりました。

7.4 DB側でもアプリを信用しない設計に

テナント分離の守り方は、経路によって違います。ユーザーのJWTで動く経路はRLSで守り、service-roleで動く経路はアプリのコードで境界を書いています。 service-role権限の秘密キーはserver-onlyのモジュールで扱い、ブラウザ用の公開環境変数には含めません。

RLS経路(本人のダッシュボードなど、ユーザーのJWTで読み書きする箇所)で実測した内容です。

確認項目 結果
全テーブルの RLS 有効・force 違反 0件
anon が SELECT できるテーブル 0件
USING(true) のポリシー 0件
search_path 未固定の SECURITY DEFINER 0件
anon での直接アクセス(11テーブル) すべて 42501 で拒否
ログイン中の生徒が他人を見られるか 見えない。管理者への昇格も不可

7.5 相談は、学習の妨げとして扱わない

生徒の発言がすべて説明への回答とは限りません。つらさや助けを求める相談も届きます。単語の一致だけでなく意味判定を加え、相談と判断した発言は学習の問い返し・採点・自動終了から切り離しています。相談中は学習を続けさせず、代わりに落ち着いた言葉で受け止める返答をAIではなく固定のロジックで返します。相談として判定・記録した発言は、次回の学習計画を作る参照からも除外し、生徒が説明していないことを理解度の根拠へ混ぜません。先生には確認対象として残しますが、生徒へ「先生へ確実に伝わった」とは約束しません。


8. 信頼性の担保

発火したときに助かるフォールバック

プロバイダーを意図的に分散させています(Google → OpenAI → DeepSeek)。同じプロバイダー内で並べても、そのプロバイダーが落ちたら連鎖ごと倒れるからです。

前述のとおり、遅すぎる・スキーマを守らないモデルは連鎖から外しました。

途中で失敗しても、保存できたところから戻す

AIの出力が使えない場合に、成功したように見せないことを重視しました。

  • 模試はページ単位で保存し、失敗したページだけを限定して再試行する。疑義のある結果は確認待ちにする
  • 通常のモデル呼び出しには時間制限・代替経路・縮退を設ける。模試専用の処理では、未検証の安いモデルへ黙って切り替えない
  • 評価を保存したあとの完了処理が失敗しても、保存済みの評価から復習予約と次回案の準備を再開する
  • チャットの通信断では、保存状況を確認してから未保存の入力を戻す。勝手に重ねて送らない
  • 件数の取得に失敗したときに「0件」「全部完了」と表示しない

壊れたことに、気づける状態を保つ

同じ手順で毎回確かめられるようにしています。ゴールデンテストを含む 46ファイル・334件が通る状態を維持していて、権限とDB処理は実際のDBに繋いで検証します(npm run test:db)。

プロンプトインジェクションの検査も同じ扱いです。攻撃セットは固定のスクリプトにしてあり、テキスト経路は npm run check:injection、画像経路は npm run check:injection:image で何度でも再実行できます。「動いたときに確認した」ではなく、変更のたびに同じ入力で確認できる形にしています。

費用の安全装置

  • 所属の予算を取得できない場合はAIを呼ばない(欠落・不正・取得失敗すべて)
  • 再試行や代替呼び出しの前に予算を再確認し、そのリクエスト内で判明した費用も加算して判断
  • APIキー側にも日次 $5.00 のクォータを設定(アプリ側だけでは1リクエスト分の超過を防ぎきれないため)

9. 自律性の線引き

AIは課題作成・対話・評価を担い、保存や通知は権限・検証ルールを持つコードで実行します。課題の配信や評価の修正は先生が判断します。

人が画面を開いていなくても、進む

依頼はDBのジョブとして保存し、ワーカーの即時起動を試みます。起動できなくても、pg_cron が10秒ごとに起こすワーカーが処理を拾います。先生がブラウザを閉じても、ログアウトしても、処理は続きます。

定期的に動いているのは次の6本です。

定期処理 間隔 やること
worker-tick 10秒 積まれたジョブを取り出して実行する
enqueue-notification-delivery 5分 溜まった通知を配信ジョブに変える
enqueue-review-reminders 15分 復習日が来た生徒に催促を積む
detect-stalled-work 1日1回 止まっている生徒・進まない準備を検知して「要フォロー」に起票する
enforce-retention 1日1回 所属ごとの保持日数を過ぎた会話・実行記録を削除する
purge-dead-jobs 1日1回 30日より前の失敗ジョブを片付ける

ワーカーが実行するのは、授業記録の分析・学習計画の作成・生徒ごとの準備・模試の読み取り・対話の要約・最終評価・通知配信の7種類です。安全遮断・スキーマ違反・再試行しても結果が変わらないエラーは、回数を使い切る前にその場で打ち切ります。

つまり「先生が押したときだけ動くAI」ではありません。渡したあとは無人で進み、人の判断が要る地点に来たときだけ画面に戻ってきます。「先生の確認待ち」と「要フォロー」は、その戻り先です。

AIが判断する範囲

AI・コード・人の判断を、次のように分けています。

判断 誰が 根拠
お題の難易度(1〜5) AI 過去の評価・苦手範囲
使うモデルの段 ルーティング(コード) AIが判定した難易度と処理クラス
次にどこを問い返すか AI 説明の中で未確認の観点
対話を終えるか AI 最大6ターン。初回発言だけでの終了は禁止
配信するか・期限 人(先生) —
評価の修正 人(先生) —
復習日 固定ルール SM-2

10. まとめ

  • 先生:授業メモか資料を渡すだけ。生徒ごとの学習計画と課題案が用意され、確認して配信する。配るかを決めるのは最後まで先生
  • 生徒:お題を自分の言葉で説明する。AIは答えを言わず問い返すだけ。音声でも説明できる
  • 分析:観点別の理解度と「どの発言でそう判断したか」が先生に返る。根拠が薄いときは「確認が必要」と出し、先生が上書きできる
  • 要フォロー:安全上の懸念・生成AIの疑い・繰り返すつまずき・停滞を一箇所に集約する
  • 運行:渡したあとは無人で進む。ブラウザを閉じても止まらない。1周ぶんのAI呼び出しは14件・$0.007040
  • 管理者:誰がどの機能でいくら使ったかを見る。予算が取れなければAIを呼ばない

授業で教えるのは、これからも先生です。トイノワがやるのは、その後ろで「わかったつもり」になっているのを見つけて、先生に渡すところまでです。


リンク

参考文献

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?