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

# Agent Output

> Design strict agent envelopes so orchestration code can execute tool calls without heuristics.

Agent loops typically alternate between "call a tool" and "return a final answer." This pattern is for manual tool handling: the model emits a structured `tool_call` envelope, and your application is responsible for executing the tool and continuing the loop. That is different from OpenAI-style built-in tool calling, where the API returns tool call objects in its own response format. Without a strict output contract, the model might mix fields from both branches, invent new action types, or omit the information your executor needs to dispatch. A `oneOf` schema with a `const` discriminator eliminates this: the model picks exactly one branch, emits exactly the right fields, and your orchestration code can dispatch without parsing heuristics.

## Use case

You run an agent that can either call a tool or return a final answer. You want one schema that covers both outcomes, with a confidence score on every action for routing and observability.

## Schema pattern

<CodeGroup>
  ```json JSON Schema theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "oneOf": [
      {
        "type": "object",
        "properties": {
          "kind": { "const": "tool_call" },
          "tool": { "type": "string", "enum": ["search_docs", "lookup_order", "send_email"] },
          "arguments": { "type": "object", "additionalProperties": true },
          "confidence": { "type": "number", "minimum": 0, "maximum": 1 }
        },
        "required": ["kind", "tool", "arguments", "confidence"],
        "additionalProperties": false
      },
      {
        "type": "object",
        "properties": {
          "kind": { "const": "final_answer" },
          "answer": { "type": "string", "minLength": 1, "maxLength": 1200 },
          "confidence": { "type": "number", "minimum": 0, "maximum": 1 }
        },
        "required": ["kind", "answer", "confidence"],
        "additionalProperties": false
      }
    ]
  }
  ```

  ```python Pydantic theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  from typing import Annotated, Any, Literal
  from pydantic import BaseModel, ConfigDict, Field

  class ToolCall(BaseModel):
      model_config = ConfigDict(extra="forbid")
      kind: Literal["tool_call"]
      tool: Literal["search_docs", "lookup_order", "send_email"]
      arguments: dict[str, Any]
      confidence: float = Field(..., ge=0, le=1)

  class FinalAnswer(BaseModel):
      model_config = ConfigDict(extra="forbid")
      kind: Literal["final_answer"]
      answer: str = Field(..., min_length=1, max_length=1200)
      confidence: float = Field(..., ge=0, le=1)

  AgentOutput = Annotated[ToolCall | FinalAnswer, Field(discriminator="kind")]
  ```

  ```typescript Zod theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  import { z } from "zod";

  const agentOutputSchema = z.discriminatedUnion("kind", [
    z.object({
      kind: z.literal("tool_call"),
      tool: z.enum(["search_docs", "lookup_order", "send_email"]),
      arguments: z.record(z.string(), z.any()),
      confidence: z.number().min(0).max(1),
    }).strict(),
    z.object({
      kind: z.literal("final_answer"),
      answer: z.string().min(1).max(1200),
      confidence: z.number().min(0).max(1),
    }).strict(),
  ]);
  ```

  ```bash curl theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  curl https://api.dottxt.ai/v1/chat/completions \
    -H "Authorization: Bearer $DOTTXT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "openai/gpt-oss-20b",
      "messages": [{ "role": "user", "content": "Use the lookup_order tool to check order ORD-9842." }],
      "response_format": {
        "type": "json_schema",
        "json_schema": {
          "name": "agent_output",
          "schema": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "kind": { "const": "tool_call" },
                  "tool": { "type": "string", "enum": ["search_docs", "lookup_order", "send_email"] },
                  "arguments": { "type": "object", "additionalProperties": true },
                  "confidence": { "type": "number", "minimum": 0, "maximum": 1 }
                },
                "required": ["kind", "tool", "arguments", "confidence"],
                "additionalProperties": false
              },
              {
                "type": "object",
                "properties": {
                  "kind": { "const": "final_answer" },
                  "answer": { "type": "string", "minLength": 1, "maxLength": 1200 },
                  "confidence": { "type": "number", "minimum": 0, "maximum": 1 }
                },
                "required": ["kind", "answer", "confidence"],
                "additionalProperties": false
              }
            ]
          }
        }
      }
    }'
  ```
</CodeGroup>

## Prompt snippet

```text theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
Choose exactly one action.
- If a tool is required, return kind="tool_call" with tool and arguments.
- If no tool is required, return kind="final_answer" with answer.
Never include fields from both branches.
```

## Example outputs

Tool call:

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "kind": "tool_call",
  "tool": "lookup_order",
  "arguments": {
    "order_id": "ORD-9842"
  },
  "confidence": 0.94
}
```

Final answer:

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "kind": "final_answer",
  "answer": "Order ORD-9842 shipped on 2026-02-20 and is expected tomorrow.",
  "confidence": 0.88
}
```

## Why this works

`oneOf` enforces mutual exclusivity: the model must produce either a `tool_call` or a `final_answer`, never a hybrid. Your executor reads `kind`, dispatches to the right handler, and never needs to guess what the model intended.

The `const` discriminator on `kind` removes ambiguity. Without it, both branches might structurally overlap (both could have a string field), and validation alone couldn't tell them apart.

Confidence appears in both branches, so your orchestration layer can apply the same routing logic regardless of action type, for example, escalating to a human when confidence drops below a threshold.

## Related docs

* [Composition reference](/json-schema/reference/composition)
* [Object reference](/json-schema/reference/object)
* [Union of Objects cookbook](/json-schema/union-of-objects)
