
プログラミング
イベント
マガジン
技術ブログ
G-gen の勝島です。Google Workspace Studio を使用して、Google ドライブに追加されたファイルを Gemini Notebook(旧称 NotebookLM)にソースとして自動で追加するフローの作成手順を紹介します。 はじめに 当記事の概要 Google Workspace Studio とは Gemini Notebook とは 作成するフロー 処理の全体像 追加できるソースの種類 注意事項 フロー作成手順 ノートブックの作成 開始条件の指定 ソース追加ステップの設定 動作確認 はじめに 当記事の概要 当記事では、Google Workspace の業務自動化ワークフローツールである Google Workspace Studio を使用して、 Gemini Notebook (旧称 NotebookLM)へのソース追加を自動化するフローを作成します。具体的には、Google ドライブの特定フォルダにファイルが追加されたことをきっかけに、そのファイルを Gemini Notebook にソースとして自動で追加するよう設定します。 これまで Gemini Notebook にソースを追加するには、ユーザーがノートブックを開き、1つずつ手動で登録する必要がありました。Google Workspace Studio には、Gemini Notebook にソースを追加するステップ(以下、ソース追加ステップ)が用意されており、追加作業をフローに組み込めます。当記事では、このソース追加ステップを中心にフローの作成手順を解説します。 参考 : Workspace Studio の開始条件とステップに関するガイド - Workspace Studio ヘルプ Google Workspace Studio とは Google Workspace Studio は、Google Workspace のアプリケーションを連携させて定型業務を自動化できる、Gemini を搭載したノーコードの自動化ツールです。あらかじめ用意された開始条件(トリガー)とステップ(アクション)を組み合わせて「フロー」を作成することで、プログラミングなしに業務を自動化できます。 たとえば「特定のフォルダにファイルが追加されたら、その内容を Gemini で要約して Google Chat に通知する」といった処理を、コードを書かずに実現できます。 参考 : Google Workspace Studio の使用を開始する - Workspace Studio ヘルプ Google Workspace Studio の概要や基本的な使い方は、以下の記事で詳しく解説しています。当記事ではフローの作成手順に絞るため、基礎的な概念は以下を参照してください。 blog.g-gen.co.jp Gemini Notebook とは Gemini Notebook は、ユーザーが追加したソース(情報源)のみに基づいて回答を生成する、Google の AI リサーチツールです。 PDF や Google ドキュメント、Web URL などをソースとしてノートブックに追加すると、それらの内容に基づいた質問への回答や要約、音声概要(Audio Overview)の生成などを行うことができます。AI の回答がソースに基づくため、ハルシネーション(事実と異なる内容の生成)を抑制できる点が特徴です。 Gemini Notebook の詳細については、以下の記事を参照してください。 blog.g-gen.co.jp 作成するフロー 処理の全体像 今回作成するフローは、Google ドライブの特定フォルダにファイルが追加されたことをきっかけに動き出します。フロー全体は、次の2つのステップで構成されます。 ステップ 種類 処理内容 1 開始条件 フォルダへのアイテム追加を検知する 2 アクション 追加されたファイルを Gemini Notebook にソースとして追加する ステップ1でファイルの追加を検知し、ステップ2でそのファイルを Gemini Notebook のソースに追加します。フォルダにファイルを保存するだけで、ノートブックの情報源が自動で拡充される点が、当記事のポイントです。 追加できるソースの種類 ソース追加ステップでは、以下の種類のソースを Gemini Notebook に追加できます。 テキスト Google ドライブファイルへのリンク Web または YouTube のリンク 当記事ではこのうち、Google ドライブファイルへのリンクを使用します。 注意事項 ソースの追加は即時だが、質問できるようになるまで数分かかる ソース自体はすぐに追加されますが、追加した内容がノートブックにインデックス化され、質問に反映されるまでには数分かかります。フローの実行直後にノートブックへ質問しても、追加したばかりのソースの内容が回答に反映されない場合があります。 Gemini Notebook のソース数には上限がある Gemini Notebook に追加できるソース数には、プランごとの上限があります。フローで自動追加する場合は、上限に達しないよう運用を設計してください。各プランの上限については、以下の公式ドキュメントを参照してください。 参考 : ノートブックの新しいソースを追加または検索する - Gemini Notebook ヘルプ フロー作成手順 ノートブックの作成 フローの作成に先立ち、ソースの追加先となる Gemini Notebook のノートブックを1つ用意します。既存のノートブックを使用する場合は、この手順は不要です。 開始条件の指定 開始条件 は、フローが動き出すきっかけとなるイベントです。Google Workspace Studio では、スケジュール実行やメールの受信など複数の開始条件が用意されています。 当フローでは [フォルダにアイテムが追加されたとき] を開始条件に選びます。監視対象のフォルダにファイルが追加されたタイミングで、フローを起動します。 開始条件の選択画面で [フォルダにアイテムが追加されたとき] を選択します。 続いて、監視するフォルダを指定します。[ドライブ] をクリックし、ソースとして追加したいファイルを格納するフォルダを選択します。 これで、対象のフォルダに新しいファイルが追加されるたびに、フローが実行されます。 ソース追加ステップの設定 開始条件の次に、追加されたファイルを Gemini Notebook のソースに追加するステップを設定します。アクションの [ステップの選択] から、[Gemini Notebook にソースを追加します] を選択します。 ステップを追加すると、ソースの追加先や追加する内容を指定するフィールドが表示されます。各フィールドを以下のとおり設定します。 フィールド名 値 追加先のノートブック ソースの追加先となるノートブックを指定 ソースの種類を選択 ドライブ を指定 ファイルまたはリンクを選択 ドライブ をクリックして ステップ 1: フォルダにアイテムが追加されたとき > アイテムへのリンク を指定 以上で、フォルダへのファイル追加を起点に、Gemini Notebook へソースを自動追加するフローが完成です。 動作確認 フローが完成したら、画面左上のフロー名を分かりやすい名前(ここでは「ナレッジソース自動追加」)に変更し、[オンにする] でフローを有効化します。 フローをオンにした状態で、監視対象のフォルダにファイルを追加します。 フローが実行されると、追加したファイルが Gemini Notebook のソースに追加されます。ノートブックを開くと、ソースの一覧に対象のファイルが表示されています。 なお、ソースの一覧に表示された後も、その内容がインデックス化され質問に反映されるまでには数分かかります。追加直後に質問する場合は注意してください。 フローの実行結果は、画面右上のアクティビティから確認できます。 勝島 祐太郎 (記事一覧) クラウドソリューション部 ソリューションアーキテクト課 2025年1月G-genにジョイン!飲食業界からIT業界に転身したエンジニア。 コーヒーが好きです。
私はC#が好きなのですが、デリゲートを使ったことがないと気付きました。 昔やったプロジェクトでデリゲートを見たことはありますし、先輩が使っているのを見たこともあります。しかし自分自身が使ったことはなくて、食わず嫌いしていたかもと思えてきました。 そこでデリゲートをコードを書きながら学ぼうと考えたのが今回の記事です。私が書いて動かしたコードで解説していきます。 デリゲート(delegate)とは # デリゲートとは委譲という意味です。 委譲とはコトバンクによると「権利・権限などを他の人・機関に譲って任せること」となっています。つまり自分がやることを他人に任せることです。例えば上司が部下に仕事を振ることなどはいい例でしょう。 https://kotobank.jp/word/%E5%A7%94%E8%AD%B2-432326 ならばプログラミングにおいてはやることは処理です。処理の実行を他の機能に任せるということになります。 しかしこれだけだとなんのこっちゃか分かりません。そこでもう少し具体的に言うと、処理(メソッド)の参照を代入して、値ではなく処理(メソッド)を渡すことで、他の機能(クラス)から処理(メソッド)を実行できるようにするとなります。 例えばある処理が完了したらコールバック処理を呼び出してもらうときに使えます。 事前準備 # この記事のサンプルコードを動かすにはVisual StudioまたはVisual Studio CodeでC#開発ができるようセットアップを行ってください。Visual Studio Codeでのセットアップはこちらの記事を参照してください。 VS Codeで始める!わかる&できるC#開発環境の構築【2025年版マニュアル】 IDEのセットアップができたら、次はコンソールアプリのプロジェクトを作成してください。この記事のサンプルコードは手軽に動作確認するためにコンソールアプリで作っています。 デリゲートの基本をサンプルコードで解説 # デリゲートの基本的な書き方 # 早速ですがコードを書いて動きを確認していきます。 動かせるサンプルコードを掲載します。 DelegateSample というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSample { // デリゲートを宣言 // デリゲート型のメソッドを宣言しているようなもの delegate void SampleDelegate(string message); // サンプルの実行 public void ExecSample() { // 宣言したデリゲートを型としてメソッドを引数に渡す // 違和感ある書き方だが、メソッド型のインスタンスを作ってメソッドを代入しているようなイメージ SampleDelegate sampleDelegate = new SampleDelegate(Console.WriteLine); // 試しにメッセージを出してみる // Console.WriteLineを代入したので、デリゲートを実行するとConsole.WriteLineが実行される sampleDelegate("テストメッセージです"); // 次は独自に作ったメソッド(コンソールに文字列を出すだけだが)を代入してみる sampleDelegate = TestMessage; sampleDelegate("カレーとサラダ"); Console.WriteLine(); } void TestMessage(string message) { Console.WriteLine($"今夜の夕食は{message}の予定です。"); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); DelegateSample delegateSample = new DelegateSample(); delegateSample.ExecSample(); このコードを実行すると、次のようにメッセージが表示されます。 デリゲート初心者には分かりづらいとか違和感があると感じたかもしれません。 デリゲートの宣言は delegate 型のメソッドを定義することです。そして次に宣言したメソッドを型としたインスタンスを作成します。型がメソッドだなんて一瞬意味が分からなく感じますよね。 そして delegate 型のインスタンスにはメソッドを代入できます。メソッドを実行するための仕組みなので、このようになります。メソッドを代入するということ自体が慣れない行為ですよね。 でもここまで見てみると、引数さえ合っていれば、色んなメソッドを代入して実行可能だろうと分かりますね。 複数のメソッドをまとめるマルチキャストデリゲートというものもあります。例えば先ほどの DelegateSample クラスに次のようにコードを追加してみましょう。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSample { // デリゲートを宣言 // デリゲート型のメソッドを宣言しているようなもの delegate void SampleDelegate(string message); // サンプルの実行 public void ExecSample() { // 宣言したデリゲートを型としてメソッドを引数に渡す // 違和感ある書き方だが、メソッド型のインスタンスを作ってメソッドを代入しているようなイメージ SampleDelegate sampleDelegate = new SampleDelegate(Console.WriteLine); // 試しにメッセージを出してみる // Console.WriteLineを代入したので、デリゲートを実行するとConsole.WriteLineが実行される sampleDelegate("テストメッセージです"); // 次は独自に作ったメソッド(コンソールに文字列を出すだけだが)を代入してみる sampleDelegate = TestMessage; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // ここから追加 // 実はメソッドの追加が可能 sampleDelegate += TestMessageLunch; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // 追加済みメソッドの削除も可能 sampleDelegate -= TestMessage; sampleDelegate("カレーとサラダ"); // ここまで } void TestMessage(string message) { Console.WriteLine($"今夜の夕食は{message}の予定です。"); } // 昼食用のメソッドを追加 void TestMessageLunch(string message) { Console.WriteLine($"今夜の昼食は{message}でした。"); } } } Program.cs を実行すると以下のようになります。 このコードにはデリゲートに対してメソッドを加算及び減算しています。 なんと delegate 型のインスタンスには複数のメソッドの追加や追加したメソッドの削除ができるのです。 これをマルチキャストデリゲートと呼びます。 デリゲートの進化の歴史を知る # 実は先ほど書いたデリゲートのやり方は昔ながらのやり方でした。それがもう少し進化し、C# 2.0の時代には匿名メソッドが使えるようになりました。 匿名メソッドのサンプルコードを掲載します。 DelegateSampleAnonymous というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleAnonymous { // デリゲートを宣言 // デリゲート型のメソッドを宣言しているようなもの delegate void SampleDelegate(string message); // サンプルの実行 public void ExecSample() { // 次は独自に作ったメソッド(コンソールに文字列を出すだけだが)を代入してみる // 定義されたメソッドだけでなく、匿名メソッドも代入可能 SampleDelegate sampleDelegate = delegate(string message) { Console.WriteLine($"今夜の夕食は{message}の予定です。"); }; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // 匿名メソッドを追加してみる sampleDelegate += delegate(string message) { Console.WriteLine($"今夜の昼食は{message}でした。"); }; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // ちなみに匿名メソッドで追加すると削除できない // このように一度別のインスタンスに入れてから追加する必要がある SampleDelegate workDelegate = delegate(string message) { Console.WriteLine($"今夜の朝食は{message}でした。"); }; sampleDelegate += workDelegate; sampleDelegate("カレーとサラダ"); Console.WriteLine(); sampleDelegate -= workDelegate; sampleDelegate("カレーとサラダ"); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("匿名メソッドのサンプルです"); Console.WriteLine(); DelegateSampleAnonymous delegateSampleAnonymous = new DelegateSampleAnonymous(); delegateSampleAnonymous.ExecSample(); 実行すると以下のようになります。 ここでちょっと厄介なことは、匿名メソッドを使った場合は追加したメソッドの削除ができないことです。どうしても削除したければ、一度インスタンスに入れてから追加する必要があるのです。 上記のサンプルで言うと workDelegate が該当します。 現在はラムダ式を使うのが主流となっています。ラムダ式を使うサンプルコードを掲載します。 DelegateSampleLambda というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleLambda { // デリゲートを宣言 // デリゲート型のメソッドを宣言しているようなもの delegate void SampleDelegate(string message); // サンプルの実行 public void ExecSample() { // 次は独自に作ったメソッド(コンソールに文字列を出すだけだが)を代入してみる // 定義されたメソッドだけでなく、ラムダ式も代入可能 SampleDelegate sampleDelegate = (string message) => { Console.WriteLine($"今夜の夕食は{message}の予定です。"); }; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // ラムダ式を追加してみる sampleDelegate += (string message) => { Console.WriteLine($"今夜の昼食は{message}でした。"); }; sampleDelegate("カレーとサラダ"); Console.WriteLine(); // ちなみにラムダ式で追加すると削除できない // このように一度別のインスタンスに入れてから追加する必要がある SampleDelegate workDelegate = (string message) => { Console.WriteLine($"今夜の朝食は{message}でした。"); }; sampleDelegate += workDelegate; sampleDelegate("カレーとサラダ"); Console.WriteLine(); sampleDelegate -= workDelegate; sampleDelegate("カレーとサラダ"); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("ラムダ式のサンプルです"); Console.WriteLine(); DelegateSampleLambda delegateSampleLambda = new DelegateSampleLambda(); delegateSampleLambda.ExecSample(); 実行すると以下のようになります。 ラムダ式になるともはや delegate という記述すらなくなります。一番シンプルな書き方です。 FuncとActionを使えば宣言不要 # 戻り値がない処理ならAction # ここまでのやり方は delegate 型のメソッドを宣言して、そのインスタンスにメソッドを代入するというものでした。わざわざ宣言しているので、用途別にデリゲートを作成する必要がありました。 そこで汎用型として戻り値がない場合は Action 、戻り値がある場合は Func というものが登場しました。 Action を使ったサンプルコードを掲載します。 DelegateSampleAction というクラスを作り、以下のコードを書いてください。 このようにジェネリックで引数の型を指定します。わざわざ delegate 型のメソッドを宣言する必要がないので、コードもシンプルになります。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleAction { public void ExecSample() { // Actionデリゲートを使用するサンプル // Actionデリゲートは戻り値がvoidで、引数の型を指定できる Action<string> actionDelegate = (message) => { Console.WriteLine($"今夜の夕食は{message}の予定です。"); }; actionDelegate("カレーとサラダ"); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("Actionのサンプルです"); Console.WriteLine(); DelegateSampleAction delegateSampleAction = new DelegateSampleAction(); delegateSampleAction.ExecSample(); 実行すると以下のようになります。 ちなみにメソッドの追加や削除については delegate を使う場合と同様に+や-などの演算子を使ってください。また匿名メソッドやラムダ式では削除ができないことも同様ですので、削除したいときは一度インスタンスに入れてから追加してください。 戻り値がある処理ならFunc # Func を使ったサンプルコードを掲載します。 DelegateSampleFunc というクラスを作り、以下のコードを書いてください。ジェネリックの型指定で、<引数の型, 戻り値の型>というように指定します。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleFunc { public void ExecSample() { // Actionデリゲートを使用するサンプル // Actionデリゲートは戻り値がvoidで、引数の型を指定できる Func<int, string> funcDelegate = (num) => { return $"入力された値は{num}です。"; }; Console.WriteLine(funcDelegate(5)); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("Funcのサンプルです"); Console.WriteLine(); DelegateSampleFunc delegateSampleFunc = new DelegateSampleFunc(); delegateSampleFunc.ExecSample(); 実行すると以下のようになります。 Func もメソッドの追加や削除については delegate を使う場合と同様です。 デリゲートの本当の使い道 # コールバック処理 # コールバック処理のサンプルコードを掲載します。 DelegateSampleAction というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class CallbackSample { // コールバックのサンプル // ここでは、処理が終わった後に呼び出されるメソッドをデリゲートとして渡す // デリゲートを使うことで、処理が終わった後に呼び出されるメソッドを自由に変更できる public void ExecSample(string message, Action<string> callback) { Console.WriteLine($"処理中: {message}"); // 処理が終わった後にコールバックを呼び出す callback?.Invoke($"処理が完了しました: {message}"); } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("コールバックのサンプルです"); Console.WriteLine(); CallbackSample callbackSample = new CallbackSample(); callbackSample.ExecSample("カレーとサラダ", (result) => { Console.WriteLine(result); }); 実行すると以下のようになります。 このようにメソッドの引数に Func や Action を定義することで処理を受け取ります。そして Invoke メソッドで処理を実行します。 条件に応じた処理の差し替え # 実はLINQの Where は引数が Func になっています。つまり Where メソッドの中ではデリゲートを使っているのです。 では Func の内容次第で結果が異なる例を作ってみましょう。 DelegateSampleLinq というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleLinq { public void ExecSample() { List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // まずは偶数だけを抽出してみる var evenNumbers = FilterNumbers(numbers, (num) => num % 2 == 0); Console.WriteLine("偶数のリスト:"); Console.WriteLine(evenNumbers.Count > 0 ? string.Join(", ", evenNumbers) : "該当する値はありません。"); // 続いて奇数を抽出してみる var oddNumbers = FilterNumbers(numbers, (num) => num % 2 != 0); Console.WriteLine("奇数のリスト:"); Console.WriteLine(oddNumbers.Count > 0 ? string.Join(", ", oddNumbers) : "該当する値はありません。"); } /// <summary> /// 引数として渡されたFuncを実行し、条件に一致する値だけを返す /// </summary> /// <param name="numbers"></param> /// <param name="predicate"></param> /// <returns></returns> static List<int> FilterNumbers(List<int> numbers, Func<int, bool> predicate) { List<int> resultList = new List<int>(); foreach (var number in numbers) { if (predicate(number)) { resultList.Add(number); } } return resultList; } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("条件を切り替えるサンプルです"); Console.WriteLine(); DelegateSampleLinq delegateSampleLinq = new DelegateSampleLinq(); delegateSampleLinq.ExecSample(); 実行すると以下のようになります。 デリゲートの脆弱性を補うためのevent # ここまでデリゲートの使い方について色々と見てきました。 しかし便利な反面、危険も伴います。何せ代入次第で何とでも処理内容を変えられます。また Invoke メソッドによって好きなところで実行が可能です。脆弱な面があることは否めません。 下手すると不具合の元になるのはもちろん、セキュリティ面での脆弱性も怖いところです。 そこで event というキーワードを付けることで、外部クラスからの代入や実行ができなくなります。 event を使ったサンプルコードを掲載します。 DelegateSampleEvent というクラスを作り、以下のコードを書いてください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp { internal class DelegateSampleEvent { // パブリックだから他のクラスからアクセス可能 public event Func<int, string> SampleEvent; } internal class DelegateSampleExternal { public void ExecSample() { DelegateSampleEvent sample = new DelegateSampleEvent(); // 外部クラスからのデリゲートの代入は不可 sample.SampleEvent = (int x) => $"入力された値は{x}です。"; // 外部クラスからのデリゲートの追加と削除は可能 sample.SampleEvent += (int x) => $"入力された値は{x}です。"; Func<int, string> func = (int x) => $"今日はコーヒーを{x}杯飲みました。"; sample.SampleEvent += func; sample.SampleEvent -= func; // 外部クラスからの実行は不可 sample.SampleEvent.Invoke(99); } } } 上記のようにコードを書くと、以下のスクリーンショットのように代入と Invoke メソッドのところでコンパイルエラーが発生します。 ひとまず先ほどのコードでコンパイルエラーが出ている個所はコメントアウトしてください。 フードコートでの注文を例にデリゲートを試す # デリゲートのやり方が分かったところで、現実世界をイメージして練習してみたいと思います。そこでフードコードのスイーツ屋をテーマに扱います。ただ文字の表示や計算をするよりは楽しい内容になるし、委譲をイメージしやすいと考えたためです。 業務フローは以下の図のようになります。 ここではコードをかなり端折って解説します。本当は注文内容だって明細を持てるようにしたり、注文内容も動的に変えられるようにしたりする必要がありますが、今回はそこが主題でないため簡易的なメッセージで済ませます。 それではソースコードを掲載します。 Models というフォルダに作って、その中に以下の5つのクラスを作成してください。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp.Models { internal class Buzzer { public int No { get; set; } public Buzzer(int no) { No = no; } public void Ring() { Console.WriteLine($"ピー!ピー!ピー!{No}番のブザーが鳴っています!"); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp.Models { internal class Cook { public void CookOrder(Order order) { Console.WriteLine($"料理人が注文を受け取りました: {order.OrderContent}"); Console.WriteLine("料理を作っています..."); Console.WriteLine("料理が完成しました!"); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp.Models { internal class Customer { private Buzzer Buzzer { get; set; } public Order MakeOrder() { Console.WriteLine("お客様が注文しました。"); return new Order("パンケーキとコーヒーのセット"); } public void Pay() { Console.WriteLine("お会計を済ませました。"); } public void PickUpBuzzer(Buzzer buzzer) { Buzzer = buzzer; Console.WriteLine($"お客様が{buzzer.No}番のブザーを受け取りました。"); } public void PickUpFood() { Console.WriteLine($"お客様が{Buzzer.No}番のブザーを渡しました。"); Console.WriteLine("お客様が料理を受け取りました。"); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp.Models { internal class Order { public string? OrderContent { get; set; } public Order(string? orderContent) { OrderContent = orderContent; } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DelegateApp.Models { internal class Staff { private delegate void BuzzerRingHandler(); private IDictionary<int, BuzzerRingHandler> _buzzerHandlers = new Dictionary<int, BuzzerRingHandler>(); public void TakeOrder(Order order) { Console.WriteLine($"スタッフが注文を受け取りました: {order.OrderContent}"); Console.WriteLine("お会計は1,500円になります。"); } public Buzzer HandOverBuzzer(int no) { Buzzer buzzer = new Buzzer(no); Console.WriteLine($"スタッフが{no}番のブザーを渡しました。"); _buzzerHandlers.Add(no, buzzer.Ring); return buzzer; } public void RingBuzzer(int no) { if (_buzzerHandlers.TryGetValue(no, out var buzzer)) { buzzer.Invoke(); _buzzerHandlers.Remove(no); } } } } そして Program.cs には以下のように書いてください。 using DelegateApp; using DelegateApp.Models; Console.WriteLine("デリゲートを開始します"); Console.WriteLine(); Console.WriteLine("フードコートで料理が出来たらブザーを鳴らすサンプルです"); Console.WriteLine(); Customer customer = new Customer(); Staff staff = new Staff(); Cook cook = new Cook(); Order order = customer.MakeOrder(); // 注文とお会計 staff.TakeOrder(order); customer.Pay(); // ブザーを渡す int no = 1; Buzzer buzzer = staff.HandOverBuzzer(no); customer.PickUpBuzzer(buzzer); // 調理 cook.CookOrder(order); // ブザーを鳴らして料理を渡す staff.RingBuzzer(no); customer.PickUpFood(); 実行すると以下のようになります。 ポイントとしては店員がどのお客さんに何番のブザーを渡したかを管理しているということです。 プログラムとしては Staff クラスが Dictionary で番号とブザーを鳴らすメソッドの組み合わせを持っています。 こうすることで、料理ができたときに店員が顧客の手元にあるブザーを鳴らせるようにできます。 おわりに # コードを書いて動かしてみることで、デリゲートとは何かが少し分かりました。やっぱり書いて動かしてみるのが一番ですね。 デリゲートが分からないという方はこの記事のフードコートみたいなものを作ってみてください。
はじめに 2026年8月21日から2026年8月22日にかけて開催された PyCon JP 2026 に参加しました。 2026.pycon.jp 弊社からはバックエンドエンジニアの陶山 嶺が登壇しました。また、今年は弊社 RevComm もゴールドスポンサーとして PyCon JP 2026 に協賛いたしました。 今回はイベントの振り返りを紹介します。 登壇振り返り 詳解ASGIミドルウェア シンプルながらも奥が深いASGIミドルウェアを使いこなす 概要: Pythonで本格的なWebアプリケーションを開発するためには、ASGIに対する理解は極めて重要です。本セッションではそのASGIのしくみの全てが詰まっていると言っても過言ではないASGIミドルウェアに焦点を当てて紹介します。アイディア次第で用途が広がるASGIミドルウェアのしくみを理解し、使いこなしていきましょう。 登壇者: 陶山 嶺 登壇の感想 PyCon JPでの登壇はこれまでにも何度かありますが、それでもやはり直前はスポンサーブースにいる同僚たちから「今日ずっとソワソワしていますね」と言われるくらい緊張していました笑 発表自体は直前まで何度も練習していたこともあり、全体的に上手く進めることができてよかったです。登壇後には「知らない話も聞けてすごく面白かったです」と言っていただけて発表して良かったなと改めて感じました。 今回の自分の発表時間帯は2日目午後だったので、次回があればパーティよりも前に発表できたら嬉しいです!笑 ブース運営について 今年はスポンサーとしてブースを出展させていただきました。 ブースまでお越しいただいた皆様、大変ありがとうございました。 PyCon JP 2026 RevComm ブース ブースには弊社で開発・提供している MiiTel の特徴を視覚的に理解いただけるよう、動画とパネルを展示しました。 また、ちょうど PyCon JP 2026 開催直前の2026年8月13日に、登壇者の陶山 嶺が執筆した「Python Web開発実践入門 ―― FastAPIによるWeb API開発と非同期処理」が発売されたため、ブースにも展示しました。FastAPI や Python による Web アプリケーション開発にご興味がありましたら、ぜひご覧ください! gihyo.jp 印象に残ったセッション Python and language learning: How did Python help me get the JLPT N1 英語を母語とする学生として来日し、やがて日本語のエンジニアリングチームを率いながら JLPT N1 に合格するまでの 10 年間の歩みについてのJose 氏のトークです。海外出身のエンジニアが日本で働くうえで日本語力の重要性が高まっている昨今のビザ要件もあり、このセッションは非常にタイムリーで意義深いものに感じました。 特に印象的だったのは、Jose 氏が自身の言語学習の経験を、ソフトウェアエンジニアとしての専門性と直接結びつけていた点です。一般的な教科書や単調な暗記に頼るのではなく、Python や最新の LLM を活用することで、自分に最適化された学習ツールを作れることを示してくれました。以前は学習素材を集めるために Web スクレイパーを書いて、ニュースサイトなどからデータを収集していたそうですが、現在では LLM を使うことで、そのようなコンテンツをその場で生成できるようになっています。 また、フラッシュカード作成のための genanki や、発音分析のための onsei といったオープンソースライブラリを活用し、自分の弱点に合わせたパーソナルな学習環境を、まさにプログラムによって構築していた点も印象的でした。この話を通じて、プログラミングのスキルを使って、新しい言語を学ぶという複雑なプロセスを「ハック」できるのだと視点が変わりました。紹介された具体的な方法にとどまらず、Python を活用すれば、さまざまな言語学習の目標に対して、もっと創造的なアプローチができるのではないかと感じました。 2026.pycon.jp Retry Is Not a Strategy: Classifying and Recovering from AI Agent Failures in Python LLM エージェントを開発していると、原因が分かりにくいサイレントな失敗や、意図しないループへの対処に悩まされることがあります。Cyrus Mante 氏のトークは、まさにそのような課題に正面から向き合う内容でした。単に最初からリトライして「今度はうまくいくはず」と期待するのではなく、Cyrus 氏は triage というオープンソースライブラリを紹介していました。 このライブラリを使うことで、開発者はエージェントの実行ステップを分析し、失敗の原因を具体的に特定できます。たとえば、誤ったツールを呼び出したのか、ループに陥っていたのか、外部 API で問題が発生したのか、といった切り分けが可能になります。 さらに優れていると感じたのは、復旧方法の扱いです。発生したエラーの種類に応じて、システムがエージェントの状態をロールバックするのか、再計画を強制するのか、あるいは人間にエスカレーションするのかを自動的に判断します。また、このようなルーティングロジックを肥大化した LLM プロンプトに任せるのではなく、Python のコードとして明確に保つという考え方にも強く共感しました。堅牢な AI システムを構築するうえで非常に実践的なアプローチであり、自分たちのエージェントにもぜひ取り入れて試してみたいと感じました。 2026.pycon.jp Pyxelで、プログラミングを遊ぼう! Pyxel の作者である北尾崇氏 ( https://github.com/kitao ) による 2 日目の基調講演です。 github.com 本質的な価値を追求するためにライブラリに意図的に制約を加えている、という点はライブラリの開発だけでなくプロダクト開発においても重要な視点なのではないかと感じました。その他にも、Pyxel v3 の構想や AI 時代における活用や意義など、興味深い内容がたくさんありました。 Pyxel は今まで使ったことがなかったのですが、今回の発表を聞いてぜひ遊んでみたいと思いました。 おわりに 筆者は PyCon JP に参加したのは今回が初めてのことでしたが、想像以上に参加者やセッション数が多く、非常に熱気を感じるイベントでした。セッションや基調講演にも興味深いテーマの発表が多く、素晴らしいイベントであったと感じています。 RevComm において Tech イベントでの本格的なブース運営は初めてという状況ではありましたが、ブースまで足を運んでいただいてありがとうございました。 お知らせ 2026年9月9日 (水) 19:00〜20:30 にSanSan株式会社様・株式会社ニーリー様・弊社の共同で PyCon JP 2026 の非公式アフターイベントを開催いたします!オンラインで開催いたしますため、ぜひお気軽にご参加ください! sansan.connpass.com
























