サポート > Wagby Developer Network(R8) > 高度な使い方 > Progress Corticon を呼び出す

推論型AIエンジンである Progress Corticon に登録したルールを Wagby から呼び出すことができます。Wagby Developer Day 2017 で発表しました。

Progress Corticon は、株式会社アシストが提供するルールエンジン (BRMS; Business Rule Management System) です。 詳細は"Progress Corticon"のページをお読みください。

Progress Corticon を呼び出すための操作画面を Wagby で生成し、(Progress Corticon の) ルールを呼び出すことができます。

図1 Wagby x Corticon 説明イメージ(Wagby Developer Day 2017 キーノートセッション資料の抜粋)

ここではアシスト様が提供するサンプル「Super Sento」を題材に、このルールを Wagby から呼び出す方法を説明します。「Super Sento」は、性別や年齢、クーポンの有無といった条件から料金を算出するというものです。

今回は REST API を利用した Wagby との連携を行います。

図2 Super Sento を呼び出す画面

1. REST クライアント部の開発

JSON Wrapper クラス

「Super Sento」で用意されている(データ格納用の)Java クラスに対応した JSON Wrapper クラスを定義します。例として reception というクラス定義を示します。(このクラスに対応した Wagby のモデルを後半で用意します。)

package corticon.vocabulary;

import java.util.Date;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown=true)
public class reception {
    @JsonProperty("totalFee")
    private Double totalFee;
    @JsonProperty("childUser")
    private user[] childUser;
    @JsonProperty("__metadata")
    private __metadata metadata;

    public Double getTotalFee() {
        return totalFee;
    }
    public void setTotalFee(Double totalFee) {
        this.totalFee = totalFee;
    }
    public user[] getChildUser() {
        return childUser;
    }
    public void setChildUser(user[] _user) {
        this.childUser = _user;
    }
    public __metadata getMetadata() {
        return metadata;
    }
    public void setMetadata(__metadata _metadata) {
        this.metadata = _metadata;
    }
}

ここで利用しているのはオープンソースのJacksonというライブラリです。これはJava用のJSONパーサーライブラリであり、JavaオブジェクトとJSONの相互変換機能を提供します。

「Super Sento」のように Corticon 側で利用するための Java クラスを REST API として利用できるようにするため、JSON として扱えるようにします。このため、上のような Wrapper クラスを用意していきます。

Adapterクラス

REST API のクライアントとして動作する Adapter クラスを作成します。HTTP 通信を行うため、ここではオープンソースの HttpClient を使います。通信処理のコア部分は次のようになります。

public class SuperSentoAdapter {
...
    public Optional<String> sendReception() {
    ...
        String reqJsonStr = null;
        // POST request:
        HttpPost post = new HttpPost(this.serverUrl_);//接続先URLが格納済み
        post.setHeader("dsName", this.decisionServiceName_);//接続先サービス名が格納済み
        StringEntity content = new StringEntity(reqJsonStr, "utf-8");
        content.setContentType("application/json; charset=UTF-8"); 
        content.setContentEncoding("utf-8");
        post.setEntity(content);
        // SEND:
        HttpClient client = HttpClientBuilder.create().build();
        HttpResponse resp;
        String resJsonStr = null;
        try {
            resp = client.execute(post);
            // Response:
            int status = resp.getStatusLine().getStatusCode();
            if (status == 200) {
                resJsonStr = EntityUtils.toString(resp.getEntity(), "utf-8");
            } else {
                //throw new IOException("Failed to get the response [HTTP Status: "+status+"]");
                return Optional.empty();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Optional.of(resJsonStr);
    }
...
}

単体テスト

この Adapter クラスは単体で動作するようにします。ユニットテストとして、次のようなコードを用意することができます。

package corticon.client;

import static org.junit.Assert.*;
import java.util.Optional;
import org.junit.Test;

import corticon.client.SuperSentoAdapter.Gender;

/**
 * Unit test to check if SuperSentoAdatpter works fine.
 */
public class SuperSentoAdapterTest {
    // Corticonが稼働しているサーバ
    private static String SERVER_URL = "http://localhost:8080/axis/corticon/execute";
    // Corticonが提供するサービス
    private static String SERVICE_NAME = "superSento";

    /**
     * Test to send a request and obtain user information.
     */
    @Test
    public void testSendRequest() {
        SuperSentoAdapter adapter = new SuperSentoAdapter();
        adapter.setServerUrl(SERVER_URL);
        adapter.setServiceName(SERVICE_NAME);
        
        // add customer information:
        adapter.addPersonInfo("1", 85, Gender.Male.toString(), false);
        adapter.addPersonInfo("2", 34, Gender.Male.toString(), false);
        adapter.addPersonInfo("3", 32, Gender.Female.toString(), true);
        
        // Send request and get result JSON:
        Optional<String> opt = adapter.sendReception();
        // Parse:
        opt.ifPresent(resultJson -> {
            // output JSON:
            System.out.println(resultJson);    
            
            // Parse JSON (option):
            ReceptionResult result = adapter.parseResultJson(resultJson);
            System.out.println("Total Fee: " + result.getTotalFee());
            result.getMessages().forEach(msg -> {
                System.out.println(" msg: " + msg);
            });
            System.out.println("Unit price: ");

            result.getAllUnitPrices().values().forEach(unitPrice -> {
                assertNotNull(unitPrice);
                //System.out.println(" - " + unitPrice.toString());
            });

            UnitPrice up = result.getUnitPriceById("1");
            System.out.println(" - " + up.toString());
            up = result.getUnitPriceById("2");
            System.out.println(" - " + up.toString());
            up = result.getUnitPriceById("3");
            System.out.println(" - " + up.toString());
        });
	}
}

PersonクラスやUnitPriceクラスの定義は省略していますが、いずれも JSON Wrapper として用意しています。

2. Wagbyで動作するスクリプトを用意する

モデル定義

「Super Sento」サンプルの入力部となるモデル reception を定義します。詳細は割愛します。

サーバサイドで動作するスクリプト

上で用意した SupperSentoAdapter を通信部として利用します。Wagby が保持する入力値を Corticon に渡し、受け取った結果を Wagby に戻します。タイミングは「自動計算」です。

// Define super sento:
var SuperSentoAdapter = Java.type("corticon.client.SuperSentoAdapter");
var ReceptionResult = Java.type("corticon.client.ReceptionResult");
var UnitPrice= Java.type("corticon.client.UnitPrice");

// Adapter Instance:
var adapter = new SuperSentoAdapter();
adapter.setServerUrl("http://localhost:8080/axis/corticon/execute");
adapter.setServiceName("superSento");

// Set user info:
var persons = reception.person;
for (var i = 0 ; i < persons.length ; i++) {
    var pid = ""+persons[i].pid;
    if (pid === null || pid === "") {
        pid = "p"+i;
    }
    if (persons[i].age === null || persons[i].age === ""
        || persons[i].gender === null || persons[i].gender === ""
        || persons[i].coupon === null || persons[i].coupon === ""
        ) {
        continue;
    }
    var age = parseInt(persons[i].age);
    var gender = persons[i].gender;
    var hasCoupon = (persons[i].coupon == "true");
    adapter.addPersonInfo(pid, age, gender, hasCoupon);
}   

if (adapter.canSendReception() === false) {
    stdout.println("cannot send request to Corticon yet.")
    return;
}
// Send Request:
var jsonRes = adapter.sendReceptionSimple();
// Parse resut:
var result = adapter.parseResultJson(jsonRes);

// Get total price:
reception.totalFee = result.getTotalFee();

// msgs:
reception.notes = "";
for (var i = 0 ; i < result.getMessages().length ; i++) {
    stdout.println(result.getMessages()[i]);
    reception.notes += result.getMessages()[i];
}

// unit price:
for (var i = 0; i < persons.length ; i++) {
var pid = ""+persons[i].pid;
    if (pid === null || pid === "") {
        pid = "p"+i;
    }
    var unitPrice = result.getUnitPriceById(pid);
    if (unitPrice === null) {
        continue;
    }
    persons[i].unitPrice = unitPrice.getBathingFee();
    persons[i].discountRate = unitPrice.getDiscountRate();
    var discountRate = unitPrice.getDiscountRate();
}

adapter の戻り値 result の結果を、Wagbyのモデルである reception にセットしています。persons は reception の繰り返しコンテナとして用意されています。

ここで説明した内容を試すためには、Progress Corticon および株式会社アシスト様が提供するサンプル「Super Sento」の EDS ファイル (superSento_v1_0.eds) が必要です。

詳細は株式会社アシストへお問い合わせください。

Wagby Developer Day 2018