TECH PLAY

サイオステクノロジー(Tech.Lab)

サイオステクノロジー(Tech.Lab) の技術ブログ

621

ども!久しぶりに金曜日もお家で、ブログを執筆している龍ちゃんです。突然ですが、Google Calendar使っていますか?僕は入社してから、いろいろな予定をGoogle Calendarで管理するようになりました。ほぼ、TODOリスト化しています。手動で管理するのもいいんですが、システム化できそうと思っちゃったので検証したのでその報告になります。 今回は、Google Apps Script(GAS)を使ってGoogle Calendarに予定の登録・取得についてまとめました。ドキュメントは充実していましたが、例がなんとも言えない感じだったのでGoogle Calendarヘビーユーザーの皆さんに代わってソースコードを用意したので、コピペして動かしながら試してください。 事前準備 検証を進めていくにあたっての準備なので、ソースを知りたい方は読み飛ばしていただいて大丈夫です。 新しいGoogle Calendarを用意する うっかり、「メインのカレンダーの予定を消してしまった!」なんてことにならないように、検証用の新しいカレンダーを用意しておきましょう。 https://calendar.google.com/ にアクセスして、新しいカレンダーを作成しましょう。 今回は「GAS API Test」という名前のカレンダーを用意しました。次に、カレンダーの設定に入り「 カレンダーの統合>カレンダーID 」というIDを控えてください。 こちらのIDを用いてカレンダーにアクセスするので、この手順を覚えておきましょう。 共通設定:GASでGoogle Calndarにアクセス準備 まずは、GASエディタ上でGoogle Calndar APIへのサービス追加を行います。こちらを設定することでサジェスチョン機能が有効になります。 次に、カレンダーへのアクセス処理をグローバル変数として設置します。カレンダーへの参照はすべてのコードの前段として必要になります。先ほど事前準備で取得した カレンダーID を以下の xxxxx 部分に変更してください。 const calenarID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // カレンダーへの参照 const calendar = CalendarApp.getCalendarById(calenarID) JSのDate型で日付と時間を指定する 今回の内容で取り扱う、Date型セットを用意しました。 // 実行したタイミングの日付情報を取得する const date = new Date() Logger.log(date) // 年:月:日をそれぞれ取得する // 月も日も0からカウントされるため、実世界と合わせるためには+1する Logger.log(`${date.getFullYear()}:${date.getMonth()+1}:${date.getDate()+1}`) // 西暦で設定する date.setFullYear(2024) Logger.log(date) // 次の月に設定 date.setMonth(date.getMonth()+1) Logger.log(date) // 次の日に設定 date.setDate(date.getDate()+1) Logger.log(date) // 時間情報をセットする // 0時12分00秒 date.setHours(00, 12, 00) Logger.log(date) 取得:年・月・日 const date = new Date() Logger.log(date) // 年:月:日をそれぞれ取得する Logger.log(`${date.getFullYear()}:${date.getMonth()+1}:${date.getDate()}`) 月に関しては、 0 からカウントがスタートします。そのため、現実と合わせるためには月は +1 させることで現実と即した値になります。 情報設定(日付): setFullYear / setMonth / setDate const date = new Date() Logger.log(date) // 西暦で設定する date.setFullYear(2024) Logger.log(date) // 次の月に設定 date.setMonth(date.getMonth()+1) Logger.log(date) // 次の日に設定 date.setDate(date.getDate()+1) Logger.log(date) 月は情報は、 0 からカウントがスタートします。月情報が12月を超えて設定した場合は、年が +1 されます。日付情報が有効な日付の場合は設定され、超えた場合は月が +1 されます。 情報設定(時間情報): setHours const date = new Date() Logger.log(date) // 時間情報をセットする // .setHours(hours, min, sec) date.setHours(00, 12, 00) Logger.log(date) 時間情報設定する setHours の引数は3つあります。 パラメータ 説明 hours 時間を設定する(0~23):24以降では日付がプラスされる min 分を設定する(0~59) sec 秒を設定する(0~59) こちらの設定方法は、現実と即した形で設定することができます GASでGoogle Calendarを操作する よく使用する登録・取得の方法についてまとめていきます。コマンドの種類が豊富なので、用途別にまとめて行きます。公式リファレンスでオブジェクトの説明があるので開きながら見てもらえると良いですね。 説明 URL Google Calendarで扱えるオブジェクト https://developers.google.com/apps-script/reference/calendar/calendar?hl=ja Eventオブジェクト https://developers.google.com/apps-script/reference/calendar/calendar-event?hl=ja こちらで、カレンダーオブジェクトの取得が完了します。 単一の予定を追加する: createEvent ドキュメントとしては、 こちら になります。サンプルでは、実行した当日のカレンダーに対して、15:00~16:00の一時間で予定が追加されます。 const calenarID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" const calendar = CalendarApp.getCalendarById(calenarID) const setEvent = () => { const startTime = new Date() startTime.setHours(15, 0, 0) const endTime = new Date() endTime.setHours(16, 0, 0) const options = { // description: "説明", // location:"自宅", // <guests:"example1@example.com>, example2@example.com", // sendInvites:false } const event = calendar.createEvent("title", startTime, endTime, options) } createEvents の引数は、4つです。それぞれ、以下を設定することができます。 名前 型 説明 title String イベントのタイトル:必須 startTime Date イベントの開始日時:必須 endTime Date イベントの終了日時:必須 options Object イベントの詳細設定:オプション options は名前の通り、設定しなくても動作します。optionsで設定できる値としては、以下のようになります。 名前 型 説明 description String イベントの説明 location String イベントの場所 guests String ゲストとして追加するメールアドレス(複数人の場合はカンマ区切り) sendInvites boolean 招待メールを送信するか(デフォルト:false) コメントアウトを外して実行すると、optionsで指定した内容のカレンダー予定が登録されます。 終日のイベントを追加する: createAllDayEvent ドキュメントとしては、 こちら になります。サンプルでは、終日(一日)・終日(二日)・終日(二日:詳細付き)の予定が3つ登録されます。 const calenarID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" const calendar = CalendarApp.getCalendarById(calenarID) const setAllDayEvent = () => { const startTime = new Date() calendar.createAllDayEvent("test", startTime) // 終了日は含まれないため(予定を入れたい日+1)指定 const endTime = new Date() endTime.setDate(endTime.getDate() + 2) calendar.createAllDayEvent("test", startTime, endTime) const options = { description: "説明", location: "自宅", guests: "example1@example.com, example2@example.com", sendInvites: false } calendar.createAllDayEvent("test", startTime, endTime, options) } createAllDayEvent の引数は、4つです。 名前 型 説明 title String イベントのタイトル:必須 startTime Date イベントの開始日時(日付のみが参照される):必須 endTime Date イベントの終了日時(日付のみが参照され、終了日は範囲に含まれない):オプション options Object イベントの詳細設定:オプション optionsで設定することができる値は、以下になります。 名前 型 説明 description String イベントの説明 location String イベントの場所 guests String ゲストとして追加するメールアドレス(複数人の場合はカンマ区切り) sendInvites boolean 招待メールを送信するか(デフォルト:false) createAllDayEvent で気を付けたい点としては、 endTime を設定する場合です。終了日は含まれません。設定したい期間通りに設定できているかを注意深く確認する必要があります。 繰り返し単一の予定を追加する: createEventSeries それぞれ、単一・終日の登録を見てきました。それぞれのイベント形態を繰り返し登録することができる関数が createEventSeries ・ createAllDayEventSeries です。こちらのサンプルは createEventSeries です。サンプルでは、「一か月間、水曜日と木曜日の15:00~16:00に予定」が登録されます。 const calenarID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" const calendar = CalendarApp.getCalendarById(calenarID) const setSeriesEvent = () => { const startTime = new Date() startTime.setHours(15, 0, 0) const endTime = new Date() endTime.setHours(16, 0, 0) const endScedule = new Date() endScedule.setMonth(endScedule.getMonth() + 1) const recurrence = CalendarApp.newRecurrence() .addWeeklyRule() .onlyOnWeekdays([CalendarApp.Weekday.THURSDAY, CalendarApp.Weekday.TUESDAY]) .until(endScedule) const options = { description: "説明", location:"自宅", guests:"example1@example.com, example2@example.com", sendInvites:false } calendar.createEventSeries("title", startTime, endTime, recurrence, options) } createEventSeries で登録することができる引数は5つです。 名前 型 説明 title String イベントのタイトル:必須 startTime Date イベントの開始日時(最初のイベント):必須 endTime Date イベントの終了日時(最初のイベント):必須 recurrence EventRecurrence 繰り返しのルール:必須 options Object イベントの詳細設定:オプション optionsの設定する値は、 createEvent と共通です。この関数で重要になるのは recurrence の設定項目です。指定することができるパラメーターとしては、 こちらにまとまっています 。 繰り返し終了条件を決定する方法として、繰り返し回数( .times(number) )と期間( .until(Date) )があります。これらを使用用途によって使い分けてください。 以下に汎用性が高い、繰り返しルールを記載しておきます。 特定曜日のみ繰り返すサンプル ホワイトリスト方式とブラックリスト方式のそれぞれの方式で記載します。 // 記載されている曜日を削除することで機能する:ホワイトリスト方式 // 週単位で、特定の曜日のみ繰り返す const sceduleRule = CalendarApp.newRecurrence() .addWeeklyRule() .onlyOnWeekdays([ CalendarApp.Weekday.SUNDAY, CalendarApp.Weekday.MONDAY, CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.WEDNESDAY, CalendarApp.Weekday.THURSDAY, CalendarApp.Weekday.FRIDAY, CalendarApp.Weekday.SATURDAY ]).times(10) // 除外したい曜日を記載することで機能する:ブラックリスト方式 // 毎日繰り返して、特定の曜日のみ除外する const sceduleRule = CalendarApp.newRecurrence() .addDailyRule() .addWeeklyExclusion() .onlyOnWeekdays([CalendarApp.Weekday.SUNDAY]) .times(10) 特定間隔(日ごと・隔週) 特定の間隔( .interval(number) )でルールを休眠させることができます。intervalでは前段に記載され//ているルールによって間隔を決定してくれます。 addWeeklyRule では休眠期間1週間、 addDailyRule では休眠期間1日間になります。 // interval(1)で1週間になる const sceduleRule = CalendarApp.newRecurrence().addWeeklyRule().interval(2).times(10); // interval(1)で1日間隔になる const sceduleRule = CalendarApp.newRecurrence().addDailyRule().interval(2).times(10); 第三水曜日のみ addMonthlyRule と onlyOnWeekday をこねくり回して実現できないか試してみたのですが、実装が完成しませんでした。有識者の方教えてもらえると助かります。 でも、ここまでくるとGUIか月初めにトリガーで動かすプログラムを組んだ方が良い気がしています。てか月に一回しかやらない系のイベントなら手で設定するほうが確実だよな? 特定の日付の予定を取得する: getEventsForDay ドキュメントとしては、 こちら になります。指定した日付の予定情報をすべて取得します。サンプルでは、「実行当日の予定をすべて取得し、イベント名とイベントの合計時間を取得」となります。 const calenarID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" const calendar = CalendarApp.getCalendarById(calenarID) const getEventsForDay = () => { const startTime = new Date() const options = { // start: 0, // max: 10, // author: "", // search: "", // statusFilters: [ // CalendarApp.GuestStatus.OWNER, // CalendarApp.GuestStatus.INVITED, // CalendarApp.GuestStatus.YES, // CalendarApp.GuestStatus.MAYBE, // CalendarApp.GuestStatus.NO // ] } const events = calendar.getEventsForDay(startTime, options) // 各イベント情報をそれぞれ表示する events.forEach((event) => { console.log({ "id": event.getId(), "title": event.getTitle(), "time": (event.getEndTime() - event.getStartTime()) / 360 / 10000, "owner": event.isOwnedByMe(), "isAllDayEvent": event.isAllDayEvent(), "eventType": event.getEventType().toString(), "Mystatus": event.getMyStatus().toString() }) }) // イベントの合計時間を何時間表記で取得する const total = events.reduce((prev, event) => { const isAllDayEvent = event.isAllDayEvent() if (isAllDayEvent) { return prev } return prev + (event.getEndTime() - event.getStartTime()) / 360 / 10000 }, 0) Logger.log(total) } getEventsForDay の引数は2つになります。時間情報は参照されずに、年・月・日の情報のみ使用されます。 名前 型 説明 date Date イベント取得日時:必須 options Object イベントの詳細設定:オプション options情報としては、以下の情報を設定することができます。 名前 型 説明 start int 返答されるイベントのインデックス(何番目のイベントから取得するか) max int 返答されるイベントの最大数 author String イベント参加者のフィルタ(メールアドレス) search String フィルタリングに使用される全文検索クエリ statusFilters GuestStatus [] フィルタリングに使用されるステータスの配列(参加・未定・不参加 etc…) 特筆する点としては、 search ・ statusFilters[] になります。 search では、全文検索クエリとなっています。部分一致で検索することができます。Prefixなどを付けて予定を登録すると管理がしやすいですね。 statusFilters は、イベントに対するステータスで検索することができます。プロパティの説明は こちら にあります。カレンダー上で参加状況をちゃんと解答していれば、フィルターで情報を分析することができます。 こちらのサンプルでは、「招待されて参加したイベント・自分が発行したイベントの情報を取得して、合計時間を計算」となります。 const calenarID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" const calendar = CalendarApp.getCalendarById(calenarID) const getEventsJoinTotalTime = () => { const startTime = new Date() const options = { statusFilters: [ CalendarApp.GuestStatus.OWNER, CalendarApp.GuestStatus.YES ] } const events = calendar.getEventsForDay(startTime, options) // イベントの合計時間を何時間表記で取得する const total = events.reduce((prev, event) => { // 終日イベントを除外する const isAllDayEvent = event.isAllDayEvent() if (isAllDayEvent) { return prev } return prev + (event.getEndTime() - event.getStartTime()) / 360 / 10000 }, 0) Logger.log(total) } 特定の期間の予定を取得する: getEvents ドキュメントとしては、 こちら になります。 getEventsForDay と使用感はほぼ一緒ですが、期間を明確に設定することができます。サンプルでは、「実行当日の8:00~18:30のイベントを取得」です。 const calenarID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" const calendar = CalendarApp.getCalendarById(calenarID) const getEvents = () => { const startTime = new Date() startTime.setHours(8, 0, 0) const endTime = new Date() endTime.setHours(18, 30, 0) const events = calendar.getEvents(startTime, endTime) events.forEach((event) => { Logger.log({ "id": event.getId(), "title": event.getTitle(), "time": (event.getEndTime() - event.getStartTime()) / 360 / 10000, "owner": event.isOwnedByMe(), "isAllDayEvent": event.isAllDayEvent(), "eventType": event.getEventType().toString(), "Mystatus": event.getMyStatus().toString() }) }) } getEvents で設定できる引数は3つです。使用時に注意が必要なのは endTime です。こちらは、イベントの開始時間が、 endTime よりも速い場合はイベントがヒットします。 名前 型 説明 startTime Date イベントの開始日時:必須 endTime Date イベントの終了日時(イベントの開始時間が含まれる場合は検索に含む):必須 options Object イベントの詳細設定:オプション options で設定することができるのは、 getEventsForDay と同じになります。 終わり 今回は、GASでGoogle Calendarを操作する方法についてまとめました。こちらで紹介したコードと他の機能を組み合わせることで、Google Calendarを含めた自動化もできると思います。 今回紹介したコードを参考に、皆さんも自分なりのGoogle Calendar活用方法を見つけてみてください。もし、わからないことがあれば、コメント欄でお気軽に質問してくださいね。(第三水曜日を指定する方法がわかったらこっそり教えてください) それでは、また次回の記事でお会いしましょう! GAS関連ブログ 2024-02-01 GASで月初めにメールを送信する:祝日・土日対応 2023-09-04 Google Apps Script(GAS)でスプレッドシートを新規シート作成 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【サンプルコード付き】GASでGoogle Calendarを操作する方法を徹底解説 first appeared on SIOS Tech. Lab .
概要 ども!久しぶりにGoogle Apps Script(GAS)でプログラムを書いている龍ちゃんです。さくっとかけてGoogleのサービスと連携できるのが利点ですね。今回は、プログラムの実行日時が土日祝日か判定する必要が出てきたので執筆です。 今回の内容です。 土日の判定をDate型を活用して判定 Google Calendarを活用して祝日を判定 実行日から一番近い平日を取得 それぞれ関数化して共有していきます。 土日の判定をDate型を活用して判定 const isWeekend = (date) => { const day = date.getDay() if (day === 0 || day === 6) { return true; } return false; } こちらは、 Date型の曜日情報を取得 して判定しています。日曜日を0として曜日情報がカウントされています。0が日曜日、6が土曜日になります。 Google Calendarを活用して祝日を判定 const holidayCalendarId = 'ja.japanese#holiday@group.v.calendar.google.com'; const calendar = CalendarApp.getCalendarById(holidayCalendarId); const isHoliday = (date) => { const otherHoliday = [ [2, 3], // 節分 [3, 3], // ひな祭り [5, 12], // 母の日 [7, 7], // 七夕 [12, 25] // クリスマス ] // monthIndex 0~11のため+1 const month = date.getMonth() + 1 // 日付はそのまま取得でOK const day = date.getDate() const isOtherHoliday = otherHoliday.some((event) => { return event[0] == month && event[1] == day }) if (isOtherHoliday) return false const events = calendar.getEventsForDay(date); return events.length > 0; } Google Calendarの「日本の祝日」から情報を取得して、活用しています。ただ、「日本の祝日」カレンダーの欠点で、「日本で休みじゃない祝日」も登録されています。そのため、人力でその判定を行っています。処理としては、以下の二点です。 祝日として登録されているが休みではない日の判定 Google Calendar経由で祝日の判定 Go o gle Calendarの操作はこちら でまとめています。 実行日から一番近い平日を取得する const getWeekdays = () => { const targetDate = new Date() while (isWeekend(targetDate) || isHoliday(targetDate)){ targetDate.setDate(targetDate.getDate() + 1) } return targetDate } こちらは、先ほど作成した isWeekend / isHoliday を使用して、実行した日が土日祝であれば一番近い平日を取得しています。 終わり ども!今回はGASで土日祝を判定するプログラムの紹介を行いました。トリガーと組み合わせて、「毎週月曜日に定期実行を設定して、月曜日が祝日だった場合は近い平日に処理を振り返る」みたいな処理も可能です。 ほなまた~ GAS関連ブログ 2024-02-01 GASで月初めにメールを送信する:祝日・土日対応 2023-09-04 Google Apps Script(GAS)でスプレッドシートを新規シート作成 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【サンプルコード付き】GASで土日祝日判定スクリプトを作成する方法 first appeared on SIOS Tech. Lab .
こんにちは、サイオステクノロジーの遠藤です。 弊社ではこの度、Azure OpenAI サービスを基盤として最短一日でRAG環境を提供するAzure OpenAI Service RAGスターターパックのサービス提供を始めました。 本ブログではAzure OpenAI Service RAGスターターパックを利用し、独自データの登録からチャットUIによる検索までをご紹介します。 また、より詳細なサービス概要は 弊社ソリューションサイト SIOS NEXT TECH SOLUTIONS からもご確認いただけます。ご興味のある方は是非アクセスしてみてください。 関連ブログ Azure OpenAI Service RAGスターターパック サービス提供開始しました! Azure OpenAI Service RAGスターターパックとは? Azure OpenAI Service RAGスターターパック(以下「RAGSP」という)は社内データといった独自情報を外部知識として活用する生成AI技術であるRAG(Retrieval-Augmented Generation)を利用したアプリケーションを提供可能なパッケージとなります。 本パッケージではクラウドインフラからアプリケーションまでを最短一営業日で構築し、チャットUI+回答精度の評価改善までのオールインワン基盤をご提供します。   ドキュメントの登録 今回登録に使用するデータ 厚生労働省が出している モデル就業規則 から解説部分を取り除き、就業時間などのサンプルデータを入れたRAGテスト用就業規則を作成いたしました。今回はRAGSPのアプリケーション構築までは終わっている前提で作成したRAGテスト用就業規則をRAGSPで独自情報として登録を行い、チャットUI上から検索をかけるところまでを行っていきます。 作成したPDFはこちらになります : RAGテスト用就業規則 データを登録する では、用意したRAGテスト用就業規則をRAGSPに登録してみましょう。 1. RAGSPはAzure上で構築されていますので、データを登録するにまずはAzureポータルにログインしましょう。ログインが出来たら、画面上部の「リソース、サービス、ドキュメントの検索」に「ストレージアカウント」と入力します。   2.ストレージアカウントの一覧が表示されるので、独自データの保管先となる「環境名 + egstrsp」を選択します。 3.選択したストレージアカウントからコンテナーを選んで保存先になるコンテナー名(ここではai-test)を選択します。 4.「アップロード」をクリックし、ドラッグアンドドロップでRAGの検索対象としたいデータをアップロードをして完了です。ファイルをアップロードすると自動的にファイルを検索できる形に整形され、数分後からチャットUI上で登録したデータに関する検索ができるようになります。これでデータの登録作業は完了です! チャットUIを使用して検索してみる 1.RAGSPのURLにアクセスします。アクセスするとサインインが求められますので、ご所属の組織で利用している認証方法でサインインを行います。 2.チャットUIが表示されるので、登録したデータにかかれている内容について質問してみましょう。今回は「採用時に必要となる書類について教えて」と質問してみます。 3.少し待つと先ほど登録したデータをもとに回答が生成され、採用時に必要となる書類一覧が回答として返ってきました。また回答の最後には、回答を生成するために参照したドキュメントへのリンクも付属しています。 4.回答根拠ドキュメントへのリンクをクリックするとPDFが表示されるので、PDFの情報から正しく回答がなされているか確認してみます。しっかりデータ登録を行ったPDFから回答を作成してくれることが確認できました! まとめ 今回は弊社からリリースしたAzure Open AI Service RAGスターターパック(RAGSP)を利用し、独自データの登録からチャットUIによる検索までをご紹介を行いました。RAGSPは難しくなりがちなデータ登録作業をブラウザ上から簡単に利用することができるので、初めての方でも安心してご利用していただけるサービスとなっております。 改めての紹介になりますが、以下のページからより詳細なサービス概要をご確認いただけますので是非一度ご覧いただければと思います。お問い合わせをお待ちしております。 「Azure OpenAI Service RAGスターターパック」 サービス概要ページ https://nextech-solutions.sios.jp/genai/azure-openai-service-ragsp.html   ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure OpenAI Service RAGスターターパック 利用してみた! ~データ登録・チャットUI編~ first appeared on SIOS Tech. Lab .
こんにちは、サイオステクノロジーの遠藤です。 弊社ではこの度、Azure OpenAI サービスを基盤とした最短一日でRAG環境を提供するAzure OpenAI Service RAGスターターパックのサービス提供を始めました。 本ブログではAzure OpenAI Service RAGスターターパックの概要と特徴についてご紹介します。また、より詳細なサービス概要は 弊社ソリューションサイト SIOS NEXT TECH SOLUTIONS からもご確認いただけます。ご興味のあるかたは是非アクセスしてみてください。 Azure OpenAI Service RAGスターターパックとは? Azure OpenAI Service RAGスターターパック(以下「RAGSP」という)は社内データといった独自情報を外部知識として活用する生成AI技術であるRAG(Retrieval-Augmented Generation)を利用したアプリケーションを提供可能なパッケージとなります。 本パッケージではクラウドインフラからアプリケーションまでを最短一営業日で構築し、チャットUI+回答精度の評価改善までのオールインワン基盤をご提供します。 機能紹介 1. 回答根拠ドキュメント表示機能を搭載したチャットUI RAGSPではブラウザから操作可能なチャットUIを提供しています。また、生成された返答にに回答の根拠となったドキュメントの表示機能がついており、回答の正しさの確認を行うことができるようになっています。 2. ドキュメントをブラウザから簡単に登録 回答のもととなる独自情報の登録はAzure Portalを通じてブラウザ上から行うことができます。インデクシングなどの複雑な処理を意識することなく、運用者にとってもカンタンにRAGアプリケーションのメンテンスを行うことが可能です。 3. プロンプトフローを利用した評価・改善 Azure Machine Learning プロンプト フロー の評価フローを利用して、GUI上から関連度、類似性といた観点から定量的な評価を行うことが可能になっています。また評価の結果、改善の必要性がある判明された場合、こちらもプロンプトフロー上からローコードによる改善を行うことが可能になっています。 4. Bicepを利用したIaCを構築 Azure リソースをデプロイするドメイン固有言語 (DSL) であるBicepを利用してインフラの構築を行うため、最短1営業日での構築が可能になっています。またGitHub, GitLabに対応したCICDパイプラインを組んでいるため、後述するアプリケーションのカスタマイズの反映も簡単に行うことが出来ます。 様々なカスタマイズもご提案可能! RAGSPでは他にもオプション機能として、Azureの閉域対応や、様々な形式の入出力に対応できるようにするマルチモーダル対応などの機能がご提案可能です。 これ以外にもお客様ごとのご要望に合わせた機能のご提案も可能になっておりますので、ご気軽にご相談ください。 まとめ 今回は弊社からリリースしたAzure Open AI Service RAGスターターパック(RAGSP)についてご紹介しました。改めての紹介になりますが、以下のページからより詳細なサービス概要をご確認いただけますので是非一度ご覧いただければと思います。お問い合わせをお待ちしております。 「Azure OpenAI Service RAGスターターパック」 サービス概要ページ https://nextech-solutions.sios.jp/genai/azure-openai-service-ragsp.html ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure OpenAI Service RAGスターターパック サービス提供開始しました! first appeared on SIOS Tech. Lab .
こんにちは。サイオステクノロジー OSS サポート担当 山本 です。 今回は solr の仕組みの時にお話しした 辞書 を拡張するお話です。 ■おさらい:solr と辞書のお話 今回のお話を進めていく前に、軽くおさらいをしておきましょう。 solr は 予め登録しておいた一連のデータ である ドキュメント から高速な 文字列検索 を行うことに特化した、全文検索 OSS です。 この高速検索を実現するため、solr は ドキュメント を登録する時に 文字列の解析 を行い、解析の結果出てきた検索ワードとして有効なワードを元にして インデックス と呼ばれるデータベースを作成します。 検索を行う際にも 検索ワードを解析 して、出てきたワードを インデックス から検索することで、検索の際の負荷や所要時間を大幅に抑えて無駄の少ない 全文検索 ができる、というわけです。 ところで、「 文字列の解析 」と軽く言いはしましたが、これには様々なメカニズムが使われています。 特に日本語は「単語ごとに空白スペースで区切る」という言語的なルールがない故に “単語に分割する” 処理が機械的にできないため、内部の 辞書 を活用して単語に切り分けるメカニズムが使用されています。 ■解析と辞書とインデックス 解析の例を見ながら、もう少し細かくおさらいしておきます。 例えば、solr の demo 用コアで “これはあかきえんぴつです。” という文章を解析すると、以下のように分割されます。 これ / は / あかき / えんぴつ / です / 。 このように分割ができるのは、「これ」「は」「あかき」「えんぴつ」「です」という それぞれの単語が solr の内部辞書に存在している から、ということになります。 分割された後は、以前紹介した フィルタ によって基本形に戻したり検索に不適なワードを除外したりして、最終的に残ったものが インデックス に登録されます。 この文章を持つドキュメントを登録するならば、「あかい (“あかき” の基本形)」と「えんぴつ」がインデックスに追加され、ドキュメントと関連付けられます。 検索の際にはこの インデックスと一致するか をチェックしているため、例えばですが  ・「グレー」で検索して「 グレー プ」が引っかかる  ・「レンジ」で検索して「オ レンジ 」が引っかかる などのような、 一般的な文字列検索で起こってしまうような「そうなんだけど、そうじゃない…!」というタイプの検索ノイズを低減できる というのも、solr の強みの一つと言えます。 ともあれ、日本語で solr を使う場合には、 辞書に単語がちゃんと登録されているか がかなり重要であると言えます。 ちなみに、辞書を使って上手く分割できなかった部分は、文字種 (かな・カナ・漢字など) が変わる部分で区切ってそれぞれを固有名詞と想定して処理してくれます。 ■辞書を拡張する さて、では今回の本題です。 solr の採用している日本語形態素解析器 “Kuromoji” の内部辞書は非常に強力で、一般的な単語であれば殆どカバーしてくれるはずです。 しかし、それでも解析結果が常に思い描いたとおりになるわけではありません。例えば……   ・単語を分割したい 一例として、デフォルトの日本語辞書では「 ショルダーバッグ 」「 トートバッグ 」は 1単語 として扱われます。 そのため、「 バッグ 」で検索しても「ショルダーバッグ」や「トートバッグ」を含むドキュメントはヒットしません。   ・人名や社名、製品名などの固有名 一般的な単語ではない固有名は当然ながら辞書には登録されておらず、うまく解析できないケースがあります。 一例としては、「サイオステクノロジー」「OSSよろず相談室」は、demo コアのデフォルト設定では「サイ / オス / テクノロジー」「OSS / よろず / 相談 / 室」と分割され、例えば「サイ」で検索して「サイオステクノロジー」がヒットするなど意図しない形でのヒットを誘発してしまう可能性があります。 このような問題を解決できるアプローチの一つが、 辞書の拡張 です。 ■ユーザ辞書の内容確認と作成 solr では ユーザ辞書 という機能を使って、任意のフレーズが出てきた際の 単語の区切り方 を形態素解析の辞書に追加することができます。 実際に前回作成したコアにユーザ辞書を追加してみます。 (コアの作り方や前回お話ししたファイルなどについては省略します。前回記事も一読の上、読み進めてください。) ユーザ辞書もデモ用環境のコア “demo” にサンプルが入っているので、今回はこちらを参考にして、前回作成した実験用コア “test-new-core” を使って確認していきます。 まずは前回使ったデモ用環境のコンテナを起動し、”demo” のユーザ辞書のサンプル “/var/solr/data/demo/conf/lang/userdict_ja.txt” を手元の環境に持ってきます。 ## (デモ用環境起動) $ podman start test-solr ## (ユーザ辞書サンプルのコピー) $ podman cp test-solr:/var/solr/data/demo/conf/lang/userdict_ja.txt ./ 手元の環境にコピーした userdict_ja.txt を適当なテキストエディタで開き、まずは内容を確認してみます。 行頭が “#” の行はコメント行扱いとなるので、(ファイルの先頭のユーザ辞書の説明コメント部分を除くと) このファイルはこのような記述がされています。 # Custom segmentation for kanji compounds 日本経済新聞,日本 経済 新聞,ニホン ケイザイ シンブン,カスタム名詞 関西国際空港,関西 国際 空港,カンサイ コクサイ クウコウ,カスタム名詞 # Custom segmentation for compound katakana トートバッグ,トート バッグ,トート バッグ,かずカナ名詞 ショルダーバッグ,ショルダー バッグ,ショルダー バッグ,かずカナ名詞 # Custom reading for former sumo wrestler 朝青龍,朝青龍,アサショウリュウ,カスタム人名 これを見れば (あるいはファイル先頭の説明コメントを読めば) なんとなくわかるかと思いますが、ユーザ辞書は以下のような書式で 登録したいワードを列挙したテキストファイル になります。 [登録したい文字列],[登録したい文字列での単語の切り分け方],[各単語の読み方 (カナ)],[品詞] 第2要素の単語の切り分けでは、 半角スペース で単語を切り分け ます。 第3要素の読み方は、 第2要素で切り分けた単語数と同じ数 になるよう、 半角スペース区切りで各単語の読み方 を記述します。 第4要素の品詞は、主にフィルタ “ japanesePartOfSpeechStop ” で使用される要素で、このフィルタで参照しているファイルに 記述されている品詞と一致 していると、その単語は インデックスの登録対象から除外 されます。 デモ用環境では、japanesePartOfSpeechStop フィルタは “/var/solr/data/demo/conf/lang/stoptags_ja.txt” を参照しています。気になる方はこちらのファイルも確認してみてください。 以下の行を例にしてみると… 関西国際空港,関西 国際 空港,カンサイ コクサイ クウコウ,カスタム名詞  ・対象となるフレーズ (文字列) :「関西国際空港」  ・単語の切り分け方      :「関西」「国際」「空港」  ・切り分けた単語の読み方 (カナ):「カンサイ」「コクサイ」「クウコウ」  ・このフレーズの品詞     :「カスタム名詞」 となるわけですね。 さて、構造がなんとなくわかったところで、このユーザ辞書 “userdict_ja.txt” をベースにユーザ辞書を試してみましょう。 先ほど挙げた例 (「トートバッグ」「ショルダーバッグ」「サイオステクノロジー」) を使おうかと思いますが、「トートバッグ」「ショルダーバッグ」はサンプルとして既に登録されているので、「サイオステクノロジー」が1単語として扱われるように登録してみます。 以下のような行を “userdict_ja.txt” の末尾に追加しましょう。 サイオステクノロジー,サイオステクノロジー,サイオステクノロジー,カスタム名詞 ■ユーザ辞書の適用 続いて変更したユーザ辞書を、前回作成した実験用コア “test-new-core” に適用してみましょう。 まずは、作成したユーザ辞書を “test-new-core” に配置します。 “test-new-core” のスキーマファイルから参照できればどこにおいても大丈夫なはずですが、サンプルに従ってコアのディレクトリ内の “conf/lang/” 配下に配置するのが無難でしょう。 $ podman cp ./userdict_ja.txt test-solr:/var/solr/data/test-new-core/conf/lang/userdict_ja.txt 続いて、このユーザ辞書を参照するように、スキーマファイルを変更します。 “test-new-core” のスキーマファイル “managed-schema.xml” を一旦手元に持ってきて… (デモ用コンテナにはテキストエディタがないため) $ podman cp test-solr:/var/solr/data/test-new-core/conf/managed-schema.xml ./managed-schema.xml このスキーマの日本語解析の設定を行なっている部分の “ tokenizer ” 要素を以下のように変更します。 ## 変更前 &ltfieldType name="text_ja" class="solr.TextField" autoGeneratePhraseQueries="false" positionIncrementGap="100"> &ltanalyzer> &lttokenizer mode="search" name="japanese"/> &ltfilter name="japaneseBaseForm"/> : : ## 変更後 &ltfieldType name="text_ja" class="solr.TextField" autoGeneratePhraseQueries="false" positionIncrementGap="100"> &ltanalyzer> &lttokenizer mode="search" name="japanese" userDictionary="lang/userdict_ja.txt" /> &ltfilter name="japaneseBaseForm"/> : : 変更したら “test-new-core” のスキーマファイルを置き換えれば準備 OK です。 $ podman cp ./managed-schema.xml test-solr:/var/solr/data/test-new-core/conf/managed-schema.xml 変更したスキーマの設定を読み込むため、”test-new-core” をリロードします。 リロードはコマンドと管理画面のどちらからでも可能で、 コマンドでリロードするなら以下のようなコマンドを、 $ curl -X POST http://(IP or ホスト名):8984/api/cores/test-new-core/reload 管理画面でリロードするなら “ Core Admin ” の画面から対象のコアを選択して、” Reload ” ボタンを押してください。 これで “test-new-core” にカスタムしたユーザ辞書を適用できました。 早速、管理画面で試してみましょう。 “Core Selector” タブで test-new-core を選んで、” Analysis ” を開いて、ユーザ辞書を適用した FieldType “text_ja” で「ショルダーバッグ」「サイオステクノロジー」の単語分けの状況を確認してみましょう。 上手くいっていそうです。 そういえば、前回このコアに登録したドキュメントに「サイオステクノロジー」を含むものがありましたね。 折角なのでちゃんと検索もできるかを確認してみましょう。 (コマンドだと URL エンコードで見辛いので管理画面で確認してみます。) ……引っかかっていませんね?何か間違えたのでしょうか? 試しに元々の解析で出ていたワードの一つ「サイ」で検索をかけてみましょう… こちらだと引っかかってしまいますね。(こういう引っかかり方を避けるためにユーザ辞書を変更したはずなのに…) これは一体どういうことでしょう? ■ユーザ辞書変更とインデックス ユーザ辞書変更直後に検索が上手くいかない現象には、アレが深く関係しています。 そう、アレです。 インデックス です。 何度かお話ししましたが、solr は高速な文字列検索を実現するために  ・ ドキュメント (データ) の 登録時 に、登録データを 解析した結果をインデックスに登録 する  ・検索時は検索ワードの解析結果を基に インデックスから検索 する というアプローチをとっています。 これは特に変なことをしなければ上手くいくのですが、今回起こったことを (簡略化して) 順に見ていきましょう… まず、「サイオステクノロジー」をドキュメントに登録しました。 この時点では「サイオステクノロジー」は「サイ / オス / テクノロジー」と切り分けられていたため、 インデックスには「サイ」「オス」「テクノロジー」の 3ワード がこのドキュメントと関連するワードとして登録されます。 この時点では「サイオステクノロジー」で検索すると、 解析された「サイ」「オス」「テクノロジー」の3ワードでの検索 となります。 インデックスの「サイ」「オス」「テクノロジー」は、いずれもドキュメントの「サイオステクノロジー」に紐づいているので、この検索は成功します。 さて、ここで今回の変更です。 解析結果がおかしいことに気が付いて、「サイオステクノロジー」を1ワードとして扱うように ユーザ辞書 を作りました。 しかし、 この作業をする前に登録済みのインデックスは変更されていません 。 この状態で「サイオステクノロジー」で検索すると、 解析結果の「サイオステクノロジー」をインデックスから検索 しようとします。 ところが、ドキュメント「サイオステクノロジー」に紐づいているインデックスは 登録した時点 の「サイ」「オス」「テクノロジー」の3ワードのみであり、 「サイオステクノロジー」でインデックスから検索しても一致するものがありません 。 他方、「サイ」だけで検索すればこの解析結果は「サイ」です。 これは最初に「サイオステクノロジー」をドキュメント登録した時の解析結果「サイ」「オス」「テクノロジー」の 3ワードのうちの「サイ」と合致して、結果としてドキュメント「サイオステクノロジー」がヒットしてしまいます。 この問題を解決するには、 ユーザ辞書を変更した後 にドキュメントを登録しなおし て、 インデックスを作り直す 必要があります。 また、そもそも元々登録していたドキュメント・インデックスが検索の邪魔になってしまう可能性があるので、 元々のドキュメント・インデックスは一度消したほうがよい です。 つまり、 ドキュメント登録を一からやり直し する必要があります。 ドキュメントとインデックスの削除については、今回の “test-new-core” であれば以下のようなコマンドで行うことができます。 $ curl -X POST -H 'Content-Type: application/json' --data-binary '{"delete":{"query":"*:*" }}' http://(IP or ホスト名):8984/solr/test-new-core/update 上記コマンドの実行後、コアのホームディレクトリの data/index 配下 (今回の “test-new-core” ならコンテナ内の ) にファイルがない状態なのを確認できたら、再度新しくドキュメントを登録し直せば OK です。 再度ドキュメントの登録が完了した後に改めて「サイオステクノロジー」で検索すれば、今度こそ上手くいってくれるはずです。 ■最後に 今回はユーザ辞書についてと、インデックスの補足についてお話ししてみました。 最後にお話しした インデックスの再作成 は、ユーザ辞書の登録以外にも スキーマ設定の変更 (使用するフィルタの変更など) をする場合には大体必要 になってくるので、solr を使う上では忘れてはならない要素です。 以前にお話ししたとおり、解析とインデックス作成をする都合上 solr のドキュメント登録は重めの処理となっているため、何度もインデックス作成をするのはできれば避けたいところです。 少量のテストデータで試してみたり、デモ画面の “Analysis” を使って主要なワードが思った通りに解析されているかをチェックし、できる限りドキュメントの本登録を行う前にスキーマ設定やユーザ辞書をいい感じにしておくとよいでしょう。 なおユーザ辞書に登録したフレーズは、常にユーザ辞書に登録した切り分け方が採用されるので、「デフォルト辞書との競合で意図しないことが起こる」ということは基本的にはないはずです。 安心して必要なフレーズだけ登録するようにしましょう。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Solr って何者?④:解析内容を調整する① (ユーザ辞書) first appeared on SIOS Tech. Lab .
SIOS Tech Labの龍ちゃんです。2024年は「誰でも簡単に」AIツールが作れるDifyが登場して大いに沸いていましたね。弊社では、Azureを利用したサービス開発が多くあるため、Azure OpenAI Serviceがリリースしてそちらばかり触っていました。2025年はDifyなどのノーコード・ローコードツールを駆使して、AIサービスの開発・検証を進めていきたいと考えています。 いまから入門:Difyとは 生成AIアプリ開発ツール「Dify」の特徴については、おそらく皆さんなんとなくわかっているかと思います。実際触ってみた感想としては、手軽さに驚いています。 専門的なAIエンジニアやアプリエンジニアがいなくても、GUIの操作で簡単に生成AIアプリを作ることができます。また、豊富なLLMモデルと接続することができるので、異なる会社提供のLLMを同じ環境で動作するアプリなども自作することができます。 また、OSSでソースコードが提供されているので、必要に応じてどこまでもカスタマイズを行うことができます。非エンジニアからエンジニアまで、幅広いユーザー層にとって有用なツールです。 Difyブームも落ち着いて、ドキュメントやナレッジも豊富になった2024年でした。幅広い領域で使用ユーザーがいる生成AI活用ツールとして弊社でも注目していました。これから入門して追いつきたい方に向けてナレッジをまとめていきます。 Dify開発環境 Difyの開発をお手軽に始める方法としては、「Dify Cloud」「Docker Compose」「ローカルソースコード」が提供されています。複数の観点で利点などをまとめた表が以下になります。 開発環境 手軽さ カスタマイズ性 デプロイ Dify Cloud 不要 Docker Compose Docker環境 ローカルソースコード 複数必要 その他の項目 開発環境 ストレージ 複数ユーザー SSO Dify Cloud クラウド プラン次第 構築済 Docker Compose 実行環境 可能 要構築 ローカルソースコード 実行環境に保存 可能 要構築 「 Dify Cloud 」は一番お手軽に始めることができます。サイトにアクセスしてGoogleでログインすれば、すぐに利用開始です。非エンジニアの方が試すには、一番楽な方法となります。 「 Docker Compose 」では、Docker環境が必要になります。ソースコードをGitから持ってきて、Docker Composeコマンドを実行するだけで環境を立ち上げることができます。情報も実行環境に保存されるため、ローカル実行ではローカルに情報が保存されます。 「 ローカルソースコード 」では、複数の環境(データベース,Python,Node,etc…)が必要なります。ソースコードをGitから持ってきて複数の環境での起動が必要になるため、だいぶ上級者向きの環境構築となります。それぞれの環境をコードレベルで起動することができるので、カスタマイズ性はとても高いです。 それぞれの環境の特徴としてはこんな感じ! Dify Cloude:とてもお手軽!非エンジニアの方はここから始めよう Docker Compose:クラウド上で作業したくないエンジニア、カスタマイズはあんまりしないけどローカルマシンで実行したい人向け ローカルソースコード:がっつりカスタマイズしたい人向け!起動までの道のりは長い! 今回のガイドでは、「Dify Cloud」と「Docker Compose」での環境構築について記載していきます。 Dify Cloud 事前準備として以下のものが必要となります。 有効なメールアドレス or Google アカウント or GitHubアカウント Dify.aiにアクセス してログインすれば、すぐに利用可能です。以下、最初の画面です。 1人での利用で、軽いお試しであれば無料プランで作業することができます。複数人で開発を進めたい場合は、 Planをアップグレード が利用可能です。この辺りは、使用規模と利用用途によって変わるかと思います。 Docker Compose 事前準備として以下のものが必要となります。 Git Docker 19.03~ Docker Compose 1.28~ こちらの 公式リファレンス を参考に作業を進めていきます。まずはGitを使ってソースコードをローカルに持ってきましょう。 git clone https://github.com/langgenius/dify.git 持ってきたら、dokcerフォルダに移動して、環境変数をコピーしてDocker Composeを用いて起動します。 cd dify/docker cp .env.example .env docker compose up -d これで、 http://localhost/install にアクセスすれば、初期設定画面が出てきます。あとは画面のノリに従って設定すれば利用可能になります。 つまづいたポイント Dify DevContainer起動 DifyのリポジトリにDevContainerの設定が入っていました。最初は、こちらで起動できるのかと考えました。環境内には、node環境、Python環境、Docker環境がセットされていました。そのため、ローカルソースコード開発用の設定だと考えられます。 Dify ナレッジが消える・ナレッジ作成ができない Difyでは、独自の文書を知識(ナレッジ)として登録することができます。その際につまづいたポイントとしては以下になります。 ナレッジ作成後、すぐはナレッジ利用ができるがすぐ参照することができなくなる ナレッジ作成ができない 調べていくと、DifyのDocker Composeに追加で設定をしてあげる必要が出てきました。weaviateのポート設定を追記することで解消することができました。 weaviate: image: semitechnologies/weaviate:1.19.0 ports: - "8000:8000" profiles: - "" - weaviate restart: always volumes: # Mount the Weaviate data directory to the con tainer. - ./volumes/weaviate:/var/lib/weaviate environment: # The Weaviate configurations # You can refer to the [Weaviate](<https://weaviate.io/developers/weaviate/config-refs/env-vars>) documentation for more information. PERSISTENCE_DATA_PATH: ${WEAVIATE_PERSISTENCE_DATA_PATH:-/var/lib/weaviate} QUERY_DEFAULTS_LIMIT: ${WEAVIATE_QUERY_DEFAULTS_LIMIT:-25} AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: ${WEAVIATE_AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED:-false} DEFAULT_VECTORIZER_MODULE: ${WEAVIATE_DEFAULT_VECTORIZER_MODULE:-none} CLUSTER_HOSTNAME: ${WEAVIATE_CLUSTER_HOSTNAME:-node1} AUTHENTICATION_APIKEY_ENABLED: ${WEAVIATE_AUTHENTICATION_APIKEY_ENABLED:-true} AUTHENTICATION_APIKEY_ALLOWED_KEYS: ${WEAVIATE_AUTHENTICATION_APIKEY_ALLOWED_KEYS:-WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih} AUTHENTICATION_APIKEY_USERS: ${WEAVIATE_AUTHENTICATION_APIKEY_USERS:-hello@dify.ai} AUTHORIZATION_ADMINLIST_ENABLED: ${WEAVIATE_AUTHORIZATION_ADMINLIST_ENABLED:-true} AUTHORIZATION_ADMINLIST_USERS: ${WEAVIATE_AUTHORIZATION_ADMINLIST_USERS:-hello@dify.ai} 別環境で、localhost:8000番を使用していてポートの設定が勝手に割り振られたなどの可能性は無限大です。一度、コンテナを落として再アップしてください。 おわり 今回は、Difyの環境構築について解説しました。クラウド版とDocker Compose版の2つの方法を紹介しましたが、初めて触る方はクラウド版から始めることをお勧めします。次回は、実際にDifyを使って簡単なAIアプリケーションを作成する方法について解説していきたいと思います。 これからDifyを使ってゴリゴリ開発して、ブログ投稿していきます。 ではまた! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Dify入門ガイド:初期セットアップ first appeared on SIOS Tech. Lab .
はじめに 皆さんこんにちは。エンジニアの細川です。 皆さんはNestJSで開発を行っているときに Error: Nest can't resolve dependencies of the AppController のようなエラーに遭遇したことはありませんか? 基本的にこのエラーは依存性の解決がうまくできないときに発生するので、各モジュールの依存( imports や exports )を確認すれば解決できることが多いです。 しかし、コードをいくら確認しても問題ないのに、この依存性解決が出るという場面に出くわしたので共有します。同じ原因でエラーが出ている方の参考になれば幸いです。 原因 早速原因ですが、ずばりimportが以下のように型importになっていたことでした。 // 正しい状態 import { AppService } from "@modules/app/services/app.service"; // エラーが出る状態 import type { AppService } from "@modules/app/services/app.service"; この状態ではNestJSがうまく依存性を解決できず、エラーとなってしまいます。 特にBiomeを導入している方は、デフォルトでこのルールが有効になっており、formatされるタイミングで勝手に型インポートに変換されるので、注意が必要です。 Biomeの 公式ドキュメント でもルールを無効にすることを推奨されています。 エラーから原因が分かりづらいので皆さんも注意してください。 対処法(Biomeの場合) 対処法としてはこのルールをオフにすることです。 biome.json に以下の記述を追加します。 { ... "linter": { ... "rules": { // 以下を追加 "style": { "useImportType": "off" } } } } これを追加しておくことで、format時に自動で型importに変更されることもなく依存性解決エラーを解消できます。 ESLintの場合 ESLintの場合も consistent-type-imports というルールが存在するようなので、もしpluginなどに含まれていて明示的にオフにする必要がある場合は無効にしておくと良いかと思います。 詳細はESLintの 公式ドキュメント を確認してみてください。 おわりに 今回はNestJSで謎の依存性解決エラーが出る場合の対処法を紹介しました。 エラーから原因を探りづらいので、こちらの記事が参考になれば幸いです。 他にもNestJSやTypeScriptの 記事 を書いているのでぜひ読んでみてください。 今回参考にさせていただいた記事 https://biomejs.dev/ja/linter/rules/use-import-type/ https://typescript-eslint.io/rules/consistent-type-imports/ https://zenn.dev/dev_commune/articles/ba2586c6ad3efc     ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post NestJSで謎のcan`t resolve dependenciesエラーの解消方法 first appeared on SIOS Tech. Lab .
はじめに サイオステクノロジーの塚田です。Rancher入門ブログシリーズとして、 前回 はRancher上でモニタリング環境を構築する方法について解説しました。本記事では、Rancherから分散ストレージ管理サービスであるLonghornをインストールし、マルチクラスター環境下でストレージを管理する方法を解説します。 Longhornとは? Longhornとは、Rancher Lab社が開発した分散型ブロックストレージシステムです。軽量でKubernetesに特化していることが特徴です。Longhornを導入することで、複数のノードにまたがってデータを分散して管理する仕組みを容易にセットアップすることができます。 Longhornの導入手順 前提条件 Rancher Serverが構築済みであること Rancher Serverの構築方法は 過去の記事 にて紹介しております。 Rancher Serverにログインできること Rancherに管理対象となるクラスターを連携済みであること 過去の記事 にてEC2を利用して構築したKubernetesクラスターをRancherに登録する手順を紹介しております。 管理対象クラスターにrancher-monitoringをインストール済みであること rancher-monitoringのインストール方法については 前回の記事 にて紹介しております。 動作環境 今回はAmazon EC2を利用して構築したKubernetesクラスターにLonghornをインストールし、ストレージ管理のデモを行います。動作環境は以下の通りです。 ノード台数※1 Control Plane: 3 Worker Node: 2 インスタンスサイズ: t3.xlarge vCPU: 4 メモリ: 16GiB OS: Ubuntu ストレージ EBS gp3 50GB x 5 S3バケット x 1 ※1: Longhornは最低3台のノードを利用することが推奨されています。詳しくは公式ドキュメント: Minimum Recommended Hardware の項目をご参照ください。 構築イメージ 今回はRancherのダッシュボードから管理対象となるKubernetesクラスターにLonghornをインストールします。kubectlやHelmを利用して各クラスターに直接インストールすることもできますが、Rancherの統合管理機能を活用することで、Longhornのモニタリングを簡単に設定することができます。 導入後のイメージは以下の通りです。 バージョン情報 Longhorn: 1.7.2※ ※本記事執筆時点における最新バージョン Longhornのインストール Rancherにログインし、サイドバーの[Apps]→[Charts]を選択します。 チャート一覧からLonghornを選択します。 デフォルト設定のまま[Install]を押下します。 インストールが完了すると、longhorn-systemというnamespaceが作成されます。 また、Rancher UIのサイドバーにLonghornという項目が追加されます。 ここからLonghornのダッシュボードに遷移することができます。 Longhornによるストレージ管理デモ ここからはLonghornを使って、実際にストレージ管理する手順をご紹介します。 Volumeの作成と管理 Longhornをインストールすると、longhornという名前のStorage Classオブジェクトが作成されます。このStorage Classを利用することで、PersistentVolumeClaimを作成して動的にPersistentVolumeを作成することができます。 Rancherダッシュボードのサイドバーから[Storage]→[PersistentVolumeClaims]と移動します。 以下の設定でPVCを作成します。 namespace: demo-ns name: demo-pvc Storage Class: longhorn Storage Size: 10GB PVCを作成すると、PersistentVolumeが動的に作成されます。 続いて、作成したボリュームをPodにアタッチします。 Rancherダッシュボードの[Workloads]から[Deployments]に遷移し、Deploymentを新規作成します。 namespace: demo-ns Storage: PersistentVolumeClaim Deployment設定画面の[Pod]欄から[Storage]を選択し、先ほど作成したPVCを選択します。 Deployment設定画面のコンテナ設定画面の[General]に移動し、以下の設定を入力します。 Container Image: alpine:latest Stdin: Yes TTYを有効化 コンテナ設定画面の[Storage]に移動し、マウントポイントを設定します。 Mount Point: /data [Create]ボタンを押下してDeploymentを新規作成します。 Podが起動したら、Pod一覧画面から作成したPodを選択し、[Execute Shell]を押下してPodにログインします。 先ほどマウントした/dataディレクトリに移動できることが確認できました。 以下のコマンドで500MBのダミーファイルを作成してみます。 $ dd if=/dev/zero of=dummy bs=1M count=500 マウントしたディレクトリ内に500MBのファイルを作成することができました。 次に、サイドバーの[Longhorn]からLonghornのダッシュボードに遷移します。 Longhornのダッシュボードのメニューから[Volumes]を選択すると、先ほど作成したPersistentVolumeがLonghornのダッシュボード上にも表示されています。 ボリューム名を押下し、ボリューム詳細画面に遷移すると、3つのノード上でボリュームのレプリカが作成されていることがわかります。 レプリカ作成数はLonghornインストール時の設定項目[Default Storage Class Replica Count]の値を変更することで調整できます。 このように、LonghornはStorage Classの設定に基づき、ボリュームを動的にプロビジョニングするとともに、自動的にレプリカを作成して冗長性を担保しています。 データ保護: SnapshotとBackup 続いて、ストレージ内のデータを保護する機能について紹介します。 Longhornはボリュームのスナップショットとバックアップを作成することができます。スナップショットは、Longhornボリュームのある時点での状態を記録したもので、各ボリュームのレプリカ内にローカルに保存され、Kubernetesクラスター内のノード上に配置されます。バックアップは、スナップショットを元に作成されるもので、ボリュームデータの特定時点の状態を外部のセカンダリストレージ(例: S3互換オブジェクトストレージやNFS)に保存します。スナップショットはクラスター内部でのデータ管理や復元に利用され、バックアップは外部ストレージに保存して災害復旧や長期保管の用途に適しています。 実際にスナップショットやバックアップを作成してみます。 まずはLonghornのダッシュボードの[Volumes]画面に遷移し、先ほど作成したボリュームを選択します。 ボリューム詳細画面の[Snapshots and Backups]まで移動し、[Take Snapshot]ボタンを押下します。 しばらくすると、スナップショットが作成されます。 続いて、バックアップを作成してみます。 バックアップを作成するには、保管場所となる外部ストレージが必要です。 本記事では、バックアップの保存先としてS3バケットを使用します。 利用可能なストレージに関しては、公式ドキュメントの Setting up a Target の項目をご参照ください。 まずは、S3バケットを作成しておきます。 Rancherのダッシュボード[Storage]→[Secrets]へ移動し、namepsace [longhorn-system]上にAWSのクレデンシャル情報を格納するSecretを作成します。 本記事ではAddministratorAccess権限をアタッチしたIAMユーザーを使用しておりますが、必要となるIAM権限につきましては、公式ドキュメントの Set up AWS S3 Backupstore の項目をご参照ください。 Secretを作成したら、Longhornダッシュボードに遷移し、[Settings]を選択します。 Settings内のBackup欄でS3への接続情報を入力し、[Save]ボタンをクリックして保存します。 Backup Target: S3バケットのURL Backup Target Credential Secret: AWSクレデンシャル情報を格納したSecretのname これでバックアップ保存先の設定が完了しましたので、実際にバックアップを作成してみましょう。 [Volumes]よりボリューム一覧画面に遷移し、作成したボリュームを選択します。 選択したボリュームの詳細画面内[Snapshots and Backups]にて、[Create Backup]を押下し、バックアップを作成します。 [Snapshots and Backup List]にてバックアップが作成されたことを確認することができます。 また、Longhornダッシュボードの[Backup]欄の方でもバックアップを確認することができます。 スナップショット・バックアップのライフサイクル スナップショットの作成・削除、バックアップ作成はタイマー設定で自動化することができます。 今回は、以下の条件でスケジュールを設定してみます。 スナップショット 取得:日次AM02:00 保持:直近2日分 バックアップ 取得:日次AM04:00 保持:直近1日分 Longhornのダッシュボード[Volumes]画面より先ほど作成したボリュームを選択します。 [Recurring Jobs Schedule]欄の[Add]ボタンを押下し、以下のようにスケジュールを設定します。 snapshot: Retain ※2 : 3 cron: UTC17:00 ※3 (02:00 JST) every day snapshot-delete: Retain: 2 cron: UTC17:00(02:00 JST) every day backup cron: UTC19:00(04:00 JST) every day ※2: Retainはスナップショットやバックアップをいくつまで残すかを設定するパラメータです。snapshotのretain 3により直近3つまでスナップショットを保持します。snapshot-deleteのretain 2の設定により、3つ以上前のスナップショットは削除され、最終的にはスナップショットは2日分残るようになります。 ※3: LonghornのタイムゾーンはUTCで設定されています。 指定した時刻(02:00AM)になると、直近2つ分のスナップショットが作成されます。 04:00AMになると、バックアップが1つ作成されます。 翌日以降のスナップショット、バックアップは最新のデータに上書きされます。 このように、スケジューリング機能を活用することで、効率的に保存データのライフスタイル管理を実現することができます。 例えば、スナップショットは日次で保存しつつ、長期保管用のバックアップは週次で保存するなどの設定をすることで、ストレージの利用効率を向上させることができます。 Rancherモニタリング機能との連携 RancherからLonghornを導入することで、Longhornが出力するメトリクスを利用したストレージ利用状況の可視化や異常検知を簡単に設定することができます。 まずは、LonghornのメトリクスをPrometheusが受信できるよう、Longhorn用のService Monitorを作成します。 Rancherダッシュボードのサイドバーから[Monitoring]→[Serivce Monitor]画面へ遷移し、Createボタンを押下してService Monitorの新規作成画面へ移動します。 Service Monitorを作成するテンプレートは以下の通りです。今回は 公式ドキュメントに記載されているテンプレート をそのまま利用しています。 apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata:   name: longhorn-prometheus-servicemonitor   namespace: longhorn-system   labels:     name: longhorn-prometheus-servicemonitor spec:   selector:     matchLabels:       app: longhorn-manager   namespaceSelector:     matchNames:     - longhorn-system   endpoints:   - port: manager Service Monitorの作成が完了したら、Longhornから出力されたメトリクスを受信できているかGrafanaで確認します。 公式ドキュメントで紹介されているダッシュボード をインポートして、Grafana上でストレージの利用状況を可視化してみます。 上記Longhorn Example v1.1.0のページからJSONをダウンロードします。 Grafanaのサイドバーから[Dashboards]を選択し、画面右上の[New]タブから[Import]を選択します。  Import Dashboard画面にて[Upload dashboard JSON file]を選択し、先ほどダウンロードしたダッシュボード設定のJSONをアップロードします。  Import dashboard画面より、「Options」-「prometheus」からPrometheusを選択します。 Importを押下し、Longhorn用のダッシュボードを作成します。 ダッシュボード一覧画面からインポートしたダッシュボードを選択します。  これで、ストレージの利用状況や死活監視が可能になりました。 Longhornから取得できるメトリクスの一覧は公式ドキュメントの Longhorn Metrics for Monitoring ページにて確認することができます。 まとめ 本記事では、Rancherを利用したLonghornの導入方法から、ストレージ管理の基本操作、そしてモニタリングの統合までを解説しました。 RancherとLonghornを連携することで、以下のようなメリットが得られます: 効率的なストレージ管理:GUIベースでの操作により、複雑な設定が簡単に。 高度なデータ保護:スナップショットやバックアップを利用した多層的な保護機能。 運用の可視化:Rancherモニタリングとの統合により、ストレージの状態やパフォーマンスをリアルタイムで監視可能。 特に、マルチクラスター環境でのストレージ管理や災害復旧の運用において、RancherとLonghornの組み合わせは大きな効果を発揮します。 本記事を参考に、ぜひRancherとLonghornを活用して効率的なストレージ管理を実現していただければ幸いです。 参考 Longhorn公式ドキュメント ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Rancher入門:RancherとLonghornを活用したストレージ運用の始め方 first appeared on SIOS Tech. Lab .
ども!年末年始の検証結果を順次ブログ化している龍ちゃんです。思いついたらすぐ検証という流れで、いろんなことを検証していたので10本ぐらいはブログのネタに困らなさそうですね。反動が今から恐ろしいです。 今回の内容は、以下の二つになります。 ローカル開発環境をDevContainerで作成する(nest.js + postgres) nest.jsからPrismaを使用してpostgresに接続する 今回のゴールとしては、nest.js内でORMとしてPrismaを使用してpostgresへ接続して動作確認となります。 環境構築 前提条件としては、DockerとDocker Composeコマンドが使えれば大丈夫です。最終的なファイルとしては、こちらのリポジトリに上がっています。まだ、絶賛開発に使用する前段階なので、何の整備もしていなくてすいません。 参考 GitHub – Ryunosuke-Tanaka-sti/2025-nest-backend GitHub ディレクトリ構成としては、以下のようになります。postgresのデータの永続化は、ボリュームマウントとしてコンテナ内へ収めておきます。 . ├── .devcontainer │   └── devcontainer.json ├── .dockerignore ├── .env # docker用環境変数 ├── Dockerfile ├── docker-compose.yml └── nest-app # nest.jsアプリ nest.jsの環境づくり node_modulesをvolumeマウント化 postgresの環境づくり nest.jsの環境づくり まずは、nest.jsの環境を作っていこうと思います。先にNodeの環境を作成して、DevContainerでいきなりアクセスしてしまいます。 三つのファイルを作成します。 Dockerfile ARG NODE_VER FROM node:${NODE_VER} as base RUN npm install -g npm@11.0.0 FROM base as dev RUN npm i -g @nestjs/cli USER node WORKDIR /home/node/app docker-compose.yml version: "3.7" services: nest: build: args: - NODE_VER=22.12.0 context: . dockerfile: Dockerfile target: dev tty: true volumes: - type: bind source: . target: /home/node/app .devcontainer/.devcontainer.json { "name": "procject-backend", "dockerComposeFile": ["../docker-compose.yml"], "service": "nest", "workspaceFolder": "/home/node/app", "customizations": { "vscode": { "extensions": [], "settings": {} } }, "remoteUser": "node", } DevContainer内に入ったら、next.jsのプロジェクトを作成します。Dockerfile内でnest CLIをインストールしているので、実行可能かを試してみます。 nest --version バージョンが帰ってきたら、成功しているのでそのままアプリを作っていきましょう。今回のアプリ名は nest-app で作成していきます。 nest new nest-app これで環境作成は完了です。 node_modulesをvolumeマウント化 node_modulesをvolumeマウントにすることで、コンテナ内に収めてしまいます。こちらの対応をしておくことで、元ファイル側のサイズを小さくすることができます。また、bindマウントしないことによりビルド速度を上昇させることができます。 こちらでやっていることは、「.dockerignoreでビルド時にnode_modulesをなかったことにして、Dockerfileでnode_modulesを作成してビルド、dokcer-compose.ymlでnode_modulesをボリュームマウントする」となります。Dockerファイルで作成している理由としては、dokcer-compose.ymlファイル経由で作成させるとroot権限で作成されて、詰まったいい思い出があります。 追加で、 .dockerignore ファイルを作成してください。 .dockerignore nest-app/node_modules Dockerfile ARG NODE_VER FROM node:${NODE_VER} as base RUN npm install -g npm@11.0.0 FROM base as dev RUN npm i -g @nestjs/cli USER node WORKDIR /home/node/app RUN mkdir nest-app COPY --chown=node:node nest-app nest-app WORKDIR /home/node/app/nest-app RUN mkdir node_modules RUN chown node:node node_modules RUN npm install docker-compose.yml version: "3.7" services: nest: build: args: - NODE_VER=22.12.0 context: . dockerfile: Dockerfile target: dev tty: true volumes: - type: bind source: . target: /home/node/app - type: volume source: node_modules target: /home/node/app/nest-app/node_modules volumes: node_modules: ファイルの適応が完了したら、**DevContainerを必ずRebuildしてください。**以上で、node_modulesのボリュームマウント化完了です。 next.jsのアプリを作成した際に発生したnpm installで持ってきたnode_modulesが元ファイルに残っていると思います。気になる方は、コンテナから抜け出して、お好きなように処理してください。 postgres環境づくり postgresの環境は、16.6とバージョンを指定して作成していきます。公式のリファレンスを置いておきます。 参考 https://hub.docker.com/_/postgres 環境変数を渡すので、まずは.envファイルを作成しておきます。本当のファイルの場合は、もっとちゃんとしたセキュリティにしてくださいね。 .env POSTGRES_USER=user POSTGRES_PASSWORD=password docker-compose.yml version: "3.7" services: nest: build: args: - NODE_VER=22.12.0 context: . dockerfile: Dockerfile target: dev tty: true volumes: - type: bind source: . target: /home/node/app - type: volume source: node_modules target: /home/node/app/nest-app/node_modules depends_on: - postgresql postgresql: env_file: ".env" container_name: postgresql image: postgres:16.6 ports: - 5432:5432 volumes: - type: volume source: postgres_data target: /var/lib/postgresql/data environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_INITDB_ARGS: "--encoding=UTF-8" TZ: "Asia/Tokyo" hostname: postgres restart: always volumes: node_modules: postgres_data: nest.jsのアプリは、postgresの環境が立ち上がった後に立ち上がるように設定しておきます。あくまで開発環境なので、厳密である必要はないのですが、DB前提のシステムを組むと想定されるので念のためですね。 ここまでコピペできたら、**DevContainerを必ずRebuildしてください。**実行中のDevContainerからpostgresの環境にアクセスして以下のコマンドを実行してください。 psql -h postgres -U user 無事アクセスすることができれば、問題なく動作していると考えられます。 Prisma接続 参考 Documentation | NestJS – A progressive Node.js framework Documentation | NestJS – A progressive Node.js framework Prismaのセットアップは公式のドキュメントが十分なので、今回作成したpostgresへアクセスするまでについて共有していきます。これから、Prsimaの勉強もしていくので、まとまったらブログ書きます。 npm install prisma --save-dev npx prisma init # prismaセットアップ .envファイルが作成される npm i --save @nestjs/config # nest.jsのコンフィグ読み込み用ライブラリ nest-app/src/app.module.ts import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { AppController } from './app.controller'; import { AppService } from './app.service'; @Module({ imports: [ConfigModule.forRoot({isGlobal:true})], controllers: [AppController], providers: [AppService], }) export class AppModule {} Globalで環境変数を読めるようにglobalで読み込んでおきます。これで、Prismaが作成した.envファイルも読み込むことができます。 スキーマファイルに定義を記入します。 nest-app/prisma/schema.prisma // This is your Prisma schema file, // learn more about it in the docs: <https://pris.ly/d/prisma-schema> // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? // Try Prisma Accelerate: <https://pris.ly/cli/accelerate-init> generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id Int @default(autoincrement()) @id email String @unique name String? posts Post[] } model Post { id Int @default(autoincrement()) @id title String content String? published Boolean? @default(false) author User? @relation(fields: [authorId], references: [id]) authorId Int? } ここまでしてやっとで .env ファイルの接続情報変更します。今回の環境では、以下に更新することで接続することができます。 DATABASE_URL="postgresql://user:password@postgres:5432/mydb?schema=public" // DATABASE_URL="postgresql://{ユーザー}:{パスワード}@{ホスト名}:5432/mydb?schema=public" 以下のコマンドで先ほどコピペした内容がDBに反映されます。 npx prisma migrate dev --name init コマンドが無事完了したら、以下のコマンドでprisma studioを立ち上げて反映できているか確認します。 npx prisma studio かっこいい画面が立ち上がり、PostとUserが作成されていれば無事にアクセスできています。 prisma studio画面 おわり 今回の閑居構築で特に詰まった点としては、postgresの環境にアクセスできるまで長かったのでpostgresの環境作成が正しいのか試すことができなかった点ですね。postgresのイメージのドキュメントをちゃんと読みに行きましたね。今回は、DevContainerのVolumeを何度も消し飛ばして検証しました。開発体験としては、Git上でローカルのデータを管理しない想定なのでバインドはしたくありませんでした。PrismaのSeedなどを作りこんでおけば問題ないかと思います。 では2025年もよろしくお願いします。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【NestJS + PostgreSQL + Prisma】DevContainerで作る快適な開発環境 first appeared on SIOS Tech. Lab .
ども!年末年始を抜けてぬるっと仕事に復帰を始めている龍ちゃんです。年末年始は、今まで溜まっていた検証+お部屋を片付けていたので、意外と充実していました。去年は、GitHub AcitonsとDevContainerでの環境構築作成をしっかりやっていた時期かなと振り返り、またブログにまとめていきます。 今回の内容は、以下の二つになります。 Azure Static Web Apps上でNext.jsの静的サイト配信を行う(GitHub Actionsを用いたデプロイ) ローカルの開発環境をDevContainerで作成する(SWA CLIを活用してローカルでプレビュー) ローカル環境でStatic Web Apps(以降:SWA)の挙動を確認することができる、SWA CLIを開発環境にインストールします。今回のゴールとしては、SWA CLI動作確認とSWAへのNext.js静的サイトを公開になります。 SWA CLIとは 参考 Azure Static Web Apps CLI について Azureさん公式のエミュレーターになります。SWAの挙動をローカルで再現してくれます。開発時に特にうれしい機能として以下の二つかなと思います。 SWA上に構築済みの認証をモックで試すことができる APIエンドポイントへのプロキシ SWA上に構築済みの認証をモックで試すことができる 参考 Azure Static Web Apps の認証と認可 SWAでは、コンフィグファイルに追記するだけで色々な認証プロバイダー(Google / X / Apple / etc…)から認証を受けることができます。SWA上で構築済みのサービスになるので、ローカルで認証を試すことができません。そこで、ダミーのモックで認証を通った状況を作ることができます。認証後の情報取得方法もローカルで動作確認できるので、非常に助かります。 APIエンドポイントへのプロキシ 参考 Azure Static Web Apps での API サポートの概要 SWAでは、Azure Function(Freeプランでも可)やWeb Apps(Standardプランのみ可)で作成したAPIを /api ルートと接続することができます。こちらも、SWA上で構築済みのサービスなのでローカルの開発では、SWAのプロキシを再現する必要があります(まぁ無理よな)。SWA CLIを使うことで、そのあたりの挙動も模倣して提供してもらえます。 開発環境構築 環境構築の内容に入っていきます。前提条件としては、DockerとDocker Composeコマンドが使えれば大丈夫です。最終的なファイルとしては、こちらのリポジトリに上がっています。 参考 GitHub – Ryunosuke-Tanaka-sti/2025-next-static-site GitHub ディレクトリ構成としては、以下のようになります。 . ├── .devcontainer │   └── devcontainer.json # DevContainer環境設定 ├── .dockerignore ├── .env            # local用環境ファイル ├── .github │   └── workflows ├── Dockerfile ├── docker-compose.yml ├── my-app # Next.jsアプリディレクトリ └── swa-cli.config.json # SWA CLIコンフィグファイル Next.jsの環境づくり node_modulesをvolumeマウント化 SWA CLIのセットアップ (SWA CLI Config) 静的サイト用対応( staticwebapp.config / Next.js config) Next.jsの環境づくり まずは、Next.jsの環境を作っていこうと思います。ここでは、Nodeの環境を作成してDevContainerでいきなりアクセスしてしまいます。三つのファイルを作成してください。 Dockerfile ARG NODE_VER FROM node:${NODE_VER} RUN npm install -g npm@11.0.0 RUN npm install -g @azure/static-web-apps-cli USER node WORKDIR /home/node/app docker-compose.yml version: "3.7" services: next: build: args: - NODE_VER=22.12.0 context: . dockerfile: Dockerfile tty: true volumes: - type: bind source: ./ target: /home/node/app .devcontainer/.devcontainer.json { "name": "procject-dev", "dockerComposeFile": ["../docker-compose.yml"], "service": "next", "workspaceFolder": "/home/node/app", "customizations": { "vscode": { "extensions": [], "settings": {} } }, "remoteUser": "node", } DevContainer内に入ったら、Next.jsのプロジェクトを作成します。今回は、create-next-appから提案されるmy-appで話を進めていきます。ここは任意に変更してください。 npx create-next-app@latest これで、Next.jsの環境が作成することができました。 node_modulesをvolumeマウント化 node_modulesをvolumeマウントにすることで、コンテナ内に収めてしまいます。こちらの対応をしておくことで、元ファイル側のサイズを小さくすることができます。また、bindマウントしないことによりビルド速度を上昇させることができます。 こちらでやっていることは、「.dockerignoreでビルド時にnode_modulesをなかったことにして、Dockerfileでnode_modulesを作成してビルド、dokcer-compose.ymlでnode_modulesをボリュームマウントする」となります。Dockerファイルで作成している理由としては、dokcer-compose.ymlファイル経由で作成させるとroot権限で作成されて涙を呑むからですね。 追加で、 .dockerignore ファイルを作成してください。 .dockerignore my-app/node_modules Dockerfile ARG NODE_VER FROM node:${NODE_VER} RUN npm install -g npm@11.0.0 RUN npm install -g @azure/static-web-apps-cli USER node WORKDIR /home/node/app RUN mkdir my-app COPY --chown=node:node my-app my-app WORKDIR /home/node/app/my-app RUN mkdir node_modules RUN chown node:node -R node_modules RUN npm install docker-compose.yml version: "3.7" services: next: build: args: - NODE_VER=22.12.0 context: . dockerfile: Dockerfile tty: true volumes: - type: bind source: ./ target: /home/node/app - type: volume source: node_modules target: /home/node/app/my-app/node_modules volumes: node_modules: ファイルの適応が完了したら、**DevContainerを必ずRebuildしてください。**以上で、node_modulesのボリュームマウント化完了です。 Next.jsのアプリを作成した際に発生したnpm installで持ってきたnode_modulesが元ファイルに残っていると思います。気になる方は、コンテナから抜け出して、元ファイルのnode_modulesを削除してください。 SWA CLIのセットアップ SWA CLI自体は、Dockerfileのnpm globalインストールですでに導入済みです。コンテナ内で以下のコマンドで確認してください。 swa --version バージョン情報が返ってくれば、インストール自体は完了しています。また、以下のコマンドで対話的にコンフィグファイルを作成することができます。 swa init フレームワーク等も自動で判断して作成してもらえるので非常に便利なコマンドです。 対話的に作成しなくても、以下のファイルをコピペしてもらえれば起動することができます。 swa-cli.config.json { "$schema": "<https://aka.ms/azure/static-web-apps-cli/schema>", "configurations": { "app": { "appLocation": "my-app", "run": "npm run dev", "appDevserverUrl": "<http://localhost:3000>" } } } 上記のファイルを作成したディレクトリで、以下のコマンド一つでエミュレーター起動までやってもらえます。 swa start 特に設定していなければ、 http://localhost:4280 で起動しているかと思います。 静的サイト用対応 最後に、Next.jsとSWAそれぞれで静的サイト用の設定を追加していきます。Next.jsのコンフィグファイルを編集して静的エクスポートを有効にします。 my-app/next-config.ts import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ output: "export", trailingSlash: true, }; export default nextConfig; こちらの設定を有効にするだけで、 npm run build で out ファイル内に静的ファイルが生成されます。 trailingSlash: true では、静的ファイルのエクスポート時に ディレクトリ/index.html という形式でファイルを読み込んでくれます。 次に、SWA側の設定ファイルです。こちらは、ローカル用と本番用の二つのファイルを用意します。ローカルでは、コンフィグファイルを指定して起動します。本番用のファイルは npm run build で静的ファイルにそのまま書き込まれるように、Next.jsの public ディレクトリに作成します。今回は、Next.jsの設定に併せて trailingSlash の設定のみ追加します。 ローカル: .env/local/staticwebapp.config.json 本番用: my-app/public/staticwebapp.config.json { "trailingSlash": "auto" } 最後に、SWA CLIのコンフィグファイルにローカルの staticwebapp.config.json の位置を教えます。 { "$schema": "<https://aka.ms/azure/static-web-apps-cli/schema>", "configurations": { "app": { "appLocation": "my-app", "run": "npm run dev", "appDevserverUrl": "<http://localhost:3000>", "swaConfigLocation": ".env/local" } } } これで swa start では、 .env/local 内のコンフィグファイルが読み込まれて実行します。 SWA deploy with GitHub Actions 事前準備としては、Azure Static Web AppsのデプロイトークンをGitHubのSecretとして保存しておく必要があります。今回は、環境 deploy内に AZURE_TOKEN として保存しているとして進めていきます。開発環境はDockerで作成しましたが、デプロイ自体はActions内でビルドしてデプロイをしています。 on: push: branches: ["main"] workflow_dispatch: env: NODE_VERSION: "22.x" permissions: contents: read jobs: build: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: "npm" cache-dependency-path: my-app/package-lock.json - name: npm install, build, and test run: | npm install npm run build working-directory: my-app - name: Upload Cache app uses: actions/cache/save@v4 with: path: my-app/out/ key: ${{ runner.os }}-app-${{ github.sha }} deploy: runs-on: ubuntu-24.04 needs: build environment: deploy steps: - uses: actions/checkout@v4 - name: Download Cache app uses: actions/cache/restore@v4 with: path: my-app/out/ key: ${{ runner.os }}-app-${{ github.sha }} - name: Build And Deploy uses: Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }} action: "upload" output_location: "my-app/out/" app_location: "my-app/out/" skip_app_build: true 内容としてはシンプルで、以下の順序で実行されます。 ビルド アプリケーションのビルド 静的ファイルoutをキャッシュ デプロイ キャッシュからアプリを復元 静的ファイルをSWAに配信 Azure/static-web-apps-deploy はビルドもアクションの中で実行してくれる優れものです。ですが、今回はCI上でビルドを実行して、デプロイのためだけに使用しています。 終わり 今回の開発環境構築で特に詰まった点としては、Next.jsで静的ファイルを配信するための設定周りですね。Next.jsとSWAでそれぞれ設定が必要な点は盲点でした。 最近は、フロントエンドの開発環境構築はCI/CDパイプラインを組むところまでやるべきなのかもしれないと思ってきましたね。次回は、バックエンドの環境構築について記載を進めておきます。Azure上で公開するための開発環境構築なんて必要そうですね。三日間ぐらいDockerと戯れすぎて、yamlファイルアレルギーになりそうです。 では2025年もよろしくお願いします。これは1月中に毎回言っておきます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure Static Web Appsで始めるNext.js静的サイト開発 with DevContainer first appeared on SIOS Tech. Lab .
こんにちは、サイオステクノロジーの佐藤 陽です。 先日、MSの資格である Azure Cosmos DB Developer Specialty に認定されました! 久々にMSの試験を受けて合格したので、また合格体験記の方を書いていきたいと思います。 以前の合格体験記はこちら。 【合格体験記】Azure Solutions Architect Expert【AZ-305】 【合格体験記】Azure DevOps Engineer Expert【AZ-400】 はじめに 以前の合格体験記の際にも書いたのですが 試験の内容に関して詳細に記載すると Microsoft から怒られてしまい、資格剥奪されてしまうとのことです。 そのため非常にふんわりした内容の記事になりますが、その点ご了承ください。 Cosmos DB Developer Specialtyとは Azureが提供するNoSQL型のデータベースであるCosmosDBに特化した資格になります。 これまで受けてきた資格(Solutions Architect Expertや、Developer Associate等)は複数のリソースに関する問題が出題されていましたが 今回認定を受けたCosmos DB Developer Specialtyに関しては、CosmosDBのみが対象となっています。 さらに言えばCosmosDBの中でもCore SQLのアカウントだけが対象となっています。 そしてこの認定を受けるためには、DP-420の試験に合格する必要があります。 受験理由 なぜDP-420の受験をしたかに関してですが NoSQLを用いたアプリケーション開発経験が無く、体系的な知識を身につけたかった 最近AI関連でも多くCosmosDBなどが取り上げられるようになった の2点があります。 アプリケーション開発 これまでWebアプリの開発色々と行ってきましたが、いずれもRDBを用いたものが多かったです。 NoSQLに関して何となくの知識はありましたが、実際に手を動かして作業できるレベルではありませんでした。 しかし今後の開発において、NoSQLを使ったアプリ開発を行うことは容易に想像できました。 色々と変化が激しい世の中において、非構造化データを扱う場面が多くなることは間違いないと思ったのです。 もちろんRDBを使う場面も多くあるかと思うのですが、どちらを採用するべきかを判断するためにも NoSQLであるCosmosDBの知識をしっかり身に着けておきたいと思いました。 受験してみて実際どうだったかというと、体系的な知識を身に着けることが出来ました。 パーティション分割の考え方や、マルチリージョンの考え方など、基本的な知識を得ることが出来ました。 まだまだアプリにとって最適な設計が出来るか…と言われたら知識が不足しているとは思いますが 有識者から指摘やアドバイスなどが頂けたら、その内容を理解して自ら反映することはできるくらいにはなったかなと思います。 AI関連 CosmosDBはAIを活用したアプリケーション活用にも多く使われるようになりました。 最近ではベクトル検索や、ハイブリット検索の機能も追加され、RAGの構築などでも採用されるケースが多く見られます。 そのため、今後より活用が進んで行くだろうと思い、このあたりの知識習得をしていきたいと思いました。 こちらに関して実際どうだったかというと、AI関連の機能に関しては現段階(2025年1月)においては試験の対象外となっており、試験においても全く出題されませんでした。 一応公式ドキュメントなどは一通り読んで知識は入れましたが、資格取得を通して…といった意味ではあまり効果はありませんでした。 このあたりはまた独自でキャッチアップの方を進めていきたいと思います。 試験概要 先程も述べたように、CosmosDB特化型の試験です。 CosmosDBの構造の理解から、設計ポイント、実装時のコードレベルの問題まで幅広く出題されます。 特に パーティション分割 インデックスの構築 マルチリージョンに対する読み書き といった所は 基本的な部分であり、なおかつ最重要事項 であると思いました。 逆にここら辺の本質を理解していれば、あとはこれらの知識脳応用で乗り切れるんじゃないかなという印象です。 あとは他サービスとの連携部分で、SynapseやData Factoryとの連携部分も問われることが多かったです。 ここら辺の他サービスの知識がなかったので、自分は知識を詰め込んで乗り切りました…。 学習コンテンツ MS公式 鉄板ですが、MSの公式ドキュメントです。 結構分かりづらいことが言われているMS公式でしたが、今回のCosmosDBに関していえば割とスラスラ読めました。 あとは今回の試験がCosmosDBだけであるため、読む場所が一か所にまとまってるのもいいですね。 ただ、公式ドキュメントとMSLearnに知識が散らばっていたので、双方を照らし合わせながら読むと理解が捗るかと思います。 公式ドキュメント MS Learn Udemy Udemyにも本試験に特化した講座がありました。 Microsoft Azure Cosmos DB Exam Guide [Hands-on] こちらのUdemyの講座ですが、非常に分かりやすかったです。 ほとんど知識がない自分が一通り見たことで、CosmosDBの基本的な部分は理解できました。 ただ、一方で試験を合格するには内容が不足しています。 そのため、このUdemyの講座を一通り見て概要をつかんだ後、公式ドキュメントなどを利用して知識を補完する必要があるかと思います。 その他 試験勉強しているときに、ちょうどMS Ignite2024が開催されました。 そして、Igniteの中で以下のセッションがありました。 Cosmic efficiency: mastering performance and cost in Azure Cosmos DB | BRK194 Igniteなので非常に高いレベルの内容なのかな?と思われますが、とても基本的なところから説明してくれています。 どれくらい易しいかというとRU(Request Unit)とは何か?といった所から紹介してくれています。 CosmosDBを絶賛勉強中だった自分にはとてもタイムリーで、非常に刺さる内容でした。 あとはこちらの御成門プログラマーさんの 記事 でも紹介されていた 畠山さんのCosomosDBの セッション も非常に分かりやすかったのでお勧めです。 おわりに 今回はCosmos DB Developer Specialtyの合格体験記を書きました。 今回の受験を通してCosmos DBの基礎部分が分かったとともに、設計の難しさ・面白さが垣間見えた気がします。 これからアプリを開発していくうえで、ベストなCosmosDBの設計が出来るよう引き続きキャッチアップを続けていきたいと思います。 アウトプット 勉強していく過程でアウトプットした記事になります。 まだ途中までしか書けていないので引き続き書いていきたいと思います。 【Azure】CosmosDBにおけるRU入門ガイド【初心者向け】 https://tech-lab.sios.jp/archives/44424 【Azure】CosmosDBにおけるパーティション入門ガイド【初心者向け】 https://tech-lab.sios.jp/archives/44573 【Azure】CosmosDBにおけるインデックス入門ガイド【初心者向け】 https://tech-lab.sios.jp/archives/45014 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【合格体験記】Azure Cosmos DB Developer Specialty【DP-420】 first appeared on SIOS Tech. Lab .
結論 AWS EC2 t4g.small インスタンスは東京リージョンで2025年も引き続き1台分無料で利用できそうです。   説明 昨年の記事 にも書いたのですが、AWSで提供されている「 AWS 無料利用枠 」には記載がないものの、 EC2では t4g.small インスタンスが無料で利用できます (t4gの「g」はGravitonのgです、 Armアーキテクチャ ですね。ここが「a」ならAMD、「i」ならIntelで2つとも 64ビットAMD/Intelアーキテクチャ )。 英語版 Amazon EC2 FAQ を見てみると Q: How can customers get access to the T4g free trial? Until December 31, 2025, all AWS customers will be enrolled automatically in the T4g free trial as detailed in the AWS Free Tier. 昨年同様に 1年間延長 されてます(日本語版FAQは遅れて更新されると思われます)。 ということで2025年もt4g.smallインスタンスが1台無償で使える(1ヶ月720時間分)ので、是非利用していきましょう。 余談 これに限らず「ページを英語版に切り替えて」みると日本語版にはなかった情報が!となることはままあります。 世界的に見たら英語・スペイン語・中国語・フランス語あたりが強く、日本という没落していく極東の島国の優先度は低くなる一方ですし、最新情報を確認するときにはURLのロケール表記(例えば en とか en_US とかになってるところ)を確認して切り替え(ja とか ja_JP に変更)を 試みても良いかもしれません。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post AWSの t4g.small EC2インスタンスを2025年も無料で使おう first appeared on SIOS Tech. Lab .
こんにちは。本日は サイオステクノロジーのアドベントカレンダー「サイオス社員が今年一年で新たに学んだ技術」の最終日の内容をお届けします。 今回のテーマ、MCP( Model Context Protocol ) はチャット AI が外部のサービスを操作できるようにするツールです。 今回は Claude Desktop を使った MCP の入門としてファイル操作と Notion ページの編集を試してみました。 Claudeには基本無料で使うことができますが、最新のモデルClaude3.5 Sonnetは比較的すぐに1日の使用量上限に達してしまうため、本格的に使う場合はProfessional Planへのアップグレードをおすすめします。 環境のセットアップ MCP の使用には Claude Desktop と Node.js が必要です。以下のサイトからあらかじめインストールしておきます。なお、この投稿ではWindows環境を前提としております。 Claude Desktop https://claude.ai/download Node.js https://nodejs.org/en/download/prebuilt-installer/current インストールできたら、Claude Desktop の Settings から Developer>Edit Config を選択し、 claude_desktop_config.json を開きます。 このファイルをプラグインのドキュメントを参考に変更します。 { "mcpServers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "ここに操作したいディレクトリの完全パスを入力" ] } } } Claude Desktopを再起動します。(念のため、Claude Desktopは管理者権限で実行されるようにします。) 正常に反映されている場合、Available MCP Toolsの一覧にファイル操作のためのツールが確認できます。 MCPを使ったファイル操作 それではファイル操作を指示してみましょう。今回はあらかじめ「走れメロス」の冒頭部分をテキストファイルとして保存した状態でフォルダの中身を確認させます。途中スクリプトの実行許可を求められますので適宜許可します。 ファイルを認識し、中身まで確認できています。 書き込みについても試してみましょう。花の名前と花言葉の一覧をcsvで作成させます。 フォルダを確認すると、きちんとcsvファイルとして作成されています。 次に、編集をお願いしてみます。1つの花に対して複数の花言葉があるため、それぞれを別のカラムに分けるよう指示します。 期待通りに動作しています。 Notion APIの設定 Notionとの連携を試してみます。NotionのAPI設定がされていない場合は、NotionAPIの設定を行います。 まず、Notionのintegrationページから、新しいインテグレーションを作成します。 インテグレーション名に任意の名前、関連ワークスペースにアクセスしたいワークスペースを選択します。 作成できたら、APIキーをコピーしておきます。 編集したいページにアクセスし、右上の…(3点リーダー)から、接続>作成したコネクションを選択して接続します。 MCPでNotionを編集する ここからは、MCPの設定を行います。 NotionのMCPはリポジトリをローカルにクローンする必要があるため、Githubページの案内通りクローンし、任意のディレクトリにパッケージをインストールします。 git clone https://github.com/v-3/notion-server.git cd .\notion-server npm install ファイル操作の時と同じく、claude_desktop_config.jsonに設定値を追記し、Claude Desktopを再起動します。 { "mcpServers": { "filesystem": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "ここに操作したいディレクトリの完全パスを入力" ] }, "notion": { "command": "node", "args": [ "ここにクローンしたリポジトリ内buildフォルダへの完全パスを入力" ], "env": { "NOTION_API_KEY": "ここにNotionのAPIキーを入力" } } } } Claude Desktopからページ内容を更新してみます。現状はプロンプトでページID(URLの「-」以下の部分)を指定しないと上手く動作しないようです。今回は先ほど作成した花言葉一覧のファイルの変更し、履歴をNotion上に記録するようお願いします。 Notionでページを確認すると、確かに履歴が記録されています。 Claudeから呼び出せるMCPは数多く作られており、一覧はGithub上で公開されています。 https://github.com/modelcontextprotocol/servers MCPによって導入方法は少し異なる場合がありますが、普段お使いのアプリのMCPがあればぜひ試してみてください。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【Claude】MCPでChatAIからのファイル操作を試す first appeared on SIOS Tech. Lab .
サイオステクノロジーの菊地啓哉です。数学の概念の1つである体について書こうと思います。たぶん、数学に慣れている人は記号を駆使して書く方が明確で理解しやすいかとは思いますが、私が慣れていないので、記号での表現はしておりません。 ゼロ知識証明(zk-STARK)や電子署名(ECDSA)などを調べていると、有限体の話とかがしれっと出てきます。それでちょっと気になって中身を勉強しようとしても、本質じゃないところでちょっと躓きそうになったりするので、ふんわりとではありますが有限体の前提となる、体の理解を整理したいと思います。今回は一部の例などを除いて高校生くらいでも理解できるような内容にしているつもりでいます。たぶん。 群論もちゃんと理解していないエンジニアがふんわりと書いているので、ところどころ厳密性に欠ける点があることはご了承ください。 群 体の話に入る前に、前提となる、 群(ぐん、Group) について触れておきます。 群はある要素の集まり(集合;ここでは G と表します)と二項演算(ここでは ○ と表します)が定義されていて、以下の性質を持ちます。 また、特に可換性 a ○ b = b ○ a を持つ場合は、 可換群 と呼びます。 群をざっくり理解するなら、逆元を引き算と考えて、加算減算(足し算引き算)ができる集合です。 群の例 整数の足し算(逆元は (-1)倍した数) 0以外の有理数の掛け算(逆元は逆数) 行列式が0でない2次正方行列の掛け算(逆元は逆行列) n をある自然数として、mod n(nで割ったあまり)の中で足し算(aが0以外の時、aの逆元は (n-a)。位数がnの巡回群と呼ぶ。) xy平面で、{何もしない、x軸反転、y軸反転、原点対称移動}の4つの操作、二項演算は2つの操作を続けて実行することを意味する 群とならない例 整数の掛け算(±1以外の元に逆元が存在しない) 有理数の掛け算(0に逆元が無い) 2次正方行列の掛け算(行列式が0の行列には逆元が無い) n をある自然数として、mod n(nで割ったあまり)の中で掛け算(0に逆元が無い) 3次元ベクトルの内積(演算結果が3次元ベクトルでない) 3次元ベクトルの外積(単位元が無い、結合法則も成り立たない) 体 本題の 体(たい、Field) の話に入ります。まず、体の定義としては、集合 K と2つの二項演算(ここでは 加法 + と 乗法 * で表します。また、ここではそれぞれ足し算、掛け算と表現することもあります。)が以下の性質を持ちます。(一般に、体というと可換体を指すようなので、可換体の定義となっています) 体をざっくり理解するなら、加減乗除の四則演算ができる集合です。 零元 ここで、体における個人的最重要ポイントの零元に関して説明いたします。零元は加法 + の単位元です(定義)。単位元は e ○ a = a ○ e = a を満たす e のことなので、加法 + で考えると、 e + a = a + e = a となる e のこととなります。 K の任意の元 a, b の乗法 * でこの零元の定義と分配法則を考えると a * b = (a + e) * b = a * b + e * b となるので、e * b = e となることがわかります。(左辺と右辺で比較すると、 e * b は加法で値を変化させないことがわかる。これは加法の単位元、つまり零元であるということ。)まとめると、零元 e はその名の通り、普通の数の 0 のように以下の2つの性質を持ちます。 ここまで、乗法 * の単位元を e としてきましたが、単位元は加法 +、乗法 * それぞれに対して存在すること、それぞれが普通の数の 0 と 1 と同じような性質であることから、ここからは特に断りなく加法 + の単位元(零元)を 0、乗法 * の単位元を 1 と表現します。 乗法の逆元 体の定義にある、「K の零元(加法 + の単位元)を除いた全ての元に対して乗法 * に関して逆元を持つ」ですが、これは個人的に体で2番目に重要なポイントです。 K の2つの元 a, b があって、 a * b = 0 となっている時、 a ≠ 0 かつ b ≠ 0 とすると、両辺に左から a の逆元と b の逆元をかけると左辺は、 b -1 * a -1 * a * b = b -1 * b = 1 となり乗法単位元になり、右辺は零元になにをかけても零元のままなので零元となり、等式が成り立ちません。よって、 a, b の少なくともどちらかが零元ということになります。(片方でも零元なら等式が成り立つのは明らかなのでスキップします。本当に等式が成り立っていないのか、つまり零元と乗法単位元は本当に異なる元なのかは考えても良いのですが、それが成り立つのは、元が1つしかない体しかありません。) このことが何を意味するかと言えば、代数方程式を解くということが、因数分解することと同義になります。次の式で最高次の係数は0ではないとします。また、累乗は普通の数の累乗と同様、指数の数だけ底の元を掛け算した値を意味します。 という代数方程式をxについて解きたい時に、次のように因数分解できれば、解がわかります。 つまり、二次方程式などでやってきたことと同じ常識が通じるようになります。興奮しますね。 逆に、逆元が存在しないようなものを考えてみると、例えば2次正方行列は足し算と掛け算について体ではありません。これは上に書いたように、行列式が0の場合には零行列でなくとも逆行列が存在しないためですが、2つの零行列ではない行列の掛け算が零行列になることがあります。このような場合には方程式を解くのが厄介になりそうですが、体であれば厄介なことを考えずに済みます。 おわりに 全く技術的なことには触れずに数学の体について書きました。普通の数みたいなものだなとか、何の役に立つのかわからん、といった感じかもしれないですが、言葉に慣れればそんなに難しくない内容だったのではないかなと思います。続きが書けたら有限体について書きたいと思います。 ということで、今回はここで終わりとなります。 またかきます またね ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 数学に詳しくないエンジニアの体入門 first appeared on SIOS Tech. Lab .
はじめに こんにちはサイオステクノロジーの小野です。これまでOpenShift AIについての記事を多数書いてきたのでそれらのリンクを整理します。 OpenShift AI概要 OpenShift AIについてざっくりとした説明をしています。まずはこちらの記事を見てOpenShift AIとは何なのか知ってください。 Red Hat OpenShift AIとは? OpenShift GPU設定 OpenShift AIとは直接関係ありませんが、OpenShift AIでGPUを利用した機械学習を行う場合には必要な設定になります。 OpenShiftへのノード追加 OpenShiftのマシンセットを設定することでノードを追加する方法について解説しています。この方法を利用してGPUノードを追加します。 OpenShift(AWS)へのノード追加 OpenShiftのGPU設定方法 OpenShiftへGPUノードを追加した後、GPUをOpenShiftに認識させるための設定を解説しています。 OpenShiftのGPU設定の方法について OpenShift AI導入 OpenShift AIのオペレータをダウンロードして、ワークベンチのJupyterLabを開くところまで解説しています。 OpenShift AI を導入してみた OpenShift AIのワークベンチ機能 OpenShift AIのワークベンチ機能を利用して機械学習を行う記事です。ワークベンチ機能についてよくわからないという方はこの記事を読んで何となく理解してください。 OpenShift AIで機械学習をやってみた OpenShift AIのモデルサービング機能 OpenShift AIのモデルサービング機能を利用してモデルAPIを実装する解説をしています。これにより学習したモデルを簡単にAPIとして公開できます。 OpenShift AIのモデルサービング機能について OpenShift AIのデータサイエンスパイプライン機能 OpenShift AIのデータサイエンスパイプライン機能を利用して機械学習のパイプラインを実行する方法を解説しています。パイプラインを利用することで迅速なモデル開発が可能になります。 OpenShift AIのデータサイエンスパイプラインについて パイプラインのスケジュール設定 データサイエンスパイプラインをスケジュール設定する方法を解説しています。この設定をすることで常に最新のモデルAPIを提供することができます。 OpenShift AIのパイプラインにスケジュール設定をしてみた ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OpenShift AIブログまとめ first appeared on SIOS Tech. Lab .
はじめに こんにちはサイオステクノロジーの小野です。 前回 はOpenShift AIのデータサイエンスパイプラインを実装して実行しました。今回はそのパイプラインをスケジュール設定により、定期実行する方法を解説します。MLOpsにおいて重要な設定なので、ぜひ覚えて帰ってください。 パイプラインのスケジュールについて OpenShift AIではパイプラインのスケジュール設定を行うことが可能です。 パイプラインの定期実行により、常に最新のモデルAPIを利用することができます。 パイプラインのスケジュール実装例 モデルのデータ対応 今回は2つの画像データを用いて、スケジュールによるモデルの更新を確認します。 A画像 今まで利用してきたMNIST画像です。今までの記事で作成したモデルはこのA画像に対応したモデルになります。 B画像 A画像の色を反転させた画像です。 今回用いるデータ。A画像は今まで利用してきた画像。B画像はA画像の色を反転させた画像。モデルAPIをA画像に対応したモデルからB画像に対応したモデルに更新する。 A画像に対応しているモデルAPIをパイプラインのスケジュールによる自動更新により、B画像に対応したモデルに更新します。 パイプライン構成図 今回スケジュール設定を行うパイプラインは前回実行したものと同じになります。詳しくは前回の記事を参照して下さい。(URL) その構成図を再掲すると以下になります: パイプラインの構成図 このS3内の画像データをA画像からB画像に変更することで自動的にB画像に対応したモデルが作成され、デプロイされます。 前提条件 OpenShift AI:2.13 以前の記事のモデルサービングを実行済み( OpenShift AIのモデルサービング機能について ) すでにA画像で学習したモデルがデプロイされている状態になります。 以前の記事のパイプラインを実行済み( OpenShift AIのデータサイエンスパイプラインについて ) サービスアカウントの設定を行って、モデルの更新ができる状態にしてください。 今回利用するプログラムは以下のリンクからダウンロードしてください。 MLschedule モデル更新前動作確認 モデル更新前:A画像によるAPIリクエスト実施 JupyterLab上で5_test-api.ipynbを実行してください。なお実行時はコード内のエンドポイントやAPIのトークンのシークレットを各自設定した値に変更してください。 モデル更新前はA画像によって学習が行われたモデルAPIなので、A画像に対して正しい推論が行われていることが確認できます。 モデル更新前のA画像に対する推論結果。推論と実際の結果が一致しているのが確認できる。 以前の記事でデプロイしたモデルはA画像で学習している。したがって、A画像の推論はうまくいく。 モデル更新前:B画像によるAPIリクエスト実施 JupyterLab上で6_test-api-inv.ipynbを実行してください。なお実行時はコード内のエンドポイントやAPIのトークンのシークレットを各自設定した値に変更してください。このプログラムは色が反転したB画像をモデルAPIに送信しています。 モデルAPIはB画像を学習していないので、推論した画像の数字と実際の画像の数字が一致しないことが確認できます。 モデル更新前のB画像に対する推論結果。推論と実際の結果が一致していないのが確認できる。 デプロイされているモデルはA画像でしか学習していないのでB画像の推論はうまくいかない。 スケジュール設定 パイプラインのエクスポート JupyterLab上で4_pipeline-demo.pipelineファイルを開いてください。このパイプラインファイルをKubeflow Pipelines形式のyamlファイルにエクスポートしてください。詳しくは前回の記事を参照してください。(URL) エクスポートしたyamlの一部が以下になります。デフォルトだとパイプラインのキャッシング機能がオンになっていますが、今回構築したパイプラインはキャッシング機能がついていると毎回の実行が省略されてしまいます。スケジュール設定によって毎回実行してほしいのでキャッシング機能をオフにします。そのためにenableCacheをfalseに設定します。 root: dag: tasks: run-a-file: cachingOptions: enableCache: false # enableCacheをfalseに変更する componentRef: name: comp-run-a-file taskInfo: name: 1_dataprocessed run-a-file-2: cachingOptions: enableCache: false # enableCacheをfalseに変更する componentRef: name: comp-run-a-file-2 dependentTasks: - run-a-file taskInfo: name: 2_training run-a-file-3: cachingOptions: enableCache: false # enableCacheをfalseに変更する componentRef: name: comp-run-a-file-3 dependentTasks: - run-a-file-2 taskInfo: name: 3_modeldeploy schemaVersion: 2.1.0 sdkVersion: kfp-2.8.0 パイプラインのインポート 編集したyamlファイルをパイプラインサーバーにインポートします。Import Pipelineを押下して、パイプライン名を記入して、upload fileでyamlファイルをアップロードします。 パイプラインのインポート パイプラインのスケジュール設定 作成したパイプラインのActionsから、Create Scheduleを押下すると、スケジュール設定できます。 Create scheduleを押下するとスケジュール設定が行える スケジュール設定は以下のように設定します: Experiment:Default Name:スケジュール名 Trigger type:Periodic(スケジュール設定する形式を指定) Run every:実行する間隔を指定(今回はモデルの更新を確認するだけなので5分程度の間隔で十分です) Maximum concurrent runs:10(並行して実行するパイプラインの最大数) Start date:オフ(スケジュール実行の始める日にちを指定) End date:オフ(スケジュール実行が終わる日にちを指定) Catch up:オン(スケジュールが遅れた場合、本来のスケジュールに追いつく必要があるかどうかの設定) Pipeline:スケジュール設定するパイプラインを指定 Pipeline version:スケジュール設定するパイプラインのバージョンを指定 スケジュール設定 パイプラインのスケジュール実行 スケジュールを実行するとパイプラインのRunsのSchedulesに追加されます。 パイプラインのスケジュール Runsにスケジュール設定通りの間隔でパイプラインが実行されます。 パイプラインのスケジュール実行。設定した5分間隔でパイプラインが実行されているのが確認できる。 学習元データの変更 B画像アップロード 以上の操作で、スケジュールによってモデルの自動更新がされている状態になります。 ここでモデルの更新がされることを確認します。そこで、パイプライン中で取り扱っているA画像をB画像に上書きします。 JupyterLab上で7_dataupload-inv.ipynbを実行してください。これはパイプラインで取り扱うA画像が保存されている場所にB画像を上書きするプログラムです。 A画像が保存されている場所にB画像を上書きする。 モデル更新後動作確認 モデル更新後:B画像によるAPIリクエスト実施 B画像をアップロードしてからパイプラインが実行されるのを確認したら、もう一度JupyterLab上で6_test-api-inv.ipynbを実行してください。B画像に対応したモデルが自動的にデプロイされているので正しく推論できるようになったことが確認できます。 モデル更新後のB画像に対する推論結果。推論と実際の結果が一致しているのが確認できる。 パイプラインが実行されてB画像を学習したモデルがデプロイされたのでB画像の推論がうまくいく。 モデル更新後:A画像によるAPIリクエスト実施 次にJupyterLab上で5_test-api.ipynbを実行してください。更新後のモデルAPIはB画像でしか学習していないので、A画像に対しては推論ができていないことが確認できます。 モデル更新後のA画像に対する推論結果。推論と実際の結果が一致していないのが確認できる。 B画像でしか学習していないモデルなのでA画像に対してはうまく推論できない。 最後に このようにパイプラインのスケジュール設定を行うと自動的にモデルの更新を行うことができます。MLOpsを実現するにはパイプラインのスケジュール設定が必須だと思います。モデルを常に最新の品質を保つためにもぜひパイプラインを実装して、スケジュール設定してみてください。 参考 データサイエンスパイプラインの設定: https://docs.redhat.com/ja/documentation/red_hat_openshift_ai_self-managed/2.13/html/working_with_data_science_pipelines/index 前回: OpenShift AIのデータサイエンスパイプラインについて ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OpenShift AIのパイプラインにスケジュール設定してみた first appeared on SIOS Tech. Lab .
はじめに こんにちはサイオステクノロジーの小野です。 前回 はOpenShift AIのモデルサービング機能を用いて、推論APIを実装しました。今回はOpenShift AIのもう一つの重要な機能であるデータサイエンスパイプラインを実装します。パイプラインを利用することでモデル開発を高速化することが可能になります。MLOpsの実現には欠かせない機能なので、しっかり実装できるようにしましょう。 データサイエンスパイプラインについて データサイエンスパイプラインとは、機械学習のワークフローを標準化および自動化し、データサイエンスモデルを開発およびデプロイします。 OpenShift AI(バージョン2.9以降)ではKubeflow Pipelines バージョン2.0を利用してパイプラインを実行しています。 Kubeflow Pipelines バージョン2.0はワークフローエンジンとしてArgo Workflowを利用しています。 パイプラインの種類 パイプラインの実装方法は2通りあります。 Elyra ElyraはJupyterLab上でGUIを利用してパイプラインを構築できます。パイプラインで自動化したいソースコードファイルをドラッグ&ドロップし、そのファイル同士に線を引くだけで構築できるので、直感的にパイプラインを構築できます。 パイプラインの実行もJupyterLab上で簡単に実行できるので非常にお手軽です。 また、Kubeflow Pipelines形式にエクスポートすることも可能です。 Kubeflow Pipelines SDK Kubeflow Pipelines SDKはPythonコードを使用して作成したパイプラインをyaml形式にコンパイルするSDKです。 Pythonでパイプラインを構築するので、細かい処理や設定を行うことが可能です。 パイプラインの実装例 今回はパイプラインの設定を行い、データの準備からモデルの更新を行うところまで行うパイプラインを構築します。 MLOpsに対応する部分としては以下のようになります: 今回実装するパイプラインに対応するMLOpsの範囲 今回実装するパイプラインでは、データの準備、モデルの学習、モデルの提供を作成しております。 パイプライン概要 今回実行するパイプラインは以下の構成になっています: 今回実行するパイプラインの構成 1_dataprocessed.ipynb(データの準備) 画像データをS3からダウンロードして、学習がしやすい形に前処理を行います。その後、前処理済み学習データをS3にアップロードします。 2_training.ipynb(モデルの学習) 前処理を行った学習データをS3からダウンロードして、モデルの学習を行います。その後、ONNX形式にモデルをエクスポートし、S3にアップロードします。 3_modeldeploy.ipynb(モデルの提供) モデルサーバーを更新して、モデルの再デプロイを行います。 パイプライン構成図 パイプラインの構成図は以下に示します。 ➀ユーザーのパイプライン実行 ②パイプラインアーティファクトと呼ばれるパイプライン情報をS3に保存 ③パイプラインサーバーのパイプライン実行におけるパイプライン情報取得 ④パイプラインのコードを順次実行 ⑤1_dataprocessed.ipynbにおける画像データの取得 ⑥1_dataprocessed.ipynbによって前処理した学習データのアップロード ⑦2_training.ipynbにおける学習データの取得 ⑧2_training.ipynbによってONNX形式にエクスポートした学習モデルのアップロード ⑨3_modeldeploy.ipynbによるモデルサービングサーバーの更新 ⑩モデルサービングサーバー再起動によるモデル再デプロイ パイプラインの構成図 前提条件 OpenShift AI:2.13 データサイエンスプロジェクト作成済み 以前の記事を参考にしてください( OpenShift AI を導入してみた ) データ接続設定済み 以前の記事を参考にしてください( OpenShift AIで機械学習をやってみた ) モデルデプロイ済み 以前の記事を参考にしてください( OpenShift AIのモデルサービング機能について ) 注意:ワークベンチをすでに作成している場合はパイプラインサーバーを設定した後に作り直してください。 パイプラインサーバー設定 OpenShift AIのデータサイエンスプロジェクト内のPipelineというタブを開いて下さい。Configure pipelineを押下すると設定画面が開きます。S3ストレージの接続設定が行えるので、データ接続と同じ設定にするか新しくS3の接続情報を入力してください。 パイプラインサーバー設定 この接続先にパイプラインのアーティファクトと呼ばれるパイプラインの実行に必要な情報や実行した際の中間成果物が保存されます。 ワークベンチ作成 パイプラインサーバーを作成した後にワークベンチを作成します。作成方法は以前の記事を参考にして下さい。( OpenShift AIで機械学習をやってみた ) サービスアカウント設定 今回構築するパイプラインにはモデルの更新を行う処理が含まれています。したがって、パイプラインを動作させるサービスアカウントに対して、モデルサーバーの設定を行うinferenceserviceというリソースとモデルサーバーのポッドを管理するDeploymentを操作する権限を付与する必要があります。 最初にOpenShiftコンソールからユーザー管理 > Rolesを開きます。Roleの作成を押下し、InferenceserviceとDeploymentを操作する権限があるロールをyamlで作成します: kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: pipeline-modeldeploy-role namespace: test-ai # ロールが属するネームスペースを指定 rules: - verbs: - get - list - watch - create - update - patch - delete apiGroups: - serving.kserve.io resources: - inferenceservices - verbs: - get - list - watch - create - update - patch - delete apiGroups: - apps resources: - deployments ロール設定 次に作成したロールとパイプラインを動作させるサービスアカウントにロールバインディングを設定して連携させます。 ユーザー管理 > RoleBindingsを開いてください。 バインディングの作成を押下して、以下のように設定してください: バインディングタイプ:namespace のロールバインディング (RoleBinding) 名前:ロールバインディングの名前 Namespace:ロールバインディングが属するネームスペース Role名:先ほど作成したロールの名前 サブジェクト:ServiceAccount namespaceの選択:データサイエンスプロジェクト名(サービスアカウントが属するネームスペース) サブジェクトの名前:pipeline-runner-dspa ロールバインディング設定 これでパイプラインでモデルの更新ができるようになりました。 パイプラインの実装 それではいよいよパイプラインを実行します。今回はElyraを用いてパイプラインを構築します。以下のリンクからファイルをダウンロードし、JupyterLabにアップロードしてください。 MLpipeline パイプラインの実行前に0_dataupload.ipynbを実行して、S3内に画像データを保存してください。すでに実行している場合は省略して下さい。 4_pipeline-demo.pipelineファイルを開いてください。1~3のプログラムがつながったパイプラインが確認できます。 Elyraの画面 Elyraの上部メニューのRun pipelineを押下するとパイプラインを実行できます。 Run pipelineによりパイプラインの実行が可能 OpenShiftAIのコンソール画面に移動して、データサイエンスプロジェクトのPipelineのタブを開いてください。 実行したパイプラインと同じファイル名(4_pipeline-demo)のパイプラインが実行されているのが確認できます。 また、パイプライン名の左の を押下するとパイプラインのバージョンを確認できます。 パイプライン一覧 その一番新しいバージョンのメニューをクリックして、View runを押下すると実行中のパイプラインを確認することができます。 メニューからView runより実行中のパイプラインを表示できる 実行されているパイプラインを開くとパイプラインの進行状況を確認できます。 実行中のパイプライン パイプラインの実行がSuccseedになったら実行完了です。 Succseedになったら実行完了 もしエラーが起きてFailedとなったら該当のプログラムをクリックして、ログを確認してみてください。 パイプラインのログ確認 次にパイプラインのアーティファクトが保存されているのを確認します。S3に移動して実行したパイプラインのバージョンと同じ名前のディレクトリが作成されていることが分かります。 パイプラインのアーティファクト保存場所 そのディレクトリの中身を見ると、パイプラインで実行した1~3のプログラムが圧縮されて保存されていたり、htmlにエクスポートされ保存されたりしています。 パイプラインのディレクトリ中身 最後に 今回はデータの取得からモデルの更新を行うパイプラインを実行しました。パイプライン機能をうまく活用して、AIを効率よく開発してみてください。次回はパイプラインのスケジュール設定を解説します。 参考 Kubeflow Pipelines: https://www.kubeflow.org/docs/components/pipelines/ パイプライン設定: https://docs.redhat.com/ja/documentation/red_hat_openshift_ai_self-managed/2.13/html/working_with_data_science_pipelines/index 前回: OpenShift AIのモデルサービング機能について ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OpenShift AIのデータサイエンスパイプラインについて first appeared on SIOS Tech. Lab .
メリークリスマス!12月も終盤ですね。年末向けて仕事を爆速で消化しなければならなく、濃密な最終週を過ごす予定の龍ちゃんです。 実は昨日まで、弊社でもアドベントカレンダーの実施を行っていました。お題は「サイオス社員が今年一年で新しく学んだ技術」というタイトルになります。だいぶ幅広く、いろいろな内容の投稿を募集していました。1201~1224まで見事に切れ目なく投稿が続いたので、宣伝を兼ねて紹介記事を作成していきます。 アドベントカレンダー さすがにそれぞれを読んで紹介するのも大変なので、生成AIをフルに活用して要約してもらっていこうと思います。 【Azure】CosmosDBにおけるRU入門ガイド【初心者向け】 2024-12-01 【Azure】CosmosDBにおけるRU入門ガイド【初心者向け】 Azure Cosmos DBは、Microsoft Azureが提供するグローバル分散型NoSQLデータベースサービスで、高スループットと低レイテンシーが特徴です。重要な概念である「Request Unit(RU)」は、操作に必要なリソースを統一指標化したもので、データサイズや操作の複雑さに応じて消費量が変動します。スループット設定には、需要に応じた自動調整が可能な「AutoScale」モードや固定設定の「Manual」モードがあり、効率的なコスト管理が可能です。また、RU消費を最適化するためには、データモデリングやパーティションキー設計が重要となります。Cosmos DBの柔軟な設定や最適化手法を理解し、運用効率を高める詳細が本記事で解説されています。 Hyperleger Fabric v3.0の解説! 2024-12-02 Hyperleger Fabric v3.0の解説! Hyperledger Fabric v3.0が2024年9月にリリースされ、新機能としてビザンチン障害耐性(BFT)オーダリングサービスが導入されました。これにより、最大でノードの1/3未満が悪意ある行動をしてもシステムが正常に動作します。また、Ed25519暗号アルゴリズムのサポートにより、署名と検証の高速化とセキュリティ強化が図られています。さらに、全ての承認済みチェーンコードのクエリ機能が追加され、管理が容易になりました。これらの新機能により、Fabric v3.0は分散システムの信頼性と性能を向上させています。 【Ragas】日本語テストセットの生成方法のご紹介【v0.2】 2024-12-03 【Ragas】日本語テストセットの生成方法のご紹介【v0.2】 Ragas v0.2では、日本語のテストセットを自動生成する機能が強化されました。これにより、例えば「社内規約」の文書から「有給休暇は何日取れますか?」といった質問と、その回答ペアを自動的に作成できます。従来、英語で出力されていたテストセットも、プロンプトの多言語対応機能を活用することで、日本語での生成が可能となりました。具体的な実装方法やコード例については、公式ドキュメントや関連資料を参照することをおすすめします。 LINE LIFFアプリを開発する際にはまったポイント 2024-12-04 LINE LIFFアプリを開発する際にはまったポイント LINEのLIFFアプリ開発における注意点として、以下の3点が挙げられます。 トークンの取り扱いとユーザー情報の取得 : ユーザー特定のため、フロントエンドで取得したAccessTokenをサーバーに送信し、サーバー側でトークン検証とユーザー情報の取得を行う方法が推奨されています。 LINEからのアクセス確認 LIFFアプリ外からのアクセス時にエラーを防ぐため、 liff.isInClient() 関数を使用して実行環境を判定し、適切な対応を取ることが重要です。 動作確認環境の構築 開発中の動作確認には、手元の環境をHTTPS化する必要があります。その際、ngrokなどのツールを活用すると効率的です。 これらのポイントを押さえることで、LIFFアプリ開発の効率と品質を向上させることができます。 【Azure】CosmosDBにおけるパーティション入門ガイド【初心者向け】 2024-12-05 【Azure】CosmosDBにおけるパーティション入門ガイド【初心者向け】 Azure Cosmos DBのパーティションは、データの効率的な管理とスケーラビリティに不可欠な概念です。主に「論理パーティション」と「物理パーティション」の2種類が存在します。論理パーティションは、指定したパーティションキーに基づき、コンテナ内のデータを分類する単位で、各パーティションは最大20GBのデータを保持できます。一方、物理パーティションは、Azure Cosmos DBが内部的に管理するリソース単位で、複数の論理パーティションが1つの物理パーティションにマッピングされます。適切なパーティションキーの選択は、データの均等な分散とスループットの最適化に直結し、特定のパーティションに負荷が集中する「ホットパーティション」を避けるためにも重要です。さらに、クエリの効率性にも影響を与え、単一のパーティション内で完結する「インパーティションクエリ」は高速でリソース消費が少なく、複数のパーティションにまたがる「クロスパーティションクエリ」はその逆となります。これらの特性を理解し、適切なパーティションキーを設定することで、Azure Cosmos DBの性能を最大限に引き出すことが可能です。 Tailwindをstyle属性へ変換【mailwind】 2024-12-06 Tailwindをstyle属性へ変換【mailwind】 Tailwind CSSでデザインされたHTMLを、インラインスタイル形式に変換するツール「mailwind」が紹介されています。このツールを使用することで、HTMLメールの作成や、特定の要件に応じたスタイル変換が容易になります。インストールは npm install mailwind で簡単に行え、コマンドラインから入力HTMLファイルを指定して、インラインスタイルのHTMLや外部CSSファイルを生成できます。具体的な使用例として、Tailwindクラスで記述されたHTMLをmailwindで変換し、インラインスタイル形式に変換する手順が示されています。ただし、Tailwindの設定ファイルで指定したGoogle Fontsの情報は自動的にロードされないため、必要に応じてHTML内に直接記載するなどの対応が必要です。このツールを活用することで、HTMLメールのデザインや特定の要件に合わせたスタイル変換が効率的に行えます。 Firestoreの使い方を改めて図解しておく 2024-12-07 Firestoreの使い方を改めて図解しておく Firestoreは、Googleが提供するNoSQLデータベースで、ドキュメント指向のデータ構造を持ち、柔軟なスケーラビリティとリアルタイムのデータ同期を特徴としています。データはコレクションとドキュメントの階層構造で管理され、ドキュメント内にサブコレクションを持つことも可能です。データ取得方法として、ドキュメントIDを指定した単一ドキュメントの取得、コレクション全体の取得、コレクショングループを用いた複数コレクションからの一括取得などがあり、用途に応じて使い分けられます。クエリを使用する際には、特定の条件でのデータ抽出が可能ですが、複雑なクエリやコレクショングループを使用する場合、インデックスの作成が必要となることがあります。また、配列データの取り扱いには制限があり、配列内のオブジェクト検索では完全一致が求められるため、データ設計時に注意が必要です。Firestoreの柔軟性を活かすためには、データ構造の設計やクエリの最適化が重要であり、公式ドキュメントやリファレンスを参照しながら適切な実装を心掛けることが推奨されます。 バージョン管理ツールasdfを使ってみた 2024-12-08 バージョン管理ツールasdfを使ってみた asdfは、複数のプログラミング言語やツールのバージョンを一元管理できるバージョンマネージャーです。従来、各言語ごとに異なるバージョン管理ツールを使用していましたが、asdfを導入することで、これらを統合的に管理できます。Mac環境では、Homebrewを用いて簡単にインストール可能で、プラグインの追加やバージョンの切り替えも直感的に行えます。さらに、プロジェクトごとに異なるバージョンを設定することも可能で、ディレクトリ単位でのバージョン管理が容易です。これにより、複数のプロジェクトを並行して進める際の環境構築や管理が効率化されます。asdfの活用により、開発環境の整備がシンプルになり、作業効率の向上が期待できます。 【Azure Functions】No HTTP triggers found発生時の調査方法 2024-12-09 【Azure Functions】No HTTP triggers found発生時の調査方法 Azure Functionsでデプロイ後に関数が表示されない問題に対処する方法が紹介されています。具体的には、デプロイ時にエラーメッセージが表示されないものの、関数が見当たらない場合、Azureポータルの「問題の診断と解決」機能を活用することが推奨されています。特に、Python関数に関連する例外情報を確認することで、問題の原因を特定できます。記事では、 Client.__init__() 関数に予期しないキーワード引数 'proxies' が渡されたことによるエラーの例が挙げられています。このような診断手法を用いることで、デプロイ時の問題解決に役立てることができます。 【RAG評価手法】評価できないものは改善できない!?体系的に評価指標をご紹介! 2024-12-10 【RAG評価手法】評価できないものは改善できない!?体系的に評価指標をご紹介! 自然言語生成(NLG)の評価は、伝統的なプログラムのテストとは異なり、多様な表現が可能であるため評価が困難です。人手評価や模範回答を用いる自動評価、または記述解答を使わない新方式LLM as a judgeといったアプローチに分かれます。RAGの評価は特に難易度が高く、質問、コンテキスト、回答、模範解答を基に総合的に評価する手法が必要です。主要な評価手段にRAGAsやRAGCheckerがあり、それぞれの指標を使い再現性・忠実性などを測っています。今後の研究と改善で評価手法の成熟が期待されます。 AIエージェントでロボットを制御する【langchain・Streamlit】 2024-12-11 AIエージェントでロボットを制御する【langchain・Streamlit】 生成AIをテーマにしたデモを通じて、AIエージェントによるロボット操作の実装を紹介します。使用した技術にはStreamlit、Qumcum、Langchainが含まれます。システムはユーザーの入力を解釈し、ロボットの動作を制御します。設計はPythonで行い、QumcumをAPI経由で操作。Streamlitはページ制御を担い、接続状況に応じたインターフェースを提供します。課題としては、同期処理によるフリーズやUIの改善点が挙げられ、今後の取り組みが期待されます。このデモはAIエージェントとロボットの新たな可能性を示しました。 Rancher入門:Rancherを用いたKubernetesクラスター管理 2024-12-12 Rancher入門:Rancherを用いたKubernetesクラスター管理 本記事では、Rancherを使用して複数のKubernetesクラスターを管理する方法を解説します。RancherからEKSクラスターを作成し、既存のKubernetesクラスターをRancherにインポートする手順を詳述。Node数の調整やクラスターのアップグレード、ワークロード管理の方法も紹介。特に、Rancherのダッシュボードを活用することで、効率的なクラスター管理が可能となります。次回はRancherのモニタリング機能について探ります。 Apache HTTPとApache Tomcatを連携するWebサーバを構築してみた 2024-12-13 Apache HTTPとApache Tomcatを連携するWebサーバを構築してみた Apache HTTPとApache Tomcatを連携したWebサーバの構築手順を解説します。まず、連携の利点や使用環境を紹介し、次にApache HTTPとTomcat、OpenJDKのインストール手順を詳述。続いて、自己署名証明書の作成やApache HTTPの設定(HTTP、SSL、Virtual Hostなど)、Tomcatの設定を行います。特に、複数のWebサイトを提供するための設定や、管理者用Webアプリの設定もカバー。最後に、サービスの起動とWebサイトの確認を行い、全体の流れをまとめています。構築の参考にどうぞ。 linterとformatterが一つに!?Biome使ってみた 2024-12-14 linterとformatterが一つに!?Biome使ってみた 本記事では、TypeScript界隈で注目されているツール「Biome」を紹介します。Biomeは、LinterとFormatterが一体化したツールで、ESLintやPrettierの代替として利用可能です。Rustで動作しており、高速な静的解析とコード整形を実現します。導入は簡単で、npmコマンドでインストール後、設定ファイルを生成するだけです。使い方もシンプルで、 format や lint コマンドでそれぞれの機能を実行可能。さらに、 check コマンドで両方を同時に行えます。今回の内容は、エンジニアの細川氏によるアドベントカレンダーの記事からの情報です。 ChatGPTでニュアンスを数値化する 2024-12-15 ChatGPTでニュアンスを数値化する 生成AIを活用して日本人が感覚的に理解する「表現」を数値化する方法を紹介しています。特に「喜びの感情」を1~100で評価するプロンプトを用い、シンプルな数値化から、評価基準を追加することで精度を向上させる手法が解説されています。デモとして「泳げ!進め!カツオくん」や「AIエージェントでロボットを制御する」などが挙げられ、生成AIの可能性を示しています。この技術により、多様なアプリケーションが期待されています。 OpenShift AIで機械学習をやってみた 2024-12-16 OpenShift AIで機械学習をやってみた OpenShift AIを使用した機械学習の手法を解説します。まず、データサイエンスプロジェクトを作成し、データ接続を設定します。次に、ワークベンチを構築し、JupyterLabにプログラムをアップロード。具体的には、MNISTデータセットを使用して数字の画像を学習するモデルを作成します。作業はデータ収集、前処理、モデルの学習と進み、最終的に精度を評価します。OpenShift AIは、インフラ目線での学びを提供し、外部ストレージとの接続も可能です。これにより、機械学習の実践が容易になります。 Rancher入門:Rancherを用いたKubernetesクラスターの監視 2024-12-17 Rancher入門:Rancherを用いたKubernetesクラスターの監視 本記事では、Rancherを利用したKubernetesクラスターの監視方法について解説しています。Rancherの標準モニタリング機能を有効化し、PrometheusやGrafanaと連携することで、クラスターのリソース消費状況をダッシュボードで可視化し、アラート通知を設定できます。導入手順としては、まずRancherのダッシュボードからrancher-monitoringアプリケーションをインストールし、その後各クラスターのリソースを監視する方法を紹介。アラート設定では、Slackへの通知も実現可能です。Rancherの一元管理により、複数クラスターの運用負担を軽減できる点が大きなメリットです。 【Azure】CosmosDBにおけるインデックス入門ガイド【初心者向け】 2024-12-18 【Azure】CosmosDBにおけるインデックス入門ガイド【初心者向け】 今回はCosmosDBのインデックス機能についてご紹介します。インデックスはデータベースの検索パフォーマンスを向上させる重要な機能であり、CosmosDBでも自動的にインデックスが作成されます。特に逆インデックスという仕組みが使われ、アイテムが追加されるたびにツリー構造が更新されます。検索時にはインデックスシークやインデックススキャンなどの手法が用いられ、効率的にデータを取得します。適切なフィルター句を選択することで、検索速度の向上やコスト削減が可能です。また、インデックスポリシーを調整することで、さらに最適化が可能です。詳細な設定方法や具体例については公式ドキュメントを参照してください。 SIOS輪読会の取り組み 2024-12-19 SIOS輪読会の取り組み PS/SLの佐々木が、2年間続けている輪読会の取り組みを紹介しています。この輪読会は、エンジニアの基礎スキル向上や共通言語の形成、学習文化の定着を目的としており、週1回の活動を通じて若手エンジニアが書籍を基に議論しながら学んでいます。これにより、心理的安全性も向上し、円滑なコミュニケーションが実現。輪読会は単なる学習に留まらず、技術的な議論を活発にし、組織全体の成長に寄与しています。今後はペアプロなど新たな試みも考えています。 Red Hat OpenShift Serverlessとは? 2024-12-20 Red Hat OpenShift Serverlessとは? サーバーレスアーキテクチャを導入することで、開発者はインフラ管理から解放され、コスト削減とパフォーマンス向上を図れます。Red Hat OpenShift Serverlessは、Knativeを基盤に、アプリケーションの自動デプロイやスケーリングを実現します。これにより、イベント駆動型アプリやマイクロサービスの構築が容易になり、リアルタイムデータ処理が可能です。OpenShift Serverlessはリソースの効率的な使用を促進し、AWS LambdaやKubelessなどの他のサービスと比較して、特定のクラウドプロバイダーへの依存を軽減します。 Azure Pipelinesのタスク「AzureBlob File Copy」をバージョンアップした際にハマった話 2024-12-21 Azure Pipelinesのタスク「AzureBlob File Copy」をバージョンアップした際にハマった話 11月にAzure DevOpsのCDパイプラインでデプロイ時に発生したエラーの解決方法を紹介します。デプロイ中に「Unsupported authentication scheme ‘WorkloadIdentityFederation’」というエラーが発生し、調査の結果、クライアントシークレットの有効期限切れが原因と判明。サービス接続をワークロードIDフェデレーションに切り替え、無事にデプロイが成功しました。しかし、デプロイしたアプリにCSSが適用されず、CDNリソースで404エラーが発生。設定を見直したところ、無事にCSSも適用されました。公式情報が少ない中、同様の問題を抱える方の参考になれば幸いです。 NestJSでJestからVitestに移行してみた 2024-12-22 NestJSでJestからVitestに移行してみた 今回は、JavaScriptおよびTypeScript開発向けのテスト用パッケージ「Vitest」を紹介します。Vitestは、Jestに似た機能を持ちながら、動作が高速で、移行が容易な点が特徴です。特にNestJSプロジェクトでの単体テストでは、体感で3-5倍の速度向上が見られます。また、Viteを使用していないプロジェクトでも利用可能で、ESMOnlyパッケージにも対応しています。一方で、既存のJestテストを完全に移行するのは難しい場合があります。導入方法としては、Jestを削除し、Vitestをインストールする手順が推奨されています。興味がある方はぜひ試してみてください。 Azure OpenAIの音声認識試してみた 2024-12-23 Azure OpenAIの音声認識試してみた サイオステクノロジーの和田拓也氏が、Azure OpenAIを利用して音声認識アプリを作成した手順を紹介します。使用するのはPythonのStreamlitで、ブラウザのマイクから音声を入力し、AIで文字起こしを行います。まず、AzureポータルでOpenAIリソースを作成し、音声認識モデル「whisper」をデプロイします。次に、実装に必要なソースコードをPythonで記述します。動作確認も行い、全体の流れをまとめて解説します。 React 19でuseActionStateで入力フォーム【Typescript】 2024-12-24 React 19でuseActionStateで入力フォーム【Typescript】 React 19の新機能useActionStateを使用して、TypeScriptでの入力フォーム実装方法を解説。フォームのバリデーション、非同期処理の状態管理、エラーハンドリングなど、実践的な実装例を通じて、新しいHookの活用方法を紹介しています。 OpenShift AIのモデルサービング機能について 2024-12-25 OpenShift AIのモデルサービング機能について OpenShift AIのモデルサービング機能について解説しています。モデルサービングとは、学習済みのモデルをデプロイし、APIとして提供する技術です。OpenShift AIでは、以下の2種類のサービングプラットフォームを提供しています。 シングルモデルサービング 大規模モデル向けで、各モデルが独立したサーバー上で動作します。KServeを基盤としており、OpenShift ServerlessやService Meshの設定が必要です。 マルチモデルサービング 小規模から中規模のモデル向けで、複数のモデルを同一サーバー上で管理します。ModelMeshを基盤としており、リソースの効率的な利用が可能です。 記事では、マルチモデルサービングの設定手順や、デプロイしたモデルへのAPIリクエスト方法について具体的に説明しています。これにより、OpenShift AI上でのモデル運用がよりスムーズに行えるようになります。 終わり せっかくなので最終的な総評も生成AIを使ってまとめていきたいと思います。 2024年のSIOS Tech Labアドベントカレンダーでは、以下のような技術的なトピックが投稿されました: 技術分野の分布 クラウドサービス: Azure CosmosDB、Red Hat OpenShift Serverless、Azure OpenAIなど、クラウド関連の記事が多く投稿されました。 開発ツール: NestJSのテストツール(VitestへのJestからの移行)、Azure Pipelinesなど、開発効率化に関する記事が見られました。 組織文化: 輪読会の取り組みなど、技術組織の文化形成に関する記事も含まれています。 特徴的な傾向 実践的な内容: 具体的な実装方法や、実際に遭遇した問題の解決方法など、実務に直結する内容が多く含まれています。 最新技術への注目: Azure OpenAIの音声認識など、最新のAI技術の実践的な活用例が紹介されています。 知識共有の重視: 技術的なトピックだけでなく、組織内での学習文化の醸成についても触れられており、継続的な技術力向上への取り組みが見られます。 全体として、クラウドサービスとAIを中心に、実践的で最新の技術トレンドを反映した投稿が多く、技術組織としての成長と学習を重視する企業文化が垣間見える内容となっています。 アドベントカレンダーの総評 今年は、いろんな社員の協力をいただいてアドベントカレンダーの投稿が間に合いました。この場を借りてお礼を申し上げます。特に月の前半は、同じチームのメンバーの協力が半端なかったです。おかげで12月の投稿数も伸びているので、ブログの運営チームうっはうはです。 これからも時には企画・執筆を継続していきますのでぜひ共有・コメントよろしくお願いします。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 2024年 SIOS Tech Lab アドベントカレンダー first appeared on SIOS Tech. Lab .
はじめに こんにちはサイオステクノロジーの小野です。 前回 はOpenShift AIのワークベンチを設定して、実際に機械学習を行いました。今回はOpenShift AIの主要な機能の一つであるモデルサービング機能を利用して、学習したモデルをAPIとして提供する方法を解説します。 モデルサービングとは モデルサービングとは学習したモデルをモデルサーバーにデプロイすることで、推論APIとしてモデルを提供する機能です。 サービングの種類 OpenShiftAIではマルチモデルサービングとシングルモデルサービングという2つのモデルサービングプラットフォームを提供しています。 シングルモデルサービング LLMなどの大規模モデル用のプラットフォームです。KServeコンポーネントをベースとしているため、OpenShift AIのほかにRed Hat OpenShift ServerlessとRed Hat OpenShift Service Meshのインストールをする必要があります。各モデルが独自のモデルサーバーからデプロイされるため、多くのリソースを必要とする大規模なモデルに適しています。 マルチモデルサービング 小規模および中規模モデル用のプラットフォームです。ModelMeshコンポーネントをベースとしています。同じモデルサーバーに複数のモデルをデプロイできます。 マルチモデルサービング実例 今回はマルチモデルサービングの設定をして、APIのリクエストを送るところまで解説します。マルチモデルサービングの一般的な構成図は以下のようになります: マルチモデルサービングの構成図 モデルデプロイ OpenShiftAIの設定 前提条件 OpenShift AI:2.13 データサイエンスプロジェクト作成済み 以前の記事を参考にしてください( OpenShift AIを導入してみた ) データ接続設定済み 以前の記事を参考にしてください( OpenShift AIで機械学習をやってみた ) 前回の機械学習サンプルプログラム実行済み 以前の記事を参考にしてください( OpenShift AIで機械学習をやってみた ) 実行済みだとS3内にtest-demo/models/mnist_model_latest.onnxというファイルが作成されます。このONNXファイルをデプロイします。 今回デプロイするモデルデータ モデルのデプロイ方法 モデルのデプロイ方法を説明します。データサイエンスプロジェクト内のModelsの項目に移動します。 最初にモデルサーバーの設定を行います。Add model serverを押下して以下の項目を入力してください: Model server name:test-serving Serving runtime:OpenVINO Model Server Number of model server replicas to deploy:1 Model server size:small Accelerator: NVIDIA GPU(GPUがない場合None) Number of accelerators:1(GPUがある場合設定する) Make deployed models available through an external route: (APIを外部公開するかどうかの設定) Require token authentication: (認証トークンを利用するかどうかの設定。外部公開する場合必須。) Service account name:トークン名 モデルサーバー設定 以上を設定することでモデルサーバーを立ち上げることができます。また、Tokensの項目から認証トークンの値が確認できます。 モデルサーバー設定後画面 Tokensを押下すると、認証トークンのシークレットが確認できる deploy modelを押下することでモデルのデプロイ設定を行います: Model name:test-onnx Model framework(name – version):onnx-1 Existing data connection: Name:test-connection(設定したデータ接続名を選択) Path:test-demo/models/mnist_model_latest.onnx モデルデプロイ設定 設定後、statusが になればデプロイ完了です。エンドポイントが表示されるので、以降のAPIリクエストする手順で用いるためメモしておいてください。 モデルデプロイ設定後画面。ここからAPIエンドポイントを確認できる。 APIインターフェース情報 インターフェース情報は以下のようになります: 内部公開用エンドポイント gRPC用エンドポイント:grpc://modelmesh-serving.<プロジェクト名>:8033 rest用エンドポイント:http://modelmesh-serving.<プロジェクト名>:8008 外部公開エンドポイント:https://<モデル名>-<プロジェクト名>.apps.<クラスタードメイン>/v2/models/<モデル名>/infer リクエストヘッダー { "Content-Type": "application/json", "Authorization": "Bearer {トークンのシークレット}" } リクエストボディ { "model_name": {モデル名}, "inputs": [ { "name": {入力名}, "shape": {入力するデータの形}, "datatype": {入力するデータの型}, "data": {リクエストを送るデータの値} } ] } レスポンス {      "model_name": {モデル名},      "model_version": {モデルのバージョン},      "outputs": [           {                "name": {出力名},                "datatype": {出力するデータの型},                "shape": {出力するデータの形},                "data": {レスポンスとして返ってきた推論の値}           }      ] } APIリクエスト手順 前回 のMNISTの画像を識別するモデルを例にAPIリクエストを行います。 curlでAPI情報確認 APIリクエストを送る手順を説明します。以降に説明するコマンドやコードはOpenShift AIワークベンチのJupyterNotebookで実行することを想定します。 リクエストを送る前にcurlでAPI情報を確認します。エンドポイントの末尾/inferを削除したURLに対してcurlコマンドを実施します。 <token> の部分には実際の認証トークンの値を入れてください。 curl -ks https://test-onnx-test-ai.apps.<クラスタードメイン>/v2/models/test-onnx -H 'Authorization: Bearer <token>' | jq curlの結果が以下のようになります。結果からモデルAPIの情報を確認することができます。 {    "name": "test-onnx__isvc-045f6c46c3",    "versions": [       "1"    ],    "platform": "OpenVINO",    "inputs": [       {          "name": "input",          "datatype": "FP32",          "shape": [             "-1",             "1",             "28",             "28"          ]       }    ],    "outputs": [       {          "name": "output",          "datatype": "FP32",          "shape": [             "-1",             "10"          ]       }    ] } PythonコードでAPIリクエスト実行 実際にリクエストを送るPythonコードを以下に示します。 <クラスタードメイン> の部分にはクラスタードメインを入力してください。またはinfer_url変数にモデルAPI設定後に表示されてメモしたエンドポイントを入力してください。 <token> の部分には実際の認証トークンの値を入れてください。 また、APIリクエストに対する構成図は以下のようになります: PythonコードによるAPIリクエスト実行の構成図 import numpy as np import torchvision.transforms as transforms from torchvision.datasets import MNIST from torch.utils.data import DataLoader import matplotlib.pyplot as plt import requests def load_mnist_image(): transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,)) ]) mnist_dataset = MNIST(root='./data', train=False, download=True, transform=transform) dataloader = DataLoader(mnist_dataset, batch_size=5, shuffle=True) images, labels = next(iter(dataloader)) return images.numpy(), labels.numpy() def show_image(image): # 画像を表示 plt.imshow(image.squeeze(), cmap='gray') plt.title("MNIST Image") plt.show() deployed_model_name = "test-onnx" # クラスタードメインの値を入れる infer_endpoint = "https://test-onnx-test-ai.apps.<クラスタードメイン>" infer_url = f"{infer_endpoint}/v2/models/{deployed_model_name}/infer" # トークンの値を入れる token = " " def rest_request(data): # NumPy配列をリストに変換 data_list = data.flatten().tolist() headers = { "Content-Type": "application/json", "Authorization": f"Bearer {token}" } json_data = { "model_name": deployed_model_name, "inputs": [ { "name": "input", "shape": [1, 1, 28, 28], "datatype": "FP32", "data": data_list } ] } response = requests.post(infer_url, headers=headers, json=json_data, verify=True) response.raise_for_status() # HTTPエラーが発生した場合に例外をスロー response_dict = response.json() return response_dict['outputs'][0]['data'] # MNISTデータセットから画像を読み込む data, label = load_mnist_image() for i in range(5): # 画像を表示 show_image(data[i]) # RESTAPIリクエストの送信 result = rest_request(data[i]) predicted_label = np.argmax(result) print("Inference result:", result) print("Predicted label:", predicted_label) print("Actual label:", label[i]) 実行した結果が以下になります。MNIST Imageという画像はAPIにリクエストとして送った画像になります。Inference resultが実際にAPIからレスポンスとして返ってきた結果です。10個の値は画像が0~9のどの数字に該当するかスコアを示しています。例えば0番目の値が最も高い値になっていれば、モデルAPIはリクエストとして受け取った画像を0の可能性が一番高いと推論を行ったことを示しています。Predicted labelはInference resultの中で最も高いスコアの数字です。すなわちモデルAPIがリクエストとして受け取った画像がどの数字であるかを推論したか示しています。Actual labelはリクエストとして送った画像が実際にはどの数字なのかを示しています。Predicted labelとActual labelが一致していればモデルAPIが正しい推論ができていることを示しています。 モデルAPIの推論結果 最後に 以上の手順によりOpenShift AIのモデルサービング設定を行い、推論APIを利用することができました。このようにモデルサービング機能を活用することで、簡単にモデルAPIを提供することが可能になります。このモデルサービング機能をぜひAIアプリケーション開発に役立ててください。 次回はパイプライン機能の解説を行う予定なので、そちらもよろしくお願いします。 参考 モデルサービング設定: https://docs.redhat.com/ja/documentation/red_hat_openshift_ai_self-managed/2.13/html/serving_models/index OpenShift AIチュートリアル不正検出の例: https://docs.redhat.com/ja/documentation/red_hat_openshift_ai_self-managed/2.13/html/openshift_ai_tutorial_-_fraud_detection_example/deploying-and-testing-a-model 前回: OpenShift AIで機械学習をやってみた   ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OpenShift AIのモデルサービング機能について first appeared on SIOS Tech. Lab .
ども!年末の追い込みが激しくどかどか働いている龍ちゃんです。寒すぎて暖房で過ごしていますが、電気代におびえています。 皆さん、 React 19のドキュメント は確認しましたか?12/5についに React 19がstableになりました 。僕はお恥ずかしながら、Next.jsの検証環境を作っていた時にNext15がStableになっていることで気づきました。Stableになったので、そろそろ学ぼうということで新しいHookを学んでいこうと思います。 今回の内容は、「新しいHookである、useActionStateを使ってTypescriptで入力フォームを作る」という内容になります。極力Typescriptで型を付けた状態で紹介していきます。 useActionStateについて確認 まずは、基本の使い方についてまとめていきます。いろいろと利用シーンがあると思います。ざっくりとuseActionStateのうれしいところは以下の点です。 useActionState内で非同期処理の状態を取得することができる ライブラリを使用せずにReactのみでフォームの管理が楽になった ここでは二つの例を紹介していきます。二つの例の違いとしては、Formの入力値の使用の有無になります。 どちらの使い方でも共通しているのは、useStateと同じようにStateを保存することができます。イメージとしては、asyncの処理を連携したState更新処理をForm(入力)とセットで管理することができるHookとなります。 Formの値を使用しないuseActionState 例として、カウントアップを作成します。こちらの内容は、Reactの公式ドキュメントにも記載されています。 useActionStateで 設定がマストな引数 は2つになります。第一引数には action 関数、第二引数には初期値を渡します。 action 関数では、初回の実行時には初期値が渡り、それ以降は前回のaction関数で返答された値が返答されます。型付け行った場合は、 action 関数の戻り値は型と一致する必要があります。 import { useActionState } from "react"; export const FormTest1 = () => { const [count, countAction, isCountPending] = useActionState<number>( async (prevCount: number) => { await new Promise((res) => setTimeout(res, 1000)); console.log(prevCount); return prevCount + 1; }, 0 ); return ( <> <form action={countAction}> <button type="submit" disabled={isCountPending}> カウントアップ </button> <p>{count}</p> </form> </> ); }; 今回のaction関数はuseActionStateの機能を試すために、処理を2秒止めています。useActionStateからの返り値は3つです。 count:Stateの値 初回では初期値が、実行後はaction関数によって更新された値が挿入 countAction:action関数の実行トリガー isCountPending:action実行状態を取得 useActionStateの素晴らしい点としては、ソース中の isCountPending にあります。これまで、useStateやuseRefを組み合わせて作成していた。loading表示などもこちらを使用することで一つにまとめることができます。 Formの値を使用するuseActionState 例としては、簡易的なバリデーションがついたフォームとなります。実用性はありませんが、useActionStateの挙動を理解する手助けと、Typescriptでの型検証には有用だと思います。 フォームの入力情報を受け取る場合は、action関数の第二引数に情報が飛んできます。Stateとしては、 Error | null の状態を持つことで、バリデーションの有無を表現しています。 import { useActionState } from "react"; export const FormTest1 = () => { const [error, action, isPending] = useActionState<Error | null, FormData>( async (prevError: Error | null, formData: FormData) => { console.log(prevError); // 値の取得方法法 const data = Object.fromEntries(formData.entries()); console.log(data); // APIの処理などを行ってResultによって処理を分岐させる // returnを返せばエラー発生とする const error = new Error("Failed to submit data"); if (error) { return error; } return null; }, null ); return ( <> <form action={action}> <input type="text" name="name" /> <button type="submit" disabled={isPending}> 送信 </button> {error && <p>{error.message}</p>} </form> </> ); }; この例で確認できる内容としては以下になります。 useActionStateで送信されるForm情報の構造化 State設定の自由度がそれなりにある useActionStateで入力フォームを作成する 2つの例でuseActionStateの挙動については理解できたと仮定して、実際利用するフォームの作成を進めていきます。 作成するフォームの情報をまとめます。 名前:String・年齢:number バリデーション 名前:空文字禁止・10文字以内 年齢:0以上 バリデーション通過後、API通信をするイメージ(今回は2秒後エラー) ソースコードの全体を先に置いておきます。 import { useActionState } from "react"; type FormType = { name: string; age: number; }; type PrevFormDataType = { value: FormType; validationError: { name: Error | null; age: Error | null }; apiError: Error | null; }; const validationName = (name: string) => { if (name === "") { return new Error("名前を入力してください"); } else if (name.length > 10) { return new Error("名前は10文字以内で入力してください"); } return null; }; const validationAge = (age: number) => { if (age <= 0) { return new Error("年齢は0以上で入力してください"); } return null; }; export const FormTest3 = () => { const initialFormData: PrevFormDataType = { value: { name: "", age: 0 }, validationError: { name: null, age: null }, apiError: null, }; const [formData, action, isPending] = useActionState< PrevFormDataType, FormData >(async (_: PrevFormDataType, formData: FormData) => { // FormDataをobjectに変換 const _formData = Object.fromEntries(formData.entries()); const data: FormType = { name: _formData.name as string, age: Number(_formData.age), }; // validationを掛ける いい感じのライブラリがあれば参考にする const nameError = validationName(data.name); const ageError = validationAge(data.age); if (nameError || ageError) { return { value: { name: data.name, age: data.age }, validationError: { name: nameError, age: ageError, }, apiError: null, }; } // ここでAPI処理を実装・今回は2秒待ってエラーを返す await new Promise((res) => setTimeout(res, 2000)); const apiError = new Error("Failed to submit data"); return { value: { name: data.name, age: data.age }, validationError: { name: nameError, age: ageError, }, apiError: apiError, }; }, initialFormData); return ( <> <form action={action} className="flex w-full max-w-xl flex-col gap-2 rounded-md p-4 shadow" > <label className="flex flex-col"> <div className="flex flex-row text-xl"> <span className="w-1/3">名前:</span> <input className="w-full border p-1 text-right" type="text" name="name" defaultValue={formData.value.name} /> </div> <span className="h-4 text-xs text-red-500"> {formData.validationError.name && ( <>{formData.validationError.name.message}</> )} </span> </label> <label className="flex flex-col"> <div className="flex flex-row text-xl"> <span className="w-1/3">年齢:</span> <input className="w-full border p-1 text-right" type="number" name="age" defaultValue={formData.value.age} /> </div> <span className="h-4 text-xs text-red-500"> {formData.validationError.age && ( <>{formData.validationError.age.message}</> )} </span> </label> <button className={ "w-full rounded-md py-4 text-lg text-white" + (isPending ? " bg-gray-400" : " bg-blue-500") } type="submit" formAction={action} disabled={isPending} > 送信{isPending && "中"} </button> <span className="h-4 text-xs text-red-500"> {formData.apiError && <p>{formData.apiError.message}</p>} </span> </form> </> ); }; Stateの型定義 useActionStateでaction関数実行後は、Formの入力値はリセットが掛かってしまいます。フォームのバリデーション評価の間は値を継続させたいので、Stateの定義としてはFormの入力値・バリデーションエラー・apiエラーの三つを取得できるオブジェクトとして定義しておきます。 // Formのタイプ type FormType = { name: string; age: number; }; // Stateの型定義 type PrevFormDataType = { value: FormType; validationError: { name: Error | null; age: Error | null }; apiError: Error | null; }; バリデーション 将来的にはライブラリを使って運用を進めていきたいのですが、ここでは簡易的に自作したバリデーションを使用します。 名前のバリデーション const validationName = (name: string) => { if (name === "") { return new Error("名前を入力してください"); } else if (name.length > 10) { return new Error("名前は10文字以内で入力してください"); } return null; }; 年齢のバリデーション const validationAge = (age: number) => { if (age <= 0) { return new Error("年齢は0以上で入力してください"); } return null; }; バリデーション関数としては、型定義と合わせて Error | null を戻り値として設定しています。 useActionStateの実装 初期化の値を別途定義しています。初期状態では、各種エラーは null を入れておきます。フォームの値も初期値を設定します。 const initialFormData: PrevFormDataType = { value: { name: "", age: 0 }, validationError: { name: null, age: null }, apiError: null, }; const [formData, action, isPending] = useActionState< PrevFormDataType, FormData >(async (_: PrevFormDataType, formData: FormData) => { // FormDataをobjectに変換 const _formData = Object.fromEntries(formData.entries()); const data: FormType = { name: _formData.name as string, age: Number(_formData.age), }; // validationを掛ける いい感じのライブラリがあれば参考にする const nameError = validationName(data.name); const ageError = validationAge(data.age); if (nameError || ageError) { return { value: { name: data.name, age: data.age }, validationError: { name: nameError, age: ageError, }, apiError: null, }; } // ここでAPI処理を実装・今回は2秒待ってエラーを返す await new Promise((res) => setTimeout(res, 2000)); const apiError = new Error("Failed to submit data"); return { value: { name: data.name, age: data.age }, validationError: { name: nameError, age: ageError, }, apiError: apiError, }; }, initialFormData); action 関数の中身としては、以下のような流れになっています。 formDataの積み替え → FormTypeの情報へ変換 バリデーションチェック・早期リターンでバリデーションエラー表示 API通信 今回は、検証の意味を込めてAPIエラーも用意しています。ここは使用用途によって、ErrorBoundaryでキャッチする仕様でも問題ないかと思います。 終わり 今回は、useActionStateをTypescriptで型付けしながら入力フォームの実装をしてみました。新しい機能が出ても、Stableまで手を出さないというのは、良いことなのか悪いことなのかわかりませんね。きっと、技術選定でリジェクトされた思い出が強く残っているのだと思います。 useActionState以外にも便利そうなHooksが追加されていたので、React 19とNext 15で色々作ってみるのも楽しそうですね。ふんわりと年末に入りますが、一旦はメリークリスマス! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post React 19でuseActionStateで入力フォーム【Typescript】 first appeared on SIOS Tech. Lab .