Schema-Normalisierung
ADK-Rust normalisiert automatisch MCP tool schemas für jeden LLM-Anbieter zum Zeitpunkt der Anfrage. Das bedeutet, dass MCP tools nahtlos über Gemini, OpenAI, Anthropic und andere Anbieter hinweg funktionieren, ohne manuelle Schema-Anpassung.
Funktionsweise
MCP Server → raw JSON Schema → McpToolset (stores verbatim)
↓
Model.generate_content()
↓
SchemaAdapter.normalize_schema()
↓
Provider API (Gemini/OpenAI/Anthropic)
- McpToolset entdeckt tools und speichert ihre rohen
inputSchemaohne Modifikation - Wenn das Modell eine Anfrage erstellt, ruft es
schema_adapter().normalize_schema()auf das Schema jedes tools auf - Jeder Anbieter hat seinen eigenen Adapter, der nur die Transformationen anwendet, die seine API benötigt
- Ergebnisse werden nach Inhalts-Hash zwischengespeichert, um redundante Normalisierung zu vermeiden
Anbieterverhalten
| Merkmal | Gemini | OpenAI Strict | OpenAI | Anthropic | Generic |
|---|---|---|---|---|---|
$ref Auflösung | ✅ Inline-Elemente | ❌ Behält bei | ❌ Behält bei | ❌ Behält bei | ❌ Behält bei |
anyOf/oneOf | Kollabiert | Bewahrt | Bewahrt | Bewahrt | Bewahrt |
allOf | Zusammenführt | Bewahrt | Bewahrt | Bewahrt | Bewahrt |
additionalProperties | Entfernt | Setzt false | Bewahrt | Bewahrt | Bewahrt |
| Typ-Arrays | Kollabiert | Bewahrt | Bewahrt | Bewahrt | Bewahrt |
$schema | Streifen | Streifen | Streifen | Streifen | Streifen |
if/then/else | Streifen | Streifen | Streifen | Streifen | Streifen |
const | → enum | → enum | → enum | Bewahrt | → enum |
Nicht unterstützt format | Entfernt | Entfernt | Entfernt | Behält bei | Entfernt |
| Verschachtelungstiefenlimit | 5 Ebenen | Keine | Keine | Keine | Keine |
exclusiveMin/Max | Entfernt | Bewahrt | Bewahrt | Bewahrt | Bewahrt |
Der SchemaAdapter Trait
Alle Adapter implementieren diesen Trait aus 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;
}
Jede Llm Implementierung gibt ihren Adapter über schema_adapter() zurück:
use adk_core::Llm;
let model = GeminiModel::new(&api_key, "gemini-2.5-flash")?;
let adapter = model.schema_adapter(); // Returns &GeminiSchemaAdapter
Verfügbare Adapter
GeminiSchemaAdapter
Der aggressivste Adapter. Wendet alle destruktiven Transformationen an, die von Geminis function-calling API benötigt werden:
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();
Transformations-Pipeline:
$refauflösen (inline aus Definitionen, Zyklen bei Tiefe 10 unterbrechen)$schemaentfernenanyOf/oneOfkollabieren → erstes nicht-null Sub-SchemaallOfSub-Schemas zusammenführen- Typ-Arrays kollabieren (
["string", "null"]→"string") if/then/elseentfernenconst→ Einzelelementenumkonvertieren- Null aus
enumArrays entfernen - Implizites
type: "object"hinzufügen - Nicht unterstützte Schlüsselwörter entfernen
- Nicht unterstützte
formatWerte entfernen - Verschachtelungstiefe erzwingen (5 Ebenen)
definitions/$defsentfernen
OpenAiStrictSchemaAdapter
Bewahrt die Schema-Struktur und fügt additionalProperties: false für strukturierte Ausgaben hinzu:
use adk_model::openai::OpenAiStrictSchemaAdapter;
use adk_core::SchemaAdapter;
let adapter = OpenAiStrictSchemaAdapter;
OpenAiSchemaAdapter
Minimale sichere Korrekturen für den nicht-strengen Modus:
use adk_model::openai::OpenAiSchemaAdapter;
let adapter = OpenAiSchemaAdapter;
AnthropicSchemaAdapter
Nahezu unverändert — Anthropic unterstützt die meisten JSON Schema-Funktionen:
use adk_model::anthropic::AnthropicSchemaAdapter;
let adapter = AnthropicSchemaAdapter;
GenericSchemaAdapter
Standard für unbekannte Anbieter (Ollama, DeepSeek, etc.):
use adk_core::GenericSchemaAdapter;
let adapter = GenericSchemaAdapter;
Schema-Caching
Normalisierte Schemas werden anhand des Inhaltshashs zwischengespeichert, um redundante Berechnungen zu vermeiden:
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();
Der Cache befindet sich auf Modellinstanzen und wird während generate_content() automatisch verwendet.
Kürzung von Tool-Namen
Alle Adapter kürzen Tool-Namen, die 64 Bytes überschreiten, an gültigen UTF-8-Zeichengrenzen:
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());
Gemeinsame Dienstprogramme
Das adk_core::schema_utils Modul stellt zusammensetzbare Transformationsfunktionen bereit, die Adapter intern verwenden:
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"]);
Verfügbare Dienstprogramme:
strip_schema_keyword— entfernt$schemastrip_conditional_keywords— entferntif/then/elseadd_implicit_object_type— fügttype: "object"hinzu, wennpropertiesexistiertconvert_const_to_enum— konvertiertconstin ein ein-elementigesenumstrip_unsupported_formats— entfernt Formatwerte, die nicht in der Zulassungsliste sindstrip_null_from_enum— entfernt null aus Enum-Arraystruncate_tool_name— kürzt an der UTF-8-Grenzeresolve_refs— inlined$ref-Referenzen aus Definitionencollapse_combiners— kollabiertanyOf/oneOfzum ersten Nicht-Null-Wertmerge_all_of— führtallOf-Sub-Schemas zusammencollapse_type_arrays— kollabiert["string", "null"]zu"string"enforce_nesting_depth— ersetzt tiefe Schemas durch{"type": "object"}
Benutzerdefinierte Adapter
Implementieren Sie SchemaAdapter für benutzerdefinierte Anbieter:
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
}
}
Beispiel
Führen Sie die Schema-Normalisierungs-Demo aus, um alle Adapter in Aktion zu sehen:
cd examples/schema_normalization
cargo run
Keine API-Schlüssel erforderlich — demonstriert die Normalisierungslogik lokal.
Migration von sanitize_schema
Wenn Sie sich zuvor auf McpToolset verlassen haben, um vor-bereinigte Schemas zurückzugeben:
- Keine Code-Änderungen erforderlich für die Standardnutzung. Die
Toolsettrait API ist unverändert. - Schemas werden jetzt zur Anfragezeit vom Modelladapter normalisiert, nicht bei der Tool-Registrierung.
- Wenn Sie
parameters_schema()aufgerufen und Gemini-formatierten Output erwartet haben, wird stattdessen jetzt das rohe Schema zurückgegeben. Verwenden Sie das entsprechendeSchemaAdapter, um es bei Bedarf selbst zu normalisieren.
Zurück: ← MCP Tools | Weiter: Sessions →