ソフトウェア エンジニアは、この世界で刺激的な地位を占めています。技術スタックや業界に関係なく、私たちは雇用主の目標や目的に直接貢献する問題を解決する使命を負っています。おまけに、テクノロジーを利用して、目の前に現れるあらゆる課題を軽減できるようになります。
この例では、Postgres 用のオープンソースのベクトル類似性検索であるpgvector を使用して、企業データ内に存在するデータの類似性を特定する方法に焦点を当てたいと思いました。
簡単な例として、マーケティング部門が開始予定のキャンペーンについて支援を必要としていると仮定します。目標は、ソフトウェア業界と密接に連携する業界のすべてのSalesforceアカウントに連絡することです。
最終的には、最も類似した上位 3 つの業界のアカウントに焦点を当て、将来的にはこのツールを使用して他の業界の類似点を見つけられるようにしたいと考えています。可能であれば、常に上位 3 つを返すのではなく、希望する数の一致する業界を提供するオプションが欲しいと考えています。
この使用例は、類似性検索の実行を中心としています。この演習を手動で完了することも可能ですが、複数の言語用に事前にトレーニングされた埋め込みがすでに作成されているため、 Wikipedia2Vecツールが思い浮かびます。単語の埋め込み (ベクトルとも呼ばれます) は、構文情報と意味情報の両方を含む単語の数値表現です。単語をベクトルとして表すことにより、どの単語が他の単語に意味的に「近い」かを数学的に判断できます。
この例では、Salesforce で構成されている業界ごとにワード ベクトルを作成する簡単な Python プログラムを作成することもできます。
pgvector
拡張機能には Postgres データベースが必要です。ただし、この例のエンタープライズ データは現在 Salesforce に存在します。幸いなことに、 Heraku Connect は、Salesforce アカウントを Heroku Postgres と同期し、 salesforce.account
というテーブルに保存する簡単な方法を提供します。次に、Salesforce 内の各業界 (VARCHAR キーとして) とそれに関連付けられた単語ベクトルを含むsalesforce.industries
という別のテーブルを作成します。
Postgres の Salesforce データとワード ベクトルを使用して、Java と Spring Boot を使用して RESTful API を作成します。このサービスは必要なクエリを実行し、結果を JSON 形式で返します。
ソリューションの概要を次のように説明します。
ソース コードは GitLab に存在します。 git push heroku
コマンドを発行すると Heroku でのデプロイメントがトリガーされ、マーケティング チームが簡単に利用できる RESTful API が導入されます。
高レベルの設計が完了したら、ソリューションの構築を開始できます。 Salesforce ログインを使用して、 「アカウント」画面に移動して、この演習のデータを表示することができました。エンタープライズ データの最初のページの例を次に示します。
今回の取り組みでは、マーケティングチームのリクエストを解決するために Heroku を使用することを計画しました。 Heroku アカウントにログインし、 [Create New App]ボタンを使用して、 similarity-search-sfdc
という新しいアプリケーションを作成しました。
アプリを作成した後、 [リソース]タブに移動して Heroku Postgres アドオンを見つけました。アドオンの検索フィールドに「Postgres」と入力しました。
リストからHeroku Postgresを選択した後、 Standard 0プランを選択しましたが、 pgvector は、PostgreSQL 15 またはベータ版の Essential 層データベースを実行する Standard 層 (またはそれ以上) のデータベース製品で利用できます。
アドオンを確認すると、Heraku はDATABASE_URL
接続文字列を生成して提供しました。これは、アプリの [設定] タブの[構成変数]セクションで見つかりました。この情報を使用してデータベースに接続し、次のように pgvector 拡張機能を有効にしました。
CREATE EXTENSION vector;
次に、 Heraku Connectアドオンを検索して見つけました。これにより、Salesforce のエンタープライズ データに簡単に接続できることがわかりました。
この演習では、無料のDemo Editionプランが問題なく機能します。
この時点で、 similarity-search-sfdc
アプリの[リソース]タブは次のようになります。
「 Heroku Connect のセットアップ」の手順に従って、Salesforce アカウントを Heroku Connect にリンクしました。次に、同期するアカウントオブジェクトを選択しました。完了すると、同じ Salesforce アカウント データを Heroku Connect と基盤となる Postgres データベースで確認できるようになりました。
SQL の観点から見ると、私が行った結果、次の設計のsalesforce.account
テーブルが作成されました。
create table salesforce.account ( createddate timestamp, isdeleted boolean, name varchar(255), systemmodstamp timestamp, accountnumber varchar(40), industry varchar(255), sfid varchar(18), id serial primary key, _hc_lastop varchar(32), _hc_err text );
類似性検索が期待どおりに機能するためには、Salesforce アカウント業界ごとに単語ベクトルを生成する必要がありました。
主な使用例では、ソフトウェア業界の類似点を見つける必要があることが示されているため、その業界のワード ベクトルも生成する必要があります。
この演習では作業を簡単にするために、Python 3.9 とembed.py
というファイルを使用してこのタスクを手動で実行しました。これは次のようになります。
from wikipedia2vec import Wikipedia2Vec wiki2vec = Wikipedia2Vec.load('enwiki_20180420_100d.pkl') print(wiki2vec.get_word_vector('software').tolist())
注意してください – get_word_vector()
メソッドは業界を表す小文字を想定しています。
python embed.py
を実行すると、 software
ワードの次のワード ベクトルが生成されました。
[-0.40402618050575256, 0.5711150765419006, -0.7885153293609619, -0.15960034728050232, -0.5692323446273804, 0.005377458408474922, -0.1315757781267166, -0.16840921342372894, 0.6626015305519104, -0.26056772470474243, 0.3681095242500305, -0.453583300113678, 0.004738557618111372, -0.4111144244670868, -0.1817493587732315, -0.9268549680709839, 0.07973367720842361, -0.17835664749145508, -0.2949991524219513, -0.5533796548843384, 0.04348105192184448, -0.028855713084340096, -0.13867013156414032, -0.6649054884910583, 0.03129105269908905, -0.24817068874835968, 0.05968991294503212, -0.24743635952472687, 0.20582349598407745, 0.6240783929824829, 0.3214546740055084, -0.14210252463817596, 0.3178422152996063, 0.7693028450012207, 0.2426985204219818, -0.6515568494796753, -0.2868216037750244, 0.3189859390258789, 0.5168254971504211, 0.11008890718221664, 0.3537853956222534, -0.713259220123291, -0.4132286608219147, -0.026366405189037323, 0.003034653142094612, -0.5275223851203918, -0.018167126923799515, 0.23878540098667145, -0.6077089905738831, 0.5368344187736511, -0.1210874393582344, 0.26415619254112244, -0.3066694438457489, 0.1471938043832779, 0.04954215884208679, 0.2045321762561798, 0.1391817331314087, 0.5286830067634583, 0.5764685273170471, 0.1882934868335724, -0.30167853832244873, -0.2122340053319931, -0.45651525259017944, -0.016777794808149338, 0.45624101161956787, -0.0438646525144577, -0.992512047290802, -0.3771328926086426, 0.04916151612997055, -0.5830298066139221, -0.01255014631897211, 0.21600870788097382, -0.18419665098190308, 0.1754663586616516, -0.1499166339635849, -0.1916201263666153, -0.22884036600589752, 0.17280352115631104, 0.25274306535720825, 0.3511175513267517, -0.20270302891731262, -0.6383468508720398, 0.43260180950164795, -0.21136239171028137, -0.05920517444610596, 0.7145522832870483, 0.7626600861549377, -0.5473887920379639, 0.4523043632507324, -0.1723199188709259, -0.10209759324789047, -0.5577948093414307, -0.10156919807195663, 0.31126976013183594, 0.3604489266872406, -0.13295558094978333, 0.2473849356174469, 0.278846800327301, -0.28618067502975464, 0.00527254119515419]
単語ベクトルを保存するには、次の SQL コマンドを使用して、Postgres データベースにindustries
テーブルを追加する必要がありました。
create table salesforce.industries ( name varchar not null constraint industries_pk primary key, embeddings vector(100) not null );
industries
テーブルを作成したら、生成された各単語ベクトルを挿入します。これは、次のような SQL ステートメントを使用して実行します。
INSERT INTO salesforce.industries (name, embeddings) VALUES ('Software','[-0.40402618050575256, 0.5711150765419006, -0.7885153293609619, -0.15960034728050232, -0.5692323446273804, 0.005377458408474922, -0.1315757781267166, -0.16840921342372894, 0.6626015305519104, -0.26056772470474243, 0.3681095242500305, -0.453583300113678, 0.004738557618111372, -0.4111144244670868, -0.1817493587732315, -0.9268549680709839, 0.07973367720842361, -0.17835664749145508, -0.2949991524219513, -0.5533796548843384, 0.04348105192184448, -0.028855713084340096, -0.13867013156414032, -0.6649054884910583, 0.03129105269908905, -0.24817068874835968, 0.05968991294503212, -0.24743635952472687, 0.20582349598407745, 0.6240783929824829, 0.3214546740055084, -0.14210252463817596, 0.3178422152996063, 0.7693028450012207, 0.2426985204219818, -0.6515568494796753, -0.2868216037750244, 0.3189859390258789, 0.5168254971504211, 0.11008890718221664, 0.3537853956222534, -0.713259220123291, -0.4132286608219147, -0.026366405189037323, 0.003034653142094612, -0.5275223851203918, -0.018167126923799515, 0.23878540098667145, -0.6077089905738831, 0.5368344187736511, -0.1210874393582344, 0.26415619254112244, -0.3066694438457489, 0.1471938043832779, 0.04954215884208679, 0.2045321762561798, 0.1391817331314087, 0.5286830067634583, 0.5764685273170471, 0.1882934868335724, -0.30167853832244873, -0.2122340053319931, -0.45651525259017944, -0.016777794808149338, 0.45624101161956787, -0.0438646525144577, -0.992512047290802, -0.3771328926086426, 0.04916151612997055, -0.5830298066139221, -0.01255014631897211, 0.21600870788097382, -0.18419665098190308, 0.1754663586616516, -0.1499166339635849, -0.1916201263666153, -0.22884036600589752, 0.17280352115631104, 0.25274306535720825, 0.3511175513267517, -0.20270302891731262, -0.6383468508720398, 0.43260180950164795, -0.21136239171028137, -0.05920517444610596, 0.7145522832870483, 0.7626600861549377, -0.5473887920379639, 0.4523043632507324, -0.1723199188709259, -0.10209759324789047, -0.5577948093414307, -0.10156919807195663, 0.31126976013183594, 0.3604489266872406, -0.13295558094978333, 0.2473849356174469, 0.278846800327301, -0.28618067502975464, 0.00527254119515419] ');
注 – ソフトウェア業界 (ソフトウェア) を小文字で表現したワード ベクトルを作成しましたが、industry.name industries.name
は大文字の業界名 (ソフトウェア) と一致する必要があります。
生成されたすべてのワード ベクトルがindustries
テーブルに追加されたら、RESTful API の導入に焦点を移すことができます。
目の前の課題を解決するためのすべてが整っていたため、ソフトウェア エンジニアとしての私の情熱が最高潮に達したのはこの時でした。
次に、Spring Boot 3.2.2 と Java (temurin) 17 を使用して、IntelliJ IDEA で次の Maven 依存関係similarity-search-sfdc
プロジェクトを作成しました。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.pgvector</groupId> <artifactId>pgvector</artifactId> <version>0.1.4</version> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
AccountオブジェクトとIndustry (embedding) オブジェクトの両方に簡略化されたエンティティを作成し、前に作成した Postgres データベース テーブルと並べました。
@AllArgsConstructor @NoArgsConstructor @Data @Entity @Table(name = "account", schema = "salesforce") public class Account { @Id @Column(name = "sfid") private String id; private String name; private String industry; } @AllArgsConstructor @NoArgsConstructor @Data @Entity @Table(name = "industries", schema = "salesforce") public class Industry { @Id private String name; }
JpaRepository インターフェイスを使用して、Postgres テーブルに簡単にアクセスできるように次の拡張機能を追加しました。
public interface AccountsRepository extends JpaRepository<Account, String> { @Query(nativeQuery = true, value = "SELECT sfid, name, industry " + "FROM salesforce.account " + "WHERE industry IN (SELECT name " + " FROM salesforce.industries " + " WHERE name != :industry " + " ORDER BY embeddings <-> (SELECT embeddings FROM salesforce.industries WHERE name = :industry) " + " LIMIT :limit)" + "ORDER BY name") Set<Account> findSimilaritiesForIndustry(String industry, int limit); } public interface IndustriesRepository extends JpaRepository<Industry, String> { }
findSimilaritiesForIndustry()
メソッドは、このユースケースを解決するためにすべての面倒な作業が行われることに注意してください。このメソッドは次のパラメータを受け入れます。
industry
: 類似点を見つける業界limit
: アカウントのクエリ時に検索する業界の類似性の最大数
上記のクエリのユークリッド距離演算子 (<->) に注目してください。これは、類似性検索を実行するための拡張機能の組み込み演算子です。
元の「ソフトウェア」業界のユースケースと、最も近い 3 つの業界に制限が設定されている場合、実行されるクエリは次のようになります。
SELECT sfid, name, industry FROM salesforce.account WHERE industry IN (SELECT name FROM salesforce.industries WHERE name != 'Software' ORDER BY embeddings <-> (SELECT embeddings FROM salesforce.industries WHERE name = 'Software') LIMIT 3) ORDER BY name;
そこから、JPA リポジトリと対話するためのAccountsService
クラスを構築しました。
@RequiredArgsConstructor @Service public class AccountsService { private final AccountsRepository accountsRepository; private final IndustriesRepository industriesRepository; public Set<Account> getAccountsBySimilarIndustry(String industry, int limit) throws Exception { List<Industry> industries = industriesRepository.findAll(); if (industries .stream() .map(Industry::getName) .anyMatch(industry::equals)) { return accountsRepository .findSimilaritiesForIndustry(industry, limit); } else { throw new Exception( "Could not locate '" + industry + "' industry"); } } }
最後に、 AccountsController
クラスに RESTful エントリ ポイントを提供させ、 AccountsService
に接続させました。
@RequiredArgsConstructor @RestController @RequestMapping(value = "/accounts") public class AccountsController { private final AccountsService accountsService; @GetMapping(value = "/similarities") public ResponseEntity<Set<Account>> getAccountsBySimilarIndustry(@RequestParam String industry, @RequestParam int limit) { try { return new ResponseEntity<>( accountsService .getAccountsBySimilarIndustry(industry, limit), HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } }
Spring Boot サービスの準備ができたら、次のProcfile
プロジェクトに追加して、Heraku にサービスの詳細を知らせます。
web: java $JAVA_OPTS -Dserver.port=$PORT -jar target/*.jar
安全のため、 system.properties
ファイルを追加して、期待される Java と Maven のバージョンを指定しました。
java.runtime.version=17 maven.version=3.9.5
Heroku CLI を使用して、 similarity-search-sfdc
サービスのリモートを GitLab リポジトリに追加して Heroku プラットフォームに追加しました。
heroku git:remote -a similarity-search-sfdc
また、次のコマンドを使用して、 similarity-search-sfdc
サービスのビルドパック タイプを設定します。
heroku buildpacks:set https://github.com/heroku/heroku-buildpack-java
最後に、次のコマンドを使用して、 similarity-search-sfdc
サービスを Heroku にデプロイしました。
git push heroku
これで、 similarity-search-sfdc
アプリの[リソース]タブが次のように表示されます。
RESTful API を実行した状態で、次の cURL コマンドを発行して、ソフトウェア業界に最も近い上位 3 つの Salesforce 業界 (および関連するアカウント) を見つけました。
curl --location 'https://HEROKU-APP-ROOT-URL/accounts/similarities?industry=Software&limit=3'
RESTful API は、次のペイロードとともに200 OK HTTP
応答ステータスを返します。
[ { "id": "001Kd00001bsP80IAE", "name": "CleanSlate Technology Group", "industry": "Technology" }, { "id": "001Kd00001bsPBFIA2", "name": "CMG Worldwide", "industry": "Media" }, { "id": "001Kd00001bsP8AIAU", "name": "Dev Spotlight", "industry": "Technology" }, { "id": "001Kd00001bsP8hIAE", "name": "Egghead", "industry": "Electronics" }, { "id": "001Kd00001bsP85IAE", "name": "Marqeta", "industry": "Technology" } ]
結果として、この例では、テクノロジー業界、メディア業界、およびエレクトロニクス業界がソフトウェア業界に最も近い業界になります。
現在、マーケティング部門は、次のキャンペーンのために連絡できるアカウントのリストを持っています。
何年も前、私は自分では認められないほど多くの時間を、 Team Fortress 2マルチプレイヤー ビデオ ゲームのプレイに費やしました。これは 2012 年に開催された、とても楽しかったイベントのスクリーンショットです。
私の人生のこの側面に詳しい人なら、私がデフォルトで選択したプレイヤー クラスは兵士だったことがわかるでしょう。これは、兵士が健康、動き、速度、火力のバランスが最も優れているためです。
ソフトウェア エンジニアは、どんな状況にも適応し、期待に応えるソリューションを効率的に提供することに注力できるため、現実世界の「兵士階級」であると感じています。
ここ数年、私は次のミッション ステートメントに焦点を当ててきました。これはあらゆる IT プロフェッショナルに適用できると考えています。
「知的財産の価値を高める機能を提供することに時間を集中してください。その他すべてにフレームワーク、製品、サービスを活用してください。」
- J. ヴェスター
この投稿の例では、Heraku Connect を利用してエンタープライズ データを Postgres データベースと同期することができました。 pgvector 拡張機能をインストールした後、これらの Salesforce アカウントから固有の業界ごとにワード ベクトルを作成しました。最後に、Spring Boot サービスを導入しました。これにより、別の業界に最も近い業界の Salesforce アカウントを見つけるプロセスが簡素化されました。
私たちは、既存のオープンソース テクノロジ、小さな Spring Boot サービス、および Heroku PaaS の追加を使用して、このユースケースを迅速に解決しました。これは私のミッション ステートメントに完全に準拠しています。これらのフレームワーク、製品、サービスがなければ、どれほどの時間がかかるか想像できません。
ご興味がございましたら、この記事の元のソース コードを GitLab で見つけることができます。
https://gitlab.com/johnjvester/similarity-search-sfdc
本当に素晴らしい一日をお過ごしください!