Free cookie consent management tool by TermsFeed

Output JSON

On This Page

Even for simple responses, returning JSON is a best practice. It ensures consistency and allows your AI skill to scale and prepares your application for future changes. If you need to add more fields later, the existing structure will already support it.

Why return JSON?

Most AI skills should output JSON. Here's why:

  • It's structured and machine-readable.
  • It's easy to extract and use in Appian.
  • It's scalable when you need to add more fields later.

Define the expected output

Completed in: prompt builder

Clearly describe the expected JSON structure in the prompt. For example:

1
2
3
4
5
6
Your response should be valid JSON with keys for totalCost and purchaseOrderNumber. Use the following structure:

{
  "totalCost": decimal,
  "purchaseOrderNumber": text
}`

Formatting tips

Completed in: prompt builder

To help the model return valid JSON:

  • Avoid trailing commas.
  • Escape quotation marks properly by using a backslash (\) before each quote inside the string: "They said \"yes\"".
  • Replace line breaks with \n.

Use examples

Completed in: prompt builder

Examples help the model understand how to respond by showing it a few sample inputs and their expected outputs. This approach doesn't require training. Just demonstrate the format and structure you want.

When using examples, make sure each output follows the same JSON structure you expect in the final response. This helps the model stay consistent and easier to parse.

Scroll or drag to pan · ⌘+/⌘− to zoom · Esc to closeimages/automation/few-shot-example.png

Raw text equivalent

1
2
3
4
5
6
<examples>
Human: "The team gathered around the newly arrived shipment, a collection of specialized sensors critical for the ongoing environmental monitoring project. Upon careful inspection, it was confirmed that all twenty units were present and undamaged, aligning perfectly with the details outlined in purchase order number PO-2025-0411-Alpha. After accounting for the unit price, expedited shipping, and a small handling fee, the totalCost for this essential equipment came to $7,850.00."
Assistant: {"totalCost": "7850.00", "purchaseOrderNumber": "PO-2025-0411-Alpha"}
Human: "The accounting department finalized the invoice for the bulk order of office supplies placed last week. Reviewing the documentation, they noted that purchase order number 10-Delta-77 covered items ranging from stationery to new ergonomic chairs for the marketing team. After applying the negotiated corporate discount and factoring in the delivery charges, the totalCost billed to the department amounted to $1,245.75."
Assistant: {"totalCost": "1245.75", "purchaseOrderNumber": "10-Delta-77"}
</examples>

Set a low temperature

Completed in: prompt builder

Lower temperature values make responses more predictable. This is useful when you need consistent structure like JSON.

  • Set to 0 for the most deterministic output.
  • Use 10 or 20 if you want slightly more flexibility without sacrificing structure.

Scroll or drag to pan · ⌘+/⌘− to zoom · Esc to closeimages/automation/temp_20.png

Choosing the right temperature often takes some experimentation. The goal is to strike a balance between consistent structure and flexible, natural-sounding responses. Start low and adjust as needed based on how well the model follows the expected format.

Handle preamble in responses

Completed in: process modeler

Some models might include extra text before the JSON (e.g., "Here is your response:"). This can break downstream processing.

Use the APP_extractJson() expression rule to extract the JSON, remove the preamble, and store the result in a process variable of type Map.:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
a!localVariables(
  local!outputTag: local!find("<output>", ri!json),
  local!removeOutput: if(
    local!outputTag = 0,
    ri!json,
    right(ri!json, len(ri!json) - (local!outputTag + 7))
  ),
  local!firstBrace: find("{", local!removeOutput),
  local!firstSquare: find("[", local!removeOutput),
  local!allEndBraces: regexsearch("\}", local!removeOutput, "gms"),
  local!allEndSquares: regexsearch("\]", local!removeOutput, "gms"),
  local!lastBrace: tointeger(if(
    a!isNotNullOrEmpty(local!allEndBraces),
    index(
      local!allEndBraces,
      length(local!allEndBraces)
    ).startPosition,
    0
  )),
  local!lastSquare: tointeger(if(
    a!isNotNullOrEmpty(local!allEndSquares),
    index(
      local!allEndSquares,
      length(local!allEndSquares)
    ).startPosition,
    0
  )),
  local!removePrefix: if(
    or(
      local!firstBrace = 1,
      local!firstSquare = 1
    ),
    local!removeOutput,
    right(
      local!removeOutput,
      len(local!removeOutput) - if(
        and(
          local!firstBrace > local!firstSquare,
          local!firstSquare <> 0
        ),
        local!firstSquare,
        local!firstBrace
      ) + 1
    )
  ),
  local!removeSuffix: if(
    or(
      local!firstBrace = len(local!removePrefix),
      local!firstSquare = len(local!removePrefix)
    ),
    local!removePrefix,
    left(
      local!removePrefix,
      if(
        local!lastBrace > local!lastSquare,
        local!lastBrace - local!firstBrace,
        local!lastSquare - local!firstSquare
      ) + 2
    )
  ),
  local!result: try(
    a!fromJson(local!removeSuffix),
    null
  ),
  if(
    a!isNullOrEmpty(local!result),
    null,
    if(
      a!defaultValue(ri!returnList, true),
      local!result,
      if(
        runtimetypeof(local!result) = runtimetypeof(a!flatten({local!result})),
        null,
        local!result
      )
    )
  )
)

To store the result in a process variable of type Map:

  1. Gen AI skill output: Store response as pv!responseString (type: Text).
  2. Script task for JSON extraction: Use the expression APP_extractJson(json: pv!responseString) to remove any preamble and cast to map.
  3. Store parsed JSON in a process variable of type map(s)
    • pv!responseMap for single object JSON response {}
    • pv!responseMaps for multiple object JSON response [{}]

Guard against invalid JSON

Completed in: process modeler

Even with examples and low temperature, the model might not always return valid JSON. Use a two-step approach:

  1. Try to extract JSON from the initial response followed by a validation step.
  2. If parsing fails, prompt the model again with a follow-up prompt that converts the original output into valid JSON.

This two-step approach adds a safeguard to your AI skill. If the initial response isn't valid JSON, a follow-up prompt helps correct the format and ensure the output can be used reliably in downstream processes.

Example

After parsing the JSON response into a map, use the index() function to safely access values. This adds a layer of protection by returning a default value if the expected key is missing.

1
index(pv!responseMap, "totalCost", 0)

In this process model, the additional logic node sets a default value if the JSON is still unreadable.

Scroll or drag to pan · ⌘+/⌘− to zoom · Esc to closeimages/automation/json-guardrail.png

In this example, if "totalCost" is not found in the response, the expression returns 0 instead of causing an error. This helps ensure your process continues smoothly, even if the model's output isn't perfect.

Feedback