Các kỹ sư phần mềm chiếm một vị trí thú vị trong thế giới này. Bất kể ngành công nghệ hay ngành nào, chúng tôi đều có nhiệm vụ giải quyết các vấn đề góp phần trực tiếp vào mục tiêu và mục đích của người sử dụng lao động. Như một phần thưởng, chúng tôi có thể sử dụng công nghệ để giảm thiểu mọi thách thức xảy ra với chúng tôi.
Trong ví dụ này, tôi muốn tập trung vào cách pgvector – một tìm kiếm tương tự vectơ nguồn mở cho Postgres – có thể được sử dụng để xác định các điểm tương đồng dữ liệu tồn tại trong dữ liệu doanh nghiệp.
Ví dụ đơn giản: giả sử bộ phận tiếp thị yêu cầu hỗ trợ cho chiến dịch mà họ dự định triển khai. Mục tiêu là tiếp cận tất cả các tài khoản Salesforce thuộc các ngành có liên kết chặt chẽ với ngành phần mềm.
Cuối cùng, họ muốn tập trung vào các tài khoản thuộc ba ngành giống nhau nhất, với khả năng sử dụng công cụ này trong tương lai để tìm ra những điểm tương đồng với các ngành khác. Nếu có thể, họ muốn có tùy chọn cung cấp số lượng ngành phù hợp như mong muốn thay vì luôn trả về ba ngành hàng đầu.
Ca sử dụng này xoay quanh việc thực hiện tìm kiếm tương tự. Mặc dù có thể hoàn thành bài tập này một cách thủ công, nhưng công cụ Wikipedia2Vec lại xuất hiện trong tâm trí bạn vì các phần nhúng được đào tạo trước đã được tạo cho nhiều ngôn ngữ. Phần nhúng từ—còn được gọi là vectơ—là cách biểu thị bằng số của các từ chứa cả thông tin cú pháp và ngữ nghĩa của chúng. Bằng cách biểu diễn các từ dưới dạng vectơ, chúng ta có thể xác định một cách toán học những từ nào “gần” hơn về mặt ngữ nghĩa với các từ khác.
Trong ví dụ của chúng tôi, chúng tôi cũng có thể viết một chương trình Python đơn giản để tạo vectơ từ cho từng ngành được định cấu hình trong Salesforce.
Phần mở rộng pgvector
yêu cầu cơ sở dữ liệu Postgres. Tuy nhiên, dữ liệu doanh nghiệp trong ví dụ của chúng tôi hiện nằm trong Salesforce. May mắn thay, Heroku Connect cung cấp một cách dễ dàng để đồng bộ hóa các tài khoản Salesforce với Heroku Postgres, lưu trữ nó trong một bảng có tên salesforce.account
. Sau đó, chúng ta sẽ có một bảng khác gọi là salesforce.industries
chứa từng ngành trong Salesforce (dưới dạng khóa VARCHAR), cùng với vectơ từ liên quan của nó.
Với dữ liệu Salesforce và vectơ từ trong Postgres, chúng ta sẽ tạo API RESTful bằng Java và Spring Boot. Dịch vụ này sẽ thực hiện truy vấn cần thiết và trả về kết quả ở định dạng JSON.
Chúng ta có thể minh họa quan điểm cấp cao của giải pháp như thế này:
Mã nguồn sẽ nằm trong GitLab. Việc đưa ra lệnh git push heroku
sẽ kích hoạt quá trình triển khai trong Heroku, giới thiệu API RESTful mà nhóm tiếp thị có thể dễ dàng sử dụng.
Với thiết kế cấp cao đã có, chúng ta có thể bắt đầu xây dựng giải pháp. Bằng cách sử dụng thông tin đăng nhập Salesforce của mình, tôi có thể điều hướng đến màn hình Tài khoản để xem dữ liệu cho bài tập này. Dưới đây là ví dụ về trang đầu tiên của dữ liệu doanh nghiệp:
Với nỗ lực này, tôi đã lên kế hoạch sử dụng Heroku để giải quyết yêu cầu của nhóm tiếp thị. Tôi đã đăng nhập vào tài khoản Heroku của mình và sử dụng nút Tạo ứng dụng mới để thiết lập một ứng dụng mới có tên similarity-search-sfdc
:
Sau khi tạo ứng dụng, tôi điều hướng đến tab Tài nguyên để tìm tiện ích bổ sung Heroku Postgres. Tôi đã nhập “Postgres” vào trường tìm kiếm tiện ích bổ sung.
Sau khi chọn Heroku Postgres từ danh sách, tôi đã chọn gói Tiêu chuẩn 0 , nhưng pgvector có sẵn trên các dịch vụ cơ sở dữ liệu Cấp tiêu chuẩn (hoặc cao hơn) chạy PostgreSQL 15 hoặc cơ sở dữ liệu cấp cơ bản beta .
Khi tôi xác nhận tiện ích bổ sung, Heroku đã tạo và cung cấp chuỗi kết nối DATABASE_URL
. Tôi đã tìm thấy điều này trong phần Cấu hình Vars của tab Cài đặt trong ứng dụng của tôi. Tôi đã sử dụng thông tin này để kết nối với cơ sở dữ liệu của mình và kích hoạt tiện ích mở rộng pgvector như thế này:
CREATE EXTENSION vector;
Tiếp theo, tôi tìm kiếm và tìm thấy tiện ích bổ sung Heroku Connect . Tôi biết điều này sẽ giúp tôi dễ dàng kết nối với dữ liệu doanh nghiệp trong Salesforce.
Đối với bài tập này, gói Phiên bản Demo miễn phí hoạt động tốt.
Tại thời điểm này, tab Tài nguyên cho ứng dụng similarity-search-sfdc
trông như thế này:
Tôi đã làm theo hướng dẫn “ Thiết lập Heroku Connect ” để liên kết tài khoản Salesforce của tôi với Heroku Connect. Sau đó, tôi chọn đối tượng Tài khoản để đồng bộ hóa. Sau khi hoàn tất, tôi có thể xem dữ liệu tài khoản Salesforce tương tự trong Heroku Connect và trong cơ sở dữ liệu Postgres cơ bản.
Từ góc độ SQL, những gì tôi đã làm đã dẫn đến việc tạo ra bảng salesforce.account
với thiết kế sau:
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 );
Để tính năng tìm kiếm tương tự hoạt động như mong đợi, tôi cần tạo vectơ từ cho từng ngành tài khoản Salesforce:
Vì trường hợp sử dụng chính cho thấy nhu cầu tìm kiếm những điểm tương đồng cho ngành công nghiệp phần mềm nên chúng tôi cũng cần tạo một vectơ từ cho ngành đó.
Để đơn giản hóa bài tập này, tôi đã thực hiện tác vụ này theo cách thủ công bằng cách sử dụng Python 3.9 và một tệp có tên embed.py
, trông giống như sau:
from wikipedia2vec import Wikipedia2Vec wiki2vec = Wikipedia2Vec.load('enwiki_20180420_100d.pkl') print(wiki2vec.get_word_vector('software').tolist())
Xin lưu ý – phương thức get_word_vector()
yêu cầu đại diện cho ngành bằng chữ thường.
Chạy python embed.py
đã tạo ra vectơ từ sau cho từ 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]
Để lưu trữ các vectơ từ, chúng tôi cần thêm bảng industries
vào cơ sở dữ liệu Postgres bằng lệnh SQL sau:
create table salesforce.industries ( name varchar not null constraint industries_pk primary key, embeddings vector(100) not null );
Với bảng industries
đã được tạo, chúng tôi sẽ chèn từng vectơ từ được tạo. Chúng tôi thực hiện việc này bằng các câu lệnh SQL tương tự như sau:
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] ');
Xin lưu ý - trong khi chúng tôi tạo một vectơ từ có chữ viết thường của Ngành phần mềm (phần mềm), cột industries.name
cần phải khớp với tên ngành được viết hoa (Phần mềm).
Sau khi tất cả các vectơ từ được tạo đã được thêm vào bảng industries
, chúng ta có thể thay đổi trọng tâm sang giới thiệu API RESTful.
Đây là thời điểm mà niềm đam mê kỹ sư phần mềm của tôi tăng vọt vì tôi đã có sẵn mọi thứ để giải quyết thử thách trong tay.
Tiếp theo, bằng cách sử dụng Spring Boot 3.2.2 và Java (temurin) 17, tôi đã tạo dự án similarity-search-sfdc
trong IntelliJ IDEA với các phần phụ thuộc Maven sau:
<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>
Tôi đã tạo các thực thể đơn giản hóa cho cả đối tượng Tài khoản và đối tượng Công nghiệp (nhúng), xếp hàng với các bảng cơ sở dữ liệu Postgres đã tạo trước đó.
@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; }
Sử dụng giao diện JpaRepository, tôi đã thêm các tiện ích mở rộng sau để cho phép truy cập dễ dàng vào các bảng 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> { }
Lưu ý rằng phương thức findSimilaritiesForIndustry()
là nơi diễn ra tất cả các công việc nặng nhọc để giải quyết trường hợp sử dụng này. Phương thức sẽ chấp nhận các tham số sau:
industry
: ngành để tìm ra điểm tương đồng cholimit
: số lượng điểm tương đồng tối đa trong ngành để tìm kiếm khi truy vấn tài khoản
Lưu ý toán tử khoảng cách Euclide (<->) trong truy vấn của chúng tôi ở trên. Đây là toán tử tích hợp của tiện ích mở rộng để thực hiện tìm kiếm tương tự.
Với trường hợp sử dụng ban đầu của ngành “Phần mềm” và giới hạn được đặt cho ba ngành gần nhất, truy vấn được thực thi sẽ như sau:
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;
Từ đó, tôi đã xây dựng lớp AccountsService
để tương tác với kho JPA:
@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"); } } }
Cuối cùng, tôi đã yêu cầu lớp AccountsController
cung cấp điểm vào RESTful và kết nối với 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); } } }
Với dịch vụ Spring Boot đã sẵn sàng, tôi đã thêm Procfile
sau vào dự án, để Heroku biết thêm về dịch vụ của chúng tôi:
web: java $JAVA_OPTS -Dserver.port=$PORT -jar target/*.jar
Để an toàn, tôi đã thêm tệp system.properties
để chỉ định phiên bản Java và Maven nào được mong đợi:
java.runtime.version=17 maven.version=3.9.5
Bằng cách sử dụng Heroku CLI, tôi đã thêm một điều khiển từ xa vào kho lưu trữ GitLab của mình cho dịch vụ similarity-search-sfdc
với nền tảng Heroku:
heroku git:remote -a similarity-search-sfdc
Tôi cũng đặt loại gói xây dựng cho dịch vụ similarity-search-sfdc
thông qua lệnh sau:
heroku buildpacks:set https://github.com/heroku/heroku-buildpack-java
Cuối cùng, tôi đã triển khai dịch vụ similarity-search-sfdc
cho Heroku bằng lệnh sau:
git push heroku
Bây giờ, tab Tài nguyên cho ứng dụng similarity-search-sfdc
xuất hiện như hiển thị bên dưới:
Khi API RESTful đang chạy, tôi đã ban hành lệnh cURL sau để xác định ba ngành Salesforce hàng đầu (và các tài khoản được liên kết) gần nhất với ngành Phần mềm :
curl --location 'https://HEROKU-APP-ROOT-URL/accounts/similarities?industry=Software&limit=3'
API RESTful trả về trạng thái phản hồi 200 OK HTTP
cùng với tải trọng sau:
[ { "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" } ]
Do đó, các ngành Công nghệ , Truyền thông và Điện tử là những ngành gần nhất với ngành Phần mềm trong ví dụ này.
Giờ đây, bộ phận tiếp thị đã có danh sách các tài khoản mà họ có thể liên hệ cho chiến dịch tiếp theo của mình.
Nhiều năm trước, tôi đã dành nhiều thời gian hơn mức tôi muốn thừa nhận để chơi trò chơi điện tử nhiều người chơi Team Fortress 2 . Đây là ảnh chụp màn hình từ một sự kiện rất thú vị vào năm 2012:
Những người quen thuộc với khía cạnh này trong cuộc sống của tôi có thể cho bạn biết rằng lựa chọn mặc định của tôi về lớp người chơi là người lính. Điều này là do người lính có sự cân bằng tốt nhất về sức khỏe, khả năng di chuyển, tốc độ và hỏa lực.
Tôi có cảm giác như các kỹ sư phần mềm là “tầng lớp lính” của thế giới thực vì chúng ta có thể thích ứng với mọi tình huống và tập trung vào việc cung cấp các giải pháp đáp ứng mong đợi một cách hiệu quả.
Trong vài năm nay, tôi đã tập trung vào tuyên bố sứ mệnh sau đây mà tôi cảm thấy có thể áp dụng cho bất kỳ chuyên gia CNTT nào:
“Hãy tập trung thời gian vào việc cung cấp các tính năng/chức năng giúp nâng cao giá trị tài sản trí tuệ của bạn. Tận dụng các khuôn khổ, sản phẩm và dịch vụ cho mọi thứ khác.”
- J. Vester
Trong ví dụ cho bài đăng này, chúng tôi có thể tận dụng Heroku Connect để đồng bộ hóa dữ liệu doanh nghiệp với cơ sở dữ liệu Postgres. Sau khi cài đặt tiện ích mở rộng pgvector, chúng tôi đã tạo vectơ từ cho từng ngành riêng biệt từ các tài khoản Salesforce đó. Cuối cùng, chúng tôi đã giới thiệu dịch vụ Spring Boot, giúp đơn giản hóa quy trình định vị các tài khoản Salesforce có ngành gần nhất với ngành khác.
Chúng tôi đã giải quyết trường hợp sử dụng này một cách nhanh chóng bằng các công nghệ nguồn mở hiện có, việc bổ sung dịch vụ Spring Boot nhỏ và Heroku PaaS – hoàn toàn tuân thủ tuyên bố sứ mệnh của tôi. Tôi không thể tưởng tượng được sẽ cần bao nhiêu thời gian nếu không có những khuôn khổ, sản phẩm và dịch vụ này.
Nếu quan tâm, bạn có thể tìm mã nguồn gốc của bài viết này trên GitLab:
https://gitlab.com/johnjvester/similarity-search-sfdc
Chúc bạn có một ngày thật tuyệt vời!