TECH PLAY

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

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

546

こんにちは、 サイオステクノロジー の織田です。 最近CMでよく見る話題の生成AI、 Gemini を使ってみました!今回は、実際に使ってみた感想をブログ形式でまとめてみます。 Geminiとは? Geminiは、Google Workspaceに組み込まれた生成AI機能です。Gmail、Docs、SpreadSheetなど、普段使い慣れたアプリでAIの力を活用できるのが特徴です。同じ生成AIツールですとChatGPTがありますが、あちらはチャットボットとしてしか活用ができません。一方でGeminiは、Gmail、Docs、SpreadSheetといったGoogle Workspaceのツールと組み合わせて作業を行うことができます。 たとえば、Gmailでメールを作成する場合、Geminiはメールの下書きを作成したり特定のメールを抽出したりできます。ドキュメントでは、Geminiはドキュメントの要約を作成したり、ドキュメント内のテキストの表現や誤字を変更したりすることができます。スライドでは、Geminiはプレゼンテーション用の画像を作成することができます。 GeminiとGmailの連携:領収書管理の効率化 テスト用のタクシー代とホテル代の領収書付きメールで試した結果、両方の合計金額が算出された。 ここからは、Gmail、Docs、SpreadSheetのそれぞれについてGeminiがどのように活躍するかを詳しく説明します。(Slidesは日本語未対応のためここでは割愛します。)Geminiは、Gmailと連携することで、メール作成や管理を大幅に強化します。実際に触って特に便利だと感じたのが、領収書付きメールの処理です。受信トレイから領収書付きのメールのみ表示させるのはもちろん、添付された領収書の合計金額を計算してくれます。これにより、手作業で領収書を探したり、電卓で計算したりする手間が省け、大幅な時間短縮が実現できます。 GeminiとDocsの連携:長文中の誤字を一瞬で検知 Geminiは、Docsと連携することで、文書作成を強力にサポートしてくれます。例えば、簡単なプロンプトを与えるだけで、説明資料、企画書、小説など、さまざまな種類の文書を生成することができます。また、既存の文書を要約したり、テキストの表現を変更したりすることも可能です。これにより、文書作成にかかる時間を大幅に短縮することが可能になります。また、特に便利だと感じたのは誤字検出です。Docs上の文書についてGeminiに「誤字を検出して」と指示をだすと、一瞬ですべての誤字を指摘してくれました。長い文章を書くとどうしても誤字が数ヵ所混じってしまいますが、人手で確認するのはとても億劫です。Geminiを使えばこの面倒な作業を一瞬で終わらせることができます。 ダミーの長文を用意し誤字を検出してみたところ、事前に入力しておいた「ウェフサイト」が検出された GeminiとSpreadSheetの連携:目的に適した関数を提案 Geminiは、SpreadSheetと連携することで、データ分析を簡単に実施できるようにしてくれます。例えば、やりたい分析内容を指示するだけで、Geminiが適切な関数や数式を提案してくれます。「こういう処理をしてほしいんだけど、どの関数使えば良いんだっけ?」ということがよくあると思います。そんなとき、Geminiはユーザーのやりたいことを理解し、最適な関数や数式を提案してくれるため、作業効率が大幅に向上します。また、複雑なスプレッドシートの内容を分かりやすく要約することも可能です。これにより、データ分析の効率が向上し、より深い洞察を得ることができます。 例えば、特定の条件に合致するデータの出現回数を求めたい場合、Geminiに「このファイル中のhogehogeの出現回数を求める関数を教えてください」と指示するだけで、適切な関数(COUNTIF関数など)を提案してくれます。また、大量のデータを含むスプレッドシートの内容を要約したい場合、Geminiに「このシートの内容を要約して」と指示するだけで、主要なポイントをまとめた要約文を生成してくれます。 SpreadSheet用の関数は非常に多く実装されており、目的にあった関数がぱっと思い出せない時もあると思います。そんなときにGeminiを頼ってみてはいかがでしょうか。 特定の単語の出現回数を求める関数をGeminiに尋ねた結果、COUNTIF関数が正しく提案された Geminiを使ってみた感想 Geminiを実際に使用する前は、「AIチャットボットが仕事で役に立つだろうか?」と考えていました。実際に使用すると、領収書の整理や関数の提案など、「これ欲しかったんだよなあ」という機能が多くありとても便利だと感じています。 特に、Gmailでの領収書の整理、Docsでの長文中の誤字検出、SpreadSheetでの関数提案は、日々の業務に大きな変化をもたらしてくれると思います。 回答の精度についてはまだ改善の余地があると感じますが、将来的にGeminiのような生成AIツールが私たちの働き方を大きく変えていくのではないかと期待しています。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 1人がこの投稿は役に立ったと言っています。 The post Geminiを使ってみた感想:Gemini × Google Workspace first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの遠藤です。 今回は、 JavaScript Rising Stars で2023年、2024年と二年連続で1位に輝いたUIコンポーネントコレクションであるshadcn/uiについてまとめていきます。 では始めていきましょう! shadcn/uiとは? shadcn/uiはRadix UIとTailwindCSSを用いて作成されたUIコンポーネントコレクションです。 shadcn/uiの一番の特徴は公式ドキュメントにも This is not a component library. と書かれているように既存のコンポーネントライブラリではない点にあります。 一般的なコンポーネントライブラリの場合、NPMからパッケージをインストールしてコンポーネントをインポートすることで対象のコンポーネントを使用します。この方法の場合、ザイン システムに合わせてコンポーネントをカスタマイズしたり、ライブラリに含まれていないコンポーネントが必要になったりするまではうまく機能しますが、自分でコンポーネントに対して修正を加えたくなった場合、コンポーネントをラップしたり、スタイルをオーバーライドするための回避策を記述したり、互換性のない API を持つ異なるライブラリのコンポーネントを混在させたりすることになります。 一方shadcn/uiでは、実際のコンポーネント コードを自分のプロジェクトに落としてきて扱います。そのため、コンポーネントをカスタマイズおよび拡張する完全な制御権を持つことができ、コンポーネントを自分の要件に合わせて修正することが容易になります。 また生成AIに対応している点を特徴として謳っており、生成AIがshadcn/uiのコンポーネントを組み合わせて自然言語にあったwebデザインを作成するのはもちろん、shadcn/uiをベースに新しいコンポーネントの生成を行えるようなことを想定しています。 実際に先日投稿した 【React】プロンプトからNext.jsアプリを自動生成!v0を試してみた! で使用したv0ではshadcn/uiを利用してuiを作成しているようです。 公式ドキュメント : https://ui.shadcn.com/docs インストール 今回はNext.jsで導入する方法を確認していきます。 公式ガイド : https://ui.shadcn.com/docs/installation/next npm,pnpm,yarn,bunのいずれかを使用してinitコマンドを走らせます。今回私はbunを利用しました。 ❯ bunx --bun shadcn@latest init 各種設定を聞かれるので答えていきます。 ✔ Which style would you like to use? › New York ✔ Which color would you like to use as the base color? › Neutral ✔ Would you like to use CSS variables for theming? … no / yes これで完了です! 注意点 2025/02現在、Next.js or React19を利用していてnpmを利用している場合、  --force  or   --legacy-peer-deps  のフラグが必要になるようです。npmを利用している方はご注意を! npm i <package> --force npm i <package> --legacy-peer-deps 公式ガイド : Using shadcn/ui with Next.js 15 and React 19. Buttonコンポーネントを使ってみる では実際に利用してみましょう。まずはチュートリアルにもあるButtonコンポーネント利用する方法を確認してみます。 shadcn/uiはコンポーネントコレクションのため、一般的なコンポーネントライブラリでよくあるライブラリからButtonコンポーネントを呼び出してimportするといったことができません。使用するには使用したいコンポーネントを追加して上げる必要があります。追加するためにまずは以下のコマンドを実行してみましょう。 bunx --bun shadcn@latest add button そうすると、デフォルトだとcomponents/ui直下にbutton.tsxが作成されます。そしてこのbutton.tsxを呼び出すことでButtonコンポーネントを使用することが可能です。page.tsxに以下のように記述してnext.jsを動かし、表示を確認してみましょう。 import { Button } from "@/components/ui/button"; export default function Home() { return ( <div> <Button>Click me</Button> </div> ); } 無事表示することが出来ました!これがshadcn/uiの基本的な使用方法です。 Buttonコンポーネントのスタイルを変更してみる ここでButton.tsのコードの一部を確認してみます。 ・・・ const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", { variants: { variant: { default: "bg-primary text-primary-foreground shadow hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", }, size: { default: "h-9 px-4 py-2", sm: "h-8 rounded-md px-3 text-xs", lg: "h-10 rounded-md px-8", icon: "h-9 w-9", }, }, defaultVariants: { variant: "default", size: "default", }, } ) ・・・ 中を確認するとvariantsというものがあり、variantと、sizeというプロパティがあります。これらを変更することで、コンポーネントに当てるスタイルを変更することが出来ます。今回はvariantに”outline”, sizeに”lg”を当ててみました。 <Button variant="outline" size="lg"> Click me </Button> 無事スタイルが変更されました!ここまでは一般的なコンポーネントライブラリでも利用できることの多い機能ですね。 Buttonコンポーネントをカスタマイズする 次は実際に自分なりのカスタマイズをコンポーネントに加えてみましょう。全体的にボタンが赤くなる”red”variantを加えてみます。 variant: { default: "bg-primary text-primary-foreground shadow hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", red: "bg-red-500 text-white shadow-sm hover:bg-red-600", #追加 }, そうすると無事ボタンが赤くなりました!このコンポーネントの直接編集ができるのがコンポーネントコレクションであるメリットですね! aschild shadcn/uiではradix-uiのSlotを利用してasChild パターンというものが利用されています。asChildを利用すると、 asChild   false  のときデフォルトとして指定したコンポーネントをレンダリングする asChild   true  のとき渡した子要素をレンダリングする といったことが可能になります。 adChildパターンの理解に関してはこちらのブログを参考にさせていただきました。 asChild Pattern 今回は以下のコードを利用して Buttonコンポーネントをそのまま利用した場合 asChildを利用せずにButtonコンポーネントの子要素としてaタグを渡した場合 asChildを利用してButtonコンポーネントの子要素としてaタグを渡した場合 の挙動を確認します。 import { Button } from "@/components/ui/button"; export default function Home() { return ( <div className="flex flex-col items-end justify-center h-screen space-y-4"> <Button>ボタンそのまま</Button> <Button> <a href="<https://google.com>">そのままaタグを渡す</a> </Button> <Button asChild> <a href="<https://google.com>">asChildでaタグを渡す</a> </Button> </div> ); } 1つ目のButtonコンポーネントをそのまま利用した場合、buttonタグとしてそのまま出力されます。 2つ目のasChildを利用せずにButtonコンポーネントの子要素としてaタグを渡した場合では、buttonタグの子要素として <a href=” https://google.com “>そのままaタグを渡す</a> が扱われる形になります。 3つ目のasChildを利用してButtonコンポーネントの子要素としてaタグを渡した場合では、直接aタグに対してスタイルやhref要素がマージされて扱うことができるようになります。 終わりに 今回はshadcn/uiについてご紹介しました。実際に使用してみている感想としてはコードを直接変更できることによる取り回しの良さが最高です。またデフォルトのデザインでもおしゃれかつ、複数のvariantが用意されているので、そのままでも使っていけると感じています。また、新しいコンポーネントも追加されているみたいですので、これからにも期待大ですね! ではまた~ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post コンポーネント「ライブラリ」ではなく「コレクション」?shadcn/uiに入門してみた first appeared on SIOS Tech. Lab .
アバター
こんにちは! 今月も「OSSのサポートエンジニアが気になった!OSSの最新ニュース」をお届けします。 2025/2/6、The Linux Foundation Japan が SBOM の利用について解説した資料の日本語版である「SPDX 3.0を用いたAI部品表(AI BOM)の実装」を公開しました。 The Linux Foundation、AI部品表「AI BOM」の日本語資料を公開 https://japan.zdnet.com/article/35229116/ 生成 AI 技術「DeekSeek」のセキュリティ面に関する危険性について、情報が出ています。 個人情報ダダ漏れ。まだまだ出てくるDeepSeekの脆弱性 https://www.gizmodo.jp/2025/02/deepseek-data-protection-vulnerabilities.html Appleは、Xcode のビルドエンジン「Swift Build」をオープンソース化したと発表しました。 Apple、Xcodeで使われているビルドエンジン「Swift Build」をオープンソースで公開。WindowsとLinuxもサポートへ https://www.publickey1.jp/blog/25/applexcodeswift_buildwindowslinux.html ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【2025年2月】OSSサポートエンジニアが気になった!OSS最新ニュース first appeared on SIOS Tech. Lab .
アバター
今号では、前回紹介しきれなかったログローテーションの設定について、もう少し深堀りして見ていきます! 設定ファイルに設定可能な情報 前回、デフォルトの設定からどのような設定項目があるかを見ていきました。 今回は、それ以外にどのような設定があるのか、よく使用されると考えられる設定項目からピックアップして解説します。 ローテートの頻度 – daily:毎日 – weekly:毎週 – mounthly:毎月 – yearly:毎年 圧縮の有無 – compress:圧縮する – nocompress:圧縮しない (デフォルト) 空のログファイルの扱い – notifempty:空のログはローテートしない – ifempty:空のログもローテートする (デフォルト) さらに、下記設定項目を使用することで、より詳細に動作を設定することができます。 delaycompress ローテート直後のログをすぐに圧縮せず、次回ローテート時に圧縮します。 【例】ローテート1回目 log → log.1、2回目 log.1 → log.2.gz (ここで log.1 を圧縮する) …と続く olddir ローテート後のログの保存先を指定します。 ディレクトリは事前に作成しておく必要があります。 copytruncate ログをコピーしてから元のログを空にします。 特定のアプリが、 ファイルを参照し続けている (開きっぱなし) の場合などに有用 な設定です。 ただし ローテート中にログが書き込まれた場合、当該ログが消えるリスクがあるので要注意 です。ログの書き込みが少ない時間帯に実行するなど、運用でカバーする工夫が必要です。 運用における補足事項 設定ファイルは /etc/logrotate.d 配下で運用 /etc/logrotate.conf は基本的に編集せず、 個別のログローテーション設定を /etc/logrotate.d 配下に配置することを推奨 します。 設定変更後は、まずは動作テストを実行 logrotate -d コマンドでテスト実行 (実際にはローテートしない) ができます。 実際にローテートを実行する前に動作に問題が発生しないか確認しましょう。 反対に、logrotate -f コマンドを実行すると設定変更後ただちにローテートされます。 ディスク容量の定期的なチェック 長期間ファイルをローテートしていくと、古いログが溜まりすぎてシステムの容量を圧迫する可能性があります。 定期的にディスク容量をチェックするようにし、必要に応じてログの退避などの対応をしましょう。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 知っておくとちょっと便利!logrotate の仕組みについて2 first appeared on SIOS Tech. Lab .
アバター
今回は、以前Difyで作成したプロトタイプ「 Dify入門ガイド:X投稿を自動生成!10分で作るブログPR効率化ワークフロー 」をGASと Azure OpenAI Service で業務アプリとして作成しました。Difyで作成したプロトタイプから、普段使いしやすいように業務アプリ化する具体的な流れについて解説していきます。 Dify版プロトタイプ プロトタイプについては、「 Dify入門ガイド:X投稿を自動生成!10分で作るブログPR効率化ワークフロー 」で紹介しています。 こちらのプロトタイプでは、「URLと文字数を入力として、HTMLから情報を取得しXの投稿文を生成」します。プロトタイプでは、2ステップで処理を作成しています。 HTMLから記事情報を取得 取得したテキストからPR文を生成する 業務アプリ検討 業務アプリケーションのモチベーションとしては、「ブログの投稿と連動してXの投稿を自動生成し、業務効率を改善」となります。GASを選択した背景を含めて解説していきます。 要件 業務アプリは、以下の要件を満たす必要があると考えました。 投稿の管理:投稿ステータス管理(投稿済み・未投稿) ブログの更新と連動して投稿を取得する(定期実行) Xの自動投稿はせずに人の手で確認をする 複数の担当者が一覧にアクセスすることができる(アクセス管理) 定期実行・アクセス管理・一覧表示などの要件から、GASとスプレッドシートで業務アプリの作成を決定しました。GASとDifyの連携で処理を行うことも考えましたが、Difyの環境をAzure上で構成するよりもREST APIでAOAIにリクエストを投げるほうがお手軽なので、生成AIにはAOAIを使用します。 GASの制限 GASを採用するにあたって業務アプリに関係のある制限事項についてまとめていきます。 公式の情報としてはこちら になります。 制限 閾値 スクリプトの実行時間 6 min URL Fetch数 20,000 件/日 スクリプトあたりの同時実行数 1,000 トリガー 20 ユーザー/スクリプト トリガー実行に関しては、日付ベースの場合では実行時間の範囲を指定することができます。この実行時間は、必ずその範囲で実行するわけではないので、スクリプト間で前処理が必要な場合の処理に関しては工夫が必要です。こちらの制限を回避する方法としては、スクリプトを独立させておくなどが挙げられます。 上記の条件に抵触しないように業務アプリとして作成する必要があります。 GAS:XのPR文生成 from RSS それでは、GASコードの解説に入っていきます。構成図としては、以下のような構成になっています。データソースとしては、Google Spred Sheetを使用しています。 スクリプトとしては、3つになります。 トリガー実行:RSSから記事情報を取得しURL・タイトル・投稿日をシートに書き込む トリガー実行:シートからURLを取得し情報を圧縮してシートに書き込む 通常実行:圧縮したテキストをAOAIに投げて投稿本文を生成しシートに書き込む トリガー実行をする部分に関しては、それぞれ単独で運用しても問題なく機能するように作成しています。 投稿管理を行うシートの構成としては、 RSS というシート名で以下のヘッダーを持っています。 ヘッダー名 説明 タイトル RSSから情報を取得した記事タイトル リンク RSSから情報を取得した記事リンク 日付 RSSから情報を取得した記事投稿日 圧縮生データ HTMLタグなどを除外し圧縮したテキストデータ PR AOAIが生成したXの投稿文 実際の出力画面としては、以下のようになります。手動でフラグ管理としてステータス(投稿済み・未投稿)の列を追加して管理しています。 もし使用される方がいれば、上記のヘッダーを持った RSS というシートを用意してください。 共通スクリプト シートへのアクセスはどのスクリプトでも実行するため、共通として書き出しています。 // SpreadSheetのアクセスSheet取得 const accessSheet = (sheetName = "RSS") => { const file = SpreadsheetApp.getActiveSpreadsheet() const sheet = file.getSheetByName(sheetName) return sheet } 引数として sheetName を受け取れるようにしていますが、デフォルトとして RSS を指定しています。 RSSから情報を取得しシートに記載する このスクリプトでは「RSSから情報を取得( getRSSFeed )しシート内に情報が記載されていない場合、行の最後にタイトル・リンク・投稿日の情報を追記」を処理しています。工夫として、重複を避けるために記載済みの情報を削除する仕組みが搭載されています。 // スケジュール用:RSSから取得してスプレッドシートに書き込む const createRowData = () => { const sheet = accessSheet() const lastRow = sheet.getLastRow() // 2-2(B2) からスタート const linkList = lastRow == 1 ? [] : sheet.getRange(2, 2, lastRow - 1, 1).getValues().map((row) => row[0]) const dataFromRSS = getRSSFeed() const data = [] dataFromRSS.forEach((value) => { const [, link] = value if (!linkList.includes(link)) data.push(value) }) if (data.length == 0) return sheet.getRange(lastRow + 1, 1, data.length, data[0].length).setValues(data) } RSSの取得部分は処理として分割しています。こちらでは、RSSフィードからXML情報を取得しています。XMLをパースし、配列化して、最後に投稿日順にソートをして返答しています。 // RSSから情報を取得する const getRSSFeed = () => { // RSSフィードのURLを指定 const rssUrl = 'https://ch-lab.sios.jp/feed'; // ここにRSSフィードのURLを入力してください // RSSフィードを取得 const response = UrlFetchApp.fetch(rssUrl); const xml = response.getContentText(); const document = XmlService.parse(xml); const root = document.getRootElement(); // RSSフィードのエントリを解析 const items = root.getChild('channel').getChildren('item'); const data = []; items.forEach(function (item) { const title = item.getChild('title').getText(); const link = item.getChild('link').getText(); const pubDate = new Date(item.getChild('pubDate').getText()); data.push([title, link, pubDate]); }); // 日付sort data.sort((a, b) => a[1] > b[1] ? 1 : -1) return data } URLから圧縮済みテキストを生成しシートに記載 このスクリプトでは「シートから圧縮済みテキストを生成していない場合、URLから情報を取得し圧縮済みテキストを生成( compressHTML )しシートに追記」を処理しています。工夫として、重複を防ぐために圧縮済みテキストが ”” の場合のみURLを取得しています。また、HTML情報を取得する部分に関しては fetchAll でリクエストを同時実行しています。 // スケジュール用:URLから圧縮のテキストデータ未作成の場合は作成 function getCompressTextFromURL() { const sheet = accessSheet() const lastRow = sheet.getLastRow() // 情報がなければスルー if (lastRow == 1) return const itemList = sheet.getRange(2, 1, lastRow - 1, 5).getValues() const linkList = itemList.map((row) => row[1]) const nonComporessTextlinkList = [] itemList.forEach((row) => { const [, , , comporessText] = row if (comporessText == "") nonComporessTextlinkList.push(row[1]) }) const response = UrlFetchApp.fetchAll(nonComporessTextlinkList) response.forEach((value, index) => { const html = value.getContentText() const compressText = compressHTML(html) const url = nonComporessTextlinkList[index] const listIndex = linkList.indexOf(url) if (listIndex == -1) return itemList[listIndex][3] = compressText }) sheet.getRange(2, 1, lastRow - 1, 5).setValues(itemList) } URLから情報を取得して、情報を圧縮するスクリプトは以下になります。こちらは「 GASコード付き:URLからHTML取得&圧縮する方法|AI入力の最適化に 」で詳細に解説しています。 // HTMLから圧縮したTextを作成 const compressHTML = (html) => { // HTMLを解析 const document = HtmlService.createHtmlOutput(html).getContent(); // 必要な部分を抽出(この記事の内容が <section class="entry-content"> タグ内にあると仮定します var content = ''; const regex = /<section class="entry-content"[^>]*>([\s\S]*?)<\/section>/; const match = regex.exec(document); if (match && match[1]) { const temp = match[1]; // プロンプト圧縮用 content = temp.replace(/<img([^>]*?)alt="([^"]+)"([^>]*)>/g, '<img$1$3>$2') .replace(/<("[^"]*"|'[^']*'|[^'">])*>/g, '') //タグ削除 .replace(/[\r\n]+/g, "") //改行削除 .replace(/[\s\t\n]/g, "") //空白削除 .trim(); //ダメ押しのTrim(文字列の前後の空白削除) } return content } 圧縮の処理は以下になります。 imgタグのalt情報の分離 HTMLタグの削除 空白の削除 生成AIから投稿文を生成しシートに記載 このスクリプトでは「圧縮済みテキストからPR文を生成していない場合、AOAIのリクエストを生成( createRequestToAOAI )し、Xの投稿文を同時に5件まで生成しシートに追記」を処理しています。工夫として、重複を防ぐために圧縮済みテキストが ”” の場合のみ圧縮済みテキストを取得しています。また、APIの負荷を考慮してリクエストを最大5件まで制限し、 fetchAll でリクエストを同時実行しています // 通常実行:シートからPR文未作成のものを抽出して生成、最大同時生成5個 function getCreateContent() { const sheet = accessSheet() const lastRow = sheet.getLastRow() // 情報がなければスルー if (lastRow == 1) return const itemList = sheet.getRange(2, 1, lastRow - 1, 5).getValues() const comporessTextList = itemList.map((row) => row[3]) let nonContentRequestList = [] let nonConentComporessText = [] itemList.forEach((row) => { const [, , , comporessText, prText] = row if (prText == "") { nonContentRequestList.push(createRequestToAOAI(comporessText)) nonConentComporessText.push(comporessText) } }) nonContentRequestList = nonContentRequestList.slice(-5) nonConentComporessText = nonConentComporessText.slice(-5) const responses = UrlFetchApp.fetchAll(nonContentRequestList) responses.forEach((response, index) => { try { const responsJson = JSON.parse(response.getContentText()); const res = responsJson.choices[0].message.content.trim(); Logger.log(responsJson) const listIndex = comporessTextList.indexOf(nonConentComporessText[index]) if (listIndex == -1) return itemList[listIndex][4] = res } catch (e) { Logger.log(e) } }) sheet.getRange(2, 1, lastRow - 1, 5).setValues(itemList) } リクエスト作成の処理に関しては以下になります。 GASからのREST APIへの書き込みに関しては公式の情報を参考 に構築しています。 // AOAIへのリクエスト作成 function createRequestToAOAI(text) { const apiEndpoint = PropertiesService.getScriptProperties().getProperty("AOAI_API_URL"); const modelName = PropertiesService.getScriptProperties().getProperty("AOAI_API_MODEL"); const apiVersion = PropertiesService.getScriptProperties().getProperty("AOAI_API_VERSION"); const apiKey = PropertiesService.getScriptProperties().getProperty("AOAI_API_KEY"); const apiUrl = `${apiEndpoint}/openai/deployments/${modelName}/chat/completions?api-version=${apiVersion}`; const payload = { messages: [ { role: "system", content: `## 出力要件: 1投稿140文字以内で作成する 記事の要点を簡潔にまとめる 魅力的なフレーズや問いかけを活用する ## 出力例: - AIを活用した最新のマーケティング戦略とは?成功事例を交えて解説.詳しくはこちら - 生成AIが変えるデザインの未来。クリエイター必見のトレンドをチェック ## 注意事項:企業アカウント向けの場合、ブランドトーンを意識する ユーザーの関心を引く表現を心がける クリックを促すアクション(例: 詳しくはこちら)を含める 上記の条件に従い、適切なX向けの投稿文を生成してください。` }, { role: "user", content: text } ] } const request = { url: apiUrl, method: 'post', headers: { "Content-Type": "application/json", "api-key": apiKey }, payload: JSON.stringify(payload), muteHttpExceptions: true } return request } 使用しているモデルとAPIバージョンは以下になります。 項目 バージョン モデル GPT-4o-mini AOAI APIバージョン 2024-10-21 システムプロンプトとしては、以下のプロンプトを与えています。 ## 出力要件: 1投稿140文字以内で作成する 記事の要点を簡潔にまとめる 魅力的なフレーズや問いかけを活用する ## 出力例: - AIを活用した最新のマーケティング戦略とは?成功事例を交えて解説.詳しくはこちら - 生成AIが変えるデザインの未来。クリエイター必見のトレンドをチェック ## 注意事項: 企業アカウント向けの場合、ブランドトーンを意識する ユーザーの関心を引く表現を心がける クリックを促すアクション(例: 詳しくはこちら)を含める 上記の条件に従い、適切なX向けの投稿文を生成してください。 コラム こちらのスクリプトは通常実行しています。理由としては、主に二つあります。 一つ目は金銭的な側面です。現状、リクエスト単位でお金が発生しています。自動実行で再リクエストなどを実装した場合、コストが予期せず発生する可能性があります(だいぶ恐怖です)。 二つ目は生成した結果を人間の目でチェックしたいからです。SNS投稿の効率化がこちらのアプリのモチベーションとなっています。SNS投稿は社外の目があるとのことで、生成した結果を自動で投稿するのは、ハルシネーションなどのリスクを含めて断念しました。再生成なども行うことを視野に入れて実装しました。 上記の二点から、手動での実行にしています。 おわり 今回は、Difyで作成したプロトタイプをGASとAzure OpenAI Serviceを利用して業務アプリ化する方法について解説しました。投稿内容の自動生成と管理を実現しつつ、リスク管理の観点から手動での確認プロセスを組み込むことで、効率的かつ安全なワークフローを構築することができました。今後も改善を重ねながら、より使いやすいシステムを目指していきたいと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【GAS】Difyプロトタイプを本格業務アプリに!実践的な自動化 first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの遠藤です。 最近はブログ執筆に精力的に取り組んでおり、キーボードでたくさんの日本語を打ち込んでいます。日本語を入力する機会が多い方なら入力効率をあげる方法はないかな?と考えたこともあると思います。 今回はそんな方におすすめな、一般的な配列を乱さず打鍵効率を上げてくれる拡張ローマ字入力AZIKをご紹介します。 AZIKとは? AZIKは、一般のローマ字入力のキー配列をそのままに、日本語によく出てくる文字列(読み)を2〜3ストロークで打てるようにし、さらに打ちにくいパターンの互換キーを提供する拡張ローマ字入力です。 例えば、「入場券」というキーワードを入力する場合を考えてみます。 通常のローマ字で入力する場合、「NYUUJOUKEN」と11回のストロークが必要となります。 一方AZIKを利用した場合「NGHJPKD」の7ストロークで入力することが可能で、通常のローマ字入力と比べて4文字もストローク数を減らすことができます。 AZIKを利用されたことがない方だと、どういう仕組みでこのような入力になるのかさっぱりわからないと考えると思われますし、このようなルールを覚えるのは大変そうといった印象を抱かれると思います。が、実はこのルールの覚えやすさに配慮されている点がAZIKの特徴です。 AZIKでは1ストローク目は通常のローマ字と同様に子音の決定に使用されます。そして2ストローク目は文字の意味からではなく、実際の打鍵の指の動きによって決定されます。そのため、通常のローマ字入力のタッチタイピングができている人であればAZIKの基本については1時間程度で習得することが可能です。 また、ひとつのルールを除いて通常のローマ字と共存が可能のため、AZIK特有の入力方法を忘れてしまった場合、通常のローマ字入力を使用することも可能です。そのため導入のため一気にすべてのルールを覚える必要がなく、段階的に通常のローマ字入力からAZIKに移行することが可能です。  注意点としては、通常のローマ字の指の動きをベースにしているため、通常のローマ字のタッチタイプがある程度できる方じゃないと効果を発揮できません。AZIKに挑戦したい方はまずはタッチタイピングを身に付けてから挑戦しましょう。 作者様の解説書 : 拡張ローマ字入力  AZIK AZIKの入力方法 まずはこれだけは覚えよう 「っ」の入力方法 まず通常のローマ字入力と大きく違う点として、「っ」の入力を行うときには「;」キーを使用します。この変更によるメリットとしては、「あっ」といった「母音 + 子音」といった組み合わせの場合の入力が大幅に早くなります。このルールが先程あげていた通常のローマ字入力と共存出来ないルールです。「ssa」で「っさ」と入力することができなくなるのでこのルールだけは最初に習得する必要があります。 拡張キー 日本語には出現頻度が高い「読み」があります。たとえば、「かん」、「さい」などは非常に出現頻度が高い文字列で、これらのパターンには、「2文字めに《ん》がくるパターン」と「二重母音」のパターンが多いです。そこで、ローマ字テーブルを拡張する基本方針としてこれら2つのパターンの文字列の入力を2ストロークキーで打てるようにされています。 撥音拡張 「2文字めに《ん》がくるパターン」の打ち方です。これを撥音拡張といいます。 撥音拡張ではローマ字の2ストローク目の母音キーの代わりに、その母音キーの下のキーを使います。 たとえば「かん」を打つには、1ストロークめの子音は「K」そのままですが、2ストロークめは「A」の下のキーの「Z」を使います。つまり「KZ」と打ちます。「か」を打つのと同じ指使いで、2ストローク目の左の小指を一段下にずらすと「かん」になります。 機能 キー 覚え方 ann Z Aの下 inn K Iの下 unn J Uの下 enn D Eの下 onn L Oの下 単独の「ん」はQを使います。これは以下のように、「母音+ん」を打つときに使います。 あん(AQ)、いん(IQ)、うん(UQ)、えん(EQ)、おん(OQ) ※単独の「ん」は通常のように「NN」で打っても構いません。 また、 AZIKの原則ではAの下のZを「あ段」の撥音拡張に使いますが、この原則では「さん、だん、わん」など、1ストローク目の子音キーが左手に位置する文字は非常に打ちにくくなります。そこで、1ストロークめの子音が左手になる行の「あ段撥音拡張キー」は「N」で代用できるようにしてあります。(撥音互換キー) 読み 入力 だん DN がん GN ざん ZN さん SN たん TN わん WN らん RN 二重母音拡張 [ai][uu][ei][ou] などの二重母音が含まれる場合の打ち方です。原則は対応する母音キーの(下以外の)すぐ近くのキーを使います。 たとえば「さい」を打つには、さ行の子音キーの「S」と、「A」の上のキー「Q」、つまりSQと打ちます。「さ」を打つ指使いで、2ストローク目の左の小指を一段上隣りにずらすと「さい」になります。 機能 キー 覚え方 ai Q Aの上隣り uu H Uの斜め下隣り ei W Eの左隣り ou P Oの右隣り あ行の二重母音「あい」「うう」「えい」「おう」は、普通のローマ字のように、それぞれ、AI、UU、EI、OUと打ちます。 特殊拡張 頻出文字列の中には、「もの」「こと」などのように、撥音でもなければ、二重母音でもないパターンがあります。これらの文字列も次のように2ストロークで打てるようになっています。 これらの拡張を《特殊拡張》と呼びます。特殊拡張は、前述の撥音拡張や二重母音拡張とちがって文字のイメージから連想できるようになっています。 KT こと ST した TT たち HT ひと WT わた MN もの MS ます DS です KM かも TM ため DM でも KR から SR する TR たら NR なる YR よる RR られ ZR ざる MT また TB たび NB ねば BT びと GR がら GT ごと NT にち DT だち WR われ その他 外来語に多い特殊な拗音のうち、ウォ、ティ、ディなどは以下のように、1ストロークめの子音キーに続けてその下のキーを打ち、その後母音キーや拡張キーを打ちます。 ウォ WSO ティ TGI ディ DCI 特に外来語にしか見られない読みは直接長音記号が入力されるようにしてあります。 ウォー WP フォー FP それでもまだ打ちにくいパターンでは個別に次のような形で打てるようにしてあります。 わい WF(WQ) さい SF(SQ) せい SS(SW) ざ ZC(ZA) ざい ZV(ZQ) ぜ ZF(ZE) ぜい ZX(ZW) AZIKの導入の仕方 AZIKを利用するにはIMEに対してAZIK用のローマ字テーブルを設定して上げる必要があります。私の場合は日本語入力としてGoogle日本語入力を使用しているので、Google日本語入力での導入方法を紹介します。 まず設定用のtxtファイルをダウンロードしてください。 azik ダウンロード 続いて右下の「あ」を右クリックして、「プロパティ」を開きます。 「ローマ字テーブル」の「編集」をクリックします。  ポップアップが表示されたら、「編集」をクリック後、「インポート」を選択します。ポップアップで「現在のローマ字テーブルを上書きしますか?」と出てくるので、「OK」を選択後、先ほどダウンロードしたファイルを選択します。その後、「適応」をクリックすることでAZIKが使用可能になります。 まとめ 今回は、標準的なローマ字入力よりも効率的に日本語を入力できる拡張方式 AZIKについてご紹介しました。慣れるまでは少し時間がかかるかもしれませんが、AZIKを導入することで大幅に入力速度を向上させることが可能ですので是非1度試してみてください。 また、AZIKをさらにカスタマイズしてより多くの単語を2語で入力できる拡張AZIKなるものを考えている方もいらっしゃるみたいなので今回の記事で興味を持った方はそちらも調べてみると、さらなる入力速度向上を目指せるかもしれません。 ではまた~ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 日本語の打鍵効率を上げる! 拡張ローマ字入力AZIKのご紹介 first appeared on SIOS Tech. Lab .
アバター
今回は、「 実体験:Notion AIでブログ執筆が楽になる!3つの使い方をふんわり解説 」で紹介したタイトルとメタディスクリプション作成プロンプトを改善しました。プロンプトの紹介をしつつ、プロンプト作成の方法について紹介していきます。 プロンプト紹介 前回のプロンプトでは、タイトルとメタディスクリプションを同時に作成していました。プロンプトをより特化させるために分割しています。検証するためにサンプルとして、「 GASコード付き:URLからHTML取得&圧縮する方法|AI入力の最適化に 」を対象として実行しています。 Notion AIでは、記事の内容を入力として使用することができます。タイトル用とメタディスクリプション用では、記事の内容から抽出したキーワードをプロンプトに組み込んでいます。これによって汎用的なプロンプトとして利用することができます。 タイトル用プロンプト 主要なキーワードを抽出し、それをもとにクリックされやすいブログ記事タイトルを作成してください。 抽出した内容をもとにクリックされやすいブログ記事タイトルを作成してください。以下のルールを守ってください。 - 文字数は32〜40文字(検索結果で適切に表示される長さ) - 重要なキーワードを前半に配置(SEO効果を最大化) - 具体的な数字、疑問形、ベネフィットを含める(例:「最新」「初心者向け」「〇〇の方法」) - クリックを促す表現を使用(例:「今すぐ知るべき」「〇〇とは?」) - ブログタイトルを複数提案してください。 実行例 ## クリックされやすいブログ記事タイトル案 主要キーワード:Google Apps Script (GAS)、HTML取得、テキスト圧縮、AI入力最適化、正規表現 - 【GAS完全ガイド】HTMLスクレイピング&圧縮の実践手順|AI入力を最適化する3つの方法 - 【2025年版】GASでURL取得からHTML圧縮まで!初心者でもできるAI入力の効率化テクニック - 【GAS×AI開発】HTMLデータを最適化!実務で使える自動テキスト圧縮の実装方法とは? - 【保存版】GASプログラミング入門:HTMLスクレイピングから圧縮まで完全解説 - 【実践ガイド】GASでAI入力を最適化!HTMLデータ取得&圧縮の基本から応用まで メタディスクリプション用プロンプト 主要なキーワードを抽出し、それをもとにSEOに強いメタディスクリプションを作成してください。 抽出した内容をもとにブログ記事のメタディスクリプションを作成してください。以下のルールを守ってください。 - 文字数は70〜100文字(検索結果で適切に表示される長さ) - 記事の概要を簡潔に伝える(読者が知りたい情報を明確に) - 行動を促すフレーズを含める(例:「今すぐチェック」「詳しくはこちら」) - 自然な文章にする(単なるキーワードの羅列ではなく、読者が読みたくなる文章に) - 複数のメタディスクリプションを作成してください。 実行例 ## SEOメタディスクリプション案 主要キーワード:Google Apps Script (GAS)、HTML取得、テキスト圧縮、AI入力最適化、正規表現 - 案1:「GASでURLからHTMLを取得し、AIモデル入力用に最適化する方法を詳しく解説。imgタグのalt抽出から空白削除まで、実践的なコード例で学べます。効率的なテキスト処理を今すぐマスター!」 - 案2:「Google Apps ScriptでWebページのテキストを効率的に圧縮する方法を完全解説。AI入力の最適化に役立つHTMLタグ処理テクニックを、サンプルコード付きで詳しく紹介します。」 - 案3:「URLからHTMLを取得し、AI入力に最適な形式に圧縮するGASの実装方法を解説。正規表現を使った実践的なコード例で、すぐに使える技術が身につきます。」 お気に入りのプロンプトはお気に入り登録しましょう。 Notion AIではプロンプトをお気に入り登録することができます。登録したプロンプトは入力せずに実行することができます。お気に入りの登録手順を解説しておきます。 まずは、プロンプトを一度実行しておく必要があります。保存したいプロンプトを「最近」から選択してお気に入り登録します。 お気に入り登録したプロンプトは、「お気に入り」欄に表示され編集が可能になります。 編集では、プロンプト内容とアクション名を登録することができます。「すぐに実行」を True にすることで、「お気に入り」から選択したタイミングで実行されます。 これとは別にプロンプト改修を行えるように、Notionのページやブログでプロンプトを保存しておくと便利です。 プロンプト改修の手順紹介 Notionの公式ではプロンプトのテクニックが紹介 されています。プロンプト作成では、明確で具体的に記載することが求められます。指示が明確であるほど、求めている情報を取得することができます。 この具体化がとても難しいです。そこで、複数の生成AIを活用してプロンプトを作成しました。今回の手順は、以下の手順です。 Felo を用いて初期調査・プロンプトのひな形を作成 ChatGPTやGeminiを用いて、初期プロンプトをしばいて中間プロンプトを作成 中間プロンプトを別のAIに評価させる まずは、検索型のAIを用いてプロンプト作成に必要な情報を収集して、初期プロンプトを作成させます。初期プロンプトを複数のAIの力を借りて改修していきます。最終的に複数の生成AIから合格ができれば、採用して使用感をチェックすることでよいプロンプトを作成できます。 龍ちゃん この辺りも自分の中でまとまれば執筆しますね! 終わり 今回は、Notion AIのプロンプト改善について、タイトルとメタディスクリプションの分割方法や具体的なルール、プロンプトのお気に入り登録機能、そして複数の生成AIを活用したプロンプト作成手法を紹介しました。これらの手法を活用することで、より効果的なプロンプトを作成することができます。皆さんもぜひ試してみてください! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【2025年版】Notion AIプロンプト改善|タイトル&メタディスクリプション first appeared on SIOS Tech. Lab .
アバター
はじめに 近年、コンテナ技術は企業のITインフラにおいて不可欠な存在となりました。 アプリケーションの開発・運用をスムーズにする一方で、適切に管理しなければスケールやセキュリティの課題が発生します。 前回の記事でコンテナプラットフォームの概要として紹介した代表的なものとして Red Hat OpenShift や SUSE Rancher などがあり、Kubernetesを基盤としながら、運用を簡素化する機能を提供しています。 本記事では、コンテナ管理プラットフォームに求められる主な機能について解説し、どのようなポイントが選定時に重要になるかを考えていきます。 コンテナプラットフォームに求められる主な機能 コンテナプラットフォームを適切に運用するためには、以下のような機能が求められます。 ① クラスタ管理 コンテナ管理プラットフォームの最も基本的な役割は、 Kubernetesクラスタの管理 です。 Kubernetesクラスタ管理についての主な機能は下記となります。 マルチクラスタ対応 :複数のクラスタを統合的に管理し、異なる環境(オンプレミス、クラウド)にデプロイ可能 オートスケーリング :トラフィックの増減に応じて、Podやノードのスケールを自動化 セルフヒーリング :障害が発生した際に、正常な状態へ自動復旧 Kubernetesクラスタ自体の機能を管理するのはもちろんのこと、1つの組織が1つのクラスタを管理することは少なく、 異なるクラウド環境にある多数クラスタを一元管理できる点などが重要となってきています。 ② デプロイと運用の自動化 開発者がスムーズにアプリケーションをデプロイできるよう、 CI/CDパイプラインとの統合 が求められます。 GitOpsサポート :Gitによるコード管理と運用自動化を組み合わせることで Blue-Greenデプロイ :Canaryリリース等を実装することで サービスの切り替えを容易にし運用負荷を軽減 ローリングアップデート  :ダウンタイムなしの更新 OpenShiftやRancherなどでは CIツールとCDツールを組み合わせて CI/CDパイプラインに組み込むことで、開発とデプロイの自動化などを実現できます。 ③ ネットワークとセキュリティ コンテナ環境では、ネットワークの設定とセキュリティ対策が不可欠です。 ネットワークポリシーの適用 (Calico, Cilium, OVN-Kubernetes など) サービスメッシュの統合 (Istio, Linkerd など)でマイクロサービス間の通信管理 コンテナイメージのスキャン (Trivy, Clair など)で脆弱性の事前検知 RBAC(Role-Based Access Control)による権限管理 特にエンタープライズ環境では、 ゼロトラストセキュリティの導入 が求められるため、認証・認可の強化が重要です。 ④ 監視・ログ管理 大規模なコンテナ環境では、可観測性(Observability)が必要になります。 メトリクス監視(Prometheus + Grafana) でリソース使用率を可視化 ログ管理(Fluentd, ELK, Loki) でコンテナの挙動を記録 分散トレーシング(Jaeger, OpenTelemetry) でマイクロサービスのパフォーマンスを解析 これらのツールをコンテナプラットフォームと併せて活用するすることで、障害発生時の迅速な対応が可能となります。 ⑤ マルチクラウド・ハイブリッドクラウド対応 企業では、単一のクラウドプロバイダーに依存せず、 マルチクラウドやハイブリッドクラウド環境 を採用するケースが増えています。 オンプレミス + クラウドの統合管理 異なるクラウド(AWS, Azure, GCP)間のワークロード移動 コスト最適化のための柔軟なリソース管理 まとめ コンテナ管理プラットフォームを選定する際には、以下のポイントが重要です。 ・ クラスタ管理の自動化と拡張性 ・ デプロイの効率化(GitOps、CI/CD) ・ ネットワーク & セキュリティの強化 ・ 監視・可観測性の確保 ・ マルチクラウド・ハイブリッドクラウド対応 OpenShift は エンタープライズ環境 に適し、 Rancher は マルチクラウド運用  などに向いています。 前回の記事でも触れましたがコンテナ技術の進化とともにこれらのプラットフォームも継続的に進化しており、導入前には 自社のユースケースに合ったを重点とすべき機能を適切に把握することが重要 となってきます 。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 企業のDXを支えるコンテナプラットフォームに求められる重要なポイント解説 first appeared on SIOS Tech. Lab .
アバター
概要 こんにちは、サイオステクノロジーの安藤 浩です。 今回はOpenseaで提供されているAPIを利用して、アプリ開発をしていますので、Opensea のAPIについて紹介していきます。 OpenSeaは NFT マーケットプレイスで、そのサービスでAPIが提供されているので、APIで提供されている機能をAPIへリクエストしながらご紹介します。 提供されている Endpoints について 大きく分けて3分類のEndpoint があります。ERC721, ERC1155 に対応しているようです。 詳細は以下に記載があります。 https://docs.opensea.io/reference/api-overview Analytics Endpoints コレクションやイベントの分析用のAPIが提供されています。 NFT Endpoints NFTに関連するコレクション、NFT のメタデータ、所有権情報などを取得するためのAPIが提供されています。 OpenSea Marketplace Endpoints OpenSea のNFT マーケットプレイス上での取引に関するAPIなどが提供されています。 ※Stream API が提供されていますが、ここでは触れません。 OpenAPI について OpenAPI Spec は以下で取得できるのでこちらを利用するとリクエストするのに便利です。 https://docs.opensea.io/reference/openapi-definition API Key について 本番用のOpenSea API キーを取得するには、申請書に記入する必要があります。 テストネットではAPI Keyは不要です。 API Keyの詳細に関しては以下に記載があります。 https://docs.opensea.io/reference/api-keys NFT Endpoints 今回は、主にNFTに関する情報を取得したいと思うので、NFT Endpoints のテストネットのAPIを試したいと思います。(一部抜粋) テストネットのAPIのベースになるURLは以下です。 APIには v1 と v2 がありますが、今回はv2で試します。 https://testnets-api.opensea.io/api/v2   テストネットに対応するOpensea のURLは  こちら  です。 VSCode の拡張機能: Rest Client  を利用して、HTTP リクエストしてみたいので以下のように書いてリクエストしてみます。 以下で利用したRest Client のコードは以下のリポジトリに配置してあります。 https://github.com/Hiroshi-Ando-sti/introduction-openseapi/tree/main @BASE_URL=https://testnets-api.opensea.io @BASE_PATH=/api/v2 ### GET /accounts/{address_or_username} @address=0xCebC36de334CE12dFD08f4C39E833016263Ba5B0 GET {{BASE_URL}}{{BASE_PATH}}/accounts/{{address}} HTTP/1.1 まずは、アカウントの情報を取得するAPIにリクエストしてみます。 ※以降、 @BASE_URL ,  @BASE_PATH  は省略します。 上記を実行した結果はこちらです。 { "address": "0xcebc36de334ce12dfd08f4c39e833016263ba5b0", "username": "hir-ando", "profile_image_url": "https://i.seadn.io/s/raw/files/a02cdbb10a9305a3148d4197f0b0de20.png?w=500&auto=format", "banner_image_url": "https://i.seadn.io/s/raw/files/635aa07c9d1248c744b9c5dd2a709fc0.jpg?w=500&auto=format", "website": "", "social_media_accounts": [], "bio": "STI", "joined_date": "2024-12-24" } Opensea には Collection という概念があり、NFTをまとめるグループのようなものです。 Collection の情報を取れるAPIは以下です。 ### GET /collections GET {{BASE_URL}}{{BASE_PATH}}/collections HTTP/1.1 hiroshinft という Collection があるのでこの情報を取得してみます。 テストネットのOpensea では以下から確認できます。 https://testnets.opensea.io/ja/collection/hiroshinft ### GET /collections/{collection_slug} @collection_slug=hiroshinft GET {{BASE_URL}}{{BASE_PATH}}/collections/{{collection_slug}} HTTP/1.1 実行結果は以下の通りです。(一部省略) Collection の情報がとれていることが分かります。 { "collection": "hiroshinft", "name": "HiroshiNFT", "description": "", "image_url": "", "banner_image_url": "", "owner": "0x36da942099c028275321130b5e503f37da446487", "safelist_status": "not_requested", "category": "", "is_disabled": false, "is_nsfw": false, "trait_offers_enabled": false, "collection_offers_enabled": true, "opensea_url": "https://testnets.opensea.io/collection/hiroshinft", "project_url": "", "contracts": [ { "address": "0xe88df35e01e3e33df38fb0b5e324282feceb20c2", "chain": "sepolia" } ], "editors": [ "0x36da942099c028275321130b5e503f37da446487" ], "fees": [ { "fee": 0.5, "recipient": "0x0000a26b00c1f0df003000390027140000faa719", "required": true } ], "total_supply": 2, "created_date": "2025-02-20" } 今度は Opensea で取得できるChainとContract Address を指定してContract を指定したいと思います。 ### GET /chain/{chain}/contract/{address} @chain=sepolia @contract_address=0xE88Df35e01e3e33Df38FB0B5e324282feCeb20c2 GET {{BASE_URL}}{{BASE_PATH}}/chain/{{chain}}/contract/{{contract_address}} HTTP/1.1 NFTの名前、供給量などが確認できます。 { "address": "0xe88df35e01e3e33df38fb0b5e324282feceb20c2", "chain": "sepolia", "collection": "hiroshinft", "contract_standard": "erc721", "name": "HiroshiNFT", "total_supply": 0 } 個別のNFT の情報を取得したいと思います。ここでは  @identifier  を2とします。 ### GET /chain/{chain}/contract/{address}/nfts/{identifier} @chain=sepolia @contract_address=0xE88Df35e01e3e33Df38FB0B5e324282feCeb20c2 @identifier=2 GET {{BASE_URL}}{{BASE_PATH}}/chain/{{chain}}/contract/{{contract_address}}/nfts/{{identifier}} HTTP/1.1 NFTの作成者や所有者情報、Opensea のURLなどが確認できます。 { "nft": { "identifier": "2", "collection": "hiroshinft", "contract": "0xe88df35e01e3e33df38fb0b5e324282feceb20c2", "token_standard": "erc721", "name": null, "description": null, "image_url": null, "display_image_url": "", "display_animation_url": null, "metadata_url": null, "opensea_url": "https://testnets.opensea.io/assets/sepolia/0xe88df35e01e3e33df38fb0b5e324282feceb20c2/2", "updated_at": "2025-02-20T13:42:02.649572", "is_disabled": false, "is_nsfw": false, "animation_url": null, "is_suspicious": false, "creator": "0x36da942099c028275321130b5e503f37da446487", "traits": null, "owners": [ { "address": "0xcebc36de334ce12dfd08f4c39e833016263ba5b0", "quantity": 1 } ], "rarity": null } } 上記で取得できた所有者のAddress を指定して、その Address が所有するNFTを情報を取得します。 ### Get NFTs (by account) ### GET /chain/{chain}/account/{address}/nfts @chain=sepolia @address=0xCebC36de334CE12dFD08f4C39E833016263Ba5B0 GET {{BASE_URL}}{{BASE_PATH}}/chain/{{chain}}/account/{{address}}/nfts HTTP/1.1 1件しかないですが、nfts は配列であり複数件あれば複数件取得できます。 { "nfts": [ { "identifier": "2", "collection": "hiroshinft", "contract": "0xe88df35e01e3e33df38fb0b5e324282feceb20c2", "token_standard": "erc721", "name": null, "description": null, "image_url": null, "display_image_url": "", "display_animation_url": null, "metadata_url": null, "opensea_url": "https://testnets.opensea.io/assets/sepolia/0xe88df35e01e3e33df38fb0b5e324282feceb20c2/2", "updated_at": "2025-02-20T13:42:02.649572", "is_disabled": false, "is_nsfw": false } ] } また、Collection を指定して、その Collection 内のNFTの情報を取得できます。 ### Get NFTs (by collection) ### GET /collection/{collection_slug}/nfts @collection_slug=hiroshinft GET {{BASE_URL}}{{BASE_PATH}}/collection/{{collection_slug}}/nfts HTTP/1.1 こちらも先ほど同様で複数件取得が出来るようになっています。 { "nfts": [ { "identifier": "2", "collection": "hiroshinft", "contract": "0xe88df35e01e3e33df38fb0b5e324282feceb20c2", "token_standard": "erc721", "name": null, "description": null, "image_url": null, "display_image_url": "", "display_animation_url": null, "metadata_url": null, "opensea_url": "https://testnets.opensea.io/assets/sepolia/0xe88df35e01e3e33df38fb0b5e324282feceb20c2/2", "updated_at": "2025-02-20T13:42:02.649572", "is_disabled": false, "is_nsfw": false }, { "identifier": "1", "collection": "hiroshinft", "contract": "0xe88df35e01e3e33df38fb0b5e324282feceb20c2", "token_standard": "erc721", "name": null, "description": null, "image_url": null, "display_image_url": "", "display_animation_url": null, "metadata_url": null, "opensea_url": "https://testnets.opensea.io/assets/sepolia/0xe88df35e01e3e33df38fb0b5e324282feceb20c2/1", "updated_at": "2025-02-20T13:39:02.836098", "is_disabled": false, "is_nsfw": false } ] } GET /chain/{chain}/contract/{address}  と似ていますが、Contract Address のNFTの情報を取得します。 ### Get NFTs (by contract) ### GET /chain/{chain}/contract/{address}/nfts @chain=sepolia @contract_address=0xE88Df35e01e3e33Df38FB0B5e324282feCeb20c2 GET {{BASE_URL}}{{BASE_PATH}}/chain/{{chain}}/contract/{{contract_address}}/nfts HTTP/1.1 上記で @identifier=2  を指定した情報と同じ情報が含まれています。  @identifier=1  もMintしてあるのでその情報も含まれていることが確認できます。 { "nfts": [ { "identifier": "2", "collection": "hiroshinft", "contract": "0xe88df35e01e3e33df38fb0b5e324282feceb20c2", "token_standard": "erc721", "name": null, "description": null, "image_url": null, "display_image_url": "", "display_animation_url": null, "metadata_url": null, "opensea_url": "https://testnets.opensea.io/assets/sepolia/0xe88df35e01e3e33df38fb0b5e324282feceb20c2/2", "updated_at": "2025-02-20T13:42:02.649572", "is_disabled": false, "is_nsfw": false }, { "identifier": "1", "collection": "hiroshinft", "contract": "0xe88df35e01e3e33df38fb0b5e324282feceb20c2", "token_standard": "erc721", "name": null, "description": null, "image_url": null, "display_image_url": "", "display_animation_url": null, "metadata_url": null, "opensea_url": "https://testnets.opensea.io/assets/sepolia/0xe88df35e01e3e33df38fb0b5e324282feceb20c2/1", "updated_at": "2025-02-20T13:39:02.836098", "is_disabled": false, "is_nsfw": false } ] } 上記で指定していた Contract Address では traits がないので先ほどの情報に含まれていませんでしたが、Traits を取得することができます。 ### Get Traits ### GET /traits/{collection_slug} @traits_collection_slug=usernft-14 GET {{BASE_URL}}{{BASE_PATH}}/traits/{{traits_collection_slug}}/nfts HTTP/1.1 例えば、Collection が  usernft-14  というものだとTraits に  stage  というパラメータがあります。 { "categories": { "stage": "number" }, "counts": { "stage": { "min": 1, "max": 4 } } } まとめ Opensea のAPI の特に NFT Endpoints について取り上げました。 NFT Endpoints のAPIを利用するとCollection 、NFT のメタデータ、所有権情報などを取得することができます。 Opensea のAPIを利用して、NFT の所有者や作成者、Contract の情報が取得できるので活用してみてはいかがでしょうか。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post [Web3] Opensea API を利用した NFT の情報取得 first appeared on SIOS Tech. Lab .
アバター
Google Apps Script(GAS)でURLから不要なタグや改行を削除した圧縮したテキスト情報を取得する方法について解説します。 圧縮の処理は以下になります。 imgタグのalt情報の分離 HTMLタグの削除 空白の削除 URLからHTMLを取得して情報を圧縮する まずは、ソースコード全体を貼ります。こちらは「URLを引数として受け取り、HTML情報を取得し、特定のタグ情報内に含まれる情報を圧縮し、テキストで返答する:返答するサンプルとなります。 function testFunction(url) { // HTMLを取得 const html = UrlFetchApp.fetch(url) // HTMLを解析 const document = HtmlService.createHtmlOutput(html).getContent(); var content = ''; // 必要な部分を抽出(この記事の内容が <section class="entry-content"> タグ内にあると仮定します // 当ブログのコンテンツ部分になります const regex = /<section class="entry-content"[^>]*>([\\s\\S]*?)<\\/section>/; const match = regex.exec(document); if (match && match[1]) { const temp = match[1]; // プロンプト圧縮用置換プログラム content = temp.replace(/<img([^>]*?)alt="([^"]+)"([^>]*)>/g, '<img$1$3>$2') //imgのalt属性の抜き出し .replace(/<("[^"]*"|'[^']*'|[^'">])*>/g, '') //タグ削除 .replace(/[\\r\\n]+/g, "") //改行削除 .replace(/[\\s\\t\\n]/g, "") //空白削除 .trim(); //文字列の前後の空白削除 } Logger.log(content) return content } HTML情報の取得には、GASの UrlFetchApp と HtmlService を使用して、テキスト情報を取得しています。 UrlFetchApp : GASでfetchリクエストを行うサービス 。コードでは、GETリクエストを投げてHTML情報を取得しています。 HtmlService : GASでHTMLを扱うサービス 。コードでは、 UrlFetchApp で取得したHTML情報からテキスト情報としてコンテンツを取得しています。 取得したテキスト情報から特定の領域を取得するために、正規表現を使用しています。今回は、当ブログのコンテンツ領域を取得するタグを指定して、タグ間を抜き出しています。 imgタグのalt情報の分離 正規表現を使用して、imgタグに含まれるalt情報をタグの外側に抽出します。 function testFunctionImg() { const htmlText = ` <p>imgタグからalt情報を抽出する</p> <img src="example.jpg" alt="サンプル画像"> <img src="test.png" alt="テスト画像"> `; const updatedText = htmlText.replace(/<img([^>]*?)alt="([^"]+)"([^>]*)>/g, '<img$1$3>$2'); console.log(updatedText); } 実行した結果は、以下になります。 <p>imgタグからalt情報を抽出する</p> <img src="example.jpg" >サンプル画像 <img src="test.png" >テスト画像 HTMLタグの削除 正規表現を使用して、HTMLタグ <> を空文字に置換しています。 function testFunctionRemoveTag(){ const htmlText = ` <p>htmlタグを置換して削除する</p> <img src="example.jpg" >サンプル画像 <img src="test.png" >テスト画像 ` const updatedText = htmlText.replace(/<("[^"]*"|'[^']*'|[^'">])*>/g, ''); console.log(updatedText); } 実行した結果は、以下になります。 htmlタグを置換して削除する サンプル画像 テスト画像 空白の削除 正規表現と JavaScriptのtrim を使用して空白を削除しています。 function testFunctionRemoveBlank(){ const htmlText =` htmlタグを置換して削除する サンプル画像 テスト画像 ` const updatedText = htmlText.replace(/[\\s\\t\\n]/g, "").trim(); console.log(updatedText) } 実行した結果は、以下になります。 htmlタグを置換して削除するサンプル画像テスト画像 情報を圧縮するモチベーション 処理自体は正規表現と既存関数の組み合わせで実現しています。ここでは、なぜ情報を圧縮するのかについて解説をしていきます。 「 Dify入門ガイド:X投稿を自動生成!10分で作るブログPR効率化ワークフロー 」では、URLからPR文の生成を行っていました。内部の処理は、HTMLから情報を取得してLLMに入力しPR文を生成しています。HTML取得の部分はDify製のツールを使用して実装しています。 こちらで作成したプロトタイプを、GASとAOAIを使用したアプリで実現するために本記事を執筆しました。Dify製ツールを使用していた部分を自作しています。HTMLを取得しただけの生の状態では、タグ情報や空白が含まれています。「 生成AIの入力時のトークン量を節約 」のために不要情報を落として情報を圧縮しています。 アプリ化の検討に関しては、「 GASでブログの投稿と連動してAIにPR文を考えてもらう:プロトタイプ 」で検討しています。 終わり 本記事では、GASを使用してURLからHTMLを取得し、必要な情報を抽出・圧縮する方法について解説しました。特にAIモデルへの入力を最適化するために、imgタグのalt情報の分離、HTMLタグの削除、空白の削除という3つの処理を実装しました。このアプローチにより、効率的なテキスト処理が可能になります。 今週は狂ったようにブログを書いてた龍ちゃんです。DifyとGASの検証をしていますが、フロントエンドの検証もやりたいなとふつふつと湧き上がるものを感じています。3月はそっちにも取り組みたいですな!!! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post GASコード付き:URLからHTML取得&圧縮する方法|AI入力の最適化に first appeared on SIOS Tech. Lab .
アバター
1. 初めに PS/SLの佐々木です。 最近ContractAddressがどのように導出されているのかが気になり調べていたところ、ContactAddressの導出方法は二つがあることを知りました。その中で事前にコントラクトアドレスを計算して、デプロイ前のコントラクトに対して送金できたり、署名を作成できたりと結構面白そうだったのでまとめてみました。 Ethereumのスマートコントラクトをデプロイするにオペコードというものが存在します。オペコードには * CREATE ** および ** CREATE2 * の二つの種類があります。 この記事では、 CREATE と CREATE2 の違いを比較し、 CREATE2 の理解や使いどころの理解の手助けになればと思います。 2. opcodeとは EthereumのスマートコントラクトはEVM上で実行され、EVMは低レベルの命令セット(opcode)を用いて処理を行います。 CREATE や CREATE2 は、新しいスマートコントラクトをデプロイするためのopcodeです。 *CREATE :一般的なスマートコントラクトのデプロイ手段。 *CREATE2 :特定の計算方法を用いてコントラクトアドレスを決定できる。 これらの仕組みを理解することで、スマートコントラクトの事前アドレス計算や資金送付の最適化が可能になります。 3. CREATEの仕組み *CREATE* * opcodeは、新しいスマートコントラクトをデプロイする際に利用されます。デプロイされたコントラクトのアドレスは、以下の要素から決定されます。* keccak256(RLP_ENCODED(msg.sender+ nonce)) ここで、 nonce はコントラクトを作成するアカウント(EOAまたはコントラクトアカウント)のトランザクション回数です。このため、任意のタイミングでコントラクトをでデプロイしたときのコントラクトアドレスを予測することができません。 4. CREATE2の仕組み *CREATE2* * opcode は EIP-1014 で導入され、より予測可能な方法でコントラクトのアドレスを決定できるようになりました。 CREATE2 を使用した場合、コントラクトのアドレスは以下の式で決定されます。* keccak256(0xFF + 創設者のアドレス + salt + keccak256(バイトコード)) ここで salt は任意の32バイトの値です。 CREATE2 を使用することで、生成されるコントラクトアドレスが nonce に依存しなくなり任意のタイミングでデプロイする場合でもコントラクトアドレスを事前に計算することができます。 CREATE2の制約 デプロイ時のバイトコードが一致している必要がある 事前に計算したアドレスと実際にデプロイされるアドレスが一致するためには、同じ bytecode を使用する必要があります。 異なる salt を用いると異なるアドレスが生成される *salt* を変更すると、同じコントラクトコードでも異なるアドレスが生成されます。* 事前にアドレスを計算できるが、バイトコードの変更は不可 事前に計算したアドレスに対してバイトコードを変更すると、意図したアドレスでデプロイできなくなります。 デプロイにはコントラクトのみから実行可能で外部アカウントであるEOAからは呼び出し不可 CREATE2のユースケース 異なるチェーン間で同じコントラクトアドレスを使用したい場合 デプロイ前にトランザクション署名が可能 NFTををMintするコントラクトをデプロイする前に署名を事前に作成可能 デプロイのタイミングを最適化&コントラクトアドレスを事前告知可能 デプロイ前に送金したETHの扱いについて CREATE2 を使用すると、デプロイ前のアドレスが確定するため、実際にコントラクトがデプロイされる前に、そのアドレスへETHを送ることが可能になります。しかし、この時点ではそのアドレスにコードは存在しません。 EVMの仕様上、コードが存在しないアドレスに送金されたETHはそのアドレスにロックされ、誰もアクセスできなくなります。そのため、コントラクトが正しくデプロイされる前にETHを送る場合は、確実にデプロイできることを確認する必要があります 5. CREATE と CREATE2 の違い 項目 CREATE CREATE2 アドレス計算 keccak256(address + nonce) keccak256(0xFF + address + salt + keccak256(bytecode)) 事前計算 不可能 可能 デプロイ方法 通常のトランザクション saltを利用してアドレスを固定化 送金 デプロイ前に送金できない デプロイ前に送金可能 8. まとめ *CREATE2* を活用すると、特定のアドレスでのスマートコントラクトの動作を保証できる。* *computeAddress* 関数を活用することで、デプロイ前にコントラクトアドレスを予測できる。* ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post デプロイ前のコントラクトアドレスを知りたい – CREATE2 Opecodeについて – 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+回答精度の評価改善までのオールインワン基盤をご提供します。 評価の利用手順の概要 RAGSPでは以下の手順で評価を行うことが出来ます。 プロンプトフローのインポート 評価の実行 利用するプロンプトフローの選択 評価用のデータセットの追加 評価項目の設定 評価の実行 結果の確認 注目 プロンプトフローとは? プロンプトフロー(Prompt Flow)は、Azureが提供している生成AIのプロンプト開発・評価・管理を効率化するためのツールです。RAGSPでは概要図のバックエンドAPIに当たる部分がプロンプトフローで構築されています。 プロンプトフローの最大の特徴は「ノード」と言われる処理の単位の流れをグラフィカルに表示し、フローの流れを直感的に追える点にあります。例えば以下の画像の例では、inputノードでアプリの入力を受け取り、LLMノードで入力された質問を処理し、それをpythonできれいな形に整形して最終的にアプリケーションの出力として吐き出している様子が表現されています。 RAGSPでの具体的なノードの構成と処理の流れは以下のような形になっていて、これをAzure Functions上で動作させてBackend APIとして扱っています。 プロンプトフロー単体に関するブログも執筆しておりますので、よりプロンプトフローについての知識を深めたい方は以下の記事も合わせてご拝読ください。 2025-02-19 【Azure】プロンプトフローで生成AIを評価してみよう プロンプトフローのインポート では、実際に評価を行う方法について確認していきましょう。プロンプトフローの評価はAzure AI FoundryまたはAzure Machine Learning Studioを利用して行うことが可能ですが、今回はAzure AI Foundryを利用してプロンプトフローを利用する方法について確認していきます。 また今回インポートを行うプロンプトフローの接続先として、 RAGテスト用就業規則 をデータソースとして学習させたものを利用しております。 RAGSPで環境を作成すると、「hub-<環境名>」というリソースが作成されます。そのリソースに移動し、「Launch Azure AI Foundry」をクリックしましょう。 クリックすると、Azure AI Foundry 管理センターが表示されます。「新しいプロジェクト」をクリックします。そうするとポップアップでプロジェクト名が求められるので入力します。 完了後、プロジェクト画面に遷移します。遷移後の左のタブの「プロンプトフロー」を選択することでプロンプトフローを利用することが出来ます。 画面が遷移したら、「作成」をクリックします。 「ローカルからアップロード」を選択します。 そうするとポップアップが表示されで、「参照」クリックしてRAGSPのソースコードからプロンプトフローをアップロードします。その後、「フローの種類の選択」から「Chat Flow」を選択し、アップロードを行います。 そうするとRAGSPを構成しているプロンプトフローをAzure AI Foundryにアップロードが完了します。 評価の実行 インポートが完了したら、ここからは実際に評価を行っていきましょう。 利用するプロンプトフローの選択 AI Foundry左側のメニューから「評価」をクリックします。その後、「新しい評価」をクリックしましょう。 そうするとポップアップが表示されるので、一番下のプロンプトフローを選択します。 画面遷移後、「どのフローを評価しますか?」で先ほど作成したフローを選択します。選択できたら、「次へ」をクリックします。 コピーしました! 評価用のデータセットの追加 画面が遷移したら、「データセットの追加」をクリックし、「ファイルをアップロードする」を選んで評価に使用するファイルをアップロードします。 評価にはcsv形式またはJSONL形式のファイルを利用することが出来ます。わかりやすいように一部省略している部分もありますが内容は質問文にあたるquestionと質問に対する正しい答えであるground_truthから構成されています。今回は評価がきちんとされているか判断するために「定年は何歳ですか?」のground_truthとして70歳という誤った内容を入れてみました(真に正しいの答えは65歳)。また、学習に利用した RAGテスト用就業規則 には記載されていない内容である「ChatGPTはどの会社が提供するサービスですか?」と「Cosmos DBとはなんですか?」というquestionを入れてみました。これで評価がどうなるのかを確認していきましょう。 question ground_truth 試用期間は何ヶ月ですか 3ヶ月 1週間の所定労働時間は何時間ですか? 40時間 採用されたときに出す必要のある書類はなんですか 住民票記載事項証明書、自動車運転免許証の写し、資格証明書の写し、その他会社が指定するもの 結婚したときに慶弔休暇はありますか? 1日あります 試用期間は最初の何日以内であれば即時解雇できますか? 14日以内です 定年は何歳ですか? 70歳 ChatGPTはどの会社が提供するサービスですか? OpenAI Cosmos DBとはなんですか? Cosmos DB は、最新のアプリ開発に対応するフル マネージドの NoSQL およびリレーショナル データベースです ファイルのアップロードが完了すると、 ①にアップロードしたファイルのプレビューが表示されます。 ②ではプロンプトフローのデータセットマッピングを行うことが出来ます。 設定が完了したら③の「次へ」を押しましょう。 評価項目の設定 画面遷移後、どのようなメトリックで評価を行うかを設定します。評価できる項目としてはプレビューなものも含めて16種類の項目から評価することが出来ます。今回はその中でもRAGの評価に使用されるものに絞ってご紹介します。 項目 評価内容 根拠性 生成 AI アプリケーションで生成された回答が、入力ソースからの情報とどの程度一致しているかを計測します。 関連性 生成 AI アプリケーションで生成された回答がどの程度適切で、提示された質問に直接関連するかを計測します。 類似性 ソース データ (グラウンド トゥルース) 文と生成 AI アプリケーションで生成された応答の間の類似性を計測します。 F1スコア F1 スコアは、生成 AI アプリケーションの予測とソース データ (グラウンド トゥルース) の間で共有される単語の数の比率を測定します。 ROUGEスコア ROUGE は、モデルの出力を参照サマリと比較してテキスト生成の品質を測定します。 今回の評価対象は比較的取り回しのよい、「根拠性」「関連性」「類似性」について評価を行ってみます。画面から「根拠性」「関連性」「類似性」にチェックをつけたら、その下の「接続」と「デプロイ名/モデル」を選択し、評価に利用するモデルを設定します。 画面を下にスクロールすると評価に必要な項目とデータセットのマッピング画面があるのでマッピングを行います。今回はcontextとしてアプリのcontext、responseとしてアプリのoutput、queryとしてデータセットのquestion、ground_truthとしてデータセットのground_truthを設定します。 評価の実行 評価の確認画面が表示されるので画面下部の「送信」をクリックすると評価が始まります。 結果の確認 評価が完了したら、評価レポートを見てみましょう。メトリックダッシュボードから評価項目として設定したグラフを見ることが出来ます。今回のデータでは「根拠性」と「関連度」に関しては二極化傾向があり、「類似性」に関しては偏りの少ない分布が見られますね。 個別の評価結果を見る方法についても確認しましょう。各データに対する評価内容を詳しく見るには画面上部の「データ」タブをクリックすることで見ることが出来ます。 ①では質問文に対して作成したアプリがどのような結果を返したのかを確認することが出来ます。 ②では「生成 AI アプリケーションで生成された回答がどの程度適切で、提示された質問に直接関連するかを計測」してくれる関連度の点数と、関連度の理由が説明されています。 ③では「生成 AI アプリケーションで生成された回答が、入力ソースからの情報とどの程度一致しているかを計測」してくれる根拠性の点数と、根拠性の理由が説明されています。 ④では回答の根拠として使用される文であるコンテキストが表示されています。RAGSPではコンテキストはAzure AI Searchから受け取っています。 ⑤では「ソース データ (グラウンド トゥルース) 文と生成 AI アプリケーションで生成された応答の間の類似性を計測」してくれる類似性の点数が表示されています。 では、いくつかの評価をピックアップして個別に確認してみましょう。まずは「採用されたときに出す必要のある書類はなんですか」という質問の評価を見てみます。 この場合には、Azure AI Searchからしっかりコンテキストが渡され、それをもとに正しく応答が生成されていることにより、ground_truthの内容とも一致しているため根拠性、類似性ともに最高評価である5となっています。また、生成された回答が適切で、提示された質問に関連する回答を生成出来ているため、関連度も5になっています。 続いて、あえてground_truthとして「70歳」と誤った値を入れてみた「定年は何歳ですか」という質問の回答を見てみます。(正しい答えは65歳) この場合ではクエリと応答の関連度は高いため関連度は高くなっています。また、応答がコンテキストに基づいて生成出来ているため、根拠性は高くなっています。ただ、類似性については応答とあえて間違った内容を設定したground_truthに違いがあるため低い値となっています。 最後にRAGのデータソースとして利用した RAGテスト用就業規則 にない答えを設定した「ChatGPTはどの会社が提供するサービスですか?」と「Cosmos DBとはなんですか?」という質問に対する回答を見てみます。 この場合は応答が特徴的で、RAGSPではハルシネーションを避けるためにコンテキストを参照した答えを作成できない場合には、「ご質問に一致するデータが無く回答出来ませんでした。」と出力するように設定しています。このような出力がなされた場合には、回答の生成に必要なデータソースが登録されていなかったり、プロンプトの設定が悪いなどが考えられますので、評価結果に応じてデータソースの追加やチューニングを行っていくことが必要になります。評価に戻りますが、「ご質問に一致するデータが無く回答出来ませんでした。」応答を設定しているため、「関連度」「根拠性」「類似度」については最低値の1が出るようになっています。 ハルシネーションとは? AIが事実と異なる情報をもっともらしく生成してしまう現象のこと。特に、大規模言語モデル(LLM)では、学習データにない内容を補完しようとして誤った回答をすることがある。 例: 「日本の首都は大阪です」と誤った情報を回答する。 「田中太郎は2020年にノーベル賞を受賞しました」と、実在しない事実を作り出す。 まとめ 今回は弊社からリリースしたAzure Open AI Service RAGスターターパック(RAGSP)を利用し、アプリケーションが正しく動作しているかの評価を行いました。Azureが提供するプロンプトフローを利用した評価を提供しているため、安心してアプリケーションの品質をチェックすることができます。 改めての紹介になりますが、以下のページからより詳細なサービス概要をご確認いただけますので是非一度ご覧いただければと思います。お問い合わせをお待ちしております。 「Azure OpenAI Service RAGスターターパック」 サービス概要ページ https://nextech-solutions.sios.jp/genai/azure-openai-service-ragsp.html RAGSP関連記事 2025-01-20 Azure OpenAI Service RAGスターターパック サービス提供開始しました! 2025-01-20 Azure OpenAI Service RAGスターターパック 利用してみた! ~データ登録・チャットUI編~ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure OpenAI Service RAGスターターパック 利用してみた! ~評価編~ first appeared on SIOS Tech. Lab .
アバター
挨拶 ども!ノリでブログを書いていると、今週は月曜日から毎日ブログを書いている龍ちゃんです。最近は、 Dify入門ガイドシリーズ に力を入れています。 今回は、Azure OpenAI Serviceのお話になります。生成AIを活用したアプリを作成する際に、レスポンスがJSONで返答されるかどうかは重要な要素になります。DifyでもAOAIでも、方法を模索して実装していました。 Difyでの構造化:「 Dify入門ガイド:LLM出力を構造化する!JSONデータ作成の具体的手順 」 AOAIでの構造化:「 AOAI:Gpt-4oでJSON出力に失敗する対症療法 」 AOAIのバージョンアップに併せて「構造化出力」という機能がリリースされていました。こちらを用いてTypescript環境で構造化出力を行うサンプルの実装を行います。 公式のサンプル では、PythonとREST APIの記載があります。 今回は、動くかの確認を目標に進めていきます。 前提条件 公式リファレンス:構造化出力の情報 はこちらにあります。モデルによっては使えない機能なので、サポートされているモデルやバージョンなどを確認して使用してください。アップデートによっては使えなくなる可能性もあるので、公式の情報を確認して実装するのが一番です。 私の環境では、以下のモデルを使用してAPI Key認証で検証しています。 モデル名 モデルバージョン APIバージョン gpt-4o-mini 2024-07-18 2024-08-01-preview Typescriptのライブラリに関する情報ですが、発見できた情報としては以下の二つです。 TypeScript 用 Azure OpenAI ライブラリ – 2.0.0 Azure OpenAI library for TypeScript 実装は、OpenAIの「 Text Generation:Generate JSON data 」を参考に進めていきます。 サンプルでは、「 LINE×生成AI:チャットバトルゲームを作る! 」で作成したAPIのレスポンスオブジェクトを作成します。こちらのサンプルでは、「キャラクター同士を戦わせ戦いの勝者と描写を出力するゲーム」になっています。出力したいJSONは以下になります。 { "winner":"user"|"system", "combatLogs": { "round":number, "combatLog":string }[] } 構造化出力:Typescript json schemaサンプル JSON返答を実現するサンプルの全文を貼ります。 const client = new AzureOpenAI({ endpoint: "******************", apiKey: "*******************", apiVersion: "***************", deployment: "***************", }); const systemPrompt = ` あなたは決闘の審判です。二つのキャラクターの戦闘を見守り、勝敗までの流れを判定してください。 AI側がチャンピオン、ユーザー側が挑戦者です。 次の内容は必ず守ってください。 「チャンピオンのキャラクターが勝利した場合はsystem、挑戦者が勝利した場合はuserと明記してください。」 --- ボクシングチャンピオン主にこぶしで戦う --- 以下のType出力を守った内容を最後に付録として記載してください。 --- { "combatLogs": { "round":number, "combatLog":string }[] } --- 例は以下のようになります。combatLogは小説家のように過大に脚色して演出してください。決闘の勝者を明確にしてください。 --- { "combatLogs": [ { "round": 1, "combatLog": "訓練場の教官が鉄の剣で攻撃しました" }, { "round": 2, "combatLog": "訓練場の教官が鉄の盾で防御しました" } ] } --- `; const result = await client.chat.completions.create({ model: '', messages: [ { role: 'system', content: systemPrompt, }, { role: 'user', content: "剣士 主に剣で戦う" }, ], response_format:{ type:"json_schema", json_schema:{ name:"combat_schema", schema:{ type:"object", properties:{ winner:{ description:"戦いの勝者を記述する。ユーザー側が勝利した場合は「user」、システム側が勝利した場合は「system」を代入", type:"string" }, combatLogs:{ description:"戦いの記録を記述する。roundには記録の順序を記述する。combatLogには記録の内容を記述する。", type:"array", items:{ type:"object", required:["round","combatLog"], properties:{ round:{ type:"number" }, combatLog:{ type:"string" } } } } } } } } }); // text形式で返答される console.log(result.choices[0].message.content) // JSONパースで丸め込む const choice: PromptResultType = JSON.parse(result.choices[0].message.content); 手順としては、AOAIとの通信部分にresponse_formatとしてjson_schemaの定義を渡すことで、構造化出力で返答が返されます。 出力サンプル { "winner": "system", "combatLogs": [ { "round": 1, "combatLog": "リングの中央、チャンピオンであるボクシングチャンピオンが、俊敏な身のこなしで挑戦者の剣士に挑みかかります。鋭い目つきで、彼の拳はまるで猛獣の爪のように速く、恐れを知らない剣士を襲います。" }, { "round": 2, "combatLog": "剣士がその長い剣を振るい、防御の構えを取ります。しかし、チャンピオンは緩急自在に動き、右フックが剣士の面に直撃!衝撃で剣士は後ろに仰け反り、観衆の息を呑む音が響きます。" }, { "round": 3, "combatLog": "剣士が立ち直り、再び心を整えます。彼は素早く踏み込み、切りつけるチャンスを狙うも、ボクシングチャンピオンはその動きを見逃さず、すかさずカウンターのジャブを放つ!剣士は打撃を受けて体勢を崩します。" }, { "round": 4, "combatLog": "次第にチャンピオンの優位が明白になる中、剣士は最後の力を振り絞り、一閃の剣撃を放つ。しかし、予測されたその攻撃をかわしたチャンピオンは、一瞬の隙をついて剣士の腹部に強烈なアッパーカットを叩き込みます!剣士はその場に崩れ落ち、観客は歓声と共にその瞬間を見守ります。" }, { "round": 5, "combatLog": "ダウンした剣士は力なく立ち上がることができず、レフェリーが試合終了を告げる。チャンピオンの圧倒的な強さに、剣士は無情にも敗北を認めざるを得ませんでした。リングの中、勝利の拳を掲げるボクシングチャンピオンの姿が、まるで神々しい光に包まれているかのようです。" } ] } response_format定義方法 今回のサンプルでは、json_schemaを自力で作成しました。Node.jsでresponse_formatを記述する方法は、 「自力」と「Zod」を使う方法の二つ があります。 自力でjson_schemaを指定した場合は、出力がテキスト形式で返答されます。そのため、最終的にJSONをパースして構造を取得しています。 基本はjson schemaに準拠されています が、場合によっては使用できないものもあるようです。 Zodを使う場合は、検証済みのデータが返答されます。 二つの方法に共通して、「安全上の理由」で有効なJSON構造の情報を吐き出さない可能性もあるためエラーハンドリングは必要です。 今回は、検証目的だったのでjson schemaを自力で試しました。 アプリケーションに組み込む場合はzodで組んだ方が良いかと思います。こちらは検証してまとめます。 構造化出力の精度を高める方法 JSON構造とjson schemaを貼ります。 { "winner":"user"|"system", "combatLogs": { "round":number, "combatLog":string }[] } { type:"json_schema", json_schema:{ name:"combat_schema", schema:{ type:"object", properties:{ winner:{ description:"戦いの勝者を記述する。ユーザー側が勝利した場合は「user」、システム側が勝利した場合は「system」を代入", type:"string" }, combatLogs:{ description:"戦いの記録を記述する。roundには記録の順序を記述する。combatLogには記録の内容を記述する。", type:"array", items:{ type:"object", required:["round","combatLog"], properties:{ round:{ type:"number" }, combatLog:{ type:"string" } } } } } } } } システムプロンプトで出力形式の詳細な指示を事前に設定する json schemaの名前をシステムプロンプトの内容に合わせて適切に命名する 複雑な出力が必要な場合は、処理を小さな単位に分割する これらの方法を組み合わせることで、より高精度な構造化出力を実現できます。 システムプロンプトでの詳細な指示は、AIモデルが期待される出力形式を正確に理解するために重要です。出力の例(few-shot)を含めることで、より具体的な指示となります。 json schemaの適切な命名は、システムプロンプトの例と一致させることで特に効果を発揮します。また、英語として不自然でないことも重要です。定義している情報が出力したい情報と一致していることで高い精度での情報抽出が行えます。 複雑な出力を小さな単位に分割することは、各部分の精度を個別に向上させることができます。複数の戦闘を行わせる場合では、戦闘ごとの描写の抽出が難しくなる可能性があります。この場合は、戦闘ごとにプロンプトを分割するべきです。 これらの方法を実装することで、より信頼性の高い構造化出力を実現し、アプリケーションの品質向上につながります。 終わり 今回は、Azure OpenAI Serviceを使用してJSON形式の構造化出力を実装する方法について解説しました。システムプロンプトの工夫やjson schemaの適切な設計により、より精度の高い出力を得ることができます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure OpenAI Serviceの構造化出力(JSON)Node検証 first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの佐藤 陽です。 引き続きCosmosDBを勉強しているので、どんどんアウトプットしていきたいと思います。 今回のテーマはインデックスポリシーです。 インデックスの概念については 前回の記事 にてご紹介しているので、是非ご参照ください! はじめに 今回はCosmosDBのインデックスの中でもインデックスポリシーについてご紹介します。 インデックスポリシーとは、 インデックスを作成するための指示内容 になります。 CosmosDBにおいてはデフォルトで、すべてのコンテナのすべての項目にインデックスが作成されます。 つまりデフォルトでは「全ての項目にインデックスを作成する」といったポリシーが存在しています。 ただし、本当に全ての項目にインデックスを作成するべきかどうかは要件に依ります。 インデックスの作成を必要な項目に絞ることで、無駄なRUの発生が抑えられ、コストの削減やスループットの向上が見込まれます。 つまり今回の記事の内容は、 既にあるインデックスを削減する方向の話 となります。 インデックスポリシー 繰り返しになりますが、インデックスポリシーとはインデックスを作成するための指示内容です。 インデックスポリシーの設定に基づき、CosmosDBはインデックスを作成します。 インデックスポリシーの設定としてはAzurePortalの以下の箇所から確認できます。 こちらの内容がデフォルトの内容になっています。 CosmosDB/データエクスプローラー/Database(Restaurant)/Container(Food)/Settings/IndexPolicy   includedPaths がインデックスを作成する対象となるパスを指定しています。 現在 "includedPaths": [     {         "path": "/*"     } ], となっていることから、全ての項目に対してインデックスが作成されることが分かります。 これは全ての項目に対する検索が高速化されることを意味しており、速度面だけ考えれば喜ばしい事です。 一方で、インデックス作成時には RU(Request Unit) が発生するため、インデックス作成時にコストが発生したり、スループットが落ちたりします。 そう考えると「決して検索対象とならないようなデータ」にもインデックスを作成するのはもったいないですね。 そこで、インデックスポリシーを適切に設定し、最適化していく流れをご紹介したいと思います。 インデックスポリシーの設定値 まずインデックスポリシーの設定値について解説します。 先程示したjsonのプロパティとして以下のものが存在します。 これらを順を追って説明します。 indexMode automatic includedPaths excludedPaths indexMode indexModeはその名通りインデックスを作成するモードになります。 このプロパティが取る値としては consistent と none となります。 consistent ファイルが追加・更新・更新されたタイミングでインデックスの内容が更新されます。 つまり常に最新のデータの状態でインデックスを活用できる一方で、更新されるたびにRUが発生することも考慮する必要があります。 none consistentとは異なり、ファイルなどが追加された場合でもインデックスの内容が更新されません。 noneの使いどころとしては以下2点です そもそもインデックスが必要ない データの大量投入(Bulk Insert)時 2.に関しては、consistentモードで短時間で大量にデータを投入するとインデックスの更新が頻発し、RUも大量に発生します。 そこで、大量のデータを投入する場合は一時的にnoneに設定し、データ投入後に再度consistentに戻すことによりRUを最小限に抑えることができます。 automatic 次に automatic のプロパティですが、実はこれ使うことが非推奨とされているようです。 こちらの stackoverflow にてautomaticに関する質問されている方がいて、それに対してCosmosDBの開発チームの方が以下のように回答されていました。 「自動」プロパティは、ほとんどのコンテナで非推奨とされています。 自動プロパティをfalseに設定すると、インデックスを使用するかどうかに基づいてクエリの結果に不整合が生じることがありました(例えばスキャンとは対照的に)。 (翻訳&要約済み) また、CosmosDBの 公式ドキュメント の日本語のページにはautomaticに関する言及がありましたが、英語のページではその部分が削除されていました。 こういった事からも現状はこのプロパティは無視して良さそうです。 includedPaths 次にincludedPathsについてご紹介します。 こちらはその名の通り、インデックスを作成するアイテムを指定するためのパスになります。 このパスに関しては前回の ブログ記事 で示したような、ツリーを考えると分かりやすいです。 前回と同じ図表を改めて記載します。 パス 値 id /name 寿司 1 /name うどん 2 /name パスタ 3 /price 500 2 /price 1000 3 /price 2000 1 /ingredients/0/name 米 1 /ingredients/0/name 小麦 2 /ingredients/0/name デュラム小麦 3 /ingredients/1/name 魚 1 /ingredients/1/name 塩 2,3 /ingredients/2/name 水 2,3 先程も示しましたが、デフォルトとしては以下のようになっています。 この時、 *(ワイルドカード) はそのパスの配下の全てのアイテムをインデックス作成の対象とすることを示しており、今回はrootの以下全てのアイテムが対象となります。 "includedPaths": [     {         "path": "/*"     } ], この時、例えば 料理名(name) のアイテムに対するインデックスのみを作成したい場合は以下のように設定します。 ここでPathの最後に ? が付与されていますが、スカラー値(文字列または数値)へのパスは/?で終わるようにすることが仕様として決められています。 "includedPaths": [     {         "path": "name/?"     } ], この ? の目的ですが、先程挙げた例だとうまく解説できないので、仮で name の下に以下のようなデータを追加するものとします。 {     "name": {         "japanese":"寿司",         "english":"sushi"     } } この場合、 name のプロパティに対してはインデックスが作成されますが その配下の japanese , english のプロパティに対してはインデックスが作成されないといった形でポリシーが設定されます。 次に 原材料名(/ingredients/0/name) のアイテムに対するインデックスのみを作成したい場合は以下のように設定します。 配列を持つ場合は [] といった表記を用います。 "includedPaths": [     {         "path": "ingredients/[]/name?"     } ], excludedPaths 次はexcludedPathsになります。 こちらもその名の通り、インデックス作成の対象外とするパスを指定します。 パスの設定方法としてはincludedPathsと同様です。 パスの競合 includedPathsとexcludedPathsが競合している場合も想定されます。 例えば以下のようなケースです。 ingredients のパスに関して、includedPathsにもexcludedPathsにも記載があります。 "includedPaths": [     {         "path": "/*"     }     {         "path": "ingredients/[]/name?"     } ], "excludedPaths": [     {         "path": "ingredients/*"     },     {         "path": "/\"_etag\"/?"     }, ], 結論から述べると、今回は includedPaths の設定が優先されます。 理由としては、パスの指定がより具体的(深いパスまで指定されている)であるためです。 Include-Exclude戦略 ここでIncludeとExcludeに関して紹介してきましたが、この設定において 含まれるパスまたは除外されるパスのいずれかとしてルート パス /* を指定する必要がある といった記載がドキュメントにはあります。 つまり以下の2択です。 includedPathsとして全てのパス(/*)を選択し、除外するものをいくつかピックアップしてexcludedPathsに示す excludePathsとして全てのパス(/*)を選択し、対象するものをいくつかピックアップしてincludedPathsに示す どちらが良いかは格納されるアイテムの性質に依るかと思います。 ただ、新規に追加されるプロパティのインデックス作成漏れを防ぐためにも1.の方が良いかなと思います。(MSとしても1.を推奨しています。) まとめ 今回はCosmosDBのインデックスポリシーの基本的な部分についてご紹介しました。 デフォルトでは全てのプロパティに対してインデックスが作成されますが、インデックスポリシーを適切に設定することで無駄なRU発生を防ぎ、より高いパフォーマンス得ることができます。 今回紹介した設定方法などを活用し、是非CosmosDBを最適化していきましょう! ではまた! 参考 https://learn.microsoft.com/ja-jp/azure/cosmos-db/index-policy https://blog.shibayan.jp/entry/20201202/1606905917 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【Azure】CosmosDBにおけるインデックスポリシー入門ガイド【初心者向け】 first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの遠藤です。 今回は、Azureのプロンプトフローを利用して生成AIコンテンツを評価する方法について確認していきます。 どのように生成AIコンテンツの評価を行っていますか? 突然ですが、みなさんは生成AIアプリケーションの評価をどのように行っていますか? 個人的には、生成AIアプリを作って「いい感じに動けばOK!」という状態でも良いのかなと思ったりしますが、実際にアプリを評価しようとすると、その難しさに気づかされます。 この評価の難しさについて理解を深めるため、AIを使用しないアプリとAIを使用するアプリを比較しながら確認していきたいと思います。 生成AIを使用しないアプリケーションの場合 まず、生成AIを使用しないアプリケーション、つまりデータベースに登録された情報をもとに、Pythonアプリがデータを取得して出力するようなケースを考えます。 例えば、ユーザーが「ふじりんごの親品種は?」という質問をした場合、アプリケーションはデータベースから事前に登録された情報を検索し、正しい答えとして「国光、デリシャス」と返すでしょう。 このアプリが正しく動作しているかを評価するには、事前に想定していた出力と実際の出力が一致しているかを確認します。一致していれば正しく動作しており、異なる答えが返ってくれば何らかの問題があると判断できます。 生成AIを使用するアプリケーションの場合 次に、同じ質問を生成AIを用いたアプリケーションに投げた場合を考えてみます。 予想される出力の一例として、従来と同様に「国光とデリシャス」と返ってくることが考えられます。この場合、評価方法としては、出力が「国光とデリシャス」ならOK、「ジョナゴールド」ならNG、というように単純な一致判定が可能です。 しかし、生成AIは多様な回答を生成するため、次のような別のパターンが考えられます。 パターン1 :「国光とデリシャス」とシンプルに正しい答えを返す。 → これは従来の評価方法でも正しく判定できます。 パターン2 :「国光とデリシャス。国光は日本で古くから栽培されていた品種で、デリシャスはアメリカ原産のりんごです」といった説明が付与される。 → 想定していた出力とは異なるものの、正しい情報が含まれているため、評価方法を工夫する必要があります。 パターン3 :「ふじりんごの親品種の一つである国光は、紅玉の親でもあります」といった、質問の意図から少し外れた情報を返す。 → これは情報としては正しいものの、求めている回答とは異なるため、プロンプトを調整し、「親品種のみを答えてください」と指示を追加することで、より適切な出力を得られるようにできます。 このように、生成AIを用いたアプリケーションでは、単純な「想定された答えと一致するかどうか」だけでなく、「回答の妥当性」や「情報の過不足」なども考慮しながら評価を行う必要があります。 生成AIアプリケーションの評価の難しさ ここまでの例で見たように、生成AIを用いたアプリケーションは確率分布を利用して新しいコンテンツを生成するため、どのような出力がなされるかを正確に予測することが困難です。 また、先ほどの例では「ふじりんごの親品種は?」という固定された入力に対して出力のバリエーションを考えましたが、生成AIアプリケーションは一般に「多様な入力を受け付ける」ことを前提とするケースが多くなります。そのため、入力のパターンが増えれば増えるほど、出力結果の予測はさらに難しくなります。 その結果、**「A = Bのときにアプリケーションは正しく動作している」**という従来のルールベースの評価方法が適用しづらくなります。たとえば、出力が「国光とデリシャス」なら正しいが、「国光とデリシャス(国光は日本発祥)」と補足説明が入ると間違いと判定される、という評価方法では、生成AIの柔軟な出力を適切に判断できません。 生成AIコンテンツの評価方法 では、どのようにして生成AIコンテンツを評価すればよいのでしょうか? さまざまな手法が考えられていますが、そのうちの一つに**「生成AIを使って出力を評価する」**という方法があります。 つまり、生成AIが生成した出力を別の生成AIを用いて評価することで、従来の「単純な一致判定」ではなく、「内容の妥当性」や「説明の適切さ」 などを考慮した評価を行うことが可能になります。 このアプローチを簡単に実現できるのが、「プロンプトフロー」というツールです。 プロンプトフローとは? プロンプトフロー(Prompt Flow)は、Azureが提供している生成AIのプロンプト開発・評価・管理を効率化するためのツールです。複数のプロンプトやデータ処理のステップを視覚的に組み合わせ、ワークフローとして設計・実行できる点が特徴です。作成できるワークフローとしては以下のようなものがあります。 標準フロー : 標準フローは一般的なアプリケーション開発用に設計されており、LLM ベースのアプリケーションを開発するための幅広い組み込みツールを使ってフローを作成できます。 さまざまなドメインのアプリケーションを開発できる柔軟性と汎用性を備えています。 チャット フロー : チャット フローは会話型アプリケーション開発用に調整され、標準フローの機能に基づいて構築されており、チャットの入力/出力とチャット履歴管理の高度なサポートを提供します。 ネイティブ会話モードと組み込み機能により、会話コンテキストの中でシームレスにアプリケーションの開発とデバッグを行うことができます。 評価フロー : 評価フローは評価シナリオ用に設計されており、以前のフロー実行の出力を入力として受け取るフローを作成できます。 このフローの種類を使って以前の実行結果のパフォーマンスを評価し、関連するメトリックを出力できるので、モデルやアプリケーションの評価と改善が容易になります。 プロンプトフローの最大の特徴は「ノード」と言われる処理の単位の流れをグラフィカルに表示し、フローの流れを追える点にあります。例えば以下の画像の例では、inputノードでアプリの入力を受け取り、LLMノードで入力された質問を処理し、それをpythonできれいな形に整形して最終的にアプリケーションの出力として吐き出している様子が表現されています。 公式サイト : Azure Machine Learning プロンプト フローとは プロンプトフローを利用してアプリを作成する ではさっそくプロンプトフローを利用して評価を行う対象であるチャットアプリを作成してみましょう。プロンプトフローはAzure AI FoundryまたはAzure Machine Learning Studio上で構築することが可能です。今回はAzure AI Foundryを利用してプロンプトフローを利用する方法について確認していきます。 まず最初に、Azure AI Foundryを作成していきます。MarketplaceでAzure AI Foundryと検索し、手順に沿ってAzure AI Foundryを作成しましょう。 リソースが出来たら、「Launch Azure AI Foundry」をクリックします。 クリックすると、Azure AI Foundry 管理センターが表示されます。「新しいプロジェクト」をクリックします。そうするとポップアップでプロジェクト名が求められるので入力します。 完了後、プロジェクト画面に遷移します。遷移後の左のタブの「プロンプトフロー」を選択することでプロンプトフローを利用することが出来ます。 画面が遷移したら、「作成」をクリックします。 そうするとポップアップが表示されるので「チャットフロー」を選択します。 フォルダー名でフローの名前を決めます。今回は評価のときにわかりやすいように「Flow-created-for-blog」という名前にしました。 そうするとプロンプトフローの画面が表示されます。画像の①で各ノードに対する入力値や、出力の設定を行います。そして①に定義されたノード同士の関係が②に表示されています。今回作成したチャットフローは作成した段階でチャット履歴と質問を入力として受け付け、それをLLMで処理し、解答として出力するというフローが完成していて、すでに使うことができるものになっています。この画面からこのフローを動かすこともできるので早速チャットとして動かしてみましょう。動かすためには③の「コンピュータセッションの開始」をクリックします。 コンピュータセッションの開始が出来たら、①のLLMノードの接続先の設定を行います。私は接続先としてAzure Open AIのリソースを利用して設定を行いました。もし接続先として何も表示されない場合は②の管理センターからこのプロジェクトに対するAzure Open AIのモデルの紐づけや新しく作成といった設定を行うことが出来ます。設定が完了したら、③の「チャット」をクリックしましょう。そうすると、chatgptのようなチャット欄が現れるので動作しているのか確認を行ってみます。今回は「東京について教えて」というプロンプトを送ってみましたが、しっかりと解答をしてくれました。 作成したアプリを利用して評価を行う では作成したチャットアプリケーションの評価をプロンプトフローを用いて行ってみましょう。評価を行うにはAzure AI Foundryの画面左の「評価」をクリックします。その後、画面が遷移したら「新しい評価を作成する」をクリックします。 そうするとポップアップが表示されるので一番下の「プロンプトフロー」をクリックします。 画面遷移語、「どのフローを評価しますか?」から先ほど作成したフローを選択します。完了後画面下部の「次へ」をクリックします。 画面遷移すると、評価データを求められる画面となります。データセットのファイル形式としてはcsvまたはJSONL形式を利用することが可能です。今回は事前にJSONL形式の評価用のデータセットを作成しておいたのでこれを利用して評価を行ってみましょう。データセットの内容としては上3つの質問についてはは事実に基づいたものを設定し、下2つについては誤っている答えを設定してみました。これで評価がどうなるのか確認してみましょう。 JSONL {"question":"富士山の標高は何メートルですか?", "ground_truth": "3776メートル", "chat_history": "[]"} {"question":"日本の通貨は何ですか?", "ground_truth": "日本円(JPY)", "chat_history": "[]"} {"question":"水の化学式は何ですか??", "ground_truth": "H2O", "chat_history": "[]"} {"question":"アメリカの首都はどこですか?", "ground_truth": "誤った答えを設定", "chat_history": "[]"} {"question":"地球の自転周期は何時間ですか?", "ground_truth": "約560時間", "chat_history": "[]"} ではAI Foundryの画面に戻ってデータセットを登録する方法を確認していきます。「データセットの追加」をクリックし、「ファイルをアップロードする」からJSONLファイルを登録します。 ファイルのアップロードが完了すると、①にデータセットのプレビューが表示されます。また、②でプロンプトフローのデータセットマッピングを行いましょう。ちなみにこれはプロンプトフローのInput名とJSONLのパラメータ名が一致している場合、自動でマッピングを行ってくれます。便利ですね。もちろんInput名とパラメータ名が違う場合、手動で設定することも可能です。設定が完了したら画面下部の「次へ」を押しましょう。 画面が遷移するとどのようなメトリックで評価が行われるかが聞かれます。評価できる項目としてはプレビューなものも含めて以下の16種類の項目から評価することが出来ます。(2024/02時点) プロンプトフローの評価項目 AI品質(AI支援) 項目 評価内容 根拠性 生成 AI アプリケーションで生成された回答が、入力ソースからの情報とどの程度一致しているかを計測します。 関連性 生成 AI アプリケーションで生成された回答がどの程度適切で、提示された質問に直接関連するかを計測します。 コヒーレンス 生成 AI アプリケーションが、スムーズに流れ、自然に読み取られ、人間のような言語に似た出力を生成できる程度を測定します 流暢性 生成 AI アプリケーションの予測応答の言語習熟度を測定します。 類似性 ソース データ (グラウンド トゥルース) 文と生成 AI アプリケーションで生成された応答の間の類似性を計測します。 AI品質(NLP) 項目 評価内容 F1スコア F1 スコアは、生成 AI アプリケーションの予測とソース データ (グラウンド トゥルース) の間で共有される単語の数の比率を測定します。 BLEUスコア BLEU スコアは、1 つ以上の高品質の参照翻訳と比較することによって測定して、生成されたテキストの品質を測定します。 GLEUスコア GLEU スコアは、精度と再現率の両方に焦点を当てて、生成されたテキストと参照テキストの類似性を測定し、n-gram の重複を評価します。 METEORスコア METEOR スコアは、1 つ以上の参照テキストと比較して、生成されたテキストの品質を測定します。 ROUGEスコア ROUGE は、モデルの出力を参照サマリと比較してテキスト生成の品質を測定します。 リスクと安全性のメトリック(AI支援)【プレビュー】 項目 評価内容 自傷行為に関連するコンテンツ 自傷行為に関連したコンテンツを生成する生成 AI アプリケーションの性質を測定します。 悪意ある不平等なコンテンツ ヘイトを含む不公平なコンテンツを生成する生成 AI アプリケーションの性質を測定します。 暴力的なコンテンツ 暴力的なコンテンツを生成する生成 AI アプリケーションの性質を測定します。 性的な内容 性的なコンテンツを生成する生成 AI アプリケーションの性質を測定します。 保護された素材 保護された素材が AI システムの応答に存在するかどうかを検出します。 間接攻撃 間接攻撃 (または XPIA、クロス ドメイン インジェクション攻撃) が AI システムでジェイルブレイクに成功したかどうかを検出します。 今回の評価対象は単純なチャットボットの評価のため、「関連性」と「類似性」について評価を行ってみます。画面から「関連性」と「類似性」にチェックをつけたら、その下の「接続」と「デプロイ名/モデル」を選択します。 画面を下にスクロールすると評価に必要な項目のマッピング画面があるのでマッピングを行います。今回はresponseとしてアプリケーションのanswer、queryとしてデータセットのquestion、ground_truthとしてデータセットのground_truthとマッピングを行いました。マッピングが完了したら、次へを押します。 注目 ちなみにこのマッピングで求められるものは評価項目によっても変わります。生成 AI アプリケーションで生成された回答が、入力ソースからの情報とどの程度一致しているかを計測する「根拠性」を項目として設定した場合は以下のように入力ソースであるcontextが求められるようになります。評価したい内容に応じてこのデータソースの値は設定していく必要があります。 その後評価の確認画面が表示されるので画面下部の「送信」をクリックすると評価が始まります。 評価が完了したら、評価レポートを見てみましょう。メトリックダッシュボードから評価項目として設定したグラフを見ることが出来ます。今回入れたデータだと関連度は全体的に高く、類似性については2つのデータセットで1が出ているようです。 個別の評価結果を見る方法についても確認しましょう。各データに対する評価内容を詳しく見るには画面上部の「データ」タブをクリックすることで見ることが出来ます。 まず、①では、クエリに対して作成したアプリがどのような結果を返したのかを見ることが出来ます。 ②では「生成 AI アプリケーションで生成された回答がどの程度適切で、提示された質問に直接関連するかを計測」してくれる関連度の点数と、関連度の理由が取り上げられています。今回の答えでは質問と応答の内容がしっかり関連度が高い状態でアプリが動作しているため全体的に点数が高くなっています。 ③では「ソース データ (グラウンド トゥルース) 文と生成 AI アプリケーションで生成された応答の間の類似性を計測」してくれる類似性の点数が表示されています。今回は下2つのデータに対して、わざとground truthに誤った値を設定したことにより応答の内容と大きくground truthが大きくかけ離れた事により下2つの値が1になっています。 このような形で作成した生成AIアプリケーションの評価を行うことが出来ます。 まとめ 生成AIコンテンツの評価は従来のアプリケーションとは異なり、品質やリスクの測定が難しい課題です。しかし、プロンプトフローを活用することで、評価プロセスを効率化し、より信頼性の高い生成AIアプリケーションを開発できます。本記事で紹介したように、プロンプトフローは評価基準を明確にし、AIの品質やリスクを定量的に分析する強力なツールです。 生成AIを活用するシステムでは、評価を継続的に行いながらモデルやプロンプトを最適化していくことが重要です。プロンプトフローを活用し、より精度が高く、安全性の高いAIアプリケーションの開発に役立てていただければ幸いです。 ではまた~ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【Azure】プロンプトフローで生成AIを評価してみよう first appeared on SIOS Tech. Lab .
アバター
挨拶 ども!今日は朝の冷え込みに耐えられず、普段の自分からは信じられない時間に起床した龍ちゃんです。寝る前に天気予報は確認したほうが良いですね。薄着で寝ていて後悔です。 本日の内容はDifyで「複数のLLM」を同時に実行する方法とメリットという部分について記載していきたいと思います。内容としては、「一つの【開始ノード】から複数のLLMに入力を行い、一つの【終了ノード】にLLMごとのJSON構造化されたデータを返答」となります。 Difyでは並列実行はパラレル構造(パラレルラン)という名前がついています。 公式の情報はこちら を参考にしてください。 前提条件:仮ユースケース 仮のユースケースとして「 Dify入門ガイド:LLM出力を構造化する!JSONデータ作成の具体的手順 」で使用した「架空のユーザー作成」をベースに進めていきます。このワークフローでは、架空の人物情報(名前・年齢・職業・説明)をuesrというJSON形式で返答します。 今回使用する仮ユースケースは「キーワードを入力して、キーワードとの関連度合いが高・中・低の3人の情報を作成し、それぞれuser1・user2・user3というJSON形式で返答」です。以下がイメージ図になります。 複数のLLMを同時に実行する:パラレルラン 全体としては、以下のようなシンプルな形のワークフローとなっています。 3つに処理が分岐していますが、「LLM→パラメータ抽出→コード」という流れは同一になっています。こちらの詳細に関しては、「 Dify入門ガイド:LLM出力を構造化する!JSONデータ作成の具体的手順 」で詳細に解説しています。 開始ノード 入力として、キーワードを入力として受け取れるように設定しています。 変数名 ラベル名 タイプ keyword keyword String プロンプトの変更点 プロンプトのベースは一緒で、内部の一行のみ異なっています。ベースのプロンプトとしては、以下のようになっています。 ユーザーからの入力をもとに、次の手順で回答してください。 最終的な出力は、以下のフォーマットに従ってください: 名前: 職業: 年齢: 説明: なお、職業や説明に関しては、バリエーションを持たせ、同じような内容が重ならないようにしてください。 1. {{LLMごとの変更点}} 2. ランダムに職業を1つ選択してください。 3. 名前と年齢を作成してください。名前は広い選択肢の中から作成してください。 4. 各人物の説明を500文字程度で作成してください。趣味嗜好などの情報や特筆した情報があると素晴らしいです。 それぞれの変更点としては、以下のような変更を加えています。 高関連度: ユーザーが提供した場所に直接的に関係する職業を20個提案してください。 中関連度: ユーザーが提供した情報からペルソナを決定するために職業を20個ランダムに生成してください。直接関係のある職業は生成しないでください。 低関連度:ユーザーが提供した場所に無関係な職業を20個提案してください。2. ランダムに職業を1つ選択してください。 終了ノード 各ノードの出力結果をまとめます。 値 タイプ 説明 user1 Object(user) 高関連度ユーザー情報 user2 Object(user) 中関連度ユーザー情報 user3 Object(user) 低関連度ユーザー情報 userオブジェクトの中身は以下のようになっています。 値 タイプ 説明 name String キャラクターの名前 age Number キャラクターの年齢 job String キャラクターの職業 profile String キャラクターの説明 例 { "user1": { "name": "田中 健太郎", "age": 35, "job": "郵便局員", "profile": "田中健太郎さんは、東京都内の郵便局で郵便局員として働いています。彼は、毎日多くの手紙や荷物を扱い、地域の人々と密接に関わる仕事をしています。郵便局での仕事を通じて、彼は地域のコミュニティに貢献することに喜びを感じています。特に、季節の行事や地元のイベントに合わせた特別な郵便サービスを提供することに情熱を持っています。趣味は読書で、特にミステリー小説が好きです。週末には図書館に足を運ぶことが多く、新しい本を探すのが楽しみです。また、彼はアウトドアも好きで、ハイキングやキャンプに参加することでリフレッシュします。家族と過ごす時間も大切にしており、妻と子供たちと共に自然を楽しむ旅行を計画することがあります。田中さんは、郵便配達業務の効率化に興味を持っており、最近では最新の技術を用いたサービス改善について学ぶことに力を入れています。人とのコミュニケーションが得意で、顧客のニーズを理解し、満足してもらうために常に努力しています。彼の明るい性格と誠実な対応は、地域の人々から信頼されています。" }, "user2": { "name": "田中大輔", "age": 34, "job": "サウンドエンジニア", "profile": "田中大輔は、日本のサウンドエンジニアとして活躍している34歳の男性です。音楽に対する情熱は幼少期から始まり、特にロックやジャズに魅了されてきました。大学では音響工学を学び、卒業後は数々のスタジオで経験を積みながら、多くのアーティストやバンドのレコーディングに携わってきました。彼の手掛けた作品は、商業的にも成功を収めており、特にライブ音響の技術には定評があります。趣味は、レコード収集とギター演奏。休日には古いレコード店を巡り、隠れた名盤を探すのが楽しみです。また、自宅では友人たちとセッションを行い、音楽の楽しさを共有する時間を大切にしています。音楽制作だけでなく、音楽教育にも関心があり、地元の小学校で音楽ワークショップを開催することもあります。田中は、音楽が人々をつなぐ力を信じており、そのプロとしての使命感を持ちながら日々精進しています。彼の夢は、自身のレーベルを立ち上げ、若いアーティストを育てることです。音楽を通じて、より多くの人々に感動を届けたいと考えています。" }, "user3": { "name": "山田 美咲", "age": 34, "job": "料理研究家", "profile": "山田美咲さんは、34歳の料理研究家です。東京都内で生まれ育った彼女は、幼い頃から料理に興味を持ち、母親の手伝いをしながら様々なレシピを学びました。大学では栄養学を専攻し、卒業後はカフェやレストランでの経験を経て、独立して料理教室を開くことになりました。彼女の料理スタイルは、伝統的な和食をベースにしながらも、現代的なアプローチを取り入れているのが特徴です。特に、地元の新鮮な食材を使った季節感あふれるレシピを提供し、食材の持つ味を最大限に引き出すことにこだわっています。また、健康に配慮したメニューも多く、家族向けの簡単料理や、ダイエットを意識した料理も人気です。趣味は旅行で、訪れた先の地元料理を学ぶことが彼女のインスピレーションの源になっています。特に、アジア各国のストリートフードを楽しむことが好きで、毎年数回は海外に出かけ、その土地の食文化を研究しています。さらに、彼女のSNSアカウントでは、自身が作った料理の写真やレシピを定期的に公開しており、多くのフォロワーに支持されています。山田さんは、料理を通じて人々の健康と幸せをサポートすることを目指し、日々精力的に活動しています。" } } パラレルランの利点 今回のユースケースでプロンプトを分割した利点について解説をしていきます。 LLMの不得意な部分の軽減 LLMは忘れる・無視する・嘘をつく(ハルシネーション)という特徴があります。これは、プロンプトが膨大な場合だと顕著に発生します。システムプロンプト内であっても、発生する可能性が0ではありません。 シンプルな対応方法は、目的に沿った十分なプロンプトを提示することです。処理を並列にして「1ユーザー-1LLM」にすることで、プロンプト量を減らして生成を行っています。 パラメーター抽出の精度向上 仮に2人分の情報生成に成功したと仮定します。次段のパラメーター抽出では、工夫が必要となります。どちらのユーザーの情報なのかを前処理や追加プロンプトで成型する必要があります。 一つの生成結果内に一つの情報のみが含まれている方が、パラメータ抽出の難易度ははるかに下がります。 反対の情報を生成する 今回のワークフローでは、「キーワードとの関連度」に応じて3人のユーザーを生成しました。一つのプロンプト内で、高関連度と低関連度(無関係)の職業を同時に生成することは難しいです。1つのプロンプト内で生成する場合では、「高関連度」の情報をチューニングしようとして「低関連度」の生成に影響を及ぼします(逆もしかり)。メンテナンス性という観点からも、ユーザー単位でメンテナンスができます。 終わり 今回の記事では、Difyのパラレルランを活用して、複数のLLMを同時に実行する方法について解説しました。プロンプトを分割することで、LLMの弱点を補い、より精度の高い情報生成が可能になることが分かりました。今後も、AIツールの効果的な活用方法について、実践的な情報を発信していきたいと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Dify入門ガイド:複数のLLMを並列実行!パラレルランで実現する高精度な情報生成 first appeared on SIOS Tech. Lab .
アバター
こんにちは!今月から生成AI活用事業に復帰したなーがです。前回から少し時間が空いてしまいましたが、今回はDifyのチャットフローでRAGを実装する方法について書こうと思います。チャットボットによるRAGの構築は こちら の記事で作成方法を解説していますが、ここではチャートフローを使用します。 Difyの環境構築やアプリ公開方法等については下記の記事で紹介しているので、Difyについて詳しくない方は是非見てみて下さい。 SIOS Tech Lab 「Dify」 作成手順 完成図 中央に「質問分類器」があります。これはユーザーの入力を区別して関係性のある入力に対してはナレッジから回答分を生成し、無関係な入力に対しては決まった文章を返すようにしています。 ワークフロー作成 画面上部の「スタジオ」タブから「最初から作成」を選択します。 「アプリの種類を選択」で「チャットフロー」を選択して「アプリのアイコンと名前」に「チャットフローRAG」と入力し、「作成する」をクリックします。「名前」は自由に設定して大丈夫です。 まず「質問分類器」を追加します。「開始」と「LLM」を繋ぐ線の上で「+」をクリックします。 ポップアップが表示されるので、「質問分類器」を選択します。 「質問分類器」を選択し、「クラス1」と「クラス2」にそれぞれ以下のように入力します。 クラス1 ユーザーが就業規則に関連した質問をする クラス2 ユーザーが無関係な質問をする まず、「クラス1」の「関係性のある入力」に対してのパターンを作成します。「クラス1」の「+」をクリックし、「知識取得」を選択します。 「ナレッジ」の「+」をクリックします。 参照したいドキュメントを追加するために「作成に進む」をクリックします。 ナレッジの追加 ローカルファイルを登録したいので、「データソース」で「テキストファイルからインポート」を選択し、「参照」からファイルを選択して「次へ」をクリックします。 「チャンク設定」と「検索設定」は初期設定で行います。「インデックス方法」で「経済的」を選択し、「保存して処理」をクリックします。 「ドキュメントへ移動」をクリックします。 ドキュメントがアップロードされたことが確認出来たら、「スタジオ」をクリックします。 ちなみに、アップロードされたファイルを選択するとどのように分割して保存されているか確認できます。 「有給休暇」で検索してみると、4か所の関連個所があることが分かります。 作成中のアプリをクリックします。 再度「ナレッジ」の「+」をクリックします。 追加したドキュメントが表示されるので、選択して「追加」をクリックします。 LLMの接続 「知識取得」の「+」をドラッグして「LLM」の入力に接続します。 「LLM」を選択し、「コンテキスト」の「変数を設定」で「知識取得」の [x] result を選択します。 「SYSTEM」に以下を入力します。 あなたは{{#context#}}に基づいて{{#sys.query#}}に回答してください。 「クラス2」の「無関係な入力」に対してのパターンを作成します。「クラス2」の「+」をクリックし、「回答」を選択します。 回答の作成 「回答」に以下のように入力します。 申し訳ありませんが、ご質問にはお答えできません。もっと助けが必要な場合は、[help documentation](<https://docs.dify.ai>)をチェックしてください。 公開 完成したのでアプリをデプロイします。右上の「公開する」から「公開する」をクリックします。 アプリの実行 「アプリを実行」をクリックしてチャットボットを開きます。 チャットボットが開くので、「チャットを開始」をクリックします。 それでは質問をしてみます。まずは無関係な入力として「明日の天気」を訊いてみます。 無関係な入力として設定した「クラス2」の「回答」に設定した文章が出力されました。 次に関係性のある入力をしてみます。「有給休暇」について訊いてみます。アップロードしたドキュメントを基に回答を作成してくれていることが分かります。 さいごに 今回はチャットフローでRAGを実装する方法について書きました。チャットボットで作成するよりは手順が多いですが、詳細なフローを指定することが出来ます。今後もDifyを活用した内容ついて発信していこうと思います。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Dify入門ガイド:チャットフローでRAGを実装 first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの遠藤です。 Reactでアプリを作っていると、「状態管理ってどうすればいいの?」と悩むことがありますよね。たとえば、 useState を利用していると、コンポーネントの階層が深くなってきたときにpropsのバケツリレーで複雑になりうまく管理できなくなることがあります。 そんなときに便利なのが Jotai というライブラリです! Jotaiを使うと、シンプルな書き方で状態を管理でき、複雑なアプリでも扱いやすくなります。 この記事では、Jotaiの基本的な使い方を初心者向けにわかりやすく解説します。「Reactの状態管理をもっと簡単にしたい!」という方は、ぜひ最後まで読んでみてください! Jotaiとは? Jotaiは、Reactの状態管理をシンプルにするライブラリです。「Atom(アトム)」と呼ばれる単位で状態を管理し、それらを組み合わせることでアプリの状態を作ります。 Jotaiの特徴は、 必要な部分だけを効率よく更新すること 。ReactのContextを使った状態管理では、不要な再レンダリングが発生しがちですが、Jotaiは依存するAtomだけを更新するため、パフォーマンスが最適化されます。そのため、メモ化(memoization)を意識する必要が少なく、スムーズに開発できます。 また、 小規模なアプリから大規模なTypeScriptアプリまで対応 でき、公式のユーティリティや拡張機能も豊富に用意されています。シンプルな useState の代わりに使うことも、大規模なプロジェクトで本格的に活用することもできる柔軟なライブラリです。 公式サイト : https://jotai.org/ Core API JotaiのAPIはとてもシンプルで、必要最小限の機能だけが提供されています。基本的には「atom」「useAtom」「Store」「Provider」という4つのCore APIで状態管理が完結します。 atom Jotaiのatomは、アプリの状態を定義するための基本的な単位です。Reactの useState に似ていますが、 グローバルに管理できる のが特徴です。 atomは「状態の設定(atom config)」を作るだけで、実際に値を保持しているわけではありません。値は Store と呼ばれる仕組みに保存されます。そのため、atom自体は変更できず、常に不変(immutable)なオブジェクトとして扱われます。 atomの作成方法 基本的なatomは atom 関数を使って作成します。初期値を渡せば、その値を持つatomが作成されます。 import { atom } from 'jotai' const priceAtom = atom(10) // 数値の状態 const messageAtom = atom('hello') // 文字列の状態 const productAtom = atom({ id: 12, name: 'good stuff' }) // オブジェクトの状態 useAtom useAtom は、 atomの値を読み取ったり更新したりするためのフック です。Reactの useState に似た使い方ができ、atomの値と更新関数を返します。 const [value, setValue] = useAtom(anAtom) この value は現在のatomの値で、 setValue を使って新しい値に更新できます。 useAtomの基本的な使い方 まず、atomを作成します。 import { atom, useAtom } from 'jotai' const countAtom = atom(0) // 初期値 0 の atom 次に、コンポーネント内で useAtom を使って、この countAtom の値を取得・更新します。 const Counter = () => { const [count, setCount] = useAtom(countAtom) return ( <div> <p>現在のカウント: {count}</p> <button onClick={() => setCount(count + 1)}>+1</button> </div> ) } このように、 useAtom を使えば、コンポーネント内で簡単にグローバルな状態を管理できます。 注意点: useAtom内でatomを直接作らない useAtom(atom(0)) のように、コンポーネント内で毎回新しいatomを作ると、 レンダリングのたびに異なるatomが作られてしまい、無限ループが発生する ことがあります。 const [count] = useAtom(atom(0)) // ❌ 毎回新しいatomを作るのでNG 代わりに、 コンポーネントの外でatomを定義する ようにしましょう。 const countAtom = atom(0) // ✅ 外で定義すればOK const [count] = = useAtom(doubleCountAtom) // ✅ 正しく動作 Store Store は、atomの値を管理するための独立した状態コンテナです。通常、Jotaiはデフォルトの Store を使用するため、特に意識しなくても状態を管理できます。しかし、 createStore を使ってカスタム Store を作成すると、 複数の状態ツリーを独立して管理 できるようになります。 Storeの作成と使用 import { atom, createStore } from 'jotai' const myStore = createStore() // 新しいStoreを作成 const countAtom = atom(0) // Storeを直接操作 myStore.set(countAtom, 1) // countAtom の値を 1 に更新 console.log(myStore.get(countAtom)) // 1 // 値の変更を監視 const unsubscribe = myStore.sub(countAtom, () => { console.log('countAtomが変更されました:', myStore.get(countAtom)) }) Providerとは? Provider は、 JotaiのStoreを特定のコンポーネントツリーに適用するためのコンポーネント です。 通常、Jotaiはデフォルトの Store を使うので Provider なしでも動作しますが、以下のような場合に Provider を使うと便利です。 Providerを使うメリット 異なる状態を持つ複数のコンポーネントツリーを作れる 例えば、2つの Provider を使うことで、同じ atom を異なる状態として管理できます。 初期値を適用できる Provider の store に初期値を設定して、コンポーネントごとに異なるデータを扱えます。 Provider の再マウントで状態をリセットできる Provider を再マウントすると、その配下のatomの状態をクリアできます。 Providerの使い方 デフォルトのProvider(特に指定しない場合) import { atom, useAtom, Provider } from 'jotai' const countAtom = atom(0) const Counter = () => { const [count, setCount] = useAtom(countAtom) return ( <div> <p>カウント: {count}</p> <button onClick={() => setCount(count + 1)}>+1</button> </div> ) } const App = () => ( <Provider> <Counter /> </Provider> ) この場合、 Provider を使わなくてもデフォルトのStoreが適用されます。 カスタムStoreをProviderに適用 作成した Store を Provider に適用すると、 独立した状態管理 が可能になります。 const myStore = createStore() const Root = () => ( <Provider store={myStore}> <App /> </Provider> ) こうすることで、 myStore を使用した状態管理が App 以下のコンポーネントに適用されます。 異なるProviderで独立した状態を持たせる 異なる Provider を使うと、同じ atom でも別々の状態として扱えます。 const Counter = () => { const [count, setCount] = useAtom(countAtom) return ( <div> <p>カウント: {count}</p> <button onClick={() => setCount(count + 1)}>+1</button> </div> ) } const App = () => ( <div> <Provider> <h2>Provider 1</h2> <Counter /> </Provider> <Provider> <h2>Provider 2</h2> <Counter /> </Provider> </div> ) この例では、それぞれの Provider が独立した Store を持っているため、1つのカウンターを更新してももう1つには影響しません。 Tips : Jotaiで状態をローカルストレージに保存する方法 CoreAPIの機能ではありませんが、、 atomWithStorage を使うことで、状態を localStorage や sessionStorage に簡単に保存できます。これにより、ユーザーの設定やデータを次回のセッションでも保持できます。 atomWithStorage の基本 atomWithStorage は Jotai の jotai/utils モジュールに含まれており、指定したキーで localStorage または sessionStorage と同期されます。ページの再読み込み後も、保存された値が自動的に取得されます。 例: テーマの状態を保存する 以下のコードでは、 atomWithStorage を使ってテーマ( dark or light )を localStorage に保存し、ページをリロードしても選択したテーマが保持されるようにしています。 import { useAtom } from 'jotai'; import { atomWithStorage } from 'jotai/utils'; // 'dark' というキーでローカルストレージに保存するAtom const theme = atomWithStorage('dark', false); export default function Page() { const [appTheme, setAppTheme] = useAtom(theme); const handleClick = () => setAppTheme(!appTheme); return ( <div className={appTheme ? 'dark' : 'light'}> <h1>テーマ切り替え</h1> <button onClick={handleClick}> {appTheme ? 'DARK' : 'LIGHT'} </button> </div> ); } まとめ 本記事では、 Jotaiの基本的な使い方 や 便利な機能 について解説しました。Jotaiは useState のように直感的に使え、 シンプルな記述でグローバルな状態管理ができる のが特徴です。 また、 createStore() や Provider を活用することで、画面ごとに異なる状態を管理したり、状態をリセットしたりすることも可能です。Reduxのような複雑なセットアップが不要で、 初心者でも扱いやすいライブラリ なので、「もっと簡単に状態を管理したい!」という方におすすめです。Jotaiを使って、Reactアプリの状態管理をシンプルにしてみましょう! 🎉 ではまた~ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【初心者向け】React状態管理ライブラリ Jotaiを解説 first appeared on SIOS Tech. Lab .
アバター
挨拶 ども!久しぶりにブログを連続で執筆していて、日常の至るところでブログのネタを考えていますね。非常に便利な習慣が戻ってきている龍ちゃんです。 今回は「Dify上で、LLMからの出力から構造化情報(JSON)を作成して返答」となります。LLMからの出力をJSONに成型してアプリに組み込む方法に関しては、Azure OpenAI Serviceを活用していた際にもぶつかっていた問題になります。( AOAI:Gpt-4oでJSON出力に失敗する対症療法 ) LLMから構造化データを作成することができればアプリの表現の幅が大幅に広がります。方法としては、「コードブロックでオブジェクトを詰め替える」という力技で実現しています。 2025/02/17時点でのDify Cloudの情報となります。 おそらく、バージョンアップで「終了」ブロックで対応されるかなと予想中。 実現したいこと Dify(API)では、レスポンスは【終了】ブロックで書いた情報がJSONで返答されます。ですが、単純に記述するだけでは、以下のようなレスポンスになってしまいます。 { "name":"TEST User", "age":24, "job":"WEBデザイナー", "profile":"都内で一流のデザイナーを目指す、駆け出しWEBデザイナー" } 返答する情報量が少ない場合、こちらのままでも問題ありません。ですが、複数の事柄に関する情報を返答する場合は、情報が同一階層に表示されることで不都合が生じます。命名の工夫や呼び出し側での整形で対応できますが、Dify側でObjectとして返してあげるほうが自然です。 以下のような返答を送れるように情報を成形して渡す方法を解説していきます。 { "user":{ "name":"TEST User", "age":24, "job":"WEBデザイナー", "profile":"都内で一流のデザイナーを目指す、駆け出しWEBデザイナー" } } ワークフローで構造化情報(JSON)を返答する 説明のために「架空の人物情報(名前・年齢・職業・説明)を生成AIを活用して生成して、情報をuserというオブジェクトで取得」という仮のユースケースを使用して進めていきます。 全体感としては、以下のようなステップのシンプルなワークフローとなります。 LLM:架空のユーザーを作成するプロンプト パラメータ抽出:LLMの出力から情報を抽出(name, job, age, profile) コード:パラメータ抽出のアウトプットを引数としてuserオブジェクトを作成 終了:userオブジェクトを返信 架空のユーザー作成プロンプト 以下の手順に従ってください。 1. 広い選択肢からランダムに職業を1つ選択してください。 2. 名前と年齢を決定してください。名前は広い選択肢の中からより現実的な情報にしてください。 3. 人物の説明を500文字程度で作成してください。趣味嗜好などの情報や特筆した情報があると素晴らしいです。 最終的な出力は、以下の情報のみにしてください。 名前: 職業: 年齢: 説明: プロンプトで注目する点は、次のブロックである【パラメータ抽出】で抽出しやすいようにプロンプトを調整している部分です。余分な情報をそぎ落とすことで、【パラメータ抽出】の精度を上げています。 例:出力 名前:佐藤健一 職業:公務員 年齢:34歳 説明:佐藤健一は、東京都内に住む34歳の公務員です。彼は地元の市役所で、地域の福祉政策を担当しており、住民の生活向上に貢献することに情熱を注いでいます。大学では社会学を専攻し、卒業後すぐに公務員試験に合格して現在の職に就きました。仕事に対する責任感が強く、特に高齢者支援のプロジェクトに深く関与しており、地域のボランティア団体と連携しながら活動しています。\n\n趣味はハイキングと料理で、週末には友人たちと近郊の山を登ったり、自宅で新しいレシピに挑戦したりしています。特にイタリア料理が得意で、パスタやリゾットを作るのが好きです。また、彼は猫を飼っており、愛猫の「ミケ」と一緒に過ごす時間が何よりの癒しとなっています。健一は、身近な人々との交流を大切にしており、地域のイベントにも積極的に参加することで、コミュニティの一員としてのつながりを大事にしています。将来的には、より多くの人々に影響を与えられるよう、政策提案を行う立場にステップアップしたいと考えています。 パラメータ抽出 ここでは、LLMを使用してテキスト情報から特定の情報を抽出します。前段【LLM:架空のユーザー作成プロンプト】の生成文を入力とします。抽出する情報は以下となります。 名前 タイプ 説明 name String キャラクターの名前 job String キャラクターの職業 age Number キャラクターの年齢 profile String キャラクターの説明 名前と説明を適切に設定することで、追加でプロンプト(指示)を与えることなく動作します。もし抽出がうまくいかない場合は、以下の二つに注目してみてください。 抽出しやすい入力になるように成型しておく(今回の方法) 追加のプロンプトを設定して抽出方法を調整する コード:userオブジェクトを生成 パラメータ抽出で取得した情報をuserオブジェクトに詰め替えて処理を終了します。入力変数・コード・出力変数を一致させる必要があります。今回使用しているコードは以下になります。 def main(name: str, job: str, age: int, profile:str): return { "user":{ "name":name, "age":age, "job":job, "profile":profile } } 入力変数と出力変数はそれぞれ、コードの引数と出力と一致させてください。出力は user で Object を指定します。 特に出力変数はリターン内で自分で定義した命名と完全に一致させてください。 命名と型指定を忘れずに!! コードブロック参考画像 終了:情報を返答していく ここでは、コードブロックの出力をそのまま出力してください。入力を選択した際に user がオブジェクトの型定義がついていれば一安心です。 終わり 今回はLLMからの出力を構造化情報(JSON)として取得する方法を解説しました。パラメータ抽出を活用することで、プロンプトの出力を簡単に構造化できることが分かりましたね。この手法を応用することで、より複雑なJSONデータの生成も可能になるはずです。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Dify入門ガイド:LLM出力を構造化する!JSONデータ作成の具体的手順 first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの遠藤です。 近年、AIを活用したコード生成ツールが急速に発展しています。その中でも、Vercelが提供するv0は、プロンプトを入力するだけでNext.jsアプリのコードを自動生成できるWebアプリとして注目を集めています。本記事ではv0を実際に試し、どのような形でNext.jsアプリが生成されるのかを確認していきます。では始めていきましょう! v0とは? Next.jsを開発しているVercelが提供しているAIを活用した生成型ユーザーインターフェースシステムです。プロンプトをもとにshadcnを利用したNext.jsアプリが生成され、すぐにプロジェクトに取り込むことが出来ます。 v0を利用してアプリを作成してみる では早速ですが実際に利用してみましょう。まずはv0のサイトにアクセスします。 v0 by Vercel 初回利用時はVercelアカウントでのサインアップが必要となります。メールアドレス、githubアカウント、gitlabアカウント、Bitbucketアカウントの4つの方法でサインアップすることが出来ます。 サインアップが済んだら早速プロンプトを入力エリアに設定していきます。。今回は以下のプロンプトを設定してみました。ちなみにこのプロンプトはChatGPTで作成してもらいました。 シンプルでモダンなSaaSダッシュボードのUIをデザインしてください。 左側にナビゲーションサイドバーを配置し、上部には検索機能付きのトップバーを設置。 メインコンテンツには、総売上、アクティブユーザー数、成長率などの主要指標を表示してください。 青と白を基調としたカラーリングで、データ表示には角丸のカードデザインを採用。 詳細分析のためのインタラクティブなグラフやフィルター機能を含めてください。 そうすると画面が移り、早速出力が始まります。これがv0のメインの画面になっており、①のチャットインタフェースと②の生成されたUIやコードの表示場所から成り立っています。生成されたUIはプロンプトの条件を確認してみましたが、しっかりプロンプトの条件を満たしており、検索用のトップバーや丸角のカードデザインなどコンポーネントやデザインを含めてきれいに作成してくれています。 またUIのプレビューだけでなくコードの確認も画面上部のタブを切り替えることで行うことが出来ます。構成としてはappとcomponentsに分けて作成されているようなので、コンポーネント単体だけを導入したりするのも簡単そうですね。 アプリを自分のローカル環境で呼び出す ではここからはアプリを自分の環境で動かす方法を確認してみましょう。方法としては ZIP形式でダウンロードを行い展開する npxを利用してローカルで展開する の2つの方法があり、今回は②の方法でローカルで動かす方法を確認してみます。 npxを利用して動かすにはアプリを作成したv0のチャットの画面右上の「⋯」をクリックし、「npx shadcn add “https://~~~」となっているコピーアイコンをクリックします。 クリックできたら、node.jsが動く環境上でコピーしたコマンドを貼り付け実行します。そうするとNext.jsアプリの作成が始まるので、聞かれている質問に対して答えて行きます。私はほどんどデフォルトを指定して答えていきました。 作成が完了したら、作成されたアプリのディレクトリに移動し、 npm install を実行後 npm run dev を実行します。 そうすると http://localhost:3000/ にアプリが展開されます。 アプリを確認してみると、右上の画像が入っていなかったりはしますが、カードがウィンドウ幅に合わせて2行になったりとレスポンス対応もしてくれていることが確認できました。いやぁ~すごいですね。プロンプトだけでここまで生成してくれるならアプリの雛形としては十分実用的なように感じます。 企業で利用する場合に気になること 企業で生成AI周りのツールを利用するときには「入力したデータが学習に利用されるか」と「出力されたデータの扱い方」は気になりますよね。公式のFAQで触れられていたので確認してみましょう。 入力されたデータがAIの学習に利用されるか Will my generations be used for training? v0 は、製品を改善するために、ユーザーが生成したプロンプトやコンテンツを、サードパーティ プロバイダーのモデルや学習システムへの入力として使用することがあります。このデータを使用することで、v0 はユーザーに対してより正確で関連性の高い推奨事項を提供できるようになります。 v0 Enterprise 顧客のコンテンツはトレーニングには使用されません。 さらに、Vercel のプラットフォーム サービスを使用する顧客の顧客データやコードは、v0 で使用されるモデルのトレーニング、改善、または微調整には使用されません。 Enterpriseプランを利用することでコンテンツが学習に利用されなくなるようです。この書きっぷりだとFreeプランはもちろん、PremiumやTeamでも学習に利用されてしまいそうなので、これらのプランで業務データをいれるのは避けたほうが良さそうです。 出力されたデータの商用利用 Can I use output from v0 for commercial uses? Vercel は、クエリやプロンプトに基づいて生成されたコードを所有しません。ただし、受け取る出力は、他のユーザーの出力やサードパーティの IP と同じか類似していたり、不完全であったり、バグを含んでいたり、使用に適していない場合があります。v0 を通じて生成したコンテンツを評価し、商用またはその他の目的に適しているかどうかを独自に判断するのは、ユーザーの責任です。 出力された成果物を商用利用することができるのは良いですね。 ただ、v0 は画像をもとに出力を生成する機能を利用することで、特に実在するサービスやブランドに類似したデザインが出力される可能性があります。例えば、既存のロゴや UI デザインに似たものが生成された場合、それをそのまま商用利用すると意図せず権利侵害にあたる可能性があるのでそこは注意点ですね。 まとめ 今回はv0を利用してプロンプトからNext.jsアプリケーションを作成を試してみました。プロンプトの意図を読み取ってかなりイメージと近いアプリケーションを生成してくれるのでとりあえずの雛形作成としては非常に優秀に感じました。今回は基本のテキストベースのプロンプトを利用したアプリ生成を試してみましたが、他にもできることが多いみたいのなのでそちらも試してみようと思います。 ではまた~ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【React】プロンプトからNext.jsアプリを自動生成!v0を試してみた! first appeared on SIOS Tech. Lab .
アバター