Business Logic Samples
Advance-Agent
Efficient management and adjustment of advance payments
Tip
The Advance-Agent specializes in the automated processing of customer requests for adjustment of advance payments. It analyzes, validates, and processes modification requests for monthly payments and ensures a seamless implementation in the billing system. Intelligent verification mechanisms enable changes in advance payments within sensible parameters while at the same time ensuring payment security.
Core functionalities
The Advance-Agent handles the entire process of adjusting advance payments and offers the following key services:
- Needs analysis: Evaluation of the desired change in the context of actual consumption
- Plausibility check: Automatic validation of whether the requested change is within sensible limits
- Implementation: Direct adjustment of the advance payment amount in all relevant systems
- Communication: Automated confirmation of the change with indication of the effective date
Process flow
The agent follows a structured procedure that ensures both customer satisfaction and financial stability:
- Recognition of the issue and extraction of relevant information (desired advance payment amount, time)
- Comparison with consumption data to verify the appropriateness of the requested change
- Evaluation of the change request based on predefined rules and limits
- Execution of the adjustment in case of positive evaluation or suggestion of alternative amounts
- Transparent communication with the customer about the result and next steps
By combining data analysis and clear decision rules, the Advance-Agent facilitates rapid, consistent, and customer-centric processing of change requests while simultaneously relieving the customer service team.
<?php
use EnneoSDK\Api;
use EnneoSDK\IntentInfo;
use EnneoSDK\IntentOption;
use EnneoSDK\Interaction;
require(getenv()['SDK'] ?? 'sdk.php');
/** @var stdClass $in */
######### Expected Input:
// contractId -> ID of the contract (required)
// requestedDeposit -> Amount of the deposit (required)
// channel -> ticket channel. Voice and chat can be handled synchronously (required)
######### Possible Actions:
// For better readability, actions can be prefixed with "base_", if they are handled in main agent business logic
// Increase requested deposit to minimum allowed
const BASE_INCREASE_DEPOSIT = 'base_increase_deposit';
// Decrease requested deposit to maximum allowed
const BASE_DECREASE_DEPOSIT = 'base_decrease_deposit';
// Deposit is not valid. Inform customer.
const DEPOSIT_INVALID = 'deposit_invalid';
// Save valid deposit
const SAVE_DEPOSIT = 'save_deposit';
$interaction = new Interaction($in);
$deposit = getCurrentDeposit($in->contractId);
######### ACTION: Deposit already validated, "save deposit" action selected
if ($in->_action === SAVE_DEPOSIT) {
$params = [
'contractId' => $in->contractId,
'depositValue' => $in->requestedDeposit,
];
try {
saveDeposit($in->contractId, $in->requestedDeposit);
stopProcessing($interaction);
} catch (Throwable $t) {
$interaction->infos[] = new IntentInfo(
type: 'danger',
message: sprintf('API-Aufruf fehlgeschlagen: %s', $t->getMessage())
);
// add the "try again" option using an empty action type.
$interaction->options[] = new IntentOption(
type: '',
name: 'Erneut versuchen',
recommended: true
);
}
}
######### STEP: Fetch current deposit value
$currentDepositValue = getCurrentDeposit($in->contractId);
$minAllowed = round($currentDepositValue * 0.95, 2);
$maxAllowed = round($currentDepositValue * 2.5, 2);
$interaction->infos[] = new IntentInfo(
type: 'neutral',
message: sprintf('Aktueller Abschlag: %s. Min.: %s Max.: %s', $currentDepositValue, $minAllowed, $maxAllowed)
);
######### ACTION: Set Deposit to the minimum
if ($in->_action === BASE_INCREASE_DEPOSIT) {
$interaction->infos[] = new IntentInfo(
type: 'neutral',
message: sprintf('Abschlag auf %s hochgesetzt', $currentDepositValue)
);
$in->requestedDeposit = $minAllowed;
}
######### ACTION: Set Deposit to the maximum
if ($in->_action === BASE_DECREASE_DEPOSIT) {
$interaction->infos[] = new IntentInfo(
type: 'neutral',
message: sprintf('Abschlag auf %s reduziert', $currentDepositValue)
);
$in->requestedDeposit = $maxAllowed;
}
######### STEP: Validate deposit
// this is a sample validation with a tolerance while decreasing and increasing the allowed deposit values
// you can adjust this to your needs
$isDepositValid = true;
if ($in->requestedDeposit < $minAllowed) {
$interaction->infos[] = new IntentInfo(
type: 'warning',
message: sprintf('Gewünschter Abschlag zu niedrig. Mindestabschlag: %s', $minAllowed)
);
$interaction->options[] = new IntentOption(
type: BASE_INCREASE_DEPOSIT,
name: 'Auf Mindestabschlag erhöhen',
recommended: isSyncChannel($in)
);
$isDepositValid = false;
}
if ($in->requestedDeposit > $maxAllowed) {
$interaction->infos[] = new IntentInfo(
type: 'warning',
message: sprintf('Abschlag zu hoch. Max: %s €', $maxAllowed)
);
$interaction->options[] = new IntentOption(
type: BASE_DECREASE_DEPOSIT,
name: 'Auf Maximalabschlag reduzieren',
recommended: isSyncChannel($in)
);
$isDepositValid = false;
}
// If asking the customer for an adjusted deposit amount is not possible (email / letter etc),
// recommended action would be to stop processing and let the customer know that the deposit can't be adjusted
if (!$isDepositValid && !isSyncChannel($in)) {
$interaction->options[] = new IntentOption(
type: DEPOSIT_INVALID,
name: 'Kunden informieren',
recommended: true
);
}
if (!$isDepositValid) {
stopProcessing($interaction);
}
######### STEP: Add Action to save deposit in the system
$interaction->infos[] = new IntentInfo(
type: 'success',
message: 'Angefragter Abschlag nicht valid'
);
$interaction->options[] = new IntentOption(
type: SAVE_DEPOSIT,
name: 'Abschlag speichern',
recommended: true
);
stopProcessing($interaction);
######### HELPER FUNCTIONS #########
/**
* Function simulating a request to an external API and returning the current deposit value
*/
function getCurrentDeposit(int $contractId): float
{
$response = Api::call(
method: 'POST',
url: 'https://echo.enneo.ai',
params: ['value' => 123.45]
);
return $response->vlaue ?? 123.45;
}
function saveDeposit(int $contractId, float $requestedDeposit): void
{
$response = Api::call(
method: 'POST',
url: 'https://echo.enneo.ai',
params: [
'contractId' => $contractId,
'depositValue' => $requestedDeposit,
]
);
}
function isSyncChannel(stdClass $in): bool
{
return in_array($in->channel, ['chat', 'phone']);
}
/**
* Business logic execution should always return an Interaction object in JSON format
*/
function stopProcessing(Interaction $interaction): void
{
echo json_encode($interaction);
exit();
}import importlib.util
import os
import json
import requests
file_path = os.getenv('SDK', 'sdk.py')
spec = importlib.util.spec_from_file_location('sdk', file_path)
sdk = importlib.util.module_from_spec(spec)
spec.loader.exec_module(sdk)
######### Expected Input:
# contractId -> ID of the contract (required)
# requestedDeposit -> Amount of the deposit (required)
# channel -> ticket channel. Voice and chat can be handled synchronously (required)
######### Possible Actions:
# For better readability, actions can be prefixed with "base_", if they are handled in main agent business logic
# Increase requested deposit to minimum allowed
BASE_INCREASE_DEPOSIT = 'base_increase_deposit'
# Decrease requested deposit to maximum allowed
BASE_DECREASE_DEPOSIT = 'base_decrease_deposit'
# Deposit is not valid. Inform customer.
DEPOSIT_INVALID = 'deposit_invalid'
# Save valid deposit
SAVE_DEPOSIT = 'save_deposit'
######### HELPER FUNCTIONS #########
def get_current_deposit(contract_id):
"""
Function simulating a request to an external API and returning the current deposit value
"""
try:
# Make the API call directly using requests to handle JSON decode errors properly
headers = {'Content-Type': 'application/json', 'User-Agent': 'enneo/1.0.0'}
response = requests.post(
'https://echo.enneo.ai',
headers=headers,
json={'value': 123.45},
timeout=60
)
# Handle the response similar to PHP version
if response.status_code < 200 or response.status_code >= 400:
raise Exception(f"Api call to https://echo.enneo.ai failed with code {response.status_code} and response: {response.text}")
# Check if the response is valid JSON
if response.text and (response.text.strip().startswith('{') or response.text.strip().startswith('[')):
try:
result = response.json()
return result.get('vlaue', 123.45)
except json.JSONDecodeError:
# If JSON decode fails, return default value
return 123.45
else:
# Special case when a REST API does not respond with a json-encoded response
return 123.45
except Exception as e:
# Handle any other exceptions by returning default value
return 123.45
def save_deposit(contract_id, requested_deposit):
"""
Function to save the deposit via API call
"""
try:
# Make the API call directly using requests to handle JSON decode errors properly
headers = {'Content-Type': 'application/json', 'User-Agent': 'enneo/1.0.0'}
response = requests.post(
'https://echo.enneo.ai',
headers=headers,
json={
'contractId': contract_id,
'depositValue': requested_deposit,
},
timeout=60
)
# Handle the response similar to PHP version
if response.status_code < 200 or response.status_code >= 400:
raise Exception(f"Api call to https://echo.enneo.ai failed with code {response.status_code} and response: {response.text}")
# Check if the response is valid JSON
if response.text and (response.text.strip().startswith('{') or response.text.strip().startswith('[')):
try:
return response.json()
except json.JSONDecodeError:
# If JSON decode fails, return raw response
return {"response": response.text, "code": response.status_code}
else:
# Special case when a REST API does not respond with a json-encoded response
return {"response": response.text, "code": response.status_code}
except Exception as e:
# Handle any other exceptions
return {"response": str(e), "code": 500, "error": "API call failed"}
def is_sync_channel(input_data):
"""
Check if the channel is synchronous (chat or phone)
"""
return input_data['channel'] in ['chat', 'phone']
def stop_processing(interaction):
"""
Business logic execution should always return an Interaction object in JSON format
"""
print(json.dumps(interaction.model_dump()))
exit()
input_data = sdk.load_input_data()
interaction = sdk.Interaction(data=input_data)
deposit = get_current_deposit(input_data['contractId'])
######### ACTION: Deposit already validated, "save deposit" action selected
if input_data.get('_action') == SAVE_DEPOSIT:
params = {
'contractId': input_data['contractId'],
'depositValue': input_data['requestedDeposit'],
}
try:
save_deposit(input_data['contractId'], input_data['requestedDeposit'])
stop_processing(interaction)
except Exception as e:
interaction.infos.append(
sdk.IntentInfo(
type='danger',
message=f'API-Aufruf fehlgeschlagen: {str(e)}'
)
)
# add the "try again" option using an empty action type.
interaction.options.append(
sdk.IntentOption(
type='',
name='Erneut versuchen',
recommended=True
)
)
######### STEP: Fetch current deposit value
current_deposit_value = get_current_deposit(input_data['contractId'])
min_allowed = round(current_deposit_value * 0.95, 2)
max_allowed = round(current_deposit_value * 2.5, 2)
interaction.infos.append(
sdk.IntentInfo(
type='neutral',
message=f'Aktueller Abschlag: {current_deposit_value}. Min.: {min_allowed} Max.: {max_allowed}'
)
)
######### ACTION: Set Deposit to the minimum
if input_data.get('_action') == BASE_INCREASE_DEPOSIT:
interaction.infos.append(
sdk.IntentInfo(
type='neutral',
message=f'Abschlag auf {current_deposit_value} hochgesetzt'
)
)
input_data['requestedDeposit'] = min_allowed
######### ACTION: Set Deposit to the maximum
if input_data.get('_action') == BASE_DECREASE_DEPOSIT:
interaction.infos.append(
sdk.IntentInfo(
type='neutral',
message=f'Abschlag auf {current_deposit_value} reduziert'
)
)
input_data['requestedDeposit'] = max_allowed
######### STEP: Validate deposit
# this is a sample validation with a tolerance while decreasing and increasing the allowed deposit values
# you can adjust this to your needs
is_deposit_valid = True
if input_data['requestedDeposit'] < min_allowed:
interaction.infos.append(
sdk.IntentInfo(
type='warning',
message=f'Gewünschter Abschlag zu niedrig. Mindestabschlag: {min_allowed}'
)
)
interaction.options.append(
sdk.IntentOption(
type=BASE_INCREASE_DEPOSIT,
name='Auf Mindestabschlag erhöhen',
recommended=is_sync_channel(input_data)
)
)
is_deposit_valid = False
if input_data['requestedDeposit'] > max_allowed:
interaction.infos.append(
sdk.IntentInfo(
type='warning',
message=f'Abschlag zu hoch. Max: {max_allowed} €'
)
)
interaction.options.append(
sdk.IntentOption(
type=BASE_DECREASE_DEPOSIT,
name='Auf Maximalabschlag reduzieren',
recommended=is_sync_channel(input_data)
)
)
is_deposit_valid = False
# If asking the customer for an adjusted deposit amount is not possible (email / letter etc),
# recommended action would be to stop processing and let the customer know that the deposit can't be adjusted
if not is_deposit_valid and not is_sync_channel(input_data):
interaction.options.append(
sdk.IntentOption(
type=DEPOSIT_INVALID,
name='Kunden informieren',
recommended=True
)
)
if not is_deposit_valid:
stop_processing(interaction)
######### STEP: Add Action to save deposit in the system
interaction.infos.append(
sdk.IntentInfo(
type='success',
message='Angefragter Abschlag nicht valid'
)
)
interaction.options.append(
sdk.IntentOption(
type=SAVE_DEPOSIT,
name='Abschlag speichern',
recommended=True
)
)
stop_processing(interaction)