Make decisions
The decision-making process is based on the rule engine that relies on two distinct rule files written in the Drools MVEL dialect:
Default rule file is an out-of-the-box set of rules that should not be modified. These rules are designed to handle standard decision-making scenarios and serve as the baseline for the system. The file is located in MinIO S3:
doc-upload/payment_sanctions_screening/{tara_version}/rules/rules_v1.drl.Custom rule file allows the implementation of specific rules tailored to business requirements. These rules are flexible and are used instead of the default ones, providing a way to handle a unique set of requirements. You can copy the default rule file, modify its content as needed, assign any name to your custom file, and add the name to the configuration. The file must be manually created and uploaded to MinIO S3:
doc-upload/payment_sanctions_screening/rules/custom_rules.drl.
Do not change the filenames.
Data model for rules
The following data model is available during rule evaluation.
Message
See sample message in JSON
"message": {
"id": "35166_resolve",
"type": "PAYMENT_TRANSFER",
"subtype": null,
"content": null,
"contentFormat": null,
"payment": null,
"customAttributes": null
}
| Field | Type | Values in DRL |
|---|---|---|
id | String | |
type | Enum |
|
subtype | Enum | 103 |
content | String | Original payment message or null |
contentFormat | Enum |
|
payment | Object | Can be null |
payment.direction | Enum |
|
payment.sender | Object |
|
payment.receiver | Object |
|
customAttributes | Map<String, String> |
Hit
A hit is a complex object that contains:
General hit information
Input information: screened entity data
Derived information received from Named Entity Recognition (NER) and LOOKUP
See sample hit object in JSON
"hit": {
"id": "",
"tagContent": "/SE1350000000054910000011 \nWXYZSESS",
"tagName": "50A",
"tagType": "FREETEXT",
"hitText": "Key",
"hitStartIndex": null,
"hitEndIndex": null,
"paymentInput": {
"type": null,
"subType": null,
"name": null,
"dob": "01/01/2020",
"address": null,
"country": "Sweden",
"city": null,
"state": null,
"zip": null,
"ids": {
"IBAN": "SE1350000000054910000011",
"BIC": "WXYZSESS"
}
},
"derived": {
"id": "WXYZSESS",
"idSource": "NER",
"name": "Joe Doors",
"nameConfirmed": null,
"nameScore": null,
"nameSource": "LOOKUP",
"country": null,
"countryConfirmed": null,
"countrySource": null,
"address": null,
"addressConfirmed": null,
"addressSource": null,
"zip": null,
"zipConfirmed": null,
"zipSource": null,
"type": "ID",
"subType": "BIC",
"typeConfirmed": null,
"typeScore": null,
"typeSource": null
}
}
| Field | Type | Values in DRL |
|---|---|---|
id | String | |
tagContent | String | |
tagName | String | |
tagType | Enum | |
hitText | String | Can be null |
hitStartIndex | Integer | |
hitEndIndex | Integer |
Payment input object
| Field | Type | Values in DRL |
|---|---|---|
type | Enum |
|
subType of ID | Enum |
|
name | String | |
dob | String | |
address | String | |
country | String | |
city | String | |
state | String | |
zip | String | |
codeMap | Map<String, String> | Map of collected IDs |
locations | Object | Additional set of location data |
Derived object
| Field | Type | Values in DRL |
|---|---|---|
id | String | Value detected by NER. |
idSource | Enum |
|
name | String | Value detected by NER. |
nameConfirmed | String | Currently, always false. |
nameScore | Double | NER score for the name detected in free text. |
nameSource | Enum |
|
country | String | |
countryConfirmed | String | Set to true if Google geocoding can confirm the address. |
countrySource | Enum |
|
address | String | Value detected by NER. |
addressConfirmed | String | Currently, always false. |
addressSource | Enum |
|
zip | String | Value detected by NER. |
zipConfirmed | String | Currently, always false. |
zipSource | Enum |
|
type | Enum |
|
subType | String |
|
typeConfirmed | String | Currently, always false. |
typeScore | Double | NER score for the detected type. Currently, always null. |
typeSource | Enum |
|
WLE
A watchlist entity (WLE) refers to the sanctioned entity information.
See sample WLE in JSON
"wle": {
"id": null,
"name": "Sanction Name Investment LLC",
"primaryName": "CONSOLIDATED INVESTMENTS LLC",
"aliases": null,
"address": "6820 S. Harl Ave., Tempe, Ariz., 85283, U.S.",
"locations": null,
"country": "US",
"city": null,
"state": null,
"zip": null,
"type": "ORGANIZATION",
"dob": null,
"listType": null,
"source": null,
"sourceEntryId": "id_1",
"sourceKeywords": null,
"codeMap": null,
"customAttributes": null
},
| Field | Type | Values in DRL |
|---|---|---|
id | String | |
type | EntityType |
|
name | String | Sanctioned name or location |
aliases | String array | AKA names |
address | String | |
country | String | |
city | String | |
state | String | |
zip | String | |
dob | String | |
codeMap | Map<String, String> | |
locations | List<Object> | |
customAttributes | Map<String, String> | |
source | String | |
listType | String | |
sourceEntryId | String | |
sourceKeywords | String |
Thresholds
You can define thresholds during the AI Agent configuration. They are also available in the model:
dateMatchingThreshold: integer value in daysnameMatchingThreshold: double value
Decision factors from ML model
idMatch
See sample idMatch in JSON
"idMatch": {
"match": false,
"input": "WXYZSESS",
"wle": "",
"source": "NER",
"inputProcessed": null,
"wleProcessed": null
}
| Field | Type | Values in DRL |
|---|---|---|
match | Boolean |
|
input | String | |
wle | String | |
inputProcessed | String | |
wleProcessed | String | |
source | SourceType |
|
typeMatch
See sample typeMatch in JSON
"typeMatch": {
"match": false,
"input": "John Doe",
"wle": "Johnson LLC",
"source": "NER",
"inputProcessed": null,
"wleProcessed": null,
"inputType": "INDIVIDUAL",
"inputTypeConfidenceScore":0.9
"wleType": "ORGANIZATION"
}
| Field | Type | Values in DRL |
|---|---|---|
match | Boolean |
|
input | String | |
wle | String | |
inputProcessed | String | |
wleProcessed | String | |
source | SourceType |
|
inputType | EntityType |
|
inputTypeConfidenceScore | Double | A value between 0 and 1 (probability) reflecting how confident the model is about the predicted type of the input entity |
wleType | EntityType |
|
nameMatch
See sample nameMatch in JSON
"nameMatch": {
"score": 0.5593499999999999,
"blackListed": false,
"blackListedWordStr": "",
"blacklistedSource": "NONE",
"nameMatcherSource": "wf-name-matcher",
"matchedWLEEntity": "PRIMARY_NAME",
"inputHasLegalEnding": true,
"wleHasLegalEnding": true,
"legalEndingsAreDifferent": true,
"match": false,
"input": "INVESTMENT HOLDINGS INC",
"inputProcessed": "investment holdings",
"wle": "CONSOLIDATED INVESTMENTS LLC",
"wleProcessed": "consolidated investments",
"source": "NER"
}
| Field | Type | Values in DRL |
|---|---|---|
match | Boolean |
|
input | String | |
wle | String | |
score | Double | |
blacklisted | Boolean |
|
blackListedWordStr | String | Blacklisted keyword value |
blacklistedSource | BlacklistedSource Enum |
|
inputProcessed | String | |
wleProcessed | String | |
source | SourceType |
|
inputHasLegalEnding | Boolean |
|
wleHasLegalEnding | Boolean |
|
nameMatcherSource | String | wf-name-matcher |
matchedWLEEntity | MatchedWLEEntity Enum |
|
countryMatch
See sample countryMatch in JSON
"countryMatch": {
"match": false,
"input": "Roslagsgatan 10 \n113 51 Stockholm, Sweden",
"wle": "123 Adams Street \n United States",
"blacklisted": false,
"source": "LOOKUP",
"inputProcessed": "SE",
"wleProcessed": "US",
},
| Field | Type | Values in DRL |
|---|---|---|
match | Boolean |
|
input | String | |
wle | String | |
blacklisted | Boolean |
|
blacklistedSource | BlacklistedSource Enum |
|
blackListedStr | String | Blacklisted country value |
inputProcessed | String | |
wleProcessed | String | |
source | SourceType Enum |
|
addressMatch
See sample addressMatch in JSON
addressMatch": {
"matcherSource": "AVSERVICE",
"comparisonOutput": {
"house_number_match": true,
"city_match": true,
"state_match": true,
"country_match": true,
"postal_code_match": null,
"road_match": true
},
"match": true,
"input": "6820 S Harl Ave, Tempe, AZ 85283, USA",
"inputProcessed": "6820 South Harl Avenue Tempe Arizona United States ",
"wle": "6820 S Harl Ave, Tempe, AZ 85283, USA",
"wleProcessed": "6820 South Harl Avenue Tempe Arizona United States ",
"source": "SCREENED"
}
| Field | Type | Values in DRL |
|---|---|---|
matcherSource | AddressCompareSource |
|
comparisonOutput.house_number_match | Boolean | Available with AVSERVICE only |
comparisonOutput.city_match | Boolean | Available with AVSERVICE only |
comparisonOutput.state_match | Boolean | Available with AVSERVICE only |
comparisonOutput.country_match | Boolean | Available with AVSERVICE only |
comparisonOutput.postal_code_match | Boolean | Available with AVSERVICE only |
comparisonOutput.road_match | Boolean | Available with AVSERVICE only |
match | Boolean | |
input | String | |
inputProcessed | String | |
wle | String | |
wleProcessed | String | |
source | SourceType |
|
AVSERVICE indicates that the external address matcher service was selected as the address matching source during the configuration. The addressMatch object is populated with values from the comparisonOutput object only when AVSERVICE is used.
Model decision
In the default rule file, if no rules fired, the default decision is displayed:
"modelDecision: {
"suggestedAction": "NO_DECISION",
"comment": ""Cannot resolve potential match on sanctioned name(John Doe)
}
Create rule
Facts and globals
In Drools, globals and facts are two fundamental concepts used to pass external data and context to rules and represent the data that rules act upon:
Facts are the data objects or domain objects that rules use to make decisions. A fact can be any Java object, and it serves as the basis for rule conditions, such as the
whensection of a rule. The following facts are defined:PaymentMessagePaymentMessageHitPaymentInputEntityPaymentDerivedDataPaymentWatchListEntity
Globals are external objects made available to your rules, but, unlike facts, they are not part of the working memory. Globals provide a way to inject external resources, services, or configuration settings into your rules. Globals are typically immutable within a single rule execution. Once set, the value does not change during the rule execution. The following globals are defined:
idMatchtypeMatchnameMatchcountryMatchaddressMatchnameMatchingThresholddateMatchingThresholdmodelDecision
By combining facts and globals, you can build complex and flexible rule-based systems that leverage external resources while making decisions based on the current state of your data.
See the example to illustrate the use of facts and globals in Drools:
import com.example.Order
global com.example.ShippingService shippingService
rule "Apply Shipping Discount"
when
$order: Order(totalAmount > 100)
then
double discount = shippingService.calculateDiscount($order);
$order.applyDiscount(discount);
end
In the example:
Orderis a fact that represents the order with a total amount.shippingServiceis a global that provides access to a shipping service to calculate discounts.The rule applies a discount to the order if its total amount is larger than 100. It uses both the fact and the global to make this decision.
Additional functions
In the rule file, you can use the functions defined in the RulesUtils class:
resolve("comment", rulesDecision)makes theRESOLVEdecision.escalate("comment", rulesDecision)makes theNO_DECISIONdecision.minimumDistance(String date1, String date2)calculates the period between two dates.mapContainsValue(Map<String, String> messageMap, String key, String value)checks if a map key contains a given value.textContainsMapValue(String text, Map<String, String> valueMap)checks if the text contains a map value.textContainsValue(String text, String value)checks if the text contains a value.isValidStr(String value).isValidMap(Map<String, String> valueMap).isWleLocationInText(String text, PaymentWatchListEntity wle)checks if any sanctioned location elements are found in the text; used when a sanctioned type is LOCATION.isWleLocationInText(String text, PaymentWatchListEntity wle, boolean searchName)checks if any sanctioned location elements (name, city, country) are found in the text. Setboolean searchNametofalseto exclude name search, which allows using this function for any entity type or searching the whole content for the matching location.isLocationAnyMatch(String scrCountry, String wleCountry).Countrycan contain multiple pipe-separated values, such asRU|GB.isNameInText(String text, String name, boolean tokensOnly)returnstrueif all name tokens are found in the text.isNamesInText(String text, List<String> names, boolean tokensOnly)returnstrueif any name in the list or all name tokens are found in the text.String prepareConcatValue(String value)returns text in uppercase, without spaces and punctuation marks.String prepareValue(String value)returns text in uppercase, with trimmed spaces and without punctuation marks.hasMultipleTokens(String value)returnstrueif a string has multiple tokens separated by spaces or punctuation marks.hasNTokens(String value, int n)returnstrueif a string has more than N tokens separated by spaces or punctuation marks.int charsDifferenceDirectional(String one, String two)returns a minimum set of characters ofString onethat must be removed from (or added to)String twoto make the strings equal.int charsDifference(String one, String two)returns a minimum set of characters that must be removed from (or added to) the respective strings to make the strings equal.isNameInclusive(String one, String two, boolean bidirectional)checks if one name token is contained within another. The boolean parameter controls if the name inclusion works both ways (true); otherwise (false), it returnstrueif the first name is contained within the second one.isHieroglyphsChars(String text)returnstrueif a string does not contain hieroglyphs.isLatinChars(String text)returnstrueif a string contains only Latin characters.
When
In most cases, the section is required to qualify a hit or watchlist entity for a new rule. You can use any objects mentioned above. See the code sample:
$hit: PaymentMessageHit(conditions via comma)
$wle: PaymentWatchListEntity(conditions via comma)
eval (expression)
Then
The section contains two available options:
Resolve:
resolve("comment", rulesDecision)Escalate:
escalate("comment", rulesDecision)
Salience
The out-of-the-box default rules have salience in the range of 3000 to 1000.
In Drools, the salience attribute is a crucial element in the rule definition that influences the execution order of rules within a rule file. It allows you to specify the priority of a rule relative to the other ones. Rules with higher salience values are executed before rules with lower ones. You can assign an integer value to the salience attribute to set up the rule execution order.
Mind the following key points about the salience attribute in Drools:
Execution order. The
salienceattribute determines the execution order of rules. The higher the value, the more important the rule is, and it gets executed earlier in the rule execution cycle.Default value. If a rule does not specify a
saliencevalue, it is assigned the default value of0. Rules without explicitsaliencevalues are executed in an arbitrary order and can depend on the rule engine's internal decisions.Ascending order. When evaluating rules with the
saliencevalues, Drools arranges them in the ascending order. Rules with the lowestsaliencevalues are executed first, followed by rules with higher values.Negative values. You can assign negative
saliencevalues to rules if you want specific rules to be executed earlier than rules with positivesaliencevalues. For example, a rule withsalience -1is executed before a rule withsalience 0.Use cases. The
salienceattribute is used to control the rule execution order or prioritize rules based on a specific business logic, for example, when specific rules should have precedence over others.Caution. While the
salienceattribute can be a powerful tool for rule prioritization, it should be used judiciously. Overusing or misusing ofsaliencevalues can make rule sets complex and hard to manage. Maintain clarity in your rule definitions and document the reasons for setting particularsaliencevalues.
Thus, the salience attribute is a mechanism used to control the rule execution order based on assigned priorities. By assigning salience values to rules, you ensure that rules critical to your business logic execute in the desired order, allowing you to create a robust and efficient rule-based system.
Add custom rule file
To write rules, you can use the following applications:
IntelliJ IDEA with the Drools plugin
Eclipse with the Drools plugin (Business Rules Tools)
To add a rule, complete the following steps:
Create a custom rule file locally. The file name should be
custom_rules.drl.Edit the rule file. Use any text editor of your choice to open the file.
Create a rule using the following template:
rule "Name of the rule"
activation-group "default"
salience 2003
when
// Evaluation of the rule conditions goes here
then
// Define the actions to resolve or escalate based on the rule
endReplace
"Name of the rule"with a meaningful and descriptive name for your rule.Leave
activation-groupas"default".Set
saliencebased on specific requirements.In the
whensection, define the conditions to be met for the rule to trigger.In the
thensection, specify the actions to be taken when the rule is triggered, including how to resolve or escalate the decision.
Save the changes.
Upload the modified file to MinIO S3:
doc-upload/payment_sanctions_screening/rules/custom_rules.drl.
Example
As an example, let's implement the following business rule: "A hit should be resolved when the sender and the receiver are from the same country. Should be executed before the default rules."
Define the rule structure:
Leave
activation-group "default". This is a required value.Set
salienceto 4000. As the rule should be executed before the default one, you must set the salience value to more than 3000.
rule "Sender and Receiver from the same country"
activation-group "default"
salience 4000
when
then
endDefine the
whensection:- Specify the
$messageobject.
rule "Sender and Receiver from the same country"
activation-group "default"
salience 4000
when
$message: PaymentMessage()
then
end- Add base filtering for the message based on the payment info object.
tipIn Drools MVEL, you can call fields of the objects in
()and also use.to access fields in the nested objects.rule "Sender and Receiver from the same country"
activation-group "default"
salience 4000
when
$message: PaymentMessage(payment != null, payment.sender != null, payment.reciever != null)
then
end- Add filtering for the country existing in the sender and receiver objects. Check for null values to avoid a Null Pointer Exception.
rule "Sender and Receiver from the same country"
activation-group "default"
salience 4000
when
$message: PaymentMessage(payment != null, payment.sender != null, payment.reciever != null,
payment.sender.country != null, payment.reciever.country != null)
then
end- Add a condition to evaluate the hit. You can use
$messageto get the sender and receiver data. Wrap all conditions in theeval()method to produce a Boolean outcome.
rule "Sender and Receiver from the same country"
activation-group "default"
salience 4000
when
$message: PaymentMessage(payment != null, payment.sender != null, payment.reciever != null,
payment.sender.country != null, payment.reciever.country != null)
eval($message.payment.sender.country == $message.payment.reciever.country)
then
end- Specify the
Define the
thensection.rule "Sender and Receiver from the same country"
activation-group "default"
salience 4000
when
$message: PaymentMessage(payment != null, payment.sender != null, payment.reciever != null,
payment.sender.country != null, payment.reciever.country != null)
eval($message.payment.sender.country == $message.payment.reciever.country)
then
resolve("Sender and Receiver countries are the same: " + $message.payment.sender.country, rulesDecision)
end
Migrate custom rules for version compatibility
The rule file structure can change between Tara versions, which might require manual synchronization with custom rules created in earlier versions. Mind the following:
Check import statements and update them if needed.
Update location match objects if applicable. Thus, starting with v3.3.0,
locationMatchwas split intocountryMatchandaddressMatch, and theLocationMatchobject was renamed toCountryMatch.If you set up filtering rules in previous Tara versions, transfer their settings to your custom rule file. As of v3.3.1, the BP messages filter bot step was removed, so any filtering logic must now be included directly in the custom rule file.