Skip to main content

Write goals

Goals compose multiple prompts into named workflows. This guide explains how to define goal groups in goals.yml, write filter expressions, and manage goals at runtime.

Define a goal group

Add goal groups under goals.globals in goals.yml:

goals:
globals:
- id: document-analysis
goals:
- promptId: basePrompt
filter: "true"
index: 1
- promptId: arenderContext
filter: "true"
index: 10
- promptId: contractSummary
filter: "[[${documentType == 'contract'}]]"
index: 20
- promptId: invoiceSummary
filter: "[[${documentType == 'invoice'}]]"
index: 30

When a request includes { "type": "goal", "value": "document-analysis" }, the system evaluates each entry's filter. Entries where the filter returns true have their prompts rendered and injected into the LLM call, in ascending index order.

Fields

FieldDescription
idUnique identifier for the goal group. Referenced as the value in request content items.
goals[].promptIdID of the prompt to include when the filter passes.
goals[].filterThymeleaf boolean expression. Use "true" for unconditional inclusion.
goals[].indexExecution order. Lower values run first. Gaps are allowed.

Filter expressions

Filters are evaluated using the same Thymeleaf context as prompt rendering. The request payload variables are available:

"[[${documentType == 'contract'}]]"
"[[${language != null}]]"
"[[${priority > 5}]]"

Use "true" for a filter that always passes. Use "false" to temporarily disable a goal entry.

Payload variables in filters

Variables in the payload of the request content item are available in filter expressions. For example, if the request sends "payload": { "documentType": "invoice" }, the filter [[${documentType == 'invoice'}]] evaluates to true.

Tenant overrides

Define per-tenant goal groups under goals.tenants:

goals:
tenants:
- tenantId: my-tenant
mergeStrategy: merge
goalGroups:
- id: document-analysis
goals:
- promptId: tenantSpecificAnalysis
filter: "true"
index: 5
- promptId: arenderContext
filter: "true"
index: 10

mergeStrategy: merge updates matching goal groups for the tenant. Goal entries within the group are replaced entirely (the group is treated as a unit). mergeStrategy: replace replaces the full tenant goal configuration.

Runtime management

Goal groups can be created, updated, and deleted via the Admin API without restarting:

# List goal groups for the current tenant
curl https://your-gateway/api/v1/admin/goals

# Create a goal group
curl -X POST https://your-gateway/api/v1/admin/goals \
-H "Content-Type: application/json" \
-d '{
"id": "my-workflow",
"goals": [
{ "promptId": "basePrompt", "filter": "true", "index": 1 },
{ "promptId": "arenderContext", "filter": "true", "index": 10 }
]
}'

# Update a goal group
curl -X PUT https://your-gateway/api/v1/admin/goals/{id} \
-H "Content-Type: application/json" \
-d '{ ... }'

# Delete a goal group
curl -X DELETE https://your-gateway/api/v1/admin/goals/{id}

Changes are persisted in OpenSearch and take effect immediately for the tenant.

Example: document-type routing

A goal that routes to different prompts depending on document type:

- id: analyse
goals:
- promptId: basePrompt
filter: "true"
index: 1
- promptId: arenderContext
filter: "true"
index: 10
- promptId: contractReview
filter: "[[${documentType == 'contract'}]]"
index: 20
- promptId: invoiceReview
filter: "[[${documentType == 'invoice'}]]"
index: 20
- promptId: genericReview
filter: "[[${documentType == null}]]"
index: 20

Only one of the three prompts at index 20 will be included for any given request, depending on the documentType payload variable.