Use AI tools with the AiFoundation module
Edit on GitHubThis document describes how to create and use AI tools with the AiFoundation module to extend AI capabilities by providing custom functions that AI models can invoke during conversations.
Overview
AI tool support enables large language models to call custom functions during conversations. Instead of only generating text, AI models can invoke your application’s functionality, retrieve data, perform calculations, or trigger business logic. This creates more powerful and interactive AI-powered features.
The AI model decides when to call tools based on the conversation context, executes them with appropriate arguments, receives the results, and incorporates them into its response. The AiFoundation module handles the complete tool execution flow automatically.
Prerequisites
- AiFoundation module installed and configured. For details, see AiFoundation module Overview.
- Configured AI provider that supports tool calling (OpenAI, Anthropic Claude)
Not all AI providers support tool calling. Ensure your configured provider supports this feature. OpenAI and Anthropic Claude have native support for function calling.
Use cases
AI tools are ideal for scenarios where AI needs to interact with your application:
Data retrieval
Enable AI to fetch data from your system:
use Spryker\Client\AiFoundation\Dependency\Tools\ToolPluginInterface;
use Spryker\Client\AiFoundation\Dependency\Tools\ToolParameter;
// AI can call this tool to get product information
class GetProductInfoTool implements ToolPluginInterface
{
public const NAME = 'get_product_info';
public function getName(): string
{
return static::NAME;
}
public function getDescription(): string
{
return 'Retrieves product information by SKU';
}
public function getParameters(): array
{
return [
new ToolParameter(
name: 'sku',
type: 'string',
description: 'The product SKU to retrieve information for',
isRequired: true
),
];
}
public function execute(...$arguments): mixed
{
$sku = $arguments['sku'] ?? null;
$product = $this->getProductClient()
->getProductBySku($sku);
return json_encode([
'name' => $product->getName(),
'price' => $product->getPrice(),
'availability' => $product->getAvailability(),
]);
}
}
Calculations and processing
Provide specialized calculations:
use Spryker\Client\AiFoundation\Dependency\Tools\ToolPluginInterface;
use Spryker\Client\AiFoundation\Dependency\Tools\ToolParameter;
// AI can call this tool to calculate shipping costs
class CalculateShippingTool implements ToolPluginInterface
{
public const NAME = 'calculate_shipping';
public function __construct(
private ShippingCalculatorInterface $shippingCalculator
) {
}
public function getName(): string
{
return static::NAME;
}
public function getDescription(): string
{
return 'Calculates shipping cost based on weight, destination, and shipping method';
}
public function getParameters(): array
{
return [
new ToolParameter(
name: 'weight',
type: 'number',
description: 'Package weight in kilograms',
isRequired: true
),
new ToolParameter(
name: 'destination',
type: 'string',
description: 'Destination country code (e.g., US, DE, FR)',
isRequired: true
),
new ToolParameter(
name: 'method',
type: 'string',
description: 'Shipping method: standard, express, or overnight',
isRequired: false
),
];
}
public function execute(...$arguments): mixed
{
$weight = $arguments['weight'] ?? 0;
$destination = $arguments['destination'] ?? '';
$method = $arguments['method'] ?? 'standard';
$cost = $this->shippingCalculator
->calculate($weight, $destination, $method);
return (string) $cost;
}
}
Business logic execution
Allow AI to trigger business operations:
use Spryker\Client\AiFoundation\Dependency\Tools\ToolPluginInterface;
use Spryker\Client\AiFoundation\Dependency\Tools\ToolParameter;
// AI can call this tool to create support tickets
class CreateSupportTicketTool implements ToolPluginInterface
{
public const NAME = 'create_support_ticket';
public function __construct(
private SupportClientInterface $supportClient
) {
}
public function getName(): string
{
return static::NAME;
}
public function getDescription(): string
{
return 'Creates a support ticket with title, description, and priority';
}
public function getParameters(): array
{
return [
new ToolParameter(
name: 'title',
type: 'string',
description: 'Brief title summarizing the issue',
isRequired: true
),
new ToolParameter(
name: 'description',
type: 'string',
description: 'Detailed description of the issue or request',
isRequired: true
),
new ToolParameter(
name: 'priority',
type: 'string',
description: 'Priority level: high, medium, or low',
isRequired: false
),
];
}
public function execute(...$arguments): mixed
{
$title = $arguments['title'] ?? '';
$description = $arguments['description'] ?? '';
$priority = $arguments['priority'] ?? 'medium';
$ticketId = $this->supportClient
->createTicket($title, $description, $priority);
return sprintf('Ticket created with ID: %s', $ticketId);
}
}
Create tool sets
Tool sets group related tools together. Create a tool set class that implements ToolSetPluginInterface:
<?php
namespace Pyz\Client\YourModule\Plugin\AiFoundation;
use Spryker\Client\AiFoundation\Dependency\Tools\ToolSetPluginInterface;
class CustomerServiceToolSet implements ToolSetPluginInterface
{
public const NAME = 'customer_service_tools';
public function getName(): string
{
return static::NAME;
}
public function getTools(): array
{
return [
new GetOrderStatusTool(),
new CreateRefundTool(),
new UpdateCustomerAddressTool(),
new SendNotificationTool(),
];
}
}
Organizing tools into sets
Group tools by functional domain:
// Product-related tools
class ProductToolSet implements ToolSetPluginInterface
{
public const NAME = 'product_tools';
public function getName(): string
{
return static::NAME;
}
public function getTools(): array
{
return [
new SearchProductsTool(),
new GetProductDetailsTool(),
new CheckStockTool(),
];
}
}
// Order-related tools
class OrderToolSet implements ToolSetPluginInterface
{
public const NAME = 'order_tools';
public function getName(): string
{
return static::NAME;
}
public function getTools(): array
{
return [
new CreateOrderTool(),
new GetOrderHistoryTool(),
new CancelOrderTool(),
];
}
}
Register tool sets
Register tool sets in your AiFoundationDependencyProvider:
<?php
namespace Pyz\Client\AiFoundation;
use Pyz\Client\YourModule\Plugin\AiFoundation\CustomerServiceToolSet;
use Pyz\Client\YourModule\Plugin\AiFoundation\ProductToolSet;
use Pyz\Client\YourModule\Plugin\AiFoundation\OrderToolSet;
use Spryker\Client\AiFoundation\AiFoundationDependencyProvider as SprykerAiFoundationDependencyProvider;
class AiFoundationDependencyProvider extends SprykerAiFoundationDependencyProvider
{
/**
* @return array<\Spryker\Client\AiFoundation\Dependency\Tools\ToolSetPluginInterface>
*/
protected function getAiToolSetPlugins(): array
{
return [
new CustomerServiceToolSet(),
new ProductToolSet(),
new OrderToolSet(),
];
}
}
Use tools in AI requests
Specify which tool sets to make available to the AI by adding tool set names to your prompt request:
<?php
namespace Pyz\Zed\YourModule\Business\Assistant;
use Generated\Shared\Transfer\PromptMessageTransfer;
use Generated\Shared\Transfer\PromptRequestTransfer;
use Pyz\Client\YourModule\Plugin\AiFoundation\CustomerServiceToolSet;
use Pyz\Client\YourModule\Plugin\AiFoundation\OrderToolSet;
use Spryker\Client\AiFoundation\AiFoundationClientInterface;
class CustomerAssistant
{
public function __construct(
protected AiFoundationClientInterface $aiFoundationClient
) {
}
public function handleCustomerInquiry(string $customerMessage): string
{
$promptRequest = (new PromptRequestTransfer())
->setAiConfigurationName('openai')
->setPromptMessage(
(new PromptMessageTransfer())->setContent($customerMessage)
)
->addToolSetName(CustomerServiceToolSet::NAME)
->addToolSetName(OrderToolSet::NAME)
->setMaxRetries(2);
$promptResponse = $this->aiFoundationClient->prompt($promptRequest);
if ($promptResponse->getIsSuccessful() !== true) {
return 'I apologize, but I encountered an error processing your request.';
}
return $promptResponse->getMessage()->getContent();
}
}
The AI model has access to all tools from all specified tool sets.
Handle tool call results
Tool calls are automatically executed and their results are included in the response:
$promptResponse = $this->aiFoundationClient->prompt($promptRequest);
if ($promptResponse->getIsSuccessful() === true) {
// Get the final AI response
$finalMessage = $promptResponse->getMessage()->getContent();
// Access tool invocation information
foreach ($promptResponse->getToolInvocations() as $toolInvocation) {
$toolName = $toolInvocation->getName();
$toolArguments = $toolInvocation->getArguments();
$toolResult = $toolInvocation->getResult();
// Log or process tool invocations
$this->logger->info(sprintf(
'AI invoked tool "%s" with arguments: %s. Result: %s',
$toolName,
json_encode($toolArguments),
$toolResult
));
}
}
Tool call flow
The complete flow when tools are used:
- AI receives the prompt and available tools
- AI decides to call one or more tools
- AiFoundation automatically executes the tools with provided arguments
- Tool results are sent back to the AI
- AI incorporates results and may call additional tools
- Process continues until AI provides final response
- Final response and all tool invocations are returned in
ToolInvocationTransferobjects
Best practices
Provide clear tool descriptions
Write descriptions that help the AI understand when and how to use tools:
// Good: Clear, specific description
public function getDescription(): string
{
return 'Searches for products by name, category, or SKU. Returns product name, price, and availability status.';
}
// Avoid: Vague description
public function getDescription(): string
{
return 'Product search';
}
Design focused tools
Create tools with single, well-defined purposes:
// Good: Focused tool
class GetOrderStatusTool implements ToolPluginInterface
{
public const NAME = 'get_order_status';
public function getName(): string
{
return static::NAME;
}
}
// Avoid: Tool that does too much
class ManageOrdersTool implements ToolPluginInterface
{
public function execute(...$arguments): mixed
{
$action = $arguments['action']; // create, update, cancel, status...
// Too many responsibilities
}
}
Return structured data
Return JSON-encoded data for complex results:
public function execute(...$arguments): mixed
{
$order = $this->getOrderDetails($arguments['order_id']);
return json_encode([
'order_id' => $order->getOrderId(),
'status' => $order->getStatus(),
'total' => $order->getTotal(),
'items' => $order->getItems(),
'created_at' => $order->getCreatedAt(),
]);
}
Validate tool arguments
Always validate arguments before execution:
public function execute(...$arguments): mixed
{
$orderId = $arguments['order_id'] ?? null;
if ($orderId === null || !is_string($orderId)) {
return json_encode([
'error' => 'Invalid order_id parameter',
]);
}
// Proceed with execution
}
Handle errors gracefully
Return error information in a consistent format:
public function execute(...$arguments): mixed
{
try {
$result = $this->performOperation($arguments);
return json_encode([
'success' => true,
'data' => $result,
]);
} catch (\Exception $e) {
return json_encode([
'success' => false,
'error' => $e->getMessage(),
'error_code' => $e->getCode(),
]);
}
}
Security considerations
- Validate permissions. Always check user permissions before executing sensitive operations;
- Sanitize inputs. Sanitize all inputs before using them in queries or operations;
Limitations
Provider-specific constraints
Different AI providers have varying limitations:
- Maximum number of tools per request
- Maximum tool description length
- Supported parameter types
- Tool execution timeout limits
Performance considerations
Tool execution adds latency to AI requests:
- Each tool call requires additional round-trip to the AI provider
- Complex tools may take time to execute
- Multiple sequential tool calls increase total response time
Consider these factors when designing time-sensitive features.
Token usage
Tool definitions and results consume tokens from your AI provider quota. Monitor token consumption when using tools extensively.
Related documentation
Thank you!
For submitting the form