diff --git a/docs/agents/llm-agents.md b/docs/agents/llm-agents.md index 4a2578dece..10cfed3034 100644 --- a/docs/agents/llm-agents.md +++ b/docs/agents/llm-agents.md @@ -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 @@ -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. @@ -574,6 +595,25 @@ schema definitions. .build(); ``` +=== "Kotlin" + + 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. diff --git a/examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt b/examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt index 468c64073b..5f859d4feb 100644 --- a/examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt +++ b/examples/kotlin/snippets/agents/llm-agent/CapitalAgent.kt @@ -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 @@ -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(