> ## Documentation Index
> Fetch the complete documentation index at: https://developer.kodexa.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# AI Naming

> Use an LLM to automatically generate contextual titles and descriptions for activities, tasks, and intake documents to keep dashboards readable.

When an activity, task, or intake document is created without an explicit title, the platform can use an LLM to generate a contextual title and description automatically. This keeps dashboards, task lists, and audit trails readable without requiring users to write titles by hand.

## When AI Naming Runs

AI naming is evaluated at three trigger points:

| Trigger                                  | Created Resource               | Where to Configure     |
| ---------------------------------------- | ------------------------------ | ---------------------- |
| Activity creation                        | Activity title and description | Activity Plan metadata |
| Task creation (from a CREATE\_TASK step) | Task title and description     | Task Template metadata |
| Intake upload (from a task template)     | Task title and description     | Task Template metadata |

At each trigger point, the platform checks whether the resource already has an explicit title. If it does, AI naming is skipped entirely.

## Enabling AI Naming

Add an `aiNaming` block to the `metadata` of an Activity Plan or Task Template.

### JSON (API)

```json theme={null}
{
  "metadata": {
    "aiNaming": {
      "enabled": true,
      "prompt": "Generate a title for {templateName} processing {documentFamilyPaths}"
    }
  }
}
```

### YAML (kdx sync)

```yaml theme={null}
metadata:
  aiNaming:
    enabled: true
    prompt: "Generate a title for {templateName} processing {documentFamilyPaths}"
```

<Note>
  The `aiNaming` block lives inside the resource's `metadata` object, not at the top level. This is the same `metadata` field used for other resource-level configuration.
</Note>

## Prompt Placeholders

The prompt string supports placeholders that the platform resolves before sending the text to the LLM. Wrap each placeholder in curly braces.

| Placeholder                  | Description                                                                     | Example Output                                 |
| ---------------------------- | ------------------------------------------------------------------------------- | ---------------------------------------------- |
| `{templateName}`             | Activity plan or task template name                                             | `Invoice Review`                               |
| `{activityPlanName}`         | Alias for `{templateName}`                                                      | `Invoice Review`                               |
| `{documentFamilyPaths}`      | Comma-separated file paths of attached documents                                | `invoices/acme-001.pdf, invoices/acme-002.pdf` |
| `{documentFamilyCount}`      | Number of document families                                                     | `2`                                            |
| `{knowledgeFeatures}`        | Comma-separated knowledge feature names                                         | `Net 30 Terms, Auto-Renewal`                   |
| `{metadata:key}`             | Top-level metadata value from document families                                 | `Acme Corp`                                    |
| `{metadata:key.nested.path}` | Dot-path into metadata JSON (GJSON syntax)                                      | `Austin`                                       |
| `{externalData:key}`         | Entire JSON blob for an external data key                                       | `{"invoiceNumber":"INV-001"}`                  |
| `{externalData:key.path}`    | Dot-path into external data JSON (first segment is the key, rest is GJSON path) | `INV-001`                                      |

### Metadata and External Data Paths

The `{metadata:...}` and `{externalData:...}` placeholders use a dot-separated path to reach nested values.

For metadata, the path resolves directly against the document family metadata JSON:

```
{metadata:companyName}          -> top-level key
{metadata:sender.address.city}  -> nested path using GJSON syntax
```

For external data, the first segment identifies the external data key, and the remaining segments navigate into that key's JSON value:

```
{externalData:erp-reference}                 -> entire JSON blob
{externalData:erp-reference.invoiceNumber}   -> specific field within the blob
```

### Multi-Document Behavior

When multiple document families are involved in a single activity or task, metadata and external data values are collected from all documents, deduplicated, and joined with `; `.

For example, if two documents have `companyName` metadata values of "Acme Corp" and "Beta Inc", the placeholder `{metadata:companyName}` resolves to:

```
Acme Corp; Beta Inc
```

## Title Resolution Fallback Chain

The platform uses the first available title from this ordered chain:

1. **Explicit title** -- If the caller provides a title directly, it is used as-is.
2. **AI naming** -- If `aiNaming.enabled` is true and a prompt is configured, the LLM generates a title and description.
3. **Template rendering** -- If `defaultTitleTemplate` or `defaultDescriptionTemplate` is set on the plan or template, it is rendered using Go template syntax with `{{ .inputs.field }}` placeholders.
4. **Plan or template name** -- The name of the Activity Plan or Task Template is used as the title.
5. **Generic fallback** -- `"Untitled"`.

<Tip>
  You can use `defaultTitleTemplate` and AI naming together. The template acts as a reliable fallback if the LLM call fails for any reason.
</Tip>

## Example Prompts

### Invoice processing with company context

```
Generate a concise title for a {templateName} task processing
{documentFamilyCount} document(s): {documentFamilyPaths}.
The company is {metadata:companyName}.
```

This might produce: **"Acme Corp Invoice Review - 3 documents"**

### Task with external reference data

```
Name this task based on the invoice from {metadata:vendorName}
with invoice number {externalData:erp-data.invoiceNumber}
for {metadata:companyName}.
```

This might produce: **"GlobalParts Inc Invoice #INV-2026-0412 for Acme Corp"**

### Contract review with knowledge features

```
Generate a title for reviewing a contract with these key terms:
{knowledgeFeatures}. Documents: {documentFamilyPaths}.
```

This might produce: **"Contract Review - Net 30 Terms, Auto-Renewal Clause"**

## LLM Response Format

The LLM must respond with a JSON object containing `title` and `description` fields:

```json theme={null}
{
  "title": "Acme Corp Invoice Review - 3 documents",
  "description": "Review of 3 invoices uploaded from Acme Corp for Q2 processing"
}
```

The platform parses this JSON from the LLM response and applies both fields to the created resource.

<Warning>
  If AI naming fails -- whether from an LLM error, a JSON parse error, or a timeout -- the system silently falls back to the next step in the fallback chain. No error is surfaced to the user. Design your `defaultTitleTemplate` as a reliable backup.
</Warning>

## Best Practices

**Keep prompts concise.** The platform uses a small, fast LLM model for naming to minimize latency. Long, detailed prompts do not improve results and slow down resource creation.

**Include the most distinctive data points.** Company name, document type, and reference numbers produce the most useful titles. Avoid generic placeholders that add little differentiation.

**Always set a `defaultTitleTemplate` as a fallback.** AI naming depends on an external LLM call. If the call fails, a well-crafted template ensures activities and tasks still get meaningful titles.

**Test with representative data.** Use documents that reflect your production workload to verify that placeholders resolve to useful values and that the LLM produces titles at the right level of detail.

```yaml theme={null}
# Complete example: Activity Plan with AI naming and template fallback
name: Invoice Intake
slug: invoice-intake
description: Classify, extract, review, and post invoice data
metadata:
  aiNaming:
    enabled: true
    prompt: >-
      Generate a title for {templateName} processing
      {documentFamilyCount} document(s) from {metadata:companyName}
defaultTitleTemplate: "Invoice intake for {{ .inputs.documentFamilyId }}"
inputsSchema:
  type: object
  required:
    - documentFamilyId
  properties:
    documentFamilyId:
      type: string
steps:
  - slug: extract
    kind: EXECUTION
    config:
      moduleRef: kodexa/invoice-extractor
      options:
        documentFamilyId: "{{ .inputs.documentFamilyId }}"
```
