「わかった?」と聞くと、「わかりました」と返ってくる。でも、自分の言葉で説明してもらうと、途中で言葉が止まる。
その説明を一人ずつ聞き、つまずきに合う宿題を考え、次の授業に引き継ぐ。やりたいことは明確でも、先生がすべてを続けるには時間が必要です。
そこで作ったのが、先生が授業記録を渡すと、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. 作ったもの
全体の流れ
黄色が人が判断する箇所です。ループは必ずここを通ります。
画面
① 先生:授業メモを渡す
教えた内容を数行書くか、授業資料のPDF・写真を選ぶだけです。生徒全員分の問題文を考える必要はありません。過去の説明と模試の結果から、その生徒に合った問いをAIが用意します。
実際に使っている授業メモはこの程度です。
今日は一次関数の傾きと切片を学習。式からグラフを描く練習をした。傾きが「横に1進むときの縦の変化」であることを説明した。文章題はまだ扱っていない。
② 先生:確認して配信
「先生の確認待ち」にカードが並びます。文面・対象・期限を確認して生徒に送信します。
③ 生徒:AIに教える
AIはまだ知らない聞き手として振る舞い、答えを直接教えず「傾きって、何を表している数なの?」「じゃあ、傾きがマイナスのときは?」と問い返すよう設計しています。
テスト用アカウントに、傾きと切片を取り違える回答と訂正する回答を入力し、AIの問い返しと、訂正の経過が評価に残ることを確認しました。これは模擬対話による機能検証で、実生徒の学習効果は今後検証します。
声でも説明できます。 マイクを押して話すと、その場で会話に書き起こされます。書くのが苦手な生徒ほど、話すほうが説明できることがあるからです(後述しますが、この音声経路には別の意味もあります)。
④ 先生:根拠つきの分析
観点別の理解度と、そう判断した根拠の発言が残ります。点数だけでなく「どの発言でそう判断したか」まで開いて確認できます。
安全上の懸念・生成AIの疑い・繰り返すつまずき・学習停滞は「要フォロー」に集約されます。ガードレールで遮断した入力もここに上がります。
4. なぜAIが必要だったのか
「それ、ChatGPTに生徒が説明すればいいのでは?」という問いには、3つの理由で答えられます。
1. 聞き手が「知らないふり」を続ける必要がある
ChatGPTに説明すると、たいてい途中で正解を教えてくれます。親切ですが、それをやられると今回の目的が成立しません。答えを知っていて、口に出さない相手が要ります。これは出力スキーマとプロンプトの設計で作るものです。
2. 問いが、その生徒の履歴から出る必要がある
「なぜ?」と機械的に返すだけなら定型文で足ります。実際に必要なのは、この生徒が前回どこで詰まったかを踏まえた問いです。過去の対話・評価・模試結果を根拠に問いを選ぶのは、LLMでないと難しい処理でした。
3. 説明の分析は、先生へ返さないと意味がない
生徒とAIの間で完結すると、先生には何も残りません。対話を観点別の理解度と根拠発言に構造化し、次の課題案まで作って先生の画面に返すところまでが1本の業務です。
5. システム構成
環境構成図
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を呼ばない
授業で教えるのは、これからも先生です。トイノワがやるのは、その後ろで「わかったつもり」になっているのを見つけて、先生に渡すところまでです。
リンク
- トイノワ:https://toinowa.vercel.app
- ソースコード(MIT):https://github.com/Ryuto42/toinowa
- OrcaRouter:https://www.orcarouter.ai/ja
- AI HACK 2026:https://aihackathon.jp/
参考文献
- Chase, C. C., Chin, D. B., Oppezzo, M. A., & Schwartz, D. L. (2009). Teachable Agents and the Protégé Effect: Increasing the Effort Towards Learning. Journal of Science Education and Technology, 18(4), 334–352. https://link.springer.com/article/10.1007/s10956-009-9180-4
- OECD国際教員指導環境調査(TALIS)2024 報告書のポイント(文部科学省) https://www.mext.go.jp/b_menu/toukei/data/Others/20251006-ope_dev02-2.pdf
- 我が国の教員の現状と課題 – TALIS 2024結果より –(文部科学省) https://www.mext.go.jp/b_menu/toukei/data/Others/20251006-ope_dev02-1.pdf






