Deferred Annotations
The sections on this page provide information about the various aspects of Deferred Annotations:
Concept
Deferred Annotations allow for the creation of on-demand annotations and provide a way to control potentially expensive actions, such as calls to Gen AI Models or databases, within a solution. Deferred Annotations ensure these expensive actions are executed only when they are needed and only the minimum number of times.
This is implemented natively in the engine so that:
- The solution developer uses the annotations in the same way as any other annotation, meaning usage is seamless for the developer.
- The engine then ensures that the annotation script is executed only when the annotation is needed, meaning execution is optimized to minimize impact on costs, latency, etc.
By calling expensive or time-consuming actions on demand during the matching process, Deferred Annotations provide solution developers with a tool for optimizing solutions at runtime. They can be used to control:
- Latency - a slow process can be deferred and only run once, and only if it is actually needed.
- Cost - an expensive process will be called only when needed and maximum once per transaction.
- Log Size/Clarity - many Deferred Annotations can be created but they will only be logged to the session log when actually used in testing.
Gen AI Annotators also make use of Deferred Annotations to create their annotations “behind the scenes”. By combining Deferred Annotations (either directly or via Gen AI Annotators) and Secrets, potentially expensive calls (such as Gen AI) can be encapsulated securely to not leak sensitive data. Also, they can be used seamlessly within the solution to ensure that the developer has the information needed to build a fully hybrid Gen AI solution, which automatically optimizes its own cost.
Deferred Annotation Script
A Deferred Annotation document consist of a list of Annotations and a script responsible for creating the listed annotation(s) using the engine method _.createInputAnnotation.
When one of the declared annotations is tested for the first time, the script of the Deferred Annotation will be requested and executed. An annotation is considered "tested” when a TLML Syntax containing the annotation is considered for matching, e.g. if a trigger or transition is tested and it contains a TLML match containing the annotation. If it (or any other annotation declared by the same Deferred Annotation) is already deferred at this stage, then the script is not executed again. If none of the declared annotations are tested against an input, then the script will not be executed for that transaction.
Please note that the script of a Deferred Annotation also would be requested and executed if calling the Engine method _.getInputAnnotations().getAll() in scripting.
This allows you to configure to use Annotations as needed within the solution, while Teneo ensures that they are only processed if and when they are needed.
UI
Deferred Annotations Overview
- Teneo Studio Desktop
- Teneo Studio Web
You can access Deferred Annotations from the backstage view of your Solution from the Globals section and the Annotations tab.

You can access Deferred Annotations from the Annotations section in the Globals tile in the main solution overview.

Deferred Annotations Window
- Teneo Studio Desktop
- Teneo Studio Web
When you open a Deferred Annotation, the following window opens:

In the Deferred Annotations window, the following information is available:
- Top ribbon
- List of Deferred Annotations
- Execution script
- Used By
To open a Flow that uses a Deferred Annotation: Ctrl + Click. To edit a Flow that uses a Deferred Annotation: Ctrl + Alt.
Top ribbon:
| Section | Button | Description | Related pages |
|---|---|---|---|
| Deferred Annotations | Close | Closes the Deferred Annotations window. | |
| Save | Saves the current Deferred Annotation. | ||
| Edit | Undo | Undoes the recent action. | |
| Redo | Redoes the recent action. | ||
| Stable version | Set | Sets stable version. | Versioning |
| Unset | Unsets stable version. | Versioning | |
| Branching | Include | Includes Deferred Annotation in branching. | Localization |
| Exclude | Excludes Deferred Annotation from branching. | Localization | |
| Script | Generate | Creates a draft for a Deferred Annotation script to be adapted. |
A TODO label is shown for a Deferred Annotation without a script, indicating that the implementation of the script still is pending. To get started, a draft of the script can be created with the Generate button.
Deferred Annotations Properties
The Deferred Annotations Properties window opens when you add a new Deferred Annotation:

In the Properties window, the following information is available:
- Name and Description: give the Deferred Annotation a name and a description
- Save: save Deferred Annotation
- History: observe versions of Deferred Annotation
- Close: close Deferred Annotation
- Top arrow: go back to the Deferred Annotations Window
When you open a Deferred Annotation, it looks like this:

In the Deferred Annotations window, the following information is available:
- List of Annotations
- Generate script - This creates a draft for a Deferred Annotation script that should be adapted accordingly.
- Execution script
- Tryout
A TODO label is shown for a Deferred Annotation without a script, indicating that the implementation of the script still is pending. To get started, a draft of the script can be created with the Generate Script button.
Practical Examples
Check for Credit card number
Let's add a Deferred Annotation that finds and annotates a credit card number when it appears in the user input:
-
Add a new Deferred Annotation and give it a name like
Creditcard numbers. -
Add an annotation named
CREDITCARD_NUMBER. -
Add to the Deferred Annotation the following script:
//Find a credit card number in the input using a regex
def generalCreditCardRegex = /\b(?:\d[ -]*?){13,16}\b/
// Remove any blank spaces from the number for the variable value
def normalizeNumber = number -> number.replace(' ', '')
def matchToWordIndexes = (match, sentence) -> sentence.words.findIndexValues {
it.beginIndex >= match.start()
&& it.endIndex <= match.end()
}.collect { it as Integer } as HashSet<Integer>
_.sentences.eachWithIndex { sentence, sentenceIndex ->
def regexMatches = sentence.text =~ generalCreditCardRegex
regexMatches.results().each { match ->
_.getInputAnnotations().add(
_.createInputAnnotation(
'CREDITCARD_NUMBER',
sentenceIndex,
matchToWordIndexes(match, sentence),
["cardNumber": normalizeNumber(match.group())]
)
)
}
} -
Now, create a flow where you want to match if a user input contains a credit card number. Add to its Tigger a TLML syntax match with the syntax:
%$CREDITCARD_NUMBER. If you want to save the value from the annotation variable, you can add a flow variable calledcardNumberto the flow and add a propagation script to fetch the variable value from the annotation:%$CREDITCARD_NUMBER^{cardNumber=lob.cardNumber}.
When testing with an input in in Tryout that contains a numeric sequence that corresponds to a credit card number, the flow should be triggered, the variable should be populated with the sequence and you should see the annotation in the Annotations section under Input in Advanced Tryout. If the input does not contain the number sequence, you can see that the annotation is not created.