JANOGerのみなさま、こんにちは。
プログラムサポーター委員・プログラム委員の齋藤です。
さて、前回のJANOG57に引き続き今回もプログラムサポーター委員(以下、PS委員)として、プログラム委員(以下、PC委員)が行う作業の自動化や、参加者の皆さんがアンケートに回答しやすくなるように。。などの改善を行いましたので、またまた長いNewsLetterを書いてみることにしました。
PS委員っていったいなんぞや?などの部分は、前回のJANOG57のNewsLetterをご覧ください。(このNewsLetterは前回の続きと思ってください。)
今回PS委員が成し遂げたこと
今回のPS委員の成果を少しまとめてみました。いろいろ変わっていることが多いです。特にスタッフ側(PC委員)ですね。参加者のみなさんにも恩恵がある改善もあります。
- PC委員が行う作業を大幅に自動化しました!
- 今まで、PC委員の作業を自動化するスクリプト郡を「pc-app」と呼んでいましたが、今回のリニューアルを機に「Program-app」という名前に変わりました。本文中では単に「アプリ」や「Webアプリ」と表現します。
- アンケートへ回答しやすくすることを目的に、プログラムアプリと連携したアンケートシステムを新規に導入しました!
これら改善を約4か月間にわたる長い期間をかけて行ったわけですね。(どうもカレンダーをみると、3月10日には初回のPSミーティングをしていました。振り返ると長い長ーい道のりでした。ちなみに、JANOG57のFinal Staff Meetingは2月27日でしたから、短いスパンですね。)
さて、今までのPS委員は「大まかな自動化方針に従って、Google App Script (以下、GASという)を主軸にプログラミングする」というプログラマー要素が強かったわけですが今回は違います。当初より「PC委員の大半の作業をアプリ化(自動化)する」という目標がありましたので、会期までの日数等を踏まえ「今回はAIを全面に活用する」という方針のもと作業が進められました。
「なーんだ。AIなんだ。」という感じがしますが、実はかなり大変な道のりだったんです。
というわけで、今回は「どんなシステムの構成になっているの?」という軸よりかは「どんなことが大変だったの?工夫したの?」という軸で記事を書いてみます。
PC委員って何をするの?
といいつつ、本題に入る前にまずは、PC委員が何をしているのかざっくりまとめてみます。
採点, シェパーディング
みなさんから頂戴した応募について、告知されている採点観点に従って点数付けを行います。特に難しい作業はありません。
応募によっては、応募者に対して応募内容について追加でヒアリングを行い、その回答を採点に反映させる「シェパーディング」が行われます。シェパーディングで応募内容の変更があった場合は、その内容を応募内容に反映させる必要があります。(PC委員は反映後の内容に対して採点を行います。)
最強のプログラムづくり・選出
PC委員は上記の採点結果を基に、各々の最強のプログラム(タイムテーブルのことです)を作ります。「このプログラムの横はこれがいいな」、「このあと休憩を挟もう」など色々なこだわりが反映されます。
各PC委員が作成した最強のプログラム案について、PC委員は「これが最強だ!」と思う案に対し票を投じます。最多票を獲得したものが、最強のプログラムになり皆さんが目にするタイムテーブルとなります。
採否通知
応募者に対して、プログラムの採否を通知します。その際、プログラムの位置づけを把握するために採点結果も合わせて通知されます。
登壇者打ち合わせ
各プログラムに対して、原則1名以上のPC委員が担当PCとして割り当てられます。担当PC委員は、登壇者と各種事務連絡やプログラムの方向性の確認、またブラッシュアップ作業といった、よりよい発表となるようなアシストを行います。
当日
当日は、司会や議論用Slackへの通知といった議論促進・プログラム進行に関することを行います。
JANOG58におけるアプリ化の契機
以上が概ねの作業となりますが、これらを基本的にはGoogle SpreadsheetやConfluenceを活用して進められていました。しかし、以下のような問題が発生します。
- 準備が大変
- たとえば、最強のプログラムづくりではGoogle Spreadsheetを用いますがレイアウトの準備など手作業での準備がともなう工程が数多くありました。

- 情報が分散しがち, 更新漏れが起きやすい
- 過去の自動化によって、応募時点の情報はGoogle Spreadsheetを擬似的なデータベースとして情報をストアしていってましたが、採否決定後の情報についてはJANOG Webページに記載するものがマスター的な位置づけとなっており、せっかくの情報を有効に活用できていませんでした。また、同様の内容がConfluenceにもあるため「Webページは更新したけど、Confluenceを更新していない」なんでことも。。
- Google Spreadsheetだからこその制約が大きい
- 前回のNewsLetter末尾にも書いたのですが、何しろ処理が重かったり準備が大変だったりと回を増すごとに技術的コストが高くなっていました。
- また、あくまでもこれは「表計算ソフト」であって「データベース」ではありません。ゆえに、様々な機能を作り込んでいく上での限界を生み出していました。
そこで今回は、「情報は全部データベースに格納しよう!」という事になったわけですが、これが大成功。おかげで様々な形で情報を活用することができ、また処理も劇的に高速化することができました。(感覚的には数分かかっていた処理が数十msecレベルに、という感じです)
データベースに情報を持つようになったというのが最大の革命ではないでしょうか。
自動化の方針
活動に先立って、JANOG58における自動化方針は以下のようになりました。
- GASから完全に脱却する
- 情報はデータベースに持つ
- Webアプリ化し、Web上から採点や最強のプログラムづくり、採否通知などの様々な作業を行えるようにする
- Confluence連携し、アプリで情報が更新されるとそれが反映されるようにする
できあがったもの
Backend, Frontendの両方ともAIによる成果物です。(人間は何をするんだ?というのは下に書いていますので読み進めてね。)
見づらいと思うので、気になる方はページをズームしてください。
結果として、上記に挙げたようなPC委員の作業はほぼアプリ化(自動化)することができました。(Webページの内容が自動で書き換わる!といったようないわゆるWordpress連携までは達しませんでしたので、今後の課題としたいですね)



AIと人間との役割の分け方
基本的に、人間は設計とコードレビュー・動作テスト、AIはコーディングという分け方をしています。
人間がやることパート
設計
大半の作業は「タスクの分解・操作の分解」と「データモデリング」です。(この工程が一番の肝です)
例えば、最強のプログラム案作りを対象に考えてみましょう。ひとえにプログラム案を作るといっても以下のようなタスク/操作が考えられます。
- Chairによってベースタイムテーブルを作成する
- いわゆる、配置する場所やプログラムが決まっているものを落としこむことです
- Chairによって制約を設定する
- このプログラムは、同時刻に他の部屋でプログラムを開催してはならない(シングル必須)などを登録します
- Chairによって休憩などの非プログラム情報を入力する
- 休憩などはタイムテーブル上に配置されるものの、プログラムではありません
- Chairによって開会式などの、応募ではないプログラムを登録する
- 開会式やProduce枠といったプログラムは通常応募プログラムではないため、アプリに情報がありません。これらを配置するために必要な情報を入力します
- PC委員は上記の情報をもとに、プログラムをタイムテーブルに配置する
- ベースタイムテーブルの内容を変更することはできませんし、制約に反するプログラムの配置はできません
- PC委員はタイムテーブル配置の関係でプログラム時間を変更する
- 場合によっては応募時の希望発表時間を変えて配置することもあります
- PC委員はChairに編成案を提出する前に、システムによる制約チェックを受ける
- 配置すべきプログラムが配置されているかなどの、プログラム配置時にはチェックできない制約を遵守しているか判定します
- PC委員はChairにアピールコメントとともに編成案を提出する
- 編成案を提出後は、その編成案を編集できないようにするロックをかける必要があります
- PC委員はChairに提出した編成案を期間内であれば取り下げできる
- もちろん、期間外の場合は提出・取り下げの一切をできてはなりません
ざっと洗い出してもこれだけ見つかるわけです。実際にはもっと細かい分解がなされます。(制約の設定について、この制約とこの制約は共存不可など。。)
タスク/操作の分解と並行してデータモデリングも行います。ざっと上を読んでも「プログラムといっても”通常応募プログラム”と”手動で登録するプログラム”と”非プログラム”があるんだな」というのがわかります。どのようなエンティティがあり、またどのようなアトリビュートを持つかを洗い出します。
ここでの設計段階が、AIを活用したアプリ開発の最大の肝ではないでしょうか。アプリ開発を進めていく上で「仕様として曖昧な部分があると、過去のプロンプトや一般常識から勝手に仕様を類推する」ということを防ぐためですね。実際、これとの戦いでもありますし、一番苦労した点でもあります。
単に、「こういう情報があって、PC委員はこれをタイムテーブルに配置するんだよ」という指示だと類推によって以下のような想定しない操作ができてしまうでしょう。
- PC委員が、Chairの作成したベースタイムテーブルを変更できてしまう
- シングル必須などの制約を無視して配置できてしまう
- 1度配置したプログラムを再度配置できてしまう
想定外の挙動を作り込まれないためにも、可能な限りタスクとそこで行われる操作を分解し、またエンティティやアトリビュートを漏れなく認識することが最大の工夫であり、今回の実装を進めるうえでの最大のポイントでした。
ただ、最初に完璧な分解とデータモデリングを行えるかというと難しいところがあります。そのため、「ある程度完成形と思われるレベルまで洗い出し、プロンプト作成」→「動作確認のうえ、洗い出しきれていない要件を洗い出し、再度指示」という工程を繰り返し行います。
AIに指示を与える上で必ずしもプログラムチックに指示を与える必要はありませんでした。定性的な表現でも、よしなに認識してくれる箇所が多く大変助かりました。(むしろ、この方がAIにとっては認識しやすいのかな?と思ったり。)
参考に担当PCなどの役割を決めるAPIを作るために出した初期プロンプトを置いておきます。
次のようなAPIを書いて
- このAPIは確定したプログラム(Fixしたもの)に対して、プログラムの略称及びスタッフの役割を決めるためのAPI
- 役割を割り当てるのは、通常プログラムと手動登録プログラムのみ
- programフォルダ配下に作って
- エラーを返す type の宣言などが再度必要と思います。
- scoring 配下の handle.go ファイルを参考にして
- utils の IsTimetableVoteEnded() が true でないと実行できません
- utils/system.go を参考にして
- データベースの設計
- fix_timetables
- short_code / プログラム略称 [追加]
- fix_program_staff (プログラム役職をFixさせるためのテーブル)
- fix_timetables.id (*)
- users.id (*)
- role (manager, moderator, room_chair のいずれか) (*)
- created_at
- updated_at
- 上記 * つきの 3 キーの複合主キーでよい
- fix_program_staff_enquete (プログラム役職のアンケートを記録するためのテーブル)
- fix_timetables.id (*)
- users.id (*)
- priority (1が二重丸, 2が丸, 3がバツを意味する)
- 上記 * つきの 2 キーの複合主キーでよい
- APIについて
- /program/staff/list [GET] [pc, Chair, ps]
- 割り当て対象のプログラム(通常応募, 手動登録)一覧を 日ごと, 部屋ごと に 時間順で返す
- Day1 - 2026/07/15 [日付は会期開始日が system_configs.start_session_date に記録されてるのでDBから読み取って計算する]
- 部屋名 [rooms.room_nameを読み取って当てはめる]
- 10:00 - 11:00
- program_id, title, short_code
- 11:00 - 12:00
- program_id, title, short_code
- /program/staff/enquete/me [GET] [pc]
- 自分自身のアンケートの回答状況を返す
- program_id, priority
- priority が 1, 2 であるプログラムの裏(少しでも他部屋で時間が被っているプログラムのこと)の id を返す
- /program/staff/enquete/other [GET] [pc, Chair, ps]
- 自分以外のPC委員の回答状況を返す
- program_id, user_id, priority
- /program/staff/enquete [POST] [pc]
- アンケートの回答を送信する
- program_id, priority
- PCユーザ以外の回答を記録することができない (バリデーションを実装)
- /program/short_code [POST] [Chair, ps]
- プログラムへのショートコードを割り当てる
- program_id, short_code (重複する short_code を割り当てることはできない)
- /program/staff/fix [POST] [Chair, ps]
- プログラムへのスタッフ役割を割り当てる
- program_id, user_id, role を受け取って割り当てる
- このとき、裏被りは許容できない
- 例: 10:00~11:00 で room1 のプログラムを担当する人は、それに少しでも被るプログラムはいかなるrole も割り当てできない
- /program/staff/fix-support [GET] [Chair, ps]
- どのプログラムには誰を割り当てできない という情報を返す
- program_id, user_id を返す
- /program/staff [GET] [誰でも]
- system_configs の view_roll_open が false のとき、Chair, ps のみ実行可能
- /program/staff/list [GET] に スタッフ役職を加えたものを返す
- Day1 - 2026/07/15 [日付は会期開始日が system_configs.start_session_date に記録されてるのでDBから読み取って計算する]
- 部屋名 [rooms.room_nameを読み取って当てはめる]
- 10:00 - 11:00
- program_id, title, short_code
- manager: [user名, user名] [users から読みとる。managerが複数存在する可能性がある]
- moderator: user名
- room_chair: null [まだ割り当てがないとき]
- 11:00 - 12:00
- program_id, title, short_code
/program/staff/ura-list に 裏被り一覧 (program_id, title, short_code)を返すものも追加して
* program_id (このプログラムの裏一覧が以下に続く)
* program_id, title, short_code
コードレビュー
AIが出したコードを、一応、人間がチェックします。
この段階で「あれ、ここでこの属性を変更できちゃ駄目でしょ」という要件漏れや「ここはこういう実装にすればもっといいんじゃない?」という提案をすることができます。
ここは品質を高めるための作業です。(といっても最初に出すプロンプトが完璧なほど、品質の高いコードを出力してくれるので、その時は「コードレビューは不要なんじゃないか?」と思うときもあるほどでした。)
動作テスト
実際にステージングにデプロイして、挙動を確認します。基本的には考えられうる操作やデータを用いて人間が検証し、期待と違う部分があればAIに指示して直してもらってまた確認というのを完成まで繰り返します。
この部分もAI化すると品質はだいぶ変わるかもしれませんが、テストデータセットの準備などのコストを考えると難しいところです。
AIがやることパート
ひたすらにコードを書いてもらいます。書いて書いて書き続けてもらいます。ときに、実装のための提案をしてもらったり、実装上のディスカッションを対話的に行って実装を進めてもらいます。
UI/UXの追求
Webアプリを行う上で考慮しなければならないのが、UI/UXです。特に、今回はUXの部分をこだわっています。
実は、ここがかなり苦労した点ですね。
「こういう操作をするとき、こういう情報も出してほしいな」、「こういう操作をしたとき、こういう動作をしてほしいな」というアプリ上の構想と、過去のPCスタッフの経験を基に設計を固めていきます。
UI/UXの部分をAIとともに作り込んでいく時に困ったのが、画面上のレイアウトや挙動を言語化することです。人間同士のやり取りなら、「ここをこういうふうに操作するとね」のように抽象的に表現できることもありますが、AIが相手だとそうはいきません。「どこどこにこういうボタンがあって、これをクリックするとこういうオーバレイがでて。またオーバレイの中身はこういう情報があって。このパーツは文字数が多いと別のパーツに被るので。。。」というやり取りを繰り返します。実に重い作業でした。
バックエンドの挙動というのは、しっかりと言語化することが割と容易でしたが、挙動の言語化に視覚の言語化という要素が加わることでプロンプトを考える難易度が更に増しました。
「違う違うそうじゃない、これは〜〜〜〜っていうことなんだよ」、「なんでそういう見た目にしちゃうの」、「このボタンがズレてるよ」、「ページをスクロールするとこのパーツが別のパーツの下に回り込んじゃうよ」など人間が確認して気づいたポイントをひたすらにAIと殴り合います。
人間の言葉というものは実に曖昧です。もっとうまくやる手法があったのかもしれませんが。。
参考に、PC Chairがプログラム役職の割り当てを行うためのフロント生成プロンプトを置いておきます。これ単体で成功するわけもなく、このあと数回にわたる殴り合いをして、ようやく実装が決着します。
担当を割り当てる ボタンを押下した後のページ (AssignRolesOverlay.tsx) について仕様を以下の通りとしますので作成して
* ページレイアウトは RoomConfigOverlay.tsx と同様 (ただし、表が横長なのでページ(描画)幅は広げる)
* タイトルは プログラム担当等割り当て
* 説明は以下の通り
* プログラムごとのスタッフ役割、及びプログラムの短縮名 (ショートコード) を操作します。
* 割り当てを受けているスタッフについて、その裏に当たるプログラムには一切の役職を設定できません。
* プログラムには、 プログラム担当 (複数名), 部屋チェア (1名), 司会 (1名) を割り当てることができます。
* 割り当てを受けるスタッフは、 JANOG Program-app への初回ログイン(登録)を完了している必要があります。
* 操作後、担当を公開するボタンを押下してください。
* ボタン押下の操作は取り消すことができませんが、公開後も役職やショートコードの操作など全般的に行うことができます。
* 公開操作によって、PC委員の担当PC希望調査がロックされます。
* 担当を公開する ボタン
* system_configs の view_roll_open が true のとき、ボタンを無効に。ボタンしたに 「※ 担当は公開済みです」を赤文字で
* ボタン押下時、 system_configs の view_roll_open を true に。AdminTimetableTab も内容を更新。
* 役割割り当てのための表
* 見出し行は プログラム名, プログラム種別, 時間及び部屋, 裏被り, ショートコード, 担当PC, 部屋チェア, 司会, [PC委員のユーザ名]
* 担当PC, 部屋チェア, 司会 行は背景を薄いオレンジ色に
* PC委員のユーザ名は、 /users [GET] で role が pc のユーザだけを表示
* 委員ごとに名前長がことなるが、列幅は同じに揃えて(セル内で文字を折り返して良い)
* プログラム一覧は、program/staff/list [GET] を用いて取得する
* プログラム名は title
* プログラム種別は、program_table_type 。 Proposals のとき、「通常」。ManualProposals のとき、「手動」。手動かつ is_produce が true のときは手動の前に「P(丸枠の中に白文字。青塗りつぶし)」というアイコンを出す
* 時間及び部屋は、 room_name, date(day) start_time - end_time
* 裏被りは program/staff/ura-list [GET] を用いて取得。そのプログラムの裏に位置するプログラムのタイトルを書く( 1行に1プログラム名 )
* ショートコードは、文字の入力フィールドを配置。program/staff/list [GET] で取得時に short_code が空でない(null, "")とき、フィールドにその文字を反映。フィールド内の値に変化(入力、削除など)があったら /program/short_code [POST] を使って反映していく。文字列が空になったときは"" で更新。
* 担当PC, 部屋チェア, 司会 はユーザ選択用のリストボックスを配置。リストには登録されているユーザの表示名。 /staff/fix-support [GET] を取得して、そのプログラムで選択できないユーザは選択肢から外す。リストボックスの内容は program/staff [GET] を用いて取得した値を反映する。値を更新した時はprogram/staff/fix [POST]を用いて反映。空への更新も可能。担当PCだけ複数名が可能なので、+ボタンなども追加。2個目以降のフィールドをーアイコンで削除できるようにもする。
* PC委員のユーザ名は、progam/staff/enquete/otherを取得して、回答状況を反映。priority が 1 のとき◎、 2のとき◯、3のとき✗を表示。未回答は空欄
- 見出し行が左揃えのものや中央揃えが混じってて気味が悪い。 中央揃えに揃えたら?
- プログラムの表示順について、同時間帯のものについては room_id の昇順と表示する
- 裏被りの文字サイズをもう少し大きく
- ショートコードの更新失敗時、フィールド内のテキストがそのままなので、更新できたと誤認してしまう
- リストボックスについて (裏被り) は (選択不可) に変更
- 担当PCの追加押下後、新しく出たボックスでユーザを選択したところ、新しくリストボックスが出現し、その出現したボックスの下のリストボックスに、選択した値が反映されてしまう
- 追加, 削除について、もう少しボタンらしくして(削除はゴミ箱アイコンにするなど)
- 時間及び部屋について、部屋名は文字色濃くて、日時等は薄いけど、どちらも大事な情報なので濃くしよう(部屋名のあとに区切り線を入れるなど)
AIとともに歩んでいくうえでの苦労
いくつか苦労した点をまとめておきたいと思います。
UIやUXの言語化
上部にも記載したのですが、UIやUXのこだわりをプロンプト、つまり言語情報として落とし込む/指示を与えるのはやや苦労しました。おそらく、もっとよいプラクティスがあると思います。
過去の指示内容を意図せず読み込む
似通った機能について、仕様が異なるにもかかわらず例えば「過去に修正を指示したときのプロンプト」や「過去に別機能で与えた仕様」を読み出して不用意な変更を加える事が多々ありました。
この過去のプロンプトを参考にするというのは、AIの挙動としては期待されるもので問題ないです。今回はbackendのチャット, frontendのチャットそれぞれで最初から最後までアプリの作り込みを行ったわけですが、作業が進むにつれてチャット履歴(そのチャットにストアされる情報)は膨大になっていきます。その影響か、作業後半では意図しない修正をされてしまうことがあり、苦労しました。
例えば、あるファイルに対して「こういった部分を修正して」と指示したにもかかわらず、全く別ファイルかつ関係のない変更を加えることがありました。
トークン上限量との戦い
ある機能を作るために与えるプロンプトは大抵が、長く複雑なものです。これは、上部の設計の段階で詳細な仕様を策定してからプロンプトを出すということに起因するのですが、仕様が複雑であればあるほど思考やファイル読み出しが複数回走って、結果として初回のプロンプトから成果物を得られる前に5時間のトークン使用量の上限にぶつかることもありました。(なので初回に与えるプロンプトをあえて簡素化する、なんて工夫をすることもありました)
作業が後半にいくにつれて、参考にすべきソースコードが増えたりするのでなかなか苦労しました。
効率的にプロンプトを書くということも大事ですね。
DB化することで受けられる恩恵
今回のJANOGから、応募時点、プログラム採用後の公開情報(本番情報)を含めたすべての情報をデータベースにストアすることができました。
Google Spreadsheetベースの情報管理からデータベースに移行したことで、処理速度の高速化を達成することができましたが、何よりもスプシによる制約がなくなったことで比較的自由にアプリの作り込みや取り組みを行うことが可能になったということが最大の恩恵として考えています。後述するアンケートシステムもデータベース化したことによる恩恵ですし、今後の工夫次第ではタイムテーブルを取得するためのAPIを参加者向けに展開する、それら公開APIを用いた様々な取り組みを行う、、、などの新しい取り組みもすることができるようになりました。
アンケートシステム
さて、PS委員が取り組んだこととしてもう一つ「アンケートシステムの導入」が挙げられます。
過去のJANOGでは、イベントに対する「全体アンケート」とプログラムに対する「プログラムアンケート」が1つのGoogle Formにまとまっていました。そのため、全体としてアンケート分量が多く、目的の設問へのアクセス性がやや悪いのではないかという懸念もありました。
また、各プログラムについて「こういう回答をしたときだけ、この設問をだす」というこだわったアンケート設計を行うことが難しく。。。(できはするんですが、準備が大変だったり”技術的には可能です”状態にあったと思います)
そこで今回はLimeSurveyというOSSを用いることとしました。理由としては、プラグイン開発によって機能を拡張できる点が強みですね。AIを用いてプラグインを実装し、プログラム情報を取得するAPIを用いて情報を取得することでアンケートを効率的に生成できるようにしました。また、条件式による設問内容の変化も容易に行えるようになりました。
最大の利点は、プログラム単位でプログラムアンケートを表示できるようにしたところですね。プログラム参加後に、その場でアンケートに回答ということができるようになりました。
ぜひ、アンケートに回答してみてください。
デプロイの単純化
さて、GASでの運用を行っていた際はコードメンテ・デプロイのコストが回を増すごとに高まっていました。原因としては多機能化の伴いセットが必要なパラメータが増えたこと、モジュール分割を強いられるために処理ロジック的に分断されたようなコードになっていること。。などが考えられます。
今回は、アンケートシステムを含めた全体の構成をコンテナで動かしています。構成はdocker-composeに書いて、単純にコードを持ってきてdocker-compose up -dすればよいだけになりデプロイコストを大幅に下げる事ができました。
デプロイの際のコストが下がったという点も恩恵ではありますが、何よりも冪等性が確保されたのが大きな恩恵ではないでしょうか。
まとめ
さて、様々なノウハウをNLにまとめてみました。これらの取り組みによる恩恵はJANOG Staffのみならず、参加者のみなさんも実感することができるのではないかと期待しています。
これら取り組みのノウハウが、なにかに活用できたらなーと思っています。

