Skip to content
48 changes: 44 additions & 4 deletions docs/agents/llm-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,10 +475,10 @@ schema definitions.
Using `output_schema` with `tools` in the same LLM request is only supported
by specific models, including [Gemini
3.0](https://ai.google.dev/gemini-api/docs/function-calling?example=meeting#structured-output).
For other models, workarounds using [function
tools](https://github.com/google/adk-python/blob/main/src/google/adk/flows/llm_flows/_output_schema_processor.py))
in ADK may not work reliably. In such cases, consider using sub-agents that
handle output formatting separately.
For other models, ADK falls back to a [`set_model_response` function
tool](https://github.com/google/adk-python/blob/main/src/google/adk/flows/llm_flows/_output_schema_processor.py)
to collect the structured output, which may not work reliably. In such
cases, consider using sub-agents that handle output formatting separately.

- **`output_key` (Optional):** Provide a string key. If set, the text content of
the agent's *final* response will be automatically saved to the session's
Expand All @@ -490,6 +490,27 @@ schema definitions.
- In Golang, within a callback handler: `ctx.State().Set(output_key,
agentResponseText)`

When `output_schema` is also set, the *parsed* response is stored instead of
the text: a `dict` in Python, and a `Map` in Java and Kotlin.

!!! note "Schema validation in Java and Kotlin"

Java and Kotlin check the response against the *structure* of the schema —
`type`, `required`, `nullable`, `anyOf` and `items` (see
[`SchemaUtils`](https://github.com/google/adk-kotlin/blob/v0.8.0/core/src/commonMain/kotlin/com/google/adk/kt/SchemaUtils.kt)).
Constraint fields such as `pattern`, `minLength` and `minimum` are sent to
the model as part of the schema, but ADK does not re-check them, so the
model decides whether to honor them. Python validates against a Pydantic
model, which does enforce the constraints declared on it.

Java and Kotlin accept only a top-level object schema; a top-level array or
primitive fails validation. Python also supports list and primitive output
schemas.

If the response fails validation, ADK logs the error and stores the raw
response string under `output_key` instead of the parsed object (see
[`LlmAgent`](https://github.com/google/adk-kotlin/blob/v0.8.0/core/src/commonMain/kotlin/com/google/adk/kt/agents/LlmAgent.kt)).

=== "Python"

The input and output schema is typically a `Pydantic` BaseModel.
Expand Down Expand Up @@ -574,6 +595,25 @@ schema definitions.
.build();
```

=== "Kotlin"
Comment thread
joefernandez marked this conversation as resolved.

The input and output schema is ADK's own `com.google.adk.kt.types.Schema`,
not the same-named type in the GenAI SDK. Starting with ADK Kotlin v0.8.0,
the JSON schema includes constraints for the following fields: `pattern`,
`minLength`, `maxLength`, `minimum`, `maximum`, `minItems`, `maxItems`,
`format`, `nullable`, `default`, `anyOf` and `title`.

```kotlin
--8<-- "examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt:schema_example"
```

The `format` field accepts only the values the model allows for the field's type. For
the accepted values, see the Gemini [`Schema`
reference](https://ai.google.dev/api/caching#Schema).

The `default` field must contain a JSON-native value. ADK's own `Json` serializes one,
but a hand-rolled serializer without a contextual `Any` serializer does not.

### Manage agent context

Control whether the agent receives the prior conversation history.
Expand Down
42 changes: 42 additions & 0 deletions examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import com.google.adk.kt.sessions.InMemorySessionService
import com.google.adk.kt.types.Content
import com.google.adk.kt.types.GenerateContentConfig
import com.google.adk.kt.types.Part
import com.google.adk.kt.types.Schema
import com.google.adk.kt.types.Type
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking

Expand Down Expand Up @@ -81,6 +83,46 @@ fun main() =
)
// --8<-- [end:gen_config]

// --8<-- [start:schema_example]
val capitalOutput =
Schema(
type = Type.OBJECT,
description = "Schema for capital city information.",
properties =
mapOf(
"capital" to
Schema(
type = Type.STRING,
description = "The capital city of the country.",
// Constraint fields, added in adk-kotlin 0.8.0.
minLength = 2,
maxLength = 60,
),
"countryCode" to
Schema(
type = Type.STRING,
description = "ISO 3166-1 alpha-2 code for the country.",
pattern = "^[A-Z]{2}$",
),
),
required = listOf("capital", "countryCode"),
)

val structuredCapitalAgent =
LlmAgent(
name = "structured_capital_agent",
model = Gemini(name = "gemini-flash-latest"),
instruction =
Instruction(
"You are a Capital Information Agent. Given a country, respond ONLY " +
"with a JSON object holding the capital city and the country's " +
"ISO 3166-1 alpha-2 code.",
),
outputSchema = capitalOutput,
outputKey = "found_capital",
)
// --8<-- [end:schema_example]

// --8<-- [start:full_example]
val finalAgent =
LlmAgent(
Expand Down
Loading