TECH PLAY

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

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

524

今号では、nmcli コマンドの使い方やオプションについてご紹介します! nmcli コマンドとは nmcli コマンドは、NetworkManager の機能をコマンドライン上から使用できるコマンドです。 GUI が利用できないサーバーやリモートマシンでよく使用されます。 nmcli を使用することで、各ネットワークインターフェースの状態の確認、接続の作成や削除、 IP アドレスの設定などを実行できます。 なお、nmcli コマンドは非常に多機能であり、多くのサブコマンドとオプションがあります。 そのため、数回に分けて各サブコマンド、オプションについて紹介していきます。 今回の記事では、 物理デバイスに関するサブコマンド、オプションをご紹介します。 基本の書式 サブコマンドに “device” もしくは “dev” を指定すると、物理デバイスの情報を表示します。 なお、これはオプションに “status” を指定した時と同じ動作になります。 # nmcli device DEVICE TYPE STATE CONNECTION eth0 ethernet connected System eth0 lo loopback connected (externally) lo nmcli device コマンドのオプション よく使用されると考えられるオプションを抜粋してご紹介します。 オプションに “show” 、その後に対象となるデバイスを指定すると、当該デバイスの 詳細情報を表示します。 # nmcli device show eth0 GENERAL.DEVICE: eth0 GENERAL.TYPE: ethernet GENERAL.HWADDR: 06:EC:94:58:92:9D GENERAL.MTU: 9001 GENERAL.STATE: 100 (connected) GENERAL.CONNECTION: System eth0 GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/2 WIRED-PROPERTIES.CARRIER: on IP4.ADDRESS[1]: 172.31.7.153/20 IP4.GATEWAY: 172.31.0.1 IP4.ROUTE[1]: dst = 172.31.0.0/20, nh = 0.0.0.0, mt = 100 IP4.ROUTE[2]: dst = 0.0.0.0/0, nh = 172.31.0.1, mt = 100 IP4.DNS[1]: 172.31.0.2 IP4.DOMAIN[1]: ap-northeast-1.compute.internal IP6.ADDRESS[1]: 2406:da14:ac9:2100:8fc0:6d84:a95b:fc9b/128 IP6.ADDRESS[2]: fe80::4ec:94ff:fe58:929d/64 IP6.GATEWAY: fe80::809:7dff:fec0:1 IP6.ROUTE[1]: dst = fe80::/64, nh = ::, mt = 1024 IP6.ROUTE[2]: dst = 2406:da14:ac9:2100::/64, nh = ::, mt = 100 IP6.ROUTE[3]: dst = ::/0, nh = fe80::809:7dff:fec0:1, mt = 100 IP6.ROUTE[4]: dst = 2406:da14:ac9:2100:8fc0:6d84:a95b:fc9b/128, nh = ::, mt = 100 ・ GENERAL.DEVICE:  デバイス名 ・ GENERAL.TYPE:  デバイスの種類 ・ GENERAL.HWADDR:  MAC アドレス ・ GENERAL.MTU:  1回に送信できる最大のデータサイズ ・ GENERAL.STATE:  デバイスの状態 (connected は接続済みであることを示す) ・ GENERAL.CONNECTION:  接続の名前 ・ GENERAL.CON-PATH:  NetworkManager 上での接続のパス ・ WIRED-PROPERTIES.CARRIER:  信号の状態 (on は接続がアクティブであることを示す) ・ IP4.ADDRESS[1]:  IPv4 アドレス、およびサブネットマスク ・ IP4.GATEWAY:  IPv4 のデフォルトゲートウェイ ・ IP4.ROUTE[1]~[2]:  IPv4 のルーティング情報 ・ IP4.DNS[1]:  IPv4 の DNS サーバ ・ IP4.DOMAIN[1]:  ドメイン名 ・ IP6.ADDRESS[1]:  IPv6 アドレス ・ IP6.ADDRESS[2]:  IPv6 アドレス (ローカルアドレス) ・ IP6.GATEWAY:  IPv6 のデフォルトゲートウェイ ・ IP6.ROUTE[1]~[4]:  IPv6 のルーティング情報 オプションに “connect” 、その後に対象となるデバイスを指定すると、当該デバイスに 接続します。 # nmcli device connect eth0 Device 'eth0' successfully activated with '5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03'. オプションに “disconnect” 、その後に対象となるデバイスを指定すると、当該デバイスから 切断します。 # nmcli device disconnect eth0 Device 'eth0' successfully disconnected. オプションに “modify” 、その後に対象となるデバイス、対象となるパラメータを指定すると、 当該パラメータの設定内容を変更できます。 例えば、eth0 の MTU (Maximum Transmission Unit) サイズを変更したい場合、下記の様に実行します。 # nmcli device modify eth0 mtu 5000 Connection successfully reapplied to device 'eth0'. nmcli device show eth0 コマンドで各パラメータの内容を確認すると、MTU サイズが 上記で変更した 5000 に変更されています。 # nmcli device show eth0 GENERAL.DEVICE: eth0 GENERAL.TYPE: ethernet GENERAL.HWADDR: 06:EC:94:58:92:9D GENERAL.MTU: 5000 GENERAL.STATE: 100 (connected) GENERAL.CONNECTION: System eth0 GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/4 WIRED-PROPERTIES.CARRIER: on IP4.ADDRESS[1]: 172.31.7.153/20 IP4.GATEWAY: 172.31.0.1 IP4.ROUTE[1]: dst = 172.31.0.0/20, nh = 0.0.0.0, mt = 100 IP4.ROUTE[2]: dst = 0.0.0.0/0, nh = 172.31.0.1, mt = 100 IP4.DNS[1]: 172.31.0.2 IP4.DOMAIN[1]: ap-northeast-1.compute.internal IP6.ADDRESS[1]: 2406:da14:ac9:2100:8fc0:6d84:a95b:fc9b/128 IP6.ADDRESS[2]: fe80::4ec:94ff:fe58:929d/64 IP6.GATEWAY: fe80::809:7dff:fec0:1 IP6.ROUTE[1]: dst = fe80::/64, nh = ::, mt = 1024 IP6.ROUTE[2]: dst = 2406:da14:ac9:2100::/64, nh = ::, mt = 100 IP6.ROUTE[3]: dst = ::/0, nh = fe80::809:7dff:fec0:1, mt = 100 IP6.ROUTE[4]: dst = 2406:da14:ac9:2100:8fc0:6d84:a95b:fc9b/128, nh = ::, mt = 100 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 知っておくとちょっと便利!nmcli コマンドの使い方 ~デバイス編~ first appeared on SIOS Tech. Lab .
アバター
初めに こんにちは。 PS/SLの佐々木です。 最近輪読会でDDDについての書籍を扱っているのですが、その中でValueObjectを作るのか作らないのか論争が巻き起こっています。 私自身作るに越したことはないと思うのですが、実装量が多くなるのと、必要なものだけ作ればいいのではないかと思う反面、作る作らないの判断が人によると一貫性のないコードになってしまう懸念点があります。 そこで今回はTypescriptでDomainObjectaのコンストラクタで定義されているプロパティからValueObjectを自動生成する ts-vo-generator というライブラリを作成してみました。 ts-vo-generatorの使い方 こちらのライブラリはnpmとyarnで公開しています。( npm registry ) 今回はnpmでインストールする例を紹介します。 npm install -g ts-vo-generator 使用する際には npx typescript-value-object-generator <input_file> <output_directory> このように使用します。 input_file には解析対象のclassのパスを指定し、 output_directory にはValueObjectを生成するディレクトリを指定します。 実際に使用してみる 今回は以下のようなクラスからValuObjectを自動生成してみます。 type MountainType = { id: number; name: string; elevation: number; description?: string; range: string; } // Mountain.ts export class Mountain { private constructor( private id: number, private name: string, private elevation: number, private range: string, private description?: string ) {} static new(props: MountainType): Mountain { return new Mountain( props.id, props.name, props.elevation, props.range, props.description ); } public genMessage(): string { return `${this.name} の標高は ${this.elevation} mです。`; } } 以下のコマンドを実行します。 npx ts-vo-generator ./src/model/Mountain.ts ./src/ValueObject 実行すると /src/ValueObject 配下に Description.ts や Id.ts が生成されていることが確認できます。 // Id.ts export class Id { private constructor(private readonly value: number) {} static create(value: number): Id { if (!Id.isValid(value)) { throw new Error('Invalid value'); } return new Id(value); } getValue(): number { return this.value; } private static isValid(value: number): boolean { // validation logic here return true; } } 生成されたコードを見てみると上記のようなValueObjectのスケルトンコードが生成されています。 この後は好きなロジックを追加していきます。 また一度生成したものに関して再度生成しようとすると上書きするか処理をスキップするかを聞かれます。 今後の展望 現在の ts-vo-generator ではValueObjectを作成後、元のClassの方にも手動でValueObjectを定義しないといけません。 近いうちにこちらの対応も行っていきたいと思います。 最終的には以下のようなところまで自動で生成したいと思っています。(現時点ではこの修正は手動です) import { Id } from '../ValueObject/Id'; import { Description } from '../ValueObject/Description'; import { Elevation } from '../ValueObject/Elevation'; import { Name } from '../ValueObject/Name'; import { Range } from '../ValueObject/Range'; type MountainType = { id: Id; name: Name; elevation: Elevation; description?: Description; range: Range; } // Mountain.ts export class Mountain { private constructor( private id: Id, private name: Name, private elevation: Elevation, private range: Range, private description?: Description ) {} static new(props: MountainType): Mountain { return new Mountain( props.id, props.name, props.elevation, props.range, props.description ); } public genMessage(): string { return `${this.name.getValue()} の標高は ${this.elevation.getValue()} mです。`; } } 終わりに 最後まで読んでいただきありがとうございました。 もしよろしけべばStarやコントリビュートお待ちしています。 gihtub ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post DomainObjectからValueObjectを自動生成するOSS作ってみた ~ ts-vo-generator~ first appeared on SIOS Tech. Lab .
アバター
こんにちは。サイオステクノロジー OSS サポート担当 山本 です。 今回は前回紹介した Solr がなぜ早いのかと、Solr を使いこなすためには必須となる概念についてお話ししてみようと思います。 ■Solr の検索が早いカラクリ 繰り返しになりますが Solr の主な強みは 検索が超高速 であることです。 その検索の早さを支えているのは、 インデックス という仕組みです。 Solr のインデックスはその名のとおり 登録されたドキュメント (データ) の 目次 であり、基本的には ドキュメントを登録する際に自動的に作成 されます。 ※ 前回似ていると紹介した RDB にもインデックスという概念はありますが、Solr のそれとはやり方が大きく異なるものです。 そして、検索の際にはこのインデックスから検索を行うことで超高速な文字列検索を実現できる、というわけです。 さて、ではどのようにしてこの「目次」を作っているのかと言うと…… スキーマ において各 フィールド で設定した内容に応じて ドキュメント内の 文章を解析 して、その解析結果から作られています。 特に日本語を使いたい場合、この解析のために必要になってくるのが自然言語処理の一種である 形態素解析 というものです。 ■形態素解析をなんとなく知る 自然言語処理 は非常にざっくり言ってしまえば、 人間が普段使っている言葉をコンピュータで適切に処理できるようにする ための技術・学問です。 形態素解析 は自然言語処理の一分野で、 文章を単語に分け て品詞の種類や原形などを特定する、ということを扱います。 コンピュータの分野では、更に細分化して 単語に分ける部分だけ を トークナイズ などと呼んだりもするようです。 トークナイズの例を考えてみましょう。 以下の文章を単語に分けてみてください。 This is a red pencil. これは… This / is / a / red / pencil / . こうですね。 このように単語に分割するのがトークナイズで、更に分けた単語の品詞などを特定してやれば形態素解析、と言えるでしょう(非常にざっくりとした考え方でよければ、という前提は付きますが)。 さて、この例で見てもらった英語を含め、 大半の言語では最初から単語ごとに スペースで区切って 文章を書きます 。 そのため、コンピュータで扱う場合でも基本的には 機械的に「スペース」という文字が出てきたら区切る 、という処理で単語に分けられる(一部例外はあり)ため、形態素解析というものは原則としてあまり難しいものではないようです。 一方で日本語はというと……例えば以下の文章を単語に分けてみましょう。 これはあかいえんぴつです。 これは… これ / は / あかい / えんぴつ / です / 。 こうなりますね。 さて、我々のように日常的に日本語を使っている人間であれば特に問題なく単語に分割できるでしょうが、これをコンピュータでやりたいとなるとどうでしょう。 日本語というのは改めて見てみると、明確な区切りとなる目安がなく、文法も比較的自由で、活用形だったり造語だったり口語やスラングなど変則的な単語が乱れ飛ぶ…… そうです。 日本語の形態素解析というのは、非常に難しい ものなのです。 現在の日本語の形態素解析は、コンピュータに単語を判別するための 辞書データ を登録した上で、上手く解析するための様々なアプローチ……例えば  ・考えうる区切り方を全て抜き出して、品詞の繋がり方などから一番それっぽい区切り方を選ぶ  ・「ここで区切ると前後が適切な言語になるか」を逐一チェックしていく などのような処理を実装した、いくつかのアルゴリズムがあるとされています。 現在では高い精度で形態素解析をすることができますが、先述のとおり日本語があまりにも変則的で自由な言語であるため、 100% 完璧に正しく形態素解析をできる保証はない 、というのが現状です。 色々ごちゃごちゃとお話しましたが、Solr で日本語を使う上では、  ・ なんか単語に分けてるらしい  ・ 単語に分けるために 形態素解析 って考え方を使うらしい  ・ 形態素解析には 辞書データ ってものが必要らしい  ・ 確実に意図した通りの単語に区切れるとは限らないらしい という点を覚えておいてください。 余談ですが、例えば英語だと同じスペルでも違う品詞だったり全く違う意味を持つ単語が多数あるため、単語に分割した後の適切な意味を判定するステップが難しかったりするなど、それぞれの言語で違った自然言語処理の難しさがあったりします。 ■Solr のインデックス作成の様子を見てみる 前置きが長くなりましたが、最初にお話した Solr の インデックス作成 (= インデキシング ) がどのように行われるか見てみましょう。 Solr ドキュメントの登録時には、 フィルタ と呼ばれる一連の処理を 複数組み合わせ て、その処理結果をインデックスとして登録します。 フィルタはフィールドごとに組み合わせ方を設定 することができます。 また、 検索時にも設定した組み合わせでフィルタの処理を行った結果を、 実際の検索ワード として使用 します。 フィルタにはデフォルトで用意されているものだけでも様々な種類がありますが、一例としては  ・文章を単語に分割して品詞を特定する  ・単語を基本形に戻す  ・半角文字を全角文字に直す  ・助詞・助動詞・代名詞など検索に適さない要素を削除する などのようなものがあります。(ここで形態素解析の概念を使っていますね) 前回見た Solr の管理画面で実際のインデキシングの処理の様子を確認することができる ので、今回もデモ用環境を使って試してみましょう。 デモ環境の立ち上げは以下のコマンドで、 ## 前回の環境を消してしまっている場合 $ podman run -dt --name test-solr -p 8984:8983 solr solr-demo ## 前回の環境から続けて試す場合 $ podman start test-solr 管理用の WebUI はブラウザで以下のアドレスにアクセスすれば OK でしたね。 http://(IPアドレス or ホスト名):8984/solr WebUI にアクセスできたら、「プルダウンから “demo” を選ぶ」→「Analysis」の順に遷移しましょう。 この画面からインデキシングの処理のテストができます。 今回は “ Analyse Fieldname / FieldType ” を、デフォルトで用意されている日本語向けの設定である “ text_ja ” にして試してみましょう。 画面上部の入力部分 にテストしてみたい文章を入れて、” Analyse Values ” ボタンを押下すると、 設定されているフィルタごとの処理結果 が順に表示されるはずです。 実際にインデックスとして登録される(または検索ワードとして使用される)のは、設定された全てのフィルタを適用した結果となる 一番下 に表示されているものです。 ■うまくいかない例も見てみる 折角なので思い通りにいかない例も見てみましょう。 先の “Analysis” のページで、今度は「 うらにわにはにわにわとりがいる。 」という文章を解析させてみましょう。 これはご存じのとおり 裏庭 / に / は / 二羽 / 鶏 / が / いる / 。 なので、このとおりになってほしいところですが…… 結果はご覧のとおり、「うら / に / わに / はにわ / にわとり / が / いる」と何か鰐や埴輪が生えてきてしまっています。あくまで実験として見るだけなら、ちょっと愉快な感じですね。 ともあれ、このように意図した通りの形になってくれないケースもあり得る、ということは覚えておいてください。 余談ですが、解析させる文章を「裏庭には二羽鶏がいる」と漢字も使った形にしてやれば、大体ちゃんと上手くやってくれます。 意図しない形になってしまう可能性があるのは、あくまで 文法として正しい、意図しない分け方 がある場合という点も気を付けてください。 ■最後に 今回は Solr のカラクリの核である “ インデックス ” と、インデックスを考える上で必要不可欠になる “形態素解析” という概念についてお話ししてみました。 見てもらった通り、Solr では 登録されたドキュメントを単語に分割して “インデックス” を作成する ので、例えば登録したデータ (ドキュメント) から 「ぶどう」という 単語が含まれている ものを探す、というような処理に圧倒的に強くなります。 前回似ている部分もあるというお話をした RDB ではこのような機能はないため、同じようなことをしようとすると検索のたびに逐一全てのデータをチェックする必要があり、例えるなら「本の目次から目標を探す」か「その本を全て読みながら目標を探す」かくらいの差が出る、というわけです。 ※ RDB でも “LIKE” 句で似たようなことはできますし、文字列検索という限定的な用法以外では原則 RDB のほうが便利な点には留意してください。 ここさえ押さえてしまえば、あとはデータの登録手順を覚えれば Solr を使えると言っても差し支えないはずです。 あとは思い通りにいかなかった場合の対処案などもありますが……そのあたりはまたいずれ。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Solr って何者?②:早さの秘訣、インデックスのカラクリ first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジー の佐藤 陽です。 今日は Azure の AI サービスである Document Intelligence を利用した RAG システムを構築してみたいと思います。 PDF データを独自データとした RAG を構築したい! Document Intelligence とか Azure AI Search ってどうやって使うの? セマンティックチャンキングって何? といった方は最後までご覧ください! はじめに こちらの サンプル に従って実装の方を行っていきます。 日本語の説明も無いですし、RAG初心者&python 不慣れな自分には分かりづらい部分もあったため、そのあたり丁寧に解説していきたいと思います。 ただの公式サンプル解説記事にはなりますが、同じような境遇の誰かの役に立てば幸いです。 要素の紹介 今回のサンプルで登場する技術要素、サービスとしては以下のようなものがあります。 Azure AI Document Intelligence Azure AI Search Azure OpenAI Service LangChain Azure AI Document Intelligence Document Intelligence の機能を使って PDF からテキストデータを抽出します。 Document Intelligence の layout モデルを利用することで、 文章の構造を保ったまま Markdown の形式で抽出することが可能です。 Markdown の形式で抽出することで、今回の記事の肝であるセマンティックチャンキング(後述)が可能となります。 また、Document Intelligenceに関してはこちらの記事でも以前解説しています。 Azure AI Document Intelligence入門【Resultパラメータ解説付き!】 Azure AI Search RAG を構築する際に利用するベクトルストアです。 チャンク化されたデータをベクトル化し、保存しておきます。 またユーザーからのリクエストに対して、関連のある情報を検索し、抽出します。 Azure OpenAI Service Chat や Embedding の LLM の機能を持ちます。 チャットのやり取りや、チャンキングした情報のベクトル化などを行います。 LangChain LangChain は LLM を手軽に使えるようにしてくれるライブラリです。 様々な機能が提供されていますが、今回は主にチャンクキングの処理や、Azure OpenAI Service を実行するためのラッパー、RAGシステムの構築のために利用します。 システム構成 参考サイトのものをそのまま張っておきます。  全体の処理の流れとしては AI Document Intelligence で PDF を読み取る PDF の内容を分析し、Markdown として出力する LangChain の機能を利用し、Markdown の内容を見出し位置(#,##,###)でチャンク化する チャンク化した内容をベクトル化し、ベクトルストアである AI Search に登録する ユーザーからの質問の内容に関連する情報を AI Search から取り出す Chat に投げる Prompt を構築する Chat から回答を得る といった形となります。 セマンティックチャンキングとは 実装を行う前に、今回の記事のタイトルにもなっている 「セマンティックチャンキング」 について解説します。 まず、セマンティック(Semantic)とは?ということなのですが、以下のように定義されていました。 《言語学》意味の、語義の 次にチャンキングについては、Wikipedia によると あるものをより小さな断片に分割したり(チャンキング・ダウン)、逆により大きな断片にまとめたり(チャンキング・アップ)すること。 とあります。 一般的にRAGの話におけるチャンキングというと、Chat AIが参照するドキュメントの単位に分割することを指します。 分割するサイズなどによってもRAGの性能が変わってくることが言われており、チャンキング戦略はRAGにおける重要なポイントとされています。 これを踏まえると セマンティックチャンキング=意味の塊部分で区切って小さく分割する と捉えることができます。 ちなみに一般的なチャンキングの方法はというと 事前にチャンクサイズ(分割するサイズ)が固定で決まっており、 文章の意味や区切りなどはお構いなしに、決まったサイズで文章を区切るような方法が多いです。 メリット ではセマンティックチャンキングを使うメリットは何でしょうか? こちらも先程のサンプルのページにおいて言及されており、メリットとして以下の 3 つが挙げられています。 効率的な保管と検索(Efficient storage and retrieval) 関連性の向上(Improved relevance) 解釈可能性の向上(Enhanced interpretability) つまり ベクトルストアに対して効率的に保存ができ、関連の強いドキュメントの検索が可能になり、LLM が独立した情報として理解できるため、明確な回答が行えるようになる。 ということがいえます。 参考: セマンティックチャンキング – Microsoft Document Intelligence を活用したセマンティックチャンキング 今回は Document Intelligence を活用して、セマンティックチャンキングを行います。 Document Intelligence は PDF などのデータを、文章の構造を保ったまま Markdown 形式で出力することが可能です。 Markdown 形式で出力することで、見出し(#, ## etc.)の位置でチャンク化することが可能となり、セマンティックチャンキングを実現できます。 実装 では実際にステップを踏んで実行していきます。 ソースコードは基本的にサンプルのコピペです。 環境構築 必要なパッケージのインストールを行います。 ! pip install python-dotenv langchain langchain-community langchain-openai langchainhub openai tiktoken azure-ai-documentintelligence azure-identity azure-search-documents==11.6.0b3 次に環境変数(API キーや、Azure OpenAI Service のエンドポイント等)の設定をします。 .env ファイルを作成して、各パラメータを設定してください。 いつものお約束ですが、KEYの情報などはGitHubなどに公開しないよう注意して下さい。 """ This code loads environment variables using the `dotenv` library and sets the necessary environment variables for Azure services. The environment variables are loaded from the `.env` file in the same directory as this notebook. """ import os from dotenv import load_dotenv load_dotenv() os.environ["AZURE_OPENAI_ENDPOINT"] = os.getenv("AZURE_OPENAI_ENDPOINT") os.environ["AZURE_OPENAI_API_KEY"] = os.getenv("AZURE_OPENAI_API_KEY") doc_intelligence_endpoint = os.getenv("AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT") doc_intelligence_key = os.getenv("AZURE_DOCUMENT_INTELLIGENCE_KEY") .envファイル AZURE_OPENAI_ENDPOINT = "https://{resouce-name}.openai.azure.com/" AZURE_OPENAI_API_KEY = "{openapi_key_from_azure_portal}" AZURE_SEARCH_ENDPOINT = "https://{resource-name}.search.windows.net" AZURE_SEARCH_ADMIN_KEY = "{ai_search_key_from_azure_portal}" AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT= "https://{resource-name}.cognitiveservices.azure.com/" AZURE_DOCUMENT_INTELLIGENCE_KEY = "{document_intelligence_key_from_azure_portal}" 実装で活用する LangChainの機能を import します from langchain import hub from langchain_openai import AzureChatOpenAI from langchain_community.document_loaders import AzureAIDocumentIntelligenceLoader from langchain_openai import AzureOpenAIEmbeddings from langchain.schema import StrOutputParser from langchain.schema.runnable import RunnablePassthrough from langchain.text_splitter import MarkdownHeaderTextSplitter from langchain.vectorstores.azuresearch import AzureSearch Document Intelligence を使ったセマンティックチャンキング 実際に Document Intelligence を使って、PDF を読み込んでセマンティックチャンキングを行っていきます。 まずは、PDF を読み込んで Markdown 形式に変換します。 今回、対象とする PDF(it_servey.pdf)は 前回の記事 で扱ったものと同じものを用意します。 PDFは、ソースコードのファイルと同じ階層に置きました。 # Initiate Azure AI Document Intelligence to load the document. You can either specify file_path or url_path to load the document. loader = AzureAIDocumentIntelligenceLoader(file_path="it_servey.pdf", api_key = doc_intelligence_key, api_endpoint = doc_intelligence_endpoint, api_model="prebuilt-layout") docs = loader.load() もちろん StorageAccount などのサーバー上に公開されている PDF でも読み込み可能であり、 その場合は file_path の引数を、 url_path に変更して URL を設定します。 また Markdown への変換に関してですが、 AzureAIDocumentIntelligenceLoader の Optional の引数として用意されています。 ただし、Default として markdown が設定されているため、引数として明示的に記載する必要はありません。 mode: Optional[str]     The type of content representation of the generated Documents.     Use either "single", "page", or "markdown". Default value is "markdown". 次に、Markdown の内容を LangChain の機能を使って分割します。 今回は、Markdown の # , ## , ### のタグでチャンキングします。 # Split the document into chunks base on markdown headers. headers_to_split_on = [     ("#", "Header 1"),     ("##", "Header 2"),     ("###", "Header 3"), ] text_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on) docs_string = docs[0].page_content #DocumentIntelligenceでMarkdown化したコンテンツ splits = text_splitter.split_text(docs_string) #設定したsplitterで分割を行う # ヘッダー情報を表すmetadataのみ出力 for split in splits: print(split.metadata) LangChain の機能である、 MarkdownHeaderTextSplitter で Markdown のチャンキングを行い、splits の配列に格納します。 セマンティックチャンキングの出力確認 セマンティックチャンキングの出力確認のため、分割されたsplitsの中身のヘッダー情報を示す metadata のみを出力してみました。 {} # SIOS HOGEHOGE TECHNOLOGY 111-1111 東京都...の部分 {'Header 1': '調査概要 2024 年9月4日', 'Header 2': '調査目的'} {'Header 1': '調査方法'} {'Header 1': '市場概況'} {'Header 1': '市場概況', 'Header 2': 'クラウドコンピューティング ♡'} {'Header 1': '市場概況', 'Header 2': 'AIN'} {'Header 1': '市場概況', 'Header 2': 'AIN', 'Header 3': 'ブロックチェーン'} するとMarkdownの各ヘッダーの位置で文章が区切れてチャンキングされ、7つのデータに分割されていることが確認できます。 一応併せて元データの画像も載せておきます。 概ね良さそうに見えるのですが、いくつか誤っている部分も見られます。 split[2]の {'Header 1': '調査方法'} は、本来は {'Header 1': '調査概要 2024 年9月4日', 'Header 2': '調査方法'} が正しいように思います。 またsplit[6]に関しても、 {'Header 1': '市場概況', 'Header 2': 'AIN', 'Header 3': 'ブロックチェーン'} とあり、 ブロックチェーン が AI の子要素であると認識されていたりします。 これらはいずれもDocument IntelligenceにおいてMarkdownとして出力する際に、誤った見出しが付けられていることが原因でした。 ここらへんはDocument Intelligenceの精度向上を祈るしかないかもしれないですね…。 一方で、split[3]の中に 技術別市場シェア という見出しがありますが こちらはDocument Intelligenceから出力されたMarkdownには 見出し2(##)として記録されていました。(↓) ## 技術別市場シェア ただ、MarkdownHeaderTextSplitterの対象とならなかったようです。 このあたりの細かい振る舞いに関して、もう少し調査が必要そうです。 情報のベクトル化とベクトルストアへの格納 次にチャンク化した情報をベクトル化し、ベクトルストアに格納していきます。 ベクトル化を行う方法としては、Azure OpenAI Service の embedding model を活用し、 ベクトル化したデータは Azure AI Search に格納していきます。 Langchainの機能を使い、Azure OpenAI のEmbeddingモデルをラップして利用します。 # Embed the splitted documents and insert into Azure Search vector store # AOAIのEmbeddings LLMをラップ aoai_embeddings = AzureOpenAIEmbeddings(     azure_deployment="<Azure OpenAI embeddings model>", e.g., "text-embedding-ada-002"     openai_api_version="<Azure OpenAI API version>",  # e.g., "2023-12-01-preview" ) vector_store_address: str = os.getenv("AZURE_SEARCH_ENDPOINT") vector_store_password: str = os.getenv("AZURE_SEARCH_ADMIN_KEY") index_name: str = "<your index name>" #Azure AI Serch上に作成するIndex名 # ベクトルストアから簡単にデータを取り出すためのvector_storeのインスタンスを構築. # インスタンスを構築したタイミングでIndexが作成されます # 今回は対象をAzure AI Searchとする. vector_store: AzureSearch = AzureSearch(     azure_search_endpoint=vector_store_address,     azure_search_key=vector_store_password,     index_name=index_name,     embedding_function=aoai_embeddings.embed_query ## Azure OpenAI Serviceのembeddingsモデルを利用 ) # 先程チャンク化した情報を格納する # 格納するタイミングでベクトル化も行われる vector_store.add_documents(documents=splits) なお、利用する Azure OpenAI Service のバージョンは こちら を参照してください。 今だと、”2024-02-01″がGAされてる最新バージョンかと思います。  質問に関連する情報をベクトルストアから取り出す 次に実際にベクトルストアから、質問に対して関連した情報を抽出します。 この時 Retriever というインスタンスを作成して情報を取り出していきます。 この Retriever の振る舞いに関しては こちら の記事で分かりやすくまとまっていたので参照ください。 サンプルコードでは SearchType を similarity としているため類似検索が行われ、 search_kwargs を k:3 としているため 3 件の情報が返ってくるよう設定されています。 その後、実際に Retriever に質問を与え、関連する情報を retrieved_docs に格納し表示しています。 # Retrieve relevant chunks based on the question # 先ほど作成したベクトルストアをRetrieverとして定義 retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 3}) retrieved_docs = retriever.get_relevant_documents(     "この調査の目的は何ですか?" ) print(retrieved_docs) #サンプルコードでは[0].page_contentだけ出力していますが、内容理解のため全部出力します。 出力結果 出力された内容としては以下のようなものです。(分かりやすさのため改行しています。) [Document(page_content='本市場調査の目的は、最新の IT 技術動向を詳細に把握し、各技術の市場シェア、成長予測、およ び競争状況を包括的に明らかにすることであり、特にクラウドコンピューティング、AI(人工知能)、loT (モノのインターネット)、およびブロックチェーン技術の各分野に焦点を当てるものである。', metadata={'Header 1': '調査概要 2024 年9月4日', 'Header 2': '調査目的'}), Document(page_content='1\\. プライマリーデータはアンケート調査およびインタビューによって収集し、直接的かつ現場の 視点から得られる情報を重視する一方で、セカンダリーデータは公開データベースおよび業 界レポートから取得し、信頼性の高い既存の情報源を利用して補完的なデータ収集を行う。  \n2\\. データ分析に関しては、定量分析として統計解析ソフトウェアを駆使して大量の数値データを 精緻に解析し、定性分析として内容分析およびテーマ別分析を行い、収集されたデータの質 的側面を多角的に検討する。', metadata={'Header 1': '調査方法'}), Document(page_content='AI 技術に関しては、IBM が市場シェアの 25%を占め、Google が 20%、Microsoft が 18%、その他が 37%を占めている。', metadata={'Header 1': '市場概況', 'Header 2': 'AIN'}), Document(page_content='クラウドコンピューティング技術に関しては、AWS が市場シェアの 32%を占め、Microsoft Azure が 22%、Google Cloud が18%、その他が28%を占めている。', metadata={'Header 1': '市場概況', 'Header 2': 'クラウドコンピューティング ♡'})] 今回の質問に対して、関連するドキュメントが4件抽出されてました。 ただ、 search_kwargs の値として "k":3 を設定しており、3件返ってくるのかなと思っていました。 retrieverの値をデバッグしてみたところ、k=4が設定されていました。 kのDefault値が4みたいなので、うまくパラメータが設定できていないみたいですね。 このあたりlangchainをうまく使いこなせてない可能性があるので、また再調査します。 件数は一旦さておき、出力の中身としては 以下2 つの内容を含んでいることが分かります。 page_content 参考にした文章内容 metadata Section情報(今回でいえばMarkdown における見出し位置) 一番上の結果のpage_contentは「本市場調査の目的は~」から始まっており、まさに質問に関連する部分ですね。 AI Searchによって、質問に関連するドキュメントが抽出できていることが分かります。 プロンプトを構築する 次にプロンプトを構築していきます。 「え?”このドキュメントの想定される読者は誰ですか?”がプロンプトじゃないんですか?」 と思われた方もいるんじゃないでしょうか? 自分も最初はそうでした。 ただ、LLM に投げかけるプロンプトの内容は非常に重要であり、 プロンプトエンジニアリング としても技術が確立されています。 プロンプトエンジニアリングの知識がないユーザーからの質問文をそのままプロンプトとして投げた場合、求める品質の回答が得られない場合があります。 そのため、システム側である程度プロンプトのテンプレートを構築しておき、そこにユーザーからの質問を組み込む形を取ります。 この時に利用するのが LangChain の Prompt Template の機能になります。 では実際にソースコードを見てみます。 なお、今回は既に定義された Prompt Template を利用します。 # Use a prompt for RAG that is checked into the LangChain prompt hub (https://smith.langchain.com/hub/rlm/rag-prompt?organizationId=989ad331-949f-4bac-9694-660074a208a7) prompt = hub.pull("rlm/rag-prompt") hub というのは GitHub とか、DockerHub のようにこう言った Template などが確立されている場所だと思ってください。 この hub から今回は rlm/rag-prompt というテンプレートを pull してきます。 参照リンク へ飛んで、テンプレートの中身を見てみると以下のように定義されています。 You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise. Question: {question} Context: {context} Answer: なおこの時、変数 { } として定義されている値としては以下のような内容になります。 question ユーザーからの質問文そのまま contex ベクトルストアから抽出した関連情報 このようなテンプレートを介すことで、ユーザーから単純な質問を投げられたとしても LLM にとって分かりやすい形のプロンプトを構築することが可能になります。 この後のステップとして、このテンプレートに入れるための {question} と {context} を構築していきます。 余談 今回日本語のシステムを構築しようとしているので、日本語でテンプレート定義しなおした方がいいかもしれないです。 ただ逆に LLM は英語の方が理解しやすいかとおもうので、ぎりぎりまで英語でやり取りした方がいいかもしれないです。 このあたり日本語の扱いが難しいですね… RAG の Chain 構築 最後に、実際に RAG の Chain を構築していきます。 Chain とはその名の通り、LLM や Prompt などのコンポーネントを繋げて、システムを作り上げる機能になります。 なお Chain を実装するにあたり、 LangChain Expression Language (LCEL) といった記述方法を利用しています。  LLM や Prompt 等のコンポーネントを | で繋げるだけで簡単に Chain を構築することが可能です。 Linux のパイプみたいな感じですね。 llm = AzureChatOpenAI(     openai_api_version="<Azure OpenAI API version>",  # e.g., "2023-12-01-preview"     azure_deployment="<your chat model deployment name>",     temperature=0, ) # ベクトルストアから取り出したdocumentからpage_contentの内容だけを抽出し、連結します。 def format_docs(docs):     return "\n\n".join(doc.page_content for doc in docs) rag_chain = (     {"context": retriever | format_docs, "question": RunnablePassthrough()} # step1     | prompt # step2     | llm # step 3     | StrOutputParser() # step4 ) llm の変数に関しては、Azure OpenAI Service の Chat の LLM モデルを構築します。 その後、処理の chain を rag_chain 変数として定義します。 chain のステップとしては以下の通りです。 テンプレートに与える変数である context と、 question を構築する 質問文が retriever に渡され、関連情報が得られた後に context の中に代入される question に関しては、RunnablePassthrough()となっているので、そのまま文字列が渡される テンプレートを利用してプロンプトを構築する プロンプトを Chat LLM に投げる 得られた回答から必要な部分のみ抽出 1, 2, 3 はこれまで言及してきた部分を繋げただけなので省略します。 4 の StrOutputParser() に関しては、LangChain の Output parsers の機能になります。 Chat LLM から得られた回答のうち、最も関連性が高いものを文字列として出力します。 RAG の chain 実行 あとは先程組んだ Chain に質問文を与えて実行するだけです。 rag_chain.invoke("この調査の目的は何ですか?") 以下の回答が得られました。 本調査の目的は、最新の IT 技術動向を詳細に把握し、各技術の市場シェア、成長予測、および競争状況を包括的に明らかにすることです。 ドキュメント参照する RAG システム サンプルのドキュメントにはもう一つ例が載っていました。 特に難しい事はしていないですが、少し複雑な内容になっているので解説していきたいと思います。 まずこのコードの目的ですが、 「回答に 加えて 、参照したドキュメントの情報を返すこと」 を目的としています。 どんな情報を参照したうえで回答したかをユーザーに教えることができるので、よりユーザーフレンドリーですね。 ソースコードの解説をする前に、全体の処理の流れを図に起こしたので載っけておきます。 コード見ていて分からなくなったらこの図を見てみてください。 では実際にソースコードを見ていきます。 # Return the retrieved documents or certain source metadata from the documents from operator import itemgetter from langchain.schema.runnable import RunnableMap rag_chain_from_docs = (     {         "context": lambda input: format_docs(input["documents"]),         "question": itemgetter("question"),     }     | prompt     | llm     | StrOutputParser() ) rag_chain_with_source = RunnableMap(     {"documents": retriever, "question": RunnablePassthrough()} ) | {     "documents": lambda input: [doc.metadata for doc in input["documents"]],     "answer": rag_chain_from_docs, } rag_chain_with_source.invoke("<your question>") 少し長いので分割してみていきます。 まず後半部分から rag_chain_with_source = RunnableMap(     {"documents": retriever, "question": RunnablePassthrough()} ) | {     "documents": lambda input: [doc.metadata for doc in input["documents"]],     "answer": rag_chain_from_docs, } rag_chain_with_source.invoke("<your question>") rag_chain_with_source という chain が構築されており、最後にその chain が実行されています。 chain の中身を見ていきます。 まずは入力に対して retriever の処理を加えたものを documents へ、何も処理を加えないものを questions へ代入します。 ただ、先程の例では RunnableMap を利用しておらず、今回の例で RunnableMap を利用してる点が理解できませんでした。 この点分かる方いらしたらコメントお願いします。 次に、 documents と questions を入力として {     "documents": lambda input: [doc.metadata for doc in input["documents"]],     "answer": rag_chain_from_docs, } の中身を定義していきます。 まず documents の値に関しては、 入力として与えられた documents の metadata 情報を、今回の定義の documents に代入します。 answer に関しては、入力( documents , questions )に対して rag_chain_from_docs の処理を行ったものを代入します。 rag_chain_from_docs = (     {         "context": lambda input: format_docs(input["documents"]),         "question": itemgetter("question"),     }     | prompt     | llm     | StrOutputParser() ) rang_chain_from_docs において、まず context と question を定義します。 context に関しては、 入力として与えられた documents の値に対して、format_docs の処理(連結処理)を行ってから context に代入します。 question に関しては、入力の question の値をそのまま入れます。(つまりユーザーからのリクエストそのままですね。) こうして作られた 値 に対して prompt, llm, StrOutputParser のコンポーネントの処理を通して出力します。 そして出力したものが、 answer の中に入れることになります。。 ということで、最終的に得られた出力はこちらです。 {'documents': [{'Header 1': '調査概要 2024 年9月4日', 'Header 2': '調査目的'}, {'Header 1': '調査方法'}, {'Header 1': '市場概況', 'Header 2': 'AIN'}, {'Header 1': '市場概況', 'Header 2': 'クラウドコンピューティング ♡'}], 'answer': '本調査の目的は、最新の IT 技術動向を詳細に把握し、各技術の市場シェア、成長予測、および競争状況を包括的に明らかにすることです。'} document の中には参照したドキュメントの Metadata が入っており answer の中には Chat LLM から得られた回答が入っていることが分かります。 まとめ Document Intelligence を利用し、セマンティックチャンキングを行う RAG システムを構築しました。 サンプルでも丁寧に解説してくれているのですが、初学者の自分からすると分からないことも結構あったので整理してみました。 Document Intelligenceというよりは、langchainのRAG構築部分が少し複雑でしたね。 今回は「やってみた」で終わってしまったので、また機会があれば評価ツールなどを使いながら マンティックチャンキングの効果を測定してみたりしたいと思います。 ではまた! ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【徹底解説】Document Intelligenceを利用してRAGを構築する【セマンティックチャンキング】 first appeared on SIOS Tech. Lab .
アバター
初めに PS/SLの佐々木です。 今までWeb3チームではEthereumのようなパブリックチェーンを使用しての開発や技術検証を行ってきましたが、今年から新しい取り組みとしてコンソーシアム型のチェーンやプライベートチェーンを使用した開発を行っていこうと思っています。 コンソーシアムチェーンで最も有名なところで行くとHyperledger Fabricが候補に挙がってくるのでキャッチアップがてらチュートリアル( 参考 )を進めて使用感を見てみようと思っています。 Hyperledger Fabricの詳細な説明は別ブログで行いますので今後出る記事を参考にしてください。 今回はHyperledger fabricをドキュメント通りに動かしてみようと思います。 動作環境 windows 10 pro wsl ubuntsu 20.04.3 事前準備 Git cURL Docker Go Hyperleger install サンプルとバイナリとDockerイメージをインストールできる curl -sSL https://bit.ly/2ysbOFE | bash -s ka-sasaki@1020-00001:~/web3$ ls fabric-samples ka-sasaki@1020-00001:~/web3$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE hyperledger/fabric-peer 2.5 2d02d49d3d1a 3 weeks ago 142MB hyperledger/fabric-peer 2.5.8 2d02d49d3d1a 3 weeks ago 142MB hyperledger/fabric-peer latest 2d02d49d3d1a 3 weeks ago 142MB hyperledger/fabric-orderer 2.5 c833f32f9244 3 weeks ago 111MB hyperledger/fabric-orderer 2.5.8 c833f32f9244 3 weeks ago 111MB hyperledger/fabric-orderer latest c833f32f9244 3 weeks ago 111MB hyperledger/fabric-ccenv 2.5 b4bf6fadf0c7 3 weeks ago 638MB hyperledger/fabric-ccenv 2.5.8 b4bf6fadf0c7 3 weeks ago 638MB hyperledger/fabric-ccenv latest b4bf6fadf0c7 3 weeks ago 638MB hyperledger/fabric-baseos 2.5 6f1a89df96f3 3 weeks ago 129MB hyperledger/fabric-baseos 2.5.8 6f1a89df96f3 3 weeks ago 129MB hyperledger/fabric-baseos latest 6f1a89df96f3 3 weeks ago 129MB hyperledger/fabric-ca 1.5 c2449e2873d5 3 weeks ago 209MB hyperledger/fabric-ca 1.5.11 c2449e2873d5 3 weeks ago 209MB hyperledger/fabric-ca latest c2449e2873d5 3 weeks ago 209MB サンプルとイメージがダウンロードされていることを確認できます。 最後にbinファイルのパスを通す export PATH=$PATH:$HOME/fabric-samples/bin echo "export PATH=\\$PATH:\\$HOME/fabric-samples/bin" >> ~/.bashrc source ~/.bashrc テストネットワークの構築 Hyperledger Fabricの構築 ディレクトリは/test-network peer 2つ orderer 1つ CAはなし test-networkディレクトリに移動し、 network.sh を使用してローカルマシン上のDockerからネットワークを構築する 以前の実行結果などあれば削除する ./network.sh down ネットワーク起動 ./network.sh up 起動するとPeer ノード二つとordererノード一つが起動していることが確認できる。 docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f3bf9f73341c hyperledger/fabric-peer:latest "peer node start" 3 minutes ago Up 3 minutes 0.0.0.0:7051->7051/tcp, :::7051->7051/tcp, 0.0.0.0:9444->9444/tcp, :::9444->9444/tcp peer0.org1.example.com 0c7fb71179d6 hyperledger/fabric-peer:latest "peer node start" 3 minutes ago Up 3 minutes 0.0.0.0:9051->9051/tcp, :::9051->9051/tcp, 7051/tcp, 0.0.0.0:9445->9445/tcp, :::9445->9445/tcp peer0.org2.example.com 8d55459a26dd hyperledger/fabric-orderer:latest "orderer" 3 minutes ago Up 3 minutes 0.0.0.0:7050->7050/tcp, :::7050->7050/tcp, 0.0.0.0:7053->7053/tcp, :::7053->7053/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp orderer.example.com Channelの作成 ./network.sh createChannel Channel 'mychannel' joined Org1とOrg2がmychannelに参加できたことが確認できる チェーンコードのデプロイ ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go goで作成されたchaincode(ethereumでいうところの smart contract)をデプロイし、channel上でチェーンコードを使用できるようにする ネットワークとやり取り export FABRIC_CFG_PATH=$PWD/../config/ // 以下はOrg1のPeerに対して実行するための環境変数 export CORE_PEER_TLS_ENABLED=true export CORE_PEER_LOCALMSPID="Org1MSP" export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp export CORE_PEER_ADDRESS=localhost:7051 台帳の初期化 peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"InitLedger","Args":[]}' // 以下のようなメッセージが出ればOK -> 2024-06-17 18:48:11.035 JST 0001 INFO [chaincodeCmd] chaincodeInvokeOrQuery -> Chaincode invoke successful. result: status:200 台帳に登録されているAssetの一覧を取得 peer chaincode query -C mychannel -n basic -c '{"Args":["GetAllAssets"]}' // 以下のようなメッセージが出ればOK -> [{"AppraisedValue":300,"Color":"blue","ID":"asset1","Owner":"Tomoko","Size":5},{"AppraisedValue":400,"Color":"red","ID":"asset2","Owner":"Brad","Size":5},{"AppraisedValue":500,"Color":"green","ID":"asset3","Owner":"Jin Soo","Size":10},{"AppraisedValue":600,"Color":"yellow","ID":"asset4","Owner":"Max","Size":10},{"AppraisedValue":700,"Color":"black","ID":"asset5","Owner":"Adriana","Size":15},{"AppraisedValue":800,"Color":"white","ID":"asset6","Owner":"Michel","Size":15}] Assetの移動 peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"TransferAsset","Args":["asset6","Christopher"]}' // 以下のようなメッセージが出ればOK -> 2024-06-17 18:51:02.629 JST 0001 INFO [chaincodeCmd] chaincodeInvokeOrQuery -> Chaincode invoke successful. result: status:200 payload:"Michel" // 以下はOrg2のPeerに対して実行するための環境変数 export CORE_PEER_TLS_ENABLED=true export CORE_PEER_LOCALMSPID="Org2MSP" export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp export CORE_PEER_ADDRESS=localhost:9051 転送結果の確認 peer chaincode query -C mychannel -n basic -c '{"Args":["ReadAsset","asset6"]}' // 以下のようなメッセージが出ればOK -> {"AppraisedValue":800,"Color":"white","ID":"asset6","Owner":"Christopher","Size":15} ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Hyperledeger Fabricのチュートリアルをやってみる Part1 first appeared on SIOS Tech. Lab .
アバター
想定読者 オブジェクト指向プログラミングに興味がある 可読性が高く、変更に強いプログラムを作りたい SOLID原則を理解して周りに「ドヤァ( ^)o(^ )」したい(自己満足でも可) はじめに SOLID原則は以下の5つの原則の頭文字を並べて出来たネーミングです。 単一責任の原則(single-responsibility principle) There should never be more than one reason for a class to change. 変更するための理由が、一つのクラスに対して一つ以上あってはならない。 開放閉鎖の原則(open/closed principle)←今回のターゲット software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. ソフトウェアの実体(クラス、モジュール、関数など)は、拡張に対して開かれているべきであり、修正に対して閉じていなければならない リスコフの置換原則(Liskov substitution principle) Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. ある基底クラスへのポインタないし参照を扱っている関数群は、その派生クラスのオブジェクトの詳細を知らなくても扱えるようにしなければならない インターフェース分離の原則(interface segregation principle) Many client-specific interfaces are better than one general-purpose interface. 汎用なインターフェースが一つあるよりも、各クライアントに特化したインターフェースがたくさんあった方がよい 依存性逆転の原則(dependency inversion principle) High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces), [not] concretions. 上位モジュールはいかなるものも下位モジュールから持ち込んではならない。双方とも具象ではなく、抽象(インターフェースなど)に依存するべきである ふむ、なんじゃこりゃって感じですよね。大丈夫です。今から具体例をTypeScriptのコードを用いて解説していきます! 解説は、「悪い例」→「何が悪いか解説」→「良い例」といった順序になります。 開放閉鎖の原則 // 悪い例 // クレジットカードのランクをオブジェクトリテラルで定義 const CreditCardRank { NORMAL: 0, GOLD: 1, PLATINUM: 2, BLACK: 3, } as const; type CreditCardRank = (typeof CreditCardRank)[keyof typeof CreditCardRank]; // クレジットカードを表現するクラス class CreditCard { constructor(private CreditCardRank rank) {} } class Program { run1() { const creditCard = new CreditCard(CreditCardRank.GOLD); if (creditCard.rank !== CreditCardRank.NORMAL) { // クレジットカードのランク判別処理 // 省略 } } run2() { const creditCard = new CreditCard(CreditCardRank.PLATINUM); if (creditCard.rank !== CreditCardRank.NORMAL) { // クレジットカードのランク判別処理 // 省略 } } run3() { const creditCard = new CreditCard(CreditCardRank.BLACK); if (creditCard.rank !== CreditCardRank.NORMAL) { // クレジットカードのランク判別処理 // 省略 } } } 上記のコードの説明を以下に箇条書きします。 CreditCardというクレジットカードを表現したクラス定義 CreditCardはrankというクレジットカードのランクを表す属性を持つ Programはアプリケーションの実行処理を記述したクラス(run1, run2, run3という三つの処理が定義されている) run1, run2, run3それぞれにクレジットカードのランク判別処理が記述されている さて、一体このコードはどこが悪いのでしょうか?「なんかクレジットカードのランク判別処理が重複しているなー」という感想くらいでしょうか? 上記の感想を抱いた読者の方は大変鋭い感覚を持っています。同じようなコードが至る所に表れているのは間違いなく「このソースコードには改善の余地がある」という証です。 ここで仮にクレジットカードのランク名「NORMAL」が「ORDINARY」に変更されたとします。変更されるソースコードの処理内容に注目してください。 // 悪い例(NORMALをORDINARYに変更) // クレジットカードのランクをオブジェクトリテラルで定義 const CreditCardRank { // NORMAL: 0, ORDINARY: 0, // 変更後 GOLD: 1, PLATINUM: 2, BLACK: 3, } as const; type CreditCardRank = (typeof CreditCardRank)[keyof typeof CreditCardRank]; // クレジットカードを表現するクラス class CreditCard { constructor(private CreditCardRank rank) {} } class Program { run1() { const creditCard = new CreditCard(CreditCardRank.GOLD); /* if (creditCard.rank === CreditCardRank.NORMAL) { // クレジットカードのランク判別処理 // 省略 } */ // 変更後 if (creditCard.rank !== CreditCardRank.ORDINARY) { // クレジットカードのランク判別処理 // 省略 } } run2() { const creditCard = new CreditCard(CreditCardRank.PLATINUM); /* if (creditCard.rank === CreditCardRank.NORMAL) { // クレジットカードのランク判別処理 // 省略 } */ // 変更後 if (creditCard.rank !== CreditCardRank.ORDINARY) { // クレジットカードのランク判別処理 // 省略 } } run3() { const creditCard = new CreditCard(CreditCardRank.BLACK); /* if (creditCard.rank === CreditCardRank.NORMAL) { // クレジットカードのランク判別処理 // 省略 } */ // 変更後 if (creditCard.rank !== CreditCardRank.ORDINARY) { // クレジットカードのランク判別処理 // 省略 } } } 上記のソースコードを見ると、変更が入った4つの箇所のうち1つ目は単純に「NORMALからORDINARY」に名称変更したことを定義し直しただけなのでOKです。 問題なのは残りの変更箇所で「全て同じ変更内容」を3つの箇所に施しました。 まさにこれこそが上記のソースコードが悪い例であることの証明になります。「同じような処理がn個所に散りばめられている状態は単純に考えて修正量をn倍に増やす」ことになります。 ここで今回の主題である「開放閉鎖の原則」の定義をもう一度お見せします。 software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. ソフトウェアの実体(クラス、モジュール、関数など)は、拡張に対して開かれているべきであり、修正に対して閉じていなければならない うーん、少しこの定義自体が分かりにくいのでもう少し分かりやすい言葉に書き直してみます。 拡張しやすく、修正しやすいコードにすべき 今回のソースコードは「クレジットカードのランク判別処理のNORMALをORDINARYに変更する」という1つの修正内容に対して、修正箇所が3倍になってしまったので「修正しやすいコードにすべき」という部分に違反していることになりますね。 ということで早速、違反箇所を修正していきましょう! // 良い例 // クレジットカードを表現するクラス class NormalCreditCard {} class GoldCreditCard {} class PlatinumCreditCard {} class BlackCreditCard {} class Program { run1() { const creditCard = new NormalCreditCard; } run2() { const creditCard = new GoldCreditCard; } run3() { const creditCard = new BlackCreditCard; } } 上記のソースコードは悪い例のように「クレジットカードの種別をCreditCardのrankメンバ変数で管理する」のではなく「クラスごとに分別」するようにしています。 このようにすることで「NORMALからORDINARYへの名称変更」という修正が入っても // 良い例 // クレジットカードを表現するクラス // class NormalCreditCard {} class OrinaryCreditCard {} // 変更後 class GoldCreditCard {} class PlatinumCreditCard {} class BlackCreditCard {} class Program { run1() { // creditCard = new NormalCreditCard; const creditCard = new OrinaryCreditCard; // 変更後 } run2() { const creditCard = new GoldCreditCard; } run3() { const creditCard = new BlackCreditCard; } } このようなソースコードになり、上記の修正は明らかに「同じような修正内容を複数個所に施す」といった作業から解放されています。 これで「開放閉鎖の原則」についてマスターしたといっても過言ではありません。 是非「私はopen/closed principleを完全に理解した」とドヤってください( ^)o(^ ) 次回はSOLIDの「L」の部分である「リスコフの置換原則」について解説します! 良かったら覗いてみてください。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post SOLID原則って何ぞや?(開放閉鎖の原則編) first appeared on SIOS Tech. Lab .
アバター
Azure OpenAI ServiceによるRAGハンズオン開催のお知らせ 〜独自データを用いた生成AIの利活用を学ぶ〜【初級編】 この度、「 Azure OpenAI ServiceによるRAG実装ガイド 」 をダウンロードしていただいた方限定に、 RAGのハンズオンを無料開催いたします。 本ガイドの目的は、これからRAGを始める人に参考にしていただくため、「シンプル」「強力」「すぐ動く」をモットーにしたRAGアプリケーションを実装するためのガイドです。本ガイドではRAGのアーキテクチャのみならず「実際に動くコード」もご用意致しました。読者の皆様には、コードを動かしながらRAGをより深くご理解頂けることを一番の目的としております。 ハンズオンのお申込みは先着順となっておりますので、 ご興味がある方はお早めにお申し込みください。   【無償】初級テクニカルトレーニング Azure OpenAI ServiceによるRAGハンズオン 〜独自データを用いた生成AIの利活用を学ぶ〜【初級編】 <概要> Azure OpenAI Serviceの概要説明 RAGの概要説明 RAGの構築 <日時> 2024年7月24日(水)13:00~17:00(受付12: 30~) <場所> 〒106-0047 東京都港区南麻布2-12-3 サイオスビル3F <得られるスキル> Azure OpenAI Serviceの基礎 Azure OpenAI Serviceがどのようなサービスであり、 どのように利用するのかを学ぶことができます。これにより、 クラウドベースのAIサービスの活用方法を理解し、 自身のプロジェクトに適用するための基礎を築くことができます。 RAGの仕組み RAG(Retriever-Augmented Generation)の基本的な仕組みを理解し、 独自データを活用した生成AIの基礎理論を学ぶことができます。 RAG構築方法 実際にRAGを構築するための基本的な手順を学びます。 ハンズオンを通じて、 独自データの準備から外部データベースへの登録、 そして生成AIを用いた回答生成までの流れを体験し、 実践的なスキルを身につけることができます。 <費用> 無料 <定員> 16名(先着順) <対象者> Azure OpenAI ServiceによるRAG実装ガイドをダウンロードされた方 企業にお勤めの方 <準備いただくもの> お名刺 無線LANを利用できるPC(Windows、 MacどちらでもOK) GitHubのアカウント(GitHub Codespacesの180CPU時間の無料枠利用のためFr eeでOK) Azureのサブスクリプション Azureのサブスクリプションで利用可能なAzure Open AI Service( 承認には時間を要するため以下のURLより早めにご申請ください 。ご用意が難しい場合は別途ご相談ください。) https://aka.ms/oai/access <注意事項> 本ハンズオンでは、 受講者ご自身のAzureサブスクリプションを使用していただく ことが前提となります。そのため、 サブスクリプションにかかる費用は受講者様のご負担となります。 ハンズオン中に発生するAzure利用料金は、通常、 数百円程度、1000円未満と見込んでおります。 Azure OpenAI ServiceによるRAG実装ガイドをダウンロードしていない 方がお申込みをした場合、キャンセルとさせていただきますので、 あらかじめご了承ください。   【有償】支援サービス 上級テクニカルトレーニング Azureの最先端技術を駆使したRAGハンズオンです。 このハンズオンでは、最新の検索手法である「 セマンティックハイブリッド検索」、Azure Static Web Apps、Azure Functionsによるフルサーバーレス構成など、 最新技術を用いたRAGの構築方法をお伝えします。 業務委託・技術支援 当社では、AIだけでなく、 クラウドネイティブなアプリケーション開発、 既存システムのクラウド移行・モダナイゼーション、 システムの基盤構築など、 クラウドを中心としたシステム開発あるいは技術支援なども行って おります。システムでの困り事がありましたら、 ぜひご相談ください。 チャットボット作成 (RAGツール開発) RAG(Retrieval-Augmented Generation)を中心に、 話題の生成AIを活用した業務の効率化、 DX化をお手伝いいたします。社内ノウハウの見える化、 カスタマーサポートの省力化、 チャットボットアプリケーションの導入など、 社内資源の有効活用をお考えの場合は、ぜひご相談ください。 AI関連アプリ開発 既存のアプリ・システムを、もっと便利に・ 楽にできないかお考えですか?RAGやLangChain などにより生成AIを活用すれば、 今まで以上により業務効率を上げるのに効率的なアプリの開発を行 うことができます。 まずはコンサルティングという形からご相談いただくことも可能で す。AIで迷われたら、ぜひご相談ください。 Azure CSP販売・技術支援 Azureのライセンス販売を通じてお客様のビジネスをご支援し ています。 Azureライセンスの提供からAI技術のコンサルティングまで 全て当社にお任せください。​​​​ ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【7/24無料開催】Azure OpenAI ServiceによるRAGハンズオン 〜独自データを用いた生成AIの利活用を学ぶ〜初級編 first appeared on SIOS Tech. Lab .
アバター
始めに 皆さんこんにちは!新卒3年目エンジニアの細川です。 今回はTypeScriptの型ジェネリクスについてまとめていこうと思います。 TypeScriptを書いてて、 <T> とかよく見るけど何それ?という方は是非読んでみてください! 型ジェネリクスって? 型ジェネリクスは、型を変数として受け取り、利用するという仕組みです。 皆さんも以下のような <T> とか <K> の記述をよく目にすることがあるかと思います。 type EchoType<T> = T type StringType = EchoType<string> // string型 これが型ジェネリクスです。Tという型変数を受け取り、それを右辺で利用することができます。この例では受け取った型をそのまま代入しているので、渡した型そのままとなります T の部分は引数の名前なので、なんでもいいのですが、慣例的に T や K などがよく使われます。 関数などの引数と同様に、複数の型を受け取ることもできます。 type UnionType<K, T> = K | T type StringOrBooleanType = UnionType<string, boolean> // string | boolean型 型のみ違う関数への利用 サバイバルTypeScript で利用方法として紹介されているものに、”型のみ”が異なる関数の実装を一つにまとめられるというのがあります。サンプルを見てみましょう。 まず、以下のような、五分五分の確立で1つめの引数か2つ目の引数のどちらかを返す関数を考えてみます。 const chooseRandomlyString = (v1: string, v2: string): string => {   return Math.random() <= 0.5 ? v1 : v2; } この関数を使う場合は以下のように記述します。 const winOrLose = chooseRandomlyString("勝ち", "負け"); console.log(winOrLose);// 勝ち or 負け 次に、文字列だけでなく、数値の抽選も行う必要が出てきた場合のことを考えてみます。 この場合、chooseRandomlyNumberを作る必要があります。   // 数値用の抽選関数 const chooseRandomlyNumber = (v1: number, v2: number): number => { return Math.random() <= 0.5 ? v1 : v2; } const num: number = chooseRandomlyNumber(1, 2); console.log(num); // 1 or 2 このように内部の実装が同じでも型が違うだけで複数関数を作成しなければならない場合に型ジェネリクスを利用することができます。   const chooseRandomly = <T,>(v1: T, v2: T): T => {   return Math.random() <= 0.5 ? v1 : v2; } chooseRandomly<string>("勝ち", "負け"); chooseRandomly<number>(1, 2); このように型変数Tを受け取ることで、一つの実装で様々な型に対応できるようになります。 注意点として、上記のようなアロー関数で記述する場合 <T,> のカンマを忘れるとエラーが出る点に注意してください。 応用編 この型ジェネリクスを応用することで、様々な便利な型を作成することができます。 例えば、以下のようにある型のundefined許容の型を作ったり、配列にしたり、型をより抽象的に定めることもできるようになります。 // undefined許容に変換 type Optional<T> = T | undefined // その型の配列型に変換 type ArrayType<T> = T[] // 受け取った型のvalue1, value2というプロパティを持つ型 type Box<T> = { value1: T; value2: T; } const numberBox: Box<number> = { value1: 42 , value2: 2}; const groupBox: Box<string[]> = { value1: ["apple", "banana"], value2: ["hoge"]}; 実際には他に MappedTypes などと組み合わせて、より強力な型を作成可能ですが、それは後程 MappedTypes についての記事を書く際に紹介しようと思います。 extendsについて extends を用いることで、受けとる型変数を制限することもできます。 元々 extends 自体はinterfaceの継承で使うものというイメージが強く他言語でも目にする機会もあると思います。(例: Javaのextends ) 以下に基本的な extends の使い方を示します。 interface People { name: string age: number } // Peopleを継承したEmployeeを作成 interface Employee extends People { companyName: string employeeNumber: number } // Peopleのプロパティに加えて、Employeeで追加したプロパティを持つ const emp1: Employee = { name: "Taro",      // Peopleのプロパティ age: 25,        // Peopleのプロパティ companyName: "SIOS", // Employeeのプロパティ employeeNumber: 12345 // Employeeのプロパティ } このような使い方は皆さんも馴染み深いものではないでしょうか? この extends を使うと、型ジェネリクスで受け取る型を制限することもできます。 使い方としては以下の通りです。 type MyString<T extends string> = T // MyStringは型変数としてstringしか受け取れない const mystr: MyString<string> = "mystr" T が extends の後ろの型を継承するようになり、例の場合ではstring型しか渡せなくなります。 渡そうとすると以下のようにコンパイルエラーとなります。 このように、型定義の型引数の名前を定義する部分に extends を追加することで、型引数として受け取る型を制限することができます。 TypeScriptの組み込みの型には結構 extends も利用されています。もし型の実装の中に extends を見つけたら、受け取る型を絞っているんだなと思いましょう。 まとめ 型ジェネリクスは引数として型を受け取って変数のように利用できる仕組み type UnionType<K, T> = K | T type StringOrBooleanType = UnionType<string, boolean> // string | boolean型 extendsを使うと受け取る型に制約を持たせられる type MyString<T extends string> = T // MyStringは型変数としてstringしか受け取れない const mystr: MyString<string> = "mystr" 終わりに 今回はTypeScriptの型ジェネリクスについてまとめました。 知っているとなんともないですが、知らずに見ると「難しそう!」と感じてしまうのではないでしょうか? この記事で少しでも型ジェネリクスへの理解が進めば幸いです。 型ジェネリクスを応用すると非常に強力で柔軟な型もたくさん作れるので、追々紹介したいと思います! 他にもTypeScriptの あれこれ についてまとめていますのでぜひ読んでみてください!   ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post TypeScriptのあれこれ⑥型ジェネリクス  とかextendsって何? first appeared on SIOS Tech. Lab .
アバター
サイオステクノロジー武井です。今回は、まだ今年半分は残っていますが、おそらく今年のベストバイ間違い無しのデジタルガジェット「Anker 733 Power Bank」を紹介したいと思います。 これは、モバイルバッテリーでありながら、ACアダプターにもなるというすぐれものです。3つのUSBポート (USB-C×2、USB-A×1)を備えており、iPhoneとMacへの給電が可能です。 そして自身もモバイルバッテリーとして動作するので、例えばACアダプターとして動作中に充電しておくことが可能です。 今までは、MacのACアダプター、iPhoneの充電器、そしてモバイルバッテリーと3つのものを持って外出していましたが、Anker 733 Power Bankはこれら3つの用途をすべてまかないます。 例えば、出社するときに電車に乗り、会社につきます。Anker 733 Power BankをMacのACアダプターとして会社でお仕事をしている間に充電して、帰りの電車の中でモバイルバッテリーとしてAnker 733 Power Bankを使います。 つまり、ACアダプターとして使いながら充電してくれるので、ざわざわ「充電する」ということを意識しなくても、いつの間にかモバイルバッテリーの電池はいつも満タンになっており、必要なときにいつでも使えるというものです。 まぁ、お値段もそれなりにはしますが、充電という手間は省けるし荷物は減るし、ホントいい感じです。 絶対これは買いですよ(^o^) ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post モバイルバッテリーAnker 733 Power Bankの紹介 first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの佐藤 陽です。 今回は Azure の AI Services の 1 つである Azure AI Document Intelligence について入門していきたいと思います。 PDF や画像データを扱いやすいデータにしたい! RAG の開発で PDF の情報を独自データとして活用したい! Document Intelligence から返ってくるパラメータの構造が分からない。 といった方は最後までご覧ください。 特に、3つ目のDocument Intelligence から返ってくるパラメータについて知りたい方は コチラ へどうぞ! AI Document Intelligence概要 Azure AI Document Intelligence は Azure AI Services に含まれるサービスのひとつであり、pdf ファイルや画像データからの文字起こしが可能となります。 Document Intelligence には様々な事前学習済みのモデルが提供されており、一般的な文章の文字起こしや、領収書や請求書などの読み取りなど様々なデータに対応することが可能です。 GUI ベースで操作も可能で、数クリックするだけで簡単に画像からテキストデータを取得し、活用しやすい形へと変換することができます。 また発展的な利用として、 AI Document Intelligence と Azure AI Search を統合することで、RAG(Retrieval-Augmented Generation)をより強力なものにすることが可能 ともいわれています。 リソース作成について Document Intelligence のリソースなのですが 続々と Update が行われており、それらは以下のリージョンで先んじて行われているようです。 East US West US2 West Europe 最新の機能に触れるためにも、これらのリージョンでのリソース作成をお勧めします。 そもそもこれらのリージョンでないと、SDK が扱えなかったりするので注意です。 価格については、今回は Free プランで Document Intelligence のリソースを作成しました。 Free プランの制限として、pdf が最初の 2 ページしか読み取れなかったりしますが、検証には十分です。 また、おそらくですが Free プランは Azure のサブスクリプションあたり 1 つしか作成できません。 そのため新規に作成するためには既存のFreeプランのリソースを削除する必要があります。 ただ注意することとして、単にリソース削除するだけでは完全に削除されません。 「削除されたリソースの管理」というタブから完全に削除することで、新たに Free プランのリソースが作成できるようになります。 利用方法 Document Intelligence を利用する方法としては、以下 2 つがあります。 Document Intelligence Studio REST API & SDK Document Intelligence Studio GUI ベースでさくっと機能をお試しできます。 今回はこちらをベースに作業していきたいと思います。 REST API & SDK 実際にシステムに組み込むとなったら API や SDK を活用することになります。 SDK としては C#や python 用のものが提供されています。 今回は利用しませんが、 サンプルコード なども豊富なので是非見てみてください。 Model DocumentIntelligenceのモデルは大きく分けて3種類あります Document analysis Prebuilt models Custom model Document analysisとPrebuilt modelsは、Microsoft によって事前にトレーニングされた強力なモデル群です。 一方Custom modelに関しては、独自のトレーニングを行って作成するモデルになります。 本記事では Document analysis と Prebuild models にスポットを当てて紹介していきます。 Document analysis Document Analysisの モデル群を使用すると、 フォームやドキュメントからテキストを抽出し、文章を文字起こしすることが可能 です。 一般的な文章や、論文の解析などに使われることが多いように思います。 このDocument analysisにおいては Read Layout General documents の 3 種類のモデルが用意されています。 ただし、 General documents に関しては 公式ドキュメント において Document Intelligence バージョン 2024-02-29-preview、2023-10-31-preview 以降、一般的なドキュメント モデル (事前構築済みドキュメント) は非推奨となりました。 とあるため、今回は説明から除外したいと思います。 後ほど詳しく解説しますが、ReadモデルとLayoutモデルの違いとしては Read:文章のテキストを抽出 Layout:文章のテキストに加えて、ドキュメントの構造や表情報なども取得可能 といった感じです。 もちろん使用料金に関してはLayoutモデルの方が高い形となっています。 Layout (順序的にはReadモデルの方が先ですが、記事の解説の都合でLayoutモデルから解説します。) Layout モデルは、文章からテキストだけではなく、 構造や表情報なども取得することが可能 です。 また 文章の Markdown への変換機能 も備えており、このあたりも非常に強力です。 今回、文字起こしの対象とするデータとして、以下のような pdf データを用意しました。 表やグラフ、チェックボックスを含むよう作成しています。 (なお文章の内容に関しては ChatGPT に適当に作ってもらったものなので、参考にしないでください。) では以下のステップで分析の方進めていきます。 Document Intelligence Studio から Layout のモデルを選択 Browse for files から pdf をアップロード Run analysis のボタンをクリック 解析が完了すると、pdf 上にマーキングがされ、結果が右側のエリアに表示されます。 細かいパラメータは後ほど見ていきますが、ひとまず文章が正しく書き起こされていることが確認できます。 また、テーブル情報もチェックボックス情報も正しく読み取れていることが確認できます。 ただ、さすがに絵文字はうまく認識してくれませんでした Resultパラメータ解説 次に Result タブの内容を見てみます。 ここには、解析された結果が json 形式で表示されます。 ちなみにREST API や SDK を利用した場合も、この json 形式と同じフォーマットとして分析結果が得られます。 なお、これらのjsonの中には以下のような情報が含まれています。 SDK や API 、モデルなどの情報 要素の位置 要素の大きさ 文章の内容 構造(要素の親子関係) 信頼度 中身を見ると非常に細かい情報まで返してくれているのですが、いかんせん分かりづらいです。 そのため、今回はこのResultのパラメータについて細かく見ていきたいと思います! Result に説明コメントを付けて、以下のようなjson形式で表しました。 (可読性向上のため、内容を抜粋して書いています。) {   "apiVersion": "2024-02-29-preview", //使用されたAPIバージョン   "modelId": "prebuilt-layout", //使用されたモデルID   "stringIndexType": "textElements", //文字列のオフセットと長さを計算する方法   "content": "", //全体の文章,   "pages": [//抽出されたコンテンツ要素とレイアウト要素     {       "pageNumber": 1,       "angle": 0, //角度(時計回りの方向のコンテンツの一般的な向き)       "width": 8.5, //イメージ/PDF の幅       "height": 11, //画像/PDF の高さ       "unit": "inch", //幅、高さ、境界ポリゴンのプロパティで使用される単位       "words": [         //ページから抽出された単語         {           "content": "SIOS", //テキスト. ※日本語の場合は1文字ずつ           "polygon": [             //ページの左上を基準にして座標を指定した、単語の境界ポリゴン             0.9898, //左上x座標             1.1093, //左上y座標             1.4297, //右上x座標             1.1097, //右上y座標             1.4266, //右下x座標             1.2766, //右下y座標             0.9868, //左下x座標             1.2779 //左下y座標           ],           "confidence": 0.989, //単語を正しく抽出する信頼度           "span": {             //読み取り順序の連結されたコンテンツ内の単語の場所             "offset": 0, //スパンで表されるコンテンツの 0 から始まるインデックス             "length": 4 //スパンで表されるコンテンツ内の文字数           }         }       ],       "selectionMarks": [ // チェック ボックス、ラジオ ボタン、および選択範囲を示すその他の要素を表す選択マーク オブジェクト。         {           "state": "unselected", //選択されているかどうか(selected,unselected)           "polygon": [             //選択マークの境界ポリゴン。             //略           ],           "confidence": 0.988, //選択マークを正しく抽出する信頼度           "span": {             //読み取り順序の連結されたコンテンツ内の選択マークの位置             "offset": 1241,             "length": 12           }         }       ],       "lines": [         //単語や選択マークなどのコンテンツ要素の隣接するシーケンスで構成されるコンテンツ行オブジェクト。         {           "content": "SIOS HOGEHOGE TECHONLOGY", //コンテンツ           "polygon": [             //略           ],           "spans": [             //linesのoffsetとspan             {               "offset": 0,               "length": 24             }           ]         }       ],       "spans": [         //page自体のoffsetとspan         {           "offset": 0,           "length": 639         }       ]     }   ],   "tables": [     {       "rowCount": 5, //行数       "columnCount": 3, //列数       "cells": [         //テーブルに含まれるセル情報         {           "kind": "columnHeader", //セルの種類(content,rowHeader,columnHeader,stubHead,description)           "rowIndex": 0, //行のインデックス           "columnIndex": 0, //列のインデックス           "content": "技術分野", //セルの内容           "boundingRegions": [             //テーブルセルをカバーする境界領域             {               "pageNumber": 1, //境界領域を含む 1 から始まるページ番号               "polygon": [                 //ページ上の多角形の境界、または指定されていない場合はページ全体。左上を基準にして指定された座標.                 //略               ]             }           ],           "spans": [             {               "offset": 491,               "length": 4             }           ],           "elements": [             //テーブル セルの子要素             "/paragraphs/8"           ]         }       ]     }   ],   "paragraphs": [//一般的に、一般的な配置と間隔を持つ連続した行で構成される段落オブジェクト。     {       "spans": [         {           "offset": 63,           "length": 17         }       ],       "boundingRegions": [         {           "pageNumber": 1,           "polygon": [             //略           ]         }       ],       "role": "sectionHeading", //段落のセマンティックロール(FOOTNOTE, FORMULA_BLOCK,PAGE_FOOTER,PAGE_HEADER,PAGE_NUMBER,SECTION_HEADING,TITLE)       "content": "# 調査概要 2024 年9月4日"     }   ],   "contentFormat": "markdown",   "sections": [     //ドキュメント内のセクションを表すオブジェクト。     {       "spans": [         //読み取り順序の連結されたコンテンツ内の単語の場所         {           "offset": 0,           "length": 60         }       ],       "elements": [         //セクションの子要素。         "/sections/1"       ]     }   ],   "figures": [     {       "id": "2.1", //不明       "boundingRegions": [         {           "pageNumber": 2,           "polygon": [             //略           ]         }       ],       "spans": [         {           "offset": 639,           "length": 289         }       ],       "elements": [         //図の子要素(キャプションや脚注を除く)         "/paragraphs/23"       ]     }   ] } これで各パラメータの内容に関して雰囲気はつかめるかと思うのですが、もう少し詳しく解説していきたいと思います。 構造について Resultのjsonを構成する主な要素として、以下の配列オブジェクトがあります。 pages(ページ) tables(表) paragraphs(段落) sections(セクション) figures(図) 各要素について何となく名前から想像がつくのですが、それぞれの関係性がやや分かりづらく感じました。 そこでjsonの中から 構造(親子関係) を抜き出し、階層関係をまとめてみます。 (それぞれの要素が elements というパラメータを持っており、このパラメータにて構造(親子関係)が表現されています。) ※階層が深すぎると分かりづらいので、paragraphsを最下層として書いています。 ※重複している部分などはだいぶ省略して書いています。 sections/0   /sections/1     /paragraphs/0:"SIOS HOGEHOGE TECHONLOGY 111-1111 東京都ホゲ村 ホゲ番地 (+81) 000-0000"     /sections/2       /paragraphs/1:"調査概要 2024 年9月4日"         /sections/3           /paragraphs/2:"調査目的"           /paragraphs/3:""本市場調査の目的は、最新の IT 技術動向を詳細に把握し、各技術の市場シェア、成長予測、およ び競争状況を包括的に明らかにすることであり、特にクラウドコンピューティング、AI(人工知能)、loT (モノのインターネット)、およびブロックチェーン技術の各分野に焦点を当てるものである。"   /sections/4       /paragraphs/4:"調査方法"       /paragraphs/5:"1. プライマリーデータはアンケート調査およびインタビューによって収集し、直接的かつ現場の 視点から得られる情報を重視する一方で、セカンダリーデータは公開データベースおよび業 界レポートから取得し、信頼性の高い既存の情報源を利用して補完的なデータ収集を行う。"       /paragraphs/6:"2. データ分析に関しては、定量分析として統計解析ソフトウェアを駆使して大量の数値データを 精緻に解析し、定性分析として内容分析およびテーマ別分析を行い、収集されたデータの質 的側面を多角的に検討する。" /sections/5     paragraphs/7:"市場概況"     tables/0         cells/0             paragraphs/11":"クラウドコンピューティング"         paragraphs/8 "技術分野"     figures/0         paragraph/23~41 :"Market Size and Growth Rate of Major Technology Fields"     sections/6         paragraphs/42:"技術別市場シェア"     sections/7         paragraphs/43:"クラウドコンピューティング ♡"     sections/8         paragraphs/45:"AIN"         paragraphs/46:"AI 技術に関しては、IBM が市場シェアの 25%を占め、Google が 20%、Microsoft が 18%、その他が 37%を占めている。"         sections/9             paragraphs/47:"ブロックチェーン" また、このまま分かりずらいので図との対応表を作成しました。 このような形で pdf が各要素に分解され、それぞれの要素において位置情報やコンテンツの内容を含む構造となっています。 なおparagraphs/8という表現は、 jsonにおけるparagraphs[]配列の8番目の要素 を表しています。 この時に気を付けたい点としては、 json の階層と pdfの要素の階層は一致していない! ということです。 例えば json の階層としては pragraphs と sections は同じ階層にありますが、 pdf要素 としては sections が paragraphs を含むようになっています。 このあたり別に考える必要があるため気を付けましょう! Enum値について "role": "sectionHeading", //段落のセマンティックロール(FOOTNOTE, FORMULA_BLOCK,PAGE_FOOTER,PAGE_HEADER,PAGE_NUMBER,SECTION_HEADING,TITLE) といったようなEnum値の内容に関しては、 公式ドキュメント に説明があるのでこちらを参照ください。 wordの扱いについて pagesの配列の中にwordという要素が存在しています。 このwordに関して、日本語においては文字が1文字ずつ分割されてしまいます。 "words": [                   { "content": "概", "polygon": [ ], "confidence": 0.995, "span": { "offset": 63, "length": 1 } }, { "content": "要", "polygon": [ これは 公式ドキュメント にも書いてある通りです。 単語間にスペース区切りを使用しない言語の場合、セマンティックな単語単位を表していない場合でも、各文字は個別の単語として返されます。 そのため、日本語の文章を分析するとpagesの配列が膨大になりがちです。 この点、今後の改善に期待です。 その他のパラメータ 分析対象とする pdf の内容によっては、記事の中で触れていない要素が出てくる場合もあります。 そういった場合は 公式ドキュメント にて各要素のクラスが定義しているため確認してみてください。  Markdownへの変換 Layoutモデルの強力な機能のひとつとして、Markdownへの変換があります。 Analyze optionsのボタンをクリックし、その中でOutput form styleをMarkdownにします。 この状態で分析を行うと、Markdownというタブでの結果が表示されます。 また、Resultの中に入っている最上位の content の中身がMarkdownの形式として出力されます。 精度は完ぺきではないですが、このくらいのレベルでpdfをMarkdownに変換してくれるのは助かります。 文章の構造を認識できるLayoutモデルならではの機能ですね。 Markdownへ変換するメリット Layoutモデルの機能として、Markdownへ変換が可能なことが分かりました。 これによって得られるメリットとして、生成AIとの親和性の高さが言えるかと思います。 プロンプトエンジニアリングの一つに「 明確な構文を追加する 」というものがあります。 また、さらに 使用する構文がわからない場合は、Markdown または XML の使用を検討してください。 モデルは、XML と Markdown の大量の Web コンテンツでトレーニングされており、より良い結果が得られる可能性があります。 といった記載もみられます。 つまり、 生成AIはMarkdownの構文を理解しやすい ということが言えます。 そのため、Document Intelligenceで抽出したMarkdownのテキストを生成AIに投げることによって、より質の高い回答が得られることが期待できます。 Document IntelligenceとRAGを組み合わせたケースなどで効果を発揮しそうですね。 手書き文書 先程はpdfということで、文字崩れがないデータの読み取りを行いました。 一方で「手書きの文章の画像データを読み込む」というユースケースも大いにあり得るかと思います。 分析を行う画像として、以下のものを用意しました。 それでは分析してみます。 さすがにだいぶ崩した文章は難しいですが、それなりに汚い字でも正しく読み取ってくれました。 またこの時、Resultのjsonの中身を見てみると、先程までは含まれていなかった styles という配列オブジェクトが見られました。 中身としては以下のような感じです。 "styles": [ // 観察されたテキストスタイルを表すオブジェクト     {         "confidence": 1, //信頼度         "spans": [             {                 "offset": 0,                 "length": 30             }         ],         "isHandwritten": true // コンテンツが手書きかどうかを示す     } 基本的には先ほども出てきたパラメータですが、 isHandWritten のパラメータが新規なパラメータとして存在しています。 その名の通りですが、手書きである事の判定パラメータであり、判定が正しく行われています。 Read 次に Read のモデルを見ていきます。 Read は Layout と比較して、文章のテキスト読み取りだけの機能を持ちます。 そのため文章の構造情報やテーブル情報などは読み取ることができません。 早速 Read モデルを利用し、先程と同じ pdf を分析してみます。 結果のタブを見ると「Text」しかなく、Layout で見られたような「Tables」や「Selection marks」などが無いことが分かります。 次に Result タブから json 形式の値も見てみたいと思います。 先ほど挙げた主な構成要素に関してみてみると、Layoutと比較して pages paragraphs しか含まれていないことが分かります。 Layoutモデル に含まれていた tables sections figures に関しては存在していません。 これが構造に関する情報の読み取りをしていないということの表れでもあります。 テキストだけ分かればいいというのであれば Read モデルで十分という判断ができそうです。 ちなみに Layout と Read では使用するにあたり、価格が異なります。 Prebuild models 事前構築済みモデルを使用することで、独自モデルのトレーニングや構築をしなくても様々なドキュメントを処理することができます。 Document analysisは一般文書がターゲットでしたが、こちらの事前構築済みモデルは請求書、領収書、名刺といった特定のドキュメントの解析に対する強みを持ちます。 様々なモデルがありすぎるので、全部は試せませんが 今回は名刺の分析を行ってみたいと思います。 Business cards 名刺のモデルを選択し、先程と同じステップを踏んで分析していきます。 今回は用意されているサンプル画像を利用します。 早速分析してみると、正しく各情報が抽出できていることが確認できます。 また、Resultの値を見ると documents という配列オブジェクトがあるのが分かります。 中身を少し見てみると、 docType として businessCard が指定されており その下のfieldsの中で、住所や氏名情報が含まれていることが分かります。 "documents": [     {         "docType": "businessCard",         "boundingRegions": [             {                 "pageNumber": 1,                 "polygon": [                     0,                     0,                     1024,                     0,                     1024,                     768,                     0,                     768                 ]             }         ],         "fields": {             "Addresses": {                 "type": "array",                 "valueArray": [                     {                         "type": "address",                         "content": "〒108-0075 東京都港区港南 2-16-3" まとめ 今回は、Azure AI Servicesの一つであるAzure AI Document Intelligenceに入門してみました。 Document Intelligence Studioという、GUIでも扱いやすいサービスも提供されており、簡単に画像の分析ができることが分かりました。 ただ返されるパラメータがやや複雑ということもあり、その点は本記事を通して理解してもらえればと思います。 また、今回は入門編ということで基本的な機能だけを触っています。 まだまだ触れていない機能が盛りだくさんなので、引き続き勉強して記事にしていこうと思います ではまた! 参考 Azure AI Document Intelligence のドキュメント 【最新】Azure AI Document Intelligence による文書構造の解析(Markdown、図、セクション) ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Azure AI Document Intelligence入門【Resultパラメータ解説付き!】 first appeared on SIOS Tech. Lab .
アバター
DevOpsの歴史、導入メリット、そして直面する障壁について詳しく解説します。CI/CD、自動化、文化変革に関心のある方向けの導入記事になります。例として「GitHubで見てみるDevOps」を作っています。 ご挨拶 こんにちは、久しぶりです。最近、ブログ以外の外部発信活動を整備している龍です。目に見える成果がないのが、少し辛いですね。 今回は、「DevOps」についてまとめていきます。インターネットで情報を探していると、たまに見かけますよね。私もふわっと理解するところから始めました。最近は文脈上でよく使うので、ここで歴史を含めてまとめたいと思います。 特別な前提知識は必要ありませんが、GitHubを使ってCI/CDを組んだことがある方にとっては、理解しやすい内容だと思います。「ウォーターフォール」・「アジャイル」・「MVP」などのプロジェクトの進め方に関しては、 こちらの記事 でまとめています。 それでは始めましょう。 歴史的背景 DevOpsがどのような経緯で生まれたのかについて説明する前に、まずその背景を紹介します。 アジャイル開発 アジャイル開発は、実装したい機能を細分化し、要件定義からリリースまでを一つのサイクル内で行う開発手法です。 – 「要件定義」を行い、機能ごとの小さな開発サイクルで開発する – 「設計」を機能ごとに行うため、急な仕様変更に対応できる – すべての機能開発が終了せずに基本機能のみ開発が終了すればリリースできる – 頻繁なリリースによって迅速な機能追加・変更が可能 最大の特徴として、サイクル終了ごとにリリースが行われます。これにより、「ウォーターフォール開発」に比べて高頻度なリリースが可能となります。 メリット・デメリット リリースがサイクルごとに行われるため、仕様変更に柔軟に対応することが可能です。アジャイル開発は広く実践され、そのキーワードは深く浸透しています。 アジャイル開発が適しているサービスとしては、「自社サービス」のように機能を細分化して開発を進められるものがあります。 アジャイル開発を支える技術としては、 自動化 というキーワードがあり、GitHubやCI/CDが挙げられます。 CI/CD アジャイル開発を支える技術としてのCI/CDについて説明します。CI/CDは以下のような意味を持っています。 CDは二つの意味を持ち、どちらもリリースに関する内容です。CI/CDでは『自動化』が重視され、特にビルド・テスト・デプロイのステップの自動化が注目されています。 GitHubで見るCI/CD 開発体験としては、GitHubのソース管理も重要です。その流れを含めて、『GitHubで見るCI/CD』として図化します。 GitHubでCI/CDを実現する方法としては、GitHub Actionsを使用します。 Pull Request上でCIのみを実行し、特定のブランチにマージされた際にCI/CDを一緒に実行します。 CI/CDを実行するタイミングやレビューや開発者の取り組みは、ブランチ運用やテスト戦略の文脈と共に考える必要があります。これはプロジェクトごとの判断になるため、また別のブログで詳しく説明します。 この環境を整備することで、開発者やレビュワーがソースの変更を加えることで、リリースを行うことが可能となります。 アジャイルからDevOpsへのつながり アジャイル開発からDevOpsが提唱された背景について説明します。アジャイル開発が広く浸透し、開発チームのコミュニケーションが促進され、プロジェクトのスピードが上がりました。 2008年のカンファレンス では、プロジェクトを運用するインフラチームと開発チームとの確執について問題提起がなされました。開発チーム内でのコミュニケーションは向上しているものの、運用チームとのコミュニケーションがうまくいっていないという現場の声がまとめられています。 その後、 2009年に運用チームと開発チームの連携を密に取ることを解決策として『DevOps』という単語が提唱 されました。ここでは開発チームと運用チームが目指すゴールの違いに注目し、最終的なゴールはより良い体験をユーザーに提供することとしました。 このような背景から、『DevOps』には「アジャイル」的アプローチが根底にあり、開発チームと運用チームが協力してより良いプロジェクトを実現するという『概念』が生まれました。 Agile2008 Conference「 Agile Infrastructure & Operations 」 Velocity 2009「 10+ Deploys per Day: Dev and Ops Cooperation at Flickr 」 DevOps まず理解すべきは、「DevOps」は開発手法ではなく、運用チームと開発チームが協力し、より良いプロジェクトを目指すための考え方であるということです。まずその根本的な考え方を説明し、次に「DevOps」を実現する方法を解説します。 DevOpsの考え方 「DevOps」は上記のような図で表現されることが多いです。DevOpsでは、「開発チーム」と「運用チーム」の二つのチームを一つに統合し、同一のゴールを目指すことを提唱しています。 開発チームと運用チームはそれぞれ異なるゴールを目指していますが、DevOpsの考え方では、これらを統合し「プロジェクトの目的達成」という共通のゴールを追求する一つのDevOpsチームを形成します。 DevOpsに関する誤解 「DevOps」はツールを使って実現するという誤解がありますが、実際は、プロジェクトに関わる全ての人が円滑にプロジェクトに参加し、目的達成を目指すという考え方が根底にあります。この考え方を実現するためのツールが多く存在していますが、DevOps自体は考え方そのものです。 DevOpsの流れ DevOpsの概念図は英語で表現されていますので、それを日本語に訳して図化します。 開発手法においては、「計画」から「デプロイ」までに焦点が当てられています。しかし、DevOpsでは「運用」から「継続的なフィードバック」までを統合し、一つのプロセスとして捉えます。「運用」や「監視」でプロジェクトの問題点を特定し、「継続的なフィードバック」でプロジェクトの変更点を「計画」に反映させていきます。 DevOpsで重要な「キーワード」 DevOpsを実現するために重要なキーワードは「自動化」と「DevOps Culture」です。 自動化 は、DevOpsの基礎となるアジャイル開発手法からも理解しやすいです。顧客の要望が直接伝わる現場では、「ビルド」から「デプロイ」までをCI/CDで自動化することで、迅速な開発スピードを実現しつつ、「継続的なフィードバック」により要望を取り込むことが可能になります。 DevOps Culture は、「Respect」「Trust」「Healthy attitude about failure」「Avoiding Blame」の四つのキーワードで構成されています。これらを日本語に訳すと、「尊敬」「信頼」「寛容」「責任の共有」となります。DevOpsの目的は、異なる目的を持つもの同士が一つのゴールを共に追求することです。これらのキーワードは、プロジェクト運用だけでなく、人生にも通じるものです。 DevOpsのメリット DevOpsのメリットは以下の観点で考えられます。 人的ミスの防止 自動化プロセスにより手作業が減少し、標準化が推進されます。これにより、人的ミスが大幅に減少し、信頼性の高いシステム運用が可能となります。 生産性の向上 開発チームと運用チームの協力により、コミュニケーションが円滑になり、生産性が上がります。迅速な問題解決と効率的なタスクの進行が可能になります。 高品質かつ迅速な開発 頻繁なリリースとフィードバックサイクルを通じて、品質を保ちつつ迅速な開発を実現します。継続的なテストとデプロイにより、バグの早期発見と修正が可能です。 ランニングコストの削減 自動化と効率化により、開発と運用のコストが下がります。手作業の削減により人件費が減少し、システムのダウンタイムや障害対応のコストも軽減されます。 予定外の作業のスムーズな解決 継続的な監視とフィードバックにより、予想外の問題や変更に素早く対応できます。問題が発生した際も、自動化されたプロセスと迅速なコミュニケーションによりスムーズに解決できます。 DevOps導入の障壁 DevOpsにおける障壁としては、以下の観点が考えられます。 必要な知識の導入 効果的なDevOpsの導入には、開発者や運用担当者がCI/CD、インフラ管理、監視ツールなどの知識を身につける必要があります。これらの学習曲線は障害となることがあります。 企業やチームの文化 DevOpsの成功は、開発チームと運用チームの協力に依存しています。既存の文化や習慣を変えることは困難であり、特にセクショナリズムが根深い組織では抵抗が生じることがあります。 学習コスト DevOpsのツールやプロセスを学ぶためには時間とリソースが必要です。新たなスキルの習得やトレーニングに関するコストは、導入の障壁となることがあります。 導入コスト 新しいツールやシステムの導入には初期投資が必要です。インフラの整備、ツールの購入、運用体制の構築には費用がかかり、特に小規模な組織には大きな負担となることがあります。 GitHubで見るDevOps 開発でよく使われるGitHubを例に、「GitHub機能で見るDevOps」をまとめてみます。 プロジェクトは「GitHub Project」で管理します。ここでは、プロジェクトの進行管理やタスクのチケット管理などを行います。実際のタスクは「issue」として起票し、開発者に割り当ててチケット管理を行います。運用を担当する人が「GitHub Project」に参加し、起票することで、全ての計画や要望が集約されます。 今回の図には含めていませんが、SlackやTeamsなどの「コミュニケーションツール」も重要な要素となります。GitHubと連携して通知をコミュニケーションツールに送ることで、関係者の見落としを防ぐことができます。どの程度の通知を実装するかは、重要な要素となります。通知が多すぎると、通知の意味がなくなることもあります(例えば、迷惑メールに埋もれる重要なメールなど)。 まとめ 今回の記事では、DevOpsという概念について、その起源と歴史的背景を深く掘り下げて解説しました。GitHubを使って、DevOpsの適用方法についてもまとめました。次回の記事では、プロジェクトを進める際に必要な仕様書に注目して解説します。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post DevOpsの基礎知識:歴史、利点、導入時の障壁とは? first appeared on SIOS Tech. Lab .
アバター
このブログでは、プロジェクトの開発手法(ウォーターフォール、アジャイル、MVP)について詳しく解説します。各手法の特徴、メリット、デメリット、向いているプロジェクトの種類について学んでいきましょう。 ご挨拶 皆さん、こんにちは!龍です。長らくブログの更新を休んでいましたが、今回は久しぶりに執筆をしています。YouTubeへの出演など、ブログ以外の活動も行っていますが、常に学び続けて良いブログを書くための知識を深めています。 今回は『プロジェクトの開発手法』について解説します。X(旧:Twitter)などで話題になることもありますね。業務を通じて得たプロジェクト進行に関する体験をもとに、所感を交えながら執筆していきます。こちらの解説をベースに「DevOps」の解説を進めていきます。 DevOpsの記事はこちら です。 プロジェクトの進め方 開発手法をまとめてみます。具体的には「ウォーターフォール」「アジャイル開発」「MVP開発」について説明します。 ウォーターフォール開発 ウォーターフォール開発は、「要件定義」から「リリース」まで上流から下流まで順番に進めていく手法です。一つ一つのステップが終了するごとに次の工程に進むため、要件定義や設計が進まなければアプリケーション開発に入ることができません。各フェーズに対する理解が深まるため、各ステップの重要性が際立ちます。 – 「要件定義」から「リリース」まで上流から下流まで順番に進める – 前の工程に問題が発生した場合、手戻りの影響が大きい – 上流工程の品質を担保する必要がある メリット 各フェーズごとに分担して開発 スケジュールの管理がしやすい 理想的な状態(バグもなければ、仕様変更もない)では、スケジュールの管理がしやすいプロジェクト手法です。 デメリット 仕様変更が発生した場合の影響が大きい すべての要件を考慮する必要がある 上流から下流までのすべてのステップがつながっているため、一つのステップが遅れることで「手戻り」としてスケジュールに影響します。仕様変更や要件漏れが発生するたびに影響が大きくなります。 向いているプロジェクト ウォーターフォール向きなプロジェクトは、比較的要件が定まっているシステムです。 アジャイル開発 アジャイル開発は、「設計」から「リリース」までを1サイクルとして、短いスパンで繰り返すことで機能を開発する手法です。実装したい機能を細分化してサイクルに当てはめていきます。要件定義はサイクルごとに行い、サイクルの終了ごとにリリースが行われるため、頻繁なリリースが発生します。最終的なゴール(製品)に向かってマイルストーン(小さな機能)を設定しながら進む開発手法です。 – 「要件定義」を行い、機能ごとの小さな開発サイクルで開発する – 「設計」を機能ごとに行うため、急な仕様変更に対応できる – すべての機能開発が終了せずに基本機能のみ開発が終了すればリリースできる – 頻繁なリリースによって迅速な機能追加・変更が可能 メリット 仕様変更に柔軟に対応できる リリースの頻度が高く、高い開発スピード 機能ごとに「要件定義」を実施するため、柔軟に仕様変更に対応できます。それすなわち、フィードバックを取り込みやすくなるということでもあります。 デメリット メンバー全員に設計からテストのスキルが必要 スケジュール管理が難しい スケジュール管理が複雑になる点です。僕が知っている手法では、1サイクルを2週間と設定してアジャイル開発を行っていました。1サイクルの中で対応する機能を適切に分割する必要があります。適切に分割されていない場合、サイクル内に対応できなかったり、予定よりも多い時間を投入したりといった事態が発生します。アジャイル開発を実施するには、アジャイル開発の手法に対する知識が必要です。 向いているプロジェクト アジャイル向きなプロジェクトは、機能を細分化して実装できるシステムです。比較的に仕様の決定権がチームと近い位置にあるプロジェクト、自社サービスなどが向いています。 MVP(Minimum Viable Product) MVPは「アジャイル」と混同されがちな開発手法です。実際に僕も勘違いしていました。 MVPは、リリースごとに目的をはっきりとさせて実装していく手法です。「アジャイル」や「ウォーターフォール」では機能のリリースに注目しますが、「MVP」ではリリース後のフィードバックを取り込んでプロジェクトを成長させることに注目しています。 以下のようなイメージで進めます。 Ver 目的 成果物 1 甘いもの クッキー 2 柔らかい甘いもの 薄いパンケーキ 3 柔らかく、ホイップクリームがのっている甘いもの ベルベットケーキ 4 柔らかく、ホイップクリームと果物がのっている甘いもの 豪華なパンケーキ 最初に「甘いもの」の最小限の要望で「クッキー」を作りました。その後、フィードバックを受けながら最終的に「豪華なパンケーキ」にたどりつきました。フィードバックを取り込みながら開発を進めるのが「MVP開発」の最大の特徴です。 メリット フィードバックを開発に取り込みやすい リリースの頻度が高く、高い開発スピード デメリット フィードバックの分析や解析の知識が必要 スケジュール管理が難しい 大規模な開発には適応が難しい点が挙げられます。 プロジェクトの目的が明確に決まっていない場合、フィードバックの選択によって当初想定していたプロジェクトとは異なるプロジェクトになる可能性があります。 向いているプロジェクト フィードバックをもとに取り込むため、比較的小さなプロジェクトや仕様が決まっていない(プロトタイプ)開発に適しています。 実際に「MVP開発」に取り組んで学んだ観点ですが、プロジェクトの目的を忘れてはなりません。「フィードバック」が大きな要素となります。どのフィードバックを機能として盛り込むのかは、プロジェクトの目的と照らし合わせながら判断する必要があります。目的がぶれると、作成段階でまったく異なるアプリケーションになる可能性があります。 比較 どの開発手法が良いかはプロジェクトごとに判断する必要があります。ここでは、メリットとデメリットを列 挙し、所感を交えながらどのようなプロジェクトに向いているかを述べます。 どの開発手法を採用するにしても、要件定義はしっかりとする必要があります。「ウォーターフォール」と比較して、他の二つでは高頻度なリリースが想定されます。高頻度なリリースが発生する開発手法では、GitHubを活用したCI/CD環境の構築などが必要です。 おわり 今回の記事をまとめましょう。今回はプロジェクトの開発手法について詳しく見てきました。それぞれの手法には特徴、メリット、デメリットがあり、また、特定の種類のプロジェクトに向いていることを理解することが重要です。 「ウォーターフォール」は一つ一つのステップを順番に進める手法で、要件が比較的決まっているプロジェクトに適しています。「アジャイル」は短いスパンで開発を繰り返す手法で、機能を細分化して実装できるプロジェクトに適しています。そして、「MVP開発」はリリースごとに目的を明確にして実装を進め、フィードバックを取り込むことに焦点を当てた手法で、仕様が決まっていないプロジェクトやプロトタイプ開発に適しています。 それぞれの手法の選択はプロジェクトの具体的な状況や要件によります。適切な手法を選ぶことで、開発の効率性と成功率を高めることができます。 次回のブログでは、DevOpsについて詳しく解説します。DevOpsは開発(Development)と運用(Operations)の統合を目指す考え方で、アジャイル開発やMVP開発など高頻度リリースを支えるための重要な手法です。次回もお楽しみに! それでは、今回はこれで終わります。ご覧いただきありがとうございました。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post プロジェクト開発手法を解説:ウォーターフォール、アジャイル、MVPの特徴 first appeared on SIOS Tech. Lab .
アバター
想定読者 オブジェクト指向プログラミングに興味がある 可読性が高く、変更に強いプログラムを作りたい SOLID原則を理解して周りに「ドヤァ( ^)o(^ )」したい(自己満足でも可) はじめに SOLID原則は以下の5つの原則の頭文字を並べて出来たネーミングです。 単一責任の原則(single-responsibility principle) There should never be more than one reason for a class to change. 変更するための理由が、一つのクラスに対して一つ以上あってはならない。 開放閉鎖の原則(open/closed principle) software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. ソフトウェアの実体(クラス、モジュール、関数など)は、拡張に対して開かれているべきであり、修正に対して閉じていなければならない リスコフの置換原則(Liskov substitution principle) Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. ある基底クラスへのポインタないし参照を扱っている関数群は、その派生クラスのオブジェクトの詳細を知らなくても扱えるようにしなければならない インターフェース分離の原則(interface segregation principle) Many client-specific interfaces are better than one general-purpose interface. 汎用なインターフェースが一つあるよりも、各クライアントに特化したインターフェースがたくさんあった方がよい 依存性逆転の原則(dependency inversion principle) High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces), [not] concretions. 上位モジュールはいかなるものも下位モジュールから持ち込んではならない。双方とも具象ではなく、抽象(インターフェースなど)に依存するべきである ふむ、なんじゃこりゃって感じですよね。大丈夫です。今から具体例をTypeScriptのコードを用いて解説していきます! 解説は、「悪い例」→「何が悪いか解説」→「良い例」といった順序になります。 単一責任の原則 // 悪い例 class SmartPhone { takePhoto() { /* 省略 */ } // 撮影機能 drawPicture() { /* 省略 */ } // 絵描き機能 } class Photographer { constructor(private readonly SmartPhone smartPhone) {} takePhoto() { /* 省略 */ smartPhone.takePhoto(); // 写真家がスマホを使って撮影 /* 省略 */ } } class Illustrator { constructor(private readonly SmartPhone smartPhone) {} drawPicture() { /* 省略 */ smartPhone.drawPicture(); // 絵師がスマホを使って絵描き /* 省略 */ } } class OrdinaryPerson { constructor(private readonly SmartPhone smartPhone) {} takePhoto() { /* 省略 */ smartPhone.takePhoto(); // 一般人がスマホを使って撮影 /* 省略 */ } drawPicture() { /* 省略 */ smartPhone.drawPicture(); // 一般人がスマホを使って絵描き /* 省略 */ } } 全体的なコードの良し悪しは置いておいて今回は「単一責任の原則」の観点からのみ悪い点を探していきます。(他の原則についても同じように解説していきます) まず、ここで登場する概念は「スマホ」、「写真家」、「イラストレーター」、「一般人」の4つです。 概念間の関係としては 写真家がスマホを使って撮影 絵師がスマホを使って絵描き 一般人がスマホを使って撮影、絵描き といったものが挙げられます。 茶番劇開始 ここで一般人がスマホの絵描き機能、撮影機能に対して「仕様が難しすぎるからもっと簡単にして初心者向けの機能にして欲しいという」依頼を出すとします。 すると、スマホ設計者は「絵描き機能」、「撮影機能」をより簡単なものに変更します。 一般人はその修正に満足して感謝のメッセージをスマホ設計者に送りました。 ですが、その翌日スマホ設計者のもとに2通のクレームが届きました。 一つは写真家からで、もう一つは絵師からのものでした。 クレームの内容はどちらとも「機能が簡単になり過ぎて不便になってしまっているので元に戻してほしい」といったものでした。 無事スマホ設計者は「一般人」と「写真家、絵師」の間で板挟みになって脳みそがパンクしてしまいました。 茶番劇終了 茶番劇を見てみると 変更するための理由:写真家、絵師、一般人 と3つあり、 変更するための理由が、一つのクラスに対して一つ以上あってはならない。 という単一責任の原則を守れていないことが分かります。 何が問題かというとSmartPhoneクラスのメソッドに複数のアクター(利用者)がいることが問題です。スマホクラスのそれぞれのメソッドには takePhoto: 写真家、一般人 drawPicture: 絵師、一般人 といった複数のアクターが存在してしまっています。 この問題を解消することによって単一責任の原則が守られた「良いコード」へ進化を遂げることが出来ます。 // 良い例 class SmartPhoneForPhotographer { // 写真家用 takePhoto() { /* 省略 */ } } class SmartPhoneForIllustrator { // 絵師用 drawPicurte() { /* 省略 */ } } class SmartPhoneForOrdinaryPerson { // 一般人用 takePhoto() { /* 省略 */ } drawPicture() { /* 省略 */ } } class Photographer { constructor(private readonly SmartPhoneForPhotographer smartPhone) {} takePhoto() { /* 省略 */ smartPhone.takePhoto(); // 写真家が写真家用スマホを使って撮影 /* 省略 */ } } class Illustrator { constructor(private readonly SmartPhoneForIllustrator smartPhone) {} drawPicture() { /* 省略 */ smartPhone.drawPicture(); // 絵師が絵師用スマホを使って絵描き /* 省略 */ } } class OrdinaryPerson { constructor(private readonly SmartPhoneForOrdinaryPerson smartPhone) {} takePhoto() { /* 省略 */ smartPhone.takePhoto(); // 一般人が一般人用スマホを使って撮影 /* 省略 */ } drawPicture() { /* 省略 */ smartPhone.drawPicture(); // 一般人が一般人用スマホを使って絵描き /* 省略 */ } } ここでもう一度茶番劇の内容について振り返ってみると まず一般人が「撮影機能」、「絵描き機能」を簡単にしてほしいという依頼を出す。 これを受けて機能変更が適用されるのはSmartPhoneForOrdinaryPersonというクラスのみであり、他の「SmartPhoneForPhotographer」、「SmartPhoneForIllustrator」は影響を受けません。 つまり、 一般人用スマホの「撮影機能」、「絵描き機能」を変更するための理由は「一般人の要望」 写真家用スマホの「撮影機能」を変更するための理由は「写真家の要望」 絵師用スマホの「絵描き機能」を変更するための理由は「絵師の要望」 といった概念間の関係に変更されました。 これらの変更によって、 変更するための理由が、一つのクラスに対して一つ以上あってはならない。 という単一責任の原則が守られていることが分かります。 これで「単一責任の原則」についてはマスターしたといっても過言ではありません。 是非「私はsingle-responsibility principleを完全に理解した」とドヤってください( ^)o(^ ) 次回はSOLIDの「O」の部分である「開放閉鎖の原則」について解説します! 良かったら覗いてみてください ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post SOLID原則って何ぞや?(単一責任の原則編) first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの佐藤 陽です。 前回に引き続き、プロンプトエンジニアリングの応用編について解説していきたいと思います。 思った回答が得られないんだよな もっと生成 AI をうまく使いこなしたいな といった方は最後までご覧ください。 チートシート テクニック (個人的)要約 知識生成プロンプティング Supporting content をAIに用意してもらう Prompt Chaining 複雑なプロンプトを簡単なプロンプトに分割する Tree of Thoughts(ToT) 1. AIに出力させる 2. AIに出力を評価させる 3. 再度AIに出力させる 4. 再度AIに出力を評価させる を繰り返す はじめに 前回はプロンプトエンジニアリングの応用編の 第1弾 として、X-shot LearningやCoTなどのテクニックを紹介しました。 今回はこれに続く第2弾として、さらに応用的なテクニックを紹介したいと思います。 弁明 前回同様にはなりますが、プロンプトの投げ方(文章の構成など)によっては正解にたどり着けなかったケースが多々ありました。 そのため、本記事では試行錯誤したうえでうまくいったったケースのみ載せています。(回答の捏造はしていません。) 環境 前回に引き続き、Azure OpenAI Service を利用していきたいと思います。 モデルは gpt-35-turbo-16k を利用します。 ChatGPT を利用したり、別のモデルを利用した場合でも、問題なくプロンプトエンジニアリングの効果は得られます。 ただし、得られる回答が異なる場合がありますので、その点ご了承ください。 テクニック 知識生成プロンプティング 最初のテクニックは、知識生成プロンプティング(Generate Knowledge Prompting)です。 これは基礎編における Supporting content のテクニックを応用したものであり Supporting contentとして与える補助知識を事前に生成AIに回答してもらい、その回答を併せて再度プロンプトに投げるテクニックになります。 早速やってみたいと思います。 まずは知識生成プロンプティングを利用しない例から プロンプト バスケットボールにおいて、ボールを持ったままドリブルをせず3歩以上歩いた場合の反則を「ウォーキング」という。「はい」か「いいえ」か。 回答 はい、バスケットボールにおいて、ボールを持ったままドリブルをせずに3歩以上歩くことは「ウォーキング」と呼ばれる反則です。ウォーキングが犯されると、相手チームにボールが与えられ、違反を犯した選手のチームにはターンオーバーとなります。この反則は、プレイヤーがボールを持って移動するためには、ドリブルをする必要があるというルールに基づいています。 間違えてますね、3歩以上歩くことはウォーキングではなくトラベリングです。 ではこのやり取りを改善するために、まずはバスケットボールに関する知識を習得していきたいと思います。 ※いったんここで会話のやり取りをリセットしてください。 なお、知識を得るための質問群は こちら の内容を流用させてもらっています。 プロンプト 入力:ギリシャはメキシコよりも大きい。 知識:ギリシャは約131,957平方キロメートルであり、メキシコは約1,964,375平方キロメートルであり、メキシコはギリシャよりも1,389%大きい。 入力:眼鏡は常に曇ります。 知識:冷たい表面に汗、呼気、および周囲の湿度から水蒸気が着陸し、冷却されて液体の微小な滴に変化して、あなたが霧として見るフィルムを形成すると、眼鏡レンズに結露が生じます。あなたのレンズは、特に外気が寒い場合、あなたの呼気に比べて比較的冷たくなります。 入力:魚は考えることができる。 知識:魚は出現よりも知能が高い。記憶などの多くの分野で、魚の認知能力は、非人間の霊長類を含む「より高い」脊椎動物と同等またはそれ以上です。魚の長期記憶は、彼らが複雑な社会的関係を追跡するのを助けます。 入力:一生にたくさんのタバコを吸うことの共通の影響の1つは、肺がんの発生率が高くなることです。 知識:生涯にわたり平均して1日に1本未満のタバコを吸った人は、非喫煙者に比べて肺がんで死亡するリスクが9倍高かった。一日あたり1〜10本のタバコを吸った人の中で、肺がんで死亡するリスクは、非喫煙者の約12倍高かった。 入力:岩と小石は同じ大きさです。 知識:小石は、堆積学のUdden-Wentworthスケールに基づく4〜64ミリメートルの粒子サイズを持つ岩塊です。小石は、一般的に粒度(直径2〜4ミリメートル)よりも大きく、コブル(直径64〜256ミリメートル)よりも小さいと見なされます。 入力:ギリシャはメキシコよりも大きい。 知識:ギリシャは約131,957平方キロメートルであり、メキシコは約1,964,375平方キロメートルであり、メキシコはギリシャよりも1,389%大きい。 入力:眼鏡は常に曇ります。 知識:冷たい表面に汗、呼気、および周囲の湿度から水蒸気が着陸し、冷却されて液体の微小な滴に変化して、あなたが霧として見るフィルムを形成すると、眼鏡レンズに結露が生じます。あなたのレンズは、特に外気が寒い場合、あなたの呼気に比べて比較的冷たくなります。 入力:魚は考えることができる。 知識:魚は出現よりも知能が高い。記憶などの多くの分野で、魚の認知能力は、非人間の霊長類を含む「より高い」脊椎動物と同等またはそれ以上です。魚の長期記憶は、彼らが複雑な社会的関係を追跡するのを助けます。 入力:一生にたくさんのタバコを吸うことの共通の影響の1つは、肺がんの発生率が高くなることです。 知識:生涯にわたり平均して1日に1本未満のタバコを吸った人は、非喫煙者に比べて肺がんで死亡するリスクが9倍高かった。一日あたり1〜10本のタバコを吸った人の中で、肺がんで死亡するリスクは、非喫煙者の約12倍高かった。 入力:岩と小石は同じ大きさです。 知識:小石は、堆積学のUdden-Wentworthスケールに基づく4〜64ミリメートルの粒子サイズを持つ岩塊です。小石は、一般的に粒度(直径2〜4ミリメートル)よりも大きく、コブル(直径64〜256ミリメートル)よりも小さいと見なされます。 入力:バスケットボールにおいて、ボールを持ったままドリブルをせず3歩以上歩いた場合の反則を「ウォーキング」という。 知識: 回答 バスケットボールにおいて、ボールを持ったままドリブルをせずに3歩以上歩くことは「トラベリング」という反則です。ウォーキングは、ボールを持たずに歩いてしまうことであり、これも反則とされます。 正しい知識を得ることができました。 では今回得られた知識を用いて、再度最初に行った質問を行います。 プロンプト 質問:バスケットボールにおいて、ボールを持ったままドリブルをせず3歩以上歩いた場合の反則を「ウォーキング」という。「はい」か「いいえ」か。 知識:バスケットボールにおいて、ボールを持ったままドリブルをせずに3歩以上歩くことは「トラベリング」という反則です。ウォーキングは、ボールを持たずに歩いてしまうことであり、これも反則とされます。 説明と回答: 回答 バスケットボールにおいて、ボールを持ったままドリブルをせずに3歩以上歩くことは「ウォーキング」という反則ではありません。正しい反則名は「トラベリング」です。したがって、回答は「いいえ」です。 正しい答えが得られました。 このように補助知識についても生成AIを使用して取得し、正しい回答に近づけることができます。 ただし、今回は得られた 「知識」 が正しいものと判断できましたが、正しい/正しくないの判断ができないケースも往々にしてあるかと思います。 誤った知識を補助知識として与えてしまうと、結局誤った回答が得られてしまいそうなので、このあたり気を付ける必要がありそうです。 Prompt Chaining 次はPrompt Chainingです。 ChainingはChain(鎖)を表しており、言ってしまえばプロンプトを繋げて利用しようということです。 非常に複雑なプロンプトを投げる際、 1長文のプロンプトを投げるよりは、シンプルなプロンプトを複数回に分けて投げた方が正確な回答が得られる ことが言われています。 まずは事前準備として、生成AIに適当な物語を考えてもらいます。 プロンプト 400文字程度のSF映画のあらすじを考えてください。 回答 2045年、タイムトラベルが現実のものとなり、エージェントのアレックス・カーンは未来の自分からの警告を受け取る。 それは、2030年に起こる未曾有の災害を防ぐための手がかりを示していた。 アレックスはタイムトラベルの許可を得て、2030年へ向かう。 そこで若き日の自分と出会い、協力して災害の原因を探る中、巨大企業クロノス社の歴史改変の陰謀を突き止める。 アレックスは未来の記憶が薄れつつあることに気づきながらも、日記に重要な情報を記録し続ける。 クロノス社の施設に潜入して決定的な証拠を手に入れるが、追跡者に襲われる。 アレックスは若き自分を守るために戦い、重傷を負うが、未来を託してタイムコードへ戻す。 若きアレックスは証拠を持ち帰り、陰謀を暴いて歴史を修正する。 未来では新たな法律が制定され、アレックスは次の冒険の始まりを予感しながら微笑む。 この文章を扱ってPrompt Chainingを試してみたいと思います。 ※これ以降 {あらすじ} と表記してあるところは、この回答で得られた物語の文章が入ります。 まずは、Prompt Chainingを利用せず、複雑なタスクを1発のプロンプトで投げてみたいと思います。 プロンプト 以下の文章において、質問「陰謀を暴いて歴史を修正するためには何が必要ですか? 」に対する回答を作成してください。 回答が正確であり、誤った情報が含まれていないことを確認してください。 -- {あらすじ} 回答 陰謀を暴くためには、以下の要素が必要です: タイムトラベル技術: アレックスが未来からの警告を受け取り、過去に戻るためには、タイムトラベル技術が必要です。2045年の時点では、タイムトラベルが現実のものとなっているため、アレックスは2030年に向かうことができます。 警告の情報: 未来の自分からの警告を受け取ったアレックスは、2030年で起こる災害を防ぐための手がかりを得る必要があります。この情報は、陰謀を暴くための重要な要素となります。 協力者: アレックスは若き日の自分と出会い、協力して災害の原因を探ります。若きアレックスは証拠を持ち帰り、陰謀を暴くために重要な役割を果たします。 証拠: クロノス社の歴史改変の陰謀を暴くためには、決定的な証拠が必要です。アレックスはクロノス社の施設に潜入し、証拠を手に入れます。 身体的な戦い: アレックスは追跡者に襲われ、重傷を負いますが、若き自分を守るために戦います。この身体的な戦いは、陰謀を暴くために不可欠な要素となります。 歴史の修正: アレックスは証拠を持ち帰り、陰謀を暴くことで歴史を修正します。未来では新たな法律が制定され、アレックスは次の冒険の始まりを予感しながら微笑みます。 以上の要素が揃った場合、アレックスは陰謀を暴き、歴史を修正することができます。ただし、この物語はフィクションであり、実際の歴史修正には異なる要素や手法が必要です。 内容としては正しいですし、別に悪くない気はしますね。 次に比較としてPrompt Chainingを試してみたいと思います。 なお、ここで注意するポイントが2つあります。 サブタスクをシンプルかつ明確にする 各サブタスクには、明確に定義された目的とシンプルな指示が必要です。これにより、Claude が理解して従いやすくなります。 (今回利用するのはClaudeではありませんが、根本は同じかと思うのでそのまま利用します。) XML タグを使用する 入力と出力をXML タグで囲むと、データを構造化するのに役立ち、プロンプトを連鎖させるときにデータを抽出して次のステップに渡すのが容易になります。 参考: https://docs.anthropic.com/en/docs/chain-prompts では実際にプロンプトを投げてみます。 まずは、質問の回答に関係する文章だけを抽出します。 プロンプト1:引用文の抽出 XML タグで囲まれたドキュメントは次のとおりです: <document>{あらすじ}</d ocument> -- 質問 「陰謀を暴いて歴史を修正するためには何が必要ですか?」 に関連する引用を逐語的に抽出してください。 引用の完全なリストを XML タグで囲んでください。 このドキュメントにこの質問に関連すると思われる引用がない場合は、「関連する引用が見つかりません」と記入してください。 回答1: クロノス社の施設に潜入して決定的な証拠を手に入れるが、追跡者に襲われる。 アレックスは若き自分を守るために戦い、重傷を負うが、未来を託してタイムコードへ戻す。 若きアレックスは証拠を持ち帰り、陰謀を暴いて歴史を修正する。 文章から適切な場所を抜き出してくれているのが分かります。 XMLタグで囲ってくれませんでしたが… これを踏まえて2つ目のプロンプトを投げます。 2つ目のプロンプトは抽出した文章を利用して実際に回答してもらいます。 プロンプト2:質問に答える 質問に回答するために、文書と、その文書からの関連する引用文を使用してください。 文書は<document>タグで囲われた次のとおりです: <document>{あらすじ}</document> 質問に最も関連する文書からの直接引用文は次のとおりです: <quotes> クロノス社の施設に潜入して決定的な証拠を手に入れるが、追跡者に襲われる。アレックスは若き自分を守るために戦い、重傷を負うが、未来を託してタイムコードへ戻す。若きアレックスは証拠を持ち帰り、陰謀を暴いて歴史を修正する。 </ quotes> これらを使用して、質問「陰謀を暴いて歴史を修正するためには何が必要ですか? 」に対する回答を作成してください。 回答が正確であり、引用文で直接裏付けられていない情報が含まれていないことを確認してください。 回答 陰謀を暴いて歴史を修正するためには、クロノス社の施設に潜入して決定的な証拠を手に入れる必要があります。 アレックスはこの証拠を持ち帰り、陰謀を明らかにすることで歴史を修正します。 また、彼は若き自分を守るために戦い、重傷を負いながらも未来を託してタイムコードへ戻しました。 若きアレックスはこの証拠を使って陰謀を暴き、歴史を修正することができました。 先程よりも非常に自然な回答をしてくれました。 プロンプトを投げかけたユーザーにとって、より優しいの回答は後者ですね。 質問している内容としては、Prompt Chainingあり/なしの場合でほとんど変わりませんが 複数回に分割して投げることにより、より質の高い回答が得られることが分かりました。 もちろん今回は手動で2回のプロンプトを投げましたが、システム化する際はこれをブラックボックス化してユーザーに利用してもらうのだと思います。 プロンプト1とプロンプト2を裏側でChainする感じですね。 今回は文章の要約としてこのテクニックを利用しましたが、 並列処理や出力のエラーチェックなど、様々な用途に使えるようです。 このテクニックだけで記事が一本書けそうですね。 今回は概要だけにとどめますが、また他に利用できるケースについても調査してみたいと思います。 参考: https://promptengineering.org/getting-started-with-prompt-chaining/ Tree of Thoughts (ToT) 次はTree of Thoughtsです。 ここまで来ると、だいぶ難解になってきます。 基礎編のところで「丁寧な言葉でプロンプトを投げてあげましょう」って言ってたのが懐かしいです。 こちらは探索木で想像されるようなTreeを使って回答にたどり着くテクニックです。 以下の論文の図が分かりやすいかと思います。 本記事でも、図で示されている Game-of-24 というゲームを用いて解説していきたいと思います。 出典:   Yao et el. (2023) Game-of-24とは、与えられた4つの数字と算術操作(+,-,*,/)を使って24を作り出すゲームです。 これまで通りに、シンプルなプロンプトを投げてみたいと思います。 プロンプト 4, 9, 10, 13の数字を使って24のゲームを行ってください。 回答 以下は、与えられた数字4, 9, 10, 13を使って24になる式の1つです: 10 - 4 + 9 + 13 = 24 他にも解があるかもしれませんが、これは一例です。他にも解法がある場合は教えてください。 うーん、間違えてますね…。 これをToTのテクニックで解いていくプロセスを解説していきます。 なお、実際にAzure OpenAI Service上のチャットベースで試したのですが、うまくいかず… 机上の話にはなりますが、論文の情報を交えて解説していきたいと思います。 step1:出力 プロンプト 4, 9, 10, 13の数字を使って24のゲームを行ってください。 まず4つの数字のうち2つの数字を使った計算をいくつかピックアップしてください。 回答例 10 - 4 = 6 (残り 6, 9, 13) 9 + 13 = 22 (残り 4, 10, 22) ... more step2:AIによる評価 プロンプト 6, 9, 13 4, 10, 22 ... more これらの数字のうち、24に近づきそうなものはありますか? [確実]/[おそらくできる]/[不可能]の3つにカテゴライズしてください。 回答例 6, 9, 13 (おそらくできる) 4, 10, 22 (不可能) ... step3:再度出力 [確実]/[おそらくできる]にカテゴライズされた数字の組み合わせについて、再度step1から繰り返す。 プロンプト 残った6, 9, 13の数字を使って24のゲームの続きを行います。 3つの数字のうち2つの数字を使った計算をいくつかピックアップしてください。 といった流れです。 流れの確認 まず生成AIに出力を行わせ(step1)、その出力に対する評価も生成AIに行わせる(step2) そして、再度確度が高いものに対して計算の出力を行わせて、再度評価させる という繰り返しになります。 こうすることで正解にたどり着けるようですが、自分が試行した限りでは正解にたどり着くことができませんでした…。 原因としては以下のことが考えられました。 いちいちチャット形式で投げていたので試行回数が圧倒的に少なかった step2におけるAIによる判定が誤っていた 1.に関しては、システム化して試行回数を膨大にすることで、正解にたどり着ける枝を見つけることができるようにも思いました。 2.に関しては、CoTなどを併用して判定精度を上げる等でしょうか…。 残念ながら今回は正解にたどり着くことができなかったですが この手法を取ることで、正解への到達率がCoTなどの手法より優れていることが 論文 の中で述べられています。 ToTプロンプティング もっと分かりやすくToTを利用したプロンプティングの例も見つけました。 参考: https://github.com/dave1010/tree-of-thought-prompting こちらはプロンプト上で3人の専門家を登場させ、複雑な問題に対して議論させています。 先程の24のゲームとは違い、1回のプロンプト内で複数回の評価を行わせてる形になります。 参考にした例と同じプロンプトを投げてみたいと思います。 プロンプト この質問について、3人の異なる専門家が回答していると想像してください。 すべての専門家は、自分の思考の1つのステップを書き留め、それをグループと共有します。 その後、すべての専門家は次のステップに進みます。以後同様です。 もし専門家の中に、いかなる時点で誤りに気づいた場合は、退場します。 質問は... ボブはリビングルームにいる。 カップを持ってキッチンに行く。 コップの中にボールを入れ、寝室まで運ぶ。 コップを逆さまにし、庭に行く。 庭にコップを置き、車庫まで歩く。 ボールはどこにある? 回答 専門家1: ボールは庭にあります。ボブがコップを庭に置いたので、ボールも一緒に庭に置かれたはずです。 専門家2: ボールは車庫にあります。ボブがコップを逆さまにして庭に置いた後、車庫まで歩いたので、ボールも車庫にあるはずです。 専門家3: ボールは寝室にあります。ボブがリビングルームからキッチンに行き、コップの中にボールを入れ、寝室まで運んだので、ボールは寝室にあるはずです。 専門家1: しかし、ボブはコップを庭に置いた後、車庫まで歩いたので、ボールも車庫にある可能性があります。 専門家2: その通りです。ボールは庭にある可能性もありますが、車庫にある可能性もあります。 専門家3: しかし、ボブは寝室までボールを運んだので、ボールは寝室にある可能性もあります。 専門家1: なるほど、確かに寝室にもボールがある可能性がありますね。 専門家2: そうですね、寝室にもボールがあるかもしれません。 専門家3: では、ボールは庭にあるかもしれないし、車庫にあるかもしれないし、寝室にあるかもしれないという結論になりますね。 専門家1: その通りです。ボールの位置は確定できないようです。 …うーん、「確定できない」という結論で終わってしまいました。 ちなみに英語の原文そのままコピペでもダメでした。 利用するモデルによっても差が出てくるかもしれません…。 プロンプトの文章を少しずつ修正していったら、もしかしたら正解にたどり着けるかもしれませんが、 ToTの本質とはずれるように思うので、今回はここで断念したいと思います。 ただ、ToTの雰囲気はなんとなく掴んでいただけたのではないでしょうか! なお、今回は正解がある問題に対して利用しましたが、正解がない問題に対しても利用できそうです。 例えば有効なマーケティングの調査方法だったり、PV数が上がるブログの書き方だったりですね。 まとめ 今回は 知識生成プロンプティング Prompt Chaining Tree of Thoughts の3つのテクニックを紹介しました。 基礎編に比べるとだいぶ複雑になってきており、LLMを理解して、使いこなしてる感が出てきました。 ただ一方で、これらのテクニックをお手軽使いこなせないケースもあるように見られました。 相変わらずですが、 このあたりの再現や評価が難しいのが生成AIを活用するうえでの課題 な気がしています。 本記事は一応「初心者向け」と題して書いてあるのであまり深堀りはしていませんが、 今回紹介した方法はいずれも論文が書かれており、より詳細を知りたい方はそちらをご覧いただければと思います。 自分もまだ論文まではしっかり読めていないので、Prompt Chainingを利用して頑張って読んでみたいと思います。 ではまた! 参考 Prompt Engineering Guide Getting Started with Prompt Chaining Chain prompts – ANTHROPIC   tree-of-thought-prompting ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 初心者必見!プロンプトエンジニアリング入門ガイド ~応用編②~ first appeared on SIOS Tech. Lab .
アバター
みなさん、こんにちは。サイオステクノロジー武井です。今回は「世界一わかりみが深いかもしれないフロントエンドのビルド周り」と題しまして、多分C言語のポインタやAzureのサービスプリンシパルと同じくらい苦しむであろうフロントエンドのビルド周りをまとめたいと思います。いつも「あれ、なんだっけ?」的な感じになるので、備忘録的な意味も込めています。 複雑なフロントエンドのビルド周り そう思っているのは、多分、私だけじゃないかと思います。とにかくReactなどのフロントエンドのビルド周りは複雑です。webpack、babel、esbuild、polyfillなんてたくさんの横文字が飛び交い、最近はViteなるものも登場してきました。初めてフロントエンドをやる人は、混乱してしまうかもしれません。 ただ、これらの複雑な処理を理解するのには、やっぱり基本に立ち返ることが大事です。 ビルドに必要なプロセス フロントエンドのビルド周りは、たくさんの横文字ツールが乱立していますが、理解するには以下の2点が大事だと思います。 ビルドまでに必要なプロセスはどういうものがあって、どのような順序で行われるか? 数多あるツールは、どのプロセスを処理することができるか? この2点をきちんと理解することで、私はフロントエンド周りの複雑性をしっかりと把握し、納得することができました。 ちなみに、ここでいうビルドとは、開発者が書いたソースコード(TypeScript)を、ブラウザで実行できる最適化された形式に変換するプロセスのことを指します。TypeScriptはJavaScriptを拡張し、型の概念を取り入れたものです。TypeScriptはそのままではブラウザ上で動作しないため、JavaScriptに変換する必要があります。この変換プロセスを「ビルド」と呼びます。 ということで、本章では「ビルドに必要なプロセス」を説明します。そのプロセスは以下のとおりです。これはReactだろうがVueだろうが、概ね変わりません。 型チェック TypeScript→JavaScript変換 TSX/JSX→JavaScript変換 ECMAScript構文変換 Polyfill バンドル これを図解したのが以下になります。 以降は、これらのプロセスを詳しく説明していきます。 型チェック TypeScriptには、JavaScriptにはない「型」というものが存在します。それがTypeScriptがTypeScriptである由縁なのですが、その型が正しいかどうかをチェックします。 型チェックについて改めておさらいしますが、 型チェック、プログラムの中で変数や関数が正しいデータの種類(型)を持っているかどうかを確認する作業です。これにより、プログラムが意図しない動作をするのを防ぎます。 具体的な例で解説してみます。 let age: number = 25; // ここでageは数字(number)の型 age = "twenty-five"; // ここでエラー!文字列(string)を数字に代入しようとしている このコードは、age という変数が最初に数字の型として宣言されています。しかし、後で文字列を代入しようとすると、型チェックがエラーを検出します。これは、TypeScriptが型の一致を確認しているためです。 上記のコードをコンパイルしようとすると、エラーになります。 では、具体的に型チェックを行う方法をご紹介します。 まずは、TypeScriptをインストールします。 $ npm install typescript 型チェックを行いたいTypeScriptファイルを作成します。例えば、 index.ts というファイルを作成し、以下のコードを追加します。 // index.ts let age: number = 25; age = "twenty-five"; // ここで型エラーが発生します 以下のコマンドを実行します。 $ tsc index.ts --noEmit --target es5 --module commonjs --strict --jsx react --skipLibCheck 以下のようなエラーメッセージが表示されます。このエラーメッセージは、「文字列型は数値型に代入できません」という意味です。 index.ts:3:1 - error TS2322: Type 'string' is not assignable to type 'number'. 3 age = "twenty-five"; // ここで型エラーが発生します ~~~~~~~~~~~~~~~~~~ このエラーメッセージは、「文字列型は数値型に代入できません」という意味です。 --noEmit オプションは、型チェックのみ実施し、ビルドは行いません。tscコマンドは、TypeScript純正のコマンドであり、型チェックに限らず、Polyfillを除くビルドプロセス( TypeScript→JavaScript変換、 TSX/JSX→JavaScript変換、ECMAScript構文変換 )ができるのですが、ここでは型変換のみ行っています。型変換以外は、他のツールに任せたほうが都合がいいからです。 TSX/JSX→JavaScript変換 TSX/JSX→JavaScript変換 は、Reactで使われる特別な構文(TSXやJSX)を普通のJavaScriptに変換するプロセスです。これは、ブラウザが直接TSXやJSXを理解できないためです。この変換にはBabelなどのツールが使われます。 例えば、以下はよくあるReactのコードです。 // App.tsx import React from 'react'; type AppProps = { message: string; }; const App: React.FC = ({ message }) => { return <h1>{message}</h1>; }; export default App; < h1 > {message} </ h1 >の部分が、JSXの部分に該当するのですが、もちろんこの構文はJavaScriptエンジンでは理解できません。なので、このプロセスでJavaScriptに変換して上げる必要があるのです。 ECMAScript構文変換 これが一番説明が難しい部分ですので、順を追って説明します。まず、JavaScriptの歴史についてお話しします。JavaScriptは様々な事情により、多くのバージョンアップを経て進化してきました。このバージョンアップの変遷を理解することが非常に重要です。 次に、ES5からES6へのバージョンアップについて説明します。この劇的なバージョンアップが、このプロセスの存在理由と言っても過言ではありません。 最後に、具体的なソースコードを例に挙げて、ES6でしか使えないコードをES5に変換する事例を紹介します。 JavaScriptの歴史 JavaScriptは1995年にNetscape社のBrendan Eichによって開発されました。最初はウェブページに動的な機能を追加するためのスクリプト言語として誕生しました。JavaScriptの進化は以下のような段階を経ています: 1997年 : 最初の標準仕様であるECMAScript 1が登場。 1998年 : ECMAScript 2が登場。これはマイナーアップデート。 1999年 : ECMAScript 3が登場。正規表現や例外処理、文字列操作機能が追加されました。 2009年 : ECMAScript 5(ES5)が登場。厳密モード、アクセサプロパティ、Arrayメソッドの追加など、大幅な改良が行われました。 2015年 : ECMAScript 6(ES6)またはECMAScript 2015が登場。クラス構文、モジュール、アロー関数、テンプレートリテラルなど、多くの新機能が追加され、JavaScriptは大幅に進化しました。 最新のバージョンはECMAScript 2023で、毎年更新されています。 ES5からES6への劇的なバージョンアップ ES6は、JavaScriptに多くの新機能と構文を導入したため、非常に大きなアップデートでした。しかし、古いブラウザはES6の機能をサポートしていないため、ES6で書かれたコードをES5に変換する必要があります。これをECMAScript構文変換と呼びます。主にBabelというツールを使って行ってきましたが、最近はViteなるものも台頭しつつあります。 具体的なソースコード例 ということで、ではES6でしか動かないコードをES5に変換する様子をお見せします。 まず、ES6のコードを示します。これは古いブラウザではサポートされていないコードです。このコードは、アロー関数とテンプレートリテラルを使用しています。古いブラウザではこれを理解できません。 // ES6コード const greet = (name) => `Hello, ${name}!`; 次に、上記のES6コードをES5に変換したコードを示します。この変換によって、アロー関数は通常の関数表現に、テンプレートリテラルは文字列連結に置き換えられました。これにより、古いブラウザでも動作するようになります。 // ES5コード var greet = function(name) { return 'Hello, ' + name + '!'; }; つまりまとめますと、以下になります。 JavaScriptの歴史 : 1995年に誕生し、ECMAScript 5(2009年)からECMAScript 6(2015年)へと劇的に進化。 ES6の新機能 : アロー関数、クラス構文、モジュール、テンプレートリテラルなどが追加されました。 ECMAScript構文変換 : 最新のJavaScript(ES6など)を古いブラウザ(ES5)で動作させるためにコードを変換するプロセス。具体例として、アロー関数とテンプレートリテラルの変換を示しました。 Polyfill Polyfill は、最新のJavaScript機能をサポートしていない古いブラウザや環境でその機能を提供するためのコードやライブラリです。Polyfillの主な方法は以下の2つです: 自分で置き換えるためのコードを書く : 必要な機能を自分で実装する。 ライブラリを使ってコードを読み込む : 既存のライブラリを使ってPolyfillを簡単に導入する。 先のECMAScript構文解析は、古いブラウザも動くようにコードを書き換えることですが、Polyfillは 新しい機能を古い環境で提供するための追加コードです。これは実際のソースコードをお見せして説明したほうが早いかと思います。 まずはPolyfill前のコードです。このコードは、 JavaScriptの Array.prototype.includes メソッドを使用しています。このメソッドは、配列内に特定の要素が存在するかどうかを調べて、その結果を真偽値( true または false )で返します。 const array = [1, 2, 3]; console.log(array.includes(2)); // true 実は、この Array.prototype.includes メソッドは、ES2016(ES7)で導入された新しいJavaScriptの機能です。そのため、ES2016以前のバージョンのJavaScriptをサポートしている古いブラウザでは、このメソッドを直接使用することはできません。 よって、Polyfillによる変換が必要になります。先ほどご説明した2つの方法「 自分で置き換えるためのコードを書く 」「 ライブラリを使ってコードを読み込む 」で変換したいと思います。 では、まず最初に「 自分で置き換えるためのコードを書く 」です。 このコードは、古いブラウザで Array.prototype.includes メソッドが存在しない場合に、そのメソッドを自分で実装しています。これにより、古いブラウザでも includes メソッドを使えるようになります。 if (!Array.prototype.includes) { Array.prototype.includes = function(searchElement, fromIndex) { if (this == null) { throw new TypeError('"this" is null or not defined'); } const o = Object(this); const len = o.length >>> 0; if (len === 0) { return false; } const n = fromIndex | 0; let k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); while (k < len) { if (o[k] === searchElement) { return true; } k++; } return false; }; } const array = [1, 2, 3]; console.log(array.includes(2)); // true 次は、もう一つの方法「 ライブラリを使ってコードを読み込む 」です。 この方法は、手動でPolyfillを実装する代わりに、既存のライブラリを使用して必要な機能を追加するものです。一般的に使用されるライブラリの1つが core-js です。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Polyfill Example</title> </head> <body> <script src="https://cdn.jsdelivr.net/npm/core-js-bundle/minified.js"></script> <script> const array = [1, 2, 3]; console.log(array.includes(2)); // core-jsライブラリがArray.prototype.includesをサポート </script> </body> </html> core-jsは、最新のJavaScript機能を古いブラウザでも使用できるようにするためのPolyfillライブラリです。このライブラリは、ECMAScriptの標準機能だけでなく、その他のプロポーザルやグローバルオブジェクトのPolyfillも提供します。 このPolyfillライブラリは、core-js以外にもpolyfill.ioやes5-shimなどいろいろあります。 バンドル バンドル とは、複数のJavaScriptファイルを一つのファイルにまとめることです。これにより、ウェブページの読み込み速度が向上し、依存関係の管理が容易になります。バンドルは、webpackなどのツールを使って行います。 バンドルが必要な理由については以下のとおりです。 依存関係の管理 : 大規模なプロジェクトでは、多くのJavaScriptファイルが互いに依存しています。バンドルを使うと、これらの依存関係を自動的に解決し、正しい順序でファイルを読み込むことができます。 読み込み速度の向上 : 複数のJavaScriptファイルを一つにまとめることで、HTTPリクエストの数を減らし、ウェブページの読み込み速度を向上させます。 モジュール化 : モジュールごとにコードを分割して開発できます。これにより、コードの再利用性が高まり、保守性が向上します。 では、ここでwebpackを使って簡単な具体例をご紹介します。その具体例を図解したのが以下です。index.jsとmath.jsという2つのJavaScriptファイルがあり、webpackはwebpack.config.jsという設定ファイルを参照してバンドルコマンドを実施し、index.jsとmath.jsをbundle.jsという一つのファイルにガッチャンコします。index.htmlはそのガッチャンコされたbundle.jsを参照します。 まずは、 プロジェクトディレクトリを作成し、必要なパッケージをインストールします。 $ mkdir my-bundle-project $ cd my-bundle-project $ npm init -y $ npm install webpack webpack-cli --save-dev 一つにまとめる(バンドル)する前の、複数のJavaScriptファイルを作成します。まずは、足し算を掛け算をするモジュールをexportするJavaScriptを作成します。 // src/math.js export function add(a, b) { return a + b; } export function multiply(a, b) { return a * b; } 次に、足し算と掛け算をするモジュールを使って計算するJavaScriptを作成します。 // src/index.js import { add, multiply } from './math'; console.log(add(2, 3)); // 5 console.log(multiply(2, 3)); // 6 これらのファイルをバンドルするためのwebpackの設定ファイル「 webpack.config.js 」を作成します。 const path = require('path'); module.exports = { // バンドルの開始時点である最初のファイルを指定する。このファイルから // 順々に依存関係をたどっていって、必要なファイルを一つにまとめる entry: './src/index.js', // バンドルしたファイルを出力するためのディレクトリとファイル名を指定する。 output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, mode: 'development' }; webpackのコマンドを実行して、バンドルファイルを作成します。 $ npx webpack このコマンドを実行すると、 dist ディレクトリに bundle.js というファイルが生成されます。このファイルには、 index.js と math.js の内容がすべて含まれています。 最後に dist ディレクトリに index.html ファイルを作成し、バンドルされたJavaScriptファイルを読み込みます。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JavaScript Bundling Example</title> </head> <body> <script src="bundle.js"></script> </body> </html> ビルドを実現するいろいろなツール ビルドを実現するためのツールには色々ありまして、いろんなものを組み合わせ使うのが普通です。その数多あるビルドツールの中で今回は以下を取り上げようと思います。 tsc (Type Script Compiler) webpack Babel create react app Vite それぞれのツールの概要と担当可能なビルドプロセスをご説明致します。 tsc (Type Script Compiler) tsc は、TypeScriptの公式コンパイラです。TypeScriptコードをJavaScriptに変換します。 型チェック : TypeScriptコードの型チェックを行います。 TypeScript→JavaScript変換 : TypeScriptコードをJavaScriptに変換します。 TSX/JSX→JavaScript変換 : TSX/JSXをJavaScriptに変換します。 webpack webpack は、モジュールをバンドルするためのツールです。複数のJavaScriptファイルやその他のリソースを一つのバンドルファイルにまとめます。これにより、依存関係が解決され、効率的なコード配信が可能になります。 バンドルに加えて、 ローダーという仕組みを使うことで機能を拡張できます。ローダーを使うと、JavaScript以外のファイル(TypeScript、CSS、画像ファイルなど)もバンドルに含めることができるようになります。 babelとtscのローダーを使うことで、webpackから以下のようにバンドル以外のこともできます。 バンドル : Webpackの主な役割は、複数のJavaScriptファイルを一つにまとめることです。 ECMAScript構文変換 : Babelと組み合わせることで、最新のJavaScript構文を古いブラウザでも動作するように変換できます。 TypeScript→JavaScript変換 : ts-loader を使って、TypeScriptをJavaScriptに変換します。 TSX/JSX→JavaScript変換 : Babelと組み合わせて、TSX/JSXをJavaScriptに変換します。 Babel Babel は、JavaScriptのトランスパイラです。最新のJavaScript機能を古いバージョンのJavaScriptに変換します。 ECMAScript構文変換 : 最新のJavaScript構文を古いJavaScriptに変換します。 TSX/JSX→JavaScript変換 : TSX/JSXをJavaScriptに変換します。 Polyfill : 必要に応じて、 core-js などのライブラリと連携してPolyfillを提供します。 ちなみに発音は「ばべる」ではなく「ばぶる」と読むらしいです。 create react app Create React App (以降、CRAと省略)は、Reactアプリケーションを素早くセットアップするための公式CLIツールです。CRAは、開発者がすぐにReactプロジェクトを開始できるように、必要な設定や依存関係を自動的に管理します。CRAは内部的にwebpack、Babel、tscなどのツールを使用していますが、それらの詳細を開発者が直接扱う必要はありません。 型チェック : TypeScriptプロジェクトの場合、 tsc を使用。 TypeScript→JavaScript変換 : TypeScriptコードをJavaScriptに変換します。 TSX/JSX→JavaScript変換 : Babelを使用して、TSX/JSXをJavaScriptに変換します。 ECMAScript構文変換 : Babelを使用して、最新のJavaScript構文を古いバージョンに変換します。 Polyfill : 必要に応じてPolyfillを提供します。 バンドル : Webpackを使用して、複数のファイルを一つにまとめます。 Vite Vite は、次世代のフロントエンドツールで、高速なビルドと開発サーバーを提供します。ESモジュールをネイティブにサポートし、開発中のホットモジュールリプレースメント(HMR)を実現します。 型チェック : vite-plugin-checker を使って型チェックを行うことができます。 TypeScript→JavaScript変換 : TypeScriptをサポートし、JavaScriptに変換します。 TSX/JSX→JavaScript変換 : TSX/JSXをJavaScriptに変換します。 ECMAScript構文変換 : ESモジュールをサポートし、必要に応じて変換します。 Polyfill : 必要に応じてPolyfillを提供します。 バンドル : 高速なビルドを行い、ファイルをバンドルします。   ツールとプロセスの対応表 今まで説明してきたツールと各プロセスの対応表を以下にまとめました。 ツール 型チェック TypeScript→JavaScript変換 TSX/JSX→JavaScript変換 ECMAScript構文変換 Polyfill バンドル Webpack ✕ ◯ (`ts-loader`使用) ◯ (`babel-loader`使用) ◯ (Babelと組み合わせ) ◯ (Babelと組み合わせ) ◯ Babel ✕ ✕ ◯ ◯ ◯ ✕ tsc ◯ ◯ ◯ ◯ ✕ ✕ Vite ◯ ◯ ◯ ◯ ◯ ◯ Create React App ◯ ◯ ◯ ◯ ◯ ◯ ビルドツールの変遷 フロントエンドのビルドツールは、webpackからCreate React App(CRA)、そしてViteへと進化してきました。それぞれのツールが登場した背景と、それに伴うメリット・デメリットを理解することで、なぜこのような変遷を遂げたのかが見えてきます。 最初に登場したwebpackは、2012年に開発されたモジュールバンドラーです。webpackの主な目的は、複数のJavaScriptファイルやその他のリソースを一つのバンドルファイルにまとめることでした。これにより、依存関係の管理が簡単になり、効率的なコード配信が可能となりました。webpackは、柔軟性が非常に高い一方で、その設定が非常に複雑であり、学習コストが高いという課題がありました。初心者にとって、プロジェクトの初期設定が難しく、手間がかかることが多かったのです。 次に登場したCreate React App(CRA)は、2016年にFacebookによって開発されました。CRAはwebpackやBabelを内部で使用しながらも、その設定を隠蔽し、開発者が簡単にReactプロジェクトを開始できるように設計されています。CRAの主な特徴は、ゼロ設定でプロジェクトを開始できること、一貫した開発環境を提供すること、そしてテスト、ビルド、デプロイなどのツールが内蔵されていることです。これにより、開発者はすぐに開発に集中できる環境を手に入れることができました。しかし、CRAには柔軟性の制限があり、内部の設定が隠蔽されているため、独自の設定やカスタマイズが難しいというデメリットがありました。また、大規模なプロジェクトでは、ビルド速度が遅くなることがあるという問題もありました。 最後に登場したViteは、2020年にVue.jsの開発者であるEvan Youによって開発されました。Viteは、次世代のフロントエンドビルドツールであり、ESモジュール(ESM)をネイティブにサポートし、従来のツールよりも高速なビルドと開発体験を提供します。Viteの主な利点は、高速なビルド、特に開発サーバーの起動が非常に速いことです。これにより、開発時のビルドが大幅に短縮されます。さらに、ホットモジュールリプレースメント(HMR)により、コードの変更が即座に反映され、開発者の生産性が向上します。また、Viteはデフォルト設定がシンプルで、必要に応じて簡単にカスタマイズできるため、設定の複雑さが大幅に軽減されます。Viteは、ビルド時にモジュールを自動的に最適化するため、大規模なプロジェクトでも効率的に動作します。 このように、ビルドツールは、設定の簡略化、高速ビルド、柔軟なカスタマイズなどの要件に応じて進化してきました。webpackは柔軟性が高い一方で設定が複雑であり、CRAは設定の簡略化と一貫性を提供しましたが柔軟性に制限がありました。Viteはこれらの問題を解決し、より高速で柔軟な開発環境を提供することで、現代のフロントエンド開発に適したツールとなりました。 ビルドしてみよう では実際にビルドをしてみましょう。最近はViteが流行っているのは先に述べたとおりですが、ここではあえてwebpackを使います。CRAやViteだといろんな処理が隠蔽されすぎて中でやっていることが見えなくなるためです。遠回りかもしれませんが、何事も基礎をしっかり学ぶことが、どこでも通用する普遍的な能力を身につけるための近道です。 ステップ 1: プロジェクトのセットアップ まずはプロジェクトをセットアップします。 $ mkdir my-webpack-project $ cd my-webpack-project $ npm init -y ステップ 2: 必要なパッケージのインストール TypeScript、Webpack、および関連するパッケージをインストールします。 $ npm install --save-dev typescript ts-loader webpack webpack-cli webpack-dev-server @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript babel-loader html-webpack-plugin @types/react @types/react-dom $ npm install react react-dom 上記のコマンドでインストールされるパッケージの詳細は以下のとおりです。 開発用依存パッケージ( --save-dev ) これらのパッケージは、開発環境を設定し、ビルドプロセスを管理するために使用されます。 TypeScript typescript : TypeScriptコンパイラ。TypeScriptコードをJavaScriptに変換し、型チェックを行います。ここでは型チェックの機能のみしか使用しません。 TypeScriptローダー ts-loader : webpack用のTypeScriptローダー。TypeScriptファイルをJavaScriptに変換するために使用されます。 Webpackと関連ツール webpack : モジュールバンドラー。複数のJavaScriptファイルやその他のリソースを一つのバンドルファイルにまとめます。 webpack-cli : webpackのコマンドラインインターフェース。webpackの設定をコマンドラインから実行するために必要です。 webpack-dev-server : 開発用サーバー。ローカル開発環境でホットリロードを提供し、リアルタイムで変更を反映します。 Babelと関連ツール @babel/core : Babelのコアライブラリ。JavaScriptのトランスパイラであり、最新のJavaScript構文を古いバージョンに変換します。 @babel/preset-env : Babelのプリセット。ターゲット環境に合わせて必要なトランスパイルを自動的に行います。 @babel/preset-react : Babelのプリセット。JSXをJavaScriptに変換するために必要です。 @babel/preset-typescript : Babelのプリセット。TypeScriptをJavaScriptに変換するために使用されます。 babel-loader : webpack用のBabelローダー。Babelを使用してJavaScriptやTypeScriptコードをトランスパイルします。 HTMLプラグイン html-webpack-plugin : webpackプラグイン。バンドルされたJavaScriptを自動的に含むHTMLファイルを生成します。 型定義ファイル @types/react : Reactの型定義ファイル。TypeScriptでReactを使用するために必要です。 @types/react-dom : ReactDOMの型定義ファイル。TypeScriptでReactDOMを使用するために必要です。 実行時依存パッケージ これらのパッケージは、実際にアプリケーションが動作するために必要な依存関係です。 ReactとReactDOM react : Reactのコアライブラリ。UIコンポーネントを作成するためのライブラリです。 react-dom : ReactDOMライブラリ。ReactコンポーネントをDOMにレンダリングするために使用されます。 ステップ3: tsconfig.jsonの作成 プロジェクトのルートディレクトリに tsconfig.json ファイルを作成し、以下の内容を追加します。tsconfig.jsonはtscコマンド実行時の設定ファイルになります。 { "compilerOptions": { "target": "esnext", // 最新のJavaScript機能をターゲット "module": "esnext", // ESモジュールをターゲット "strict": true, // 厳密な型チェックを有効にする "esModuleInterop": true, // ESモジュールとの互換性を有効にする "skipLibCheck": true, // ライブラリの型チェックをスキップ "noEmit": true, // 型チェックのみを実施 "moduleResolution": "node" }, "include": ["src/**/*"] } 上記の設定ファイルの詳細な説明は以下のとおりです。 target : esnext を指定することで、コンパイラは最新のECMAScript仕様に準拠したコードを生成します。例えば、 async/await や最新の構文をサポートします。トランスパイル(ECMAScript構文解析)はbabelに任せるので、ここはとりあえず最新の構文で出力します。 module : esnext を指定することで、ESモジュールを使用する設定です。これは、JavaScriptの標準モジュールシステムであり、他のモジュールシステム(CommonJSなど)よりも新しいものです。 strict : TypeScriptの厳格モードを有効にします。これにより、型の厳密なチェックが行われ、潜在的なバグを未然に防ぐことができます。具体的には、以下のオプションが含まれます: noImplicitAny : 型が明示されていない変数に対して暗黙的に any 型を許可しない。 strictNullChecks : null および undefined のチェックを厳格に行う。 strictFunctionTypes : 関数型の互換性を厳格にチェックする。 esModuleInterop : このオプションを有効にすることで、ESモジュールとCommonJSモジュールの互換性が向上します。特に、 import 文でCommonJSモジュールをインポートする際に、デフォルトエクスポートが正しく扱われます。 skipLibCheck : node_modules フォルダ内のライブラリファイルの型チェックをスキップします。これにより、コンパイル時間が短縮されますが、外部ライブラリの型定義の誤りが見逃されるリスクがあります。 noEmit : 型チェックのみを実施します。それ以外のプロセスはBabelなどに任せます。 moduleResolution : node を指定することで、Node.jsのモジュール解決アルゴリズムを使用します。これにより、 node_modules フォルダ内のモジュールが適切に解決されます。 include : 型チェック対象のファイルを指定します。ここでは、 src フォルダ内のすべてのファイルとサブフォルダが対象となります。 ステップ4: Babel設定ファイルの作成 プロジェクトのルートディレクトリに babel.config.json ファイルを作成し、以下の内容を追加します。 { "presets": [ ["@babel/preset-env", { "targets": "> 0.25%, not dead", // ターゲットブラウザを指定 "useBuiltIns": "entry", "corejs": 3 }], "@babel/preset-react", "@babel/preset-typescript" ] } 上記の設定ファイルの詳細な説明は以下のとおりです。 1. @babel/preset-env @babel/preset-env は、最新のJavaScript機能をターゲットとする環境に合わせて必要なPolyfillやトランスパイルを自動的に行うためのBabelプリセットです。以下は、その設定オプションの詳細です。 targets : このオプションは、トランスパイルのターゲットとなるブラウザや環境を指定します。 "targets": "> 0.25%, not dead" "> 0.25%, not dead" : 使用頻度が0.25%以上で、サポートが終了していない(”not dead”)すべてのブラウザを対象としています。これにより、一般的に利用されている最新のブラウザをカバーしつつ、古すぎるブラウザ(例えばIE 10など)を除外します。 具体的には、ユーザーシェアが0.25%以上のブラウザで、まだサポートが続いているものが対象となります。 useBuiltIns : Polyfillのインポート方法を指定します。 "useBuiltIns": "entry" "entry" : アプリケーションのエントリーポイントで一度だけ必要なPolyfillをインポートします。これにより、必要なPolyfillだけがインポートされ、不要なものはインポートされません。 エントリーポイントで以下のように指定します: import "core-js/stable"; import "regenerator-runtime/runtime"; corejs : 使用するcore-jsのバージョンを指定します。 "corejs": 3 "corejs": 3 : core-js のバージョン3を使用します。core-jsは標準ライブラリのPolyfillを提供するライブラリです。これにより、最新のJavaScript機能が古いブラウザでも利用できるようになります。 2. @babel/preset-react @babel/preset-react は、Reactコードをトランスパイルするためのプリセットです。これにより、JSX構文を標準のJavaScriptに変換し、Reactアプリケーションを構築できるようにします。 JSXをJavaScriptに変換します。具体的には、以下のようなJSXコード: const element = <h1>Hello, world!</h1>; が、Reactの createElement を使用したJavaScriptコードに変換されます: const element = React.createElement('h1', null, 'Hello, world!'); 3. @babel/preset-typescript @babel/preset-typescript は、TypeScriptコードをJavaScriptにトランスパイルするためのプリセットです。Babelは型チェックを行わず、単純に構文を変換するだけなので、型チェックはTypeScriptのコンパイラ( tsc )を使用して行う必要があります。 TypeScriptコードをJavaScriptに変換します。以下のようなTypeScriptコード: const greeting: string = "Hello, world!"; が、型注釈を削除したJavaScriptコードに変換されます: const greeting = "Hello, world!"; ステップ5: webpack設定ファイルの作成 プロジェクトのルートディレクトリに webpack.config.js ファイルを作成し、以下の内容を追加します。 const path = require('path'); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: './src/index.tsx', // エントリポイントを指定 output: { filename: 'bundle.js', // 出力ファイル名 path: path.resolve(__dirname, 'dist') }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, "src/index.html"), }), ], resolve: { extensions: ['.ts', '.tsx', '.js'] // 拡張子を解決 }, module: { rules: [ { test: /\.tsx?$/, // .ts, .tsxファイルを処理 use: 'ts-loader', exclude: /node_modules/ }, { test: /\.jsx?$/, // .js, .jsxファイルを処理 exclude: /node_modules/, use: { loader: 'babel-loader', // Babelでトランスパイル } } ] }, mode: "development", devServer: { static: { directory: path.resolve(__dirname, 'dist'), }, compress: true, port: 9000 } }; 上記の設定ファイルの詳細な説明は以下のとおりです。 1. entry entry: './src/index.tsx' 説明 : Webpackがバンドルの作成を開始するエントリーポイントです。この設定では、 ./src/index.tsx がエントリーポイントとして指定されています。 詳細 : エントリーポイントは、アプリケーションの依存関係のルートファイルであり、ここから全ての依存ファイルが解決されます。TypeScriptとReactのプロジェクトで index.tsx ファイルをエントリーポイントとして指定します。 2. output output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } 説明 : バンドルされたファイルの出力設定です。 filename : バンドルファイルの名前を指定します。この設定では、 bundle.js という名前で出力されます。 path : 出力先ディレクトリを絶対パスで指定します。 path.resolve(__dirname, 'dist') は、現在のディレクトリにある dist フォルダを指します。 3. plugins plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, "src/index.html"), }), ] 説明 : Webpackのプラグインを設定します。ここでは、 HtmlWebpackPlugin を使用しています。 HtmlWebpackPlugin : このプラグインは、バンドルされたJavaScriptファイルを自動的にHTMLテンプレートに挿入します。 template : テンプレートとして使用するHTMLファイルを指定します。この設定では、 src/index.html がテンプレートとして使用されます。そして、distディレクトリにindex.htmlがコピーされます。 4. resolve resolve: { extensions: ['.ts', '.tsx', '.js'] } 説明 : モジュール解決時に考慮するファイル拡張子のリストを指定します。 詳細 : import 文でファイル拡張子を省略した場合に、ここで指定された拡張子を順に試します。TypeScript( .ts 、 .tsx )とJavaScript( .js )のファイルを対象としています。 5. module.rules module: { rules: [ { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ }, { test: /\.jsx?$/, exclude: /node_modules/, use: { loader: 'babel-loader', } } ] } 説明 : モジュールの異なるタイプに対して、どのローダーを適用するかを指定します。 rules : 各ルールは、特定のファイルタイプに対する処理を定義します。 TypeScriptローダー { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ } test : 対象となるファイルの拡張子を正規表現で指定します。この設定では、 .ts と .tsx ファイルが対象です。 use : 使用するローダーを指定します。ここでは、TypeScriptファイルをJavaScriptに変換するために ts-loader を使用します。 exclude : 除外するディレクトリを指定します。通常、 node_modules ディレクトリは除外します。 Babelローダー { test: /\.jsx?$/, exclude: /node_modules/, use: { loader: 'babel-loader', } } test : 対象となるファイルの拡張子を正規表現で指定します。この設定では、 .js と .jsx ファイルが対象です。 use : 使用するローダーを指定します。ここでは、Babelを使用してJavaScriptファイルをトランスパイルするために babel-loader を使用します。 6. mode mode: "development" 説明 : Webpackのビルドモードを指定します。開発モードでは、最適化やデバッグに適した設定が有効になります。 詳細 : development モードでは、ソースマップの生成、最適化の最小化、ホットモジュールリプレースメント(HMR)など、開発に便利な機能が有効になります。もう一つのモードは production で、本番環境向けに最適化されます。 7. devServer devServer: { static: { directory: path.resolve(__dirname, 'dist'), }, compress: true, port: 9000 } 説明 : Webpack Dev Serverの設定です。これにより、開発用のローカルサーバーが起動し、ファイルの変更がリアルタイムに反映されます。 static : サーバーが提供する静的ファイルのディレクトリを指定します。ここでは、 dist ディレクトリが指定されています。 compress : サーバーからのすべてのファイルに対してgzip圧縮を有効にします。これにより、ファイルの転送速度が向上します。 port : 開発サーバーがリッスンするポートを指定します。ここでは、ポート9000を使用します。 ステップ6: TypeScriptコードの作成 src ディレクトリを作成し、その中にTypeScriptファイルを作成します。例えば、 src/index.tsx というファイルを作成し、以下のコードを追加します。 // src/index.tsx import React from 'react'; import ReactDOM from 'react-dom'; type AppProps = { message: string; }; const App: React.FC = ({ message }) => { return <h1>{message}</h1>; }; ReactDOM.render(, document.getElementById('root')); src/index.html ファイルも作成し、以下の内容を追加します。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Webpack TypeScript React</title> </head> <body> <div id="root"></div> <script src="bundle.js"></script> </body> </html> ステップ7: ビルド 以下のコマンドでビルドを実行します。distディレクトリ以下にindex.htmlとbundle.jsが作成されます。 $ npx webpack ステップ8: 実行 以下のコマンドを実行して、Webpack Dev Serverを起動し、プロジェクトを実行します。 $ npx webpack-dev-server --open ブラウザが起動して、アプリケーションにアクセスできます。Hello, World!とブラウザに表示されるはずです。 まとめ いかがでしょうか?この記事で複雑なフロントエンドのビルド周りが理解できたのなら、非常にうれしみ深いです。今後ともわかりみ深い情報をお届けしていますので、今後ともよろしくお願いいたしますm(_ _)m ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 世界一わかりみが深いかもしれないフロントエンドのビルド周り first appeared on SIOS Tech. Lab .
アバター
はじめに 皆さんこんにちは!新卒4年目の 永田 と3年目の細川です。 今回は5/15(水)に開催されたOSS推進フォーラム ビジネスリレーション部会 定例テック&ビジネス勉強会 に参加してきました。 テーマはノーコード・ローコードということで2つのOSSのノーコードツールについての講演がありました。 概要 今回は以下の2講演で構成されていました。 株式会社インプリム 代表取締役社長 内田 太志さん タイトル: OSSのノーコード・ローコード開発ツール「プリザンター」 株式会社日立アカデミー 研修開発本部 横井 一仁さん タイトル: 産業分野へのローコード・ノーコード開発ツールの適用 ノーコード・ローコードツールは数多くの種類がありますが、構築の仕方の面で以下のようなカテゴリ分けができそうです。他にもカテゴリはありそうですが、例として代表的なものを3つ挙げてみます。   カテゴリ フロー系 データベース系 webサイト系 概要 部品をつないで処理の流れを作っていく系 データが入るテーブルを中心として一覧画面や各種登録、更新画面などを作る系 webサイトを気軽に作れる系 サービス例 Node-RED Pleasanter、kintone Bubble、WordPress 今回の勉強会のうち1つめの講演題材のPleasanterはこのうちのデータベース系に、2つ目の講演の題材のNode-REDはフロー系にそれぞれ分類されるかと思います。 1. OSSのノーコード・ローコード開発ツール「プリザンター」 1つ目の講演はPleasanterを一人で開発を始めて法人化された内田 太志さんが登壇され、Pleasanterの各機能の紹介や導入事例、開発の裏話などをお話してくださいました。 Pleasanter とは無料で使えるOSSのノーコード・ローコード開発ツールです。先ほども書いた通りデータベース系のノーコードツールで、データを基本とし、各テーブルにどのような項目を入れられるかを設定できたり、表示形式を自由に変えられたりなど様々な機能があります。無料で試せる デモ環境 も存在しており、 ユーザーマニュアル も充実しているため、気軽に試すことができます。 他のノーコードツールと比べて以下のようなメリットがあり、 ITreview などの紹介サイトでも高評価をいただいているそうです。 無料で利用可能で、試しやすい 動作が軽い 拡張機能により、ノーコードで対応できない仕様にも柔軟に対応可能 オンプレで動くので、バックエンドやDBにもカスタマイズ可能 実際に使ってみましたが、動作もサクサクで機能も多く、これが無料は素晴らしいと思いました。 また、僕自身が案件で少しノーコードツールに携わっていることもあり、実際に開発をされた方から直接お話しをうかがえたのは非常に貴重な経験となりました。 ノーコードツール特有のむずかしさや課題点、お客様がノーコードツールに求めていることなどもお聞きすることができ、非常に良い機会でした! 2. 産業分野へのローコード・ノーコード開発ツールの適用 2つめの講演は、日立アカデミーの横井 一仁さんが登壇され、産業分野(工場など)で多く活用されているノーコードツールのNode-REDというシステムを紹介してくださいました。 こちらの講演資料は公開可能とのことでしたので、 URL を共有させていただきます。 Node-RED はノードと呼ばれる各部品をワイヤーでつなぎ、処理をつなげていくようなプログラミングの環境です。(画像は 講演資料 より引用) ハードウェアのセンサーなどとAPIをつないだり、クラウドサービスにつないだりなど、簡単にハードウェアとつながったwebページや簡易的なアプリなどをすぐに作成できます。 講演ではNode-REDの紹介に加えて、産業分野の要件に応えるために、 electron を使ってデスクトップアプリ化したり、 WebdriverIO を使って複数ブラウザ(Chrome, Firefox)での自動テストを行ったりしていました。 Node-REDやBlocklyなどのデモも多めで非常に楽しく聞かせていただきました。 Node-RED自体はハードウェアでよく使われる規格に対応した部品が数多く用意されており、産業でよく使われるそうですが、スマホなどとも接続できるので、自主開発などで使ってみても面白そうだと感じました。 ちなみに講演されていた横井さんは日立アカデミーにて 生成AIの研修 等もされているそうですので、興味のある方はぜひ受講されてみてください。 まとめ ノーコード・ローコードツールの分野は近年毎年規模が拡大しており、注目度も高まってきている分野です。ビジネス的に注目することももちろん重要ですが、個人的に使えるようになっておくことも大事だと感じました。無料で使えるものもあるため、フロー系、webサイト系、データベース系それぞれで一つずつでも使えるようにしておくと、業務効率が大幅にアップしそうです。 おまけ 今回は、Pleasanter開発元のインプリムさんが提供されている、 株式会社インプリム Pleasanter Lounge をお貸しいただきました。 オシャレなだけでなく、中野駅徒歩2分の便利な立地に30人以上も入れる広々としたスペースで、Wi-Fiやプロジェクターなどの設備も充実しています。 [画像: https://pleasanter.org/forms/pleasanter-lounge-resavation ] IT系のイベントや勉強会の会場として無料で提供してくださっているそうなので、使いたい方はぜひ相談してみてください! おわりに 次回のOSS推進フォーラム ビジネスリレーション部会 定例テック&ビジネス勉強会は6月で、SBOM勉強会を開催予定とのことですので、興味のある方はぜひ、参加してみてください!(リンクは こちら ) 次回は弊社社員登壇予定みたいです!     .hosokawa-table-highlight { background-color: #f0f0f0; font-weight: bold; } ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post OSS推進フォーラムノーコード勉強会参加レポ first appeared on SIOS Tech. Lab .
アバター
はじめに こんにちは、サイオステクノロジーのあさりです。生成AIアプリ開発の入門として、AIチャットbotを作成したので記事にしたいと思います。この記事では、Azure OpenAI Serviceを使用してGPT-3モデルをデプロイし、StreamlitとLangchainを使用してAIチャットbotを作成します。Azure OpenAI Serviceは、OpenAIのGPT-3などのAIモデルをクラウドで簡単に利用できるサービスです。また、 Streamlit はPythonでWebアプリケーションを簡単に作成できるフレームワークで、 Langchain は、OpenAIのモデルを簡単に扱うためのライブラリです。 この記事で作成するAIチャットbotの構成は下図のようになっています。   それでは、本題に入っていきましょう。 Azure OpenAI Serviceリソースの作成 Azure OpenAI Serviceを利用するには、事前にアクセス申請が必要です。 申請用フォーム に必要事項を入力して送信しましょう。各入力項目については、 こちら の記事を参考にしてください。 Azure OpenAI Serviceのアクセス承認メールが届いたら、リソースを作成していきましょう。まず、Azure Portalにアクセスします。画面上部の検索ボックスに「openai」と入力し、「Azure OpenAI」というサービスをクリックしてください。 次に「+作成」をクリックします。 ここでは、必要な情報を入力します。サブスクリプション、リソースグループを選択(または新規作成)します。次にリージョンを選択、名前を入力し、価格レベルを選択します(価格レベルは現時点でStandard S0のみ)。すべての情報を入力したら、「次へ」をクリックします。 次にネットワークでアクセスレベルを選択します。ここでは、「インターネットを含むすべてのネットワークがこのリソースにアクセスできます。」を選択して、「次へ」をクリックします。 次に、タグを設定する必要があれば、設定をして「次へ」をクリックします。 最後にレビューおよび送信で設定した情報の確認を行います。情報が正しければ、「作成」をクリックします。リソースの作成には数分かかる場合があります。 以上がAzure OpenAI Serviceのリソース作成の手順です。次の章では、このリソースにモデルをデプロイする方法について説明します。 Azure OpenAI Serviceモデルのデプロイ リソースの作成が完了したら、モデルをデプロイしていきましょう。さきほど作成したリソースに移動し、「モデル デプロイ」タブを開きます。「展開の管理」をクリックして、Azure OpenAI Studioに移動します。 「+新しいデプロイの作成」をクリックします。 デプロイしたいモデルとそのバージョンを選択していきましょう。モデルとバージョンについては、要件に応じて こちら を参考に決定してください。各モデルのコストについては こちら を参考にしてください。今回はgpt-35-turboを選択します(gpt-35-turbo (0301) モデルと gpt-4 (0314) モデルは、2024 年 7 月 5 日以前に廃止予定)。デプロイ名については、任意の名前を設定してください(後ほど、APIを実行する際に使用します)。1分あたりのトークンレート制限(数千)については、1分あたりに消費できるトークン数の指定が可能です。120kの場合、1分間に120,000個のトークンを消費できます。入力できたら「作成」をクリックしてデプロイしましょう。 デプロイが完了したら次のように表示されます。 環境構築 デプロイが完了したら、環境構築をしましょう。 必要なライブラリのインストール 必要なライブラリ(streamlit, langchain, langchain-openai, python-dotenv)をpip installしましょう。langchain-openaiは、langchainでAzure OpenAIのモデルを使用するために必要となります。 pip install streamlit langchain langchain-openai python-dotenv 環境変数の設定 「.env」という名前のファイルを作成して、Azure OpenAIのAPIキーとエンドポイント、デプロイ名を設定します。APIキーとエンドポイントははじめに作成したリソースから確認できます。リソースに移動したら画面左のメニューから「キーとエンドポイント」をクリックしてください。①がAPIキー、②がエンドポイントになります。 デプロイ名については、モデルのデプロイの際に設定したものを以下のように設定してください。 # .env AZUREOPENAI_API_ENDPOINT=https://*****.openai.azure.com/ AZUREOPENAI_API_KEY=***** DEPLOYMENT_NAME_GPT=***** チャットbotの実装 それでは、チャットbotを実装していきます。完成版のコードは以下のようになります。 # chatbot.py import streamlit as st from langchain_openai import AzureChatOpenAI from langchain.schema import (HumanMessage, AIMessage) import os from dotenv import load_dotenv load_dotenv() def main(): llm = AzureChatOpenAI( azure_endpoint= os.environ['AZUREOPENAI_API_ENDPOINT'], openai_api_version="2023-03-15-preview", deployment_name= os.environ['DEPLOYMENT_NAME_GPT'], openai_api_key= os.environ['AZUREOPENAI_API_KEY'], openai_api_type="azure", )   # ページの設定 st.set_page_config( page_title="My Great ChatGPT", page_icon="😎" ) st.header("My Great ChatGPT 😎") # チャット履歴の初期化 if "messages" not in st.session_state: st.session_state.messages = [] # ユーザーの入力を監視 if user_input := st.chat_input("聞きたいことを入力してね!"): st.session_state.messages.append(HumanMessage(content=user_input)) with st.spinner("GPT is typing ..."): response = llm.invoke(st.session_state.messages) st.session_state.messages.append(AIMessage(content=response.content)) # チャット履歴の表示 messages = st.session_state.get('messages', []) for message in messages: if isinstance(message, AIMessage): with st.chat_message('assistant'): st.markdown(message.content) elif isinstance(message, HumanMessage): with st.chat_message('user'): st.markdown(message.content) else: st.write(f"System message: {message.content}") if __name__ == '__main__': main() まず、streamlitやlangchain、環境変数の読み込みに必要なライブラリをインポートします。 import streamlit as st from langchain_openai import AzureChatOpenAI from langchain.schema import (HumanMessage, AIMessage) import os from dotenv import load_dotenv 次に、「.env」ファイルに登録したAzure OpenAI Serviceに接続するための環境変数を読み込みます。 load_dotenv() 次に main() 関数では、まずAzure OpenAI Serviceに接続するための AzureChatOpenAI クラスのインスタンス llm を作成します。ここで環境変数から必要な情報を取得して、各種パラメータを指定しています。これによって、Azure OpenAIのAPIを使用することができます。 llm = AzureChatOpenAI( azure_endpoint= os.environ['AZUREOPENAI_API_ENDPOINT'], openai_api_version="2023-03-15-preview", deployment_name= os.environ['DEPLOYMENT_NAME_GPT'], openai_api_key= os.environ['AZUREOPENAI_API_KEY'], openai_api_type="azure", ) 次は、 st.set_page_config() 関数を使って、streamlitアプリケーションのページタイトルとアイコンを設定しています。 page_title にはページのタイトルを文字列で指定し、 page_icon にはページのアイコンを指定します。そして、 st.header 関数を使って、ページの上部に表示する大きな見出しを設定します。 # ページの設定 st.set_page_config( page_title="My Great ChatGPT", page_icon="😎" ) st.header("My Great ChatGPT 😎") 次は、チャット履歴の初期化をしています。Streamlitでは、 st.session_state でセッション状態を保持できます。このオブジェクトは辞書のように動作し、キーと値のペアを保存できます。ここでは、 st.session_state に "messages" というキーが存在しなければ、新たに "messages" というキーを作成し、その値として空のリストを設定しています。 # チャット履歴の初期化 if "messages" not in st.session_state: st.session_state.messages = [] 次のコードは、ユーザーからの入力を受け取り、それをAzure OpenAIのモデルに送信し、その応答を取得するためのものです。 まず、 st.chat_input("聞きたいことを入力してね!") を使用してユーザーからの入力を受け取ります。この関数は、ユーザーが何かを入力するとその値を返します。 次に、 st.session_state.messages.append(HumanMessage(content=user_input)) を使用して、ユーザーからの入力をセッションの messages リストに追加します。ここで、 HumanMessage クラスはユーザーからのメッセージを表現するためのもので、 content にはユーザーの入力が設定されます。 その後、 with st.spinner("ChatGPT is typing ..."): ブロック内でモデルが応答を生成している間、ユーザーに対して”GPT is typing …”というメッセージを表示します。 response = llm.invoke(st.session_state.messages) は、ユーザーからの入力をモデルに送信し、その応答を取得します。 llm.invoke メソッドは、 messages リストを引数として受け取り、モデルからの応答を返します。 最後に、 st.session_state.messages.append(AIMessage(content=response.content)) を使用して、モデルからの応答を messages リストに追加します。ここで、 AIMessage クラスはモデルからのメッセージを表現するためのもので、 content パラメータにはモデルの応答が設定されます。 # ユーザーの入力を監視 if user_input := st.chat_input("聞きたいことを入力してね!"): st.session_state.messages.append(HumanMessage(content=user_input)) with st.spinner("GPT is typing ..."): response = llm.invoke(st.session_state.messages) st.session_state.messages.append(AIMessage(content=response.content)) 次のコードは、チャットの履歴を表示するためのものです。 まず、 st.session_state.get('messages', []) を使用して、セッションから messages リストを取得します。この関数は、指定したキーが存在する場合はその値を返し、存在しない場合はデフォルト値(ここでは空のリスト)を返します。 次に、forループを使用して、取得した messages リストを順に処理します。各メッセージに対して、その型をチェックし、それに応じて表示方法を変えます。 if isinstance(message, AIMessage): ブロックでは、メッセージがモデルからの応答である場合、 with st.chat_message('assistant'): ブロック内でそのメッセージをMarkdown形式で表示します。 同様に、 elif isinstance(message, HumanMessage): ブロックでは、メッセージがユーザーからの入力である場合、 with st.chat_message('user'): ブロック内でそのメッセージを表示します。 最後に、メッセージが上記のどちらの型でもない場合、 st.write(f"System message: {message.content}") を使用して、システムメッセージとしてその内容を表示します。 # チャット履歴の表示 messages = st.session_state.get('messages', []) for message in messages: if isinstance(message, AIMessage): with st.chat_message('assistant'): st.markdown(message.content) elif isinstance(message, HumanMessage): with st.chat_message('user'): st.markdown(message.content) else: st.write(f"System message: {message.content}") ローカルで動作確認 最後に以下のコマンドを実行して動作確認をしましょう。 streamlit run chatbot.py ブラウザが立ち上がり、以下のようにチャットができるようになれば成功です。 おわりに この記事では、StreamlitとLangchainを使用してAIチャットbotを作成する方法を紹介しました。Azure OpenAI Serviceリソースの作成からモデルのデプロイ、環境構築、そして実装まで、一連のプロセスを経て、ローカル環境での動作確認まで行いました。この記事が皆さんのAIアプリケーション開発の一助となれば幸いです。 参考 https://learn.microsoft.com/ja-jp/azure/ai-services/openai/how-to/create-resource?pivots=web-portal https://learn.microsoft.com/ja-jp/azure/ai-services/openai/quickstart?tabs=command-line%2Cpython-new&pivots=programming-language-python https://zenn.dev/ml_bear/books/d1f060a3f166a5 https://qiita.com/moritalous/items/f8864d75c9855402952a ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 【Azure OpenAI Service】Streamlit×LangChainでAIアプリ開発に入門してみた first appeared on SIOS Tech. Lab .
アバター
こんにちわ。サイオスの香西です。2024/05/22-24 で開催されているNexTech Week 2024春に行ってきましたので、そのイベントレポートを本日はお送りします。 NexTech Week 2024春 とは NexTech Week とは、<AI><ブロックチェーン><量子コンピュータ>の最新テクノロジーと、<デジタル人材>を育成するサービスが出展する4つの展示会で構成されています。開催場所は、東京ビッグサイトで、春・秋の年2会開催、HP によれば、前回の2023春では、30,414名が来場したとのことです。 今年の春は、AI 領域では271社、ブロックチェーンでは56社、量子コンピュータでは32社、デジタル人材関連では95社の企業が参加されていました。 今回私は、ブロックチェーン領域をメインに参加しながら、AI、デジタル人材、量子コンピュータをザーッと流し見してきましたので、その模様をレポートさせてもらいます。 ブロックチェーン領域 ブース紹介 出展は56社らしいですが、スペース全体はそこまでだだっ広いという印象はなく、1日あればすべての出展者をまんべんなく回れるかなぁといった印象です。今回はセミナーも受講したかったり、他の領域にも顔を出したかったのでいくつかピックアップしながら徘徊させていただきました。 まず入口入って右手にあったのが、Japan Open Chain さんのブース。Ethereum 完全互換でありながら、バリデータが日本企業で、日本の法律に準拠したチェーンを構築しているのを特徴としているブロックチェーンプラットフォーマーさんです。 国産というブランドと日本法に基づくという2点から、国内企業からの利用がメインターゲットで、テストネット、メインネットともに通常利用できるものの、メインネットで利用可能な独自トークン、JOC トークンの取引所がないとのことで一般流通はまだしていないとのこと。今年中にはIEOの実施を目指しているとのことで、IEO して環境が揃った際には、日本企業での利用がかなり見込まれるのではないかと感じられました。 入口はいった左手には、サイバーリンクスさんのブースが有りました。NFT を活用したブロックチェーン名刺サービスを紹介されていました。 個人情報・企業情報をHash化し、ハッシュはEthereum に保管。表示時に画面表示情報とハッシュを検算し情報に誤りがないかを検証しているとのこと。デモでは名刺でしたが、すでに大学の卒業証明やTOEICのスコア証明などでの事例も紹介されていました。個人の証明ということでは、DID / VC の分散IDなどの事例も出てきていますが、そのへんにも今後対応予定と言ったこともお伺いできました。 そこから一歩入った左手には、トレードログさんが、非金融分野でのブロックチェーン応用事例を紹介されていました。 私が通ったときには、ちょうど電力の再エネ関係の事例紹介をされていましたが、その他にも事例紹介が行われているとともに、事例集も配布されていました。非金融分野での応用は興味のあるところですので、しっかり参考にさせていただきたいところです。 とこの調子だと、回りきれなくなりそうなので、少しスピードアップしました。 奥まったところにブースを構えていたのは、Connectiv さん。こちらでは、NFT Garden というNFT発行サービスを展示しながら、それを活用しアプリ、サービス化した、聖地巡礼プラットフォームPOWPを紹介されていました。 聖地巡礼という言葉に吸い寄せられて話をお伺いすると、スタンプラリーとNFTを組み合わせたサービスとのこと。エンタメ関連や地方自治体などでファンや観光客を送客するのに最適だそうです。   Pallet Chain や PLT Place を運営するHashPortさんを左手に見ながら奥へ入っていくと、右手には NEM / Symbol の普及を目指すNPO法人NEMTUS さん並びに、ともにNEM上でサービスを展開されている企業さんのブースが有りました。 勉強不足であまり存じ上げなかったのですが、NEM / Symbol は、Javascript, Python などWebエンジニアが慣れ親しんだ技術を使って、スマートコントラクトが開発できる特徴を持ったブロックチェーン。ガス代も安く、ブロックチェーン固有機能はSDKが準備されているとのことで学習コストと維持/運用のし安さが売りとのこと。お伺いしたときにはちょうど関係者の方たちも訪問されていたっぽく、凄くアットホームな感じの会話がなされていました。 日本オープン協会さんのブースでは、CONCORDIUM(コンコーディアム)ブロックチェーンの紹介がされていました。 コンコーディアムではウォレットを作成する際にKYCが行われており、DIC / VC、ゼロ知識証明の技術を用いて、本人の属性確認が行えるとのこと。ヨーロッパを中心に運用されているブロックチェーンで、18歳以上であることを証明し、アルコール商品を販売するといったサービスがあるそうです。本人属性の担保が取れるというのは、非常に良いですよね。サービス運営者側としては余計なことに気を取られなくて済みそうです。 ブロックチェーン領域で、最期に訪れたのは、東芝デジタルソリューションズさんのブース。こちらでは東芝さんがこれまで培ってきた分散システムの技術を活用し、企業向けのマネージドなプライベートチェーン、あるいはコンソーシアム型のブロックチェーンを提供しつつ、その上で各企業、団体向けの応用を行っているとのことでした。 企業のデジタル資産を管理するエンタープライズNFT、契約書管理を行う電子契約システム、サプライチェーンでのトレーサビリティ、サーキュラーエコノミーでの再生材の由来証明などの事例紹介がありました。こちらも是非参考にさせていただきたいです。 ブロックチェーン セミナー セミナー会場では同時に各領域ごとのセミナーも開催されています。私が訪れた日ではブロックチェーンに関しては、2件、KDDIさんを中心とした「対談】Web3の実装で目指すデジタル社会」と日本ブロックチェーン協会による「Web3 業界最新動向2024」でした。 対談の方では各社から色々な意見が出ていましたが、関西万博を景気にweb3ウォレットの普及は1000万人ベースに普及するので2025年がweb3マスアダプションに向けた一つの大きな契機であること、SNSの導入がそうであったように先に取り組んだ企業がよりうまくweb3の世界で先行できるのでいち早く準備しようといった内容が議論されていました。 もう一方の最新動向では、日本の国家戦略としてweb3が取り上げられていること、日本のDAOに関する法整備は諸外国の10年先はいっていること、日本の暗号資産取引におけるレバレッジ規制と税制の見直しが進めば日本は一気に世界の中心となれることなどが語られていました。 両セミナーでの共通のメッセージとしては、web3はまさに夜明け前。今から準備した企業がその大きな波に乗っていけるといったメッセージだったように感じました。 その他、全体感 やはり市場の盛り上がりに比例して、AI関連はスペースも非常に広く多くの企業が自社のソリューションを展示・デモされていました。今流行りの生成AI、chatGPT 関連はもちろん、画像認識や自然言語処理、データ分析などでのAI活用事例が多く出展されていました。感触としては、製造業関連の展示が多かったのと業務改善系の提案が多かった印象があります。 弊社武井が公開している RAGの実践ガイド はおかげさまで好評を頂いておりますが、おなじくRAG推しの企業も数社見受けることができ、各社いろいろな形でAI取り組みを推進されていることが実感できました。 また、デジタル人材関連では、AI活用を念頭に置いた教育プログラム・セミナーの提案、展示が目立ちました。コンサルティングから、導入支援、現場教育としてのプロンプトエンジニアリング講習などを行い、chatGPT などの生成AIを活用した業務効率の向上が大きく取り上げられていました。      おわりに どうだったでしょうか。イベントの様子が少しでも感じられたのなら嬉しく思います。イベント自体は、今週金曜まで開催していますし、無料参加が可能なので興味を持たれた方は最終日にまだ間に合いますので、参加してみてはいかがでしょうか? それではまた、次のブログにて。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post イベント参加レポート NexTech Week 2024春 first appeared on SIOS Tech. Lab .
アバター
こんにちは、サイオステクノロジーの佐藤 陽です。 前回に引き続き、プロンプトエンジニアリングについて解説していきたいと思います。 思った回答が得られないんだよな もっと生成 AI をうまく使いこなしたいな といった方は最後までご覧ください。 チートシート テクニック (個人的)要約 システムメッセージ AIに対してRoleを与えたり、回答方法のルールを制定する Few shot-Learning いくつかの例題をプロンプトに含めたうえで回答させる CoT(Chain-of-Thought) 回答に至るまでのプロセスをプロンプトに含めたうえで回答させる Zero-shot CoT ”ステップバイステップで回答してください”とプロンプトに含めたうえで回答させる Self-Consistency 類似問題および簡単なプロセスを大量にあたえ、AIに学習させたうえで回答させる はじめに 前回はプロンプトエンジニアリングの基礎編を紹介しました。 プロンプトエンジニアリングって何? プロンプトエンジニアリングって大切なの? といった方はこちらもご覧ください。 今からでも遅くない!プロンプトエンジニアリング入門 ~基礎編~ 今回はこれに引き続き、少し応用的な内容を紹介したいと思います。 弁明 先に1点、弁明させてください。 今回色々検証していたのですが、プロンプトの投げ方によっては CoT のテクニックを使っても正解が得られなかった Zero-shot CoT では正解にたどり着けなかった 複数回同じプロンプトを投げた時に正解したり、不正解だったりした などという挙動が見られました。 そのため、これから示す例はうまくいったケースのみ載せています。( ※回答の捏造はしていません。 ) 回答に揺れがあるので、このあたりが生成 AI を使いこなすにあたって難しい所だなぁと感じています。 ただテクニックとしては有用なものが多いので、ぜひ使いこなしていきたいところです。 環境 前回に引き続き、Azure OpenAI Service を利用していきたいと思います。 モデルは gpt-35-turbo-16k を利用します。 ChatGPT を利用したり、別のモデルを利用した場合でも、問題なくプロンプトエンジニアリングの効果は得られます。 ただし、得られる回答が異なる場合がありますので、その点ご了承ください。 テクニック システムメッセージ 質問を投げかける前に、AI に対して Role を与えたり、回答のルールを制定することが可能です。 日本だと「深津式プロンプト」という方法が話題になりましたが、それに似たものかと思います。 なおAzure OpenAI Service のチャットプレイグラウンドには「システムメッセージ」という欄が存在し、今回はここで設定していきたいと思います。 もちろんプロンプトに直接含めてもOKです。 例えば今回は以下のようなシステムメッセージを追加しました。 システムメッセージ あなたは日本の歴史に精通した歴史学者です。 ・質問に対して中学生にもわかるような形で 100 文字以内で回答してください。 ・日本の歴史に関する質問にのみ答えてください。 ・日本の歴史以外の質問に対しては「専門外なので分かりません」と回答してください。 これを踏まえて以下のようなプロンプトを投げます。 プロンプト 桶狭間の戦いについて教えてください 回答 桶狭間の戦いは、1560 年に織田信長と今川義元の間で行われた戦いです。 信長が義元を破り、今川家の勢力を弱めることに成功しました。 この戦いは信長の台頭と勢力拡大のきっかけとなりました。 プロンプト イオン化傾向が最も高い元素は何ですか? 回答 専門外なので分かりません。 システムメッセージに指定した通り、歴史に関する質問については 100 文字以内で分かりやすく回答してくれました。 また、化学に関する質問には「専門外なので分かりません」と答えてくれています。 ただ一方で、以下のようなやりとりも確認できました。 プロンプト 半導体に電圧を加えるとどうなりますか? 回答 半導体に電圧を加えると、その電気的特性が変化します。 具体的には、半導体の電気抵抗や導電性が変化し、電流が流れるようになったり、電流の量が変化したりします。 この現象を利用して、半導体素子(例えばダイオードやトランジスタ)が動作し、電子機器や回路を制御することが可能となります。 物理的な質問でしたが、回答してしまっています。 もしかしたら 「歴史以外の質問に対しては「専門外なので分かりません」と回答してください。」 という指示が少し曖昧で、判定が行われなかったのでしょうか? 実験的に、システムメッセージには含めず、プロンプトに直接 Role やルールを記載してみました。 プロンプト あなたは日本の歴史に精通した歴史学者です。 ・質問に対して中学生にもわかるような形で 100 文字以内で回答してください。 ・日本の歴史に関する質問にのみ答えてください。 ・日本の歴史以外の質問に対しては「専門外なので分かりません」と回答してください。 半導体に電圧を加えるとどうなりますか? 回答 専門外なので分かりません。 今回は期待する回答が得られました。 ということは、Azure OpenAI Service の「システムメッセージ機能」の実装に何かしらの影響を受けていることでしょうか…? ただ、また別の例としてプロンプトにルールのテキストを含めた場合において、日本の歴史以外の質問に回答してくれるケースがありました。 ここらへんは生成AIのModelの都合による気がしており、なかなか使いこなすのが難しい所な気がします。 X-shot Learning ここでは X-shot Learning と書きましたが、よく知られているものに「Zero-shot Learning」や「Few-shot Learning」があります。 この shot というのは「例題を提示する」ことを指しており、その数が Zero なのか Few なのかの違いです。 このテクニックの本質には前回のブログで説明した、 Examples  があります。  Zero-shot Learning Zero-shot は例を提示しないプロンプトの方法になります。 例えば以下の会話のような形です。 プロンプト 以下の文章から嬉しさを読み取り、100 点満点でスコアリングしてください。 やったね。 回答 90 点です。 この文章からも嬉しさが感じられますが、少し控えめな表現となっているため、100 点には届きません。 うーん、肯定的な感情であることはくみ取ってくれていますが、90 点という点数は少し高い気がします。 Few-shot Learning 次に Few-shot Learning のテクニックを使い、いくつかの例を与えてみます。 Azure OpenAI Service には例(shot)を加えるフォームが用意されています。 今回はこちらを利用して、以下のような例を加えていきます。 ユーザー アシスタント もう無理、しんどい… 5点 はぁ… 10 点 そっかー 50 点 やったー! 80 点 まじでーーーーー!やったーーーーーーーー!!! 100 点 再度同じプロンプトを投げます。 プロンプト 以下の文章から嬉しさを読み取り、100 点満点でスコアリングしてください。 やったね。 回答 70 点 うん、まずまず妥当な回答が得られた気がします。 このようにいくつか例を挙げることで生成 AI がその場で学習し、回答の精度を高めてくれます。 例を挙げればあげるほど回答の精度は高くなりますが プロンプトへ投げるトークン数が増えてしまうことを考えると、コストとのトレードオフはありそうです。 Fine-tuning との比較 よく Few-shot Learning と比較されるのが Fine-tuning です。 Fine-tuning はモデル自体にデータを追加し、モデル全体に再学習させる方法です。 こうすることで特定の分野に非常に強いモデルを作成できたりします。 ただ、これはなかなか骨の折れる作業で、非常に高い専門性が必要となります。 一方で Few-shot Learning はモデル自体に修正は加えません。 既存のモデルに与えるプロンプトを工夫させることで、回答の品質を向上させるのです。 どちらが優れているかは一概に言えず、ケースバイケースかと思います。 Fine-tuning の方が恐らく特定の分野に特化した、強力なモデルを作成できるように思います。 一方で Few-shot Learning の方が柔軟性があり、状況に合わせて最適な回答が得られるように思います。 Chain-of-Thought (CoT) 次に Chain-of-Thought(CoT)のテクニックを見ていきたいと思います。 こちらも Examples の考えに基づくテクニックなのですが、質問から回答に至るまでのステップをプロンプトに含めることで、精度の高い回答を得ることが可能になります。 まず CoT を利用しないケースについて見てみます。 プロンプト A さんには 2 人の弟がいます。A さんは 10 個のたこ焼きを作って、弟にそれぞれ 4 個ずつあげました。追加で 20 個作って、弟に 5 個ずつ、自分で 5 個食べました。たこ焼きは残り何個ですか? 回答 A さんが最初に作ったたこ焼きは 10 個です。弟にそれぞれ 4 個ずつあげたので、残りは 10 - (2 人の弟 × 4 個) = 10 - (2 × 4) = 10 - 8 = 2 個です。 その後、A さんは追加で 20 個のたこ焼きを作りました。弟にそれぞれ 5 個ずつあげたので、残りは 20 - (2 人の弟 × 5 個) = 20 - (2 × 5) = 20 - 10 = 10 個です。 A さんは自分で 5 個のたこ焼きを食べたので、残りは 10 - 5 = 5 個です。 したがって、たこ焼きの残りは 5 個です。 間違えてくれました!!! (裏話をすると、間違えさせるのが結構大変でした。頭が良いのも困りものです。) ではこれを CoT のテクニックを使って改善していきます。 Azure OpenAI Serviceの「例」のところに以下のようなやり取りを追加します。 ユーザー アシスタント A さんには 3 人の弟がいます。A さんは 20 個のたこ焼きを作って、弟にそれぞれ 5 個ずつあげました。 追加で 40 個作って、弟に 3 個ずつ、自分で 5 個食べました。たこ焼きは残り何個ですか? 3 人の弟に 5 個ずつあげたので、残りのたこ焼きは 20 – (3×5) = 5 個です。 追加で 40 個作ったので、残りのたこ焼きは 5 + 40 = 45 個です。 この後、3 任の弟に 3 個ずつ、自分で 5 個食べたので、残りのたこ焼きは 45 – (3×3) – 5 = 31 個です。 そのため残りは 31 個です。 これを踏まえて、先ほどと同じプロンプトを投げます。 回答 最初に A さんは 10 個作りました。 弟が 2 人おり、それぞれ 4 個ずつあげるので合計 8 個を上げました。 10-8=2 個 そのあと追加で 20 個作りました。 2+20=22 個 そのあと弟に 5 個ずつ、自分で 5 個食べました。22-(5×2)-5=7 そのため残りは 7 個です。 自分が与えた例と同じプロセスを踏んで回答し、正解にたどり着いてくれました。 このように 考えるプロセスもプロンプトに含めること が CoT のテクニックになります。 Zero-shot CoT Prompting CoT の派生として、Zero-shot CoT というテクニックもあります。 例も提示せず(Zero-shot)、CoT とは…?という感じですが 「ステップバイステップで回答してください」 といったような言葉をプロンプトに付け加えることで実現できるようです。 早速やってみましょう。 プロンプト A さんには 2 人の弟がいます。A さんは 10 個のたこ焼きを作って、弟にそれぞれ 4 個ずつあげました。追加で 20 個作って、弟に 5 個ずつ、自分で 5 個食べました。たこ焼きは残り何個ですか? ステップバイステップで回答してください。 回答 ステップ 1: 最初に作った 10 個のたこ焼きを考えましょう。 弟に 4 個ずつあげたので、弟たちは合計で 8 個のたこ焼きを受け取りました。 自分で 5 個食べたので、残りのたこ焼きは 10 - 8 - 5 = 2 個です。 ステップ 2: 追加で作った 20 個のたこ焼きを考えましょう。 弟たちに 5 個ずつあげると、合計で弟たちは 2 * 5 = 10 個のたこ焼きを受け取ります。 自分で 5 個食べたので、残りのたこ焼きは 20 - 10 - 5 = 5 個です。 ステップ 3: 最初に作ったたこ焼きと追加で作ったたこ焼きの残りを合計します。 最初に作ったたこ焼きの残り 2 個と、追加で作ったたこ焼きの残り 5 個を合計すると、2 + 5 = 7 個のたこ焼きが残ります。 したがって、たこ焼きは残り 7 個です。 確かにできました! 例題を与えなくても正解にたどり着いてくれました。 実はこれは OpenAI の セミナー でも LLM を最適化する方法として言及されている方法であり、「 Give GPTs time to “think” 」という表現がされていました。 GPT に考える時間を与えるってなんだか未来感がありますね。  ちなみに記事冒頭でも書きましたが、この手法を取った場合でも回答が誤っているケースもありました。 なのでどの程度の複雑な質問に有効かはしっかり検討する必要がありそうです。 Self-Consistency Self-Consistency(自己整合性)は、Wang et al. (2022)さんの 論文 によると to replace the naive greedy decoding used in chain-of-thought prompting と定義されています。 日本語に翻訳すると CoTで使用されるnative greedy decodingを置き換えることを目的としています うーん、少し分かりづらいですね。 先に実例を見てみたいと思います。 なお、今回プロンプトに与える例が多いので、 参考にしたサイト からそのまま拝借しています。 また、「例」の機能は使わず、プロンプトに直接書き込んでいます。 これを踏まえ、以下のようなプロンプトを投げます。 プロンプトの最後に、先程利用したたこ焼きの問題を追加しています。 プロンプト Q:駐車場に車が 3 台あり、さらに 2 台の車が到着した場合、駐車場には何台の車がありますか? A:駐車場には既に 3 台の車があります。2 台の車が到着しました。これで、車が 3+2 = 5 台あります。 回答は 5 です。 -- Q:リアは 32 個のチョコレートを持っており、彼女の姉妹は 42 個のチョコレートを持っています。 彼らが 35 個食べた場合、彼らが残したピースの数は何ですか? A:リアは 32 個のチョコレートを持っており、リアの姉妹は 42 個のチョコレートを持っていたことを意味します。 つまり、もともとは 32 + 42 = 74 個のチョコレートがありました。35 個食べられました。したがって、 合計で残るのは 74-35 = 39 個のチョコレートです。回答は 39 です。 -- Q:ショーンは 5 つのおもちゃを持っています。クリスマスに、彼は両親からそれぞれ 2 つのおもちゃをもらいました。 今、彼は何個のおもちゃを持っていますか? A:彼は 5 つのおもちゃを持っています。彼は母親から 2 つのおもちゃをもらいました。したがって、 5 + 2 = 7 個のおもちゃがあります。 その後、父親から 2 つのおもちゃが追加されたので、合計で 7 + 2 = 9 個のおもちゃがあります。 回答は 9 です。 -- Q:マイケルは 58 個のゴルフボールを持っています。火曜日に、彼は 23 個のゴルフボールを 失いました。水曜日に、さらに 2 個を失いました。水曜日の終わりには、彼は何個のゴルフボール を持っていましたか? A:マイケルは最初に 58 個のボールを持っていました。火曜日に 23 個を失いましたので、 その後 35 個のボールが残りました。水曜日に 2 個を失ったので、現在 33 個のボールがあります。 回答は 33 です。 -- Q:オリビアは 23 ドル持っています。彼女は 1 つあたり 3 ドルのベーグルを 5 つ買いました。 彼女が残したお金はいくらですか? A:彼女は 1 つあたり 3 ドルのベーグルを 5 つ購入しました。彼女は15ドルを使った。 残したお金は8ドルです。 -- Q:A さんには 2 人の弟がいます。A さんは 10 個のたこ焼きを作って、弟にそれぞれ 4 個ずつあげました。追加で 20 個作って、弟に 5 個ずつ、自分で 5 個食べました。たこ焼きは残り何個ですか? 回答 A:A さんは最初に 10 個のたこ焼きを作りました。 弟たちにそれぞれ 4 個ずつあげたので、10 - (4 + 4) = 2 個のたこ焼きが残ります。 その後、さらに 20 個のたこ焼きを作りました。 弟たちにそれぞれ 5 個ずつあげたので、20 - (5 + 5) = 5 個のたこ焼きが残ります。 したがって、合計で 2 + 5 = 7 個のたこ焼きが残っています。 回答は 7 です。 正解です! Few-shot CoT と同じような形に見えますが、 (個人的な解釈として)Self-Consistency は 回答させたい質問とは少し形式の異なった例を与える 解答に至るプロセスをCoTほど詳しく書かない 例題の量を多めに与える といったテクニックなのかな?と感じました。 人間でいうと「テストに向けて問題集をたくさん解いて、解き方を理解したうえでテストに臨む」といった感じでしょうか? そうすると、問題集と違う問題がテストに出ても対応できますし、応用が利きそうですよね。 まとめ 今回は、前回の基礎編に続き応用編と題してプロンプトのテクニックを紹介しました。 X-Shot Learningや、CoT、Self-Consistencyなど、実際今日から使えるテクニックだと思います。 是非プロンプトに組み込んで使っていってみたいところです。 まだまだ奥が深いプロンプトエンジニアリングなので、さらなる追求をしてみたいと思います。 またネタができたら記事にしていきたいと思うのでこうご期待ください! ではまた! 参考 プロンプト エンジニアリングの手法 Prompt Engineering Guide ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 初心者必見!プロンプトエンジニアリング入門ガイド ~応用編①~ first appeared on SIOS Tech. Lab .
アバター
今号では、dig コマンドの使い方やオプションについてご紹介します! dig コマンドとは dig コマンドは、DNS サーバに問合せを行ない、様々な情報を取得することができるコマンドです。 主に、下記の用途で使用される事が多いです。  ・ホスト名から IP アドレスを調べる (正引き)  ・IP アドレスからホスト名を調べる (逆引き)  ・MX レコード (メールサーバ)、NS レコード (ネームサーバ) などの詳細な情報を取得する dig コマンドにより DNS サーバに関する詳細を確認できるため、DNS に関連する設定に誤りがないか、 またネットワークの問題が発生した際に、名前解決に問題がないか等を確認できます。 dig コマンドのインストール dig コマンドは bind-utils パッケージに含まれるコマンドです。 デフォルトではインストールされていないため (RHEL6~RHEL9 の環境で確認済み)、 まずは bind-utils をインストールしましょう。 # dnf install bind-utils 基本の使い方・結果の見方 A レコード (IPv4 アドレス) の情報を表示する dig コマンドの後にホスト名を入力すると、 A レコード (IPv4 アドレス) の情報 が表示されます。 # dig example.com ; > DiG 9.11.36-RedHat-9.11.36-11.el8_9.1 > example.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER ・ QUESTION SECTION:   example.com の A レコードへの問合せであることを示します。 ・ ANSWER SECTION:   example.com の A レコードが 93.184.215.14 であることを示します。 ・ Query time:   問合せにかかった時間 (この場合は 2ミリ秒) を示します。 ・ SERVER:   問合せを行なった DNS サーバのアドレス (この場合は 172.31.0.2) を示します。 AAAA レコード (IPv6 アドレス) の情報を表示する 最初の例では IPv4 の情報のみ表示されますが、 AAAA レコード (IPv6 アドレス) の情報を 表示したい場合は、ホスト名の後に AAAA と記載します。 すると、 ANSWER SECTION の IP アドレスが、 IPv6 に変化していること が分かります。 # dig example.com AAAA ; > DiG 9.11.36-RedHat-9.11.36-11.el8_9.1 > example.com AAAA ;; global options: +cmd ;; Got answer: ;; ->>HEADER MX レコード (メールサーバ) の情報を表示する メールサーバに関連する情報を表示したい場合は、ホスト名の後に MX と記載します。 すると、 ANSWER SECTION に MX レコードの情報 (mail.example.com) および優先度 (10) が 表示されていることが分かります。 # dig example.com MX ; > DiG 9.11.36-RedHat-9.11.36-11.el8_9.1 > example.com MX ;; global options: +cmd ;; Got answer: ;; ->>HEADER NS レコード (ネームサーバ) の情報を表示する ネームサーバに関連する情報を表示したい場合は、ホスト名の後に NS と記載します。 すると、 ANSWER SECTION に NS レコードの情報 (ns1.example.com と ns2.example.com) が 表示されていることが分かります。 # dig example.com NS ; > DiG 9.11.36-RedHat-9.11.36-11.el8_9.1 > example.com NS ;; global options: +cmd ;; Got answer: ;; ->>HEADER すべてのレコードの情報を表示する 対象サーバのすべての情報を表示したい場合は、ホスト名の後に ANY と記載します。 例として、メールサーバ example.com のすべての情報を表示した場合、 ANSWER SECTION に A レコード、AAAA レコード、MX レコードの情報が表示されていることが分かります。 # dig example.com ANY ; > DiG 9.10.3-P4-Ubuntu > example.com ANY ;; global options: +cmd ;; Got answer: ;; ->>HEADER 逆引きを行う 逆引き (IP アドレスからホスト名を問合せる) を行いたい場合は -x オプション を指定します。 すると、 ANSWER SECTION に ホスト名 (server.example.com) が表示されていることが分かります。 # dig -x 172.10.1.10 ; > DiG 9.11.36-RedHat-9.11.36-11.el8_9.1 > -x 172.10.1.10 ;; global options: +cmd ;; Got answer: ;; ->>HEADER ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post 知っておくとちょっと便利!dig コマンドの使い方 first appeared on SIOS Tech. Lab .
アバター