AI rule node: wording and default values adjustments

This commit is contained in:
Dmytro Skarzhynets 2025-07-14 16:23:13 +03:00
parent 4ce38e9ea2
commit 46bdbbdb05
No known key found for this signature in database
GPG Key ID: 2B51652F224037DF
4 changed files with 86 additions and 7 deletions

View File

@ -68,6 +68,7 @@ import static org.thingsboard.server.dao.service.ConstraintValidator.validateFie
""", """,
configClazz = TbAiNodeConfiguration.class, configClazz = TbAiNodeConfiguration.class,
configDirective = "tbExternalNodeAiConfig", configDirective = "tbExternalNodeAiConfig",
iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDkiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCA0OSA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zOC42MzExIDE3LjA3OTVDNDAuMTcwNSAxNy4wNzk2IDQxLjY1MTggMTcuNjg3MiA0Mi43NDc4IDE4Ljc3NjNDNDMuODQ0OCAxOS44NjYzIDQ0LjQ2NTkgMjEuMzUwMSA0NC40NjU5IDIyLjkwMjlWMzUuNDY1MkM0NC40NjU5IDM2LjM1MDkgNDQuMzU2NyAzNy4wNzY5IDQ0LjA5NzMgMzcuNzUxN0M0My44NDE0IDM4LjQxNjcgNDMuNDY1MSAzOC45NjE0IDQzLjA0NDggMzkuNTAyOEM0Mi40NjY3IDQwLjI0NzIgNDEuNjU2MyA0MC42ODU5IDQwLjg5MTkgNDAuOTM4OEM0MC4xMjExIDQxLjE5MzcgMzkuMzE0MyA0MS4yODg1IDM4LjYzMTEgNDEuMjg4NUgzMS4wMjU5TDIzLjM4MTIgNDUuODQ2NEMyMy4wNDMxIDQ2LjA0NzggMjIuNjI0MSA0Ni4wNTA3IDIyLjI4MzkgNDUuODUyOUMyMS45NDM3IDQ1LjY1NDcgMjEuNzMzOCA0NS4yODU5IDIxLjczMzcgNDQuODg3MlY0MS4yODg1SDE5LjY2NjNDMTguMTI2OSA0MS4yODg0IDE2LjY0NTUgNDAuNjgwOSAxNS41NDk2IDM5LjU5MThDMTQuNDUyNyAzOC41MDE5IDEzLjgzMTUgMzcuMDE3OSAxMy44MzE1IDM1LjQ2NTJWMjIuOTAyOUMxMy44MzE1IDIyLjMyMDIgMTMuOTE4NSAyMS43NDY4IDE0LjA4NTggMjEuMjAwN0wxNi4yODg5IDIxLjgxMDFMMTcuMjA5OSAyNS4yNTAyQzE3Ljk0MTYgMjcuOTg0NSAyMS43NTYyIDI3Ljk4NDQgMjIuNDg4IDI1LjI1MDJMMjMuNDA3OSAyMS44MTAxTDI2Ljc5MTcgMjAuODc0OUMyOC41NzkxIDIwLjM4MDUgMjkuMTc3IDE4LjUwMjYgMjguNTg4OCAxNy4wNzk1SDM4LjYzMTFaTTIyLjU4NDIgMzEuNTM5NUMyMS45OCAzMS41Mzk3IDIxLjQ5MDEgMzIuMDM3NiAyMS40OTAxIDMyLjY1MTlDMjEuNDkwMiAzMy4yNjYgMjEuOTgwMSAzMy43NjQgMjIuNTg0MiAzMy43NjQySDM0LjYxOTFDMzUuMjIzMyAzMy43NjQyIDM1LjcxMzEgMzMuMjY2MSAzNS43MTMyIDMyLjY1MTlDMzUuNzEzMiAzMi4wMzc1IDM1LjIyMzQgMzEuNTM5NSAzNC42MTkxIDMxLjUzOTVIMjIuNTg0MlpNMjQuNzcyMyAyNC44NjU3QzI0LjE2ODIgMjQuODY1OCAyMy42NzgzIDI1LjM2MzggMjMuNjc4MyAyNS45NzhDMjMuNjc4NCAyNi41OTIyIDI0LjE2ODMgMjcuMDkwMiAyNC43NzIzIDI3LjA5MDNIMzcuOTAxNEMzOC41MDU1IDI3LjA5MDMgMzguOTk1MyAyNi41OTIyIDM4Ljk5NTQgMjUuOTc4QzM4Ljk5NTQgMjUuMzYzNyAzOC41MDU2IDI0Ljg2NTcgMzcuOTAxNCAyNC44NjU3SDI0Ljc3MjNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjc2Ii8+CjxwYXRoIGQ9Ik0xOC43ODkxIDExLjI5NzVDMTkuMDY5MSAxMC4xODA4IDIwLjYyOTkgMTAuMTgwOCAyMC45MDk5IDExLjI5NzVMMjEuOTE0MyAxNS4zMDM2QzIyLjAxMTYgMTUuNjkxOCAyMi4zMDY1IDE1Ljk5NzggMjIuNjg2NyAxNi4xMDNMMjYuMzYxMSAxNy4xMTg3QzI3LjQzNyAxNy40MTYyIDI3LjQzNyAxOC45Njc2IDI2LjM2MTEgMTkuMjY1MUwyMi42NzYxIDIwLjI4NEMyMi4zMDE4IDIwLjM4NzQgMjIuMDA4NyAyMC42ODQ1IDIxLjkwNjggMjEuMDY1TDIwLjkwNDYgMjQuODEyNUMyMC42MTE3IDI1LjkwNTggMTkuMDg2MSAyNS45MDU5IDE4Ljc5MzMgMjQuODEyNUwxNy43OTExIDIxLjA2NUMxNy42ODkzIDIwLjY4NDcgMTcuMzk3IDIwLjM4NzUgMTcuMDIyOSAyMC4yODRMMTMuMzM2OCAxOS4yNjUxQzEyLjI2MTQgMTguOTY3MyAxMi4yNjE1IDE3LjQxNjUgMTMuMzM2OCAxNy4xMTg3TDE3LjAxMTIgMTYuMTAzQzE3LjM5MTYgMTUuOTk3OCAxNy42ODc0IDE1LjY5MTkgMTcuNzg0NyAxNS4zMDM2TDE4Ljc4OTEgMTEuMjk3NVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNzYiLz4KPHBhdGggZD0iTTEwLjAzNDMgNy4wMjQyNUMxMC4zMDY4IDUuODk0NDQgMTEuODg2OCA1Ljg5NDQ0IDEyLjE1OTQgNy4wMjQyNUwxMi42OTg5IDkuMjYyOThDMTIuNzkyNyA5LjY1MTc0IDEzLjA4NTEgOS45NTg4NyAxMy40NjQgMTAuMDY3OUwxNS41NzczIDEwLjY3NTFDMTYuNjM5MyAxMC45ODAzIDE2LjYzOTMgMTIuNTEwOSAxNS41NzczIDEyLjgxNjFMMTMuNDUzMyAxMy40MjY1QzEzLjA4MDIgMTMuNTMzOCAxMi43OTA4IDEzLjgzMzkgMTIuNjkyNSAxNC4yMTUxTDEyLjE1NTEgMTYuMzA0QzExLjg3IDE3LjQxMTYgMTAuMzIzNiAxNy40MTE2IDEwLjAzODUgMTYuMzA0TDkuNTAwMDMgMTQuMjE1MUM5LjQwMTczIDEzLjgzMzkgOS4xMTIzNSAxMy41MzM3IDguNzM5MyAxMy40MjY1TDYuNjE1MjQgMTIuODE2MUM1LjU1Mzc4IDEyLjUxMDYgNS41NTM2NCAxMC45ODA0IDYuNjE1MjQgMTAuNjc1MUw4LjcyODYyIDEwLjA2NzlDOS4xMDc2IDkuOTU4OTggOS4zOTk3OCA5LjY1MTg0IDkuNDkzNjIgOS4yNjI5OEwxMC4wMzQzIDcuMDI0MjVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjc2Ii8+CjxwYXRoIGQ9Ik0yNS45MDI4IDYuNzMzMTNDMjYuMTg3OCA1LjYyNTQxIDI3LjczNDMgNS42MjU0MSAyOC4wMTkzIDYuNzMzMTNMMjguMjAzMSA3LjQ0Njc5QzI4LjMwMyA3LjgzNDMxIDI4LjYwMDEgOC4xMzcwNSAyOC45ODA5IDguMjM5NzVMMjkuNTM0NCA4LjM4OTY1QzMwLjYxOTIgOC42ODIxMiAzMC42MTkzIDEwLjI0NjkgMjkuNTM0NCAxMC41MzkzTDI4Ljk2OTIgMTAuNjkxNEMyOC41OTQ0IDEwLjc5MjUgMjguMjk5OSAxMS4wODgzIDI4LjE5NTYgMTEuNDY4TDI4LjAxNTEgMTIuMTI4NUMyNy43MTc0IDEzLjIxMjggMjYuMjA0NyAxMy4yMTI4IDI1LjkwNyAxMi4xMjg1TDI1LjcyNTQgMTEuNDY4QzI1LjYyMTEgMTEuMDg4MiAyNS4zMjY4IDEwLjc5MjQgMjQuOTUxOCAxMC42OTE0TDI0LjM4NzcgMTAuNTM5M0MyMy4zMDI2IDEwLjI0NyAyMy4zMDI2IDguNjgxOTggMjQuMzg3NyA4LjM4OTY1TDI0Ljk0MDEgOC4yMzk3NUMyNS4zMjExIDguMTM3MDkgMjUuNjE5MSA3LjgzNDQ2IDI1LjcxOSA3LjQ0Njc5TDI1LjkwMjggNi43MzMxM1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNzYiLz4KPC9zdmc+Cg==",
ruleChainTypes = RuleChainType.CORE ruleChainTypes = RuleChainType.CORE
) )
public final class TbAiNode extends TbAbstractExternalNode implements TbNode { public final class TbAiNode extends TbAbstractExternalNode implements TbNode {

View File

@ -55,8 +55,10 @@ public class TbAiNodeConfiguration implements NodeConfiguration<TbAiNodeConfigur
@Override @Override
public TbAiNodeConfiguration defaultConfiguration() { public TbAiNodeConfiguration defaultConfiguration() {
var configuration = new TbAiNodeConfiguration(); var configuration = new TbAiNodeConfiguration();
configuration.setSystemPrompt("You are helpful assistant. Your response must be in JSON format."); configuration.setSystemPrompt(
configuration.setUserPrompt("Tell me a joke."); "You are a helpful AI assistant. Your primary function is to process the user's request and respond with a valid JSON object. " +
"Do not include any text, explanations, or markdown formatting before or after the JSON output."
);
configuration.setResponseFormat(new TbJsonResponseFormat()); configuration.setResponseFormat(new TbJsonResponseFormat());
configuration.setTimeoutSeconds(60); configuration.setTimeoutSeconds(60);
configuration.setForceAck(true); configuration.setForceAck(true);

View File

@ -1 +1,77 @@
### Prompt settings #### Example Usage: AI-Powered Alarm Analysis
This example demonstrates how to use the AI node to automatically analyze a new device alarm, generate a human-readable summary, and suggest troubleshooting steps.
This is useful for creating enriched notifications or populating a dashboard widget.
#### Scenario
An IoT freezer unit generates a "High Temperature" alarm. We want the AI to process this alarm data to create a clear summary and a recommended action plan for an operator.
1. Incoming Message Structure
When the alarm is created, the message sent through the rule chain has the following structure:
Message body (represent an alarm, usual alarm fields omitted for brevity)
```json
{
"details": {
"currentTemp_C": -5,
"threshold_C": -18
}
}
```
Message metadata
```json
{
"deviceName": "Freezer-B7",
"deviceType": "CommercialFreezer"
}
```
2. To achieve our goal, we configure the two prompt fields as follows:
**System prompt**
Here, we set the AI's role and enforce a strict JSON output format. This ensures the output is always machine-parsable.
```
You are an expert AI assistant for IoT operations.
Your task is to analyze device data and respond with a single, valid JSON object.
Do not include any text, explanations, or markdown formatting before or after the JSON output.
```
**User prompt**
This prompt defines the specific task, using templates to dynamically insert data from the incoming alarm message.
```
Analyze the following alarm from a "${deviceType}" unit named "${deviceName}".
Alarm Data:
$[*]
Based on the alarm data, generate a JSON object with two keys:
1. "summary": A brief, human-readable summary of the event.
2. "action": A concrete, recommended next step for an operator.
```
3. How It Works
When the alarm message from "Freezer-B7" is processed by the AI node, the templates are substituted with the actual data:
- `${deviceName}` becomes "Freezer-B7"
- `${deviceType}` becomes "CommercialFreezer"
- `$[*]` is replaced by the entire message body JSON: `{"alarmType": "High Temperature", "severity": "CRITICAL", "currentTemp_C": -5, "threshold_C": -18}`
The final instruction sent to the AI is a combination of the system and the substituted user prompt.
4. Expected AI Output
Given the combined instructions, the AI would generate the following structured JSON output, which can then be used in subsequent rule nodes (e.g., to send an enriched email or create a trouble ticket).
```json
{
"summary": "Critical high temperature alert on freezer unit Freezer-B7. The current temperature is -5°C, which is significantly above the required threshold of -18°C.",
"action": "Dispatch technician immediately to inspect the unit's cooling system and ensure the door is properly sealed. Investigate for potential power issues."
}
```

View File

@ -1157,13 +1157,13 @@
"max-output-tokens-min": "Must be greater than 0.", "max-output-tokens-min": "Must be greater than 0.",
"max-output-tokens-hint": "Sets the maximum number of tokens that the \nmodel can generate in a single response.", "max-output-tokens-hint": "Sets the maximum number of tokens that the \nmodel can generate in a single response.",
"endpoint": "Endpoint", "endpoint": "Endpoint",
"endpoint-required": "Endpoint id required.", "endpoint-required": "Endpoint is required.",
"service-version": "Service version", "service-version": "Service version",
"check-connectivity": "Check connectivity", "check-connectivity": "Check connectivity",
"check-connectivity-success": "Test request was successful", "check-connectivity-success": "Test request was successful",
"check-connectivity-failed": "Test request failed", "check-connectivity-failed": "Test request failed",
"no-model-matching": "No model matching '{{entity}}' were found.", "no-model-matching": "No model matching '{{entity}}' were found.",
"model-required": "AI Model is required.", "model-required": "Model is required.",
"no-model-text": "No model found" "no-model-text": "No model found"
}, },
"confirm-on-exit": { "confirm-on-exit": {
@ -5442,7 +5442,7 @@
"model": "Model", "model": "Model",
"ai-model-hint": "Select the pre-configured AI model to process requests sent by this rule node, or use \"Create new\" to configure a new one.", "ai-model-hint": "Select the pre-configured AI model to process requests sent by this rule node, or use \"Create new\" to configure a new one.",
"prompt-settings": "Prompt settings", "prompt-settings": "Prompt settings",
"prompt-settings-hint": "Configure the instructions for the AI. The optional system prompt sets the AI's general role and constraints, while the user prompt defines the specific task to perform. Both fields also support rule node templates to include dynamic data (e.g., use $[*] to access the entire message body or ${*} to access all metadata).", "prompt-settings-hint": "The optional system prompt sets the AI's general role and constraints, while the user prompt defines the specific task to perform. Both fields also support templatization.",
"system-prompt": "System prompt", "system-prompt": "System prompt",
"system-prompt-max-length": "System prompt must be 10000 characters or less.", "system-prompt-max-length": "System prompt must be 10000 characters or less.",
"system-prompt-blank": "System prompt must not be blank.", "system-prompt-blank": "System prompt must not be blank.",
@ -5465,7 +5465,7 @@
"timeout-required": "Timeout is required", "timeout-required": "Timeout is required",
"timeout-validation": "Must be from 1 second to 10 minutes.", "timeout-validation": "Must be from 1 second to 10 minutes.",
"force-acknowledgement": "Force acknowledgement", "force-acknowledgement": "Force acknowledgement",
"force-acknowledgement-hint": "Force acknowledgement" "force-acknowledgement-hint": "If enabled, the incoming message is acknowledged immediately. The model's response is then enqueued as a separate, new message."
} }
}, },
"timezone": { "timezone": {