Normalisation des schémas
ADK-Rust normalise automatiquement les schémas de Tool MCP pour chaque fournisseur de LLM au moment de la requête. Cela signifie que les Tools MCP fonctionnent de manière transparente avec Gemini, OpenAI, Anthropic et d'autres fournisseurs sans ajustement manuel du schéma.
Comment ça marche
MCP Server → raw JSON Schema → McpToolset (stores verbatim)
↓
Model.generate_content()
↓
SchemaAdapter.normalize_schema()
↓
Provider API (Gemini/OpenAI/Anthropic)
- McpToolset découvre les Tools et stocke leurs
inputSchemabruts sans modification. - Lorsque le modèle construit une requête, il appelle
schema_adapter().normalize_schema()sur le schéma de chaque Tool. - Chaque fournisseur a son propre adapter qui applique uniquement les transformations requises par son API.
- Les résultats sont mis en cache par hachage de contenu pour éviter une normalisation redondante.
Comportement du fournisseur
| Fonctionnalité | Gemini | OpenAI Strict | OpenAI | Anthropic | Générique |
|---|---|---|---|---|---|
résolution $ref | ✅ En ligne | ❌ Préserve | ❌ Préserve | ❌ Préserve | ❌ Préserve |
anyOf/oneOf | Réduit | Préserve | Préserve | Préserve | Préserve |
allOf | Fusionne | Préserve | Préserve | Préserve | Préserve |
additionalProperties | Supprime | Définit false | Préserve | Préserve | Préserve |
| Tableaux de types | Réduit | Préserve | Préserve | Préserve | Préserve |
$schema | Supprime | Supprime | Supprime | Supprime | Supprime |
if/then/else | Supprime | Supprime | Supprime | Supprime | Supprime |
const | → enum | → enum | → enum | Préserve | → enum |
format non pris en charge | Supprime | Supprime | Supprime | Préserve | Supprime |
| Limite de profondeur d'imbrication | 5 niveaux | Aucun | Aucun | Aucun | Aucun |
exclusiveMin/Max | Supprime | Préserve | Préserve | Préserve | Préserve |
Le trait SchemaAdapter
Tous les adaptateurs implémentent ce trait de adk-core:
use serde_json::Value;
use std::borrow::Cow;
pub trait SchemaAdapter: Send + Sync + std::fmt::Debug {
/// Normalize a raw JSON Schema for this provider.
fn normalize_schema(&self, schema: Value) -> Value;
/// Normalize a tool name (default: truncate to 64 bytes at UTF-8 boundary).
fn normalize_tool_name<'a>(&self, name: &'a str) -> Cow<'a, str>;
/// Fallback schema when no parameters_schema is provided.
fn empty_schema(&self) -> Value;
}
Chaque implémentation de Llm retourne son adaptateur via schema_adapter():
use adk_core::Llm;
let model = GeminiModel::new(&api_key, "gemini-2.5-flash")?;
let adapter = model.schema_adapter(); // Returns &GeminiSchemaAdapter
Adaptateurs disponibles
GeminiSchemaAdapter
L'adaptateur le plus agressif. Applique toutes les transformations destructives requises par l'API d'appel de fonctions de Gemini:
use adk_gemini::schema_adapter::GeminiSchemaAdapter;
use adk_core::SchemaAdapter;
// Standard Gemini API
let adapter = GeminiSchemaAdapter::new();
// Vertex AI (sets additionalProperties: false instead of removing)
let adapter = GeminiSchemaAdapter::vertex_ai();
Pipeline de transformation :
- Résout
$ref(en ligne à partir des définitions, rompt les cycles à la profondeur 10) - Supprime
$schema - Réduit
anyOf/oneOf→ premier sous-schéma non nul - Fusionne les sous-schémas
allOf - Réduit les tableaux de types (
["string", "null"]→"string") - Supprime
if/then/else - Convertit
const→enumà élément unique - Supprime null des tableaux
enum - Ajoute
type: "object"implicite - Supprime les mots-clés non pris en charge
- Supprime les valeurs
formatnon prises en charge - Applique la profondeur d'imbrication (5 niveaux)
- Supprime
definitions/$defs
OpenAiStrictSchemaAdapter
Préserve la structure du schéma tout en ajoutant additionalProperties: false pour les sorties structurées :
use adk_model::openai::OpenAiStrictSchemaAdapter;
use adk_core::SchemaAdapter;
let adapter = OpenAiStrictSchemaAdapter;
OpenAiSchemaAdapter
Correctifs minimaux sécurisés pour le mode non strict :
use adk_model::openai::OpenAiSchemaAdapter;
let adapter = OpenAiSchemaAdapter;
AnthropicSchemaAdapter
Quasi-transparent — Anthropic prend en charge la plupart des fonctionnalités de JSON Schema :
use adk_model::anthropic::AnthropicSchemaAdapter;
let adapter = AnthropicSchemaAdapter;
GenericSchemaAdapter
Par défaut pour les fournisseurs inconnus (Ollama, DeepSeek, etc.) :
use adk_core::GenericSchemaAdapter;
let adapter = GenericSchemaAdapter;
Mise en cache des schémas
Les schémas normalisés sont mis en cache par hachage de contenu afin d'éviter les calculs redondants :
use adk_core::{SchemaCache, GenericSchemaAdapter, SchemaAdapter};
use serde_json::json;
let cache = SchemaCache::new();
let adapter = GenericSchemaAdapter;
let schema = json!({"type": "object", "properties": {"name": {"type": "string"}}});
// First call normalizes and caches
let result = cache.get_or_normalize(&schema, &adapter);
// Subsequent calls return cached result (no re-normalization)
let cached = cache.get_or_normalize(&schema, &adapter);
// Invalidate when tools change
cache.clear();
Le cache réside sur les instances de modèle et est automatiquement utilisé pendant generate_content().
Troncation des noms d'outils
Tous les adaptateurs tronquent les noms d'outils dépassant 64 octets aux limites des caractères UTF-8 valides :
use adk_core::SchemaAdapter;
use adk_gemini::schema_adapter::GeminiSchemaAdapter;
let adapter = GeminiSchemaAdapter::new();
// Short names pass through unchanged
let name = adapter.normalize_tool_name("get_weather");
assert_eq!(name, "get_weather");
// Long names are truncated to 64 bytes
let long = "mcp_server_github_com_organization_repository_pull_request_review_comments";
let truncated = adapter.normalize_tool_name(long);
assert!(truncated.len() <= 64);
// Multi-byte characters are never split
let emoji_name = "🔧_tool_名前が長い";
let result = adapter.normalize_tool_name(emoji_name);
assert!(std::str::from_utf8(result.as_bytes()).is_ok());
Utilitaires partagés
Le module adk_core::schema_utils fournit des fonctions de transformation composables que les adaptateurs utilisent en interne :
use adk_core::schema_utils;
use serde_json::json;
let mut schema = json!({
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"status": { "const": "active" },
"email": { "type": "string", "format": "hostname" }
},
"if": { "properties": { "x": { "type": "number" } } },
"then": { "required": ["x"] }
});
// Apply individual transforms
schema_utils::strip_schema_keyword(&mut schema);
schema_utils::strip_conditional_keywords(&mut schema);
schema_utils::convert_const_to_enum(&mut schema);
schema_utils::strip_unsupported_formats(&mut schema, &["date-time", "email", "uri"]);
Utilitaires disponibles :
strip_schema_keyword— supprime$schemastrip_conditional_keywords— supprimeif/then/elseadd_implicit_object_type— ajoutetype: "object"quandpropertiesexisteconvert_const_to_enum— convertitconstenenumà élément uniquestrip_unsupported_formats— supprime les valeurs de format non autoriséesstrip_null_from_enum— supprime le null des tableaux d'énumérationstruncate_tool_name— tronque à la limite UTF-8resolve_refs— intègre les références$refdes définitionscollapse_combiners— réduitanyOf/oneOfau premier non-nulmerge_all_of— fusionne les sous-schémasallOfcollapse_type_arrays— réduit["string", "null"]à"string"enforce_nesting_depth— remplace les schémas profonds par{"type": "object"}
Adaptateurs personnalisés
Implémentez SchemaAdapter pour les fournisseurs personnalisés :
use adk_core::{SchemaAdapter, schema_utils};
use serde_json::Value;
use std::borrow::Cow;
#[derive(Debug)]
struct MyProviderAdapter;
impl SchemaAdapter for MyProviderAdapter {
fn normalize_schema(&self, mut schema: Value) -> Value {
// Apply only the transforms your provider needs
schema_utils::strip_schema_keyword(&mut schema);
schema_utils::strip_conditional_keywords(&mut schema);
schema_utils::add_implicit_object_type(&mut schema);
// Keep everything else as-is
schema
}
}
Exemple
Exécutez la démo de normalisation de schéma pour voir tous les adaptateurs en action :
cd examples/schema_normalization
cargo run
Aucune clé API n'est nécessaire — démontre la logique de normalisation localement.
Migration depuis sanitize_schema
Si vous vous appuyiez auparavant sur McpToolset renvoyant des schémas pré-sanitisés :
- Aucune modification de code n'est nécessaire pour une utilisation standard. L'API du trait
Toolsetest inchangée. - Les schémas sont maintenant normalisés au moment de la requête par l'adaptateur de modèle, et non lors de l'enregistrement de l'outil.
- Si vous appeliez
parameters_schema()et attendiez une sortie au format Gemini, le schéma brut est maintenant renvoyé à la place. Utilisez l'SchemaAdapterapproprié pour le normaliser vous-même si nécessaire.
Précédent: ← MCP Tools | Suivant: Sessions →